使用Foundry和Viem与链上合约交互

合约部分

Foundry新建项目:

forge init viem-test

编译及测试合约:

forge build
foege test

启动本地测试链:

anvil --chain-id 1234 -b 10
# 链id 1234
# 每10s生成一个区块

script 目录中新建Deploy.sol:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "forge-std/Script.sol";
import "../src/Counter.sol";

contract Deploy is Script {
    function run() external {
        vm.broadcast();
        new Counter();
    }
}

部署合约:

forge script script/Deploy.sol --rpc-url http://127.0.0.1:8545 --broadcast --sender 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

Viem交互部分

新建一个文件夹,在其中初始化项目,并安装依赖:

pnpm init

pnpm install viem dotenv
pnpm install -D typescript ts-node @types/node

初始化typescript配置文件:

npx tsc --init

创建index.ts文件:

import { defineChain, createPublicClient, http, createWalletClient } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import dotenv from "dotenv";
import { abi, contract_address } from "./contract/Conunter";
import { availableMemory } from "process";

// 定义本地链,viem集成了很多链,在viem/chains中可以找到
const localChain = defineChain({
    id:1234,
    name: 'Local Testnet',
    network: 'local',
    nativeCurrency: {
        name: 'ETH',
        symbol: 'ETH',
        decimals: 18,
    },
    rpcUrls: {
        default: {
            http: ['http://127.0.0.1:8545'],
        },
    },
    testnet: true
});

// 创建一个公共客户端
const client = createPublicClient({
    chain: localChain,
    transport: http(),
});

// 提取私钥
dotenv.config();
// 生成钱包账户
const account = privateKeyToAccount(`0x${process.env.PRIVATE_KEY}`);
// 创建一个钱包客户端
const walletClient = createWalletClient({
    account,
    chain: localChain,
    transport: http(),
});

// 获取合约中的number
async function getNumber() {
    const number = await client.readContract({
        address: contract_address,
        abi: abi,
        functionName: "number",
    });
    console.log(number);
}

async function main() {
    getNumber();

    // 调用合约中的setNumber,使用writeContract
    const transaction = await walletClient.writeContract({
        address: contract_address,
        abi: abi,
        functionName: "setNumber",
        args: [BigInt(100)],
    });
    // 等待交易被确认,并返回交易收据
    const receipt = await client.waitForTransactionReceipt({ hash: transaction });
    console.log(receipt);

    getNumber();
}

main();

创建contract/Counter.ts,往其中写入abi及部署的地址,在定义的后面加入as const,这样框架更好识别:

export const abi = [
    {
        "inputs": [],
        "stateMutability": "nonpayable",
        "type": "function",
        "name": "increment"
    },
    {
        "inputs": [],
        "stateMutability": "view",
        "type": "function",
        "name": "number",
        "outputs": [
            {
                "internalType": "uint256",
                "name": "",
                "type": "uint256"
            }
        ]
    },
    {
        "inputs": [
            {
                "internalType": "uint256",
                "name": "newNumber",
                "type": "uint256"
            }
        ],
        "stateMutability": "nonpayable",
        "type": "function",
        "name": "setNumber"
    }
] as const;

export const contract_address = "0x5FbDB2315678afecb367f032d93F642f64180aa3" as const;

创建.env文件,存入我们的私钥,供后续发起交易:

PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

package.json中加入start命令:

{
  "name": "viem-interact",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "ts-node index.ts"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.11.1",
  "dependencies": {
    "dotenv": "^16.5.0",
    "viem": "^2.30.6"
  },
  "devDependencies": {
    "@types/node": "^22.15.29",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3"
  }
}

执行pnpm start,可以看到以下结果:

(common) ➜  viem-interact pnpm start

> viem-interact@1.0.0 start /Users/xxx/Desktop/viem-start/viem-interact
> ts-node index.ts

0n
{
  type: 'eip1559',
  status: 'success',
  cumulativeGasUsed: 43696n,
  logs: [],
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  transactionHash: '0x9c1faf0ec7d1870571b4146528977bdaee0fe33d66fcbaea84b150634c1fd95e',
  transactionIndex: 0,
  blockHash: '0x1f7cd7f75936fc125091fe3ade3cd04ac4e543b5652cd25a4aa7594513d4dcf3',
  blockNumber: 95n,
  gasUsed: 43696n,
  effectiveGasPrice: 1000003547n,
  blobGasPrice: 1n,
  from: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
  to: '0x5fbdb2315678afecb367f032d93f642f64180aa3',
  contractAddress: null
}
100n
posted @ 2025-06-03 19:50  -WZM-  阅读(62)  评论(0)    收藏  举报