Skip to content

Instantly share code, notes, and snippets.

@alerdenisov
Created April 15, 2021 18:44
Show Gist options
  • Save alerdenisov/4c1a02243f6ed7be6a465ced6b69dd4c to your computer and use it in GitHub Desktop.
Save alerdenisov/4c1a02243f6ed7be6a465ced6b69dd4c to your computer and use it in GitHub Desktop.
multicall.ts
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