@buildonspark/issuer-sdk 0.1.13 → 0.1.15
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.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @buildonspark/issuer-sdk
|
|
2
2
|
|
|
3
|
+
## 0.1.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- - Upgrade `@noble/curves` minimum version to `^1.9.7`
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @buildonspark/spark-sdk@0.6.5
|
|
10
|
+
|
|
11
|
+
## 0.1.14
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies
|
|
16
|
+
- @buildonspark/spark-sdk@0.6.4
|
|
17
|
+
|
|
3
18
|
## 0.1.13
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buildonspark/issuer-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "Spark Issuer SDK for token issuance",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -82,8 +82,8 @@
|
|
|
82
82
|
"types": "tsc"
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"@buildonspark/spark-sdk": "0.6.
|
|
86
|
-
"@noble/curves": "^1.
|
|
85
|
+
"@buildonspark/spark-sdk": "0.6.5",
|
|
86
|
+
"@noble/curves": "^1.9.7",
|
|
87
87
|
"@scure/btc-signer": "^1.5.0",
|
|
88
88
|
"buffer": "^6.0.3"
|
|
89
89
|
},
|
|
@@ -284,6 +284,11 @@ describe.each(TEST_CONFIGS)(
|
|
|
284
284
|
expect(unfreezeResponse.impactedTokenOutputs.length).toBeGreaterThan(0);
|
|
285
285
|
expect(unfreezeResponse.impactedTokenAmount).toEqual(TOKEN_AMOUNT);
|
|
286
286
|
|
|
287
|
+
// Wait for local token output lock from before to expire before spending once-frozen outputs.
|
|
288
|
+
await new Promise((resolve) =>
|
|
289
|
+
setTimeout(resolve, config.tokenOutputLockExpiryMs),
|
|
290
|
+
);
|
|
291
|
+
|
|
287
292
|
// Outputs unfrozen, transfer should succeed
|
|
288
293
|
const transferBackToIssuerOfOnceFrozenToken =
|
|
289
294
|
await receiverWallet.transferTokens({
|
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
import { ConfigOptions, WalletConfig } from "@buildonspark/spark-sdk";
|
|
2
2
|
|
|
3
|
+
const SHORTER_TOKEN_OUTPUT_LOCK_EXPIRY_MS = 5000;
|
|
4
|
+
|
|
3
5
|
export const TOKENS_SCHNORR_V2_CONFIG: Required<ConfigOptions> = {
|
|
4
6
|
...WalletConfig.LOCAL,
|
|
5
7
|
tokenSignatures: "SCHNORR",
|
|
8
|
+
tokenOutputLockExpiryMs: SHORTER_TOKEN_OUTPUT_LOCK_EXPIRY_MS,
|
|
6
9
|
tokenTransactionVersion: "V2",
|
|
7
10
|
};
|
|
8
11
|
|
|
9
12
|
export const TOKENS_SCHNORR_V3_CONFIG: Required<ConfigOptions> = {
|
|
10
13
|
...WalletConfig.LOCAL,
|
|
11
14
|
tokenSignatures: "SCHNORR",
|
|
15
|
+
tokenOutputLockExpiryMs: SHORTER_TOKEN_OUTPUT_LOCK_EXPIRY_MS,
|
|
12
16
|
tokenTransactionVersion: "V3",
|
|
13
17
|
};
|
|
14
18
|
|
|
15
19
|
export const TOKENS_ECDSA_V2_CONFIG: Required<ConfigOptions> = {
|
|
16
20
|
...WalletConfig.LOCAL,
|
|
17
21
|
tokenSignatures: "ECDSA",
|
|
22
|
+
tokenOutputLockExpiryMs: SHORTER_TOKEN_OUTPUT_LOCK_EXPIRY_MS,
|
|
18
23
|
tokenTransactionVersion: "V2",
|
|
19
24
|
};
|
|
20
25
|
|
|
21
26
|
export const TOKENS_ECDSA_V3_CONFIG: Required<ConfigOptions> = {
|
|
22
27
|
...WalletConfig.LOCAL,
|
|
23
28
|
tokenSignatures: "ECDSA",
|
|
29
|
+
tokenOutputLockExpiryMs: SHORTER_TOKEN_OUTPUT_LOCK_EXPIRY_MS,
|
|
24
30
|
tokenTransactionVersion: "V3",
|
|
25
31
|
};
|
|
26
32
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
decodeBech32mTokenIdentifier,
|
|
3
|
+
filterTokenBalanceForTokenIdentifier,
|
|
4
|
+
} from "@buildonspark/spark-sdk";
|
|
5
|
+
import { OutputWithPreviousTransactionData } from "@buildonspark/spark-sdk/proto/spark_token";
|
|
2
6
|
import { jest } from "@jest/globals";
|
|
3
7
|
import { IssuerSparkWalletTesting } from "../utils/issuer-test-wallet.js";
|
|
4
8
|
import { SparkWalletTesting } from "@buildonspark/spark-sdk/test-utils";
|
|
@@ -44,7 +48,7 @@ describe.each(TEST_CONFIGS)(
|
|
|
44
48
|
expect(userBalance.ownedBalance).toBeGreaterThanOrEqual(tokenAmount);
|
|
45
49
|
});
|
|
46
50
|
|
|
47
|
-
it("should create, mint, and
|
|
51
|
+
it("should create, mint, and batch transfer tokens", async () => {
|
|
48
52
|
const tokenAmount: bigint = 999n;
|
|
49
53
|
|
|
50
54
|
const { wallet: issuerWallet } =
|
|
@@ -59,11 +63,10 @@ describe.each(TEST_CONFIGS)(
|
|
|
59
63
|
maxSupply: 1_000_000n,
|
|
60
64
|
});
|
|
61
65
|
|
|
62
|
-
const { wallet:
|
|
63
|
-
{
|
|
66
|
+
const { wallet: destinationWallet1 } =
|
|
67
|
+
await SparkWalletTesting.initialize({
|
|
64
68
|
options: config,
|
|
65
|
-
}
|
|
66
|
-
);
|
|
69
|
+
});
|
|
67
70
|
|
|
68
71
|
const { wallet: destinationWallet2 } =
|
|
69
72
|
await SparkWalletTesting.initialize({
|
|
@@ -87,7 +90,7 @@ describe.each(TEST_CONFIGS)(
|
|
|
87
90
|
{
|
|
88
91
|
tokenAmount: tokenAmount / 3n,
|
|
89
92
|
tokenIdentifier,
|
|
90
|
-
receiverSparkAddress: await
|
|
93
|
+
receiverSparkAddress: await destinationWallet1.getSparkAddress(),
|
|
91
94
|
},
|
|
92
95
|
{
|
|
93
96
|
tokenAmount: tokenAmount / 3n,
|
|
@@ -105,7 +108,7 @@ describe.each(TEST_CONFIGS)(
|
|
|
105
108
|
.balance;
|
|
106
109
|
expect(sourceBalanceAfter).toEqual(sourceBalanceBefore - tokenAmount);
|
|
107
110
|
|
|
108
|
-
const balanceObj = await
|
|
111
|
+
const balanceObj = await destinationWallet1.getBalance();
|
|
109
112
|
const destinationBalance = filterTokenBalanceForTokenIdentifier(
|
|
110
113
|
balanceObj?.tokenBalances,
|
|
111
114
|
tokenIdentifier!,
|
|
@@ -124,5 +127,580 @@ describe.each(TEST_CONFIGS)(
|
|
|
124
127
|
);
|
|
125
128
|
expect(destinationBalance3.ownedBalance).toEqual(tokenAmount / 3n);
|
|
126
129
|
});
|
|
130
|
+
|
|
131
|
+
it("should create, mint, and batch transfer multiple token types", async () => {
|
|
132
|
+
const tokenConfigs = [
|
|
133
|
+
{ amount: 1000n, ticker: "MTT1" },
|
|
134
|
+
{ amount: 2000n, ticker: "MTT2" },
|
|
135
|
+
{ amount: 1500n, ticker: "MTT3" },
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
const issuerWallets = await Promise.all(
|
|
139
|
+
tokenConfigs.map(async ({ ticker }) => {
|
|
140
|
+
const { wallet } = await IssuerSparkWalletTesting.initialize({
|
|
141
|
+
options: config,
|
|
142
|
+
});
|
|
143
|
+
await wallet.createToken({
|
|
144
|
+
tokenName: `${name}${ticker}`,
|
|
145
|
+
tokenTicker: ticker,
|
|
146
|
+
decimals: 0,
|
|
147
|
+
isFreezable: false,
|
|
148
|
+
maxSupply: 10_000_000n,
|
|
149
|
+
});
|
|
150
|
+
return wallet;
|
|
151
|
+
}),
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const { wallet: intermediateWallet } =
|
|
155
|
+
await SparkWalletTesting.initialize({
|
|
156
|
+
options: config,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const destinationWallets = await Promise.all(
|
|
160
|
+
tokenConfigs.map(() =>
|
|
161
|
+
SparkWalletTesting.initialize({ options: config }).then(
|
|
162
|
+
(r) => r.wallet,
|
|
163
|
+
),
|
|
164
|
+
),
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
await Promise.all(
|
|
168
|
+
issuerWallets.map((wallet, i) =>
|
|
169
|
+
wallet.mintTokens(tokenConfigs[i].amount),
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const tokenIdentifiers = await Promise.all(
|
|
174
|
+
issuerWallets.map(async (wallet) => {
|
|
175
|
+
const balance = await wallet.getIssuerTokenBalance();
|
|
176
|
+
expect(balance.tokenIdentifier).toBeDefined();
|
|
177
|
+
return balance.tokenIdentifier!;
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const intermediateAddress = await intermediateWallet.getSparkAddress();
|
|
182
|
+
for (let i = 0; i < issuerWallets.length; i++) {
|
|
183
|
+
await issuerWallets[i].transferTokens({
|
|
184
|
+
tokenAmount: tokenConfigs[i].amount,
|
|
185
|
+
tokenIdentifier: tokenIdentifiers[i],
|
|
186
|
+
receiverSparkAddress: intermediateAddress,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const intermediateBalanceObj = await intermediateWallet.getBalance();
|
|
191
|
+
for (let i = 0; i < tokenConfigs.length; i++) {
|
|
192
|
+
const balance = filterTokenBalanceForTokenIdentifier(
|
|
193
|
+
intermediateBalanceObj?.tokenBalances,
|
|
194
|
+
tokenIdentifiers[i],
|
|
195
|
+
);
|
|
196
|
+
expect(balance.ownedBalance).toEqual(tokenConfigs[i].amount);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
await intermediateWallet.batchTransferTokens(
|
|
200
|
+
await Promise.all(
|
|
201
|
+
tokenConfigs.map(async ({ amount }, i) => ({
|
|
202
|
+
tokenAmount: amount,
|
|
203
|
+
tokenIdentifier: tokenIdentifiers[i],
|
|
204
|
+
receiverSparkAddress: await destinationWallets[i].getSparkAddress(),
|
|
205
|
+
})),
|
|
206
|
+
),
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
for (let i = 0; i < destinationWallets.length; i++) {
|
|
210
|
+
const balanceObj = await destinationWallets[i].getBalance();
|
|
211
|
+
const balance = filterTokenBalanceForTokenIdentifier(
|
|
212
|
+
balanceObj?.tokenBalances,
|
|
213
|
+
tokenIdentifiers[i],
|
|
214
|
+
);
|
|
215
|
+
expect(balance.ownedBalance).toEqual(tokenConfigs[i].amount);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const finalIntermediateBalanceObj = await intermediateWallet.getBalance();
|
|
219
|
+
for (const tokenIdentifier of tokenIdentifiers) {
|
|
220
|
+
const balance = filterTokenBalanceForTokenIdentifier(
|
|
221
|
+
finalIntermediateBalanceObj?.tokenBalances,
|
|
222
|
+
tokenIdentifier,
|
|
223
|
+
);
|
|
224
|
+
expect(balance.ownedBalance).toEqual(0n);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("should fail when transferring more tokens than available balance", async () => {
|
|
229
|
+
const mintAmount: bigint = 1000n;
|
|
230
|
+
const transferAmount: bigint = 2000n;
|
|
231
|
+
|
|
232
|
+
const { wallet: issuerWallet } =
|
|
233
|
+
await IssuerSparkWalletTesting.initialize({
|
|
234
|
+
options: config,
|
|
235
|
+
});
|
|
236
|
+
await issuerWallet.createToken({
|
|
237
|
+
tokenName: `${name}INS`,
|
|
238
|
+
tokenTicker: "INS",
|
|
239
|
+
decimals: 0,
|
|
240
|
+
isFreezable: false,
|
|
241
|
+
maxSupply: 10_000_000n,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const { wallet: destinationWallet1 } =
|
|
245
|
+
await SparkWalletTesting.initialize({
|
|
246
|
+
options: config,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
await issuerWallet.mintTokens(mintAmount);
|
|
250
|
+
const tokenIdentifier = await issuerWallet.getIssuerTokenIdentifier();
|
|
251
|
+
|
|
252
|
+
await expect(
|
|
253
|
+
issuerWallet.transferTokens({
|
|
254
|
+
tokenAmount: transferAmount,
|
|
255
|
+
tokenIdentifier: tokenIdentifier!,
|
|
256
|
+
receiverSparkAddress: await destinationWallet1.getSparkAddress(),
|
|
257
|
+
}),
|
|
258
|
+
).rejects.toThrow(/Insufficient token amount/);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("should fail when batch transferring more tokens than available balance", async () => {
|
|
262
|
+
const mintAmount: bigint = 1000n;
|
|
263
|
+
const transferAmount: bigint = 2000n;
|
|
264
|
+
|
|
265
|
+
const { wallet: issuerWallet } =
|
|
266
|
+
await IssuerSparkWalletTesting.initialize({
|
|
267
|
+
options: config,
|
|
268
|
+
});
|
|
269
|
+
await issuerWallet.createToken({
|
|
270
|
+
tokenName: `${name}INS`,
|
|
271
|
+
tokenTicker: "INS",
|
|
272
|
+
decimals: 0,
|
|
273
|
+
isFreezable: false,
|
|
274
|
+
maxSupply: 10_000_000n,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const { wallet: destinationWallet1 } =
|
|
278
|
+
await SparkWalletTesting.initialize({
|
|
279
|
+
options: config,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const { wallet: destinationWallet2 } =
|
|
283
|
+
await SparkWalletTesting.initialize({
|
|
284
|
+
options: config,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
await issuerWallet.mintTokens(mintAmount);
|
|
288
|
+
const tokenIdentifier = await issuerWallet.getIssuerTokenIdentifier();
|
|
289
|
+
|
|
290
|
+
await expect(
|
|
291
|
+
issuerWallet.batchTransferTokens([
|
|
292
|
+
{
|
|
293
|
+
tokenAmount: transferAmount,
|
|
294
|
+
tokenIdentifier: tokenIdentifier!,
|
|
295
|
+
receiverSparkAddress: await destinationWallet1.getSparkAddress(),
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
tokenAmount: transferAmount,
|
|
299
|
+
tokenIdentifier: tokenIdentifier!,
|
|
300
|
+
receiverSparkAddress: await destinationWallet2.getSparkAddress(),
|
|
301
|
+
},
|
|
302
|
+
]),
|
|
303
|
+
).rejects.toThrow(/Insufficient token amount/);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
it("should fail batch transfer when one token type has insufficient balance", async () => {
|
|
307
|
+
const tokenAmount1: bigint = 1000n;
|
|
308
|
+
const tokenAmount2: bigint = 500n;
|
|
309
|
+
|
|
310
|
+
const { wallet: issuerWallet1 } =
|
|
311
|
+
await IssuerSparkWalletTesting.initialize({
|
|
312
|
+
options: config,
|
|
313
|
+
});
|
|
314
|
+
await issuerWallet1.createToken({
|
|
315
|
+
tokenName: `${name}BF1`,
|
|
316
|
+
tokenTicker: "BF1",
|
|
317
|
+
decimals: 0,
|
|
318
|
+
isFreezable: false,
|
|
319
|
+
maxSupply: 10_000_000n,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const { wallet: issuerWallet2 } =
|
|
323
|
+
await IssuerSparkWalletTesting.initialize({
|
|
324
|
+
options: config,
|
|
325
|
+
});
|
|
326
|
+
await issuerWallet2.createToken({
|
|
327
|
+
tokenName: `${name}BF2`,
|
|
328
|
+
tokenTicker: "BF2",
|
|
329
|
+
decimals: 0,
|
|
330
|
+
isFreezable: false,
|
|
331
|
+
maxSupply: 10_000_000n,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const { wallet: intermediateWallet } =
|
|
335
|
+
await SparkWalletTesting.initialize({
|
|
336
|
+
options: config,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
const { wallet: destinationWallet1 } =
|
|
340
|
+
await SparkWalletTesting.initialize({
|
|
341
|
+
options: config,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const { wallet: destinationWallet2 } =
|
|
345
|
+
await SparkWalletTesting.initialize({
|
|
346
|
+
options: config,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
await issuerWallet1.mintTokens(tokenAmount1);
|
|
350
|
+
await issuerWallet2.mintTokens(tokenAmount2);
|
|
351
|
+
|
|
352
|
+
const issuerBalance1 = await issuerWallet1.getIssuerTokenBalance();
|
|
353
|
+
const issuerBalance2 = await issuerWallet2.getIssuerTokenBalance();
|
|
354
|
+
|
|
355
|
+
const tokenIdentifier1 = issuerBalance1.tokenIdentifier!;
|
|
356
|
+
const tokenIdentifier2 = issuerBalance2.tokenIdentifier!;
|
|
357
|
+
|
|
358
|
+
await issuerWallet1.transferTokens({
|
|
359
|
+
tokenAmount: tokenAmount1,
|
|
360
|
+
tokenIdentifier: tokenIdentifier1,
|
|
361
|
+
receiverSparkAddress: await intermediateWallet.getSparkAddress(),
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
await issuerWallet2.transferTokens({
|
|
365
|
+
tokenAmount: tokenAmount2,
|
|
366
|
+
tokenIdentifier: tokenIdentifier2,
|
|
367
|
+
receiverSparkAddress: await intermediateWallet.getSparkAddress(),
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
await expect(
|
|
371
|
+
intermediateWallet.batchTransferTokens([
|
|
372
|
+
{
|
|
373
|
+
tokenAmount: tokenAmount1,
|
|
374
|
+
tokenIdentifier: tokenIdentifier1,
|
|
375
|
+
receiverSparkAddress: await destinationWallet1.getSparkAddress(),
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
tokenAmount: tokenAmount2 * 2n,
|
|
379
|
+
tokenIdentifier: tokenIdentifier2,
|
|
380
|
+
receiverSparkAddress: await destinationWallet2.getSparkAddress(),
|
|
381
|
+
},
|
|
382
|
+
]),
|
|
383
|
+
).rejects.toThrow(/Insufficient token amount/);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("should fail batch transfer when requesting token type wallet doesn't own", async () => {
|
|
387
|
+
const tokenAmount: bigint = 1000n;
|
|
388
|
+
|
|
389
|
+
const { wallet: issuerWallet1 } =
|
|
390
|
+
await IssuerSparkWalletTesting.initialize({
|
|
391
|
+
options: config,
|
|
392
|
+
});
|
|
393
|
+
await issuerWallet1.createToken({
|
|
394
|
+
tokenName: `${name}OWN`,
|
|
395
|
+
tokenTicker: "OWN",
|
|
396
|
+
decimals: 0,
|
|
397
|
+
isFreezable: false,
|
|
398
|
+
maxSupply: 10_000_000n,
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const { wallet: issuerWallet2 } =
|
|
402
|
+
await IssuerSparkWalletTesting.initialize({
|
|
403
|
+
options: config,
|
|
404
|
+
});
|
|
405
|
+
await issuerWallet2.createToken({
|
|
406
|
+
tokenName: `${name}NWN`,
|
|
407
|
+
tokenTicker: "NWN",
|
|
408
|
+
decimals: 0,
|
|
409
|
+
isFreezable: false,
|
|
410
|
+
maxSupply: 10_000_000n,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const { wallet: intermediateWallet } =
|
|
414
|
+
await SparkWalletTesting.initialize({
|
|
415
|
+
options: config,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const { wallet: destinationWallet } = await SparkWalletTesting.initialize(
|
|
419
|
+
{
|
|
420
|
+
options: config,
|
|
421
|
+
},
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
await issuerWallet1.mintTokens(tokenAmount);
|
|
425
|
+
await issuerWallet2.mintTokens(tokenAmount);
|
|
426
|
+
|
|
427
|
+
const issuerBalance1 = await issuerWallet1.getIssuerTokenBalance();
|
|
428
|
+
const issuerBalance2 = await issuerWallet2.getIssuerTokenBalance();
|
|
429
|
+
|
|
430
|
+
const tokenIdentifier1 = issuerBalance1.tokenIdentifier!;
|
|
431
|
+
const tokenIdentifier2 = issuerBalance2.tokenIdentifier!;
|
|
432
|
+
|
|
433
|
+
await issuerWallet1.transferTokens({
|
|
434
|
+
tokenAmount: tokenAmount,
|
|
435
|
+
tokenIdentifier: tokenIdentifier1,
|
|
436
|
+
receiverSparkAddress: await intermediateWallet.getSparkAddress(),
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
await expect(
|
|
440
|
+
intermediateWallet.batchTransferTokens([
|
|
441
|
+
{
|
|
442
|
+
tokenAmount: tokenAmount,
|
|
443
|
+
tokenIdentifier: tokenIdentifier1,
|
|
444
|
+
receiverSparkAddress: await destinationWallet.getSparkAddress(),
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
tokenAmount: tokenAmount,
|
|
448
|
+
tokenIdentifier: tokenIdentifier2,
|
|
449
|
+
receiverSparkAddress: await destinationWallet.getSparkAddress(),
|
|
450
|
+
},
|
|
451
|
+
]),
|
|
452
|
+
).rejects.toThrow(/Insufficient token amount/);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it("should fail when transfer amounts sum exceeds balance for same token type", async () => {
|
|
456
|
+
const mintAmount: bigint = 1000n;
|
|
457
|
+
|
|
458
|
+
const { wallet: issuerWallet } =
|
|
459
|
+
await IssuerSparkWalletTesting.initialize({
|
|
460
|
+
options: config,
|
|
461
|
+
});
|
|
462
|
+
await issuerWallet.createToken({
|
|
463
|
+
tokenName: `${name}SUM`,
|
|
464
|
+
tokenTicker: "SUM",
|
|
465
|
+
decimals: 0,
|
|
466
|
+
isFreezable: false,
|
|
467
|
+
maxSupply: 10_000_000n,
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const { wallet: destinationWallet1 } =
|
|
471
|
+
await SparkWalletTesting.initialize({
|
|
472
|
+
options: config,
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const { wallet: destinationWallet2 } =
|
|
476
|
+
await SparkWalletTesting.initialize({
|
|
477
|
+
options: config,
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
await issuerWallet.mintTokens(mintAmount);
|
|
481
|
+
const tokenIdentifier = await issuerWallet.getIssuerTokenIdentifier();
|
|
482
|
+
|
|
483
|
+
await expect(
|
|
484
|
+
issuerWallet.batchTransferTokens([
|
|
485
|
+
{
|
|
486
|
+
tokenAmount: 600n,
|
|
487
|
+
tokenIdentifier: tokenIdentifier!,
|
|
488
|
+
receiverSparkAddress: await destinationWallet1.getSparkAddress(),
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
tokenAmount: 600n,
|
|
492
|
+
tokenIdentifier: tokenIdentifier!,
|
|
493
|
+
receiverSparkAddress: await destinationWallet2.getSparkAddress(),
|
|
494
|
+
},
|
|
495
|
+
]),
|
|
496
|
+
).rejects.toThrow(/Insufficient token amount/);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it("should fail when transferring with unavailable selected TTXOs", async () => {
|
|
500
|
+
const mintAmount: bigint = 1000n;
|
|
501
|
+
|
|
502
|
+
const { wallet: issuerWallet } =
|
|
503
|
+
await IssuerSparkWalletTesting.initialize({
|
|
504
|
+
options: config,
|
|
505
|
+
});
|
|
506
|
+
await issuerWallet.createToken({
|
|
507
|
+
tokenName: `${name}UNA`,
|
|
508
|
+
tokenTicker: "UNA",
|
|
509
|
+
decimals: 0,
|
|
510
|
+
isFreezable: false,
|
|
511
|
+
maxSupply: 10_000_000n,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const { wallet: destinationWallet } = await SparkWalletTesting.initialize(
|
|
515
|
+
{
|
|
516
|
+
options: config,
|
|
517
|
+
},
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
await issuerWallet.mintTokens(mintAmount);
|
|
521
|
+
const tokenIdentifier = await issuerWallet.getIssuerTokenIdentifier();
|
|
522
|
+
const { tokenIdentifier: rawTokenIdentifier } =
|
|
523
|
+
decodeBech32mTokenIdentifier(tokenIdentifier!);
|
|
524
|
+
|
|
525
|
+
const fakeOutput: OutputWithPreviousTransactionData = {
|
|
526
|
+
output: {
|
|
527
|
+
id: "non-existent-output-id",
|
|
528
|
+
ownerPublicKey: new Uint8Array(33),
|
|
529
|
+
tokenIdentifier: rawTokenIdentifier,
|
|
530
|
+
tokenAmount: new Uint8Array([
|
|
531
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0xe8,
|
|
532
|
+
]),
|
|
533
|
+
},
|
|
534
|
+
previousTransactionHash: new Uint8Array(32),
|
|
535
|
+
previousTransactionVout: 0,
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
await expect(
|
|
539
|
+
issuerWallet.transferTokens({
|
|
540
|
+
tokenAmount: mintAmount,
|
|
541
|
+
tokenIdentifier: tokenIdentifier!,
|
|
542
|
+
receiverSparkAddress: await destinationWallet.getSparkAddress(),
|
|
543
|
+
selectedOutputs: [fakeOutput],
|
|
544
|
+
}),
|
|
545
|
+
).rejects.toThrow(/Insufficient input amount for token/);
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it("should fail when selected output token type does not match receiver token type", async () => {
|
|
549
|
+
const mintAmount: bigint = 1000n;
|
|
550
|
+
|
|
551
|
+
const { wallet: issuerWallet1 } =
|
|
552
|
+
await IssuerSparkWalletTesting.initialize({
|
|
553
|
+
options: config,
|
|
554
|
+
});
|
|
555
|
+
await issuerWallet1.createToken({
|
|
556
|
+
tokenName: `${name}MIS1`,
|
|
557
|
+
tokenTicker: "MIS1",
|
|
558
|
+
decimals: 0,
|
|
559
|
+
isFreezable: false,
|
|
560
|
+
maxSupply: 10_000_000n,
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
const { wallet: issuerWallet2 } =
|
|
564
|
+
await IssuerSparkWalletTesting.initialize({
|
|
565
|
+
options: config,
|
|
566
|
+
});
|
|
567
|
+
await issuerWallet2.createToken({
|
|
568
|
+
tokenName: `${name}MIS2`,
|
|
569
|
+
tokenTicker: "MIS2",
|
|
570
|
+
decimals: 0,
|
|
571
|
+
isFreezable: false,
|
|
572
|
+
maxSupply: 10_000_000n,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const { wallet: destinationWallet } = await SparkWalletTesting.initialize(
|
|
576
|
+
{
|
|
577
|
+
options: config,
|
|
578
|
+
},
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
await issuerWallet1.mintTokens(mintAmount);
|
|
582
|
+
await issuerWallet2.mintTokens(mintAmount);
|
|
583
|
+
|
|
584
|
+
const tokenIdentifier1 = await issuerWallet1.getIssuerTokenIdentifier();
|
|
585
|
+
const tokenIdentifier2 = await issuerWallet2.getIssuerTokenIdentifier();
|
|
586
|
+
const { tokenIdentifier: rawTokenIdentifier2 } =
|
|
587
|
+
decodeBech32mTokenIdentifier(tokenIdentifier2!);
|
|
588
|
+
|
|
589
|
+
const fakeOutputWithWrongToken: OutputWithPreviousTransactionData = {
|
|
590
|
+
output: {
|
|
591
|
+
id: "fake-output-id",
|
|
592
|
+
ownerPublicKey: new Uint8Array(33),
|
|
593
|
+
tokenIdentifier: rawTokenIdentifier2,
|
|
594
|
+
tokenAmount: new Uint8Array([
|
|
595
|
+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x03, 0xe8,
|
|
596
|
+
]),
|
|
597
|
+
},
|
|
598
|
+
previousTransactionHash: new Uint8Array(32),
|
|
599
|
+
previousTransactionVout: 0,
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
await expect(
|
|
603
|
+
issuerWallet1.transferTokens({
|
|
604
|
+
tokenAmount: mintAmount,
|
|
605
|
+
tokenIdentifier: tokenIdentifier1!,
|
|
606
|
+
receiverSparkAddress: await destinationWallet.getSparkAddress(),
|
|
607
|
+
selectedOutputs: [fakeOutputWithWrongToken],
|
|
608
|
+
}),
|
|
609
|
+
).rejects.toThrow(/Insufficient input amount for token/);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it("should fail batch transfer when one token type is frozen", async () => {
|
|
613
|
+
const tokenAmount: bigint = 1000n;
|
|
614
|
+
|
|
615
|
+
const { wallet: issuerWallet1 } =
|
|
616
|
+
await IssuerSparkWalletTesting.initialize({
|
|
617
|
+
options: config,
|
|
618
|
+
});
|
|
619
|
+
await issuerWallet1.createToken({
|
|
620
|
+
tokenName: `${name}FRZ1`,
|
|
621
|
+
tokenTicker: "FRZ1",
|
|
622
|
+
decimals: 0,
|
|
623
|
+
isFreezable: true,
|
|
624
|
+
maxSupply: 10_000_000n,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
const { wallet: issuerWallet2 } =
|
|
628
|
+
await IssuerSparkWalletTesting.initialize({
|
|
629
|
+
options: config,
|
|
630
|
+
});
|
|
631
|
+
await issuerWallet2.createToken({
|
|
632
|
+
tokenName: `${name}FRZ2`,
|
|
633
|
+
tokenTicker: "FRZ2",
|
|
634
|
+
decimals: 0,
|
|
635
|
+
isFreezable: true,
|
|
636
|
+
maxSupply: 10_000_000n,
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
const { wallet: intermediateWallet } =
|
|
640
|
+
await SparkWalletTesting.initialize({
|
|
641
|
+
options: config,
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
const { wallet: destinationWallet } = await SparkWalletTesting.initialize(
|
|
645
|
+
{
|
|
646
|
+
options: config,
|
|
647
|
+
},
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
await issuerWallet1.mintTokens(tokenAmount);
|
|
651
|
+
await issuerWallet2.mintTokens(tokenAmount);
|
|
652
|
+
|
|
653
|
+
const issuerBalance1 = await issuerWallet1.getIssuerTokenBalance();
|
|
654
|
+
const issuerBalance2 = await issuerWallet2.getIssuerTokenBalance();
|
|
655
|
+
const tokenIdentifier1 = issuerBalance1.tokenIdentifier!;
|
|
656
|
+
const tokenIdentifier2 = issuerBalance2.tokenIdentifier!;
|
|
657
|
+
|
|
658
|
+
const intermediateAddress = await intermediateWallet.getSparkAddress();
|
|
659
|
+
await issuerWallet1.transferTokens({
|
|
660
|
+
tokenAmount,
|
|
661
|
+
tokenIdentifier: tokenIdentifier1,
|
|
662
|
+
receiverSparkAddress: intermediateAddress,
|
|
663
|
+
});
|
|
664
|
+
await issuerWallet2.transferTokens({
|
|
665
|
+
tokenAmount,
|
|
666
|
+
tokenIdentifier: tokenIdentifier2,
|
|
667
|
+
receiverSparkAddress: intermediateAddress,
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
const intermediateBalanceObj = await intermediateWallet.getBalance();
|
|
671
|
+
expect(
|
|
672
|
+
filterTokenBalanceForTokenIdentifier(
|
|
673
|
+
intermediateBalanceObj?.tokenBalances,
|
|
674
|
+
tokenIdentifier1,
|
|
675
|
+
).ownedBalance,
|
|
676
|
+
).toEqual(tokenAmount);
|
|
677
|
+
expect(
|
|
678
|
+
filterTokenBalanceForTokenIdentifier(
|
|
679
|
+
intermediateBalanceObj?.tokenBalances,
|
|
680
|
+
tokenIdentifier2,
|
|
681
|
+
).ownedBalance,
|
|
682
|
+
).toEqual(tokenAmount);
|
|
683
|
+
|
|
684
|
+
await issuerWallet1.freezeTokens({
|
|
685
|
+
tokenIdentifier: tokenIdentifier1,
|
|
686
|
+
sparkAddress: intermediateAddress,
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
const destinationAddress = await destinationWallet.getSparkAddress();
|
|
690
|
+
await expect(
|
|
691
|
+
intermediateWallet.batchTransferTokens([
|
|
692
|
+
{
|
|
693
|
+
tokenAmount,
|
|
694
|
+
tokenIdentifier: tokenIdentifier1,
|
|
695
|
+
receiverSparkAddress: destinationAddress,
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
tokenAmount,
|
|
699
|
+
tokenIdentifier: tokenIdentifier2,
|
|
700
|
+
receiverSparkAddress: destinationAddress,
|
|
701
|
+
},
|
|
702
|
+
]),
|
|
703
|
+
).rejects.toThrow();
|
|
704
|
+
});
|
|
127
705
|
},
|
|
128
706
|
);
|