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}`);