MulticallSubprovider = {
const AGGREGATE_SELECTOR = '0x252dba42';
const sigFuncMap = {
'57de26a4': 'read()',
'51f91066': 'tag()',
'59e02dd7': 'peek()'
};
const sigToFunc = sig => {
return sigFuncMap[`${sig}`] || null;
};
return class extends SubProvider {
constructor(opts) {
super(opts);
this.multicallAddress = opts.multicallAddress;
if (!this.multicallAddress)
throw new Error('No multicall contract address specified');
this.rpcUrl = opts.rpcUrl;
if (!this.rpcUrl) throw new Error('No rpcUrl specified');
this.id = 100000;
this.latestBlock = null;
this.batchTimer = null;
this.batchDelay = opts.batchDelay || 500;
this.batchedTxs = [];
this.batchedCallbacks = [];
}
setEngine(engine) {
this.engine = engine;
engine.on('block', block => {
const blockNumber = parseInt(block.number.toString('hex'), 16);
this.latestBlock = blockNumber;
});
}
sendBatchedTxs() {
const values = this.batchedTxs.map(({ params }) => [
params[0].to,
params[0].data
]);
const calldata = ethers.utils.defaultAbiCoder.encode(
[
{
components: [{ type: 'address' }, { type: 'bytes' }],
name: 'data',
type: 'tuple[]'
}
],
[values]
);
const payload = {
jsonrpc: '2.0',
id: this.id++,
method: 'eth_call',
params: [
{
to: this.multicallAddress,
data: AGGREGATE_SELECTOR + calldata.substr(2)
},
'latest'
]
};
const callbacks = [...this.batchedCallbacks];
this.batchTimer = null;
this.batchedTxs = [];
this.batchedCallbacks = [];
fetch(this.rpcUrl, {
method: 'POST',
body: JSON.stringify(payload)
})
.then(resp => resp.json())
.then(({ result }) => {
const decoded = ethers.utils.defaultAbiCoder.decode(
['uint256', 'bytes[]'],
result
);
const [blockNumber, ...results] = decoded;
callbacks.forEach((cb, i) => cb(null, results[0][i]));
});
}
handleRequest(payload, next, end) {
if (payload.method === 'eth_call' && !payload.skipMulticall) {
const sig = payload.params[0].data.substr(2, 10);
this.batchedTxs.push(payload);
this.batchedCallbacks.push(end);
if (!this.batchTimer)
this.batchTimer = setTimeout(
() => this.sendBatchedTxs(),
this.batchDelay
);
return;
} else next();
}
};
}