Writing to Contracts
Send transactions and interact with smart contracts
Learn how to send transactions and write to smart contracts on RISE using Ethers.js.
Setup Signer
Create a wallet to sign transactions:
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://testnet.riselabs.xyz');
const wallet = new ethers.Wallet('YOUR_PRIVATE_KEY', provider);
console.log('Signing transactions as:', wallet.address);Send ETH
Transfer ETH to another address:
const tx = await wallet.sendTransaction({
to: '0x...',
value: ethers.parseEther('0.1') // 0.1 ETH
});
console.log('Transaction hash:', tx.hash);
// Wait for confirmation
const receipt = await tx.wait();
console.log('Transaction confirmed in block:', receipt.blockNumber);
console.log('Gas used:', receipt.gasUsed.toString());Write to Contract
Call a contract function that modifies state:
const contractAddress = '0x...';
const abi = [
'function transfer(address to, uint256 amount) returns (bool)'
];
const contract = new ethers.Contract(contractAddress, abi, wallet);
// Send transaction
const tx = await contract.transfer('0x...', ethers.parseEther('10'));
console.log('Transaction hash:', tx.hash);
// Wait for confirmation
const receipt = await tx.wait();
console.log('Transaction confirmed!');
console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');Deploy Contract
Deploy a new smart contract:
const abi = [ /* contract ABI */ ];
const bytecode = '0x...'; // Contract bytecode
// Create contract factory
const ContractFactory = new ethers.ContractFactory(abi, bytecode, wallet);
// Deploy contract
const contract = await ContractFactory.deploy(
// constructor arguments
'My Token',
'MTK',
18
);
console.log('Deploying contract...');
console.log('Transaction hash:', contract.deploymentTransaction().hash);
// Wait for deployment
await contract.waitForDeployment();
const address = await contract.getAddress();
console.log('Contract deployed at:', address);Wait for Confirmations
Wait for multiple confirmations:
const tx = await contract.transfer('0x...', ethers.parseEther('10'));
console.log('Transaction sent:', tx.hash);
// Wait for 1 confirmation (default)
const receipt1 = await tx.wait();
console.log('1 confirmation');
// Wait for 3 confirmations
const receipt3 = await tx.wait(3);
console.log('3 confirmations');Set Gas Parameters
Control gas settings:
// Estimate gas first
const gasEstimate = await contract.transfer.estimateGas(
'0x...',
ethers.parseEther('10')
);
console.log('Estimated gas:', gasEstimate.toString());
// Send with gas limit
const tx = await contract.transfer(
'0x...',
ethers.parseEther('10'),
{
gasLimit: gasEstimate * 120n / 100n // Add 20% buffer
}
);EIP-1559 Transactions
const feeData = await provider.getFeeData();
const tx = await contract.transfer(
'0x...',
ethers.parseEther('10'),
{
maxFeePerGas: feeData.maxFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
}
);Sign Messages
Sign a message with your private key:
const message = 'Hello RISE!';
const signature = await wallet.signMessage(message);
console.log('Signature:', signature);
// Verify signature
const recoveredAddress = ethers.verifyMessage(message, signature);
console.log('Signer:', recoveredAddress);
console.log('Valid:', recoveredAddress === wallet.address);Sign Typed Data (EIP-712)
Sign structured data:
const domain = {
name: 'My Dapp',
version: '1',
chainId: 11155931,
verifyingContract: '0x...'
};
const types = {
Mail: [
{ name: 'from', type: 'address' },
{ name: 'to', type: 'address' },
{ name: 'contents', type: 'string' }
]
};
const value = {
from: wallet.address,
to: '0x...',
contents: 'Hello!'
};
const signature = await wallet.signTypedData(domain, types, value);
console.log('Signature:', signature);Handle Transaction Errors
Properly handle errors:
try {
const tx = await contract.transfer('0x...', ethers.parseEther('10'));
const receipt = await tx.wait();
if (receipt.status === 0) {
console.log('Transaction failed');
} else {
console.log('Transaction successful');
}
} catch (error) {
if (error.code === 'INSUFFICIENT_FUNDS') {
console.error('Not enough funds');
} else if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
console.error('Transaction will likely fail');
} else if (error.code === 'NONCE_EXPIRED') {
console.error('Nonce already used');
} else {
console.error('Transaction failed:', error.message);
}
}Cancel/Replace Transaction
Replace a pending transaction:
// Send initial transaction
const tx = await wallet.sendTransaction({
to: '0x...',
value: ethers.parseEther('0.1')
});
console.log('Transaction sent:', tx.hash);
// Replace with higher gas price (same nonce)
const nonce = await provider.getTransactionCount(wallet.address, 'pending');
const feeData = await provider.getFeeData();
const replacementTx = await wallet.sendTransaction({
to: '0x...',
value: ethers.parseEther('0.1'),
nonce: tx.nonce,
maxFeePerGas: feeData.maxFeePerGas * 2n, // Double the fee
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas * 2n
});
console.log('Replacement transaction:', replacementTx.hash);Batch Transactions
Send multiple transactions:
const recipients = ['0x...', '0x...', '0x...'];
const amount = ethers.parseEther('0.1');
const transactions = await Promise.all(
recipients.map(async (recipient, i) => {
const nonce = await provider.getTransactionCount(wallet.address, 'latest') + i;
return wallet.sendTransaction({
to: recipient,
value: amount,
nonce
});
})
);
console.log('Transactions sent:', transactions.map(tx => tx.hash));
// Wait for all confirmations
const receipts = await Promise.all(transactions.map(tx => tx.wait()));
console.log('All transactions confirmed');Example: ERC-20 Token Operations
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://testnet.riselabs.xyz');
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const tokenAddress = '0x8a93d247134d91e0de6f96547cb0204e5be8e5d8'; // USDC
const abi = [
'function transfer(address to, uint256 amount) returns (bool)',
'function approve(address spender, uint256 amount) returns (bool)',
'function transferFrom(address from, address to, uint256 amount) returns (bool)',
'function balanceOf(address) view returns (uint256)',
'function allowance(address owner, address spender) view returns (uint256)'
];
const token = new ethers.Contract(tokenAddress, abi, wallet);
async function tokenOperations() {
const recipient = '0x...';
const amount = ethers.parseUnits('10', 6); // 10 USDC (6 decimals)
// Check balance
const balance = await token.balanceOf(wallet.address);
console.log('Balance:', ethers.formatUnits(balance, 6), 'USDC');
if (balance < amount) {
console.error('Insufficient balance');
return;
}
// Transfer tokens
console.log('Transferring 10 USDC...');
const transferTx = await token.transfer(recipient, amount);
console.log('Transaction hash:', transferTx.hash);
const transferReceipt = await transferTx.wait();
console.log('Transfer confirmed in block:', transferReceipt.blockNumber);
// Approve spending
const spender = '0x...';
console.log('Approving spender...');
const approveTx = await token.approve(spender, amount);
await approveTx.wait();
console.log('Approval confirmed');
// Check allowance
const allowance = await token.allowance(wallet.address, spender);
console.log('Allowance:', ethers.formatUnits(allowance, 6), 'USDC');
}
tokenOperations().catch(console.error);Example: Contract Deployment
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://testnet.riselabs.xyz');
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
// Simple ERC20 contract
const abi = [
'constructor(string name, string symbol, uint256 initialSupply)',
'function name() view returns (string)',
'function symbol() view returns (string)',
'function totalSupply() view returns (uint256)',
'function balanceOf(address) view returns (uint256)',
'function transfer(address to, uint256 amount) returns (bool)'
];
const bytecode = '0x...'; // Your compiled bytecode
async function deployToken() {
console.log('Deploying from:', wallet.address);
// Get balance
const balance = await provider.getBalance(wallet.address);
console.log('Balance:', ethers.formatEther(balance), 'ETH');
// Create factory
const factory = new ethers.ContractFactory(abi, bytecode, wallet);
// Deploy
console.log('Deploying token...');
const token = await factory.deploy(
'My Token',
'MTK',
ethers.parseEther('1000000') // 1 million tokens
);
console.log('Deployment transaction:', token.deploymentTransaction().hash);
// Wait for deployment
await token.waitForDeployment();
const address = await token.getAddress();
console.log('Token deployed at:', address);
console.log('Explorer:', `https://explorer.testnet.riselabs.xyz/address/${address}`);
// Verify deployment
const name = await token.name();
const symbol = await token.symbol();
const totalSupply = await token.totalSupply();
console.log('\nToken Info:');
console.log('Name:', name);
console.log('Symbol:', symbol);
console.log('Total Supply:', ethers.formatEther(totalSupply));
}
deployToken().catch(console.error);Next Steps
- Contract Addresses - Find deployed contract addresses on RISE
- Testnet Tokens - Get testnet tokens for testing
- Network Details - RISE network configuration