@stable-io/cctp-sdk-viem
Version:
Viem support for the CCTP SDK
115 lines • 4.94 kB
JavaScript
// Copyright (c) 2025 Stable Technologies Inc
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
import { domainOfChainId, gasTokenOf, isEvmDomain } from "@stable-io/cctp-sdk-definitions";
import { encoding } from "@stable-io/utils";
import { wordSize } from "@stable-io/cctp-sdk-evm";
import { createPublicClient, http } from "viem";
import { mainnet as ethereumMainnet, sepolia as ethereumTestnet, avalanche as avalancheMainnet, avalancheFuji as avalancheTestnet, optimism as optimismMainnet, optimismSepolia as optimismTestnet, arbitrum as arbitrumMainnet, arbitrumSepolia as arbitrumTestnet, base as baseMainnet, baseSepolia as baseTestnet, polygon as polygonMainnet, polygonAmoy as polygonTestnet, unichain as unichainMainnet, unichainSepolia as unichainTestnet, linea as lineaMainnet, lineaSepolia as lineaTestnet, sonic as sonicMainnet, sonicTestnet as sonicTestnet, worldchain as worldchainMainnet, worldchainSepolia as worldchainTestnet, } from "viem/chains";
// TODO: Wait for viem to support the codex blockchain
const codexMainnet = {};
const codexTestnet = {};
export const viemChainOf = {
Mainnet: {
Ethereum: ethereumMainnet,
Avalanche: avalancheMainnet,
Optimism: optimismMainnet,
Arbitrum: arbitrumMainnet,
Base: baseMainnet,
Polygon: polygonMainnet,
Unichain: unichainMainnet,
Linea: lineaMainnet,
Codex: codexMainnet,
Sonic: sonicMainnet,
Worldchain: worldchainMainnet,
},
Testnet: {
Ethereum: ethereumTestnet,
Avalanche: avalancheTestnet,
Optimism: optimismTestnet,
Arbitrum: arbitrumTestnet,
Base: baseTestnet,
Polygon: polygonTestnet,
Unichain: unichainTestnet,
Linea: lineaTestnet,
Codex: codexTestnet,
Sonic: sonicTestnet,
Worldchain: worldchainTestnet,
},
};
export class ViemEvmClient {
network;
domain;
client;
gasTokenCtr;
constructor(network, domain, client) {
this.network = network;
this.domain = domain;
this.client = client;
this.gasTokenCtr = gasTokenOf(this.domain);
}
static fromViemClient(client) {
const network = (client.chain.testnet ? "Testnet" : "Mainnet");
const domains = domainOfChainId(network, BigInt(client.chain.id));
const domain = domains.find(domain => isEvmDomain(domain));
return new ViemEvmClient(network, domain, client);
}
static fromNetworkAndDomain(network, domain, rpcUrl) {
const client = createPublicClient({
chain: viemChainOf[network][domain],
transport: http(rpcUrl),
});
return new ViemEvmClient(network, domain, client);
}
async getBalance(address) {
const balance = await this.client.getBalance({ address: address.unwrap() });
return this.gasToken(balance);
}
async getLatestBlock() {
return this.client.getBlockNumber();
}
async getStorageAt(contract, slot) {
return this.client.getStorageAt({
address: contract.unwrap(),
slot: `0x${slot.toString(16)}`,
}).then(res => (res ? encoding.hex.decode(res) : new Uint8Array(wordSize)));
}
async ethCall(tx) {
const txArgs = {
account: tx.from?.unwrap(),
to: tx.to.unwrap(),
data: encoding.hex.encode(tx.data, true),
value: tx.value?.toUnit("atomic"),
accessList: this.toViemAccessList(tx.accessList),
};
//type inference is not working here for some reason - when defining a concrete client locally
// via createPublicClient and invoking the call method, there's no complaint
const res = await this.client.call(txArgs);
return (res.data ? encoding.hex.decode(res.data) : new Uint8Array(0));
// return this.client.ethCall({ ...partialTx, to: partialTx.to.unwrap() });
}
async estimateGas(tx) {
const txArgs = {
account: tx.from?.unwrap(),
to: tx.to?.unwrap(),
data: tx.data ? encoding.hex.encode(tx.data, true) : undefined,
value: tx.value?.toUnit("atomic"),
accessList: this.toViemAccessList(tx.accessList),
};
return this.client.estimateGas(txArgs);
}
gasToken(amount) {
//not sure why tsc insists that gasTokenCtr doesn't return GasTokenOf<D> already
return this.gasTokenCtr(amount, "atomic");
}
toViemAccessList(accessList) {
return accessList
? accessList.map(al => ({
address: al.address.unwrap(),
storageKeys: al.storageKeys.map(sk => `0x${sk.toString(16)}`),
}))
: [];
}
}
//# sourceMappingURL=viemEvmClient.js.map