@fleet-sdk/common 0.1.0-alpha.28 → 0.1.0-alpha.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,7 @@
1
1
  import { SortingDirection, SortingSelector } from "../types";
2
2
 
3
+ type ObjectSelector<T> = (item: T) => T[keyof T];
4
+
3
5
  export function isEmpty<T extends object>(obj?: T): obj is undefined;
4
6
  export function isEmpty<T>(array?: T[]): array is undefined;
5
7
  export function isEmpty<T>(obj?: T[] | object): obj is undefined {
@@ -43,7 +45,7 @@ export function hasDuplicates<T>(array: T[]): boolean {
43
45
  /**
44
46
  * Check for duplicate keys in complex elements
45
47
  */
46
- export function hasDuplicatesBy<T>(array: T[], selector: (value: T) => unknown): boolean {
48
+ export function hasDuplicatesBy<T>(array: T[], selector: ObjectSelector<T>): boolean {
47
49
  return array.some((item, index) => {
48
50
  return array.findIndex((x) => selector(x) === selector(item)) !== index;
49
51
  });
@@ -96,6 +98,28 @@ export function areEqual<T>(array1: ArrayLike<T>, array2: ArrayLike<T>): boolean
96
98
  return true;
97
99
  }
98
100
 
101
+ export function areEqualBy<T>(
102
+ array1: ArrayLike<T>,
103
+ array2: ArrayLike<T>,
104
+ selector: ObjectSelector<T>
105
+ ): boolean {
106
+ if (array1 === array2) {
107
+ return true;
108
+ }
109
+
110
+ if (array1.length != array2.length) {
111
+ return false;
112
+ }
113
+
114
+ for (let i = 0; i < array1.length; i++) {
115
+ if (selector(array1[i]) !== selector(array2[i])) {
116
+ return false;
117
+ }
118
+ }
119
+
120
+ return true;
121
+ }
122
+
99
123
  export function startsWith<T>(array: ArrayLike<T>, target: ArrayLike<T>): boolean {
100
124
  if (array === target) {
101
125
  return true;
@@ -133,3 +157,35 @@ export function endsWith<T>(array: ArrayLike<T>, target: ArrayLike<T>): boolean
133
157
 
134
158
  return true;
135
159
  }
160
+
161
+ export function uniq<T>(array: Array<T>): Array<T> {
162
+ if (isEmpty(array)) {
163
+ return array;
164
+ }
165
+
166
+ return Array.from(new Set(array));
167
+ }
168
+
169
+ export function uniqBy<T>(
170
+ array: Array<T>,
171
+ selector: ObjectSelector<T>,
172
+ selection: "keep-first" | "keep-last" = "keep-first"
173
+ ): Array<T> {
174
+ if (isEmpty(array)) {
175
+ return array;
176
+ }
177
+
178
+ return Array.from(
179
+ array
180
+ .reduce((map, e) => {
181
+ const key = selector(e);
182
+
183
+ if (selection === "keep-first" && map.has(key)) {
184
+ return map;
185
+ }
186
+
187
+ return map.set(key, e);
188
+ }, new Map())
189
+ .values()
190
+ );
191
+ }
@@ -1,13 +1,14 @@
1
- import { Amount, NonMandatoryRegisters, TokenId } from "../types";
1
+ import { Amount, AmountType, Box, NonMandatoryRegisters, TokenAmount, TokenId } from "../types";
2
+ import { isEmpty } from "./arrayUtils";
2
3
  import { _0n } from "./bigIntLiterals";
3
4
  import { ensureBigInt } from "./bigIntUtils";
4
5
  import { isDefined, isUndefined } from "./objectUtils";
5
6
 
6
7
  const NANOERGS_TOKEN_ID = "nanoErgs";
7
8
 
8
- export function utxoSum(boxes: MinimalBoxAmounts): BoxAmounts;
9
+ export function utxoSum(boxes: MinimalBoxAmounts): BoxSummary;
9
10
  export function utxoSum(boxes: MinimalBoxAmounts, tokenId: TokenId): bigint;
10
- export function utxoSum(boxes: MinimalBoxAmounts, tokenId?: TokenId): BoxAmounts | bigint {
11
+ export function utxoSum(boxes: MinimalBoxAmounts, tokenId?: TokenId): BoxSummary | bigint {
11
12
  const balances: { [tokenId: string]: bigint } = {};
12
13
 
13
14
  for (const box of boxes) {
@@ -31,14 +32,15 @@ export function utxoSum(boxes: MinimalBoxAmounts, tokenId?: TokenId): BoxAmounts
31
32
  }
32
33
 
33
34
  return {
34
- nanoErgs: balances[NANOERGS_TOKEN_ID],
35
+ nanoErgs: balances[NANOERGS_TOKEN_ID] || _0n,
35
36
  tokens: Object.keys(balances)
36
37
  .filter((x) => x !== NANOERGS_TOKEN_ID)
37
38
  .map((tokenId) => ({ tokenId, amount: balances[tokenId] }))
38
39
  };
39
40
  }
40
41
 
41
- export function utxoSumResultDiff(amountsA: BoxAmounts, amountsB: BoxAmounts): BoxAmounts {
42
+ // todo: make it accept arrays of utxos as params
43
+ export function utxoDiff(amountsA: BoxSummary, amountsB: BoxSummary): BoxSummary {
42
44
  const tokens: TokenAmount<bigint>[] = [];
43
45
  const nanoErgs = amountsA.nanoErgs - amountsB.nanoErgs;
44
46
 
@@ -54,14 +56,14 @@ export function utxoSumResultDiff(amountsA: BoxAmounts, amountsB: BoxAmounts): B
54
56
  return { nanoErgs, tokens };
55
57
  }
56
58
 
57
- const MIN_REGISTER_NUMBER = 4;
58
- const MAX_REGISTER_NUMBER = 9;
59
+ const MIN_REGISTERS = 4;
60
+ const MAX_REGISTERS = 9;
59
61
 
60
62
  export function areRegistersDenselyPacked(registers: NonMandatoryRegisters): boolean {
61
63
  let lastValueIndex = 0;
62
- for (let i = MIN_REGISTER_NUMBER; i <= MAX_REGISTER_NUMBER; i++) {
64
+ for (let i = MIN_REGISTERS; i <= MAX_REGISTERS; i++) {
63
65
  if (registers[`R${i}` as keyof NonMandatoryRegisters]) {
64
- if (i === MIN_REGISTER_NUMBER) {
66
+ if (i === MIN_REGISTERS) {
65
67
  lastValueIndex = i;
66
68
  continue;
67
69
  }
@@ -77,17 +79,72 @@ export function areRegistersDenselyPacked(registers: NonMandatoryRegisters): boo
77
79
  return true;
78
80
  }
79
81
 
80
- type TokenAmount<AmountType> = {
81
- tokenId: TokenId;
82
- amount: AmountType;
82
+ export function utxoFilter<T extends AmountType>(
83
+ utxos: Box<T>[],
84
+ filterParams: UTxOFilterParams<T>
85
+ ) {
86
+ if (isEmpty(filterParams) || isEmpty(utxos)) {
87
+ return utxos;
88
+ }
89
+
90
+ const { by, max } = filterParams;
91
+ let filtered = utxos;
92
+
93
+ if (by) {
94
+ filtered = utxos.filter(by);
95
+ if (isEmpty(filtered)) {
96
+ return filtered;
97
+ }
98
+ }
99
+
100
+ if (!max) {
101
+ return filtered;
102
+ }
103
+
104
+ if (isDefined(max.aggregatedDistinctTokens)) {
105
+ const tokenIds = _getDistinctTokenIds(filtered, max.aggregatedDistinctTokens);
106
+ filtered = filtered.filter(
107
+ (utxo) => isEmpty(utxo.assets) || utxo.assets.every((token) => tokenIds.has(token.tokenId))
108
+ );
109
+ }
110
+
111
+ if (isDefined(max.count) && filtered.length > max.count) {
112
+ filtered = filtered.slice(0, max.count);
113
+ }
114
+
115
+ return filtered;
116
+ }
117
+
118
+ function _getDistinctTokenIds(utxos: Box<AmountType>[], max: number): Set<string> {
119
+ const tokenIds = new Set<string>();
120
+
121
+ for (let i = 0; i < utxos.length && tokenIds.size < max; i++) {
122
+ if (isEmpty(utxos[i].assets) || utxos[i].assets.length > max) {
123
+ continue;
124
+ }
125
+
126
+ for (const token of utxos[i].assets) {
127
+ tokenIds.add(token.tokenId);
128
+ }
129
+ }
130
+
131
+ return tokenIds;
132
+ }
133
+
134
+ export type UTxOFilterParams<T extends AmountType> = {
135
+ by?: (utxo: Box<T>) => boolean;
136
+ max?: {
137
+ count?: number;
138
+ aggregatedDistinctTokens?: number;
139
+ };
83
140
  };
84
141
 
85
- export type BoxAmounts = {
142
+ export type BoxSummary = {
86
143
  nanoErgs: bigint;
87
144
  tokens: TokenAmount<bigint>[];
88
145
  };
89
146
 
90
- type MinimalBoxAmounts = readonly {
147
+ export type MinimalBoxAmounts = readonly {
91
148
  value: Amount;
92
149
  assets: TokenAmount<Amount>[];
93
150
  }[];