Skip to content
This repository was archived by the owner on Aug 12, 2023. It is now read-only.

Commit 1119f2a

Browse files
authored
Merge pull request #10 from abigger87/fix/output-format
Fix/output format
2 parents 43c3fa4 + bac950e commit 1119f2a

File tree

14 files changed

+164
-233
lines changed

14 files changed

+164
-233
lines changed

README.md

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Since `pilum` makes an RPC call to a Multicall contract, we need to use a provid
3737

3838
```js
3939
import { ethers } from 'ethers';
40-
import { Multicall } from 'pilum';
40+
import { Multicall, ContractCall } from 'pilum';
4141

4242
// Create a custom provider
4343
const provider = new ethers.providers.JsonRpcProvider(
@@ -50,12 +50,14 @@ const multicall = new Multicall({
5050
});
5151

5252
// Define our calls
53-
const calls = [
53+
const calls: ContractCall[] = [
5454
{
55-
reference: 'multicall3',
55+
reference: 'blockNumCall',
5656
contractAddress: '0xcA11bde05977b3631167028862bE2a173976CA11',
5757
abi: [ { name: 'getBlockNumber', type: 'function', stateMutability: 'view', inputs: [], outputs: [ { name: 'blockNumber', type: 'uint256' }] } ],
58-
calls: [{ reference: 'blockNumCall', method: 'getBlockNumber', params: [], value: 0 }]
58+
method: 'getBlockNumber',
59+
params: [],
60+
value: 0,
5961
}
6062
];
6163

@@ -72,16 +74,17 @@ console.log(results);
7274
**Not Recommended** (due to RPC unreliability).
7375

7476
```js
75-
import { ethers } from 'ethers';
76-
import { Multicall } from 'pilum';
77+
import { Multicall, ContractCall } from 'pilum';
7778

7879
// Define our calls
79-
const calls = [
80+
const calls: ContractCall[] = [
8081
{
81-
reference: 'multicall3',
82+
reference: 'blockNumCall',
8283
contractAddress: '0xcA11bde05977b3631167028862bE2a173976CA11',
8384
abi: [ { name: 'getBlockNumber', type: 'function', stateMutability: 'view', inputs: [], outputs: [ { name: 'blockNumber', type: 'uint256' }] } ],
84-
calls: [{ reference: 'blockNumCall', method: 'getBlockNumber', params: [], value: 0 }]
85+
method: 'getBlockNumber',
86+
params: [],
87+
value: 0,
8588
}
8689
];
8790

@@ -93,6 +96,24 @@ console.log(results);
9396
```
9497

9598

99+
#### Multicall Options
100+
101+
Let's say you instantiated a multicall instance in your code, but later on want to multicall on a different network or to a different multicall address, without instantiating a new multicall instance.
102+
103+
`pilum` provides a flexible way to specify these parameters when executing a call. This is done by adding an optional `options` parameter to both the static (zero-config) and class-level `call` method.
104+
105+
To specify these parameters, execute a call with an additional object as the last parameter.
106+
107+
```typescript
108+
const { results } = await Multicall.call(calls, {
109+
provider: provider,
110+
abi: <YOUR_MULTICALL_ABI_OBJECT>,
111+
address: '<YOUR-MULTICALL-ADDRESS>',
112+
network: 5, // Any network id supported by ethers, here 5 is the Ethereum Goerli Testnet
113+
});
114+
```
115+
116+
96117
#### Custom Multicall Contracts
97118

98119
By default, the deployed [Multicall3](https://github.com/mds1/multicall/blob/master/src/Multicall3.sol) contract is used. This can be overridden by specifying the `address` parameter in the constructor like so:
@@ -141,7 +162,7 @@ src
141162
├─ index.ts — "Package Re-exports"
142163
├─ Multicall.ts — "The Multicall Contract"
143164
tests
144-
└─ Multicall.test.ts — "Multicall Tests"
165+
└─ ...
145166
```
146167

147168

src/AbiMapper.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Interface } from '@ethersproject/abi';
22
import { multicall1, multicall2, multicall3 } from './abis';
3-
import { Options } from './models';
3+
import { Options, Address, Mapping, Network } from './models';
44
import { networks } from './networks';
5-
import { Address, Mapping, Network } from './types';
65

