ecash-agora 0.1.1-rc

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.
@@ -0,0 +1,681 @@
1
+ // Copyright (c) 2024 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
+ ALL_BIP143,
10
+ DEFAULT_DUST_LIMIT,
11
+ Ecc,
12
+ P2PKHSignatory,
13
+ SLP_FUNGIBLE,
14
+ SLP_LOKAD_ID,
15
+ Script,
16
+ TxBuilderInput,
17
+ fromHex,
18
+ initWasm,
19
+ shaRmd160,
20
+ slpSend,
21
+ strToBytes,
22
+ toHex,
23
+ } from 'ecash-lib';
24
+ import { TestRunner } from 'ecash-lib/dist/test/testRunner.js';
25
+
26
+ import { AgoraPartial } from '../src/partial.js';
27
+ import { makeSlpOffer, takeSlpOffer } from './partial-helper-slp.js';
28
+
29
+ use(chaiAsPromised);
30
+
31
+ const BASE_PARAMS_SLP = {
32
+ tokenId: '00'.repeat(32), // filled in later
33
+ tokenType: SLP_FUNGIBLE,
34
+ tokenProtocol: 'SLP' as const,
35
+ dustAmount: DEFAULT_DUST_LIMIT,
36
+ };
37
+
38
+ const BIGSATS = 149 * 5000000000 - 20000;
39
+
40
+ let makerSk: Uint8Array;
41
+ let makerPk: Uint8Array;
42
+ let makerPkh: Uint8Array;
43
+ let makerScript: Script;
44
+ let makerScriptHex: string;
45
+ let takerSk: Uint8Array;
46
+ let takerPk: Uint8Array;
47
+ let takerPkh: Uint8Array;
48
+ let takerScript: Script;
49
+ let takerScriptHex: string;
50
+
51
+ function initKeys(ecc: Ecc) {
52
+ makerSk = fromHex('33'.repeat(32));
53
+ makerPk = ecc.derivePubkey(makerSk);
54
+ makerPkh = shaRmd160(makerPk);
55
+ makerScript = Script.p2pkh(makerPkh);
56
+ makerScriptHex = toHex(makerScript.bytecode);
57
+ takerSk = fromHex('44'.repeat(32));
58
+ takerPk = ecc.derivePubkey(takerSk);
59
+ takerPkh = shaRmd160(takerPk);
60
+ takerScript = Script.p2pkh(takerPkh);
61
+ takerScriptHex = toHex(takerScript.bytecode);
62
+ }
63
+
64
+ async function makeBuilderInputs(
65
+ runner: TestRunner,
66
+ values: number[],
67
+ ): Promise<TxBuilderInput[]> {
68
+ const txid = await runner.sendToScript(values, makerScript);
69
+ return values.map((value, outIdx) => ({
70
+ input: {
71
+ prevOut: {
72
+ txid,
73
+ outIdx,
74
+ },
75
+ signData: {
76
+ value,
77
+ outputScript: makerScript,
78
+ },
79
+ },
80
+ signatory: P2PKHSignatory(makerSk, makerPk, ALL_BIP143),
81
+ }));
82
+ }
83
+
84
+ describe('Agora Partial 7450M XEC vs 2p64-1 full accept', () => {
85
+ let runner: TestRunner;
86
+ let chronik: ChronikClient;
87
+ let ecc: Ecc;
88
+
89
+ before(async () => {
90
+ await initWasm();
91
+ runner = await TestRunner.setup('setup_scripts/ecash-agora_base');
92
+ chronik = runner.chronik;
93
+ ecc = runner.ecc;
94
+ initKeys(ecc);
95
+ await runner.setupCoins(1, BIGSATS + 11000);
96
+ });
97
+
98
+ after(() => {
99
+ runner.stop();
100
+ });
101
+
102
+ it('Agora Partial 7450M XEC vs 2p64-1 full accept', async () => {
103
+ const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
104
+ 10000,
105
+ BIGSATS,
106
+ ]);
107
+
108
+ const agoraPartial = AgoraPartial.approximateParams({
109
+ offeredTokens: 0xffffffffffffffffn,
110
+ priceNanoSatsPerToken: 40n, // scaled to use the XEC
111
+ makerPk: makerPk,
112
+ minAcceptedTokens: 0xffffffffffffn,
113
+ ...BASE_PARAMS_SLP,
114
+ });
115
+
116
+ expect(agoraPartial).to.deep.equal(
117
+ new AgoraPartial({
118
+ truncTokens: 0xffffffn,
119
+ numTokenTruncBytes: 5,
120
+ tokenScaleFactor: 127n,
121
+ scaledTruncTokensPerTruncSat: 189n,
122
+ numSatsTruncBytes: 2,
123
+ makerPk,
124
+ minAcceptedScaledTruncTokens: 32511n,
125
+ ...BASE_PARAMS_SLP,
126
+ scriptLen: 216,
127
+ }),
128
+ );
129
+ expect(agoraPartial.offeredTokens()).to.equal(0xffffff0000000000n);
130
+ expect(agoraPartial.askedSats(0x10000000000n)).to.equal(65536n);
131
+ expect(agoraPartial.priceNanoSatsPerToken(0x10000000000n)).to.equal(
132
+ 59n,
133
+ );
134
+ expect(agoraPartial.askedSats(0xffffff0000000000n)).to.equal(
135
+ 738825273344n,
136
+ );
137
+ expect(
138
+ agoraPartial.priceNanoSatsPerToken(0xffffff0000000000n),
139
+ ).to.equal(40n);
140
+ expect(agoraPartial.priceNanoSatsPerToken()).to.equal(40n);
141
+
142
+ const offer = await makeSlpOffer({
143
+ chronik,
144
+ ecc,
145
+ agoraPartial,
146
+ makerSk,
147
+ fuelInput,
148
+ });
149
+ const acceptTxid = await takeSlpOffer({
150
+ chronik,
151
+ ecc,
152
+ offer,
153
+ takerSk,
154
+ takerInput,
155
+ acceptedTokens: agoraPartial.offeredTokens(),
156
+ });
157
+
158
+ const acceptTx = await chronik.tx(acceptTxid);
159
+ // 0th output is OP_RETURN SLP SEND
160
+ expect(acceptTx.outputs[0].outputScript).to.equal(
161
+ toHex(
162
+ slpSend(agoraPartial.tokenId, agoraPartial.tokenType, [
163
+ 0,
164
+ agoraPartial.offeredTokens(),
165
+ ]).bytecode,
166
+ ),
167
+ );
168
+ expect(acceptTx.outputs[0].value).to.equal(0);
169
+ expect(acceptTx.outputs[0].token).to.equal(undefined);
170
+ // 1st output is sats to maker
171
+ expect(acceptTx.outputs[1].token).to.equal(undefined);
172
+ expect(acceptTx.outputs[1].value).to.equal(738825273344);
173
+ expect(acceptTx.outputs[1].outputScript).to.equal(makerScriptHex);
174
+ // 2nd output is tokens to taker
175
+ expect(acceptTx.outputs[2].token?.amount).to.equal(
176
+ 0xffffff0000000000n.toString(),
177
+ );
178
+ expect(acceptTx.outputs[2].value).to.equal(DEFAULT_DUST_LIMIT);
179
+ expect(acceptTx.outputs[2].outputScript).to.equal(takerScriptHex);
180
+ });
181
+ });
182
+
183
+ describe('Agora Partial 7450M XEC vs 2p64-1 small accept', () => {
184
+ let runner: TestRunner;
185
+ let chronik: ChronikClient;
186
+ let ecc: Ecc;
187
+
188
+ before(async () => {
189
+ await initWasm();
190
+ runner = await TestRunner.setup('setup_scripts/ecash-agora_base');
191
+ chronik = runner.chronik;
192
+ ecc = runner.ecc;
193
+ initKeys(ecc);
194
+ await runner.setupCoins(1, BIGSATS + 11000);
195
+ });
196
+
197
+ after(() => {
198
+ runner.stop();
199
+ });
200
+
201
+ it('Agora Partial 7450M XEC vs 2p64-1 small accept', async () => {
202
+ const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
203
+ 10000,
204
+ BIGSATS,
205
+ ]);
206
+
207
+ const agoraPartial = AgoraPartial.approximateParams({
208
+ offeredTokens: 0xffffffffffffffffn,
209
+ priceNanoSatsPerToken: 500000000n, // scaled to use the XEC
210
+ makerPk,
211
+ minAcceptedTokens: 0xffffffffffn,
212
+ ...BASE_PARAMS_SLP,
213
+ });
214
+ expect(agoraPartial).to.deep.equal(
215
+ new AgoraPartial({
216
+ truncTokens: 0xffffffn,
217
+ numTokenTruncBytes: 5,
218
+ tokenScaleFactor: 128n,
219
+ scaledTruncTokensPerTruncSat: 1n,
220
+ numSatsTruncBytes: 4,
221
+ makerPk,
222
+ minAcceptedScaledTruncTokens: 0x7fn,
223
+ ...BASE_PARAMS_SLP,
224
+ scriptLen: 216,
225
+ }),
226
+ );
227
+ expect(agoraPartial.offeredTokens()).to.equal(0xffffff0000000000n);
228
+ expect(agoraPartial.askedSats(0x10000000000n)).to.equal(549755813888n);
229
+ expect(agoraPartial.priceNanoSatsPerToken(0x10000000000n)).to.equal(
230
+ 500000000n,
231
+ );
232
+ expect(agoraPartial.askedSats(0xffffff0000000000n)).to.equal(
233
+ 9223371487098961920n,
234
+ );
235
+ expect(
236
+ agoraPartial.priceNanoSatsPerToken(0xffffff0000000000n),
237
+ ).to.equal(500000000n);
238
+ expect(agoraPartial.priceNanoSatsPerToken()).to.equal(500000000n);
239
+
240
+ const offer = await makeSlpOffer({
241
+ chronik,
242
+ ecc,
243
+ agoraPartial,
244
+ makerSk,
245
+ fuelInput,
246
+ });
247
+ const acceptedTokens = 0x10000000000n;
248
+ const acceptTxid = await takeSlpOffer({
249
+ chronik,
250
+ ecc,
251
+ offer,
252
+ takerSk,
253
+ takerInput,
254
+ acceptedTokens,
255
+ });
256
+
257
+ const acceptTx = await chronik.tx(acceptTxid);
258
+
259
+ // 0th output is OP_RETURN SLP SEND
260
+ expect(acceptTx.outputs[0].outputScript).to.equal(
261
+ toHex(
262
+ slpSend(agoraPartial.tokenId, agoraPartial.tokenType, [
263
+ 0,
264
+ agoraPartial.offeredTokens() - acceptedTokens,
265
+ acceptedTokens,
266
+ ]).bytecode,
267
+ ),
268
+ );
269
+ expect(acceptTx.outputs[0].value).to.equal(0);
270
+ expect(acceptTx.outputs[0].token).to.equal(undefined);
271
+ // 1st output is sats to maker
272
+ expect(acceptTx.outputs[1].token).to.equal(undefined);
273
+ expect(acceptTx.outputs[1].value).to.equal(549755813888);
274
+ expect(acceptTx.outputs[1].outputScript).to.equal(makerScriptHex);
275
+ // 2nd output is back to the P2SH Script
276
+ expect(acceptTx.outputs[2].token?.amount).to.equal(
277
+ (agoraPartial.offeredTokens() - acceptedTokens).toString(),
278
+ );
279
+ expect(acceptTx.outputs[2].value).to.equal(DEFAULT_DUST_LIMIT);
280
+ expect(acceptTx.outputs[2].outputScript.slice(0, 4)).to.equal('a914');
281
+ // 3rd output is tokens to taker
282
+ expect(acceptTx.outputs[3].token?.amount).to.equal(
283
+ acceptedTokens.toString(),
284
+ );
285
+ expect(acceptTx.outputs[3].value).to.equal(DEFAULT_DUST_LIMIT);
286
+ expect(acceptTx.outputs[3].outputScript).to.equal(takerScriptHex);
287
+ });
288
+ });
289
+
290
+ describe('Agora Partial 7450M XEC vs 2p63-1 full accept', () => {
291
+ let runner: TestRunner;
292
+ let chronik: ChronikClient;
293
+ let ecc: Ecc;
294
+
295
+ before(async () => {
296
+ await initWasm();
297
+ runner = await TestRunner.setup('setup_scripts/ecash-agora_base');
298
+ chronik = runner.chronik;
299
+ ecc = runner.ecc;
300
+ initKeys(ecc);
301
+ await runner.setupCoins(1, BIGSATS + 11000);
302
+ });
303
+
304
+ after(() => {
305
+ runner.stop();
306
+ });
307
+
308
+ it('Agora Partial 7450M XEC vs 2p63-1 full accept', async () => {
309
+ const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
310
+ 10000,
311
+ BIGSATS,
312
+ ]);
313
+
314
+ const agoraPartial = AgoraPartial.approximateParams({
315
+ offeredTokens: 0x7fffffffffffffffn,
316
+ priceNanoSatsPerToken: 80n, // scaled to use the XEC
317
+ makerPk: makerPk,
318
+ minAcceptedTokens: 0xffffffffffffn,
319
+ ...BASE_PARAMS_SLP,
320
+ });
321
+
322
+ expect(agoraPartial).to.deep.equal(
323
+ new AgoraPartial({
324
+ truncTokens: 0x7fffff42n,
325
+ numTokenTruncBytes: 4,
326
+ tokenScaleFactor: 1n,
327
+ scaledTruncTokensPerTruncSat: 190n,
328
+ numSatsTruncBytes: 2,
329
+ makerPk,
330
+ minAcceptedScaledTruncTokens: 0xffffn,
331
+ ...BASE_PARAMS_SLP,
332
+ scriptLen: 206,
333
+ }),
334
+ );
335
+ expect(agoraPartial.offeredTokens()).to.equal(0x7fffff4200000000n);
336
+ expect(agoraPartial.askedSats(0x100000000n)).to.equal(65536n);
337
+ expect(agoraPartial.priceNanoSatsPerToken(0x100000000n)).to.equal(
338
+ 15258n,
339
+ );
340
+ expect(agoraPartial.askedSats(0x7fffff4200000000n)).to.equal(
341
+ 740723589120n,
342
+ );
343
+ expect(
344
+ agoraPartial.priceNanoSatsPerToken(0x7fffff4200000000n),
345
+ ).to.equal(80n);
346
+ expect(agoraPartial.priceNanoSatsPerToken()).to.equal(80n);
347
+
348
+ const offer = await makeSlpOffer({
349
+ chronik,
350
+ ecc,
351
+ agoraPartial,
352
+ makerSk,
353
+ fuelInput,
354
+ });
355
+ const acceptedTokens = agoraPartial.offeredTokens();
356
+ const acceptTxid = await takeSlpOffer({
357
+ chronik,
358
+ ecc,
359
+ offer,
360
+ takerSk,
361
+ takerInput,
362
+ acceptedTokens,
363
+ });
364
+
365
+ const acceptTx = await chronik.tx(acceptTxid);
366
+
367
+ // 0th output is OP_RETURN SLP SEND
368
+ expect(acceptTx.outputs[0].outputScript).to.equal(
369
+ toHex(
370
+ slpSend(agoraPartial.tokenId, agoraPartial.tokenType, [
371
+ 0,
372
+ acceptedTokens,
373
+ ]).bytecode,
374
+ ),
375
+ );
376
+ expect(acceptTx.outputs[0].value).to.equal(0);
377
+ expect(acceptTx.outputs[0].token).to.equal(undefined);
378
+ // 1st output is sats to maker
379
+ expect(acceptTx.outputs[1].token).to.equal(undefined);
380
+ expect(acceptTx.outputs[1].value).to.equal(740723589120);
381
+ expect(acceptTx.outputs[1].outputScript).to.equal(makerScriptHex);
382
+ // 2nd output is tokens to taker
383
+ expect(acceptTx.outputs[2].token?.amount).to.equal(
384
+ acceptedTokens.toString(),
385
+ );
386
+ expect(acceptTx.outputs[2].value).to.equal(DEFAULT_DUST_LIMIT);
387
+ expect(acceptTx.outputs[2].outputScript).to.equal(takerScriptHex);
388
+ });
389
+ });
390
+
391
+ describe('Agora Partial 7450M XEC vs 2p63-1 small accept', () => {
392
+ let runner: TestRunner;
393
+ let chronik: ChronikClient;
394
+ let ecc: Ecc;
395
+
396
+ before(async () => {
397
+ await initWasm();
398
+ runner = await TestRunner.setup('setup_scripts/ecash-agora_base');
399
+ chronik = runner.chronik;
400
+ ecc = runner.ecc;
401
+ initKeys(ecc);
402
+ await runner.setupCoins(1, BIGSATS + 11000);
403
+ });
404
+
405
+ after(() => {
406
+ runner.stop();
407
+ });
408
+
409
+ it('Agora Partial 7450M XEC vs 2p63-1 small accept', async () => {
410
+ const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
411
+ 10000,
412
+ BIGSATS,
413
+ ]);
414
+
415
+ const agoraPartial = AgoraPartial.approximateParams({
416
+ offeredTokens: 0x7fffffffffffffffn,
417
+ priceNanoSatsPerToken: 1000000000n,
418
+ makerPk,
419
+ minAcceptedTokens: 0x100000000n,
420
+ ...BASE_PARAMS_SLP,
421
+ });
422
+ expect(agoraPartial).to.deep.equal(
423
+ new AgoraPartial({
424
+ truncTokens: 0x7fffffffn,
425
+ numTokenTruncBytes: 4,
426
+ tokenScaleFactor: 1n,
427
+ scaledTruncTokensPerTruncSat: 1n,
428
+ numSatsTruncBytes: 4,
429
+ makerPk,
430
+ minAcceptedScaledTruncTokens: 1n,
431
+ ...BASE_PARAMS_SLP,
432
+ scriptLen: 201,
433
+ }),
434
+ );
435
+ expect(agoraPartial.offeredTokens()).to.equal(0x7fffffff00000000n);
436
+ expect(agoraPartial.askedSats(0x100000000n)).to.equal(4294967296n);
437
+ expect(agoraPartial.priceNanoSatsPerToken(0x100000000n)).to.equal(
438
+ 1000000000n,
439
+ );
440
+ expect(agoraPartial.askedSats(0x7fffffff00000000n)).to.equal(
441
+ 9223372032559808512n,
442
+ );
443
+ expect(
444
+ agoraPartial.priceNanoSatsPerToken(0x7fffffff00000000n),
445
+ ).to.equal(1000000000n);
446
+ expect(agoraPartial.priceNanoSatsPerToken()).to.equal(1000000000n);
447
+
448
+ const offer = await makeSlpOffer({
449
+ chronik,
450
+ ecc,
451
+ agoraPartial,
452
+ makerSk,
453
+ fuelInput,
454
+ });
455
+ const acceptedTokens = 0x100000000n;
456
+ const acceptTxid = await takeSlpOffer({
457
+ chronik,
458
+ ecc,
459
+ offer,
460
+ takerSk,
461
+ takerInput,
462
+ acceptedTokens,
463
+ });
464
+
465
+ const acceptTx = await chronik.tx(acceptTxid);
466
+
467
+ // 0th output is OP_RETURN SLP SEND
468
+ expect(acceptTx.outputs[0].outputScript).to.equal(
469
+ toHex(
470
+ slpSend(agoraPartial.tokenId, agoraPartial.tokenType, [
471
+ 0,
472
+ agoraPartial.offeredTokens() - acceptedTokens,
473
+ acceptedTokens,
474
+ ]).bytecode,
475
+ ),
476
+ );
477
+ expect(acceptTx.outputs[0].value).to.equal(0);
478
+ expect(acceptTx.outputs[0].token).to.equal(undefined);
479
+ // 1st output is sats to maker
480
+ expect(acceptTx.outputs[1].token).to.equal(undefined);
481
+ expect(acceptTx.outputs[1].value).to.equal(4294967296);
482
+ expect(acceptTx.outputs[1].outputScript).to.equal(makerScriptHex);
483
+ // 2nd output is back to the P2SH Script
484
+ expect(acceptTx.outputs[2].token?.amount).to.equal(
485
+ (agoraPartial.offeredTokens() - acceptedTokens).toString(),
486
+ );
487
+ expect(acceptTx.outputs[2].value).to.equal(DEFAULT_DUST_LIMIT);
488
+ expect(acceptTx.outputs[2].outputScript.slice(0, 4)).to.equal('a914');
489
+ // 3rd output is tokens to taker
490
+ expect(acceptTx.outputs[3].token?.amount).to.equal(
491
+ acceptedTokens.toString(),
492
+ );
493
+ expect(acceptTx.outputs[3].value).to.equal(DEFAULT_DUST_LIMIT);
494
+ expect(acceptTx.outputs[3].outputScript).to.equal(takerScriptHex);
495
+ });
496
+ });
497
+
498
+ describe('Agora Partial 7450M XEC vs 100 full accept', () => {
499
+ let runner: TestRunner;
500
+ let chronik: ChronikClient;
501
+ let ecc: Ecc;
502
+
503
+ before(async () => {
504
+ await initWasm();
505
+ runner = await TestRunner.setup('setup_scripts/ecash-agora_base');
506
+ chronik = runner.chronik;
507
+ ecc = runner.ecc;
508
+ initKeys(ecc);
509
+ await runner.setupCoins(1, BIGSATS + 11000);
510
+ });
511
+
512
+ after(() => {
513
+ runner.stop();
514
+ });
515
+
516
+ it('Agora Partial 7450M XEC vs 100 full accept', async () => {
517
+ const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
518
+ 10000,
519
+ BIGSATS,
520
+ ]);
521
+
522
+ const agoraPartial = AgoraPartial.approximateParams({
523
+ offeredTokens: 100n,
524
+ priceNanoSatsPerToken: 7123456780n * 1000000000n, // scaled to use the XEC
525
+ makerPk: makerPk,
526
+ minAcceptedTokens: 1n,
527
+ ...BASE_PARAMS_SLP,
528
+ });
529
+ expect(agoraPartial).to.deep.equal(
530
+ new AgoraPartial({
531
+ truncTokens: 100n,
532
+ numTokenTruncBytes: 0,
533
+ tokenScaleFactor: 0x7fff3a28n / 100n,
534
+ scaledTruncTokensPerTruncSat: 50576n,
535
+ numSatsTruncBytes: 3,
536
+ makerPk,
537
+ minAcceptedScaledTruncTokens: 0x7fff3a28n / 100n,
538
+ ...BASE_PARAMS_SLP,
539
+ scriptLen: 214,
540
+ }),
541
+ );
542
+ expect(agoraPartial.offeredTokens()).to.equal(100n);
543
+ expect(agoraPartial.minAcceptedTokens()).to.equal(1n);
544
+ expect(agoraPartial.askedSats(1n)).to.equal(7130316800n);
545
+ expect(agoraPartial.askedSats(2n)).to.equal(7130316800n * 2n);
546
+ expect(agoraPartial.askedSats(3n)).to.equal(7124724394n * 3n + 2n);
547
+ expect(agoraPartial.askedSats(4n)).to.equal(7126122496n * 4n);
548
+ expect(agoraPartial.askedSats(5n)).to.equal(71236059136n / 2n);
549
+ expect(agoraPartial.askedSats(10n)).to.equal(71236059136n);
550
+ expect(agoraPartial.askedSats(100n)).to.equal(712360591360n);
551
+
552
+ const offer = await makeSlpOffer({
553
+ chronik,
554
+ ecc,
555
+ agoraPartial,
556
+ makerSk,
557
+ fuelInput,
558
+ });
559
+ const acceptTxid = await takeSlpOffer({
560
+ chronik,
561
+ ecc,
562
+ offer,
563
+ takerSk,
564
+ takerInput,
565
+ acceptedTokens: 100n,
566
+ });
567
+ const acceptTx = await chronik.tx(acceptTxid);
568
+
569
+ // 0th output is OP_RETURN SLP SEND
570
+ expect(acceptTx.outputs[0].outputScript).to.equal(
571
+ toHex(
572
+ slpSend(agoraPartial.tokenId, agoraPartial.tokenType, [0, 100n])
573
+ .bytecode,
574
+ ),
575
+ );
576
+ expect(acceptTx.outputs[0].value).to.equal(0);
577
+ expect(acceptTx.outputs[0].token).to.equal(undefined);
578
+ // 1st output is sats to maker
579
+ expect(acceptTx.outputs[1].token).to.equal(undefined);
580
+ expect(acceptTx.outputs[1].value).to.equal(712360591360);
581
+ expect(acceptTx.outputs[1].outputScript).to.equal(makerScriptHex);
582
+ // 2nd output is tokens to taker
583
+ expect(acceptTx.outputs[2].token?.amount).to.equal('100');
584
+ expect(acceptTx.outputs[2].value).to.equal(DEFAULT_DUST_LIMIT);
585
+ expect(acceptTx.outputs[2].outputScript).to.equal(takerScriptHex);
586
+ });
587
+ });
588
+
589
+ describe('Agora Partial 7450M XEC vs 100 small accept', () => {
590
+ let runner: TestRunner;
591
+ let chronik: ChronikClient;
592
+ let ecc: Ecc;
593
+
594
+ before(async () => {
595
+ await initWasm();
596
+ runner = await TestRunner.setup('setup_scripts/ecash-agora_base');
597
+ chronik = runner.chronik;
598
+ ecc = runner.ecc;
599
+ initKeys(ecc);
600
+ await runner.setupCoins(1, BIGSATS + 11000);
601
+ });
602
+
603
+ after(() => {
604
+ runner.stop();
605
+ });
606
+
607
+ it('Agora Partial 7450M XEC vs 100 small accept', async () => {
608
+ const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
609
+ 10000,
610
+ BIGSATS,
611
+ ]);
612
+
613
+ const agoraPartial = AgoraPartial.approximateParams({
614
+ offeredTokens: 100n,
615
+ priceNanoSatsPerToken: 712345678000n * 1000000000n, // scaled to use the XEC
616
+ makerPk: makerPk,
617
+ minAcceptedTokens: 1n,
618
+ ...BASE_PARAMS_SLP,
619
+ });
620
+ expect(agoraPartial).to.deep.equal(
621
+ new AgoraPartial({
622
+ truncTokens: 100n,
623
+ numTokenTruncBytes: 0,
624
+ tokenScaleFactor: 0x7ffe05f4n / 100n,
625
+ scaledTruncTokensPerTruncSat: 129471n,
626
+ numSatsTruncBytes: 4,
627
+ makerPk,
628
+ minAcceptedScaledTruncTokens: 0x7ffe05f4n / 100n,
629
+ ...BASE_PARAMS_SLP,
630
+ scriptLen: 215,
631
+ }),
632
+ );
633
+ expect(agoraPartial.offeredTokens()).to.equal(100n);
634
+ expect(agoraPartial.minAcceptedTokens()).to.equal(1n);
635
+ expect(agoraPartial.askedSats(1n)).to.equal(712964571136n);
636
+ expect(agoraPartial.askedSats(10n)).to.equal(7125350744064n);
637
+ expect(agoraPartial.askedSats(100n)).to.equal(71236327571456n);
638
+
639
+ const offer = await makeSlpOffer({
640
+ chronik,
641
+ ecc,
642
+ agoraPartial,
643
+ makerSk,
644
+ fuelInput,
645
+ });
646
+ const acceptTxid = await takeSlpOffer({
647
+ chronik,
648
+ ecc,
649
+ offer,
650
+ takerSk,
651
+ takerInput,
652
+ acceptedTokens: 1n,
653
+ });
654
+ const acceptTx = await chronik.tx(acceptTxid);
655
+
656
+ // 0th output is OP_RETURN SLP SEND
657
+ expect(acceptTx.outputs[0].outputScript).to.equal(
658
+ toHex(
659
+ slpSend(agoraPartial.tokenId, agoraPartial.tokenType, [
660
+ 0,
661
+ 99n,
662
+ 1n,
663
+ ]).bytecode,
664
+ ),
665
+ );
666
+ expect(acceptTx.outputs[0].value).to.equal(0);
667
+ expect(acceptTx.outputs[0].token).to.equal(undefined);
668
+ // 1st output is sats to maker
669
+ expect(acceptTx.outputs[1].token).to.equal(undefined);
670
+ expect(acceptTx.outputs[1].value).to.equal(712964571136);
671
+ expect(acceptTx.outputs[1].outputScript).to.equal(makerScriptHex);
672
+ // 2nd output is back to the P2SH Script
673
+ expect(acceptTx.outputs[2].token?.amount).to.equal('99');
674
+ expect(acceptTx.outputs[2].value).to.equal(DEFAULT_DUST_LIMIT);
675
+ expect(acceptTx.outputs[2].outputScript.slice(0, 4)).to.equal('a914');
676
+ // 3rd output is tokens to taker
677
+ expect(acceptTx.outputs[3].token?.amount).to.equal('1');
678
+ expect(acceptTx.outputs[3].value).to.equal(DEFAULT_DUST_LIMIT);
679
+ expect(acceptTx.outputs[3].outputScript).to.equal(takerScriptHex);
680
+ });
681
+ });