SDK
Guides
Transactions

Transaction signatures

This guide explains how transactions are signed by the Safe owners using the Protocol Kit.

ℹ️

Before starting, check this guide's setup.

Prerequisites

Steps

Install dependencies


_10
yarn install @safe-global/protocol-kit

Create a transaction

The createTransaction method in the Protocol Kit allows the creation of new Safe transactions and returns an instance of the EthSafeTransaction class.


_10
// Create a transaction to send 0.01 ETH
_10
const safeTransactionData: SafeTransactionDataPartial = {
_10
to: '0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1',
_10
value: '100000000000000000', // 0.01 ETH
_10
data: '0x'
_10
}
_10
_10
let safeTransaction = await protocolKit.createTransaction({
_10
transactions: [safeTransactionData]
_10
})

The returned safeTransaction object contains the transaction data (safeTransaction.data) and a map of the owner-signature pairs (safeTransaction.signatures). The structure is similar to the EthSafeMessage class but applied for transactions instead of messages.

We use let to initialize the safeTransaction variable because we will add the signatures later.


_10
class EthSafeTransaction implements SafeTransaction {
_10
data: SafeTransactionData
_10
signatures: Map<string, SafeSignature> = new Map()
_10
...
_10
// Other properties and methods
_10
}

Sign the transaction

Once the safeTransaction object is created, we need to collect the signatures from the signers who will sign it.

Following our setup, we will sign a Safe transaction from SAFE_3_4_ADDRESS, the main Safe account in this guide. To do that, we first need to sign the same transaction with its owners: OWNER_1_ADDRESS, OWNER_2_ADDRESS, SAFE_1_1_ADDRESS, and SAFE_2_3_ADDRESS.

ECDSA signature

This applies to OWNER_1_ADDRESS and OWNER_2_ADDRESS accounts, as both are EOAs.

The signTransaction method takes the safeTransaction together with a SigningMethod and adds the new signature to the safeTransaction.signatures map. Depending on the type of message, the SigningMethod can take these values:

  • SigningMethod.ETH_SIGN
  • SigningMethod.ETH_SIGN_TYPED_DATA_V4

_25
// Connect OWNER_1_ADDRESS
_25
protocolKit = await protocolKit.connect({
_25
provider: RPC_URL,
_25
signer: OWNER_1_PRIVATE_KEY
_25
})
_25
_25
// Sign the safeTransaction with OWNER_1_ADDRESS
_25
// After this, the safeTransaction contains the signature from OWNER_1_ADDRESS
_25
safeTransaction = await protocolKit.signTransaction(
_25
safeTransaction,
_25
SigningMethod.ETH_SIGN
_25
)
_25
_25
// Connect OWNER_2_ADDRESS
_25
protocolKit = await protocolKit.connect({
_25
provider: RPC_URL,
_25
signer: OWNER_2_PRIVATE_KEY
_25
})
_25
_25
// Sign the safeTransaction with OWNER_2_ADDRESS
_25
// After this, the safeTransaction contains the signature from OWNER_1_ADDRESS and OWNER_2_ADDRESS
_25
safeTransaction = await protocolKit.signTransaction(
_25
safeTransaction,
_25
SigningMethod.ETH_SIGN_TYPED_DATA_V4
_25
)

At this point, the safeTransaction object should look like this:


_15
EthSafeTransaction {
_15
signatures: Map(2) {
_15
'0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1' => EthSafeSignature {
_15
signer: '0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1',
_15
data: '0x969308e2abeda61a0c9c41b3c615012f50dd7456ca76ea39a18e3b975abeb67f275b07810dd59fc928f3f9103e52557c1578c7c5c171ffc983afa5306466b1261f',
_15
isContractSignature: false
_15
},
_15
'0xffcf8fdee72ac11b5c542428b35eef5769c409f0' => EthSafeSignature {
_15
signer: '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0',
_15
data: '0x4d63c79cf9d743782bc31ad58c1a316020b39839ab164caee7ecac9829f685cc44ec0d066a5dfe646b2ffeeb37575df131daf9c96ced41b8c7c4aea8dc5461801c',
_15
isContractSignature: false
_15
}
_15
},
_15
data: { ... }
_15
}

The signatures.data represents a specific signature. The isContractSignature flag set to false indicates that the signature isn't a smart contract signature but an ECDSA signature instead.

