diff --git a/.gitignore b/.gitignore index d0e47c9..8afbacb 100755 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ node_modules .npmrc *.log -dist/ diff --git a/dist/abi.d.ts b/dist/abi.d.ts new file mode 100644 index 0000000..57cff97 --- /dev/null +++ b/dist/abi.d.ts @@ -0,0 +1,5 @@ +import { ethers } from 'ethers'; +export declare class Abi { + static encode(name: string, inputs: ethers.utils.ParamType[], params: any[]): string; + static decode(outputs: ethers.utils.ParamType[], data: ethers.utils.BytesLike): ethers.utils.Result; +} diff --git a/dist/abi.js b/dist/abi.js new file mode 100644 index 0000000..055b9e9 --- /dev/null +++ b/dist/abi.js @@ -0,0 +1,46 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Abi = void 0; +var ethers_1 = require("ethers"); +var Abi = /** @class */ (function () { + function Abi() { + } + Abi.encode = function (name, inputs, params) { + var functionSignature = getFunctionSignature(name, inputs); + var functionHash = ethers_1.ethers.utils.keccak256(ethers_1.ethers.utils.toUtf8Bytes(functionSignature)); + var functionData = functionHash.substring(2, 10); + var abiCoder = new ethers_1.ethers.utils.AbiCoder(); + var argumentString = abiCoder.encode(inputs, params); + var argumentData = argumentString.substring(2); + var inputData = "0x" + functionData + argumentData; + return inputData; + }; + Abi.decode = function (outputs, data) { + var abiCoder = new ethers_1.ethers.utils.AbiCoder(); + var params = abiCoder.decode(outputs, data); + return params; + }; + return Abi; +}()); +exports.Abi = Abi; +function getFunctionSignature(name, inputs) { + var types = []; + for (var _i = 0, inputs_1 = inputs; _i < inputs_1.length; _i++) { + var input = inputs_1[_i]; + if (input.type === 'tuple') { + var tupleString = getFunctionSignature('', input.components); + types.push(tupleString); + continue; + } + if (input.type === 'tuple[]') { + var tupleString = getFunctionSignature('', input.components); + var arrayString = tupleString + "[]"; + types.push(arrayString); + continue; + } + types.push(input.type); + } + var typeString = types.join(','); + var functionSignature = name + "(" + typeString + ")"; + return functionSignature; +} diff --git a/dist/abi/multicall.d.ts b/dist/abi/multicall.d.ts new file mode 100644 index 0000000..b6c8335 --- /dev/null +++ b/dist/abi/multicall.d.ts @@ -0,0 +1,38 @@ +export declare const multicallAbi: ({ + constant: boolean; + inputs: { + components: { + internalType: string; + name: string; + type: string; + }[]; + internalType: string; + name: string; + type: string; + }[]; + name: string; + outputs: { + internalType: string; + name: string; + type: string; + }[]; + payable: boolean; + stateMutability: string; + type: string; +} | { + constant: boolean; + inputs: { + internalType: string; + name: string; + type: string; + }[]; + name: string; + outputs: { + internalType: string; + name: string; + type: string; + }[]; + payable: boolean; + stateMutability: string; + type: string; +})[]; diff --git a/dist/abi/multicall.js b/dist/abi/multicall.js new file mode 100644 index 0000000..8354ecf --- /dev/null +++ b/dist/abi/multicall.js @@ -0,0 +1,160 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.multicallAbi = void 0; +exports.multicallAbi = [ + { + constant: true, + inputs: [ + { + components: [ + { + internalType: 'address', + name: 'target', + type: 'address', + }, + { + internalType: 'bytes', + name: 'callData', + type: 'bytes', + }, + ], + internalType: 'struct Multicall.Call[]', + name: 'calls', + type: 'tuple[]', + }, + ], + name: 'aggregate', + outputs: [ + { + internalType: 'uint256', + name: 'blockNumber', + type: 'uint256', + }, + { + internalType: 'bytes[]', + name: 'returnData', + type: 'bytes[]', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [ + { + internalType: 'uint256', + name: 'blockNumber', + type: 'uint256', + }, + ], + name: 'getBlockHash', + outputs: [ + { + internalType: 'bytes32', + name: 'blockHash', + type: 'bytes32', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'getCurrentBlockCoinbase', + outputs: [ + { + internalType: 'address', + name: 'coinbase', + type: 'address', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'getCurrentBlockDifficulty', + outputs: [ + { + internalType: 'uint256', + name: 'difficulty', + type: 'uint256', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'getCurrentBlockGasLimit', + outputs: [ + { + internalType: 'uint256', + name: 'gaslimit', + type: 'uint256', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'getCurrentBlockTimestamp', + outputs: [ + { + internalType: 'uint256', + name: 'timestamp', + type: 'uint256', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [ + { + internalType: 'address', + name: 'addr', + type: 'address', + }, + ], + name: 'getEthBalance', + outputs: [ + { + internalType: 'uint256', + name: 'balance', + type: 'uint256', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, + { + constant: true, + inputs: [], + name: 'getLastBlockHash', + outputs: [ + { + internalType: 'bytes32', + name: 'blockHash', + type: 'bytes32', + }, + ], + payable: false, + stateMutability: 'view', + type: 'function', + }, +]; diff --git a/dist/call.d.ts b/dist/call.d.ts new file mode 100644 index 0000000..6fe932f --- /dev/null +++ b/dist/call.d.ts @@ -0,0 +1,3 @@ +import { ethers } from 'ethers'; +import { ContractCall } from './types'; +export declare function all(calls: ContractCall[], multicallAddress: string, provider: ethers.providers.Provider): Promise; diff --git a/dist/call.js b/dist/call.js new file mode 100644 index 0000000..d95d29f --- /dev/null +++ b/dist/call.js @@ -0,0 +1,74 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.all = void 0; +var ethers_1 = require("ethers"); +var abi_1 = require("./abi"); +var multicall_1 = require("./abi/multicall"); +function all(calls, multicallAddress, provider) { + return __awaiter(this, void 0, void 0, function () { + var multicall, callRequests, response, callCount, callResult, i, outputs, returnData, params, result; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + multicall = new ethers_1.ethers.Contract(multicallAddress, multicall_1.multicallAbi, provider); + callRequests = calls.map(function (call) { + var callData = abi_1.Abi.encode(call.name, call.inputs, call.params); + return { + target: call.contract.address, + callData: callData, + }; + }); + return [4 /*yield*/, multicall.aggregate(callRequests)]; + case 1: + response = _a.sent(); + callCount = calls.length; + callResult = []; + for (i = 0; i < callCount; i++) { + outputs = calls[i].outputs; + returnData = response.returnData[i]; + params = abi_1.Abi.decode(outputs, returnData); + result = outputs.length === 1 ? params[0] : params; + callResult.push(result); + } + return [2 /*return*/, callResult]; + } + }); + }); +} +exports.all = all; diff --git a/dist/calls.d.ts b/dist/calls.d.ts new file mode 100644 index 0000000..4d15438 --- /dev/null +++ b/dist/calls.d.ts @@ -0,0 +1 @@ +export declare function getEthBalance(address: string, multicallAddress: string): any; diff --git a/dist/calls.js b/dist/calls.js new file mode 100644 index 0000000..2b69959 --- /dev/null +++ b/dist/calls.js @@ -0,0 +1,10 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getEthBalance = void 0; +var multicall_1 = require("./abi/multicall"); +var contract_1 = require("./contract"); +function getEthBalance(address, multicallAddress) { + var multicall = new contract_1.Contract(multicallAddress, multicall_1.multicallAbi); + return multicall.getEthBalance(address); +} +exports.getEthBalance = getEthBalance; diff --git a/dist/contract.d.ts b/dist/contract.d.ts new file mode 100644 index 0000000..8621b6b --- /dev/null +++ b/dist/contract.d.ts @@ -0,0 +1,11 @@ +import { Fragment, FunctionFragment, JsonFragment } from '@ethersproject/abi'; +export declare class Contract { + private _address; + private _abi; + private _functions; + get address(): string; + get abi(): Fragment[]; + get functions(): FunctionFragment[]; + constructor(address: string, abi: JsonFragment[] | string[] | Fragment[]); + [method: string]: any; +} diff --git a/dist/contract.js b/dist/contract.js new file mode 100644 index 0000000..720f8dd --- /dev/null +++ b/dist/contract.js @@ -0,0 +1,74 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Contract = void 0; +var abi_1 = require("@ethersproject/abi"); +var ethers_1 = require("ethers"); +var Contract = /** @class */ (function () { + function Contract(address, abi) { + this._address = address; + this._abi = toFragment(abi); + this._functions = this._abi.filter(function (x) { return x.type === 'function'; }).map(function (x) { return abi_1.FunctionFragment.from(x); }); + var callFunctions = this._functions.filter(function (x) { return x.stateMutability === 'pure' || x.stateMutability === 'view'; }); + for (var _i = 0, callFunctions_1 = callFunctions; _i < callFunctions_1.length; _i++) { + var callFunction = callFunctions_1[_i]; + var name = callFunction.name; + var getCall = makeCallFunction(this, name); + if (!this[name]) { + defineReadOnly(this, name, getCall); + } + } + } + Object.defineProperty(Contract.prototype, "address", { + get: function () { + return this._address; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Contract.prototype, "abi", { + get: function () { + return this._abi; + }, + enumerable: false, + configurable: true + }); + Object.defineProperty(Contract.prototype, "functions", { + get: function () { + return this._functions; + }, + enumerable: false, + configurable: true + }); + return Contract; +}()); +exports.Contract = Contract; +function toFragment(abi) { + return abi.map(function (item) { return ethers_1.utils.Fragment.from(item); }); +} +function makeCallFunction(contract, name) { + return function () { + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + var address = contract.address; + var inputs = contract.functions.find(function (f) { return f.name === name; }).inputs; + var outputs = contract.functions.find(function (f) { return f.name === name; }).outputs; + return { + contract: { + address: address, + }, + name: name, + inputs: inputs, + outputs: outputs, + params: params, + }; + }; +} +function defineReadOnly(object, name, value) { + Object.defineProperty(object, name, { + enumerable: true, + value: value, + writable: false, + }); +} diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..baced07 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,10 @@ +import { Contract } from './contract'; +import { Provider, setMulticallAddress } from './provider'; +import { ContractCall } from './types'; +export { Contract, Provider, ContractCall, setMulticallAddress }; +declare const _default: { + Contract: typeof Contract; + Provider: typeof Provider; + setMulticallAddress: typeof setMulticallAddress; +}; +export default _default; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..db053fd --- /dev/null +++ b/dist/index.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setMulticallAddress = exports.Provider = exports.Contract = void 0; +var contract_1 = require("./contract"); +Object.defineProperty(exports, "Contract", { enumerable: true, get: function () { return contract_1.Contract; } }); +var provider_1 = require("./provider"); +Object.defineProperty(exports, "Provider", { enumerable: true, get: function () { return provider_1.Provider; } }); +Object.defineProperty(exports, "setMulticallAddress", { enumerable: true, get: function () { return provider_1.setMulticallAddress; } }); +exports.default = { Contract: contract_1.Contract, Provider: provider_1.Provider, setMulticallAddress: provider_1.setMulticallAddress }; diff --git a/dist/provider.d.ts b/dist/provider.d.ts new file mode 100644 index 0000000..fecd60f --- /dev/null +++ b/dist/provider.d.ts @@ -0,0 +1,11 @@ +import { ethers } from 'ethers'; +import { ContractCall } from './types'; +export declare class Provider { + private _provider; + private _multicallAddress; + constructor(provider: ethers.providers.Provider, chainId?: number); + init(): Promise; + getEthBalance(address: string): any; + all(calls: ContractCall[]): Promise; +} +export declare function setMulticallAddress(chainId: number, address: string): void; diff --git a/dist/provider.js b/dist/provider.js new file mode 100644 index 0000000..0acf4f9 --- /dev/null +++ b/dist/provider.js @@ -0,0 +1,121 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.setMulticallAddress = exports.Provider = void 0; +var call_1 = require("./call"); +var calls_1 = require("./calls"); +var Provider = /** @class */ (function () { + function Provider(provider, chainId) { + this._provider = provider; + this._multicallAddress = getAddressForChainId(chainId); + } + Provider.prototype.init = function () { + return __awaiter(this, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + // Only required if `chainId` was not provided in constructor + _a = this; + return [4 /*yield*/, getAddress(this._provider)]; + case 1: + // Only required if `chainId` was not provided in constructor + _a._multicallAddress = _b.sent(); + return [2 /*return*/]; + } + }); + }); + }; + Provider.prototype.getEthBalance = function (address) { + if (!this._provider) { + throw new Error('Provider should be initialized before use.'); + } + return calls_1.getEthBalance(address, this._multicallAddress); + }; + Provider.prototype.all = function (calls) { + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + if (!this._provider) { + throw new Error('Provider should be initialized before use.'); + } + return [2 /*return*/, call_1.all(calls, this._multicallAddress, this._provider)]; + }); + }); + }; + return Provider; +}()); +exports.Provider = Provider; +var multicallAddresses = { + 1: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441', + 3: '0xF24b01476a55d635118ca848fbc7Dab69d403be3', + 4: '0x42ad527de7d4e9d9d011ac45b31d8551f8fe9821', + 5: '0x77dca2c955b15e9de4dbbcf1246b4b85b651e50e', + 42: '0x2cc8688c5f75e365aaeeb4ea8d6a480405a48d2a', + 56: '0x1Ee38d535d541c55C9dae27B12edf090C608E6Fb', + 66: '0x94fEadE0D3D832E4A05d459eBeA9350c6cDd3bCa', + 97: '0x3A09ad1B8535F25b48e6Fa0CFd07dB6B017b31B2', + 100: '0xb5b692a88bdfc81ca69dcb1d924f59f0413a602a', + 128: '0x2C55D51804CF5b436BA5AF37bD7b8E5DB70EBf29', + 137: '0x11ce4B23bD875D7F5C6a31084f55fDe1e9A87507', + 250: '0x0118EF741097D0d3cc88e46233Da1e407d9ac139', + 1285: '0x665D6802d22B7FE74cF8328eB384F575c8Be2302', + 1337: '0x77dca2c955b15e9de4dbbcf1246b4b85b651e50e', + 42161: '0x813715eF627B01f4931d8C6F8D2459F26E19137E', + 43114: '0x7f3aC7C283d7E6662D886F494f7bc6F1993cDacf', + 80001: '0x08411ADd0b5AA8ee47563b146743C13b3556c9Cc', +}; +function setMulticallAddress(chainId, address) { + multicallAddresses[chainId] = address; +} +exports.setMulticallAddress = setMulticallAddress; +function getAddressForChainId(chainId) { + return multicallAddresses[chainId]; +} +function getAddress(provider) { + return __awaiter(this, void 0, void 0, function () { + var chainId; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, provider.getNetwork()]; + case 1: + chainId = (_a.sent()).chainId; + return [2 /*return*/, getAddressForChainId(chainId)]; + } + }); + }); +} diff --git a/dist/types.d.ts b/dist/types.d.ts new file mode 100644 index 0000000..5a21633 --- /dev/null +++ b/dist/types.d.ts @@ -0,0 +1,10 @@ +import { ethers } from 'ethers'; +export interface ContractCall { + contract: { + address: string; + }; + name: string; + inputs: ethers.utils.ParamType[]; + outputs: ethers.utils.ParamType[]; + params: any[]; +} diff --git a/dist/types.js b/dist/types.js new file mode 100644 index 0000000..c8ad2e5 --- /dev/null +++ b/dist/types.js @@ -0,0 +1,2 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/src/provider.ts b/src/provider.ts index 2da8735..7edd1fe 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -45,6 +45,7 @@ const multicallAddresses = { 128: '0x2C55D51804CF5b436BA5AF37bD7b8E5DB70EBf29', 137: '0x11ce4B23bD875D7F5C6a31084f55fDe1e9A87507', 250: '0x0118EF741097D0d3cc88e46233Da1e407d9ac139', + 1285: '0x665D6802d22B7FE74cF8328eB384F575c8Be2302', 1337: '0x77dca2c955b15e9de4dbbcf1246b4b85b651e50e', 42161: '0x813715eF627B01f4931d8C6F8D2459F26E19137E', 43114: '0x7f3aC7C283d7E6662D886F494f7bc6F1993cDacf',