Transaction Preparation
This guide explains how to use transaction preparation methods in the Steer Protocol staking system. These methods allow you to prepare transaction data without executing them, giving you more control over the transaction process.
Overview
The Steer Protocol SDK provides three main transaction preparation methods:
prepareStakeTx
prepareWithdrawTx
prepareGetRewardTx
These methods are useful when you need to:
- Customize transaction handling
- Implement your own transaction signing logic
- Batch multiple transactions
- Simulate transactions before execution
- Implement custom gas strategies
Preparing Stake Transactions
The prepareStakeTx
method prepares the transaction data for staking tokens:
interface StakeParams {
stakingPool: Address;
amount: bigint;
}
// Example usage
const stakeTx = await steerClient.staking.prepareStakeTx({
stakingPool: poolAddress,
amount: BigInt('1000000000000000000') // 1 token with 18 decimals
});
if (stakeTx.success) {
const { address, abi, functionName, args } = stakeTx.data;
// Example: Using the prepared data with your own contract caller
const { request } = await publicClient.simulateContract({
address,
abi,
functionName,
args,
account: userAddress
});
// Execute the transaction with custom parameters
const hash = await walletClient.writeContract({
...request,
gasLimit: calculateGasLimit(request),
maxFeePerGas: calculateMaxFee()
});
}
Preparing Withdraw Transactions
The prepareWithdrawTx
method prepares the transaction data for withdrawing staked tokens:
interface WithdrawParams {
stakingPool: Address;
amount: bigint;
}
// Example usage
const withdrawTx = await steerClient.staking.prepareWithdrawTx({
stakingPool: poolAddress,
amount: BigInt('1000000000000000000')
});
if (withdrawTx.success) {
const { address, abi, functionName, args } = withdrawTx.data;
// Example: Custom gas estimation
const gasEstimate = await publicClient.estimateContractGas({
address,
abi,
functionName,
args,
account: userAddress
});
// Execute with custom gas settings
const hash = await walletClient.writeContract({
address,
abi,
functionName,
args,
gas: gasEstimate * BigInt(120) / BigInt(100) // Add 20% buffer
});
}
Preparing Reward Claim Transactions
The prepareGetRewardTx
method prepares the transaction data for claiming rewards:
interface GetRewardParams {
stakingPool: Address;
}
// Example usage
const claimTx = await steerClient.staking.prepareGetRewardTx({
stakingPool: poolAddress
});
if (claimTx.success) {
const { address, abi, functionName, args } = claimTx.data;
// Example: Transaction simulation before execution
try {
await publicClient.simulateContract({
address,
abi,
functionName,
args,
account: userAddress
});
// Proceed with execution if simulation succeeds
const hash = await walletClient.writeContract({
address,
abi,
functionName,
args
});
} catch (error) {
console.error('Transaction would fail:', error);
}
}
Advanced Usage
Batching Multiple Transactions
You can prepare multiple transactions and execute them together:
async function batchStakingOperations(
stakingPool: Address,
stakeAmount: bigint
) {
// Prepare multiple transactions
const [stakeTx, claimTx] = await Promise.all([
steerClient.staking.prepareStakeTx({
stakingPool,
amount: stakeAmount
}),
steerClient.staking.prepareGetRewardTx({
stakingPool
})
]);
// Execute transactions in sequence
if (stakeTx.success && claimTx.success) {
const transactions = [
{
...stakeTx.data,
description: 'Stake tokens'
},
{
...claimTx.data,
description: 'Claim rewards'
}
];
// Execute each transaction
for (const tx of transactions) {
try {
const hash = await walletClient.writeContract({
address: tx.address,
abi: tx.abi,
functionName: tx.functionName,
args: tx.args
});
console.log(`${tx.description} hash:`, hash);
} catch (error) {
console.error(`${tx.description} failed:`, error);
}
}
}
}
Custom Gas Strategies
Implement custom gas strategies using prepared transactions:
async function executeWithGasStrategy(
preparedTx: any,
maxRetries: number = 3
) {
const { address, abi, functionName, args } = preparedTx;
for (let i = 0; i < maxRetries; i++) {
try {
// Get current gas prices
const gasPrice = await publicClient.getGasPrice();
const maxPriorityFeePerGas = calculatePriorityFee(i);
// Execute with calculated gas parameters
const hash = await walletClient.writeContract({
address,
abi,
functionName,
args,
maxFeePerGas: gasPrice * BigInt(120) / BigInt(100),
maxPriorityFeePerGas
});
return hash;
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
}
Transaction Simulation
Simulate transactions before execution to validate them:
async function simulateStakingTx(
preparedTx: any,
userAddress: Address
) {
const { address, abi, functionName, args } = preparedTx;
try {
// Simulate the transaction
const { request } = await publicClient.simulateContract({
address,
abi,
functionName,
args,
account: userAddress
});
// Calculate gas requirements
const gasEstimate = await publicClient.estimateContractGas(request);
// Get user's balance
const balance = await publicClient.getBalance({ address: userAddress });
// Validate user has enough for gas
if (balance < gasEstimate * await publicClient.getGasPrice()) {
throw new Error('Insufficient funds for gas');
}
return {
canExecute: true,
gasEstimate,
request
};
} catch (error) {
return {
canExecute: false,
error
};
}
}
Best Practices
Always Simulate First
// Simulate before execution
const simulation = await simulateStakingTx(preparedTx, userAddress);
if (!simulation.canExecute) {
console.error('Transaction would fail:', simulation.error);
return;
}Handle Nonce Management
// Get the next nonce
const nonce = await publicClient.getTransactionCount({
address: userAddress
});
// Execute with specific nonce
const hash = await walletClient.writeContract({
...preparedTx,
nonce
});Implement Proper Error Recovery
async function executeWithRetry(preparedTx: any) {
const maxRetries = 3;
for (let i = 0; i < maxRetries; i++) {
try {
return await walletClient.writeContract(preparedTx);
} catch (error) {
if (i === maxRetries - 1) throw error;
if (error.message.includes('nonce too low')) {
preparedTx.nonce++;
}
await new Promise(r => setTimeout(r, 1000));
}
}
}Monitor Transaction Status
async function monitorTransaction(hash: Hash) {
const receipt = await publicClient.waitForTransactionReceipt({
hash,
confirmations: 2
});
return {
status: receipt.status,
gasUsed: receipt.gasUsed,
effectiveGasPrice: receipt.effectiveGasPrice
};
}