An ECDSA signature comprises two 32-byte integers (r, s) and an extra byte for recovery (v), totaling 65 bytes. In hexadecimal string format, each byte is represented by two characters. Hence, a 65-byte Ethereum signature will be 130 characters long. Including the 0x prefix commonly used with signatures, the total character count for such a signature would be 132.

Two more characters are required to represent a byte (8 bits) in hexadecimal. Each hexadecimal character represents four bits. Therefore, two hexadecimal characters (2 x 4 bits) can represent a byte (8 bits).

The final part of the signature, either 1f or 1c, indicates the signature type.

Safe supports the following v values:

  • 0: Contract signature.
  • 1: Approved hash.
  • {27, 28} + 4: Ethereum adjusted ECDSA recovery byte for EIP-191 signed message.

Regarding the EIP-191 signed message, the v value is adjusted to the ECDSA v + 4. If the generated value is 28 and adjusted to 0x1f, the signature verification will fail as it should be 0x20 ('28 + 4 = 32) instead. If v > 30, then the default v (27, 28) was adjusted because of the eth_sign` implementation. This calculation is automatically done by the Safe{Core} SDK.

  • Other: Ethereum adjusted ECDSA recovery byte for raw signed hash.

The hexadecimal value 1f equals the decimal number 31. If the decimal value is greater than 30, it indicates (opens in a new tab) that the signature is an eth_sign signature.

The hexadecimal value 1c equals the decimal number 28, indicating that the signature is a typed data signature.

The initial signature should look like this:

0x969308e2abeda61a0c9c41b3c615012f50dd7456ca76ea39a18e3b975abeb67f275b07810dd59fc928f3f9103e52557c1578c7c5c171ffc983afa5306466b1261f:

TypeDescriptionBytesValue
HexHex string characters1
0x
SignatureSignature bytes64
969308e2abeda61a0c9c41b3c615012f50dd7456ca76ea39a18e3b975abeb67f275b07810dd59fc928f3f9103e52557c1578c7c5c171ffc983afa5306466b126
Signature Type1f hex is 31 in decimal1
1f

Smart contract signatures

When signing with a Safe account, the SigningMethod will take the value SigningMethod.SAFE_SIGNATURE.

1/1 Safe account

This applies to the SAFE_1_1_ADDRESS account, another owner of SAFE_3_4_ADDRESS.

We need to connect the Protocol Kit to SAFE_1_1_ADDRESS and the OWNER_3_ADDRESS account (the only owner of SAFE_1_1_ADDRESS) and sign the transaction.


_19
// Create a new transaction object
_19
let transactionSafe1_1 = await protocolKit.createTransaction({
_19
transactions: [safeTransactionData]
_19
})
_19
_19
// Connect OWNER_3_ADDRESS and SAFE_1_1_ADDRESS
_19
protocolKit = await protocolKit.connect({
_19
provider: RPC_URL,
_19
signer: OWNER_3_PRIVATE_KEY,
_19
safeAddress: SAFE_1_1_ADDRESS
_19
})
_19
_19
// Sign the transactionSafe1_1 with OWNER_3_ADDRESS
_19
// After this, transactionSafe1_1 contains the signature from OWNER_3_ADDRESS
_19
transactionSafe1_1 = await protocolKit.signTransaction(
_19
transactionSafe1_1,
_19
SigningMethod.SAFE_SIGNATURE,
_19
SAFE_3_4_ADDRESS // Parent Safe address
_19
)

When signing with a child Safe account, we need to specify the parent Safe address to generate the signature based on the version of the contract.

At this point, the transactionSafe1_1 object should look like this:


_10
EthSafeTransaction {
_10
signatures: Map(1) {
_10
'0x22d491bde2303f2f43325b2108d26f1eaba1e32b' => EthSafeSignature {
_10
signer: '0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b',
_10
data: '0x5edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f',
_10
isContractSignature: false
_10
}
_10
},
_10
data: { ...}
_10
}

The signatures.data represents a specific signature. The isContractSignature flag set to false indicates that the signature isn't a smart contract signature but an ECDSA signature instead.

To generate a Safe compatible signature, we use the buildContractSignature method, which takes an array of signatures and returns another signature that can be used with Safe accounts. After that, we add the signature from SAFE_1_1_ADDRESS to our initial transaction.


_10
// Build the contract signature of SAFE_1_1_ADDRESS
_10
const signatureSafe1_1 = await buildContractSignature(
_10
Array.from(transactionSafe1_1.signatures.values()),
_10
SAFE_1_1_ADDRESS
_10
)
_10
_10
// Add the signatureSafe1_1 to safeTransaction
_10
// After this, the safeTransaction contains the signature from OWNER_1_ADDRESS, OWNER_2_ADDRESS and SAFE_1_1_ADDRESS
_10
safeTransaction.addSignature(signatureSafe1_1)

The signatureSafe1_1 object should look like this:


_10
EthSafeSignature {
_10
signer: '0x215033cdE0619D60B7352348F4598316Cc39bC6E',
_10
data: '0x5edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f',
_10
isContractSignature: true
_10
}

The isContractSignature flag is now true because signatureSafe1_1 is an EIP-1271 smart contract signature from the SAFE_1_1_ADDRESS account.

The signatureSafe1_1.data signature should look like this:


_10
0x000000000000000000000000215033cdE0619D60B7352348F4598316Cc39bC6E00000000000000000000000000000000000000000000000000000000000000410000000000000000000000000000000000000000000000000000000000000000415edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f

TypeDescriptionBytesValue
HexHex string characters10x
VerifierPadded address of the contract that implements the EIP-1271 interface to verify the signature. The Safe signer address32
000000000000000000000000215033cdE0619D60B7352348F4598316Cc39bC6E
Data positionStart position of the signature data (offset relative to the beginning of the signature data). 41 hex is 65 in decimal32
0000000000000000000000000000000000000000000000000000000000000041
Signature Type00 for Safe accounts (opens in a new tab)100
Signature LengthThe length of the signature. 41 hex is 65 in decimal32
0000000000000000000000000000000000000000000000000000000000000041
SignatureSignature bytes that are verified by the signature verifier65
5edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f
2/3 Safe account

This applies to the SAFE_2_3_ADDRESS account, another owner of SAFE_3_4_ADDRESS.

We need to connect the Protocol Kit to SAFE_2_3_ADDRESS and the OWNER_4_ADDRESS and OWNER_5_ADDRESS accounts (owners of SAFE_2_3_ADDRESS) and sign the transaction.


_33
// Create a new transaction object
_33
let transactionSafe2_3 = await protocolKit.createTransaction({
_33
transactions: [safeTransactionData]
_33
})
_33
_33
// Connect OWNER_4_ADDRESS and the address of SAFE_2_3_ADDRESS
_33
protocolKit = await protocolKit.connect({
_33
provider: RPC_URL,
_33
signer: OWNER_4_ADDRESS,
_33
safeAddress: SAFE_2_3_ADDRESS
_33
})
_33
_33
// Sign the transactionSafe2_3 with OWNER_4_ADDRESS
_33
// After this, the transactionSafe2_3 contains the signature from OWNER_4_ADDRESS
_33
transactionSafe2_3 = await protocolKit.signTransaction(
_33
transactionSafe2_3,
_33
SigningMethod.SAFE_SIGNATURE,
_33
SAFE_3_4_ADDRESS // Parent Safe address
_33
)
_33
_33
// Connect OWNER_5_ADDRESS
_33
protocolKit = await protocolKit.connect({
_33
provider: RPC_URL,
_33
signer: OWNER_5_ADDRESS
_33
})
_33
_33
// Sign the transactionSafe2_3 with OWNER_5_ADDRESS
_33
// After this, the transactionSafe2_3 contains the signature from OWNER_5_ADDRESS
_33
transactionSafe2_3 = await protocolKit.signTransaction(
_33
transactionSafe2_3,
_33
SigningMethod.SAFE_SIGNATURE,
_33
SAFE_3_4_ADDRESS // Parent Safe address
_33
)

At this point, the transactionSafe2_3 object should look like this:


_15
EthSafeTransaction {
_15
signatures: Map(2) {
_15
'0xe11ba2b4d45eaed5996cd0823791e0c93114882d' => EthSafeSignature {
_15
signer: '0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d',
_15
data: '0xd3e6565e5590641db447277243cf24711dce533cfcaaf3a64415dcb9fa309fbf2de1ae4709c6450752acc0d45e01b67b55379bdf4e3dc32b2d89ad0a60c231d61f',
_15
isContractSignature: false
_15
},
_15
'0xd03ea8624c8c5987235048901fb614fdca89b117' => EthSafeSignature {
_15
signer: '0xd03ea8624C8C5987235048901fB614fDcA89b117',
_15
data: '0x023d1746ed548e90f387a6b8ddba26e6b80a78d5bfbc36e5bfcbfd63e136f8071db6e91c037fa36bde72159138bbb74fc359b35eb515e276a7c0547d5eaa042520',
_15
isContractSignature: false
_15
}
_15
},
_15
data: { ... }
_15
}

We now have two signatures from the owners, OWNER_4_ADDRESS and OWNER_5_ADDRESS. Following the same process, we can create the contract signature and examine the result.

The signatures.data represents a specific signature. The isContractSignature flag set to false indicates that the signature isn't a smart contract signature but an ECDSA signature instead.

To generate a Safe compatible signature, we use the buildContractSignature method, which takes an array of signatures and returns another signature that can be used with Safe accounts. After that, we add the signature from safe1_1 to our initial transaction.


_10
// Build the contract signature of SAFE_2_3_ADDRESS
_10
const signatureSafe2_3 = await buildContractSignature(
_10
Array.from(transactionSafe2_3.signatures.values()),
_10
SAFE_2_3_ADDRESS
_10
)
_10
_10
// Add the signatureSafe2_3 to safeTransaction
_10
// After this, the safeTransaction contains the signature from OWNER_1_ADDRESS, OWNER_2_ADDRESS, SAFE_1_1_ADDRESS and SAFE_2_3_ADDRESS
_10
safeTransaction.addSignature(signatureSafe2_3)

The signatureSafe2_3 object should look like this:


_10
0x000000000000000000000000f75D61D6C27a7CC5788E633c1FC130f0F4a62D330000000000000000000000000000000000000000000000000000000000000041000000000000000000000000000000000000000000000000000000000000000082023d1746ed548e90f387a6b8ddba26e6b80a78d5bfbc36e5bfcbfd63e136f8071db6e91c037fa36bde72159138bbb74fc359b35eb515e276a7c0547d5eaa042520d3e6565e5590641db447277243cf24711dce533cfcaaf3a64415dcb9fa309fbf2de1ae4709c6450752acc0d45e01b67b55379bdf4e3dc32b2d89ad0a60c231d61f

TypeDescriptionBytesValue
HexHex string characters1
0x
VerifierPadded address of the contract that implements the EIP-1271 interface to verify the signature. The Safe signer address32
000000000000000000000000f75D61D6C27a7CC5788E633c1FC130f0F4a62D33
Data positionStart position of the signature data (offset relative to the beginning of the signature data). 41 hex is 65 in decimal32
0000000000000000000000000000000000000000000000000000000000000041
Signature Type00 for Safe accounts (opens in a new tab)100
Signature LengthThe length of the signature. 82 hex is 130 in decimal32
0000000000000000000000000000000000000000000000000000000000000082
SignatureSignature bytes that are verified by the signature verifier (130 bytes are represented by 260 characters in an hex string)130
023d1746ed548e90f387a6b8ddba26e6b80a78d5bfbc36e5bfcbfd63e136f8071db6e91c037fa36bde72159138bbb74fc359b35eb515e276a7c0547d5eaa042520d3e6565e5590641db447277243cf24711dce533cfcaaf3a64415dcb9fa309fbf2de1ae4709c6450752acc0d45e01b67b55379bdf4e3dc32b2d89ad0a60c231d61f

The table looks very similar to the previous one, but there are two main differences:

  • The Signature Length value has doubled because safe2_3 needs two signatures.
  • The Signature value is a concatenation of the two regular signatures.

After following all the steps above, the safeTransaction now contains all the signatures from the owners of the Safe.

The safeTransaction object should look like this:


_36
EthSafeTransaction {
_36
signatures: Map(4) {
_36
'0x90f8bf6a479f320ead074411a4b0e7944ea8c9c1' => EthSafeSignature {
_36
signer: '0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1',
_36
data: '0x969308e2abeda61a0c9c41b3c615012f50dd7456ca76ea39a18e3b975abeb67f275b07810dd59fc928f3f9103e52557c1578c7c5c171ffc983afa5306466b1261f',
_36
isContractSignature: false
_36
},
_36
'0xffcf8fdee72ac11b5c542428b35eef5769c409f0' => EthSafeSignature {
_36
signer: '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0',
_36
data: '0x4d63c79cf9d743782bc31ad58c1a316020b39839ab164caee7ecac9829f685cc44ec0d066a5dfe646b2ffeeb37575df131daf9c96ced41b8c7c4aea8dc5461801c',
_36
isContractSignature: false
_36
},
_36
'0x215033cde0619d60b7352348f4598316cc39bc6e' => EthSafeSignature {
_36
signer: '0x215033cdE0619D60B7352348F4598316Cc39bC6E',
_36
data: '0x5edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f',
_36
isContractSignature: true
_36
},
_36
'0xf75d61d6c27a7cc5788e633c1fc130f0f4a62d33' => EthSafeSignature {
_36
signer: '0xf75D61D6C27a7CC5788E633c1FC130f0F4a62D33',
_36
data: '0x023d1746ed548e90f387a6b8ddba26e6b80a78d5bfbc36e5bfcbfd63e136f8071db6e91c037fa36bde72159138bbb74fc359b35eb515e276a7c0547d5eaa042520d3e6565e5590641db447277243cf24711dce533cfcaaf3a64415dcb9fa309fbf2de1ae4709c6450752acc0d45e01b67b55379bdf4e3dc32b2d89ad0a60c231d61f',
_36
isContractSignature: true
_36
}
_36
},
_36
data: {
_36
to: '0x90F8bf6A479f320ead074411a4B0e7944Ea8c9C1',
_36
value: '100000000000000000',
_36
data: '0x',
_36
operation: 0,
_36
baseGas: '0',
_36
gasPrice: '0',
_36
gasToken: '0x0000000000000000000000000000000000000000',
_36
refundReceiver: '0x0000000000000000000000000000000000000000',
_36
nonce: 0,
_36
safeTxGas: '0'
_36
}
_36
}

Propose the transaction

To store the transactions and signatures off-chain, we need to call the Safe Transaction Service API - a centralized and open-source service that anyone can deploy and run.

The Safe Transaction Service is used by Safe{Wallet} (opens in a new tab) to store transactions and signatures by default.

To store a new transaction, we need to call the proposeTransaction from the API Kit, passing the Safe address, an object with the transaction, and a signature from one owner.


_18
// Get the signature from OWNER_1_ADDRESS
_18
const signatureOwner1 = safeTransaction.getSignature(OWNER_1_ADDRESS) as EthSafeSignature
_18
_18
// Get the transaction hash of the safeTransaction
_18
const safeTransactionHash = await protocolKit.getTransactionHash(safeTransaction)
_18
_18
// Instantiate the API Kit
_18
// Use the chainId where you have the Safe account deployed
_18
const apiKit = new SafeApiKit({ chainId })
_18
_18
// Propose the transaction
_18
await apiKit.proposeTransaction({
_18
safeAddress: SAFE_3_4_ADDRESS,
_18
safeTransactionData: safeTransaction.data,
_18
safeTxHash: safeTransactionHash,
_18
senderAddress: signerAddress,
_18
senderSignature: buildSignatureBytes([signatureOwner1])
_18
})

The transaction is now publicly available in the Safe Transaction Service with the signature of the owner who submitted it.

Confirm the transaction

To add the signatures from the remaining owners, we need to call the confirmTransaction, passing the safeMessageHash and a signature from the owner.

Once a transaction is proposed, it becomes available on Safe{Wallet} (opens in a new tab). However, to execute the transaction, all the confirmations from the owners are needed.


_19
const signatureOwner2 = safeTransaction.getSignature(OWNER_2_ADDRESS) as EthSafeSignature
_19
_19
// Confirm the transaction from OWNER_2_ADDRESS
_19
await apiKit.confirmTransaction(
_19
safeTransactionHash,
_19
buildSignatureBytes([signatureOwner2])
_19
)
_19
_19
// Confirm the transaction with the owner SAFE_1_1_ADDRESS
_19
await apiKit.confirmTransaction(
_19
safeTransactionHash,
_19
buildSignatureBytes([signatureSafe1_1])
_19
)
_19
_19
// Add signature from the owner SAFE_2_3_ADDRESS
_19
await apiKit.confirmTransaction(
_19
safeTransactionHash,
_19
buildSignatureBytes([signerSafeSig2_3])
_19
)

At this point, the transaction stored in the Safe Transaction Service contains all the required signatures from the owners of the Safe.

The getTransaction method returns the transaction with the confirmations property to check all the added signatures.


_10
// Get the transactions
_10
const signedTransaction = await apiKit.getTransaction(safeTransactionHash)
_10
_10
// Get the confirmations
_10
const confirmations = signedTransaction.confirmations

Safe{Wallet} (opens in a new tab) exposes to its users the list of pending transactions.


_10
https://app.safe.global/transactions/queue?safe=<NETWORK_PREFIX>:<SAFE_ADDRESS>

Execute the transaction

Connect the Safe and an a signer to the Protocol Kit. Ensure enough funds are available in the owner's account to execute the transaction and cover the gas costs. Once the Protocol Kit is initialized, the executeTransaction method receives and executes the transaction with the required signatures.


_10
protocolKit = await protocolKit.connect({
_10
provider: RPC_URL,
_10
signer: OWNER_1_PRIVATE_KEY,
_10
safeAddress: SAFE_3_4_ADDRESS
_10
})
_10
_10
// Execute the Safe transaction
_10
const transactionResponse = await protocolKit.executeTransaction(safeTransaction)

At this point, the Safe transaction should be executed on-chain and listed on Safe{Wallet} (opens in a new tab).


_10
https://app.safe.global/transactions/history?safe=<NETWORK_PREFIX>:<SAFE_ADDRESS>

The safeTransaction.encodedSignature method returns the signatures concatenated and sorted by the address of the signers. It should look like this:


_10
0x000000000000000000000000215033cdE0619D60B7352348F4598316Cc39bC6E000000000000000000000000000000000000000000000000000000000000010400969308e2abeda61a0c9c41b3c615012f50dd7456ca76ea39a18e3b975abeb67f275b07810dd59fc928f3f9103e52557c1578c7c5c171ffc983afa5306466b1261f000000000000000000000000f75D61D6C27a7CC5788E633c1FC130f0F4a62D330000000000000000000000000000000000000000000000000000000000000165004d63c79cf9d743782bc31ad58c1a316020b39839ab164caee7ecac9829f685cc44ec0d066a5dfe646b2ffeeb37575df131daf9c96ced41b8c7c4aea8dc5461801c00000000000000000000000000000000000000000000000000000000000000415edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f0000000000000000000000000000000000000000000000000000000000000082023d1746ed548e90f387a6b8ddba26e6b80a78d5bfbc36e5bfcbfd63e136f8071db6e91c037fa36bde72159138bbb74fc359b35eb515e276a7c0547d5eaa042520d3e6565e5590641db447277243cf24711dce533cfcaaf3a64415dcb9fa309fbf2de1ae4709c6450752acc0d45e01b67b55379bdf4e3dc32b2d89ad0a60c231d61f

TypeDescriptionBytesAcc byteValue
HexHex string characters1-
0x
1/1 Safe signerSafe Address3232
000000000000000000000000215033cdE0619D60B7352348F4598316Cc39bC6E
Data position for 1/1 Safe104 hex = Signature data for 1/1 Safe start at byte 2603264
0000000000000000000000000000000000000000000000000000000000000104
Signature TypeSmart contract signature165
00
Owner signatureOWNER_1_ADDRESS signature65130
969308e2abeda61a0c9c41b3c615012f50dd7456ca76ea39a18e3b975abeb67f275b07810dd59fc928f3f9103e52557c1578c7c5c171ffc983afa5306466b1261f
2/3 Safe signerSafe Address32162
000000000000000000000000f75D61D6C27a7CC5788E633c1FC130f0F4a62D33
Data position for 2/3 Verifier165 hex = Signature data for 2/3 Safe start at byte 35732194
0000000000000000000000000000000000000000000000000000000000000165
SignatureType Smart contract signature1195
00
Owner signatureOWNER_2_ADDRESS signature65260
4d63c79cf9d743782bc31ad58c1a316020b39839ab164caee7ecac9829f685cc44ec0d066a5dfe646b2ffeeb37575df131daf9c96ced41b8c7c4aea8dc5461801c
1/1 Safe Signature LengthStart of the 1/1 Safe Signature. 41 hex = 65 bytes32292
0000000000000000000000000000000000000000000000000000000000000041
SignatureOWNER_3_ADDRESS signature65357
5edb6ffe67dd935d93d07c634970944ba0b096f767b92018ad635e8b28effeea5a1e512f1ad6f886690e0e30a3fae2c8c61d3f83d24d43276acdb3254b92ea5b1f
2/3 Safe Signature lengthStart of the 2/3 Safe Signature. 82 hex = 130 bytes32389
0000000000000000000000000000000000000000000000000000000000000082
SignatureOWNER_4_ADDRESS and OWNER_5_ADDRESS concatenated signatures130519
023d1746ed548e90f387a6b8ddba26e6b80a78d5bfbc36e5bfcbfd63e136f8071db6e91c037fa36bde72159138bbb74fc359b35eb515e276a7c0547d5eaa042520d3e6565e5590641db447277243cf24711dce533cfcaaf3a64415dcb9fa309fbf2de1ae4709c6450752acc0d45e01b67b55379bdf4e3dc32b2d89ad0a60c231d61f

Was this page helpful?