RISE Logo-Light

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