The Elder Helper class provides a convenient way to interact with rollups through a simple, object-oriented interface. This guide explains how to use the Elder Helper class in your applications to connect to the Elder network and send transactions to your rollup.
The Elder Helper class encapsulates the functionality needed to interact with rollups, including connecting to the Elder network, managing Elder addresses and public keys, sending transactions, and handling configuration parameters.

Class Structure

import { 
  ElderConfig, 
  eth_broadcastTx, 
  eth_getElderAccountInfoFromSignature, 
  eth_getElderMsgAndFeeTxRaw, 
} from "elderjs"; 
import { Signer } from "ethers"; 
import { parseEther } from "ethers/lib/utils"; 
import { TransactionLike } from "./ethers"; 

const ChainName = "testnet-4"; 
export const ELDER_REST = process.env.NEXT_PUBLIC_ELDER_REST!; 
export const ELDER_RPC = process.env.NEXT_PUBLIC_ELDER_RPC!; 
export type BigNumberish = string | number | bigint; 
export type CreateParams = { 
  rpc?: string; 
  rest?: string; 
  chainName?: string; 
  rollID: number; 
  rollChainID: bigint; 
  eth_rpc: string; 
  message?: string; 
}; 

if (!ELDER_REST || !ELDER_RPC) 
  throw new Error( 
    '"NEXT_PUBLIC_ELDER_REST" or "NEXT_PUBLIC_ELDER_RPC" missing in environment' 
  ); 

export class Elder { 
  private _elder_rpc: string; 
  private _elder_rest: string; 
  private chainName: string; 
  private rollID: number; 
  private rollChainID: bigint; 
  private eth_rpc: string; 

  public elder_address: string | null = null; 
  public elder_pub_key: string | null = null; 

  constructor(params: CreateParams) { 
    this._elder_rest = params.rest || ELDER_REST; 
    this._elder_rpc = params.rpc || ELDER_RPC; 
    this.chainName = params.chainName || ChainName; 
    this.rollID = params.rollID; 
    this.rollChainID = params.rollChainID; 
    this.eth_rpc = params.eth_rpc; 
  } 

  static async connect(signer: Signer, params: CreateParams) { 
    const message = 
      params.message || `Connect to ${params.chainName || ChainName}`; 
    const signature = await signer.signMessage(message); 

    const { recoveredPublicKey, elderAddr } = 
      await eth_getElderAccountInfoFromSignature(message, signature); 

    const obj = new Elder(params); 
    obj.elder_address = elderAddr; 
    obj.elder_pub_key = recoveredPublicKey; 

    return obj; 
  } 

  async sendTransaction( 
    tx: TransactionLike<string>, 
    opts?: { gasLimit?: BigNumberish; value?: BigNumberish } 
  ) { 
    opts = { 
      gasLimit: opts?.gasLimit || 10_000_000, 
      value: opts?.value || parseEther("0").toBigInt(), 
    }; 

    if ( 
      this.elder_address === null || 
      this.elder_pub_key === null || 
      typeof this.config === "undefined" 
    ) 
      throw new Error("network not connected"); 

    let { tx_hash, rawTx } = await eth_getElderMsgAndFeeTxRaw( 
      tx, 
      this.elder_address, 
      this.elder_pub_key, 
      opts.gasLimit!, 
      opts.value!, 
      this.config 
    ); 
    let broadcastResult = await eth_broadcastTx(rawTx, this.config.rpc); 

    return { tx_hash, result: broadcastResult }; 
  } 

  get config(): ElderConfig | undefined { 
    if ( 
      typeof this.chainName !== "undefined" && 
      typeof this.rollID !== "undefined" && 
      typeof this.rollChainID !== "undefined" && 
      typeof this.eth_rpc !== "undefined" 
    ) { 
      return { 
        rest: this._elder_rest, 
        rpc: this._elder_rpc, 
        chainName: this.chainName, 
        rollID: this.rollID, 
        rollChainID: Number(this.rollChainID), 
        eth_rpc: this.eth_rpc, 
      }; 
    } 

    return undefined; 
  } 

  get address(): string | undefined { 
    return this.elder_address || undefined; 
  } 
}

How to Use the Elder Helper

Setting Up Environment Variables

Before using the Elder Helper class, make sure to set up the required environment variables:
NEXT_PUBLIC_ELDER_REST=https://your-elder-rest-url
NEXT_PUBLIC_ELDER_RPC=https://your-elder-rpc-url

Connecting to the Elder Network

import { Elder } from "./path-to-elder-helper";
import { Wallet } from "ethers";

// Initialize your wallet with a private key
const wallet = new Wallet("your-private-key");

// Connect to Elder network
const elder = await Elder.connect(wallet, {
  rollID: 1,
  rollChainID: 1n, // Using BigInt for chain ID
  eth_rpc: "https://your-eth-rpc-url",
  // Optional parameters
  chainName: "your-chain-name", // Defaults to "testnet-4"
  message: "Custom connection message", // Custom message to sign
});

// Now you can access the Elder address
console.log("Elder address:", elder.address);

Sending Transactions

import { Contract } from "ethers";
import { parseEther } from "ethers/lib/utils";

// Prepare a transaction (example with a contract)
const contract = new Contract(contractAddress, contractABI, wallet);
const tx = await contract.populateTransaction.transfer(
  recipientAddress,
  parseEther("0.1")
);

// Send the transaction using the Elder helper
const { tx_hash, result } = await elder.sendTransaction(tx, {
  gasLimit: 1000000,
  value: parseEther("0.01").toBigInt(), // Amount of ETH to send with the transaction
});

console.log(`Transaction sent with hash: ${tx_hash}`);
console.log("Transaction result:", result);

Benefits of Using the Elder Helper Class

  1. Simplified Interface: The Elder Helper provides a clean, object-oriented interface for interacting with rollups.
  2. State Management: The class maintains the state of your connection, including Elder address and public key.
  3. Configuration Handling: The class manages configuration parameters and provides sensible defaults.
  4. Error Handling: Built-in validation ensures that required parameters are provided before sending transactions.

Complete Example

import { Elder } from "./elder-helper";
import { Wallet, Contract } from "ethers";
import { parseEther } from "ethers/lib/utils";

async function main() {
  // Initialize wallet
  const wallet = new Wallet("your-private-key");
  
  // Connect to Elder network
  const elder = await Elder.connect(wallet, {
    rollID: 1,
    rollChainID: 1n,
    eth_rpc: "https://your-eth-rpc-url",
  });
  
  console.log("Connected with Elder address:", elder.address);
  
  // Create a contract instance
  const tokenContract = new Contract(
    "0xYourTokenContractAddress",
    ["function transfer(address to, uint256 amount) returns (bool)"],
    wallet
  );
  
  // Prepare a transaction
  const tx = await tokenContract.populateTransaction.transfer(
    "0xRecipientAddress",
    parseEther("1.0")
  );
  
  // Send the transaction
  try {
    const { tx_hash, result } = await elder.sendTransaction(tx);
    console.log(`Transaction sent with hash: ${tx_hash}`);
    console.log("Result:", result);
  } catch (error) {
    console.error("Failed to send transaction:", error);
  }
}

useElder React Hook

The useElder React hook provides a convenient way to manage Elder network connections and transactions in your React components.

Implementation

import { useState } from "react";
import { BrowserProvider } from "ethers";
import { eth_getElderAccountInfoFromSignature, eth_broadcastTx, eth_getElderMsgAndFeeTxRaw } from "elderjs";

export function useElder() {
  // ...hook implementation as in the Elder docs...
}

Using the useElder Hook in React Components

import { useElder } from './path-to-useElder';
import { useEffect, useState } from 'react';
import { BrowserProvider } from 'ethers';

function ElderConnectButton() {
  const elder = useElder();
  const [isConnected, setIsConnected] = useState(false);
  
  async function handleConnect() {
    try {
      // Request access to the user's Ethereum wallet
      const provider = new BrowserProvider(window.ethereum);
      const signer = await provider.getSigner();
      
      // Connect to Elder network
      await elder.connect(signer, {
        roll_id: 1,
        chain_id: 1n, // Using BigInt for chain ID
        eth_rpc: "https://your-eth-rpc-url",
      });
      
      setIsConnected(true);
    } catch (error) {
      console.error("Failed to connect:", error);
    }
  }
  
  return (
    <div>
      <button onClick={handleConnect} disabled={isConnected}>
        {isConnected ? 'Connected to Elder' : 'Connect to Elder'}
      </button>
      
      {isConnected && (
        <div>
          <p>Ethereum Address: {elder.address}</p>
          <p>Elder Address: {elder.elder_address}</p>
        </div>
      )}
    </div>
  );
}

Sending Transactions with useElder

import { useElder } from './path-to-useElder';
import { useState } from 'react';
import { Contract } from 'ethers';
import { parseEther } from 'ethers/lib/utils';

function SendTransactionComponent({ contractAddress, contractABI }) {
  const elder = useElder();
  const [recipient, setRecipient] = useState('');
  const [amount, setAmount] = useState('');
  const [txHash, setTxHash] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  
  async function handleSendTransaction() {
    if (!elder.signer || !recipient || !amount) return;
    
    setIsLoading(true);
    
    try {
      // Create contract instance
      const contract = new Contract(contractAddress, contractABI, elder.signer);
      
      // Prepare transaction
      const tx = await contract.populateTransaction.transfer(
        recipient,
        parseEther(amount)
      );
      
      // Send transaction
      const { tx_hash } = await elder.sendTransaction(tx);
      setTxHash(tx_hash);
    } catch (error) {
      console.error("Transaction failed:", error);
    } finally {
      setIsLoading(false);
    }
  }
  
  return (
    <div>
      <h2>Send Tokens</h2>
      <div>
        <input 
          type="text" 
          placeholder="Recipient Address" 
          value={recipient} 
          onChange={(e) => setRecipient(e.target.value)} 
        />
      </div>
      <div>
        <input 
          type="text" 
          placeholder="Amount" 
          value={amount} 
          onChange={(e) => setAmount(e.target.value)} 
        />
      </div>
      <button 
        onClick={handleSendTransaction} 
        disabled={isLoading || !elder.signer}
      >
        {isLoading ? 'Sending...' : 'Send'}
      </button>
      
      {txHash && (
        <div>
          <p>Transaction sent! Hash: {txHash}</p>
        </div>
      )}
    </div>
  );
}

Next Steps

Now that you understand how to use the Elder Helper class and the useElder React hook, you can:
  • Integrate them into your dApps
  • Create reusable transaction patterns
  • Build higher-level abstractions on top of these tools

The Elder Helper class and React hook make it easy to build powerful, user-friendly rollup integrations on Shib Alpha Layer. Start building today and unlock the full potential of your rollup!