ecash-agora 2.2.0 → 2.5.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.
@@ -32,6 +32,7 @@ import {
32
32
  } from '../src/oneshot.js';
33
33
  import { Agora, AgoraOffer } from '../src/agora.js';
34
34
  import { EventEmitter, once } from 'node:events';
35
+ import { Wallet } from 'ecash-wallet/src/wallet.js';
35
36
 
36
37
  use(chaiAsPromised);
37
38
 
@@ -704,4 +705,293 @@ describe('SLP', () => {
704
705
  numPages: 1,
705
706
  });
706
707
  });
708
+ it('We can cancel an Agora Oneshot offer using cancel()', async () => {
709
+ // Create Agora object to access trades
710
+ const agora = new Agora(chronik);
711
+
712
+ const cancelerSk = fromHex('99'.repeat(32));
713
+ const cancelerWallet = Wallet.fromSk(cancelerSk, chronik);
714
+
715
+ const cancelerPk = cancelerWallet.pk;
716
+
717
+ const cancelerP2pkh = cancelerWallet.script;
718
+
719
+ await runner.sendToScript(50000n, cancelerP2pkh);
720
+
721
+ await cancelerWallet.sync();
722
+
723
+ const utxos = cancelerWallet.utxos;
724
+ expect(utxos.length).to.equal(1);
725
+ const utxo = utxos[0];
726
+
727
+ // 1. Seller creates an NFT1 GROUP token
728
+ const txBuildGenesisGroup = new TxBuilder({
729
+ inputs: [
730
+ {
731
+ input: {
732
+ prevOut: utxo.outpoint,
733
+ signData: {
734
+ sats: utxo.sats,
735
+ outputScript: cancelerP2pkh,
736
+ },
737
+ },
738
+ signatory: P2PKHSignatory(
739
+ cancelerSk,
740
+ cancelerPk,
741
+ ALL_BIP143,
742
+ ),
743
+ },
744
+ ],
745
+ outputs: [
746
+ {
747
+ sats: 0n,
748
+ script: slpGenesis(
749
+ SLP_NFT1_GROUP,
750
+ {
751
+ tokenTicker: 'SLP NFT1 GROUP TOKEN',
752
+ decimals: 4,
753
+ },
754
+ 1n,
755
+ ),
756
+ },
757
+ { sats: 10000n, script: cancelerP2pkh },
758
+ ],
759
+ });
760
+ const genesisTx = txBuildGenesisGroup.sign();
761
+ const genesisTxid = (await chronik.broadcastTx(genesisTx.ser())).txid;
762
+ const groupTokenId = genesisTxid;
763
+
764
+ expect(await chronik.token(genesisTxid)).to.deep.equal({
765
+ tokenId: groupTokenId,
766
+ tokenType: SLP_TOKEN_TYPE_NFT1_GROUP,
767
+ genesisInfo: {
768
+ tokenTicker: 'SLP NFT1 GROUP TOKEN',
769
+ tokenName: '',
770
+ url: '',
771
+ hash: '',
772
+ decimals: 4,
773
+ },
774
+ timeFirstSeen: 1300000000,
775
+ });
776
+
777
+ // 2. Seller creates an NFT1 CHILD token using the group token
778
+ const txBuildGenesisChild = new TxBuilder({
779
+ inputs: [
780
+ {
781
+ input: {
782
+ prevOut: {
783
+ txid: genesisTxid,
784
+ outIdx: 1,
785
+ },
786
+ signData: {
787
+ sats: 10000n,
788
+ outputScript: cancelerP2pkh,
789
+ },
790
+ },
791
+ signatory: P2PKHSignatory(
792
+ cancelerSk,
793
+ cancelerPk,
794
+ ALL_BIP143,
795
+ ),
796
+ },
797
+ ],
798
+ outputs: [
799
+ {
800
+ sats: 0n,
801
+ script: slpGenesis(
802
+ SLP_NFT1_CHILD,
803
+ {
804
+ tokenTicker: 'SLP NFT1 CHILD TOKEN',
805
+ decimals: 0,
806
+ },
807
+ 1n,
808
+ ),
809
+ },
810
+ { sats: 8000n, script: cancelerP2pkh },
811
+ ],
812
+ });
813
+ const genesisChildTx = txBuildGenesisChild.sign();
814
+ const genesisChildTxid = (
815
+ await chronik.broadcastTx(genesisChildTx.ser())
816
+ ).txid;
817
+ const childTokenId = genesisChildTxid;
818
+
819
+ expect(await chronik.token(childTokenId)).to.deep.equal({
820
+ tokenId: childTokenId,
821
+ tokenType: SLP_TOKEN_TYPE_NFT1_CHILD,
822
+ genesisInfo: {
823
+ tokenTicker: 'SLP NFT1 CHILD TOKEN',
824
+ tokenName: '',
825
+ url: '',
826
+ hash: '',
827
+ decimals: 0,
828
+ },
829
+ timeFirstSeen: 1300000000,
830
+ });
831
+
832
+ const emitter = new EventEmitter();
833
+ const ws = chronik.ws({
834
+ onMessage: async msg => {
835
+ if (!emitter.emit('ws', msg)) {
836
+ console.warn('Emitted msg without any listeners', msg);
837
+ }
838
+ },
839
+ });
840
+ await ws.waitForOpen();
841
+ agora.subscribeWs(ws, {
842
+ type: 'TOKEN_ID',
843
+ tokenId: childTokenId,
844
+ });
845
+ const listenNext = () => once(emitter, 'ws') as Promise<[MsgTxClient]>;
846
+
847
+ // 3. Seller sends the NFT to an ad setup output for an Agora Oneshot
848
+ // covenant that asks for 80000 sats
849
+ const enforcedOutputs: TxOutput[] = [
850
+ {
851
+ sats: BigInt(0),
852
+ script: slpSend(childTokenId, SLP_NFT1_CHILD, [0n, 1n]),
853
+ },
854
+ { sats: BigInt(80000), script: cancelerP2pkh },
855
+ ];
856
+ const agoraOneshot = new AgoraOneshot({
857
+ enforcedOutputs,
858
+ cancelPk: cancelerPk,
859
+ });
860
+ const agoraAdScript = agoraOneshot.adScript();
861
+ const agoraAdP2sh = Script.p2sh(shaRmd160(agoraAdScript.bytecode));
862
+ const txBuildAdSetup = new TxBuilder({
863
+ inputs: [
864
+ {
865
+ input: {
866
+ prevOut: {
867
+ txid: genesisChildTxid,
868
+ outIdx: 1,
869
+ },
870
+ signData: {
871
+ sats: 8000n,
872
+ outputScript: cancelerP2pkh,
873
+ },
874
+ },
875
+ signatory: P2PKHSignatory(
876
+ cancelerSk,
877
+ cancelerPk,
878
+ ALL_BIP143,
879
+ ),
880
+ },
881
+ ],
882
+ outputs: [
883
+ {
884
+ sats: 0n,
885
+ script: slpSend(childTokenId, SLP_NFT1_CHILD, [1n]),
886
+ },
887
+ { sats: 7000n, script: agoraAdP2sh },
888
+ ],
889
+ });
890
+ const adSetupTx = txBuildAdSetup.sign();
891
+ const adSetupTxid = (await chronik.broadcastTx(adSetupTx.ser())).txid;
892
+
893
+ // 4. Seller finishes offer setup + sends NFT to the advertised P2SH
894
+ const agoraScript = agoraOneshot.script();
895
+ const agoraP2sh = Script.p2sh(shaRmd160(agoraScript.bytecode));
896
+ const txBuildOffer = new TxBuilder({
897
+ inputs: [
898
+ {
899
+ input: {
900
+ prevOut: {
901
+ txid: adSetupTxid,
902
+ outIdx: 1,
903
+ },
904
+ signData: {
905
+ sats: 7000n,
906
+ redeemScript: agoraAdScript,
907
+ },
908
+ },
909
+ signatory: AgoraOneshotAdSignatory(cancelerSk),
910
+ },
911
+ ],
912
+ outputs: [
913
+ {
914
+ sats: 0n,
915
+ script: slpSend(childTokenId, SLP_NFT1_CHILD, [1n]),
916
+ },
917
+ { sats: 546n, script: agoraP2sh },
918
+ ],
919
+ });
920
+ const offerTx = txBuildOffer.sign();
921
+ const offerPromise = listenNext();
922
+ const offerTxid = (await chronik.broadcastTx(offerTx.ser())).txid;
923
+ const offerOutpoint: OutPoint = {
924
+ txid: offerTxid,
925
+ outIdx: 1,
926
+ };
927
+ const offerTxBuilderInput: TxInput = {
928
+ prevOut: offerOutpoint,
929
+ signData: {
930
+ redeemScript: agoraScript,
931
+ sats: 546n,
932
+ },
933
+ };
934
+
935
+ // Expected created offer
936
+ const expectedOffer = new AgoraOffer({
937
+ variant: {
938
+ type: 'ONESHOT',
939
+ params: agoraOneshot,
940
+ },
941
+ outpoint: offerOutpoint,
942
+ txBuilderInput: offerTxBuilderInput,
943
+ token: {
944
+ tokenId: childTokenId,
945
+ tokenType: SLP_TOKEN_TYPE_NFT1_CHILD,
946
+ atoms: 1n,
947
+ isMintBaton: false,
948
+ },
949
+ status: 'OPEN',
950
+ });
951
+
952
+ const [offerMsg] = await offerPromise;
953
+ expect(offerMsg.type).to.equal('Tx');
954
+ expect(offerMsg.msgType).to.equal('TX_ADDED_TO_MEMPOOL');
955
+ expect(offerMsg.txid).to.equal(offerTxid);
956
+
957
+ // 5. Buyer searches for NFT trades, finds the advertised one
958
+ expect(await agora.allOfferedTokenIds()).to.deep.equal([childTokenId]);
959
+
960
+ // Query by group token ID
961
+ expect(
962
+ await agora.activeOffersByGroupTokenId(groupTokenId),
963
+ ).to.deep.equal([expectedOffer]);
964
+
965
+ // Query by child token ID
966
+ expect(await agora.activeOffersByTokenId(childTokenId)).to.deep.equal([
967
+ expectedOffer,
968
+ ]);
969
+
970
+ // Query by cancelPk
971
+ expect(
972
+ await agora.activeOffersByPubKey(toHex(cancelerPk)),
973
+ ).to.deep.equal([expectedOffer]);
974
+
975
+ // We can cancel the offer using cancel()
976
+ // We need a utxo to spend to cancel this offer
977
+ await runner.sendToScript(50000n, cancelerP2pkh);
978
+ await cancelerWallet.sync();
979
+ await expectedOffer.cancel({
980
+ wallet: cancelerWallet,
981
+ });
982
+
983
+ // We can no longer find the offer
984
+ expect(await agora.allOfferedTokenIds()).to.deep.equal([]);
985
+ expect(await agora.offeredGroupTokenIds()).to.deep.equal([]);
986
+ expect(await agora.offeredFungibleTokenIds()).to.deep.equal([]);
987
+ expect(
988
+ await agora.activeOffersByGroupTokenId(groupTokenId),
989
+ ).to.deep.equal([]);
990
+ expect(await agora.activeOffersByTokenId(childTokenId)).to.deep.equal(
991
+ [],
992
+ );
993
+ expect(
994
+ await agora.activeOffersByPubKey(toHex(cancelerPk)),
995
+ ).to.deep.equal([]);
996
+ });
707
997
  });
