ecash-agora 4.1.0 → 4.2.0
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/README.md +4 -0
- package/dist/agora.d.ts +4 -3
- package/dist/agora.d.ts.map +1 -1
- package/dist/agora.js +9 -24
- package/dist/agora.js.map +1 -1
- package/dist/broadcast.d.ts +19 -0
- package/dist/broadcast.d.ts.map +1 -0
- package/dist/broadcast.js +18 -0
- package/dist/broadcast.js.map +1 -0
- package/dist/oneshot.d.ts +2 -1
- package/dist/oneshot.d.ts.map +1 -1
- package/dist/oneshot.js +2 -1
- package/dist/oneshot.js.map +1 -1
- package/dist/partial.d.ts +2 -1
- package/dist/partial.d.ts.map +1 -1
- package/dist/partial.js +3 -2
- package/dist/partial.js.map +1 -1
- package/package.json +4 -4
- package/tests/finalization.test.ts +870 -0
|
@@ -0,0 +1,870 @@
|
|
|
1
|
+
// Copyright (c) 2026 The Bitcoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
import { expect, use } from 'chai';
|
|
6
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
7
|
+
import { ChronikClient } from 'chronik-client';
|
|
8
|
+
import {
|
|
9
|
+
ALP_TOKEN_TYPE_STANDARD,
|
|
10
|
+
DEFAULT_DUST_SATS,
|
|
11
|
+
Ecc,
|
|
12
|
+
SLP_NFT1_CHILD,
|
|
13
|
+
SLP_TOKEN_TYPE_FUNGIBLE,
|
|
14
|
+
SLP_TOKEN_TYPE_NFT1_CHILD,
|
|
15
|
+
SLP_TOKEN_TYPE_NFT1_GROUP,
|
|
16
|
+
Script,
|
|
17
|
+
fromHex,
|
|
18
|
+
payment,
|
|
19
|
+
shaRmd160,
|
|
20
|
+
slpSend,
|
|
21
|
+
} from 'ecash-lib';
|
|
22
|
+
import { TestRunner } from 'ecash-lib/dist/test/testRunner.js';
|
|
23
|
+
import { Wallet } from 'ecash-wallet';
|
|
24
|
+
|
|
25
|
+
import { Agora } from '../src/agora.js';
|
|
26
|
+
import { AgoraOneshot } from '../src/oneshot.js';
|
|
27
|
+
|
|
28
|
+
use(chaiAsPromised);
|
|
29
|
+
|
|
30
|
+
const NUM_COINS = 500;
|
|
31
|
+
const COIN_VALUE = 1100000000n;
|
|
32
|
+
|
|
33
|
+
const FINALIZATION_TIMEOUT_ERROR =
|
|
34
|
+
'Error: Failed getting /broadcast-txs: 504: Transaction(s) failed to finalize within 1s';
|
|
35
|
+
|
|
36
|
+
const expectFinalizationTimedOut = async (
|
|
37
|
+
chronikClient: ChronikClient,
|
|
38
|
+
result: {
|
|
39
|
+
success: boolean;
|
|
40
|
+
broadcasted: string[];
|
|
41
|
+
unbroadcasted?: string[];
|
|
42
|
+
errors?: string[];
|
|
43
|
+
},
|
|
44
|
+
broadcastedCount: number,
|
|
45
|
+
) => {
|
|
46
|
+
expect(result.success).to.equal(false);
|
|
47
|
+
expect(result.broadcasted.length).to.equal(broadcastedCount);
|
|
48
|
+
expect(result.unbroadcasted).to.deep.equal([]);
|
|
49
|
+
expect(result.errors).to.deep.equal([FINALIZATION_TIMEOUT_ERROR]);
|
|
50
|
+
for (const txid of result.broadcasted) {
|
|
51
|
+
const tx = await chronikClient.tx(txid);
|
|
52
|
+
expect(tx.isFinal).to.equal(false);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
describe('finalizationTimeoutSecs', () => {
|
|
57
|
+
let runner: TestRunner;
|
|
58
|
+
let chronik: ChronikClient;
|
|
59
|
+
const ecc = new Ecc();
|
|
60
|
+
|
|
61
|
+
before(async () => {
|
|
62
|
+
runner = await TestRunner.setup('setup_scripts/ecash-agora_base');
|
|
63
|
+
chronik = runner.chronik;
|
|
64
|
+
await runner.setupCoins(NUM_COINS, COIN_VALUE);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
after(() => {
|
|
68
|
+
runner.stop();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('AgoraPartial.list() returns expected error when the ALP listing tx does not finalize within 1s', async () => {
|
|
72
|
+
const agora = new Agora(chronik);
|
|
73
|
+
|
|
74
|
+
const makerSk = fromHex('f6'.repeat(32));
|
|
75
|
+
const makerWallet = Wallet.fromSk(makerSk, chronik);
|
|
76
|
+
const makerPk = ecc.derivePubkey(makerSk);
|
|
77
|
+
const makerScript = Script.p2pkh(shaRmd160(makerPk));
|
|
78
|
+
|
|
79
|
+
await runner.sendToScript(50000n, makerScript);
|
|
80
|
+
await makerWallet.sync();
|
|
81
|
+
|
|
82
|
+
const genesisResult = await makerWallet
|
|
83
|
+
.action({
|
|
84
|
+
outputs: [
|
|
85
|
+
{ sats: 0n },
|
|
86
|
+
{
|
|
87
|
+
sats: 546n,
|
|
88
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
89
|
+
script: makerWallet.script,
|
|
90
|
+
atoms: 1000000n,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
sats: DEFAULT_DUST_SATS,
|
|
94
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
95
|
+
script: makerWallet.script,
|
|
96
|
+
isMintBaton: true,
|
|
97
|
+
atoms: 0n,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
tokenActions: [
|
|
101
|
+
{
|
|
102
|
+
type: 'GENESIS',
|
|
103
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD,
|
|
104
|
+
genesisInfo: {
|
|
105
|
+
tokenTicker: 'FIN ALP',
|
|
106
|
+
decimals: 4,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
})
|
|
111
|
+
.build()
|
|
112
|
+
.broadcast();
|
|
113
|
+
const tokenId = genesisResult.broadcasted[0];
|
|
114
|
+
await makerWallet.sync();
|
|
115
|
+
|
|
116
|
+
const agoraPartial = await agora.selectParams(
|
|
117
|
+
{
|
|
118
|
+
offeredAtoms: 100000n,
|
|
119
|
+
priceNanoSatsPerAtom: 1_000_000_000n,
|
|
120
|
+
minAcceptedAtoms: 546n,
|
|
121
|
+
makerPk,
|
|
122
|
+
tokenId,
|
|
123
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD.number,
|
|
124
|
+
tokenProtocol: 'ALP',
|
|
125
|
+
},
|
|
126
|
+
64n,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const listResult = await agoraPartial.list({
|
|
130
|
+
wallet: makerWallet,
|
|
131
|
+
finalizationTimeoutSecs: 1,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await expectFinalizationTimedOut(chronik, listResult, 1);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('AgoraPartial.list() returns expected error when SLP listing txs do not finalize within 1s', async () => {
|
|
138
|
+
const agora = new Agora(chronik);
|
|
139
|
+
|
|
140
|
+
const makerSk = fromHex('f7'.repeat(32));
|
|
141
|
+
const makerWallet = Wallet.fromSk(makerSk, chronik);
|
|
142
|
+
const makerPk = ecc.derivePubkey(makerSk);
|
|
143
|
+
const makerScript = Script.p2pkh(shaRmd160(makerPk));
|
|
144
|
+
|
|
145
|
+
await runner.sendToScript(50000n, makerScript);
|
|
146
|
+
await makerWallet.sync();
|
|
147
|
+
|
|
148
|
+
const genesisResult = await makerWallet
|
|
149
|
+
.action({
|
|
150
|
+
outputs: [
|
|
151
|
+
{ sats: 0n },
|
|
152
|
+
{
|
|
153
|
+
sats: 20000n,
|
|
154
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
155
|
+
script: makerWallet.script,
|
|
156
|
+
atoms: 1000000n,
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
tokenActions: [
|
|
160
|
+
{
|
|
161
|
+
type: 'GENESIS',
|
|
162
|
+
tokenType: SLP_TOKEN_TYPE_FUNGIBLE,
|
|
163
|
+
genesisInfo: {
|
|
164
|
+
tokenTicker: 'FIN SLP',
|
|
165
|
+
decimals: 4,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
})
|
|
170
|
+
.build()
|
|
171
|
+
.broadcast();
|
|
172
|
+
const tokenId = genesisResult.broadcasted[0];
|
|
173
|
+
await makerWallet.sync();
|
|
174
|
+
|
|
175
|
+
const agoraPartial = await agora.selectParams(
|
|
176
|
+
{
|
|
177
|
+
offeredAtoms: 100000n,
|
|
178
|
+
priceNanoSatsPerAtom: 1_000_000_000n,
|
|
179
|
+
minAcceptedAtoms: 546n,
|
|
180
|
+
makerPk,
|
|
181
|
+
tokenId,
|
|
182
|
+
tokenType: SLP_TOKEN_TYPE_FUNGIBLE.number,
|
|
183
|
+
tokenProtocol: 'SLP',
|
|
184
|
+
},
|
|
185
|
+
64n,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
const listResult = await agoraPartial.list({
|
|
189
|
+
wallet: makerWallet,
|
|
190
|
+
finalizationTimeoutSecs: 1,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
await expectFinalizationTimedOut(chronik, listResult, 2);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('AgoraOneshot.list() returns expected error when listing txs do not finalize within 1s', async () => {
|
|
197
|
+
const sellerSk = fromHex('f8'.repeat(32));
|
|
198
|
+
const sellerWallet = Wallet.fromSk(sellerSk, chronik);
|
|
199
|
+
const sellerPk = ecc.derivePubkey(sellerSk);
|
|
200
|
+
const sellerP2pkh = Script.p2pkh(shaRmd160(sellerPk));
|
|
201
|
+
|
|
202
|
+
await runner.sendToScript(50000n, sellerP2pkh);
|
|
203
|
+
await sellerWallet.sync();
|
|
204
|
+
|
|
205
|
+
const groupGenesisResult = await sellerWallet
|
|
206
|
+
.action({
|
|
207
|
+
outputs: [
|
|
208
|
+
{ sats: 0n },
|
|
209
|
+
{
|
|
210
|
+
sats: 10000n,
|
|
211
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
212
|
+
script: sellerWallet.script,
|
|
213
|
+
atoms: 1n,
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
tokenActions: [
|
|
217
|
+
{
|
|
218
|
+
type: 'GENESIS',
|
|
219
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_GROUP,
|
|
220
|
+
genesisInfo: {
|
|
221
|
+
tokenTicker: 'FIN GRP',
|
|
222
|
+
decimals: 0,
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
})
|
|
227
|
+
.build()
|
|
228
|
+
.broadcast();
|
|
229
|
+
const groupTokenId = groupGenesisResult.broadcasted[0];
|
|
230
|
+
|
|
231
|
+
await sellerWallet.sync();
|
|
232
|
+
const childGenesisResult = await sellerWallet
|
|
233
|
+
.action({
|
|
234
|
+
outputs: [
|
|
235
|
+
{ sats: 0n },
|
|
236
|
+
{
|
|
237
|
+
sats: 8000n,
|
|
238
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
239
|
+
script: sellerWallet.script,
|
|
240
|
+
atoms: 1n,
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
tokenActions: [
|
|
244
|
+
{
|
|
245
|
+
type: 'GENESIS',
|
|
246
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_CHILD,
|
|
247
|
+
genesisInfo: {
|
|
248
|
+
tokenTicker: 'FIN NFT',
|
|
249
|
+
decimals: 0,
|
|
250
|
+
},
|
|
251
|
+
groupTokenId,
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
})
|
|
255
|
+
.build()
|
|
256
|
+
.broadcast();
|
|
257
|
+
const childTokenId = childGenesisResult.broadcasted[0];
|
|
258
|
+
await sellerWallet.sync();
|
|
259
|
+
|
|
260
|
+
const agoraOneshot = new AgoraOneshot({
|
|
261
|
+
enforcedOutputs: [
|
|
262
|
+
{
|
|
263
|
+
sats: 0n,
|
|
264
|
+
script: slpSend(childTokenId, SLP_NFT1_CHILD, [0n, 1n]),
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
sats: 30000n,
|
|
268
|
+
script: sellerP2pkh,
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
cancelPk: sellerPk,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const listResult = await agoraOneshot.list({
|
|
275
|
+
wallet: sellerWallet,
|
|
276
|
+
tokenId: childTokenId,
|
|
277
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_CHILD,
|
|
278
|
+
finalizationTimeoutSecs: 1,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
await expectFinalizationTimedOut(chronik, listResult, 2);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('AgoraOffer.take() returns expected error when ALP partial accept tx does not finalize within 1s', async () => {
|
|
285
|
+
const agora = new Agora(chronik);
|
|
286
|
+
|
|
287
|
+
const makerSk = fromHex('a1'.repeat(32));
|
|
288
|
+
const makerWallet = Wallet.fromSk(makerSk, chronik);
|
|
289
|
+
const makerPk = ecc.derivePubkey(makerSk);
|
|
290
|
+
const makerScript = Script.p2pkh(shaRmd160(makerPk));
|
|
291
|
+
|
|
292
|
+
const takerSk = fromHex('a2'.repeat(32));
|
|
293
|
+
const takerWallet = Wallet.fromSk(takerSk, chronik);
|
|
294
|
+
const takerPk = ecc.derivePubkey(takerSk);
|
|
295
|
+
|
|
296
|
+
await runner.sendToScript(50000n, makerScript);
|
|
297
|
+
await makerWallet.sync();
|
|
298
|
+
|
|
299
|
+
const genesisResult = await makerWallet
|
|
300
|
+
.action({
|
|
301
|
+
outputs: [
|
|
302
|
+
{ sats: 0n },
|
|
303
|
+
{
|
|
304
|
+
sats: 546n,
|
|
305
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
306
|
+
script: makerWallet.script,
|
|
307
|
+
atoms: 1000000n,
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
sats: DEFAULT_DUST_SATS,
|
|
311
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
312
|
+
script: makerWallet.script,
|
|
313
|
+
isMintBaton: true,
|
|
314
|
+
atoms: 0n,
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
tokenActions: [
|
|
318
|
+
{
|
|
319
|
+
type: 'GENESIS',
|
|
320
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD,
|
|
321
|
+
genesisInfo: {
|
|
322
|
+
tokenTicker: 'FIN TAKE ALP',
|
|
323
|
+
decimals: 4,
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
})
|
|
328
|
+
.build()
|
|
329
|
+
.broadcast();
|
|
330
|
+
const tokenId = genesisResult.broadcasted[0];
|
|
331
|
+
await makerWallet.sync();
|
|
332
|
+
|
|
333
|
+
const agoraPartial = await agora.selectParams(
|
|
334
|
+
{
|
|
335
|
+
offeredAtoms: 100000n,
|
|
336
|
+
priceNanoSatsPerAtom: 1_000_000_000n,
|
|
337
|
+
minAcceptedAtoms: 546n,
|
|
338
|
+
makerPk,
|
|
339
|
+
tokenId,
|
|
340
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD.number,
|
|
341
|
+
tokenProtocol: 'ALP',
|
|
342
|
+
},
|
|
343
|
+
64n,
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
const listResult = await agoraPartial.list({ wallet: makerWallet });
|
|
347
|
+
expect(listResult.success).to.equal(true);
|
|
348
|
+
|
|
349
|
+
const offers = await agora.activeOffersByTokenId(tokenId);
|
|
350
|
+
expect(offers.length).to.equal(1);
|
|
351
|
+
|
|
352
|
+
await runner.sendToScript(200000n, Script.p2pkh(shaRmd160(takerPk)));
|
|
353
|
+
await takerWallet.sync();
|
|
354
|
+
|
|
355
|
+
const takeResult = await offers[0].take({
|
|
356
|
+
wallet: takerWallet,
|
|
357
|
+
covenantSk: takerSk,
|
|
358
|
+
covenantPk: takerPk,
|
|
359
|
+
acceptedAtoms: 50000n,
|
|
360
|
+
finalizationTimeoutSecs: 1,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
await expectFinalizationTimedOut(chronik, takeResult, 1);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('AgoraOffer.take() returns expected error when SLP partial accept tx does not finalize within 1s', async () => {
|
|
367
|
+
const agora = new Agora(chronik);
|
|
368
|
+
|
|
369
|
+
const makerSk = fromHex('a3'.repeat(32));
|
|
370
|
+
const makerWallet = Wallet.fromSk(makerSk, chronik);
|
|
371
|
+
const makerPk = ecc.derivePubkey(makerSk);
|
|
372
|
+
const makerScript = Script.p2pkh(shaRmd160(makerPk));
|
|
373
|
+
|
|
374
|
+
const takerSk = fromHex('a4'.repeat(32));
|
|
375
|
+
const takerWallet = Wallet.fromSk(takerSk, chronik);
|
|
376
|
+
const takerPk = ecc.derivePubkey(takerSk);
|
|
377
|
+
|
|
378
|
+
await runner.sendToScript(50000n, makerScript);
|
|
379
|
+
await makerWallet.sync();
|
|
380
|
+
|
|
381
|
+
const genesisResult = await makerWallet
|
|
382
|
+
.action({
|
|
383
|
+
outputs: [
|
|
384
|
+
{ sats: 0n },
|
|
385
|
+
{
|
|
386
|
+
sats: 20000n,
|
|
387
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
388
|
+
script: makerWallet.script,
|
|
389
|
+
atoms: 1000000n,
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
tokenActions: [
|
|
393
|
+
{
|
|
394
|
+
type: 'GENESIS',
|
|
395
|
+
tokenType: SLP_TOKEN_TYPE_FUNGIBLE,
|
|
396
|
+
genesisInfo: {
|
|
397
|
+
tokenTicker: 'FIN TAKE SLP',
|
|
398
|
+
decimals: 4,
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
],
|
|
402
|
+
})
|
|
403
|
+
.build()
|
|
404
|
+
.broadcast();
|
|
405
|
+
const tokenId = genesisResult.broadcasted[0];
|
|
406
|
+
await makerWallet.sync();
|
|
407
|
+
|
|
408
|
+
const agoraPartial = await agora.selectParams(
|
|
409
|
+
{
|
|
410
|
+
offeredAtoms: 100000n,
|
|
411
|
+
priceNanoSatsPerAtom: 1_000_000_000n,
|
|
412
|
+
minAcceptedAtoms: 546n,
|
|
413
|
+
makerPk,
|
|
414
|
+
tokenId,
|
|
415
|
+
tokenType: SLP_TOKEN_TYPE_FUNGIBLE.number,
|
|
416
|
+
tokenProtocol: 'SLP',
|
|
417
|
+
},
|
|
418
|
+
64n,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
const listResult = await agoraPartial.list({ wallet: makerWallet });
|
|
422
|
+
expect(listResult.success).to.equal(true);
|
|
423
|
+
|
|
424
|
+
const offers = await agora.activeOffersByTokenId(tokenId);
|
|
425
|
+
expect(offers.length).to.equal(1);
|
|
426
|
+
|
|
427
|
+
await runner.sendToScript(200000n, Script.p2pkh(shaRmd160(takerPk)));
|
|
428
|
+
await takerWallet.sync();
|
|
429
|
+
|
|
430
|
+
const takeResult = await offers[0].take({
|
|
431
|
+
wallet: takerWallet,
|
|
432
|
+
covenantSk: takerSk,
|
|
433
|
+
covenantPk: takerPk,
|
|
434
|
+
acceptedAtoms: 50000n,
|
|
435
|
+
finalizationTimeoutSecs: 1,
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
await expectFinalizationTimedOut(chronik, takeResult, 1);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('AgoraOffer.take() returns expected error when oneshot accept tx does not finalize within 1s', async () => {
|
|
442
|
+
const agora = new Agora(chronik);
|
|
443
|
+
|
|
444
|
+
const sellerSk = fromHex('a5'.repeat(32));
|
|
445
|
+
const sellerWallet = Wallet.fromSk(sellerSk, chronik);
|
|
446
|
+
const sellerPk = ecc.derivePubkey(sellerSk);
|
|
447
|
+
const sellerP2pkh = Script.p2pkh(shaRmd160(sellerPk));
|
|
448
|
+
|
|
449
|
+
const buyerSk = fromHex('a6'.repeat(32));
|
|
450
|
+
const buyerWallet = Wallet.fromSk(buyerSk, chronik);
|
|
451
|
+
const buyerPk = ecc.derivePubkey(buyerSk);
|
|
452
|
+
|
|
453
|
+
await runner.sendToScript(50000n, sellerP2pkh);
|
|
454
|
+
await sellerWallet.sync();
|
|
455
|
+
|
|
456
|
+
const groupGenesisResult = await sellerWallet
|
|
457
|
+
.action({
|
|
458
|
+
outputs: [
|
|
459
|
+
{ sats: 0n },
|
|
460
|
+
{
|
|
461
|
+
sats: 10000n,
|
|
462
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
463
|
+
script: sellerWallet.script,
|
|
464
|
+
atoms: 1n,
|
|
465
|
+
},
|
|
466
|
+
],
|
|
467
|
+
tokenActions: [
|
|
468
|
+
{
|
|
469
|
+
type: 'GENESIS',
|
|
470
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_GROUP,
|
|
471
|
+
genesisInfo: {
|
|
472
|
+
tokenTicker: 'FIN TAKE GRP',
|
|
473
|
+
decimals: 0,
|
|
474
|
+
},
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
})
|
|
478
|
+
.build()
|
|
479
|
+
.broadcast();
|
|
480
|
+
const groupTokenId = groupGenesisResult.broadcasted[0];
|
|
481
|
+
|
|
482
|
+
await sellerWallet.sync();
|
|
483
|
+
const childGenesisResult = await sellerWallet
|
|
484
|
+
.action({
|
|
485
|
+
outputs: [
|
|
486
|
+
{ sats: 0n },
|
|
487
|
+
{
|
|
488
|
+
sats: 8000n,
|
|
489
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
490
|
+
script: sellerWallet.script,
|
|
491
|
+
atoms: 1n,
|
|
492
|
+
},
|
|
493
|
+
],
|
|
494
|
+
tokenActions: [
|
|
495
|
+
{
|
|
496
|
+
type: 'GENESIS',
|
|
497
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_CHILD,
|
|
498
|
+
genesisInfo: {
|
|
499
|
+
tokenTicker: 'FIN TAKE NFT',
|
|
500
|
+
decimals: 0,
|
|
501
|
+
},
|
|
502
|
+
groupTokenId,
|
|
503
|
+
},
|
|
504
|
+
],
|
|
505
|
+
})
|
|
506
|
+
.build()
|
|
507
|
+
.broadcast();
|
|
508
|
+
const childTokenId = childGenesisResult.broadcasted[0];
|
|
509
|
+
await sellerWallet.sync();
|
|
510
|
+
|
|
511
|
+
const agoraOneshot = new AgoraOneshot({
|
|
512
|
+
enforcedOutputs: [
|
|
513
|
+
{
|
|
514
|
+
sats: 0n,
|
|
515
|
+
script: slpSend(childTokenId, SLP_NFT1_CHILD, [0n, 1n]),
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
sats: 30000n,
|
|
519
|
+
script: sellerP2pkh,
|
|
520
|
+
},
|
|
521
|
+
],
|
|
522
|
+
cancelPk: sellerPk,
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
const listResult = await agoraOneshot.list({
|
|
526
|
+
wallet: sellerWallet,
|
|
527
|
+
tokenId: childTokenId,
|
|
528
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_CHILD,
|
|
529
|
+
});
|
|
530
|
+
expect(listResult.success).to.equal(true);
|
|
531
|
+
|
|
532
|
+
const offers = await agora.activeOffersByTokenId(childTokenId);
|
|
533
|
+
expect(offers.length).to.equal(1);
|
|
534
|
+
|
|
535
|
+
await runner.sendToScript(50000n, Script.p2pkh(shaRmd160(buyerPk)));
|
|
536
|
+
await buyerWallet.sync();
|
|
537
|
+
|
|
538
|
+
const takeResult = await offers[0].take({
|
|
539
|
+
wallet: buyerWallet,
|
|
540
|
+
covenantSk: buyerSk,
|
|
541
|
+
covenantPk: buyerPk,
|
|
542
|
+
finalizationTimeoutSecs: 1,
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
await expectFinalizationTimedOut(chronik, takeResult, 1);
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it('AgoraOffer.cancel() returns expected error when ALP partial cancel tx does not finalize within 1s', async () => {
|
|
549
|
+
const agora = new Agora(chronik);
|
|
550
|
+
|
|
551
|
+
const makerSk = fromHex('b1'.repeat(32));
|
|
552
|
+
const makerWallet = Wallet.fromSk(makerSk, chronik);
|
|
553
|
+
const makerPk = ecc.derivePubkey(makerSk);
|
|
554
|
+
const makerScript = Script.p2pkh(shaRmd160(makerPk));
|
|
555
|
+
|
|
556
|
+
await runner.sendToScript(100000n, makerScript);
|
|
557
|
+
await makerWallet.sync();
|
|
558
|
+
|
|
559
|
+
const genesisResult = await makerWallet
|
|
560
|
+
.action({
|
|
561
|
+
outputs: [
|
|
562
|
+
{ sats: 0n },
|
|
563
|
+
{
|
|
564
|
+
sats: 546n,
|
|
565
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
566
|
+
script: makerWallet.script,
|
|
567
|
+
atoms: 1000000n,
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
sats: DEFAULT_DUST_SATS,
|
|
571
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
572
|
+
script: makerWallet.script,
|
|
573
|
+
isMintBaton: true,
|
|
574
|
+
atoms: 0n,
|
|
575
|
+
},
|
|
576
|
+
],
|
|
577
|
+
tokenActions: [
|
|
578
|
+
{
|
|
579
|
+
type: 'GENESIS',
|
|
580
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD,
|
|
581
|
+
genesisInfo: {
|
|
582
|
+
tokenTicker: 'FIN CAN ALP',
|
|
583
|
+
decimals: 4,
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
],
|
|
587
|
+
})
|
|
588
|
+
.build()
|
|
589
|
+
.broadcast();
|
|
590
|
+
const tokenId = genesisResult.broadcasted[0];
|
|
591
|
+
await makerWallet.sync();
|
|
592
|
+
|
|
593
|
+
const agoraPartial = await agora.selectParams(
|
|
594
|
+
{
|
|
595
|
+
offeredAtoms: 100000n,
|
|
596
|
+
priceNanoSatsPerAtom: 1_000_000_000n,
|
|
597
|
+
minAcceptedAtoms: 546n,
|
|
598
|
+
makerPk,
|
|
599
|
+
tokenId,
|
|
600
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD.number,
|
|
601
|
+
tokenProtocol: 'ALP',
|
|
602
|
+
},
|
|
603
|
+
64n,
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
const listResult = await agoraPartial.list({ wallet: makerWallet });
|
|
607
|
+
expect(listResult.success).to.equal(true);
|
|
608
|
+
|
|
609
|
+
const offers = await agora.activeOffersByTokenId(tokenId);
|
|
610
|
+
expect(offers.length).to.equal(1);
|
|
611
|
+
|
|
612
|
+
const cancelResult = await offers[0].cancel({
|
|
613
|
+
wallet: makerWallet,
|
|
614
|
+
finalizationTimeoutSecs: 1,
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
await expectFinalizationTimedOut(chronik, cancelResult, 1);
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
it('AgoraOffer.cancel() returns expected error when SLP partial cancel tx does not finalize within 1s', async () => {
|
|
621
|
+
const agora = new Agora(chronik);
|
|
622
|
+
|
|
623
|
+
const makerSk = fromHex('b2'.repeat(32));
|
|
624
|
+
const makerWallet = Wallet.fromSk(makerSk, chronik);
|
|
625
|
+
const makerPk = ecc.derivePubkey(makerSk);
|
|
626
|
+
const makerScript = Script.p2pkh(shaRmd160(makerPk));
|
|
627
|
+
|
|
628
|
+
await runner.sendToScript(100000n, makerScript);
|
|
629
|
+
await makerWallet.sync();
|
|
630
|
+
|
|
631
|
+
const genesisResult = await makerWallet
|
|
632
|
+
.action({
|
|
633
|
+
outputs: [
|
|
634
|
+
{ sats: 0n },
|
|
635
|
+
{
|
|
636
|
+
sats: 20000n,
|
|
637
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
638
|
+
script: makerWallet.script,
|
|
639
|
+
atoms: 1000000n,
|
|
640
|
+
},
|
|
641
|
+
],
|
|
642
|
+
tokenActions: [
|
|
643
|
+
{
|
|
644
|
+
type: 'GENESIS',
|
|
645
|
+
tokenType: SLP_TOKEN_TYPE_FUNGIBLE,
|
|
646
|
+
genesisInfo: {
|
|
647
|
+
tokenTicker: 'FIN CAN SLP',
|
|
648
|
+
decimals: 4,
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
],
|
|
652
|
+
})
|
|
653
|
+
.build()
|
|
654
|
+
.broadcast();
|
|
655
|
+
const tokenId = genesisResult.broadcasted[0];
|
|
656
|
+
await makerWallet.sync();
|
|
657
|
+
|
|
658
|
+
const agoraPartial = await agora.selectParams(
|
|
659
|
+
{
|
|
660
|
+
offeredAtoms: 100000n,
|
|
661
|
+
priceNanoSatsPerAtom: 1_000_000_000n,
|
|
662
|
+
minAcceptedAtoms: 546n,
|
|
663
|
+
makerPk,
|
|
664
|
+
tokenId,
|
|
665
|
+
tokenType: SLP_TOKEN_TYPE_FUNGIBLE.number,
|
|
666
|
+
tokenProtocol: 'SLP',
|
|
667
|
+
},
|
|
668
|
+
64n,
|
|
669
|
+
);
|
|
670
|
+
|
|
671
|
+
const listResult = await agoraPartial.list({ wallet: makerWallet });
|
|
672
|
+
expect(listResult.success).to.equal(true);
|
|
673
|
+
|
|
674
|
+
const offers = await agora.activeOffersByTokenId(tokenId);
|
|
675
|
+
expect(offers.length).to.equal(1);
|
|
676
|
+
|
|
677
|
+
const cancelResult = await offers[0].cancel({
|
|
678
|
+
wallet: makerWallet,
|
|
679
|
+
finalizationTimeoutSecs: 1,
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
await expectFinalizationTimedOut(chronik, cancelResult, 1);
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
it('AgoraOffer.cancel() returns expected error when oneshot cancel tx does not finalize within 1s', async () => {
|
|
686
|
+
const agora = new Agora(chronik);
|
|
687
|
+
|
|
688
|
+
const sellerSk = fromHex('b3'.repeat(32));
|
|
689
|
+
const sellerWallet = Wallet.fromSk(sellerSk, chronik);
|
|
690
|
+
const sellerPk = ecc.derivePubkey(sellerSk);
|
|
691
|
+
const sellerP2pkh = Script.p2pkh(shaRmd160(sellerPk));
|
|
692
|
+
|
|
693
|
+
await runner.sendToScript(100000n, sellerP2pkh);
|
|
694
|
+
await sellerWallet.sync();
|
|
695
|
+
|
|
696
|
+
const groupGenesisResult = await sellerWallet
|
|
697
|
+
.action({
|
|
698
|
+
outputs: [
|
|
699
|
+
{ sats: 0n },
|
|
700
|
+
{
|
|
701
|
+
sats: 10000n,
|
|
702
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
703
|
+
script: sellerWallet.script,
|
|
704
|
+
atoms: 1n,
|
|
705
|
+
},
|
|
706
|
+
],
|
|
707
|
+
tokenActions: [
|
|
708
|
+
{
|
|
709
|
+
type: 'GENESIS',
|
|
710
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_GROUP,
|
|
711
|
+
genesisInfo: {
|
|
712
|
+
tokenTicker: 'FIN CAN GRP',
|
|
713
|
+
decimals: 0,
|
|
714
|
+
},
|
|
715
|
+
},
|
|
716
|
+
],
|
|
717
|
+
})
|
|
718
|
+
.build()
|
|
719
|
+
.broadcast();
|
|
720
|
+
const groupTokenId = groupGenesisResult.broadcasted[0];
|
|
721
|
+
|
|
722
|
+
await sellerWallet.sync();
|
|
723
|
+
const childGenesisResult = await sellerWallet
|
|
724
|
+
.action({
|
|
725
|
+
outputs: [
|
|
726
|
+
{ sats: 0n },
|
|
727
|
+
{
|
|
728
|
+
sats: 8000n,
|
|
729
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
730
|
+
script: sellerWallet.script,
|
|
731
|
+
atoms: 1n,
|
|
732
|
+
},
|
|
733
|
+
],
|
|
734
|
+
tokenActions: [
|
|
735
|
+
{
|
|
736
|
+
type: 'GENESIS',
|
|
737
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_CHILD,
|
|
738
|
+
genesisInfo: {
|
|
739
|
+
tokenTicker: 'FIN CAN NFT',
|
|
740
|
+
decimals: 0,
|
|
741
|
+
},
|
|
742
|
+
groupTokenId,
|
|
743
|
+
},
|
|
744
|
+
],
|
|
745
|
+
})
|
|
746
|
+
.build()
|
|
747
|
+
.broadcast();
|
|
748
|
+
const childTokenId = childGenesisResult.broadcasted[0];
|
|
749
|
+
await sellerWallet.sync();
|
|
750
|
+
|
|
751
|
+
const agoraOneshot = new AgoraOneshot({
|
|
752
|
+
enforcedOutputs: [
|
|
753
|
+
{
|
|
754
|
+
sats: 0n,
|
|
755
|
+
script: slpSend(childTokenId, SLP_NFT1_CHILD, [0n, 1n]),
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
sats: 30000n,
|
|
759
|
+
script: sellerP2pkh,
|
|
760
|
+
},
|
|
761
|
+
],
|
|
762
|
+
cancelPk: sellerPk,
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
const listResult = await agoraOneshot.list({
|
|
766
|
+
wallet: sellerWallet,
|
|
767
|
+
tokenId: childTokenId,
|
|
768
|
+
tokenType: SLP_TOKEN_TYPE_NFT1_CHILD,
|
|
769
|
+
});
|
|
770
|
+
expect(listResult.success).to.equal(true);
|
|
771
|
+
|
|
772
|
+
const offers = await agora.activeOffersByTokenId(childTokenId);
|
|
773
|
+
expect(offers.length).to.equal(1);
|
|
774
|
+
|
|
775
|
+
const cancelResult = await offers[0].cancel({
|
|
776
|
+
wallet: sellerWallet,
|
|
777
|
+
finalizationTimeoutSecs: 1,
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
await expectFinalizationTimedOut(chronik, cancelResult, 1);
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
it('AgoraOffer.relist() returns expected error when ALP relist tx does not finalize within 1s', async () => {
|
|
784
|
+
const agora = new Agora(chronik);
|
|
785
|
+
|
|
786
|
+
const makerSk = fromHex('c1'.repeat(32));
|
|
787
|
+
const makerWallet = Wallet.fromSk(makerSk, chronik);
|
|
788
|
+
const makerPk = ecc.derivePubkey(makerSk);
|
|
789
|
+
const makerScript = Script.p2pkh(shaRmd160(makerPk));
|
|
790
|
+
|
|
791
|
+
await runner.sendToScript(450000n, makerScript);
|
|
792
|
+
await makerWallet.sync();
|
|
793
|
+
|
|
794
|
+
const genesisResult = await makerWallet
|
|
795
|
+
.action({
|
|
796
|
+
outputs: [
|
|
797
|
+
{ sats: 0n },
|
|
798
|
+
{
|
|
799
|
+
sats: 546n,
|
|
800
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
801
|
+
script: makerWallet.script,
|
|
802
|
+
atoms: 1000000n,
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
sats: DEFAULT_DUST_SATS,
|
|
806
|
+
tokenId: payment.GENESIS_TOKEN_ID_PLACEHOLDER,
|
|
807
|
+
script: makerWallet.script,
|
|
808
|
+
isMintBaton: true,
|
|
809
|
+
atoms: 0n,
|
|
810
|
+
},
|
|
811
|
+
],
|
|
812
|
+
tokenActions: [
|
|
813
|
+
{
|
|
814
|
+
type: 'GENESIS',
|
|
815
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD,
|
|
816
|
+
genesisInfo: {
|
|
817
|
+
tokenTicker: 'FIN RELIST',
|
|
818
|
+
decimals: 4,
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
],
|
|
822
|
+
})
|
|
823
|
+
.build()
|
|
824
|
+
.broadcast();
|
|
825
|
+
const tokenId = genesisResult.broadcasted[0];
|
|
826
|
+
await makerWallet.sync();
|
|
827
|
+
|
|
828
|
+
const agoraPartialInitial = await agora.selectParams(
|
|
829
|
+
{
|
|
830
|
+
offeredAtoms: 100000n,
|
|
831
|
+
priceNanoSatsPerAtom: 1_000_000_000n,
|
|
832
|
+
minAcceptedAtoms: 546n,
|
|
833
|
+
makerPk,
|
|
834
|
+
tokenId,
|
|
835
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD.number,
|
|
836
|
+
tokenProtocol: 'ALP',
|
|
837
|
+
},
|
|
838
|
+
64n,
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
const listResult = await agoraPartialInitial.list({
|
|
842
|
+
wallet: makerWallet,
|
|
843
|
+
});
|
|
844
|
+
expect(listResult.success).to.equal(true);
|
|
845
|
+
|
|
846
|
+
const offers = await agora.activeOffersByTokenId(tokenId);
|
|
847
|
+
expect(offers.length).to.equal(1);
|
|
848
|
+
|
|
849
|
+
const updatedPartial = await agora.selectParams(
|
|
850
|
+
{
|
|
851
|
+
offeredAtoms: 80000n,
|
|
852
|
+
priceNanoSatsPerAtom: 2_000_000_000n,
|
|
853
|
+
minAcceptedAtoms: 546n,
|
|
854
|
+
makerPk,
|
|
855
|
+
tokenId,
|
|
856
|
+
tokenType: ALP_TOKEN_TYPE_STANDARD.number,
|
|
857
|
+
tokenProtocol: 'ALP',
|
|
858
|
+
},
|
|
859
|
+
64n,
|
|
860
|
+
);
|
|
861
|
+
|
|
862
|
+
const relistResult = await offers[0].relist({
|
|
863
|
+
wallet: makerWallet,
|
|
864
|
+
updatedPartial,
|
|
865
|
+
finalizationTimeoutSecs: 1,
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
await expectFinalizationTimedOut(chronik, relistResult, 1);
|
|
869
|
+
});
|
|
870
|
+
});
|