Created
April 15, 2021 18:44
-
-
Save alerdenisov/4c1a02243f6ed7be6a465ced6b69dd4c to your computer and use it in GitHub Desktop.
multicall.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { BytesLike, Contract } from "ethers" | |
import { Provider } from "@ethersproject/providers" | |
import { Multicall, Multicall__factory } from "../types" | |
export interface CallRequest { | |
onResult: (response: string) => void | |
call: Call | |
} | |
export interface Call { | |
target: string | |
callData: BytesLike | |
} | |
export type CallResult< | |
TContract extends Contract, | |
TMethod extends keyof TContract["functions"] | |
> = ReturnType<TContract["functions"][TMethod]> extends Promise<infer R> | |
? R extends Array<infer EL> | |
? EL | |
: R | |
: never | |
export class Multisender { | |
private contract: Multicall | |
private queue: Array<CallRequest> = [] | |
constructor(protected provider: Provider, protected address: string) { | |
this.contract = Multicall__factory.connect(this.address, this.provider) | |
this.update() | |
} | |
async call< | |
T extends Contract, | |
TMethod extends keyof T["functions"], | |
TResult extends CallResult<T, TMethod> | |
>( | |
contract: T, | |
methodName: TMethod, | |
...args: Parameters<T["functions"][TMethod]> | |
): Promise<TResult> { | |
return new Promise<TResult>((resolve, reject) => { | |
console.log( | |
`Calling ${contract.address} as ${contract.constructor.name}.${methodName}(${args.join( | |
", " | |
)})` | |
) | |
this.queue.push({ | |
call: { | |
target: contract.address, | |
callData: contract.interface.encodeFunctionData(methodName as string, args), | |
}, | |
onResult: (result: string) => { | |
try { | |
const typedResult = contract.interface.decodeFunctionResult( | |
methodName as string, | |
result | |
) as TResult | |
resolve( | |
Array.isArray(typedResult) && typedResult.length === 1 ? typedResult[0] : typedResult | |
) | |
} catch (e) { | |
reject(new Error("Failed decode: " + result)) | |
} | |
}, | |
}) | |
}) | |
} | |
async update() { | |
while (this.queue.length) { | |
const requests = this.queue.splice(0, 100) | |
const result = await this.contract.callStatic.aggregate(requests.map((r) => r.call)) | |
// TODO: Error | |
requests.forEach((request, index) => request.onResult(result.returnData[index])) | |
} | |
await sleep(1000) | |
this.update() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment