Skip to content

Commit

Permalink
feat(ethers): bump to v6
Browse files Browse the repository at this point in the history
  • Loading branch information
Rubilmax committed Aug 24, 2023
1 parent 828fdb1 commit ffed6f5
Show file tree
Hide file tree
Showing 11 changed files with 1,150 additions and 1,927 deletions.
18 changes: 7 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0",
"description": "⚡🕰️ Drop-in solution to batch smart contract RPC calls in a single RPC query via Multicall!",
"main": "lib/index.js",
"type": "module",
"files": [
"lib/*"
],
Expand All @@ -12,7 +13,7 @@
"test:watch": "jest --watch",
"test": "jest --coverage --detectOpenHandles",
"typecheck": "tsc --noEmit",
"typechain": "typechain --target ethers-v5 --out-dir src/types 'abis/*.json'"
"typechain": "typechain --target ethers-v6 --out-dir src/types 'abis/*.json'"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -41,33 +42,28 @@
},
"homepage": "https://github.com/rubilmax/ethers-multicall-provider#readme",
"dependencies": {
"@ethersproject/abi": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"ethers": "^5.0.0",
"ethers": "^6.0.0",
"lodash": "^4.17.0"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@typechain/ethers-v5": "^11.1.1",
"@types/jest": "^29.5.3",
"@typechain/ethers-v6": "^0.5.0",
"@types/jest": "^29.5.4",
"@types/lodash": "^4.14.197",
"@types/lodash.debounce": "^4.0.7",
"commitizen": "^4.3.0",
"conventional-changelog-conventionalcommits": "^6.1.0",
"cz-conventional-changelog": "^3.3.0",
"dotenv": "^16.3.1",
"husky": "^8.0.3",
"jest": "^29.6.3",
"jest": "^29.6.4",
"lint-staged": "^14.0.1",
"prettier": "^2.8.8",
"ts-jest": "^29.1.1",
"typechain": "^8.3.1",
"typescript": "^5.1.6"
},
"peerDependencies": {
"@ethersproject/abi": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"ethers": "^5.0.0",
"lodash": "^4.17.0"
},
"config": {
Expand Down Expand Up @@ -145,4 +141,4 @@
]
}
}
}
}
56 changes: 31 additions & 25 deletions src/multicall-provider.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { BytesLike } from "ethers/lib/utils";
import { BlockTag, BytesLike, AbstractProvider, PerformActionRequest, Network } from "ethers";
import { DebouncedFunc } from "lodash";
import _debounce from "lodash/debounce";

import { Provider, BlockTag, TransactionRequest } from "@ethersproject/providers";

import { multicallAddresses } from "./constants";
import { Multicall2, Multicall3 } from "./types";
import { MinimalProvider, getBlockNumber, getMulticall, isProviderCompatible } from "./utils";
import { getBlockNumber, getMulticall } from "./utils";

export interface ContractCall {
export interface ContractCall<T = any> {
to: string;
data: BytesLike;
blockTag: BlockTag;
multicall: Multicall2 | Multicall3;
resolve: (value: string | PromiseLike<string>) => void;
resolve: (value: T | PromiseLike<T>) => void;
reject: (reason?: any) => void;
}

export type MulticallProvider<T extends Provider = Provider> = T & {
export type MulticallProvider<T extends AbstractProvider = AbstractProvider> = T & {
readonly _isMulticallProvider: boolean;

network: Network;
_multicallDelay: number;
multicallDelay: number;
maxMulticallDataLength: number;
Expand All @@ -35,7 +34,7 @@ export class MulticallWrapper {
* @param provider The provider to check.
* @returns A boolean indicating whether the given provider is a multicall-enabled provider.
*/
public static isMulticallProvider<T extends Provider>(
public static isMulticallProvider<T extends AbstractProvider>(
provider: T
): provider is MulticallProvider<T> {
if ((provider as MulticallProvider<T>)._isMulticallProvider) return true;
Expand All @@ -50,12 +49,11 @@ export class MulticallWrapper {
* @param maxMulticallDataLength The maximum total calldata length allowed in a multicall batch, to avoid having the RPC backend to revert because of too large (or too long) request. Set to 0 to disable this behavior. Defaults to 200k.
* @returns The multicall provider, which is a proxy to the given provider, automatically batching any call performed with it.
*/
public static wrap<T extends Provider>(
public static wrap<T extends AbstractProvider>(
provider: T,
delay = 16,
maxMulticallDataLength = 200_000
): MulticallProvider<T> {
if (!isProviderCompatible(provider)) throw Error("Cannot wrap provider for multicall");
if (MulticallWrapper.isMulticallProvider(provider)) return provider; // Do not overwrap when given provider is already a multicall provider.

// Overload provider
Expand Down Expand Up @@ -101,7 +99,12 @@ export class MulticallWrapper {
},
});

const multicallProvider = provider as MulticallProvider<T & MinimalProvider>;
provider.on("network", (network) => {
Object.defineProperty(provider, "network", network);
});
provider.getNetwork();

const multicallProvider = provider as MulticallProvider<T>;

// Define execution context

Expand All @@ -121,7 +124,7 @@ export class MulticallWrapper {
...acc,
[blockTag]: [queuedCall].concat(acc[blockTag] ?? []),
};
}, {} as { [blockTag: BlockTag]: ContractCall[] });
}, {} as { [blockTag: string]: ContractCall[] });

await Promise.all(
Object.values(blockTagCalls).map(async (blockTagQueuedCalls) => {
Expand Down Expand Up @@ -151,7 +154,7 @@ export class MulticallWrapper {
try {
const res = (
await Promise.all(
calls.map((call) => multicall.callStatic.tryAggregate(false, call, { blockTag }))
calls.map((call) => multicall.tryAggregate.staticCall(false, call, { blockTag }))
)
).flat();

Expand All @@ -176,30 +179,33 @@ export class MulticallWrapper {

multicallProvider.multicallDelay = delay;

// Overload `BaseProvider.perform`
// Overload `Provider._detectNetwork` to disable polling the network at each RPC call

multicallProvider._detectNetwork = async function _detectNetwork(): Promise<Network> {
return this.network;
};

// Overload `Provider._perform`

const _perform = provider.perform.bind(provider);
const _perform = provider._perform.bind(provider);

multicallProvider.perform = async function (method: string, params: any): Promise<string> {
if (method !== "call" || !this.isMulticallEnabled) return _perform(method, params);
multicallProvider._perform = async function <R = any>(req: PerformActionRequest): Promise<R> {
if (req.method !== "call" || !this.isMulticallEnabled) return _perform(req);

const {
transaction: { to, data },
blockTag,
} = params as {
transaction: TransactionRequest;
blockTag: BlockTag;
};
} = req;

const blockNumber = getBlockNumber(blockTag);
const multicall = getMulticall(blockNumber, this.network.chainId, provider);
const multicall = getMulticall(blockNumber, Number(this.network.chainId), this);

if (!to || !data || multicall == null || multicallAddresses.has(to.toLowerCase()))
return _perform(method, params);
if (!to || !data || multicall == null || multicallAddresses.has(to.toString().toLowerCase()))
return _perform(req);

this._debouncedPerformMulticall();

return new Promise<string>((resolve, reject) => {
return new Promise<R>((resolve, reject) => {
queuedCalls.push({
to,
data,
Expand Down
Loading

0 comments on commit ffed6f5

Please sign in to comment.