Skip to content

Commit ac780e3

Browse files
Merge pull request #39 from jsertx/master
Add support to override blocknumber
2 parents 2e0507b + 8021908 commit ac780e3

File tree

5 files changed

+105
-14
lines changed

5 files changed

+105
-14
lines changed

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,64 @@ console.log(results);
219219
}
220220
```
221221
222+
### specify call block number
223+
224+
The multicall instance call method has an optional second argument of type [ContractCallOptions](src/models/contract-call-options.ts).
225+
226+
One of the options is the blockNumber, so you can choose the height where you want the data from.
227+
228+
It is compatible with both ethers and web3 providers.
229+
230+
```ts
231+
import {
232+
Multicall,
233+
ContractCallResults,
234+
ContractCallContext,
235+
} from 'ethereum-multicall';
236+
import Web3 from 'web3';
237+
238+
const web3 = new Web3('https://some.local-or-remote.node:8546');
239+
240+
const multicall = new Multicall({ web3Instance: web3, tryAggregate: true });
241+
242+
const contractCallContext: ContractCallContext[] = [
243+
{
244+
reference: 'testContract',
245+
contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
246+
abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
247+
calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
248+
}
249+
];
250+
251+
const results: ContractCallResults = await multicall.call(contractCallContext,{
252+
blockNumber: '14571050'
253+
});
254+
console.log(results);
255+
256+
// results: it will have the same block as requested
257+
{
258+
results: {
259+
testContract: {
260+
originalContractCallContext: {
261+
reference: 'testContract',
262+
contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
263+
abi: [ { name: 'foo', type: 'function', inputs: [ { name: 'example', type: 'uint256' } ], outputs: [ { name: 'amounts', type: 'uint256' }] } ],
264+
calls: [{ reference: 'fooCall', methodName: 'foo', methodParameters: [42] }]
265+
},
266+
callsReturnContext: [{
267+
returnValues: [{ amounts: BigNumber }],
268+
decoded: true,
269+
reference: 'fooCall',
270+
methodName: 'foo',
271+
methodParameters: [42],
272+
success: true
273+
}]
274+
},
275+
},
276+
blockNumber: 14571050
277+
}
278+
```
279+
222280
### passing extra context to the call
223281
224282
If you want store any context or state so you don't need to look back over arrays once you got the result back. it can be stored in `context` within `ContractCallContext`.

src/__TEST-SCRIPT__/playground.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ const execute = async () => {
6060
context.results[contractCallContext.reference].callsReturnContext[0]
6161
.returnValues
6262
);
63+
const latestBlock = await provider.getBlockNumber();
64+
const blockNumber = `${latestBlock - 15}`;
65+
const contextOnBlock: ContractCallResults = await multicall.call(
66+
contractCallContext,
67+
{ blockNumber }
68+
);
69+
console.log({
70+
latestBlock,
71+
blockNumber,
72+
resultBlock: contextOnBlock.blockNumber,
73+
});
6374
};
6475

6576
execute();
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface ContractCallOptions {
2+
blockNumber?: string;
3+
}

src/models/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ export { CallReturnContext } from './call-return-context';
99
export { ContractCallContext } from './contract-call-context';
1010
export { ContractCallResults } from './contract-call-results';
1111
export { ContractCallReturnContext } from './contract-call-return-context';
12+
export { ContractCallOptions } from './contract-call-options';
13+
1214
export * from './multicall-options';

src/multicall.ts

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
MulticallOptionsCustomJsonRpcProvider,
1515
MulticallOptionsEthers,
1616
MulticallOptionsWeb3,
17+
ContractCallOptions,
1718
} from './models';
1819
import { Utils } from './utils';
1920

@@ -134,14 +135,16 @@ export class Multicall {
134135
* @param calls The calls
135136
*/
136137
public async call(
137-
contractCallContexts: ContractCallContext[] | ContractCallContext
138+
contractCallContexts: ContractCallContext[] | ContractCallContext,
139+
contractCallOptions: ContractCallOptions = {}
138140
): Promise<ContractCallResults> {
139141
if (!Array.isArray(contractCallContexts)) {
140142
contractCallContexts = [contractCallContexts];
141143
}
142144

143145
const aggregateResponse = await this.execute(
144-
this.buildAggregateCallContext(contractCallContexts)
146+
this.buildAggregateCallContext(contractCallContexts),
147+
contractCallOptions
145148
);
146149

147150
const returnObject: ContractCallResults = {
@@ -215,7 +218,7 @@ export class Multicall {
215218
);
216219
} catch (e) {
217220
if (!this._options.tryAggregate) {
218-
throw e
221+
throw e;
219222
}
220223
returnObjectResult.callsReturnContext.push(
221224
Utils.deepClone<CallReturnContext>({
@@ -342,14 +345,15 @@ export class Multicall {
342345
* @param calls The calls
343346
*/
344347
private async execute(
345-
calls: AggregateCallContext[]
348+
calls: AggregateCallContext[],
349+
options: ContractCallOptions
346350
): Promise<AggregateResponse> {
347351
switch (this._executionType) {
348352
case ExecutionType.web3:
349-
return await this.executeWithWeb3(calls);
353+
return await this.executeWithWeb3(calls, options);
350354
case ExecutionType.ethers:
351355
case ExecutionType.customHttp:
352-
return await this.executeWithEthersOrCustom(calls);
356+
return await this.executeWithEthersOrCustom(calls, options);
353357
default:
354358
throw new Error(`${this._executionType} is not defined`);
355359
}
@@ -360,22 +364,26 @@ export class Multicall {
360364
* @param calls The calls context
361365
*/
362366
private async executeWithWeb3(
363-
calls: AggregateCallContext[]
367+
calls: AggregateCallContext[],
368+
options: ContractCallOptions
364369
): Promise<AggregateResponse> {
365370
const web3 = this.getTypedOptions<MulticallOptionsWeb3>().web3Instance;
366371
const networkId = await web3.eth.net.getId();
367372
const contract = new web3.eth.Contract(
368373
this.ABI,
369374
this.getContractBasedOnNetwork(networkId)
370375
);
371-
376+
const callParams = [];
377+
if (options.blockNumber) {
378+
callParams.push(options.blockNumber);
379+
}
372380
if (this._options.tryAggregate) {
373381
const contractResponse = (await contract.methods
374382
.tryBlockAndAggregate(
375383
false,
376384
this.mapCallContextToMatchContractFormat(calls)
377385
)
378-
.call()) as AggregateContractResponse;
386+
.call(...callParams)) as AggregateContractResponse;
379387

380388
contractResponse.blockNumber = BigNumber.from(
381389
contractResponse.blockNumber
@@ -385,7 +393,7 @@ export class Multicall {
385393
} else {
386394
const contractResponse = (await contract.methods
387395
.aggregate(this.mapCallContextToMatchContractFormat(calls))
388-
.call()) as AggregateContractResponse;
396+
.call(...callParams)) as AggregateContractResponse;
389397

390398
contractResponse.blockNumber = BigNumber.from(
391399
contractResponse.blockNumber
@@ -400,7 +408,8 @@ export class Multicall {
400408
* @param calls The calls
401409
*/
402410
private async executeWithEthersOrCustom(
403-
calls: AggregateCallContext[]
411+
calls: AggregateCallContext[],
412+
options: ContractCallOptions
404413
): Promise<AggregateResponse> {
405414
let ethersProvider = this.getTypedOptions<MulticallOptionsEthers>()
406415
.ethersProvider;
@@ -425,17 +434,25 @@ export class Multicall {
425434
this.ABI,
426435
ethersProvider
427436
);
428-
437+
let overrideOptions = {};
438+
if (options.blockNumber) {
439+
overrideOptions = {
440+
...overrideOptions,
441+
blockTag: Number(options.blockNumber),
442+
};
443+
}
429444
if (this._options.tryAggregate) {
430445
const contractResponse = (await contract.callStatic.tryBlockAndAggregate(
431446
false,
432-
this.mapCallContextToMatchContractFormat(calls)
447+
this.mapCallContextToMatchContractFormat(calls),
448+
overrideOptions
433449
)) as AggregateContractResponse;
434450

435451
return this.buildUpAggregateResponse(contractResponse, calls);
436452
} else {
437453
const contractResponse = (await contract.callStatic.aggregate(
438-
this.mapCallContextToMatchContractFormat(calls)
454+
this.mapCallContextToMatchContractFormat(calls),
455+
overrideOptions
439456
)) as AggregateContractResponse;
440457

441458
return this.buildUpAggregateResponse(contractResponse, calls);

0 commit comments

Comments
 (0)