@1sat/actions 0.0.115 → 0.0.116

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.
Files changed (40) hide show
  1. package/dist/constants.d.ts +1 -1
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +1 -1
  4. package/dist/constants.js.map +1 -1
  5. package/dist/cosign/finalize.d.ts +15 -0
  6. package/dist/cosign/finalize.d.ts.map +1 -0
  7. package/dist/cosign/finalize.js +123 -0
  8. package/dist/cosign/finalize.js.map +1 -0
  9. package/dist/cosign/index.d.ts +15 -0
  10. package/dist/cosign/index.d.ts.map +1 -0
  11. package/dist/cosign/index.js +15 -0
  12. package/dist/cosign/index.js.map +1 -0
  13. package/dist/cosign/prepare.d.ts +21 -0
  14. package/dist/cosign/prepare.d.ts.map +1 -0
  15. package/dist/cosign/prepare.js +282 -0
  16. package/dist/cosign/prepare.js.map +1 -0
  17. package/dist/cosign/types.d.ts +171 -0
  18. package/dist/cosign/types.d.ts.map +1 -0
  19. package/dist/cosign/types.js +37 -0
  20. package/dist/cosign/types.js.map +1 -0
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/inscriptions/index.d.ts +3 -0
  26. package/dist/inscriptions/index.d.ts.map +1 -1
  27. package/dist/inscriptions/index.js +30 -27
  28. package/dist/inscriptions/index.js.map +1 -1
  29. package/dist/tokens/index.d.ts +80 -7
  30. package/dist/tokens/index.d.ts.map +1 -1
  31. package/dist/tokens/index.js +585 -41
  32. package/dist/tokens/index.js.map +1 -1
  33. package/dist/utils/internalizeBeef.d.ts.map +1 -1
  34. package/dist/utils/internalizeBeef.js +2 -1
  35. package/dist/utils/internalizeBeef.js.map +1 -1
  36. package/dist/utils/resolveDestination.d.ts +39 -0
  37. package/dist/utils/resolveDestination.d.ts.map +1 -0
  38. package/dist/utils/resolveDestination.js +43 -0
  39. package/dist/utils/resolveDestination.js.map +1 -0
  40. package/package.json +3 -3
@@ -5,10 +5,11 @@
5
5
  */
6
6
  import { BSV21, OrdLock } from '@1sat/templates';
7
7
  import { parseOutpoint } from '@1sat/utils';
8
- import { BigNumber, LockingScript, OP, P2PKH, PublicKey, TransactionSignature, UnlockingScript, Utils, } from '@bsv/sdk';
9
- import { BSV21_BASKET, BSV21_PROTOCOL } from '../constants';
8
+ import { Beef, BigNumber, LockingScript, OP, P2PKH, PublicKey, Transaction, TransactionSignature, UnlockingScript, Utils, } from '@bsv/sdk';
9
+ import { BSV21_AUTH_BASKET, BSV21_BASKET, BSV21_DEPLOY_FUNDING_BASKET, BSV21_PROTOCOL, } from '../constants';
10
10
  import { executeTrackedAction } from '../utils/createTrackedAction';
11
11
  import { getDisplayValue } from '../utils/displayValue';
12
+ import { resolveDestination } from '../utils/resolveDestination';
12
13
  import { signP2PKHInput } from '../utils/signP2PKH';
13
14
  // ============================================================================
14
15
  // Internal helpers
