SDK
API Kit

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

  1. Node.js and npm (opens in a new tab)
  2. A Safe with several signers

Steps

Install dependencies

First, we need to install some dependencies.


_10
yarn 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.


_10
import SafeApiKit from '@safe-global/api-kit'
_10
import Safe from '@safe-global/protocol-kit'
_10
import {
_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
_10
const RPC_URL = 'https://eth-sepolia.public.blastapi.io'
_10
_10
const SAFE_ADDRESS = // ...
_10
_10
const OWNER_1_ADDRESS = // ...
_10
const OWNER_1_PRIVATE_KEY = // ...
_10
_10
const 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.


_10
const apiKit = new SafeApiKit({
_10
chainId: 1n
_10
})

Alternatively, we can use a custom service using the optional txServiceUrl property.


_10
const 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.


_10
const 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
_23
const safeTransactionData: MetaTransactionData = {
_23
to: '0x',
_23
value: '1', // 1 wei
_23
data: '0x',
_23
operation: OperationType.Call
_23
}
_23
_23
const safeTransaction = await protocolKitOwner1.createTransaction({
_23
transactions: [safeTransactionData]
_23
})
_23
_23
const safeTxHash = await protocolKitOwner1.getTransactionHash(safeTransaction)
_23
const signature = await protocolKitOwner1.signHash(safeTxHash)
_23
_23
// Propose transaction to the service
_23
await 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.


_10
const 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.


_14
const protocolKitOwner2 = await Safe.init({
_14
provider: RPC_URL,
_14
signer: OWNER_2_PRIVATE_KEY,
_14
safeAddress: SAFE_ADDRESS
_14
})
_14
_14
const safeTxHash = transaction.transactionHash
_14
const signature = await protocolKitOwner2.signHash(safeTxHash)
_14
_14
// Confirm the Safe transaction
_14
const 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.

Was this page helpful?