API Kit
The API Kit (opens in a new tab) facilitates the interaction with the Safe Transaction Service API (opens in a new tab), allowing to propose and share transactions with the other signers of a Safe, sending the signatures to the service to collect them, getting information about a Safe (like reading the transaction history, pending transactions, enabled Modules and Guards, etc.), among other features.
Quickstart
In this guide we will see 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, we need to install some dependencies.
_10yarn add @safe-global/api-kit \_10 @safe-global/protocol-kit \_10 @safe-global/safe-core-sdk-types
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/safe-core-sdk-types'
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, we 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, we 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, we 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. We send this transaction 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 we 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.