Transaction data encoding
This guide will help you understand how to encode the data and execute smart contract interactions.
Encoding the data
property
The data
property in eth_sendTransaction
is a hexadecimal string that represents the encoded input data for the transaction.
Proper encoding is crucial for the transaction to be executed correctly. Here's how to encode the data
property for different scenarios:
1. Function Signature
- The first 4 bytes of the
data
property are typically the function signature. - This is calculated as the first 4 bytes of the Keccak-256 hash of the function name and its parameter types.
- Example: For
transfer(address,uint256)
, the signature would be0xa9059cbb
.
2. Parameter Encoding
- Parameters are encoded according to the Ethereum ABI specification.
- Each parameter is padded to 32 bytes (256 bits).
- Address types are left-padded with zeros.
- Integer types are big-endian encoded and left-padded with zeros.
- Strings and bytes are right-padded with zeros.
This guide only covers the very basic encoding of function signatures and parameters. For more complex data structures, refer to the Solidity ABI Specification.
2a. Using ethers.js for Encoding
The ethers.js library provides utilities for encoding:
const ABI = ['function transfer(address to, uint256 amount)'];
const interface = new ethers.utils.Interface(ABI);
const data = interface.encodeFunctionData('transfer', [recipientAddress, amount]);
2b. Manual Encoding
For simple cases, you can concatenate the function signature and encoded parameters:
const functionSignature = '0xa9059cbb';
const encodedAddress = ethers.utils.defaultAbiCoder.encode(['address'], [recipientAddress]).slice(2);
// Might result in: '4a9045efa0ca935ff426c54a4b92de6d3bf4b0ff'
const encodedAmount = ethers.utils.defaultAbiCoder.encode(['uint256'], [amount]).slice(2); // amount = 123
// Results in: '000000000000000000000000000000000000000000000000000000000000007b'
const data = functionSignature + encodedAddress + encodedAmount;
// Results in: '0xeea987680000000000000000000000004a9045efa0ca935ff426c54a4b92de6d3bf4b0ff000000000000000000000000000000000000000000000000000000000000007b'
This example uses ethers.js
to encode the data, but you can also encode values manually using Decimal to Hexadecimal converter.
The slice(2)
function is used to remove the 0x
prefix from the encoded data so they are not included when concatinating the strings for the final data result.
2c. Handling Dynamic Types
Dynamic types in Solidity, such as arrays and strings, require special handling during ABI encoding:
- Offset: For dynamic types, the main data area contains a pointer (offset) to where the actual data is stored.
- Length: The actual data is prefixed with its length.
This two-step process allows for efficient encoding of complex data structures.
Example: Encoding a String
Let's say we have a function setMessage(string message)
:
- The function signature would be encoded first.
- Then, a 32-byte offset pointing to the start of the string data.
- At that offset, we'd have:
- 32 bytes representing the length of the string
- The actual string data, padded to a multiple of 32 bytes
const ABI = ['function setMessage(string message)'];
const interface = new ethers.utils.Interface(ABI);
const message = "Hello, World!";
const data = interface.encodeFunctionData('setMessage', [message]);
console.log(data);
// 0x368b87720000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20576f726c642100000000000000000000000000000000000000000000
// Breakdown:
// 0x368b8772 // Function signature
// 0000000000000000000000000000000000000000000000000000000000000020 // Offset to string data
// 000000000000000000000000000000000000000000000000000000000000000d // Length of string
// 48656c6c6f2c20576f726c642100000000000000000000000000000000000000000000 // Encoded string
3. Example Usage
The following examples are for demonstration purposes and may not work as-is in your project. Please adapt them to your specific requirements.
The send transaction methods only take 3 parameters, to
, data
, value
. Other method properties typically found in a standard Ethereum transaction object are abstracted away from the user.
Also note, the from
parameter is automatically sent with the users Passport wallet address.
Unity SDK
Please refer to the Unity SDK documentation for setting up the Unity SDK.
using Immutable.Passport.Model;
TransactionRequest request = new TransactionRequest()
{
to = "0x...", // The contract address
value = 0,
data = "0x0x368b8772..." // The encoded data
}
string transactionHash = await passport.ZkEvmSendTransaction(request);
Typescript SDK
This requires a connected provider. Please refer to the Immutable Passport docs for an example of conecting a wallet.
const transactionHash = await provider.request({
method: 'eth_sendTransaction',
params: [
{
to: '0x...', // The contract address
data: '0x0x368b8772...', // The encoded data
value: 0
}
]
});
console.log(transactionHash); // ['0x...']