@fleet-sdk/common 0.1.0-alpha.29 → 0.1.0-alpha.31

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
  });
@@ -99,7 +101,7 @@ export function areEqual<T>(array1: ArrayLike<T>, array2: ArrayLike<T>): boolean
99
101
  export function areEqualBy<T>(
100
102
  array1: ArrayLike<T>,
101
103
  array2: ArrayLike<T>,
102
- selector: (item: T) => T[keyof T]
104
+ selector: ObjectSelector<T>
103
105
  ): boolean {
104
106
  if (array1 === array2) {
105
107
  return true;
@@ -155,3 +157,35 @@ export function endsWith<T>(array: ArrayLike<T>, target: ArrayLike<T>): boolean
155
157
 
156
158
  return true;
157
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,20 +32,31 @@ 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
+ export function utxoDiff(
43
+ minuend: BoxSummary | Box<Amount>[],
44
+ subtrahend: BoxSummary | Box<Amount>[]
45
+ ): BoxSummary {
46
+ if (Array.isArray(minuend)) {
47
+ minuend = utxoSum(minuend);
48
+ }
49
+
50
+ if (Array.isArray(subtrahend)) {
51
+ subtrahend = utxoSum(subtrahend);
52
+ }
53
+
42
54
  const tokens: TokenAmount<bigint>[] = [];
43
- const nanoErgs = amountsA.nanoErgs - amountsB.nanoErgs;
55
+ const nanoErgs = minuend.nanoErgs - subtrahend.nanoErgs;
44
56
 
45
- for (const token of amountsA.tokens) {
57
+ for (const token of minuend.tokens) {
46
58
  const balance =
47
- token.amount - (amountsB.tokens.find((t) => t.tokenId === token.tokenId)?.amount || _0n);
59
+ token.amount - (subtrahend.tokens.find((t) => t.tokenId === token.tokenId)?.amount || _0n);
48
60
 
49
61
  if (balance !== _0n) {
50
62
  tokens.push({ tokenId: token.tokenId, amount: balance });
@@ -54,40 +66,96 @@ export function utxoSumResultDiff(amountsA: BoxAmounts, amountsB: BoxAmounts): B
54
66
  return { nanoErgs, tokens };
55
67
  }
56
68
 
57
- const MIN_REGISTER_NUMBER = 4;
58
- const MAX_REGISTER_NUMBER = 9;
69
+ const MIN_NON_MANDATORY_REGISTER_INDEX = 4;
70
+ const MAX_NON_MANDATORY_REGISTER_INDEX = 9;
59
71
 
60
72
  export function areRegistersDenselyPacked(registers: NonMandatoryRegisters): boolean {
61
- let lastValueIndex = 0;
62
- for (let i = MIN_REGISTER_NUMBER; i <= MAX_REGISTER_NUMBER; i++) {
63
- if (registers[`R${i}` as keyof NonMandatoryRegisters]) {
64
- if (i === MIN_REGISTER_NUMBER) {
65
- lastValueIndex = i;
73
+ let lastIndex = 0;
74
+ for (let i = MIN_NON_MANDATORY_REGISTER_INDEX; i <= MAX_NON_MANDATORY_REGISTER_INDEX; i++) {
75
+ const key = `R${i}` as keyof NonMandatoryRegisters;
76
+ if (registers[key]) {
77
+ if (i === MIN_NON_MANDATORY_REGISTER_INDEX) {
78
+ lastIndex = i;
66
79
  continue;
67
80
  }
68
81
 
69
- if (i - lastValueIndex > 1) {
82
+ if (i - lastIndex > 1) {
70
83
  return false;
71
84
  }
72
85
 
73
- lastValueIndex = i;
86
+ lastIndex = i;
74
87
  }
75
88
  }
76
89
 
77
90
  return true;
78
91
  }
79
92
 
80
- type TokenAmount<AmountType> = {
81
- tokenId: TokenId;
82
- amount: AmountType;
93
+ export function utxoFilter<T extends AmountType>(
94
+ utxos: Box<T>[],
95
+ filterParams: UTxOFilterParams<T>
96
+ ) {
97
+ if (isEmpty(filterParams) || isEmpty(utxos)) {
98
+ return utxos;
99
+ }
100
+
101
+ const { by, max } = filterParams;
102
+ let filtered = utxos;
103
+
104
+ if (by) {
105
+ filtered = utxos.filter(by);
106
+ if (isEmpty(filtered)) {
107
+ return filtered;
108
+ }
109
+ }
110
+
111
+ if (!max) {
112
+ return filtered;
113
+ }
114
+
115
+ if (isDefined(max.aggregatedDistinctTokens)) {
116
+ const tokenIds = _getDistinctTokenIds(filtered, max.aggregatedDistinctTokens);
117
+ filtered = filtered.filter(
118
+ (utxo) => isEmpty(utxo.assets) || utxo.assets.every((token) => tokenIds.has(token.tokenId))
119
+ );
120
+ }
121
+
122
+ if (isDefined(max.count) && filtered.length > max.count) {
123
+ filtered = filtered.slice(0, max.count);
124
+ }
125
+
126
+ return filtered;
127
+ }
128
+
129
+ function _getDistinctTokenIds(utxos: Box<AmountType>[], max: number): Set<string> {
130
+ const tokenIds = new Set<string>();
131
+
132
+ for (let i = 0; i < utxos.length && tokenIds.size < max; i++) {
133
+ if (isEmpty(utxos[i].assets) || utxos[i].assets.length > max) {
134
+ continue;
135
+ }
136
+
137
+ for (const token of utxos[i].assets) {
138
+ tokenIds.add(token.tokenId);
139
+ }
140
+ }
141
+
142
+ return tokenIds;
143
+ }
144
+
145
+ export type UTxOFilterParams<T extends AmountType> = {
146
+ by?: (utxo: Box<T>) => boolean;
147
+ max?: {
148
+ count?: number;
149
+ aggregatedDistinctTokens?: number;
150
+ };
83
151
  };
84
152
 
85
- export type BoxAmounts = {
153
+ export type BoxSummary = {
86
154
  nanoErgs: bigint;
87
155
  tokens: TokenAmount<bigint>[];
88
156
  };
89
157
 
90
- type MinimalBoxAmounts = readonly {
158
+ export type MinimalBoxAmounts = readonly {
91
159
  value: Amount;
92
160
  assets: TokenAmount<Amount>[];
93
161
  }[];