diff --git a/content/_code-samples/use-tickets/py/use-tickets-multisig.py b/content/_code-samples/use-tickets/py/use-tickets-multisig.py new file mode 100644 index 0000000000..6afe1ffaca --- /dev/null +++ b/content/_code-samples/use-tickets/py/use-tickets-multisig.py @@ -0,0 +1,130 @@ +from xrpl.models.transactions import TicketCreate, AccountSet, SignerListSet, SignerEntry +from xrpl.transaction import safe_sign_and_submit_transaction, autofill, multisign, sign +from xrpl.models.requests.account_objects import AccountObjects, AccountObjectType +from xrpl.models.requests.submit_multisigned import SubmitMultisigned +from xrpl.wallet import generate_faucet_wallet +from xrpl.clients import JsonRpcClient + +# This code sample lists which tickets are outstanding against an account +# and use the ticket for a multisignature transaction. +# A funded account on the testnet is provided for testing purposes +# https://xrpl.org/tickets.html#tickets +# https://xrpl.org/set-up-multi-signing.html#set-up-multi-signing +# https://xrpl.org/send-a-multi-signed-transaction.html#send-a-multi-signed-transaction + +# Connect to a testnet node +JSON_RPC_URL = "https://s.altnet.rippletest.net:51234/" +client = JsonRpcClient(JSON_RPC_URL) + +# Generate a wallet and request faucet +test_wallet = generate_faucet_wallet(client=client) +myAddr = test_wallet.classic_address + +print("Setting up all the signers' accounts via the testnet faucet, this may take a while...") +signer_1_wallet = generate_faucet_wallet(client=client) +signer_2_wallet = generate_faucet_wallet(client=client) +signer_3_wallet = generate_faucet_wallet(client=client) + +# Set the list of accounts that are able to authorize transactions on behalf of our Account via a multi-sig transaction +signers = [ + SignerEntry(account=signer_1_wallet.classic_address, signer_weight=1), + SignerEntry(account=signer_2_wallet.classic_address, signer_weight=1), + SignerEntry(account=signer_3_wallet.classic_address, signer_weight=1) +] + +# Display all the signers' account address +print("\nSigners:") +signer_int = 1 +for signer in signers: + print(f"{signer_int}. {signer}") + signer_int += 1 + +# A multi-sig transaction is achievable by trusting a list of keys to authorize transactions on behalf of the account +# Construct a SignerList transaction +tx_set_signer_list = SignerListSet( + account=myAddr, + signer_quorum=3, + signer_entries=signers +) + +# We'll set the signer_quorum to 3, each key will represent 1 signer weight +# if 3 of the signers sign a particular transaction, it's an authorized transaction on the XRPL + +# Sign transaction locally and submit +tx_set_signer_list_signed = safe_sign_and_submit_transaction(transaction=tx_set_signer_list, wallet=test_wallet, client=client) + +# Construct a TicketCreate transaction, 3 tickets will be created +tx_create_ticket = TicketCreate( + account=myAddr, + ticket_count=3 +) + +# Sign transaction locally and submit +tx_create_ticket_signed = safe_sign_and_submit_transaction(transaction=tx_create_ticket, wallet=test_wallet, client=client) + +# Get a Ticket Sequence +get_ticket_sequence = client.request(AccountObjects( + account=myAddr, + type=AccountObjectType.TICKET +)) + +# Since we created 3 Tickets previously, you're able to choose which Ticket you're going to use +ticket1_sequence = get_ticket_sequence.result["account_objects"][0]["TicketSequence"] +ticket2_sequence = get_ticket_sequence.result["account_objects"][1]["TicketSequence"] +ticket3_sequence = get_ticket_sequence.result["account_objects"][2]["TicketSequence"] + +print(f"\nTickets issued:\n" + f"Ticket 1: {ticket1_sequence}\n" + f"Ticket 2: {ticket2_sequence}\n" + f"Ticket 3: {ticket3_sequence}") + +ticket_sequence = int(input("\nPick 1 ticket sequence to use for your next multi-sig transaction: ")) + +# Construct AccountSet Transaction using a Ticket +tx_1 = AccountSet( + account=myAddr, + fee="1000", + sequence=0, + last_ledger_sequence=None, + ticket_sequence=ticket_sequence +) + +autofilled_account_set_tx = autofill(tx_1, client, len(signers)) + +# Sign 'tx_1' using all the keys on signers[] +tx_result_1 = sign(transaction=autofilled_account_set_tx, wallet=signer_1_wallet, multisign=True) +tx_result_2 = sign(transaction=autofilled_account_set_tx, wallet=signer_2_wallet, multisign=True) +tx_result_3 = sign(transaction=autofilled_account_set_tx, wallet=signer_3_wallet, multisign=True) + +print(f"\nTx signature by account 1: {tx_result_1.signers}") +print(f"Tx signature by account 2: {tx_result_2.signers}") +print(f"Tx signature by account 3: {tx_result_3.signers}") + +# Combine all the signed transactions from the provided signed txs into a multi-sig transaction +tx_1_filled = multisign( + transaction=autofilled_account_set_tx, + tx_list=[ + tx_result_1, + tx_result_2, + tx_result_3 + ] +) + +response = client.request(SubmitMultisigned(tx_json=tx_1_filled)) +result = response.result + +print(f"\n Account Set tx result: {result['engine_result']}" + f"\n Tx content: {result}\n") + +if result['engine_result'] == "tesSUCCESS": + print("Multi-sig transaction using a Ticket was successful!") +elif result == "terPRE_TICKET": + print("The provided Ticket Sequence does not exist in the ledger") +elif result == "tefMAX_LEDGER": + print("Transaction failed to achieve consensus") +elif result == "tefPAST_SEQ": + print("The sequence number is lower than the current sequence number of the account") +elif result == "unknown": + print("Transaction status unknown") +else: + print(f"Transaction failed with code {result['engine_result']}")