├── .gitattributes ├── .gitignore ├── README.md ├── contract ├── mint.cash └── mint.json ├── generateContract.ts ├── invoke-payout.ts ├── minting-setup.ts ├── mintingParams.ts ├── package.json └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | *.cash linguist-language=Solidity -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # environment variables 2 | .env 3 | 4 | # dependencies 5 | /node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BCH Minting Contract 2 | 3 | Multi-threaded minting smart contract for the Bitcoin Cash network. 4 | 5 | The CashScript code for the smart contract is `contract/mint.cash` and the corresponding artifact `contract/mint.json`. 6 | 7 | The repo also includes helper functions to set up a smart contract with `minting-setup.ts` and to claim payouts with `invoke-payout.ts`. 8 | 9 | ## Contract Design 10 | 11 | ### Multi-threaded 12 | 13 | The contract design is multi-threaded meaning multiple contract UTXOs running in parallel. This set up enables multiple simultaneous mint interactions. Clients can randomly pick one of the threads or an application server can manage idle/active UTXOs. 14 | 15 | This design also means the mint is not actually sequential, i.e. NFT number 16 can be minted before NFT number 12, depending on how much each of the different threads is used. 16 | 17 | ### Practical 18 | 19 | The minting contract is designed to be practical. 20 | For example the smart contract separates the NFT output to the user from their change output to ensure compatibility with different CashTokens wallets. 21 | The contract also has a separate payout function instead of having a payout output on each minting transaction, which would create thousands of small payout outputs. 22 | Lastly, it burns the minting NFTs when the contract ends, this way there are no inactive minting NFTs remaining at the end of the contract. 23 | 24 | ### Optimized & Clean 25 | 26 | The minting contract is optimized to be minimal in size (the redeem script is only 163 bytes). 27 | For example, the `tokenId` is not included as a contract constructor argument which saves ~30 bytes in contract size. 28 | The size for the different txs are as follows: 29 | 30 | - Minting transaction, no change ~505 bytes 31 | - Minting transaction, with change ~539 bytes 32 | - Payout transaction, continue mint ~430 bytes 33 | - Payout transaction, end mint ~350 bytes 34 | 35 | The contract code itself is clean & commented as to easily be auditable and serve as an example for contract creators. 36 | The contract is flexible, it allows for an optional change output in `mintNFT` and does not impose fixed, hardcoded miner fees. 37 | The contract combines `burnMintingNft` functionality in a minimal way into the `payout` function. 38 | 39 | ## Installation 40 | 41 | ``` 42 | git clone git@github.com:cashninjas/minting-contract.git 43 | yarn install 44 | ``` 45 | 46 | ## Usage 47 | 48 | You need to create a minting NFT with a CashTokens wallet in advance and you need to separate the authChain so you can manage your NFT collection's metadata. 49 | For more info about CashTokens metadata, see the [bcmr specification](https://github.com/bitjson/chip-bcmr#zeroth-descendant-transaction-chains). 50 | 51 | Next, to set up a minting contract, configure the parameters of the mint in `mintingParams.ts`: 52 | 53 | ```js 54 | { 55 | tokenId: "", 56 | collectionSize: 10_000, 57 | mintPriceSats: 5_000_000, 58 | payoutAddres: "", // with bitcoincash: or bchtest: prefix 59 | numberOfThreads: 5, 60 | network : "chipnet" 61 | } 62 | ``` 63 | To actually do the minting setup on-chain, configure the wallet holding the minting NFT and a little BCH in a `.env` file. 64 | Example .env file: 65 | 66 | ```bash 67 | SEEDPHRASE_SETUP = "" 68 | DERIVATIONPATH_SETUP = "m/44'/145'/0'/0/0" 69 | 70 | SEEDPHRASE_PAYOUT = "" 71 | DERIVATIONPATH_PAYOUT = "m/44'/145'/0'/0/0" 72 | ``` 73 | 74 | You need to provide the correct `DERIVATIONPATH_SETUP` to an adrress so mainnet-js can initialize a single address wallet. 75 | For example if your minting NFT is at address index 4 in your Electron Cash, change `m/44'/145'/0'/0/0` to `m/44'/145'/0'/0/4`. 76 | 77 | Then create the minting set up with 78 | 79 | ``` 80 | tsx minting-setup.ts 81 | ``` 82 | 83 | This will create the different threads for the minting contract with the CashScript advanced TransactionBuilder. 84 | 85 | Each tread is a UTXO on the smart contract address with a minting NFT. 86 | The minting NFTs each have a different starting commitment, starting from the VMnumber zero (an empty commitment). 87 | 88 | To invoke the payouts from the minting contract, configure the wallet authorized to claim the payout in the `.env` file. 89 | 90 | Then claim the contract payouts with 91 | 92 | ``` 93 | tsx invoke-payout.ts 94 | ``` 95 | -------------------------------------------------------------------------------- /contract/mint.cash: -------------------------------------------------------------------------------- 1 | pragma cashscript ^0.10.0; 2 | 3 | // Multi-threaded minting smart contract 4 | 5 | // Contract holds the next NFT nummber to mint as state in the commitment field of the minting NFT 6 | // Contract consists of a mintNFT and a payout function 7 | 8 | // Opcode count: 84 (max 201) 9 | // Bytesize: 163 (max 520) 10 | 11 | contract Mint( 12 | int mintPrice, 13 | int increment, 14 | bytes20 pkhPayout, 15 | int maximumCount 16 | ) { 17 | function mintNFT() { 18 | // require minting contract to be at input index zero 19 | require(this.activeInputIndex == 0); 20 | 21 | // Read nftNumber from contract commitment 22 | bytes commitment = tx.inputs[0].nftCommitment; 23 | int nftNumber = int(commitment); 24 | 25 | // Check if minting is still allowed 26 | require(nftNumber <= maximumCount); 27 | 28 | // Limit the max number of outputs to 3 29 | require(tx.outputs.length <= 3); 30 | 31 | // Output#0 preserves the NFT minting contract with a minting nft holding the new state and increased BCH value 32 | require(tx.outputs[0].lockingBytecode == tx.inputs[0].lockingBytecode); 33 | require(tx.outputs[0].tokenCategory == tx.inputs[0].tokenCategory); 34 | int nextNftNumber = nftNumber + increment; 35 | require(tx.outputs[0].nftCommitment == bytes(nextNftNumber)); 36 | require(tx.outputs[0].value == tx.inputs[0].value + mintPrice); 37 | 38 | // Output#1 for the minted NFT 39 | require(tx.outputs[1].value == 1000); 40 | require(tx.outputs[1].nftCommitment == bytes(nftNumber)); 41 | // Strip capability to get the tokenId for an immutable NFT 42 | bytes tokenId = tx.inputs[0].tokenCategory.split(32)[0]; 43 | require(tx.outputs[1].tokenCategory == tokenId); 44 | 45 | // Allow for BCH change output 46 | if(tx.outputs.length == 3){ 47 | // Output#2 BCH change output for minter 48 | require(tx.outputs[2].tokenCategory == 0x); 49 | } 50 | } 51 | function payout(sig sigPayout, pubkey pkPayout) { 52 | // Check the signature & public key against pkhPayout 53 | require(hash160(pkPayout) == pkhPayout); 54 | require(checkSig(sigPayout, pkPayout)); 55 | 56 | // require minting contract to be at input index zero 57 | require(tx.inputs.length == 1); 58 | 59 | // Read count from contract commitment 60 | bytes commitment = tx.inputs[0].nftCommitment; 61 | int nftNumber = int(commitment); 62 | 63 | // Check if minting is still ongoing 64 | if(nftNumber <= maximumCount){ 65 | // Limit the number of outputs to 2 66 | require(tx.outputs.length == 2); 67 | 68 | // Output#0 preserves the NFT minting contract with same minting nft 69 | require(tx.outputs[0].lockingBytecode == tx.inputs[0].lockingBytecode); 70 | require(tx.outputs[0].tokenCategory == tx.inputs[0].tokenCategory); 71 | require(tx.outputs[0].nftCommitment == tx.inputs[0].nftCommitment); 72 | 73 | // Output#1 payout output 74 | require(tx.outputs[1].tokenCategory == 0x); 75 | } else { 76 | // Output#0 payout output 77 | require(tx.outputs.length == 1); 78 | 79 | // Burns minting NFT 80 | require(tx.outputs[0].tokenCategory == 0x); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /contract/mint.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Mint", 3 | "constructorInputs": [ 4 | { 5 | "name": "mintPrice", 6 | "type": "int" 7 | }, 8 | { 9 | "name": "increment", 10 | "type": "int" 11 | }, 12 | { 13 | "name": "pkhPayout", 14 | "type": "bytes20" 15 | }, 16 | { 17 | "name": "maximumCount", 18 | "type": "int" 19 | } 20 | ], 21 | "abi": [ 22 | { 23 | "name": "mintNFT", 24 | "inputs": [] 25 | }, 26 | { 27 | "name": "payout", 28 | "inputs": [ 29 | { 30 | "name": "sigPayout", 31 | "type": "sig" 32 | }, 33 | { 34 | "name": "pkPayout", 35 | "type": "pubkey" 36 | } 37 | ] 38 | } 39 | ], 40 | "bytecode": "OP_4 OP_PICK OP_0 OP_NUMEQUAL OP_IF OP_INPUTINDEX OP_0 OP_NUMEQUALVERIFY OP_0 OP_UTXOTOKENCOMMITMENT OP_BIN2NUM OP_DUP OP_5 OP_ROLL OP_LESSTHANOREQUAL OP_VERIFY OP_TXOUTPUTCOUNT OP_3 OP_LESSTHANOREQUAL OP_VERIFY OP_0 OP_OUTPUTBYTECODE OP_0 OP_UTXOBYTECODE OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_UTXOTOKENCATEGORY OP_EQUALVERIFY OP_DUP OP_3 OP_ROLL OP_ADD OP_0 OP_OUTPUTTOKENCOMMITMENT OP_EQUALVERIFY OP_0 OP_OUTPUTVALUE OP_0 OP_UTXOVALUE OP_3 OP_ROLL OP_ADD OP_NUMEQUALVERIFY OP_1 OP_OUTPUTVALUE e803 OP_NUMEQUALVERIFY OP_1 OP_OUTPUTTOKENCOMMITMENT OP_EQUALVERIFY OP_0 OP_UTXOTOKENCATEGORY 20 OP_SPLIT OP_DROP OP_1 OP_OUTPUTTOKENCATEGORY OP_EQUALVERIFY OP_TXOUTPUTCOUNT OP_3 OP_NUMEQUAL OP_IF OP_2 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ENDIF OP_2DROP OP_1 OP_ELSE OP_4 OP_ROLL OP_1 OP_NUMEQUALVERIFY OP_5 OP_PICK OP_HASH160 OP_3 OP_ROLL OP_EQUALVERIFY OP_3 OP_ROLL OP_4 OP_ROLL OP_CHECKSIGVERIFY OP_TXINPUTCOUNT OP_1 OP_NUMEQUALVERIFY OP_0 OP_UTXOTOKENCOMMITMENT OP_BIN2NUM OP_3 OP_ROLL OP_LESSTHANOREQUAL OP_IF OP_TXOUTPUTCOUNT OP_2 OP_NUMEQUALVERIFY OP_0 OP_OUTPUTBYTECODE OP_0 OP_UTXOBYTECODE OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_UTXOTOKENCATEGORY OP_EQUALVERIFY OP_0 OP_OUTPUTTOKENCOMMITMENT OP_0 OP_UTXOTOKENCOMMITMENT OP_EQUALVERIFY OP_1 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ELSE OP_TXOUTPUTCOUNT OP_1 OP_NUMEQUALVERIFY OP_0 OP_OUTPUTTOKENCATEGORY OP_0 OP_EQUALVERIFY OP_ENDIF OP_2DROP OP_1 OP_ENDIF", 41 | "source": "pragma cashscript ^0.10.0;\r\n\r\n// Multi-threaded minting smart contract\r\n\r\n// Contract holds the next NFT nummber to mint as state in the commitment field of the minting NFT\r\n// Contract consists of a mintNFT and a payout function\r\n\r\n// Opcode count: 84 (max 201)\r\n// Bytesize: 163 (max 520)\r\n\r\ncontract Mint(\r\n int mintPrice,\r\n int increment,\r\n bytes20 pkhPayout,\r\n int maximumCount\r\n) {\r\n function mintNFT() {\r\n // require minting contract to be at input index zero\r\n require(this.activeInputIndex == 0);\r\n\r\n // Read nftNumber from contract commitment\r\n bytes commitment = tx.inputs[0].nftCommitment;\r\n int nftNumber = int(commitment);\r\n \r\n // Check if minting is still allowed\r\n require(nftNumber <= maximumCount);\r\n\r\n // Limit the max number of outputs to 3\r\n require(tx.outputs.length <= 3);\r\n \r\n // Output#0 preserves the NFT minting contract with a minting nft holding the new state and increased BCH value\r\n require(tx.outputs[0].lockingBytecode == tx.inputs[0].lockingBytecode);\r\n require(tx.outputs[0].tokenCategory == tx.inputs[0].tokenCategory);\r\n int nextNftNumber = nftNumber + increment;\r\n require(tx.outputs[0].nftCommitment == bytes(nextNftNumber));\r\n require(tx.outputs[0].value == tx.inputs[0].value + mintPrice);\r\n\r\n // Output#1 for the minted NFT\r\n require(tx.outputs[1].value == 1000);\r\n require(tx.outputs[1].nftCommitment == bytes(nftNumber));\r\n // Strip capability to get the tokenId for an immutable NFT\r\n bytes tokenId = tx.inputs[0].tokenCategory.split(32)[0];\r\n require(tx.outputs[1].tokenCategory == tokenId);\r\n\r\n // Allow for BCH change output\r\n if(tx.outputs.length == 3){\r\n // Output#2 BCH change output for minter\r\n require(tx.outputs[2].tokenCategory == 0x);\r\n }\r\n }\r\n function payout(sig sigPayout, pubkey pkPayout) {\r\n // Check the signature & public key against pkhPayout\r\n require(hash160(pkPayout) == pkhPayout);\r\n require(checkSig(sigPayout, pkPayout));\r\n \r\n // require minting contract to be at input index zero\r\n require(tx.inputs.length == 1);\r\n\r\n // Read count from contract commitment\r\n bytes commitment = tx.inputs[0].nftCommitment;\r\n int nftNumber = int(commitment);\r\n\r\n // Check if minting is still ongoing\r\n if(nftNumber <= maximumCount){\r\n // Limit the number of outputs to 2\r\n require(tx.outputs.length == 2);\r\n \r\n // Output#0 preserves the NFT minting contract with same minting nft\r\n require(tx.outputs[0].lockingBytecode == tx.inputs[0].lockingBytecode);\r\n require(tx.outputs[0].tokenCategory == tx.inputs[0].tokenCategory);\r\n require(tx.outputs[0].nftCommitment == tx.inputs[0].nftCommitment);\r\n\r\n // Output#1 payout output\r\n require(tx.outputs[1].tokenCategory == 0x);\r\n } else {\r\n // Output#0 payout output\r\n require(tx.outputs.length == 1);\r\n\r\n // Burns minting NFT\r\n require(tx.outputs[0].tokenCategory == 0x);\r\n }\r\n }\r\n}\r\n", 42 | "debug": { 43 | "bytecode": "5479009c63c0009c6900cf007a810079557aa169c453a16900cd00c7876900d100ce87690079537a9300d2517a876900cc00c6537a939c6951cc02e8039c6951d2517a876900ce01207f7551d1517a8769c4539c6352d10087696851777767547a519c695579a9537a8769537a547aac69c3519c6900cf007a81007a537aa163c4529c6900cd00c7876900d100ce876900d200cf876951d100876967c4519c6900d10087696851777768", 44 | "sourceMap": "17:4:50:5;;;;;19:16:19:37;:41::42;:16:::1;:8::44;22:37:22:38:0;:27::53:1;23:28:23:38:0;;:24::39:1;26:16:26:25:0;;:29::41;;:16:::1;:8::43;29:16:29:33:0;:37::38;:16:::1;:8::40;32:27:32:28:0;:16::45:1;:59::60:0;:49::77:1;:16;:8::79;33:27:33:28:0;:16::43:1;:57::58:0;:47::73:1;:16;:8::75;34:28:34:37:0;;:40::49;;:28:::1;35:27:35:28:0;:16::43:1;:53::66:0;;:16::67:1;:8::69;36:27:36:28:0;:16::35:1;:49::50:0;:39::57:1;:60::69:0;;:39:::1;:16;:8::71;39:27:39:28:0;:16::35:1;:39::43:0;:16:::1;:8::45;40:27:40:28:0;:16::43:1;:53::62:0;;:16::63:1;:8::65;42:34:42:35:0;:24::50:1;:57::59:0;:24::60:1;:::63;43:27:43:28:0;:16::43:1;:47::54:0;;:16:::1;:8::56;46:11:46:28:0;:32::33;:11:::1;:34:49:9:0;48:31:48:32;:20::47:1;:51::53:0;:20:::1;:12::55;46:34:49:9;17:4:50:5;;;;51::82::0;;;;;53:24:53:32;;:16::33:1;:37::46:0;;:16:::1;:8::48;54:25:54:34:0;;:36::44;;:16::45:1;:8::47;57:16:57:32:0;:36::37;:16:::1;:8::39;60:37:60:38:0;:27::53:1;61:28:61:38:0;;:24::39:1;64:11:64:20:0;;:24::36;;:11:::1;:37:75:9:0;66:20:66:37;:41::42;:20:::1;:12::44;69:31:69:32:0;:20::49:1;:63::64:0;:53::81:1;:20;:12::83;70:31:70:32:0;:20::47:1;:61::62:0;:51::77:1;:20;:12::79;71:31:71:32:0;:20::47:1;:61::62:0;:51::77:1;:20;:12::79;74:31:74:32:0;:20::47:1;:51::53:0;:20:::1;:12::55;75:15:81:9:0;77:20:77:37;:41::42;:20:::1;:12::44;80:31:80:32:0;:20::47:1;:51::53:0;:20:::1;:12::55;75:15:81:9;51:4:82:5;;;11:0:83:1", 45 | "logs": [], 46 | "requires": [ 47 | { 48 | "ip": 12, 49 | "line": 19 50 | }, 51 | { 52 | "ip": 23, 53 | "line": 26 54 | }, 55 | { 56 | "ip": 27, 57 | "line": 29 58 | }, 59 | { 60 | "ip": 33, 61 | "line": 32 62 | }, 63 | { 64 | "ip": 39, 65 | "line": 33 66 | }, 67 | { 68 | "ip": 50, 69 | "line": 35 70 | }, 71 | { 72 | "ip": 59, 73 | "line": 36 74 | }, 75 | { 76 | "ip": 64, 77 | "line": 39 78 | }, 79 | { 80 | "ip": 70, 81 | "line": 40 82 | }, 83 | { 84 | "ip": 81, 85 | "line": 43 86 | }, 87 | { 88 | "ip": 90, 89 | "line": 48 90 | }, 91 | { 92 | "ip": 107, 93 | "line": 53 94 | }, 95 | { 96 | "ip": 113, 97 | "line": 54 98 | }, 99 | { 100 | "ip": 117, 101 | "line": 57 102 | }, 103 | { 104 | "ip": 132, 105 | "line": 66 106 | }, 107 | { 108 | "ip": 138, 109 | "line": 69 110 | }, 111 | { 112 | "ip": 144, 113 | "line": 70 114 | }, 115 | { 116 | "ip": 150, 117 | "line": 71 118 | }, 119 | { 120 | "ip": 155, 121 | "line": 74 122 | }, 123 | { 124 | "ip": 160, 125 | "line": 77 126 | }, 127 | { 128 | "ip": 165, 129 | "line": 80 130 | } 131 | ] 132 | }, 133 | "compiler": { 134 | "name": "cashc", 135 | "version": "0.10.0" 136 | }, 137 | "updatedAt": "2024-09-12T11:57:13.215Z" 138 | } -------------------------------------------------------------------------------- /generateContract.ts: -------------------------------------------------------------------------------- 1 | import { Contract, ElectrumNetworkProvider } from "cashscript"; 2 | import contractArtifact from "./contract/mint.json" with { type: "json" }; 3 | import { decodeCashAddress, binToHex } from "@bitauth/libauth"; 4 | import { collectionSize, mintPriceSats, payoutAddress, numberOfThreads, network } from "./mintingParams.js"; 5 | 6 | export function generateContract(){ 7 | // Convert payoutAddress to payoutLockingBytecode 8 | const addressInfo = decodeCashAddress(payoutAddress); 9 | if(typeof addressInfo == "string") throw new Error("Error in decodeCashAddress") 10 | const pkhPayout = binToHex(addressInfo.payload); 11 | 12 | // The array of parameters to use for generating the contract 13 | // maximumCount = collectionSize-1 because count starts from zero 14 | const contractParams = [ 15 | BigInt(mintPriceSats), 16 | BigInt(numberOfThreads), 17 | pkhPayout, 18 | BigInt(collectionSize - 1), 19 | ]; 20 | console.log('Generating a minting contract with the following params:\n' + contractParams); 21 | 22 | // Initialise a network provider for network operations 23 | const provider = new ElectrumNetworkProvider(network); 24 | 25 | // Instantiate a new minting contract 26 | const contract = new Contract(contractArtifact, contractParams, { provider }); 27 | console.log(`P2sh32 smart contract address is ${contract.address}`); 28 | 29 | return contract 30 | } -------------------------------------------------------------------------------- /invoke-payout.ts: -------------------------------------------------------------------------------- 1 | import { ElectrumNetworkProvider, Output, SignatureTemplate, TransactionBuilder } from "cashscript"; 2 | import { hexToBin, vmNumberToBigInt } from "@bitauth/libauth"; 3 | import { Wallet, TestNetWallet } from "mainnet-js"; 4 | import { tokenId, collectionSize, payoutAddress, network } from "./mintingParams.ts"; 5 | import { generateContract } from "./generateContract.js"; 6 | import 'dotenv/config'; 7 | 8 | // Get seedphrase + addressDerivationPath for invoke-payout from .env file 9 | const seedphrasePayout = process.env.SEEDPHRASE_PAYOUT as string; 10 | const addressDerivationPath = process.env.DERIVATIONPATH_PAYOUT; 11 | 12 | // Instantiate wallet 13 | const walletClass = network == "mainnet" ? Wallet : TestNetWallet; 14 | const wallet = await walletClass.fromSeed(seedphrasePayout, addressDerivationPath); 15 | const signatureTemplate = new SignatureTemplate(wallet.privateKeyWif); 16 | 17 | // Check if the right wallet is configured to invoke payouts 18 | if (wallet.cashaddr != payoutAddress) throw new Error("Provided wallet does not match Payout wallet (addresses don't match)") 19 | 20 | const contract = generateContract(); 21 | console.log('Total balance contracts:', await contract.getBalance()); 22 | 23 | const contractUtxos = await contract.getUtxos(); 24 | 25 | // Initialise a network provider for network operations 26 | const provider = new ElectrumNetworkProvider(network); 27 | 28 | for (const contractUtxo of contractUtxos) { 29 | // Filter UTXOs on smart contract address 30 | const isMintingUtxo = contractUtxo?.token?.category == tokenId && contractUtxo?.token?.nft?.capability == "minting"; 31 | if (!isMintingUtxo) continue 32 | 33 | const payoutAmount = contractUtxo.satoshis - 2000n; 34 | if (payoutAmount < 1000) continue 35 | 36 | const contractCommitment = contractUtxo.token?.nft?.commitment as string 37 | const contractMintingState = vmNumberToBigInt(hexToBin(contractCommitment)) 38 | if(typeof contractMintingState == "string") throw new Error("Error in vmNumberToBigInt") 39 | 40 | let newContractOutput: Output | undefined 41 | // Check commitment to see minting contract is ongoing 42 | if (contractMintingState < BigInt(collectionSize)){ 43 | newContractOutput = { 44 | to: contract.address, 45 | amount: 1000n, 46 | token: contractUtxo.token 47 | }; 48 | } 49 | 50 | const payoutOutput = { to: payoutAddress, amount: payoutAmount }; 51 | 52 | try { 53 | const transactionBuilder = new TransactionBuilder({ provider }) 54 | transactionBuilder.addInput(contractUtxo, contract.unlock.payout(signatureTemplate, signatureTemplate.getPublicKey())) 55 | // If mint is ongoing, need to recreate minting contract at payout 56 | if(newContractOutput) transactionBuilder.addOutput(newContractOutput); 57 | transactionBuilder.addOutput(payoutOutput); 58 | const { txid } = await transactionBuilder.send(); 59 | console.log(`Payout transaction of ${payoutAmount} satoshis succesfully sent! \ntxid: ${txid}`); 60 | } catch (error) { console.log(error) } 61 | } 62 | -------------------------------------------------------------------------------- /minting-setup.ts: -------------------------------------------------------------------------------- 1 | import { binToHex, bigIntToVmNumber } from "@bitauth/libauth"; 2 | import { ElectrumNetworkProvider, Output, SignatureTemplate, TransactionBuilder } from "cashscript"; 3 | import { Wallet, TestNetWallet } from "mainnet-js"; 4 | import { tokenId, numberOfThreads, network } from "./mintingParams.js"; 5 | import { generateContract } from "./generateContract.js"; 6 | import 'dotenv/config'; 7 | 8 | // Get seedphrase + addressDerivationPath for minting-setup from .env file 9 | const seedphraseSetup = process.env.SEEDPHRASE_SETUP as string; 10 | const addressDerivationPath = process.env.DERIVATIONPATH_SETUP; 11 | 12 | // Initialise wallet 13 | let walletClass = network == "mainnet" ? Wallet : TestNetWallet; 14 | const wallet = await walletClass.fromSeed(seedphraseSetup, addressDerivationPath); 15 | const signatureTemplate = new SignatureTemplate(wallet.privateKey); 16 | 17 | // Generate contract object 18 | const contract = generateContract(); 19 | 20 | // Create the different mintingThreads with CashScript transaction builder 21 | const mintingThreads: Output[] = []; 22 | for (let i = 0; i < numberOfThreads; i++) { 23 | const commitmentThread = binToHex(bigIntToVmNumber(BigInt(i))); 24 | const nftDetails = { capability: 'minting' as const, commitment: commitmentThread } 25 | const tokenDetails = { amount: 0n, category: tokenId, nft: nftDetails }; 26 | const threadOutput = { to: contract.address, amount: 1000n, token: tokenDetails }; 27 | mintingThreads.push(threadOutput); 28 | } 29 | 30 | // Use the selected inputs for the transaction 31 | const provider = new ElectrumNetworkProvider(network); 32 | const walletUtxos = await provider.getUtxos(wallet.cashaddr); 33 | const mintingUtxo = walletUtxos.find(utxo => 34 | utxo?.token?.category == tokenId && utxo?.token?.nft?.capability == 'minting' 35 | ); 36 | const fundingUtxo = walletUtxos.find(utxo => !utxo.token && utxo.satoshis >= 5000n); 37 | if(!mintingUtxo || !fundingUtxo) throw new Error("Error in mintingUtxo || fundingUtxo") 38 | const inputsMint = [ mintingUtxo, fundingUtxo ]; 39 | console.log(inputsMint); 40 | 41 | const transactionBuilder = new TransactionBuilder({ provider, maximumFeeSatoshis: 2000n }) 42 | .addInputs(inputsMint, signatureTemplate.unlockP2PKH()) 43 | .addOutputs(mintingThreads) 44 | 45 | const changeAmount = mintingUtxo.satoshis + fundingUtxo.satoshis - 1100n * BigInt(numberOfThreads) - 500n; 46 | if(changeAmount > 2000n) transactionBuilder.addOutput({to: wallet.cashaddr, amount: changeAmount}); 47 | try { 48 | const { txid } = await transactionBuilder.send(); 49 | console.log(`The txId for the minting set-up is ${txid}`); 50 | } catch (error) { 51 | console.log(error) 52 | } 53 | -------------------------------------------------------------------------------- /mintingParams.ts: -------------------------------------------------------------------------------- 1 | import { Network } from "cashscript"; 2 | 3 | // Configure minting params 4 | const tokenId = ""; 5 | const collectionSize = 10_000; 6 | const numberOfThreads = 5; 7 | const mintPriceSats = 5_000_000; 8 | const payoutAddress = ""; // with bitcoincash: or bchtest: prefix 9 | const network = "chipnet" as Network; 10 | 11 | export { tokenId, collectionSize, mintPriceSats, payoutAddress, numberOfThreads, network }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "minting-contract", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "", 6 | "type": "module", 7 | "scripts": { 8 | "generate": "tsx generateContract.ts", 9 | "setup": "tsx invoke-setup.ts", 10 | "payout": "tsx invoke-payout.ts" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/cashninjas/minting-contract.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/cashninjas/minting-contract/issues" 20 | }, 21 | "homepage": "https://github.com/cashninjas/minting-contract#readme", 22 | "dependencies": { 23 | "@bitauth/libauth": "^3.1.0-next.8", 24 | "cashscript": "^0.12.0", 25 | "dotenv": "^16.3.1", 26 | "mainnet-js": "^2.7.19", 27 | "tsx": "^4.20.6" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@bch-wc2/interfaces@^0.0.8": 6 | version "0.0.8" 7 | resolved "https://registry.yarnpkg.com/@bch-wc2/interfaces/-/interfaces-0.0.8.tgz#2f994aeaa370f2ca506f87fc23e0837e21b475d6" 8 | integrity sha512-gRt95azHVqoViGI4X3F1ObgTpYBh3//KaCHRBWSMJbAKNM09T+TA/h9kx9u9SZmE8otMtpFSy4awSHWHUQZmbg== 9 | 10 | "@bitauth/libauth@^3.1.0-next.2": 11 | version "3.1.0-next.7" 12 | resolved "https://registry.yarnpkg.com/@bitauth/libauth/-/libauth-3.1.0-next.7.tgz#efcca8d6ad8b33d7a7b600c42ea26f2edd251a00" 13 | integrity sha512-lJ+ZwDr5zIyO44XbHphdp/eEmIbd1D8gD6zbN8P2HLDDGSAE9rqUwXInWFGwaPKVR8cUbKK/MKSzbPCK+4hxDw== 14 | 15 | "@bitauth/libauth@^3.1.0-next.8": 16 | version "3.1.0-next.8" 17 | resolved "https://registry.yarnpkg.com/@bitauth/libauth/-/libauth-3.1.0-next.8.tgz#d130e5db6c3c8b24731c8d04c4091be07f48b0ee" 18 | integrity sha512-Pm+Ju+YP3JeBLLTiVrBnia2wwE4G17r4XqpvPRMcklElJTe8J6x3JgKRg1by0Xm3ZY6UFxACkEAoSA+x419/zA== 19 | 20 | "@cashscript/utils@^0.12.0": 21 | version "0.12.0" 22 | resolved "https://registry.yarnpkg.com/@cashscript/utils/-/utils-0.12.0.tgz#d4e96428ecb09792fb87970f4675214d7e54f409" 23 | integrity sha512-25sJQuEUBeZ3BwCRJ3bruqsyP/9P1VLy9gnqObB/Uz2rzW9OAmDQ5gDsx9ABywazoXmq5/mQ+gQBwlqt9qpgmA== 24 | dependencies: 25 | "@bitauth/libauth" "^3.1.0-next.8" 26 | 27 | "@electrum-cash/debug-logs@^1.0.0": 28 | version "1.0.0" 29 | resolved "https://registry.yarnpkg.com/@electrum-cash/debug-logs/-/debug-logs-1.0.0.tgz#7d021515f1b881f477176cb640f2178d094181e5" 30 | integrity sha512-GU/CvRR9lZ0d8gy9CXGW7f//OHCIydBavv9q+JcxjGj8Xr7HwlGqHx+Wzhx9y3YmJrXfExpgClcd++gTjdEmzA== 31 | dependencies: 32 | debug "^4.3.7" 33 | 34 | "@electrum-cash/network@^4.1.3": 35 | version "4.1.3" 36 | resolved "https://registry.yarnpkg.com/@electrum-cash/network/-/network-4.1.3.tgz#195a96e8bb34493c622223992da0649c753aafff" 37 | integrity sha512-amMvdcEfHhquoUkhN7x/H04KPYfqd5LilOGcg6O1OdUks1Mcrcah8WfHICHW/qyZ3Rgoos9o7Wx8gKz8qcSNzg== 38 | dependencies: 39 | "@electrum-cash/debug-logs" "^1.0.0" 40 | "@electrum-cash/web-socket" "^1.0.0" 41 | async-mutex "^0.5.0" 42 | debug "^4.3.2" 43 | eventemitter3 "^5.0.1" 44 | lossless-json "^4.0.1" 45 | 46 | "@electrum-cash/web-socket@^1.0.0": 47 | version "1.0.0" 48 | resolved "https://registry.yarnpkg.com/@electrum-cash/web-socket/-/web-socket-1.0.0.tgz#0ab57e46d41e941ffb57deff86afea3ae1379d5d" 49 | integrity sha512-+VQ6aPE7nUysyDn9SB7/uqKuVJmjrhvr0LRK2ANTeR1DfmXBnv5z29e/0zK5A7ZoAz0gdZZuLDzN46r8S5Cxig== 50 | dependencies: 51 | "@electrum-cash/debug-logs" "^1.0.0" 52 | "@monsterbitar/isomorphic-ws" "^5.3.0" 53 | "@types/ws" "^8.5.5" 54 | async-mutex "^0.5.0" 55 | eventemitter3 "^5.0.1" 56 | lossless-json "^4.0.1" 57 | ws "^8.13.0" 58 | 59 | "@esbuild/aix-ppc64@0.25.8": 60 | version "0.25.8" 61 | resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz#a1414903bb38027382f85f03dda6065056757727" 62 | integrity sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA== 63 | 64 | "@esbuild/android-arm64@0.25.8": 65 | version "0.25.8" 66 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz#c859994089e9767224269884061f89dae6fb51c6" 67 | integrity sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w== 68 | 69 | "@esbuild/android-arm@0.25.8": 70 | version "0.25.8" 71 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.8.tgz#96a8f2ca91c6cd29ea90b1af79d83761c8ba0059" 72 | integrity sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw== 73 | 74 | "@esbuild/android-x64@0.25.8": 75 | version "0.25.8" 76 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.8.tgz#a3a626c4fec4a024a9fa8c7679c39996e92916f0" 77 | integrity sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA== 78 | 79 | "@esbuild/darwin-arm64@0.25.8": 80 | version "0.25.8" 81 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz#a5e1252ca2983d566af1c0ea39aded65736fc66d" 82 | integrity sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw== 83 | 84 | "@esbuild/darwin-x64@0.25.8": 85 | version "0.25.8" 86 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz#5271b0df2bb12ce8df886704bfdd1c7cc01385d2" 87 | integrity sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg== 88 | 89 | "@esbuild/freebsd-arm64@0.25.8": 90 | version "0.25.8" 91 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz#d0a0e7fdf19733b8bb1566b81df1aa0bb7e46ada" 92 | integrity sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA== 93 | 94 | "@esbuild/freebsd-x64@0.25.8": 95 | version "0.25.8" 96 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz#2de8b2e0899d08f1cb1ef3128e159616e7e85343" 97 | integrity sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw== 98 | 99 | "@esbuild/linux-arm64@0.25.8": 100 | version "0.25.8" 101 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz#a4209efadc0c2975716458484a4e90c237c48ae9" 102 | integrity sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w== 103 | 104 | "@esbuild/linux-arm@0.25.8": 105 | version "0.25.8" 106 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz#ccd9e291c24cd8d9142d819d463e2e7200d25b19" 107 | integrity sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg== 108 | 109 | "@esbuild/linux-ia32@0.25.8": 110 | version "0.25.8" 111 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz#006ad1536d0c2b28fb3a1cf0b53bcb85aaf92c4d" 112 | integrity sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg== 113 | 114 | "@esbuild/linux-loong64@0.25.8": 115 | version "0.25.8" 116 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz#127b3fbfb2c2e08b1397e985932f718f09a8f5c4" 117 | integrity sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ== 118 | 119 | "@esbuild/linux-mips64el@0.25.8": 120 | version "0.25.8" 121 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz#837d1449517791e3fa7d82675a2d06d9f56cb340" 122 | integrity sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw== 123 | 124 | "@esbuild/linux-ppc64@0.25.8": 125 | version "0.25.8" 126 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz#aa2e3bd93ab8df084212f1895ca4b03c42d9e0fe" 127 | integrity sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ== 128 | 129 | "@esbuild/linux-riscv64@0.25.8": 130 | version "0.25.8" 131 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz#a340620e31093fef72767dd28ab04214b3442083" 132 | integrity sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg== 133 | 134 | "@esbuild/linux-s390x@0.25.8": 135 | version "0.25.8" 136 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz#ddfed266c8c13f5efb3105a0cd47f6dcd0e79e71" 137 | integrity sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg== 138 | 139 | "@esbuild/linux-x64@0.25.8": 140 | version "0.25.8" 141 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz#9a4f78c75c051e8c060183ebb39a269ba936a2ac" 142 | integrity sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ== 143 | 144 | "@esbuild/netbsd-arm64@0.25.8": 145 | version "0.25.8" 146 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz#902c80e1d678047926387230bc037e63e00697d0" 147 | integrity sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw== 148 | 149 | "@esbuild/netbsd-x64@0.25.8": 150 | version "0.25.8" 151 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz#2d9eb4692add2681ff05a14ce99de54fbed7079c" 152 | integrity sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg== 153 | 154 | "@esbuild/openbsd-arm64@0.25.8": 155 | version "0.25.8" 156 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz#89c3b998c6de739db38ab7fb71a8a76b3fa84a45" 157 | integrity sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ== 158 | 159 | "@esbuild/openbsd-x64@0.25.8": 160 | version "0.25.8" 161 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz#2f01615cf472b0e48c077045cfd96b5c149365cc" 162 | integrity sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ== 163 | 164 | "@esbuild/openharmony-arm64@0.25.8": 165 | version "0.25.8" 166 | resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz#a201f720cd2c3ebf9a6033fcc3feb069a54b509a" 167 | integrity sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg== 168 | 169 | "@esbuild/sunos-x64@0.25.8": 170 | version "0.25.8" 171 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz#07046c977985a3334667f19e6ab3a01a80862afb" 172 | integrity sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w== 173 | 174 | "@esbuild/win32-arm64@0.25.8": 175 | version "0.25.8" 176 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz#4a5470caf0d16127c05d4833d4934213c69392d1" 177 | integrity sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ== 178 | 179 | "@esbuild/win32-ia32@0.25.8": 180 | version "0.25.8" 181 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz#3de3e8470b7b328d99dbc3e9ec1eace207e5bbc4" 182 | integrity sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg== 183 | 184 | "@esbuild/win32-x64@0.25.8": 185 | version "0.25.8" 186 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz#610d7ea539d2fcdbe39237b5cc175eb2c4451f9c" 187 | integrity sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw== 188 | 189 | "@monsterbitar/isomorphic-ws@^5.3.0": 190 | version "5.3.1" 191 | resolved "https://registry.yarnpkg.com/@monsterbitar/isomorphic-ws/-/isomorphic-ws-5.3.1.tgz#acd6be86c7568682d8146f8b5fadbec74bf8789e" 192 | integrity sha512-BWfWUffbg3uO4K6Cyokg9ff43lPaXAOZcCnNe1lcjCjUMDVrRAb5qEHG5qeJp3ud2SPYbORaNsls5as6SR3oig== 193 | 194 | "@mr-zwets/bchn-api-wrapper@^1.0.1": 195 | version "1.0.1" 196 | resolved "https://registry.yarnpkg.com/@mr-zwets/bchn-api-wrapper/-/bchn-api-wrapper-1.0.1.tgz#1ecd9fca91ed7e33df9769e243ae04871d2d356f" 197 | integrity sha512-EyKT6zgXh31JqTRsi+KCX04WPDjIqs80nfsafrKiUZTg8FgWzql1wSyYdggmeByB74yLHD0LouC40S8PMmvcZA== 198 | 199 | "@noble/hashes@~1.5.0": 200 | version "1.5.0" 201 | resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" 202 | integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== 203 | 204 | "@scure/base@~1.1.8": 205 | version "1.1.8" 206 | resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.8.tgz#8f23646c352f020c83bca750a82789e246d42b50" 207 | integrity sha512-6CyAclxj3Nb0XT7GHK6K4zK6k2xJm6E4Ft0Ohjt4WgegiFUHEtFb2CGzmPmGBwoIhrLsqNLYfLr04Y1GePrzZg== 208 | 209 | "@scure/bip39@^1.2.2": 210 | version "1.4.0" 211 | resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.4.0.tgz#664d4f851564e2e1d4bffa0339f9546ea55960a6" 212 | integrity sha512-BEEm6p8IueV/ZTfQLp/0vhw4NPnT9oWf5+28nvmeUICjP99f4vr2d+qc7AVGDDtwRep6ifR43Yed9ERVmiITzw== 213 | dependencies: 214 | "@noble/hashes" "~1.5.0" 215 | "@scure/base" "~1.1.8" 216 | 217 | "@types/node@*": 218 | version "22.5.4" 219 | resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.4.tgz#83f7d1f65bc2ed223bdbf57c7884f1d5a4fa84e8" 220 | integrity sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg== 221 | dependencies: 222 | undici-types "~6.19.2" 223 | 224 | "@types/ws@^8.5.5": 225 | version "8.18.1" 226 | resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" 227 | integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== 228 | dependencies: 229 | "@types/node" "*" 230 | 231 | async-mutex@^0.5.0: 232 | version "0.5.0" 233 | resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.5.0.tgz#353c69a0b9e75250971a64ac203b0ebfddd75482" 234 | integrity sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA== 235 | dependencies: 236 | tslib "^2.4.0" 237 | 238 | base64-js@^1.3.1: 239 | version "1.5.1" 240 | resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" 241 | integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== 242 | 243 | buffer@^6.0.3: 244 | version "6.0.3" 245 | resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" 246 | integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== 247 | dependencies: 248 | base64-js "^1.3.1" 249 | ieee754 "^1.2.1" 250 | 251 | cashscript@^0.12.0: 252 | version "0.12.0" 253 | resolved "https://registry.yarnpkg.com/cashscript/-/cashscript-0.12.0.tgz#66225524eb94fa657b4c68ac90e786d4348a848b" 254 | integrity sha512-pdrBjdiBka8g/jVCtaku08R2xoFCMo2GG/pdVf/z+b95Yd0H1ioFSMlp9mx1xuxeYbvZx85ZGk643CAFm+Kzgg== 255 | dependencies: 256 | "@bitauth/libauth" "^3.1.0-next.8" 257 | "@cashscript/utils" "^0.12.0" 258 | "@electrum-cash/network" "^4.1.3" 259 | "@mr-zwets/bchn-api-wrapper" "^1.0.1" 260 | pako "^2.1.0" 261 | semver "^7.7.2" 262 | 263 | debug@^4.3.2: 264 | version "4.3.7" 265 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" 266 | integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== 267 | dependencies: 268 | ms "^2.1.3" 269 | 270 | debug@^4.3.7: 271 | version "4.4.1" 272 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.1.tgz#e5a8bc6cbc4c6cd3e64308b0693a3d4fa550189b" 273 | integrity sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ== 274 | dependencies: 275 | ms "^2.1.3" 276 | 277 | dotenv@^10.0.0: 278 | version "10.0.0" 279 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" 280 | integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== 281 | 282 | dotenv@^16.3.1: 283 | version "16.4.5" 284 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" 285 | integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== 286 | 287 | esbuild@~0.25.0: 288 | version "0.25.8" 289 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.8.tgz#482d42198b427c9c2f3a81b63d7663aecb1dda07" 290 | integrity sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q== 291 | optionalDependencies: 292 | "@esbuild/aix-ppc64" "0.25.8" 293 | "@esbuild/android-arm" "0.25.8" 294 | "@esbuild/android-arm64" "0.25.8" 295 | "@esbuild/android-x64" "0.25.8" 296 | "@esbuild/darwin-arm64" "0.25.8" 297 | "@esbuild/darwin-x64" "0.25.8" 298 | "@esbuild/freebsd-arm64" "0.25.8" 299 | "@esbuild/freebsd-x64" "0.25.8" 300 | "@esbuild/linux-arm" "0.25.8" 301 | "@esbuild/linux-arm64" "0.25.8" 302 | "@esbuild/linux-ia32" "0.25.8" 303 | "@esbuild/linux-loong64" "0.25.8" 304 | "@esbuild/linux-mips64el" "0.25.8" 305 | "@esbuild/linux-ppc64" "0.25.8" 306 | "@esbuild/linux-riscv64" "0.25.8" 307 | "@esbuild/linux-s390x" "0.25.8" 308 | "@esbuild/linux-x64" "0.25.8" 309 | "@esbuild/netbsd-arm64" "0.25.8" 310 | "@esbuild/netbsd-x64" "0.25.8" 311 | "@esbuild/openbsd-arm64" "0.25.8" 312 | "@esbuild/openbsd-x64" "0.25.8" 313 | "@esbuild/openharmony-arm64" "0.25.8" 314 | "@esbuild/sunos-x64" "0.25.8" 315 | "@esbuild/win32-arm64" "0.25.8" 316 | "@esbuild/win32-ia32" "0.25.8" 317 | "@esbuild/win32-x64" "0.25.8" 318 | 319 | eventemitter3@^5.0.1: 320 | version "5.0.1" 321 | resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" 322 | integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== 323 | 324 | fsevents@~2.3.3: 325 | version "2.3.3" 326 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" 327 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== 328 | 329 | get-tsconfig@^4.7.5: 330 | version "4.8.1" 331 | resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" 332 | integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg== 333 | dependencies: 334 | resolve-pkg-maps "^1.0.0" 335 | 336 | ieee754@^1.2.1: 337 | version "1.2.1" 338 | resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" 339 | integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== 340 | 341 | lossless-json@^4.0.1: 342 | version "4.1.1" 343 | resolved "https://registry.yarnpkg.com/lossless-json/-/lossless-json-4.1.1.tgz#b7cbac00c222a68072a9037563dfc4c71cee52f0" 344 | integrity sha512-HusN80C0ohtT9kOHQH7EuUaqzRQsnekpa+2ot8OzvW0iC08dq/YtM/7uKwwajldQsCrHyC8q9fz3t3L+TmDltA== 345 | 346 | mainnet-js@^2.7.19: 347 | version "2.7.19" 348 | resolved "https://registry.yarnpkg.com/mainnet-js/-/mainnet-js-2.7.19.tgz#71239ebbb8113bdc710aea0ec91efc3d391281c2" 349 | integrity sha512-Mr6KAKmvmI5RFOeBqhfBI/uEt1bqzVtoHlBXBc/kt6lBnHFZfxlWuKEccs5anvbB4Wd5Sf+mdInJJTjMxwzqDw== 350 | dependencies: 351 | "@bch-wc2/interfaces" "^0.0.8" 352 | "@bitauth/libauth" "^3.1.0-next.2" 353 | "@electrum-cash/network" "^4.1.3" 354 | "@scure/bip39" "^1.2.2" 355 | buffer "^6.0.3" 356 | optionalDependencies: 357 | dotenv "^10.0.0" 358 | 359 | ms@^2.1.3: 360 | version "2.1.3" 361 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 362 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 363 | 364 | pako@^2.1.0: 365 | version "2.1.0" 366 | resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" 367 | integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== 368 | 369 | resolve-pkg-maps@^1.0.0: 370 | version "1.0.0" 371 | resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" 372 | integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== 373 | 374 | semver@^7.7.2: 375 | version "7.7.2" 376 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" 377 | integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== 378 | 379 | tslib@^2.4.0: 380 | version "2.8.1" 381 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" 382 | integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== 383 | 384 | tsx@^4.20.6: 385 | version "4.20.6" 386 | resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.20.6.tgz#8fb803fd9c1f70e8ccc93b5d7c5e03c3979ccb2e" 387 | integrity sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg== 388 | dependencies: 389 | esbuild "~0.25.0" 390 | get-tsconfig "^4.7.5" 391 | optionalDependencies: 392 | fsevents "~2.3.3" 393 | 394 | undici-types@~6.19.2: 395 | version "6.19.8" 396 | resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" 397 | integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== 398 | 399 | ws@^8.13.0: 400 | version "8.18.3" 401 | resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" 402 | integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== 403 | --------------------------------------------------------------------------------