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