@@ -16,6 +16,7 @@ import {
16
16
  TxBuilderInput,
17
17
  TxBuilderOutput,
18
18
  } from 'ecash-lib';
19
+ import { Wallet } from 'ecash-wallet';
19
20
  import { expect } from 'chai';
20
21
 
21
22
  import { AgoraPartial } from '../src/partial.js';
@@ -124,22 +125,23 @@ export async function takeAlpOffer(params: {
124
125
  chronik: ChronikClient;
125
126
  offer: AgoraOffer;
126
127
  takerSk: Uint8Array;
127
- takerInput: TxBuilderInput;
128
128
  acceptedAtoms: bigint;
129
129
  allowUnspendable?: boolean;
130
130
  }) {
131
131
  const takerSk = params.takerSk;
132
- const takerPk = new Ecc().derivePubkey(takerSk);
133
- const takerPkh = shaRmd160(takerPk);
134
- const takerP2pkh = Script.p2pkh(takerPkh);
135
- const acceptTx = params.offer.acceptTx({
136
- covenantSk: params.takerSk,
132
+ const takerWallet = Wallet.fromSk(takerSk, params.chronik);
133
+ const takerPk = takerWallet.pk;
134
+ const takerP2pkh = takerWallet.script;
135
+
136
+ await takerWallet.sync();
137
+ const broadcastResult = await params.offer.take({
138
+ wallet: takerWallet,
139
+ covenantSk: takerSk,
137
140
  covenantPk: takerPk,
138
- fuelInputs: [params.takerInput],
139
141
  recipientScript: takerP2pkh,
140
142
  acceptedAtoms: params.acceptedAtoms,
141
143
  allowUnspendable: params.allowUnspendable,
142
144
  });
143
- const acceptTxid = (await params.chronik.broadcastTx(acceptTx.ser())).txid;
145
+ const acceptTxid = broadcastResult.broadcasted[0];
144
146
  return acceptTxid;
145
147
  }
@@ -14,6 +14,7 @@ import {
14
14
  TxBuilder,
15
15
  TxBuilderInput,
16
16
  } from 'ecash-lib';
17
+ import { Wallet } from 'ecash-wallet';
17
18
  import { expect } from 'chai';
18
19
 
19
20
  import { AgoraPartial, AgoraPartialAdSignatory } from '../src/partial.js';
@@ -132,22 +133,22 @@ export async function takeSlpOffer(params: {
132
133
  chronik: ChronikClient;
133
134
  offer: AgoraOffer;
134
135
  takerSk: Uint8Array;
135
- takerInput: TxBuilderInput;
136
136
  acceptedAtoms: bigint;
137
137
  allowUnspendable?: boolean;
138
138
  }) {
139
139
  const takerSk = params.takerSk;
140
- const takerPk = new Ecc().derivePubkey(takerSk);
141
- const takerPkh = shaRmd160(takerPk);
142
- const takerP2pkh = Script.p2pkh(takerPkh);
143
- const acceptTx = params.offer.acceptTx({
144
- covenantSk: params.takerSk,
140
+ const takerWallet = Wallet.fromSk(takerSk, params.chronik);
141
+ const takerPk = takerWallet.pk;
142
+ const takerP2pkh = takerWallet.script;
143
+ await takerWallet.sync();
144
+ const broadcastResult = await params.offer.take({
145
+ wallet: takerWallet,
146
+ covenantSk: takerSk,
145
147
  covenantPk: takerPk,
146
- fuelInputs: [params.takerInput],
147
148
  recipientScript: takerP2pkh,
148
149
  acceptedAtoms: params.acceptedAtoms,
149
150
  allowUnspendable: params.allowUnspendable,
150
151
  });
151
- const acceptTxid = (await params.chronik.broadcastTx(acceptTx.ser())).txid;
152
+ const acceptTxid = broadcastResult.broadcasted[0];
152
153
  return acceptTxid;
153
154
  }
@@ -53,7 +53,10 @@ async function makeBuilderInputs(
53
53
  runner: TestRunner,
54
54
  values: bigint[],
55
55
  ): Promise<TxBuilderInput[]> {
56
- const txid = await runner.sendToScript(values, makerScript);
56
+ const txid = await runner.sendToTwoScripts(
57
+ { script: makerScript, sats: values[0] },
58
+ { script: takerScript, sats: values[1] },
59
+ );
57
60
  return values.map((sats, outIdx) => ({
58
61
  input: {
59
62
  prevOut: {
@@ -84,10 +87,7 @@ describe('AgoraPartial ALP 7450M XEC vs 2p48-1 full accept', () => {
84
87
  });
85
88
 
86
89
  it('AgoraPartial ALP 7450M XEC vs 2p48-1 full accept', async () => {
87
- const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
88
- 10000n,
89
- BIGSATS,
90
- ]);
90
+ const [fuelInput] = await makeBuilderInputs(runner, [10000n, BIGSATS]);
91
91
 
92
92
  const agora = new Agora(chronik);
93
93
  const agoraPartial = await agora.selectParams({
@@ -133,7 +133,6 @@ describe('AgoraPartial ALP 7450M XEC vs 2p48-1 full accept', () => {
133
133
  chronik,
134
134
  offer,
135
135
  takerSk,
136
- takerInput,
137
136
  acceptedAtoms: agoraPartial.offeredAtoms(),
138
137
  });
139
138
 
@@ -178,10 +177,7 @@ describe('AgoraPartial 7450M XEC vs 2p48-1 small accept', () => {
178
177
  });
179
178
 
180
179
  it('AgoraPartial ALP 7450M XEC vs 2p48-1 small accept', async () => {
181
- const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
182
- 10000n,
183
- BIGSATS,
184
- ]);
180
+ const [fuelInput] = await makeBuilderInputs(runner, [10000n, BIGSATS]);
185
181
 
186
182
  const agora = new Agora(chronik);
187
183
  const agoraPartial = await agora.selectParams({
@@ -229,7 +225,6 @@ describe('AgoraPartial 7450M XEC vs 2p48-1 small accept', () => {
229
225
  chronik,
230
226
  offer,
231
227
  takerSk,
232
- takerInput,
233
228
  acceptedAtoms,
234
229
  });
235
230
 
@@ -282,10 +277,7 @@ describe('AgoraPartial 7450M XEC vs 2p47-1 full accept', () => {
282
277
  });
283
278
 
284
279
  it('AgoraPartial ALP 7450M XEC vs 2p47-1 full accept', async () => {
285
- const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
286
- 10000n,
287
- BIGSATS,
288
- ]);
280
+ const [fuelInput] = await makeBuilderInputs(runner, [10000n, BIGSATS]);
289
281
 
290
282
  const agora = new Agora(chronik);
291
283
  const agoraPartial = await agora.selectParams({
@@ -330,7 +322,6 @@ describe('AgoraPartial 7450M XEC vs 2p47-1 full accept', () => {
330
322
  chronik,
331
323
  offer,
332
324
  takerSk,
333
- takerInput,
334
325
  acceptedAtoms: agoraPartial.offeredAtoms(),
335
326
  });
336
327
 
@@ -378,10 +369,7 @@ describe('AgoraPartial ALP 7450M XEC vs 2p47-1 small accept', () => {
378
369
  });
379
370
 
380
371
  it('AgoraPartial ALP 7450M XEC vs 2p47-1 small accept', async () => {
381
- const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
382
- 10000n,
383
- BIGSATS,
384
- ]);
372
+ const [fuelInput] = await makeBuilderInputs(runner, [10000n, BIGSATS]);
385
373
 
386
374
  const agora = new Agora(chronik);
387
375
  const agoraPartial = await agora.selectParams({
@@ -429,7 +417,6 @@ describe('AgoraPartial ALP 7450M XEC vs 2p47-1 small accept', () => {
429
417
  chronik,
430
418
  offer,
431
419
  takerSk,
432
- takerInput,
433
420
  acceptedAtoms,
434
421
  });
435
422
 
@@ -482,10 +469,7 @@ describe('AgoraPartial ALP 7450M XEC vs 100 full accept', () => {
482
469
  });
483
470
 
484
471
  it('AgoraPartial ALP 7450M XEC vs 100 full accept', async () => {
485
- const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
486
- 10000n,
487
- BIGSATS,
488
- ]);
472
+ const [fuelInput] = await makeBuilderInputs(runner, [10000n, BIGSATS]);
489
473
 
490
474
  const agora = new Agora(chronik);
491
475
  const agoraPartial = await agora.selectParams({
@@ -529,7 +513,6 @@ describe('AgoraPartial ALP 7450M XEC vs 100 full accept', () => {
529
513
  chronik,
530
514
  offer,
531
515
  takerSk,
532
- takerInput,
533
516
  acceptedAtoms: 100n,
534
517
  });
535
518
  const acceptTx = await chronik.tx(acceptTxid);
@@ -574,10 +557,7 @@ describe('AgoraPartial ALP 7450M XEC vs 100 small accept', () => {
574
557
  });
575
558
 
576
559
  it('AgoraPartial ALP 7450M XEC vs 100 small accept', async () => {
577
- const [fuelInput, takerInput] = await makeBuilderInputs(runner, [
578
- 10000n,
579
- BIGSATS,
580
- ]);
560
+ const [fuelInput] = await makeBuilderInputs(runner, [10000n, BIGSATS]);
581
561
 
582
562
  const agora = new Agora(chronik);
583
563
  const agoraPartial = await agora.selectParams({
@@ -617,7 +597,6 @@ describe('AgoraPartial ALP 7450M XEC vs 100 small accept', () => {
617
597
  chronik,
618
598
  offer,
619
599
  takerSk,
620
- takerInput,
621
600
  acceptedAtoms: 1n,
622
601
  });
623
602
  const acceptTx = await chronik.tx(acceptTxid);
@@ -24,6 +24,7 @@ import { TestRunner } from 'ecash-lib/dist/test/testRunner.js';
24
24
  import { AgoraPartial } from '../src/partial.js';
25
25
  import { makeAlpOffer, takeAlpOffer } from './partial-helper-alp.js';
26
26
  import { Agora, TakenInfo } from '../src/agora.js';
27
+ import { Wallet } from 'ecash-wallet/src/wallet.js';
27
28
 
28
29
  use(chaiAsPromised);
29
30
 
@@ -57,7 +58,10 @@ describe('AgoraPartial ALP', () => {
57
58
  async function makeBuilderInputs(
58
59
  values: bigint[],
59
60
  ): Promise<TxBuilderInput[]> {
61
+ // NB for these "not big sats" tests, we can just send to both, no need to sendToTwoScripts
60
62
  const txid = await runner.sendToScript(values, makerScript);
63
+ // Send some cash to the taker, since the accept() tests spend from this wallet
64
+ await runner.sendToScript(values, takerScript);
61
65
  return values.map((sats, outIdx) => ({
62
66
  input: {
63
67
  prevOut: {
@@ -417,6 +421,7 @@ describe('AgoraPartial ALP', () => {
417
421
  },
418
422
  ];
419
423
 
424
+ let cancelTxsMatchCount = 0;
420
425
  for (const testCase of TEST_CASES) {
421
426
  it(`AgoraPartial ALP ${testCase.offeredAtoms} for ${testCase.info}`, async () => {
422
427
  const agora = new Agora(chronik);
@@ -429,10 +434,7 @@ describe('AgoraPartial ALP', () => {
429
434
  });
430
435
  const askedSats = agoraPartial.askedSats(testCase.acceptedAtoms);
431
436
  const requiredSats = askedSats + 2000n;
432
- const [fuelInput, takerInput] = await makeBuilderInputs([
433
- 4000n,
434
- requiredSats,
435
- ]);
437
+ const [fuelInput] = await makeBuilderInputs([4000n, requiredSats]);
436
438
 
437
439
  const offer = await makeAlpOffer({
438
440
  chronik,
@@ -444,12 +446,10 @@ describe('AgoraPartial ALP', () => {
444
446
  chronik,
445
447
  takerSk,
446
448
  offer,
447
- takerInput,
448
449
  acceptedAtoms: testCase.acceptedAtoms,
449
450
  allowUnspendable: testCase.allowUnspendable,
450
451
  });
451
452
  const acceptTx = await chronik.tx(acceptTxid);
452
- // TODO we do not even get here, keep debugging
453
453
  const offeredAtoms = agoraPartial.offeredAtoms();
454
454
  const isFullAccept = testCase.acceptedAtoms == offeredAtoms;
455
455
  if (isFullAccept) {
@@ -543,17 +543,40 @@ describe('AgoraPartial ALP', () => {
543
543
  recipientScript: makerScript,
544
544
  extraInputs: [fuelInput], // dummy input for measuring
545
545
  });
546
- const cancelTxSer = newOffer
547
- .cancelTx({
548
- cancelSk: makerSk,
549
- fuelInputs: await makeBuilderInputs([cancelFeeSats]),
550
- recipientScript: makerScript,
551
- })
552
- .ser();
553
- const cancelTxid = (await chronik.broadcastTx(cancelTxSer)).txid;
554
- const cancelTx = await chronik.tx(cancelTxid);
555
- expect(cancelTx.outputs[1].token?.atoms).to.equal(leftoverTokens);
556
- expect(cancelTx.outputs[1].outputScript).to.equal(makerScriptHex);
546
+ const cancelTx = newOffer.cancelTx({
547
+ cancelSk: makerSk,
548
+ fuelInputs: await makeBuilderInputs([cancelFeeSats]),
549
+ recipientScript: makerScript,
550
+ });
551
+ const cancelTxid = cancelTx.txid();
552
+
553
+ // Let's build and broadcast using cancel() instead
554
+ const cancelWallet = Wallet.fromSk(makerSk, chronik);
555
+ await cancelWallet.sync();
556
+ const cancelResult = await newOffer.cancel({
557
+ wallet: cancelWallet,
558
+ });
559
+ const broadcastCancelTxid = cancelResult.broadcasted[0];
560
+ if (broadcastCancelTxid === cancelTxid) {
561
+ cancelTxsMatchCount++;
562
+ console.log(
563
+ `${cancelTxsMatchCount} of ${TEST_CASES.length} produce equal txids with cancelTx() and cancel()`,
564
+ );
565
+
566
+ // Between ~5 and ~8 of 46 of these txs are identical from each method
567
+
568
+ // On inspection, when cancel() txid does not match,
569
+ // it is because cancel has selected different fuel inputs
570
+ // This is expected behavior, these txs still show change
571
+ // going to the cancel wallet as expected
572
+ }
573
+ const cancelChronikTx = await chronik.tx(broadcastCancelTxid);
574
+ expect(cancelChronikTx.outputs[1].token?.atoms).to.equal(
575
+ leftoverTokens,
576
+ );
577
+ expect(cancelChronikTx.outputs[1].outputScript).to.equal(
578
+ makerScriptHex,
579
+ );
557
580
 
558
581
  // takerIndex is 2 for full accept, 3 for partial accept
559
582
  const takerIndex = isFullAccept ? 2 : 3;
@@ -611,10 +634,7 @@ describe('AgoraPartial ALP', () => {
611
634
  });
612
635
  const askedSats = agoraPartial.askedSats(thisTestCase.acceptedAtoms);
613
636
  const requiredSats = askedSats + 2000n;
614
- const [fuelInput, takerInput] = await makeBuilderInputs([
615
- 4000n,
616
- requiredSats,
617
- ]);
637
+ const [fuelInput] = await makeBuilderInputs([4000n, requiredSats]);
618
638
 
619
639
  const offer = await makeAlpOffer({
620
640
  chronik,
@@ -639,7 +659,6 @@ describe('AgoraPartial ALP', () => {
639
659
  chronik,
640
660
  takerSk,
641
661
  offer,
642
- takerInput,
643
662
  acceptedAtoms: thisTestCase.acceptedAtoms,
644
663
  allowUnspendable: false,
645
664
  }),
@@ -678,10 +697,7 @@ describe('AgoraPartial ALP', () => {
678
697
  const acceptedAtoms = 500n;
679
698
  const askedSats = agoraPartial.askedSats(acceptedAtoms);
680
699
  const requiredSats = askedSats + 2000n;
681
- const [fuelInput, takerInput] = await makeBuilderInputs([
682
- 4000n,
683
- requiredSats,
684
- ]);
700
+ const [fuelInput] = await makeBuilderInputs([4000n, requiredSats]);
685
701
 
686
702
  const offer = await makeAlpOffer({
687
703
  chronik,
@@ -703,7 +719,6 @@ describe('AgoraPartial ALP', () => {
703
719
  chronik,
704
720
  takerSk,
705
721
  offer,
706
- takerInput,
707
722
  acceptedAtoms: acceptedAtoms,
708
723
  allowUnspendable: false,
709
724
  }),