Skip to main content

Contract Interaction with viem & ethers.js

Reading contract state

viem

import { createPublicClient, http } from 'viem';
import { specterTestnet } from './chains';

const client = createPublicClient({
chain: specterTestnet,
transport: http(),
});

// Read the current Merkle root
const root = await client.readContract({
address: '0xB7E37E652F3024bAaaf84b12ae301f8E1feC4D87',
abi: [{ name: 'getLastRoot', type: 'function', inputs: [], outputs: [{ type: 'bytes32' }], stateMutability: 'view' }],
functionName: 'getLastRoot',
});

// Check if a nullifier is spent
const isSpent = await client.readContract({
address: '0x0987cc3dE6f76c4c8834Dc6205De24968091C58b',
abi: [{ name: 'isSpent', type: 'function', inputs: [{ type: 'bytes32' }], outputs: [{ type: 'bool' }], stateMutability: 'view' }],
functionName: 'isSpent',
args: [nullifier],
});

ethers.js

import { ethers } from 'ethers';

const provider = new ethers.JsonRpcProvider('https://testnet.specterchain.com');

const tree = new ethers.Contract(
'0xB7E37E652F3024bAaaf84b12ae301f8E1feC4D87',
['function getLastRoot() view returns (bytes32)'],
provider
);

const root = await tree.getLastRoot();

Writing transactions

Commit native GHOST (viem)

import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(privateKey);
const walletClient = createWalletClient({
account,
chain: specterTestnet,
transport: http(),
});

const hash = await walletClient.writeContract({
address: '0x443434113980Ab9d5Eef0Ace7d1A29AB68Af6c70',
abi: [{ name: 'commitNative', type: 'function', inputs: [{ type: 'bytes32' }, { type: 'bytes32' }], stateMutability: 'payable' }],
functionName: 'commitNative',
args: [commitment, '0x' + '0'.repeat(64)],
value: parseEther('1'),
gasPrice: 1_000_000_000n,
});

Commit native GHOST (ethers.js)

const signer = new ethers.Wallet(privateKey, provider);
const vault = new ethers.Contract(
'0x443434113980Ab9d5Eef0Ace7d1A29AB68Af6c70',
['function commitNative(bytes32,bytes32) payable'],
signer
);

const tx = await vault.commitNative(commitment, ethers.ZeroHash, {
value: ethers.parseEther('1'),
gasPrice: 1_000_000_000n,
});
await tx.wait();

Event subscriptions

WebSocket (viem)

import { createPublicClient, webSocket } from 'viem';

const wsClient = createPublicClient({
chain: specterTestnet,
transport: webSocket('wss://testnet.specterchain.com/ws'),
});

const unwatch = wsClient.watchContractEvent({
address: '0xB7E37E652F3024bAaaf84b12ae301f8E1feC4D87',
abi: [{ name: 'CommitmentAdded', type: 'event', inputs: [{ name: 'commitment', type: 'bytes32', indexed: true }, { name: 'index', type: 'uint256' }] }],
eventName: 'CommitmentAdded',
onLogs: (logs) => console.log('New commitment:', logs),
});

Query historical events (ethers.js)

const filter = tree.filters.CommitmentAdded();
const events = await tree.queryFilter(filter, 0, 'latest');
console.log(`Total commitments: ${events.length}`);