76
// Checks if the provided address is a valid multicall address in our network definitions
87
const constructWithAddress = (address: Address): Mapping => {

src/Multicall.ts

Lines changed: 55 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import { Contract, ContractInterface, ethers } from 'ethers';
22
import { networks } from './networks';
33
import { multicall1, multicall2, multicall3 } from './abis';
44
import {
5-
AggregateCallResponse,
6-
AggregatedCall,
7-
AggregateFullResponse,
5+
MulticallResponse,
86
ContractCall,
7+
EncodedCall,
98
Options,
9+
RawCallResponse,
10+
Address,
1011
} from './models';
1112
import { abiMap, Mapping } from './AbiMapper';
12-
import { Address } from './types';
1313
import { Interface } from '@ethersproject/abi';
1414

1515
// Multicall - A library for calling multiple contracts in aggregate
@@ -49,27 +49,20 @@ export class Multicall {
4949
this.interface = new ethers.utils.Interface(JSON.stringify(this.abi));
5050
}
5151

52-
public static encode(calls: ContractCall[] | ContractCall): AggregatedCall[] {
53-
const callray: ContractCall[] = !Array.isArray(calls) ? [calls] : calls;
54-
const encodedCalls: AggregatedCall[] = [];
55-
callray.forEach((call, index) => {
52+
public static encode(calls: ContractCall[] | ContractCall): EncodedCall[] {
53+
const callArray: ContractCall[] = !Array.isArray(calls) ? [calls] : calls;
54+
const encodedCalls: EncodedCall[] = [];
55+
callArray.forEach((call) => {
5656
// Grab each call's abi
57-
const calliface = new ethers.utils.Interface(call.abi);
58-
call.calls.forEach((method, calli) => {
59-
// Encode the call
60-
const encodedData = calliface.encodeFunctionData(
61-
method.method,
62-
method.params
63-
);
64-
// Push to the list of encoded calls
65-
encodedCalls.push({
66-
contractContextIndex: index,
67-
contractMethodIndex: calli,
68-
allowFailure: method.allowFailure || false,
69-
target: call.contractAddress,
70-
encodedData,
71-
});
72-
});
57+
const callInterface = new ethers.utils.Interface(call.abi);
58+
// Encode the call
59+
const encodedData = callInterface.encodeFunctionData(
60+
call.method,
61+
call.params
62+
);
63+
// Push to the list of encoded calls
64+
const newCall = { encodedData, ...call };
65+
encodedCalls.push(newCall);
7366
});
7467

7568
return encodedCalls;
@@ -91,57 +84,39 @@ export class Multicall {
9184
}
9285

9386
// Explodes an Aggregate Response into the Full Response
94-
public static explodeResponse(
95-
res: AggregateCallResponse,
96-
calls: AggregatedCall[]
97-
): AggregateFullResponse {
98-
// Build the AggregateFullResponse from the Multicall3 Aggregate3Response
99-
const a3Response: AggregateFullResponse = {
87+
public static explode(
88+
callres: RawCallResponse,
89+
calls: EncodedCall[]
90+
): MulticallResponse {
91+
// Build the final result
92+
const exploded: MulticallResponse = {
93+
blockNumber: callres.blockNumber,
94+
blockHash: callres.blockHash,
10095
results: [],
10196
};
10297

103-
// Iterate over the return data
104-
for (let i = 0; i < res.returnData.length; i++) {
105-
// For existing contracts in the multicall, we can just append to the method results
106-
const existingResponse = a3Response.results.find(
107-
(c) => c.contractContextIndex === calls[i].contractContextIndex
108-
);
109-
if (existingResponse) {
110-
existingResponse.methodResults.push({
111-
blockHash: res.blockHash,
112-
blockNumber: res.blockNumber,
113-
returnData: res.returnData[i],
114-
contractMethodIndex: calls[i].contractMethodIndex,
115-
});
116-
} else {
117-
a3Response.results.push({
118-
methodResults: [
119-
{
120-
blockHash: res.blockHash,
121-
blockNumber: res.blockNumber,
122-
returnData: res.returnData[i],
123-
contractMethodIndex: calls[i].contractMethodIndex,
124-
},
125-
],
126-
contractContextIndex: calls[i].contractContextIndex,
127-
});
128-
}
129-
}
98+
// Map call responses to their original calls
99+
exploded.results = callres.returnData.map((res: any, index: number) => {
100+
return {
101+
returnData: res,
102+
...calls[index],
103+
};
104+
});
130105

131106
// Finally, return the full response
132-
return a3Response;
107+
return exploded;
133108
}
134109

135110
// Aggregate3 Call on Multicall3 Contract
136111
// Builds response from Multicall3 specific response format
137112
public static async aggregate3(
138-
calls: AggregatedCall[],
113+
calls: EncodedCall[],
139114
contract: Contract
140-
): Promise<AggregateFullResponse> {
115+
): Promise<MulticallResponse> {
141116
// Construct Multicall3 Aggregate3 Calls
142117
const a3calls = calls.map((call) => {
143118
return {
144-
target: call.target,
119+
target: call.address,
145120
allowFailure: call.allowFailure,
146121
callData: call.encodedData,
147122
};
@@ -150,22 +125,22 @@ export class Multicall {
150125
const callres = await contract.callStatic.aggregate3(a3calls);
151126

152127
// Call Multicall3 aggregate3 method and get back the returnData[]
153-
const res: AggregateCallResponse = {
128+
const res: RawCallResponse = {
154129
returnData: callres,
155130
};
156131

157-
return Multicall.explodeResponse(res, calls);
132+
return Multicall.explode(res, calls);
158133
}
159134

160135
// tryAggregate Call on Multicall2 or Multicall3 Contract
161136
public static async tryAggregate(
162-
calls: AggregatedCall[],
137+
calls: EncodedCall[],
163138
contract: Contract
164-
): Promise<AggregateFullResponse> {
139+
): Promise<MulticallResponse> {
165140
// Map calls to the Multicall Call Struct format
166141
const mcalls = calls.map((call) => {
167142
return {
168-
target: call.target,
143+
target: call.address,
169144
callData: call.encodedData,
170145
};
171146
});
@@ -178,52 +153,48 @@ export class Multicall {
178153
mcalls
179154
);
180155

181-
const res: AggregateCallResponse = {
156+
const res: RawCallResponse = {
182157
blockNumber: callres[0],
183158
blockHash: callres[1],
184159
returnData: callres[2],
185160
};
186161

187-
return Multicall.explodeResponse(res, calls);
162+
return Multicall.explode(res, calls);
188163
}
189164

190165
// tryAggregate Call on Multicall2 or Multicall3 Contract
191166
public static async aggregate(
192-
calls: AggregatedCall[],
167+
calls: EncodedCall[],
193168
contract: Contract
194-
): Promise<AggregateFullResponse> {
169+
): Promise<MulticallResponse> {
195170
// Map calls to the Multicall Call Struct format
196171
const mcalls = calls.map((call) => {
197172
return {
198-
target: call.target,
173+
target: call.address,
199174
callData: call.encodedData,
200175
};
201176
});
202177

203178
// Statically call on the multicall contract
204179
const res = await contract.callStatic.aggregate(mcalls);
205180

206-
// Translate the raw response into an aggregate3 and tryAggregate response
207-
// NOTE: Ignores the block number from the response
208-
const aggregatedResponse: AggregateCallResponse = {
181+
// Translate the raw response into our response format
182+
const callres: RawCallResponse = {
209183
blockNumber: res[0],
210184
returnData: res[1],
211185
};
212186

213-
return Multicall.explodeResponse(aggregatedResponse, calls);
187+
return Multicall.explode(callres, calls);
214188
}
215189

216190
public static async execute(
217191
abi: object,
218192
multicall: string,
219193
provider: ethers.providers.Provider | ethers.Signer,
220-
calls: AggregatedCall[]
221-
): Promise<AggregateFullResponse> {
194+
calls: EncodedCall[]
195+
): Promise<MulticallResponse> {
222196
const contract: Contract = Multicall.getContract(multicall, abi, provider);
223197

224-
// TODO: make this "adaptive" by allowing calls to not specify graceful/allowFailure param
225-
// TODO: in this case, multicalls should fall back to backwards-compatible method
226-
227198
// If the multicall contract has an aggregate3 method, use it
228199
if (
229200
contract.interface.functions['aggregate3((address,bool,bytes)[])']?.name
@@ -253,9 +224,9 @@ export class Multicall {
253224
public static async call(
254225
calls: ContractCall[] | ContractCall,
255226
options?: Options
256-
): Promise<AggregateFullResponse> {
227+
): Promise<MulticallResponse> {
257228
// Encode the calls
258-
const encoded: AggregatedCall[] = Multicall.encode(calls);
229+
const encoded: EncodedCall[] = Multicall.encode(calls);
259230

260231
// Craft default configuration
261232
const map: Mapping = abiMap(options);
@@ -273,9 +244,9 @@ export class Multicall {
273244
public async call(
274245
calls: ContractCall[] | ContractCall,
275246
options?: Options
276-
): Promise<AggregateFullResponse> {
247+
): Promise<MulticallResponse> {
277248
// Encode the calls
278-
const encoded: AggregatedCall[] = Multicall.encode(calls);
249+
const encoded: EncodedCall[] = Multicall.encode(calls);
279250

280251
// Craft custom configuration
281252
const map: Mapping =

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export { Multicall } from './Multicall';
2-
export * from './types';
2+
export * from './models';
33
export * from './AbiMapper';

src/models/AggregateCallResponse.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/models/AggregateFullResponse.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/models/AggregatedCall.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)