Transactions with off-chain signatures
Prerequisites
Steps
1
2
Imports
import SafeApiKit from '@safe-global/api-kit'
import Safe from '@safe-global/protocol-kit'
import {
MetaTransactionData,
OperationType
} from '@safe-global/types-kit'
from safe_eth.eth import EthereumClient, EthereumNetwork
from safe_eth.safe.api.transaction_service_api import TransactionServiceApi
from safe_eth.safe import Safe
from hexbytes import HexBytes
3
Create a multisig transaction
// Initialize the Protocol Kit with Owner A
const protocolKitOwnerA = await Safe.init({
provider: config.RPC_URL,
signer: config.OWNER_A_PRIVATE_KEY,
safeAddress: config.SAFE_ADDRESS
})
// Create the transaction payload
const safeTransactionData: MetaTransactionData = {
to: config.TO,
value: config.VALUE,
data: '0x',
operation: OperationType.Call
}
const safeTransaction = await protocolKitOwnerA.createTransaction({
transactions: [safeTransactionData]
})ethereum_client = EthereumClient(config.get("RPC_URL"))
# Instantiate a Safe
safe = Safe(config.get("SAFE_ADDRESS"), ethereum_client)
# Create the transaction payload
safe_tx = safe.build_multisig_tx(
config.get("TO"),
config.get("VALUE"),
HexBytes(""))4
5
Send the transaction to the service
// Initialize the API Kit
// How to get an Api key => http://docs.safe.global/core-api/how-to-use-api-keys
const apiKit = new SafeApiKit({
chainId: 11155111n
})
// Send the transaction to the Transaction Service with the signature from Owner A
await apiKit.proposeTransaction({
safeAddress: config.SAFE_ADDRESS,
safeTransactionData: safeTransaction.data,
safeTxHash,
senderAddress: config.OWNER_A_ADDRESS,
senderSignature: signatureOwnerA.data
})# Instantiate the Transaction Service API
transaction_service_api = TransactionServiceApi(
network=EthereumNetwork.SEPOLIA,
ethereum_client=ethereum_client,
)
# Send the transaction to the Transaction Service with the signature from Owner A
transaction_service_api.post_transaction(safe_tx)6
Gather Multi-Signature Approvals
const signedTransaction = await apiKit.getTransaction(safeTxHash)(safe_tx_from_tx_service, _) = transaction_service_api.get_safe_transaction(
safe_tx_hash)curl -X 'GET' \
'https://multisig.ledger.com/tx-service/sep/api/v1/multisig-transactions/0xe4ceea4ffffffffff0c9ce0af82780ffffffffffd3096836fff2528cb90d156/' \
-H 'accept: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY'// Initialize the Protocol Kit with Owner B
const protocolKitOwnerB = await Safe.init({
provider: config.RPC_URL,
signer: config.OWNER_B_PRIVATE_KEY,
safeAddress: config.SAFE_ADDRESS
})
// Sign the transaction with Owner B
const signatureOwnerB = await protocolKitOwnerB.signHash(safeTxHash)
// Send the transaction to the Transaction Service with the signature from Owner B
await apiKit.confirmTransaction(
safeTxHash,
signatureOwnerB.data
)# Sign the transaction with Owner B
owner_b_signature = safe_tx_from_tx_service.sign(
config.get("OWNER_B_PRIVATE_KEY"))
# Send the transaction to the Transaction Service with the signature from Owner B
transaction_service_api.post_signatures(
safe_tx_from_tx_service.safe_tx_hash,
owner_b_signature)7
8
Verify Execution
const transactions = await apiKit.getMultisigTransactions(config.SAFE_ADDRESS)
if (transactions.results.length > 0) {
console.log('Last executed transaction', transactions.results[0])
}transactions = transaction_service_api.get_transactions(
config.get("SAFE_ADDRESS"))
last_executed_tx = next(
(x for x in transactions if x.get('isExecuted')),
None)curl -X 'GET' \
'https://multisig.ledger.com/tx-service/sep/api/v1/safes/0xc62C5cbB96ffffffffffff2f78A4d3071317ffff/multisig-transactions/?executed=true&limit=1' \
-H 'accept: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY'Last updated