-
I'm building an SDk that will use Viem for on chain functionality. One of my SDK functions basically passes through contract interaction arguments to the If I ignore the type errors the type inference does seem to work how I would expect async mint<TAbi extends Abi, TFunctionName extends string>({
address,
abi,
functionName,
args,
chainId,
}: ContractConfig<TAbi, TFunctionName, "nonpayable" | "payable"> & {
chainId: ChainId
}): Promise<string> {
const [walletAddress] = await this._walletClient.getAddresses()
const account = getAccount(walletAddress)
⌄------------------TS Error occurs here
const { request } = await this._publicClient.simulateContract({
address: address,
abi: abi,
functionName: functionName,
args: args,
account: account,
chain: CHAIN_INFO[chainId],
})
return await this._walletClient.writeContract({
...request,
assertChain: true,
})
} The TS error is the following
Is this something obvious im missing here? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
After toying around with the different options, I've concluded that (for now), the best option for an SDK imho is to not try and abstract away the signing & sending of transactions but simply provide convenience functions to produce the right, typed inputs for the // ./utils/viem.ts
import type { Abi, Narrow } from "abitype";
import type { ExtractArgsFromAbi, ExtractFunctionNameFromAbi } from "viem";
export type FunctionParams<TAbi extends Abi | readonly unknown[] = Abi, TFunctionName extends string = string> = {
abi: Narrow<TAbi>;
functionName: ExtractFunctionNameFromAbi<TAbi, TFunctionName>;
} & ExtractArgsFromAbi<TAbi, TFunctionName>;
export function prepareFunctionParams<TAbi extends Abi | readonly unknown[], TFunctionName extends string>({
abi,
args,
functionName,
}: FunctionParams<TAbi, TFunctionName>) {
const output: {
abi: Narrow<TAbi>;
args: ExtractArgsFromAbi<TAbi, TFunctionName>["args"];
functionName: TFunctionName;
} = {
functionName: functionName as TFunctionName,
abi,
args,
};
return output;
} // ./actions/vault-setup.ts
import { IFundDeployer } from "@enzymefinance/abis/IFundDeployer";
import { Address, Bytes } from "../types.js";
import { prepareFunctionParams } from "../utils/viem.js";
import { toSeconds } from "../utils/conversion.js";
export interface SetupVaultParams {
vaultOwner: Address;
vaultName: string;
vaultSymbol: string;
denominationAsset: Address;
sharesActionTimelock?: bigint;
feeSettings?: Bytes;
policySettings?: Bytes;
}
export function setupVaultParams({
vaultOwner,
vaultName,
vaultSymbol,
denominationAsset,
sharesActionTimelock = toSeconds({ days: 1 }),
feeSettings = "0x",
policySettings = "0x",
}: SetupVaultParams) {
// ... Do whatever you must here to produce the final input args for your function.
return prepareFunctionParams({
abi: IFundDeployer,
functionName: "createNewFund",
args: [vaultOwner, vaultName, vaultSymbol, denominationAsset, sharesActionTimelock, feeSettings, policySettings],
});
} ... And then use it like this (note how I'm spreading the output of that simple util function in). const { result: [comptrollerProxy, vaultProxy] }= await publicClient.simulateContract({
account: testAccount,
address: "0x4f1c53f096533c04d8157efb6bca3eb22ddc6360",
...setupVaultParams({
vaultOwner: testAccount.address,
vaultName: "Test Vault",
vaultSymbol: "TEST",
denominationAsset: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
sharesActionTimelock: 0n,
feeSettings: encodeFeeSettings([
{
address: "0xfedc73464dfd156d30f6524654a5d56e766da0c3",
settings: encodePerformanceFeeSettings({ feeRateInBps: toBps(0.1) }),
},
{
address: "0xfaf2c3db614e9d38fe05edc634848be7ff0542b9",
settings: encodeManagementFeeSettings({ perAnnumRateInBps: toBps(0.1) }),
},
]),
policySettings: encodePolicySettings([
{
address: "0xebdadfc929c357d12281118828aea556db5be30c",
settings: encodeMinMaxInvestmentPolicySettings({ minInvestmentAmount: 100n, maxInvestmentAmount: 5000n }),
},
]),
}),
}); That leaves the consuming application with the full range of options to simulate, sign and/or send the transaction payload however they want. An added bonus is that this would also support the consuming application to use other libraries (e.g. ethers.js) if they are not able to upgrade yet as you can still easily just encode the returned values and then use the resulting bytes string with an ethers.js signer. |
Beta Was this translation helpful? Give feedback.
After toying around with the different options, I've concluded that (for now), the best option for an SDK imho is to not try and abstract away the signing & sending of transactions but simply provide convenience functions to produce the right, typed inputs for the
viem
client functions. I'm doing sth. like this now: