diff --git a/content/_code-samples/use-tickets/js/use-tickets-multisig.js b/content/_code-samples/use-tickets/js/use-tickets-multisig.js new file mode 100644 index 0000000000..5f8eab07f1 --- /dev/null +++ b/content/_code-samples/use-tickets/js/use-tickets-multisig.js @@ -0,0 +1,129 @@ +if (typeof module !== "undefined") { + // Use var here because const/let are block-scoped to the if statement. + var xrpl = require('xrpl') +} + // List which Tickets are outstanding against one’s own account and use Tickets to collect signatures for multisign transactions +// https://xrpl.org/use-tickets.html +// https://xrpl.org/signerlistset.html#signerlistset +// https://xrpl.org/multi-signing.html#multi-signing + +async function main() { + // Connect to a testnet node + console.log("Connecting to Testnet...") + const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') + await client.connect() + + // Get account credentials from the Testnet Faucet + console.log("Requesting account credentials from the Testnet faucet, this may take awhile...") + const { wallet: main_wallet } = await client.fundWallet() + + // Signer keys don't need to be funded on the ledger, it only needs to be cryptographically valid + // Thus, we could generate keys and set them as signers without the need to fund their accounts + // But we'll still fund them for testing purposes... + const { wallet: wallet_1 } = await client.fundWallet() + const { wallet: wallet_2 } = await client.fundWallet() + const { wallet: wallet_3 } = await client.fundWallet() + + console.log(" Main Account: ", main_wallet.address) + console.log(" Seed: ", main_wallet.seed) + + console.log("\n Signer 1: ", wallet_1.address) + console.log(" Signer 2: ", wallet_2.address) + console.log(" Signer 3: ", wallet_3.address) + + // Send SignerListSet transaction + // Since each signer is given a signer weight of 1 and there are 3 signers, the maximum quorom would be 3 + // SignerQuorom is a target number for the signer weights + // A multisig from this list is valid only if the sum weights of the signatures provided is greater than or equal to the SignerQuorom + const signerLiSetSignerList_tx = { + TransactionType: "SignerListSet", + Account: main_wallet.classicAddress, + SignerEntries: [ + { + SignerEntry: { + Account: wallet_1.classicAddress, + SignerWeight: 1, + }, + }, + { + SignerEntry: { + Account: wallet_2.classicAddress, + SignerWeight: 1, + }, + }, + { + SignerEntry: { + Account: wallet_3.classicAddress, + SignerWeight: 1, + }, + } + ], + SignerQuorum: 2, + } + + const signerLiSetSignerList_tx_prepared = await client.autofill(signerLiSetSignerList_tx) + const SetSignerList_tx_signed = main_wallet.sign(signerLiSetSignerList_tx_prepared) + console.log(`\n SignerListSet Tx hash: ${SetSignerList_tx_signed.hash}`) + + const setsignerlist_submit = await client.submitAndWait(SetSignerList_tx_signed.tx_blob) + console.log(`\t Submit result: ${setsignerlist_submit.result.meta.TransactionResult}`) + + const CreateTicket_tx = await client.autofill({ + TransactionType: "TicketCreate", + Account: main_wallet.address, + TicketCount: 3 + }) + + const CreateTicket_tx_signed = main_wallet.sign(CreateTicket_tx) + console.log("\n CreateTicket Tx hash:", CreateTicket_tx_signed.hash) + + const ticket_submit = await client.submitAndWait(CreateTicket_tx_signed.tx_blob) + console.log(" Submit result:", ticket_submit.result.meta.TransactionResult) + + const ticket_response = await client.request({ + command: "account_objects", + account: main_wallet.address, + ledger_index: "validated", + type: "ticket" + }) + + console.log(`\n- Tickets issued by ${main_wallet.address}:\n`) + for (let i = 0; i < ticket_response.result.account_objects.length; i++) { + console.log(`Ticket ${i+1}: ${ticket_response.result.account_objects[i].TicketSequence}`) + } + + // We'll use this ticket on our tx + const ticket_1 = ticket_response.result.account_objects[0].TicketSequence + + console.log(`\n Ticket sequence ${ticket_1} will be used for our multi-sig transaction`) + + const Payment_tx = { + "TransactionType": "AccountSet", + "Account": main_wallet.address, + "TicketSequence": ticket_1, + "LastLedgerSequence": null, + "Sequence": 0 + } + + const Payment_tx_prepared = await client.autofill(Payment_tx, signersCount=3) + + // Each signer will sign the prepared tx (AccountSet_tx) and their signatures will be combines into 1 multi-sig transaction + const { tx_blob: Payment_tx_signed_1 } = wallet_1.sign(Payment_tx_prepared, multisign=true) + const { tx_blob: Payment_tx_signed_2 } = wallet_2.sign(Payment_tx_prepared, multisign=true) + const { tx_blob: Payment_tx_signed_3 } = wallet_3.sign(Payment_tx_prepared, multisign=true) + + console.log("\n All signers have signed the transaction with their corresponding keys") + + // Combine 3 of the signers' signatures to form a multi-sig transaction + const multisignedTx = xrpl.multisign([Payment_tx_signed_1, Payment_tx_signed_2, Payment_tx_signed_3]) + + const multisig_submit = await client.submitAndWait(multisignedTx) + console.log("\n Multi-sig Submit result:", multisig_submit.result.meta.TransactionResult) + console.log("\n Multi-sig Tx Binary:", multisignedTx) + + client.disconnect() + + // End main() + } + + main()