@biconomy/abstractjs
Version:
SDK for Biconomy integration with support for account abstraction, smart accounts, ERC-4337.
136 lines • 5.13 kB
JavaScript
import { encodeFunctionData } from "viem";
export const LARGE_DEFAULT_GAS_LIMIT = 700000n;
function createChainSpecificContract(abi, chainId, address) {
return new Proxy({}, {
get: (_, prop) => {
if (!abi.some((item) => item.type === "function" && item.name === prop)) {
throw new Error(`Function ${prop} not found in ABI`);
}
return ({ args, gasLimit = LARGE_DEFAULT_GAS_LIMIT, value = 0n }) => {
const params = {
abi,
functionName: prop,
args
};
const data = encodeFunctionData(params);
const call = {
to: address,
gasLimit,
value,
data
};
return {
calls: [call],
chainId
};
};
}
});
}
/**
* Creates a contract instance that can encode function calls across multiple chains
*
* @template TAbi - The contract ABI type
* @param config - Configuration for the multichain contract
* @param config.abi - {@link Abi} The contract's ABI
* @param config.deployments - Array of tuples containing [address, chainId] for each deployment
*
* @returns {@link MultichainContract} A contract instance that works across multiple chains
*
* @throws Error if attempting to access contract on an unsupported chain
* @throws Error if attempting to call a non-existent function
* @throws Error if attempting to read a non-view/pure function
*
* @example
* const mcUSDC = getMultichainContract({
* abi: erc20ABI,
* deployments: [
* ["0x7F5c764cBc14f9669B88837ca1490cCa17c31607", optimism.id], // Optimism USDC
* ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", base.id] // Base USDC
* ]
* });
*
* // Encode a transfer on Optimism
* const transferOp = mcUSDC.on(optimism.id).transfer({
* args: ["0x123...", BigInt("1000000")], // 1 USDC
* gasLimit: 100000n
* });
*
* // Read balances across multiple chains
* const balances = await mcUSDC.read({
* onChains: [optimism, base],
* functionName: "balanceOf",
* args: ["0x123..."],
* account: myMultichainAccount
* });
*/
export function getMultichainContract(config) {
const deployments = new Map(config.deployments.map((deployment) => {
const [address, chainId] = deployment;
return [chainId, address];
}));
return {
abi: config.abi,
deployments,
on: (chainId) => {
const address = deployments.get(chainId);
if (!address) {
throw new Error(`No deployment found for chain ${chainId}`);
}
return createChainSpecificContract(config.abi, chainId, address);
},
build: async (params) => {
const { data: { chainId, args: args_, gasLimit }, type: functionName } = params;
const address = deployments.get(chainId);
if (!address) {
throw new Error(`No deployment found for chain ${chainId}`);
}
const result = createChainSpecificContract(config.abi, chainId, address)[functionName]({
args: args_,
...(gasLimit ? { gasLimit } : {})
});
return {
chainId: chainId,
calls: result.calls
};
},
addressOn: (chainId) => {
const address = deployments.get(chainId);
if (!address) {
throw new Error(`No deployment found for chain ${chainId}`);
}
return address;
},
read: async (params) => {
const abiFunction = config.abi.find((item) => item.type === "function" &&
item.name === params.functionName &&
(item.stateMutability === "view" || item.stateMutability === "pure"));
if (!abiFunction) {
throw new Error(`Function ${params.functionName} not found in ABI or is not a read function`);
}
const results = await Promise.all(params.onChains.map(async (chain) => {
const address = deployments.get(chain.id);
if (!address) {
throw new Error(`No deployment found for chain ${chain.id}`);
}
const deployment = params.account.deploymentOn(chain.id);
if (!deployment) {
throw new Error(`No deployment found for chain ${chain.id}`);
}
const client = deployment.client;
const result = await client.readContract({
address,
abi: config.abi,
functionName: params.functionName,
args: params.args
});
return {
chainId: chain.id,
result: result
};
}));
return results;
}
};
}
//# sourceMappingURL=getMultichainContract.js.map