Propose and confirm transactions
In this guide you will learn how to propose transactions to the service and collect the signatures from the owners so they become executable.
For more detailed information, see the API Kit Reference.
Prerequisites
- Node.js and npm (opens in a new tab)
- A Safe with several signers
Steps
Install dependencies
First, you need to install some dependencies.
_10yarn add @safe-global/api-kit \_10 @safe-global/protocol-kit \_10 @safe-global/types-kit
Imports
Here are all the necessary imports for this guide.
_10import SafeApiKit from '@safe-global/api-kit'_10import Safe from '@safe-global/protocol-kit'_10import {_10 MetaTransactionData,_10 OperationType_10} from '@safe-global/types-kit'
Setup
We will use a Safe account setup with two or more signers, and threshold two, so at least multiple signatures will need to be collected when executing a transaction.
_10// https://chainlist.org/?search=sepolia&testnets=true_10const RPC_URL = 'https://eth-sepolia.public.blastapi.io'_10_10const SAFE_ADDRESS = // ..._10_10const OWNER_1_ADDRESS = // ..._10const OWNER_1_PRIVATE_KEY = // ..._10_10const OWNER_2_PRIVATE_KEY = // ...
Initialize the API Kit
Firstly, you need to create an instance of the API Kit. In chains where the Safe Transaction Service is supported, it's enough to specify the chainId
property.
_10const apiKit = new SafeApiKit({_10 chainId: 1n_10})
Alternatively, you can use a custom service using the optional txServiceUrl
property.
_10const apiKit = new SafeApiKit({_10 chainId: 1n, // set the correct chainId_10 txServiceUrl: 'https://url-to-your-custom-service'_10})
Initialize the Protocol Kit
To handle transactions and signatures, you need to create an instance of the Protocol Kit with the provider
, signer
and safeAddress
.
_10const protocolKitOwner1 = await Safe.init({_10 provider: RPC_URL,_10 signer: OWNER_1_PRIVATE_KEY,_10 safeAddress: SAFE_ADDRESS_10})
Propose a transaction to the service
Before a transaction can be executed, any of the Safe signers needs to initiate the process by creating a proposal of a transaction. This transaction is sent to the service to make it accessible by the other owners so they can give their approval and sign the transaction as well.
For a full list and description of the properties see proposeTransaction
in the API Kit reference.
_23// Create transaction_23const safeTransactionData: MetaTransactionData = {_23 to: '0x',_23 value: '1', // 1 wei_23 data: '0x',_23 operation: OperationType.Call_23}_23_23const safeTransaction = await protocolKitOwner1.createTransaction({_23 transactions: [safeTransactionData]_23})_23_23const safeTxHash = await protocolKitOwner1.getTransactionHash(safeTransaction)_23const signature = await protocolKitOwner1.signHash(safeTxHash)_23_23// Propose transaction to the service_23await apiKit.proposeTransaction({_23 safeAddress: SAFE_ADDRESS,_23 safeTransactionData: safeTransaction.data,_23 safeTxHash,_23 senderAddress: OWNER_1_ADDRESS,_23 senderSignature: signature.data_23})
Retrieve the pending transactions
Different methods in the API Kit are available to retrieve pending transactions depending on the situation. To retrieve a transaction given the Safe transaction hash use the method that's not commented.
_10const transaction = await service.getTransaction(safeTxHash)_10// const transactions = await service.getPendingTransactions()_10// const transactions = await service.getIncomingTransactions()_10// const transactions = await service.getMultisigTransactions()_10// const transactions = await service.getModuleTransactions()_10// const transactions = await service.getAllTransactions()
Confirm the transaction
In this step you need to sign the transaction with the Protocol Kit and submit the signature to the Safe Transaction Service using the confirmTransaction
method.
_14const protocolKitOwner2 = await Safe.init({_14 provider: RPC_URL,_14 signer: OWNER_2_PRIVATE_KEY,_14 safeAddress: SAFE_ADDRESS_14})_14_14const safeTxHash = transaction.transactionHash_14const signature = await protocolKitOwner2.signHash(safeTxHash)_14_14// Confirm the Safe transaction_14const signatureResponse = await apiKit.confirmTransaction(_14 safeTxHash,_14 signature.data_14)
The Safe transaction is now ready to be executed. This can be done using the Safe{Wallet} web (opens in a new tab) interface, the Protocol Kit, the Safe CLI or any other tool that's available.