UNPKG

@velora-dex/sdk

Version:
193 lines (162 loc) 6.23 kB
import type { Address, ContractCallerFunctions, NoExtraKeysCheck, SignTypedDataContractCallerFn, StaticContractCallerFn, TransactionContractCallerFn, } from '../../types'; import type { JsonRpcProvider, BaseProvider, JsonRpcSigner, } from '@ethersproject/providers'; import type { Signer } from '@ethersproject/abstract-signer'; import type { Contract as EthersV5Contract, ContractFunction as EthersContractFunctionV5, PopulatedTransaction as EthersPopulatedTransactionV5, PayableOverrides, CallOverrides, ContractTransaction, } from '@ethersproject/contracts'; import type { BigNumber as EthersBigNumberV5 } from '@ethersproject/bignumber'; import { assert } from 'ts-essentials'; export interface EthersV5ProviderDeps { ethersProviderOrSigner: BaseProvider | Signer; EthersContract: typeof EthersV5Contract; // passing Contract in allows not to include ethers as dependency } export const constructEthersV5ContractCaller = ( { ethersProviderOrSigner: providerOrSigner, EthersContract: Contract, }: EthersV5ProviderDeps, account?: Address ): ContractCallerFunctions<ContractTransaction> => { const staticCall: StaticContractCallerFn = async (params) => { const { address, abi, contractMethod, args, overrides } = params; const contract = new Contract(address, abi, providerOrSigner); assertEthersContractHasMethodsV5(contract, contractMethod); // drop keys not in CallOverrides const { block, gas, ...restOverrides } = overrides; // reassign values to keys in CallOverrides const normalizedOverrides = { ...restOverrides, blockTag: block, gasLimit: gas, }; // type FinalCallOverrides = normalizedOverrides has extra props ? never : normalizedOverrides type FinalCallOverrides = NoExtraKeysCheck< typeof normalizedOverrides, CallOverrides >; // enforce overrides shape ethers accepts // TS will break if normalizedOverrides type has any keys not also present in CallOverrides const callOverrides: FinalCallOverrides = normalizedOverrides; // returns whatever the Contract.method returns: BigNumber, string, boolean return contract.callStatic[contractMethod](...args, callOverrides); }; const transactCall: TransactionContractCallerFn<ContractTransaction> = async ( params ) => { assert(account, 'account must be specified to create a signer'); assert( isEthersProviderWithSigner(providerOrSigner) || isEthersSigner(providerOrSigner), 'ethers must be an instance of Signer or JsonRpcProvider to create a signer' ); const { address, abi, contractMethod, args, overrides } = params; const signer = 'getSigner' in providerOrSigner ? providerOrSigner.getSigner(account) : providerOrSigner; const contract = new Contract(address, abi, signer); assertEthersContractHasMethodsV5(contract, contractMethod); // drop keys not in PayableOverrides const { gas, from, ...restOverrides } = overrides; // reassign values to keys in PayableOverrides const normalizedOverrides = { ...restOverrides, gasLimit: gas, }; // type FinalPayableOverrides = normalizedOverrides has extra props ? never : normalizedOverrides type FinalPayableOverrides = NoExtraKeysCheck< typeof normalizedOverrides, PayableOverrides >; // enforce overrides shape ethers accepts // TS will break if normalizedOverrides type has any keys not also present in PayableOverrides const txOverrides: FinalPayableOverrides = normalizedOverrides; const txResponse: ContractTransaction = await contract[contractMethod]( ...args, txOverrides ); return txResponse; }; const signTypedDataCall: SignTypedDataContractCallerFn = async ( typedData ) => { assert(account, 'account must be specified to create a signer'); assert( isEthersProviderWithSigner(providerOrSigner) || isEthersSigner(providerOrSigner), 'ethers must be an instance of Signer or JsonRpcProvider to create a signer' ); const signer = 'getSigner' in providerOrSigner ? providerOrSigner.getSigner(account) : providerOrSigner; assert(isTypedDataCapableSigner(signer), 'Signer can sign typed data'); const { data, domain, types } = typedData; return signer._signTypedData(domain, types, data); }; return { staticCall, transactCall, signTypedDataCall }; }; function isEthersProvider( providerOrSigner: BaseProvider | Signer ): providerOrSigner is BaseProvider { return '_isProvider' in providerOrSigner && providerOrSigner._isProvider; } function isEthersProviderWithSigner( providerOrSigner: JsonRpcProvider | BaseProvider | Signer ): providerOrSigner is JsonRpcProvider { return isEthersProvider(providerOrSigner) && 'getSigner' in providerOrSigner; } function isEthersSigner( providerOrSigner: BaseProvider | Signer ): providerOrSigner is Signer { return '_isSigner' in providerOrSigner && providerOrSigner._isSigner; } function isTypedDataCapableSigner( signer: Signer ): signer is Signer & Pick<JsonRpcSigner, '_signTypedData'> { return '_signTypedData' in signer; } /// ethers v5 type EthersContractWithMethodV5<T extends string> = EthersV5Contract & { readonly [method in T]: EthersContractFunctionV5; } & { readonly functions: { [method in T]: EthersContractFunctionV5 }; readonly callStatic: { [method in T]: EthersContractFunctionV5 }; readonly estimateGas: { [method in T]: EthersContractFunctionV5<EthersBigNumberV5>; }; readonly populateTransaction: { [method in T]: EthersContractFunctionV5<EthersPopulatedTransactionV5>; }; }; function ethersContractHasMethodsV5<T extends string>( contract: EthersV5Contract, ...methods: T[] ): contract is EthersContractWithMethodV5<T> { return methods.every((method) => typeof contract[method] === 'function'); } function assertEthersContractHasMethodsV5<T extends string>( contract: EthersV5Contract, ...methods: T[] ): asserts contract is EthersContractWithMethodV5<T> { assert( ethersContractHasMethodsV5(contract, ...methods), `Contract must have methods: ${methods.join(', ')}` ); }