🤔 Got questions? Schedule an office hours session.
Virtual TestNets
Libraries
Viem

Viem

Viem is a TypeScript Interface for Ethereum that provides low-level stateless primitives for interacting with Ethereum.

You can interact with Virtual TestNets using the standard RPC methods when you copy the Public RPC, as well as custom methods available on Node RPC when you copy the Admin RPC.

Define a custom chain

Create a file tenderly.config.ts and use defineChain to create a chain object.

If you specified a unique Chain ID during Virtual TestNet creation, you need to create a new chain object and set the id to your chosen value.

virtual-testnets/src/tenderly.config.ts
import { defineChain } from 'viem';
import path from 'path';
 
export const vMainnet = defineChain({
  id: 73571,
  name: 'Virtual Ethereum Mainnet',
  nativeCurrency: { name: 'vEther', symbol: 'vETH', decimals: 18 },
  rpcUrls: {
    default: { http: [process.env.TENDERLY_VIRTUAL_MAINNET_RPC!] },
  },
  blockExplorers: {
    default: {
      name: 'Tenderly Explorer',
      url: process.env.TENDERLY_VIRTUAL_MAINNET_EXPLORER!
    },
  },
  contracts: {
    ensRegistry: {
      address: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
    },
    ensUniversalResolver: {
      address: '0xE4Acdd618deED4e6d2f03b9bf62dc6118FC9A4da',
      blockCreated: 16773775,
    },
    multicall3: {
      address: '0xca11bde05977b3631167028862be2a173976ca11',
      blockCreated: 14353601,
    },
  },
});

Define Viem actions for custom RPC methods

Define Viem actions for custom RPC methods, such as tenderly_setBalance tenderly_setErc20Balance, and simulate.

For example, the following file defines a tenderlySetBalance action that will invoke tenderly_setBalance.

You can find more actions here:

virtual-testnets/src/viem-tenderly-actions.ts
import { Client, Hex } from 'viem';
 
type TSetBalanceParams = [addresses: Hex[], value: Hex];
 
export async function tenderlySetBalance(client: Client, params: TSetBalanceParams) {
  return client.request<{
    method: 'tenderly_setBalance',
    Parameters: TSetBalanceParams,
    ReturnType: Hex
  }>({
    method: 'tenderly_setBalance',
    params: params,
  });
}

Call standard and custom RPC methods

In this example, you can see how to:

  • Call Viem’s createPublicClient and pass the following arguments:
  • chain: set to vMainnet we defined in the previous step
  • transport: either a call to http, and set to https RPC URL.
virtual-testnets/src/viem-call-balance-top-up.ts
import { createPublicClient, http } from "viem";
import { vMainnet } from "./tenderly.config";
import { tenderlySetBalance, tenderlySetErc20Balance } from "./viem-tenderly-actions";
 
(async () => {
  const client = createPublicClient({
    chain: vMainnet,
    transport: http(vMainnet.rpcUrls.default.http[0]),
  });
 
  console.log("Block Number", await client.getBlockNumber());
  console.log("Chain", await client.getChainId());
 
  const balanceTxs = await Promise.all([
    tenderlySetBalance(client, [
      ["0x0d2026b3EE6eC71FC6746ADb6311F6d3Ba1C000B", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"],
      "0xDE0B6B3A7640000"
    ]),
    tenderlySetErc20Balance(client, [
      "0xdAC17F958D2ee523a2206206994597C13D831ec7",
      "0x40BdB4497614bAe1A67061EE20AAdE3c2067AC9e",
      "0xDE0B6B3A7640000"
    ]),
    // USDT
    tenderlySetErc20Balance(client, [
      "0x6B175474E89094C44Da98b954EedeAC495271d0F",
      "0x40BdB4497614bAe1A67061EE20AAdE3c2067AC9e",
      "0xDE0B6B3A7640000"
    ]),
    tenderlySetErc20Balance(client, [
      "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE",
      "0x40BdB4497614bAe1A67061EE20AAdE3c2067AC9e",
      "0xDE0B6B3A7640000"
    ]),
  ]);
 
  console.log(
    "ETH balance, DAI and ShibaInu topups",
    balanceTxs.map(txHash => `${(vMainnet.blockExplorers.default.url)}/tx/${txHash}`));
})().catch(e => {
  console.error(e);
  process.exitCode = 1;
});

Simulate transactions using viem

To use Virtual TestNet’s Simulation RPC methods, simply request to make the call to tenderly_simulateTransaction:

virtual-testnets/src/viem-call-simulate.ts
import { createPublicClient, http } from "viem";
import { vMainnet } from "./tenderly.config";
 
const client = createPublicClient({
  chain: vMainnet,
  transport: http(vMainnet.rpcUrls.default[0])
});
 
const simulation = await client.request({
  method: "tenderly_simulateTransaction",
  params: [
    // transaction object
    {
      from: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
      to: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
      gas: "0x0",
      gasPrice: "0x0",
      value: "0x0",
      data: "0xa9059cbb00000000000000000000000020a5814b73ef3537c6e099a0d45c798f4bd6e1d60000000000000000000000000000000000000000000000000000000000000001",
    },
    // the block
    "latest",
  ],
});
 
console.log("Trace");
console.log(JSON.stringify(simulation.trace, null, 2));
console.log("Logs");
console.log(JSON.stringify(simulation.logs, null, 2));
console.log("Asset Changes");
console.log(JSON.stringify(simulation.assetChanges, null, 2));
console.log("Balance Changes");
console.log(JSON.stringify(simulation.balanceChanges, null, 2));

Trace transactions using Viem

Use tenderly_traceTransaction to get fully decoded transaction trace.

node/samples/viem/2-transaction-trace.ts
const txTrace = await client.request({
  method: "tenderly_traceTransaction",
  params: [
    // transaction hash
    "0x6b2264fa8e28a641d834482d250080b39cbbf39251344573c7504d6137c4b793"
  ],
});
 
 
console.log("Logs");
console.log(JSON.stringify(txTrace.logs, null, 2));
console.log("=======================================================");
console.log("Asset Changes");
console.log(JSON.stringify(txTrace.assetChanges, null, 2));
console.log("=======================================================");
console.log("Balance Changes");
console.log(JSON.stringify(txTrace.balanceChanges, null, 2));
console.log("=======================================================");