@@ -150,7 +151,7 @@ export const getBsv21Balances = {
150
151
  export const sendBsv21 = {
151
152
  meta: {
152
153
  name: 'sendBsv21',
153
- description: 'Send BSV21 tokens to one or more recipients (counterparty or address)',
154
+ description: 'Send BSV21 tokens to one or more recipients',
154
155
  category: 'tokens',
155
156
  inputSchema: {
156
157
  type: 'object',
@@ -166,16 +167,12 @@ export const sendBsv21 = {
166
167
  type: 'string',
167
168
  description: 'Amount to send (as string for bigint)',
168
169
  },
169
- counterparty: {
170
- type: 'string',
171
- description: 'Recipient identity public key (hex)',
172
- },
173
- address: {
174
- type: 'string',
175
- description: 'Recipient P2PKH address',
170
+ destination: {
171
+ type: 'object',
172
+ description: 'Where to lock the output. One of lockingScript (hex), counterparty (pubkey), or address.',
176
173
  },
177
174
  },
178
- required: ['amount'],
175
+ required: ['amount', 'destination'],
179
176
  },
180
177
  },
181
178
  },
@@ -194,14 +191,10 @@ export const sendBsv21 = {
194
191
  if (amount <= 0n) {
195
192
  return { error: 'amount-must-be-positive' };
196
193
  }
197
- if (!r.counterparty && !r.address) {
198
- return { error: 'must-provide-counterparty-or-address' };
194
+ if (!r.destination) {
195
+ return { error: 'recipient-missing-destination' };
199
196
  }
200
- resolved.push({
201
- amount,
202
- counterparty: r.counterparty,
203
- address: r.address,
204
- });
197
+ resolved.push({ amount, destination: r.destination });
205
198
  }
206
199
  const totalAmount = resolved.reduce((sum, r) => sum + r.amount, 0n);
207
200
  if (!ctx.services) {
@@ -266,27 +259,12 @@ export const sendBsv21 = {
266
259
  const recipientKeyIDs = [];
267
260
  // Build recipient outputs
268
261
  for (const r of resolved) {
269
- let recipientAddress;
270
- let recipientKeyID;
271
- if (r.counterparty) {
272
- recipientKeyID = `${tokenId}-${Date.now()}`;
273
- const { publicKey } = await ctx.wallet.getPublicKey({
274
- protocolID: BSV21_PROTOCOL,
275
- keyID: recipientKeyID,
276
- counterparty: r.counterparty,
277
- forSelf: false,
278
- });
279
- recipientAddress = PublicKey.fromString(publicKey).toAddress();
280
- }
281
- else if (r.address) {
282
- recipientAddress = r.address;
283
- }
284
- else {
285
- return { error: 'must-provide-counterparty-or-address' };
286
- }
287
- recipientKeyIDs.push(recipientKeyID);
288
- const destinationLockingScript = p2pkh.lock(recipientAddress);
289
- const transferScript = BSV21.transfer(tokenId, r.amount).lock(destinationLockingScript);
262
+ const recipientResolved = await resolveDestination(ctx, r.destination, {
263
+ protocolID: BSV21_PROTOCOL,
264
+ keyIDPrefix: tokenId,
265
+ });
266
+ recipientKeyIDs.push(recipientResolved.customInstructions?.keyID);
267
+ const transferScript = BSV21.transfer(tokenId, r.amount).lock(recipientResolved.lockingScript);
290
268
  outputs.push({
291
269
  lockingScript: transferScript.toHex(),
292
270
  satoshis: 1,
@@ -415,8 +393,7 @@ export const sendBsv21 = {
415
393
  tokenId,
416
394
  recipients: resolved.map((r) => ({
417
395
  amount: r.amount.toString(),
418
- counterparty: r.counterparty,
419
- address: r.address,
396
+ destination: r.destination,
420
397
  })),
421
398
  },
422
399
  txid: result.txid,
@@ -637,6 +614,570 @@ export const purchaseBsv21 = {
637
614
  }
638
615
  },
639
616
  };
617
+ /** Miner fee rate used for manual deploy tx fee calculation. */
618
+ const DEPLOY_FEE_PER_KB = 100;
619
+ /**
620
+ * Estimate the byte size of the manually-built deploy tx (1 input, 1 output, no
621
+ * change). Conservative — sized for a typical signed P2PKH unlocking script.
622
+ */
623
+ function estimateDeployTxSize(deployScriptBytes) {
624
+ const overhead = 10; // version(4) + locktime(4) + inCount(1) + outCount(1)
625
+ const input = 148; // P2PKH spend: txid(32)+vout(4)+scriptLen(1)+script(~107)+seq(4)
626
+ const scriptLenVarint = deployScriptBytes < 0xfd
627
+ ? 1
628
+ : deployScriptBytes < 0x10000
629
+ ? 3
630
+ : 5;
631
+ const output = 8 + scriptLenVarint + deployScriptBytes;
632
+ return overhead + input + output;
633
+ }
634
+ /**
635
+ * Shared deploy flow: createAction a funding intermediate → manually build the
636
+ * deploy tx spending it → internalizeAction adopts the deploy with proper
637
+ * tokenId-bearing tags and broadcasts.
638
+ *
639
+ * Used by both deployBsv21Mint and deployBsv21Auth — they only differ in the
640
+ * deploy script (BSV21.deployMint vs BSV21.deployAuth) and target basket.
641
+ */
642
+ async function executeBsv21Deploy(args) {
643
+ const { ctx, symbol, deployScript, basket } = args;
644
+ const deployScriptBin = deployScript.toBinary();
645
+ const txSize = estimateDeployTxSize(deployScriptBin.length);
646
+ const fee = Math.ceil((txSize * DEPLOY_FEE_PER_KB) / 1000);
647
+ // 1 sat for the deploy output + computed fee + small buffer for unlocking
648
+ // script size variance (low-S signatures can be a byte shorter, etc.)
649
+ const fundingValue = 1 + fee + 5;
650
+ const fundingKeyID = `bsv21-deploy-fund-${symbol}-${Date.now()}`;
651
+ const { publicKey: fundingPubKey } = await ctx.wallet.getPublicKey({
652
+ protocolID: BSV21_PROTOCOL,
653
+ keyID: fundingKeyID,
654
+ counterparty: 'self',
655
+ forSelf: true,
656
+ });
657
+ const fundingAddress = PublicKey.fromString(fundingPubKey).toAddress();
658
+ const fundingScript = new P2PKH().lock(fundingAddress);
659
+ const fundingResult = await ctx.wallet.createAction({
660
+ description: `${args.description} (funding)`,
661
+ outputs: [
662
+ {
663
+ lockingScript: fundingScript.toHex(),
664
+ satoshis: fundingValue,
665
+ outputDescription: 'Deploy funding intermediate',
666
+ basket: BSV21_DEPLOY_FUNDING_BASKET,
667
+ customInstructions: JSON.stringify({
668
+ protocolID: BSV21_PROTOCOL,
669
+ keyID: fundingKeyID,
670
+ }),
671
+ },
672
+ ],
673
+ options: { randomizeOutputs: false },
674
+ });
675
+ if (!fundingResult.txid || !fundingResult.tx) {
676
+ return { error: 'funding-failed' };
677
+ }
678
+ const fundingBeef = Beef.fromBinary(Array.from(fundingResult.tx));
679
+ const fundingTx = fundingBeef.findAtomicTransaction(fundingResult.txid);
680
+ if (!fundingTx) {
681
+ return { error: 'funding-tx-not-in-beef' };
682
+ }
683
+ const deployTx = new Transaction();
684
+ deployTx.addInput({
685
+ sourceTransaction: fundingTx,
686
+ // Setting sourceTXID is required so BeefTx.updateInputTxids picks up
687
+ // the dependency. Without it, the deploy's inputTxids is empty and
688
+ // Beef.sortTxs orders the deploy before its ancestors, which then get
689
+ // trimmed by toBinaryAtomic.
690
+ sourceTXID: fundingResult.txid,
691
+ sourceOutputIndex: 0,
692
+ unlockingScript: new UnlockingScript(),
693
+ sequence: 0xffffffff,
694
+ });
695
+ deployTx.addOutput({
696
+ lockingScript: deployScript,
697
+ satoshis: 1,
698
+ });
699
+ const sigResult = await signP2PKHInput(ctx, deployTx, 0, BSV21_PROTOCOL, fundingKeyID);
700
+ if (typeof sigResult !== 'string') {
701
+ return { error: sigResult.error };
702
+ }
703
+ deployTx.inputs[0].unlockingScript = UnlockingScript.fromHex(sigResult);
704
+ const deployTxid = deployTx.id('hex');
705
+ const tokenId = `${deployTxid}_0`;
706
+ fundingBeef.mergeTransaction(deployTx);
707
+ const deployBeefBin = fundingBeef.toBinaryAtomic(deployTxid);
708
+ const customInstructions = args.destinationCustomInstructions
709
+ ? JSON.stringify({
710
+ protocolID: args.destinationCustomInstructions.protocolID,
711
+ keyID: args.destinationCustomInstructions.keyID,
712
+ sym: symbol,
713
+ })
714
+ : undefined;
715
+ await ctx.wallet.internalizeAction({
716
+ tx: deployBeefBin,
717
+ outputs: [
718
+ {
719
+ outputIndex: 0,
720
+ protocol: 'basket insertion',
721
+ insertionRemittance: {
722
+ basket,
723
+ tags: args.buildTags(tokenId),
724
+ customInstructions,
725
+ },
726
+ },
727
+ ],
728
+ description: args.description,
729
+ labels: ['bsv21:deploy'],
730
+ });
731
+ return {
732
+ txid: deployTxid,
733
+ tx: deployBeefBin,
734
+ tokenId,
735
+ };
736
+ }
737
+ /**
738
+ * Deploy a new BSV21 token with a fixed supply (deploy+mint).
739
+ *
740
+ * The entire supply is minted in this transaction and sent to the destination.
741
+ * No further minting is possible — total supply is locked at deploy time.
742
+ */
743
+ export const deployBsv21Mint = {
744
+ meta: {
745
+ name: 'deployBsv21Mint',
746
+ description: 'Deploy a new BSV21 token with fixed supply (deploy+mint)',
747
+ category: 'tokens',
748
+ inputSchema: {
749
+ type: 'object',
750
+ properties: {
751
+ symbol: { type: 'string', description: 'Token symbol/ticker' },
752
+ amount: {
753
+ type: 'string',
754
+ description: 'Total fixed supply (as string for bigint)',
755
+ },
756
+ decimals: {
757
+ type: 'integer',
758
+ description: 'Decimal places (0-18, default 0)',
759
+ },
760
+ icon: { type: 'string', description: 'Icon URL or data URI' },
761
+ destination: {
762
+ type: 'object',
763
+ description: 'Recipient destination. One of lockingScript (hex), counterparty (pubkey), or address. Defaults to self.',
764
+ },
765
+ },
766
+ required: ['symbol', 'amount'],
767
+ },
768
+ },
769
+ async execute(ctx, input) {
770
+ try {
771
+ const { symbol, amount: rawAmount, decimals = 0, icon, destination } = input;
772
+ const amount = typeof rawAmount === 'string' ? BigInt(rawAmount) : rawAmount;
773
+ if (amount <= 0n) {
774
+ return { error: 'amount-must-be-positive' };
775
+ }
776
+ const resolved = await resolveDestination(ctx, destination, {
777
+ protocolID: BSV21_PROTOCOL,
778
+ keyIDPrefix: `bsv21-deploy-${symbol}`,
779
+ });
780
+ const deployScript = BSV21.deployMint(symbol, amount, decimals, icon).lock(resolved.lockingScript);
781
+ const result = await executeBsv21Deploy({
782
+ ctx,
783
+ symbol,
784
+ deployScript,
785
+ destinationCustomInstructions: resolved.customInstructions,
786
+ basket: BSV21_BASKET,
787
+ description: `Deploy ${symbol} (${amount} fixed supply)`,
788
+ outputDescription: `Deploy ${symbol}`,
789
+ buildTags: (tokenId) => {
790
+ const tags = [
791
+ `bsv21:${tokenId}`,
792
+ `amt:${amount}`,
793
+ `dec:${decimals}`,
794
+ `sym:${symbol}`,
795
+ ];
796
+ if (icon)
797
+ tags.push(`icon:${icon}`);
798
+ return tags;
799
+ },
800
+ });
801
+ if (result.error)
802
+ return { error: result.error };
803
+ if (result.tx && ctx.services) {
804
+ try {
805
+ await ctx.services.overlay.submitBsv21Discovery(result.tx);
806
+ }
807
+ catch (overlayError) {
808
+ console.warn('[deployBsv21Mint] Overlay submission failed:', overlayError);
809
+ }
810
+ }
811
+ if (ctx.debug && ctx.log) {
812
+ ctx.log({
813
+ timestamp: new Date().toISOString(),
814
+ action: 'deployBsv21Mint',
815
+ input: {
816
+ symbol,
817
+ amount: amount.toString(),
818
+ decimals,
819
+ icon,
820
+ destination,
821
+ },
822
+ txid: result.txid,
823
+ rawtx: result.tx ? Utils.toHex(result.tx) : undefined,
824
+ outputs: [
825
+ {
826
+ index: 0,
827
+ protocolID: BSV21_PROTOCOL,
828
+ keyID: resolved.customInstructions?.keyID,
829
+ basket: BSV21_BASKET,
830
+ satoshis: 1,
831
+ },
832
+ ],
833
+ });
834
+ }
835
+ return result;
836
+ }
837
+ catch (error) {
838
+ console.error('[deployBsv21Mint]', error);
839
+ if (ctx.debug && ctx.log) {
840
+ ctx.log({
841
+ timestamp: new Date().toISOString(),
842
+ action: 'deployBsv21Mint',
843
+ input: { symbol: input.symbol },
844
+ error: error instanceof Error ? error.message : 'unknown-error',
845
+ });
846
+ }
847
+ return {
848
+ error: error instanceof Error ? error.message : 'unknown-error',
849
+ };
850
+ }
851
+ },
852
+ };
853
+ /**
854
+ * Deploy a new BSV21 token with mintable supply (deploy+auth).
855
+ *
856
+ * Emits a single `deploy+auth` output that doubles as the genesis auth UTXO.
857
+ * Initial supply is zero — the auth holder must spend this output via a
858
+ * separate mint transaction to create supply. Authority can be split,
859
+ * combined, transferred, or burned via subsequent auth operations.
860
+ */
861
+ export const deployBsv21Auth = {
862
+ meta: {
863
+ name: 'deployBsv21Auth',
864
+ description: 'Deploy a new BSV21 token with mintable supply via auth UTXOs (deploy+auth)',
865
+ category: 'tokens',
866
+ inputSchema: {
867
+ type: 'object',
868
+ properties: {
869
+ symbol: { type: 'string', description: 'Token symbol/ticker' },
870
+ decimals: {
871
+ type: 'integer',
872
+ description: 'Decimal places (0-18, default 0)',
873
+ },
874
+ icon: { type: 'string', description: 'Icon URL or data URI' },
875
+ destination: {
876
+ type: 'object',
877
+ description: 'Auth-holder destination. One of lockingScript (hex), counterparty (pubkey), or address. Defaults to self.',
878
+ },
879
+ },
880
+ required: ['symbol'],
881
+ },
882
+ },
883
+ async execute(ctx, input) {
884
+ try {
885
+ const { symbol, decimals = 0, icon, destination } = input;
886
+ const resolved = await resolveDestination(ctx, destination, {
887
+ protocolID: BSV21_PROTOCOL,
888
+ keyIDPrefix: `bsv21-auth-${symbol}`,
889
+ });
890
+ const deployScript = BSV21.deployAuth(symbol, decimals, icon).lock(resolved.lockingScript);
891
+ const result = await executeBsv21Deploy({
892
+ ctx,
893
+ symbol,
894
+ deployScript,
895
+ destinationCustomInstructions: resolved.customInstructions,
896
+ basket: BSV21_AUTH_BASKET,
897
+ description: `Deploy ${symbol} (mintable)`,
898
+ outputDescription: `Deploy ${symbol} auth`,
899
+ buildTags: (tokenId) => {
900
+ const tags = [`bsv21:${tokenId}`, `dec:${decimals}`, `sym:${symbol}`];
901
+ if (icon)
902
+ tags.push(`icon:${icon}`);
903
+ return tags;
904
+ },
905
+ });
906
+ if (result.error)
907
+ return { error: result.error };
908
+ if (result.tx && ctx.services) {
909
+ try {
910
+ await ctx.services.overlay.submitBsv21Discovery(result.tx);
911
+ }
912
+ catch (overlayError) {
913
+ console.warn('[deployBsv21Auth] Overlay submission failed:', overlayError);
914
+ }
915
+ }
916
+ if (ctx.debug && ctx.log) {
917
+ ctx.log({
918
+ timestamp: new Date().toISOString(),
919
+ action: 'deployBsv21Auth',
920
+ input: { symbol, decimals, icon, destination },
921
+ txid: result.txid,
922
+ rawtx: result.tx ? Utils.toHex(result.tx) : undefined,
923
+ outputs: [
924
+ {
925
+ index: 0,
926
+ protocolID: BSV21_PROTOCOL,
927
+ keyID: resolved.customInstructions?.keyID,
928
+ basket: BSV21_AUTH_BASKET,
929
+ satoshis: 1,
930
+ },
931
+ ],
932
+ });
933
+ }
934
+ // For deploy+auth, the deploy output IS the first auth UTXO.
935
+ return {
936
+ ...result,
937
+ authOutpoint: result.tokenId,
938
+ };
939
+ }
940
+ catch (error) {
941
+ console.error('[deployBsv21Auth]', error);
942
+ if (ctx.debug && ctx.log) {
943
+ ctx.log({
944
+ timestamp: new Date().toISOString(),
945
+ action: 'deployBsv21Auth',
946
+ input: { symbol: input.symbol },
947
+ error: error instanceof Error ? error.message : 'unknown-error',
948
+ });
949
+ }
950
+ return {
951
+ error: error instanceof Error ? error.message : 'unknown-error',
952
+ };
953
+ }
954
+ },
955
+ };
956
+ /**
957
+ * Spend an auth UTXO to mint new supply, transfer authority, or burn it.
958
+ *
959
+ * Both `mint` and `auth` are optional, but at least one must be present.
960
+ * Omitting `auth` ends minting permanently and requires `endMinting: true`
961
+ * as explicit confirmation.
962
+ */
963
+ export const mintBsv21 = {
964
+ meta: {
965
+ name: 'mintBsv21',
966
+ description: 'Spend an auth UTXO to mint new supply and/or re-issue authority',
967
+ category: 'tokens',
968
+ requiresServices: true,
969
+ inputSchema: {
970
+ type: 'object',
971
+ properties: {
972
+ tokenId: { type: 'string', description: 'Token ID (txid_vout format)' },
973
+ mint: {
974
+ type: 'object',
975
+ description: 'Optional mint output: { amount, destination }',
976
+ },
977
+ auth: {
978
+ type: 'object',
979
+ description: 'Optional continuing auth output: { destination }',
980
+ },
981
+ endMinting: {
982
+ type: 'boolean',
983
+ description: 'Required when auth is omitted — explicit confirmation that minting ends.',
984
+ },
985
+ },
986
+ required: ['tokenId'],
987
+ },
988
+ },
989
+ async execute(ctx, input) {
990
+ try {
991
+ const { tokenId, mint, auth, endMinting } = input;
992
+ if (!mint && !auth) {
993
+ return { error: 'must-provide-mint-or-auth' };
994
+ }
995
+ if (!auth && !endMinting) {
996
+ return { error: 'omitting-auth-requires-endMinting-true' };
997
+ }
998
+ const mintAmount = mint?.amount !== undefined
999
+ ? typeof mint.amount === 'string'
1000
+ ? BigInt(mint.amount)
1001
+ : mint.amount
1002
+ : 0n;
1003
+ if (mint && mintAmount <= 0n) {
1004
+ return { error: 'mint-amount-must-be-positive' };
1005
+ }
1006
+ if (!ctx.services) {
1007
+ return { error: 'services-required' };
1008
+ }
1009
+ // Look up the token's metadata for tag enrichment.
1010
+ const tokenDetails = await ctx.services.bsv21.getTokenDetails(tokenId);
1011
+ // Find a spendable auth UTXO for this token.
1012
+ const authList = await ctx.wallet.listOutputs({
1013
+ basket: BSV21_AUTH_BASKET,
1014
+ includeTags: true,
1015
+ includeCustomInstructions: true,
1016
+ include: 'entire transactions',
1017
+ limit: 1000,
1018
+ });
1019
+ const authUtxo = authList.outputs.find((o) => o.tags?.includes(`bsv21:${tokenId}`));
1020
+ if (!authUtxo) {
1021
+ return { error: 'no-auth-utxo-for-token' };
1022
+ }
1023
+ if (!authUtxo.customInstructions) {
1024
+ return { error: 'auth-utxo-missing-custom-instructions' };
1025
+ }
1026
+ const authCI = JSON.parse(authUtxo.customInstructions);
1027
+ // Build outputs.
1028
+ const outputs = [];
1029
+ let authOutputIndex;
1030
+ if (mint) {
1031
+ const mintResolved = await resolveDestination(ctx, mint.destination, {
1032
+ protocolID: BSV21_PROTOCOL,
1033
+ keyIDPrefix: `bsv21-mint-${tokenId}`,
1034
+ });
1035
+ const mintScript = BSV21.mint(tokenId, mintAmount).lock(mintResolved.lockingScript);
1036
+ const mintTags = [
1037
+ `bsv21:${tokenId}`,
1038
+ `amt:${mintAmount}`,
1039
+ `dec:${tokenDetails.token.dec ?? 0}`,
1040
+ ...(tokenDetails.token.sym
1041
+ ? [`sym:${tokenDetails.token.sym}`]
1042
+ : []),
1043
+ ...(tokenDetails.token.icon
1044
+ ? [`icon:${tokenDetails.token.icon}`]
1045
+ : []),
1046
+ ];
1047
+ const mintCI = mintResolved.customInstructions
1048
+ ? JSON.stringify({
1049
+ protocolID: mintResolved.customInstructions.protocolID,
1050
+ keyID: mintResolved.customInstructions.keyID,
1051
+ ...(tokenDetails.token.sym && { sym: tokenDetails.token.sym }),
1052
+ })
1053
+ : undefined;
1054
+ outputs.push({
1055
+ lockingScript: mintScript.toHex(),
1056
+ satoshis: 1,
1057
+ outputDescription: `Mint ${mintAmount} tokens`,
1058
+ basket: BSV21_BASKET,
1059
+ tags: mintTags,
1060
+ customInstructions: mintCI,
1061
+ });
1062
+ }
1063
+ if (auth) {
1064
+ const authResolved = await resolveDestination(ctx, auth.destination, {
1065
+ protocolID: BSV21_PROTOCOL,
1066
+ keyIDPrefix: `bsv21-auth-${tokenId}`,
1067
+ });
1068
+ const authScript = BSV21.auth(tokenId).lock(authResolved.lockingScript);
1069
+ const authTags = [
1070
+ `bsv21:${tokenId}`,
1071
+ `dec:${tokenDetails.token.dec ?? 0}`,
1072
+ ...(tokenDetails.token.sym
1073
+ ? [`sym:${tokenDetails.token.sym}`]
1074
+ : []),
1075
+ ...(tokenDetails.token.icon
1076
+ ? [`icon:${tokenDetails.token.icon}`]
1077
+ : []),
1078
+ ];
1079
+ const authCustomInstructions = authResolved.customInstructions
1080
+ ? JSON.stringify({
1081
+ protocolID: authResolved.customInstructions.protocolID,
1082
+ keyID: authResolved.customInstructions.keyID,
1083
+ ...(tokenDetails.token.sym && { sym: tokenDetails.token.sym }),
1084
+ })
1085
+ : undefined;
1086
+ authOutputIndex = outputs.length;
1087
+ outputs.push({
1088
+ lockingScript: authScript.toHex(),
1089
+ satoshis: 1,
1090
+ outputDescription: 'Continuing mint authority',
1091
+ basket: BSV21_AUTH_BASKET,
1092
+ tags: authTags,
1093
+ customInstructions: authCustomInstructions,
1094
+ });
1095
+ }
1096
+ // Optional fee output to overlay fund address (per token output).
1097
+ const tokenOutputCount = (mint ? 1 : 0) + (auth ? 1 : 0);
1098
+ if (tokenDetails.status.is_active &&
1099
+ tokenDetails.status.fee_per_output > 0) {
1100
+ outputs.push({
1101
+ lockingScript: new P2PKH()
1102
+ .lock(tokenDetails.status.fee_address)
1103
+ .toHex(),
1104
+ satoshis: tokenDetails.status.fee_per_output * tokenOutputCount,
1105
+ outputDescription: 'Overlay processing fee',
1106
+ tags: [],
1107
+ });
1108
+ }
1109
+ let inputBEEF = authList.BEEF;
1110
+ if (!inputBEEF || inputBEEF.length === 0) {
1111
+ const beef = await ctx.services.getBeefForTxid(authUtxo.outpoint.split('.')[0]);
1112
+ inputBEEF = beef.toBinary();
1113
+ }
1114
+ const symbol = tokenDetails.token.sym || tokenId.slice(0, 8);
1115
+ const result = await executeTrackedAction(ctx.wallet, {
1116
+ description: mint
1117
+ ? `Mint ${mintAmount} ${symbol}`
1118
+ : `Re-issue ${symbol} authority`,
1119
+ labels: [`bsv21:${tokenId}`],
1120
+ inputBEEF,
1121
+ inputs: [
1122
+ {
1123
+ outpoint: authUtxo.outpoint,
1124
+ inputDescription: 'Mint authority',
1125
+ unlockingScriptLength: 108,
1126
+ },
1127
+ ],
1128
+ outputs,
1129
+ options: { randomizeOutputs: false },
1130
+ }, input.fundingProvider, inputBEEF, async (tx) => {
1131
+ const unlocking = await signP2PKHInput(ctx, tx, 0, authCI.protocolID, authCI.keyID);
1132
+ if (typeof unlocking !== 'string')
1133
+ throw new Error(unlocking.error);
1134
+ return { 0: { unlockingScript: unlocking } };
1135
+ });
1136
+ if (result.error)
1137
+ return { error: result.error };
1138
+ // Submit to per-token overlay topic.
1139
+ if (result.tx && ctx.services) {
1140
+ try {
1141
+ await ctx.services.overlay.submitBsv21(result.tx, tokenId);
1142
+ }
1143
+ catch (overlayError) {
1144
+ console.warn('[mintBsv21] Overlay submission failed:', overlayError);
1145
+ }
1146
+ }
1147
+ const authOutpoint = result.txid && authOutputIndex !== undefined
1148
+ ? `${result.txid}_${authOutputIndex}`
1149
+ : undefined;
1150
+ if (ctx.debug && ctx.log) {
1151
+ ctx.log({
1152
+ timestamp: new Date().toISOString(),
1153
+ action: 'mintBsv21',
1154
+ input: { tokenId, mint, auth, endMinting },
1155
+ txid: result.txid,
1156
+ rawtx: result.tx ? Utils.toHex(result.tx) : undefined,
1157
+ });
1158
+ }
1159
+ return {
1160
+ txid: result.txid,
1161
+ tx: result.tx,
1162
+ authOutpoint,
1163
+ };
1164
+ }
1165
+ catch (error) {
1166
+ console.error('[mintBsv21]', error);
1167
+ if (ctx.debug && ctx.log) {
1168
+ ctx.log({
1169
+ timestamp: new Date().toISOString(),
1170
+ action: 'mintBsv21',
1171
+ input: { tokenId: input.tokenId },
1172
+ error: error instanceof Error ? error.message : 'unknown-error',
1173
+ });
1174
+ }
1175
+ return {
1176
+ error: error instanceof Error ? error.message : 'unknown-error',
1177
+ };
1178
+ }
1179
+ },
1180
+ };
640
1181
  // ============================================================================
641
1182
  // Module exports
642
1183
  // ============================================================================
@@ -646,5 +1187,8 @@ export const tokensActions = [
646
1187
  getBsv21Balances,
647
1188
  sendBsv21,
648
1189
  purchaseBsv21,
1190
+ deployBsv21Mint,
1191
+ deployBsv21Auth,
1192
+ mintBsv21,
649
1193
  ];
650
1194
  //# sourceMappingURL=index.js.map