您当前的位置: 首页 > 

【03】

暂无认证

  • 0浏览

    0关注

    196博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

二次开发ethers-multicall

【03】 发布时间:2021-11-03 09:21:43 ,浏览量:0

为什么要对ethers-multicall进行二次开发?

在项目中,假设有100处使用了multicall同时发了600个call请求,每个muticall里有1~N个call,则至少要发送100个multicall rpc请求,在浏览器中很容易造成堵塞或者失败的情况

实现:100处muticall600个call,只用2个rpc进行请求

思路:

1、重写multicall.all方法

export function all(
  calls: ContractCall[],
  multicallAddress: string,
  provider: any,
): Promise {
return new Promise(async (resolve, reject) => {
    if (QData['call_' + callId[chainId] + chainId].queryCalls.length + calls.length > MAX_CALLS) {
      await handleData();
      debounce(resolve);
    } else {
      debounce(resolve);
    }
  });
}

2、在all方法中收集所有的calls,provider

3、设置muticall最大容量(容器)以及使用溢出id与chainId区分

const MAX_CALLS = 500;
const callId = {
	//128:0,
	//56:0
};
const QData = {
  // call_1280: {
    // queryCalls: [],
    // resolveList: [],
    // timeDebounce: null,
    // lenArr: []
  // }
};

4、,初始化:设置callId区分每一个容器,callId需要使用chainId进行区分,provider可以获取

  const chainId = provider._network.chainId;
  if (!callId[chainId]) {
    callId[chainId] = 0;
  }
  if (!QData['call_' + callId[chainId] + chainId]) {
    QData['call_' + callId[chainId] + chainId] = {
      queryCalls: [],
      resolveList: [],
      timeDebounce: null,
      lenArr: []
    };
  }

5、当每一次容器满了之后,再去发请求获取数据

  async function handleData() {
    const qKey = 'call_' + callId[chainId] + chainId;
    const {queryCalls, resolveList, timeDebounce, lenArr} = QData[qKey];
    clearTimeout(timeDebounce);
    delete QData[qKey];
    callId[chainId] = callId[chainId] + 1;
    QData[qKey] = {
      queryCalls: [],
      resolveList: [],
      timeDebounce: null,
      lenArr: []
    };
    const multicall = new ethers.Contract(multicallAddress, multicallAbi, provider);
    const callRequests = queryCalls.map(call => {
      const callData = Abi.encode(call.name, call.inputs, call.params);
      return {
        target: call.contract.address,
        callData,
      };
    });
    const response: any = await multicall.callStatic.aggregate(callRequests);
    const callResult = [] as T;
    for (let i = 0; i  {
      const data = callResult.splice(0, lenArr.shift());
      resolve(data);
    });
  }

6、设置debounce,避免容器不满导致过长的请求,以及不满的情况下进行push处理,calls,resolve,calls.length

function debounce(resolve) {
    const qKey = 'call_' + callId[chainId] + chainId;
    QData[qKey].queryCalls.push(...calls);
    QData[qKey].resolveList.push(resolve);
    QData[qKey].lenArr.push(calls.length);
    clearTimeout(QData[qKey].timeDebounce);
    QData[qKey].timeDebounce = setTimeout(async () => {
      await handleData();
    }, 100);
  }
code
const MAX_CALLS = 500;
const callId = {};
// tslint:disable-next-line:variable-name
const QData = {
  // call_0: {
    // queryCalls: [],
    // resolveList: [],
    // timeDebounce: null,
    // lenArr: []
  // }
};

export function all(
  calls: ContractCall[],
  multicallAddress: string,
  provider: any,
): Promise {
  const chainId = provider._network.chainId;
  if (!callId[chainId]) {
    callId[chainId] = 0;
  }
  if (!QData['call_' + callId[chainId] + chainId]) {
    QData['call_' + callId[chainId] + chainId] = {
      queryCalls: [],
      resolveList: [],
      timeDebounce: null,
      lenArr: []
    };
  }
  async function handleData() {
    const qKey = 'call_' + callId[chainId] + chainId;
    const {queryCalls, resolveList, timeDebounce, lenArr} = QData[qKey];
    clearTimeout(timeDebounce);
    delete QData[qKey];
    callId[chainId] = callId[chainId] + 1;
    QData[qKey] = {
      queryCalls: [],
      resolveList: [],
      timeDebounce: null,
      lenArr: []
    };
    const multicall = new ethers.Contract(multicallAddress, multicallAbi, provider);
    const callRequests = queryCalls.map(call => {
      const callData = Abi.encode(call.name, call.inputs, call.params);
      return {
        target: call.contract.address,
        callData,
      };
    });
    const response: any = await multicall.callStatic.aggregate(callRequests);
    const callResult = [] as T;
    for (let i = 0; i  {
      const data = callResult.splice(0, lenArr.shift());
      resolve(data);
    });
  }

  function debounce(resolve) {
    const qKey = 'call_' + callId[chainId] + chainId;
    QData[qKey].queryCalls.push(...calls);
    QData[qKey].resolveList.push(resolve);
    QData[qKey].lenArr.push(calls.length);
    clearTimeout(QData[qKey].timeDebounce);
    QData[qKey].timeDebounce = setTimeout(async () => {
      await handleData();
    }, 100);
  }

  return new Promise(async (resolve, reject) => {
    if (QData['call_' + callId[chainId] + chainId].queryCalls.length + calls.length > MAX_CALLS) {
      await handleData();
      debounce(resolve);
    } else {
      debounce(resolve);
    }
  });
}
插件地址

https://www.npmjs.com/package/ethers-multicall-x

npm i ethers-multicall-x

用法,同ethers-multicall,以上优化只在插件内部实现

关注
打赏
1657344724
查看更多评论
立即登录/注册

微信扫码登录

0.0410s