1- import { fromHexString , toHexString } from '@chainsafe/ssz' ;
2- import { DefaultStateManager } from '@ethereumjs/statemanager' ;
3- import { defaultAbiCoder } from '@ethersproject/abi' ;
4- import { Api , ApiError , getClient } from '@lodestar/api' ;
5- import { createChainForkConfig } from '@lodestar/config' ;
6- import { genesisData as networkGenesis , NetworkName , networksChainConfig } from '@lodestar/config/networks' ;
7- import { Lightclient , LightclientEvent , RunStatusCode } from '@lodestar/light-client' ;
8- import { LightClientRestTransport } from '@lodestar/light-client/transport' ;
9- import { getLcLoggerConsole } from '@lodestar/light-client/utils' ;
10- import { allForks , bellatrix , ssz } from '@lodestar/types' ;
11- import { keccak256 , toBuffer } from 'ethereumjs-util' ;
1+ import { NetworkName } from '@lodestar/config/networks' ;
2+ import { LightclientEvent } from '@lodestar/light-client' ;
3+
4+ import { allForks } from '@lodestar/types' ;
125import { formatUnits } from 'ethers' ;
136import Web3 from 'web3' ;
14- import { hexToNumberString , numberToHex } from 'web3-utils' ;
157import { createVerifiedExecutionProvider , LCTransport , Web3jsProvider , ProofProvider } from '@lodestar/prover' ;
8+ import { hexToNumberString } from 'web3-utils' ;
9+ import { Multicall , ContractCallResults , ContractCallContext } from 'ethereum-multicall' ;
1610
17- const balanceOfABI = [
11+ const ERC20ABI = [
1812 {
1913 constant : true ,
2014 inputs : [
@@ -34,6 +28,19 @@ const balanceOfABI = [
3428 stateMutability : 'view' ,
3529 type : 'function' ,
3630 } ,
31+ {
32+ constant : true ,
33+ inputs : [ ] ,
34+ name : 'decimals' ,
35+ outputs : [
36+ {
37+ name : 'decimals' ,
38+ type : 'uint8' ,
39+ } ,
40+ ] ,
41+ payable : false ,
42+ type : 'function' ,
43+ } ,
3744] ;
3845
3946export type ERC20Contract = {
@@ -52,7 +59,6 @@ export type LightClientVerifierInitArgs = {
5259 network : NetworkName ;
5360 elRpcUrl : string ;
5461 beaconApiUrl : string ;
55- erc20Contracts : Record < string , ERC20Contract > ;
5662 initialCheckpoint : string ;
5763} ;
5864
@@ -94,22 +100,18 @@ export type AccountsToVerify = Record<string, AccountBalance>;
94100
95101export class LightClientVerifier {
96102 private web3 : Web3 | undefined ;
97- private stateManager : DefaultStateManager ;
98- private erc20Contracts : Record < string , ERC20Contract > ;
99103 private network : NetworkName ;
100104 private beaconApiUrl : string ;
101105 private initialCheckpoint : string ;
102106 private elRpcUrl : string ;
103107 public provider : Web3jsProvider | undefined ;
104108 public proofProvider : ProofProvider | undefined ;
105109
106- constructor ( { network, elRpcUrl, beaconApiUrl, erc20Contracts , initialCheckpoint } : LightClientVerifierInitArgs ) {
110+ constructor ( { network, elRpcUrl, beaconApiUrl, initialCheckpoint } : LightClientVerifierInitArgs ) {
107111 this . elRpcUrl = elRpcUrl ;
108112 this . beaconApiUrl = beaconApiUrl ;
109113 this . network = network ;
110- this . erc20Contracts = erc20Contracts ;
111114 this . initialCheckpoint = initialCheckpoint ;
112- this . stateManager = new DefaultStateManager ( ) ;
113115 }
114116
115117 public async verifyBalances (
@@ -119,14 +121,8 @@ export class LightClientVerifier {
119121 ) : Promise < BalanceVerificationResult > {
120122 const accountsResult : Record < string , BalanceComparisonAtBlock > = { } ;
121123 for ( const [ address , balance ] of Object . entries ( accountsToVerify ) ) {
122- const matchingContracts : ERC20Contract [ ] = [ ] ;
123- Object . entries ( this . erc20Contracts ) . forEach ( ( [ address , erc20Contract ] ) => {
124- if ( Object . keys ( balance . erc20Balances ) . includes ( address ) ) {
125- matchingContracts . push ( erc20Contract ) ;
126- }
127- } ) ;
128-
129- const accountResult = await this . fetchAndVerifyAccount ( address , matchingContracts ) ;
124+ const erc20Addresses : string [ ] = Object . keys ( balance . erc20Balances ) ;
125+ const accountResult = await this . fetchAndVerifyAccount ( address , erc20Addresses ) ;
130126 const balanceComparisonResult = this . compareBalances (
131127 balance ,
132128 accountResult ?. balance ,
@@ -213,22 +209,23 @@ export class LightClientVerifier {
213209 this . provider = provider ;
214210 this . proofProvider = proofProvider ;
215211 this . web3 = new Web3 ( provider ) ;
212+ await this . waitForClientToStart ( ) ;
216213 }
217214
218215 public async stop ( ) {
219216 await this . proofProvider ?. lightClient ?. stop ( ) ;
220217 }
221218
222- private async waitForClientStarted ( ) : Promise < void > {
219+ private async waitForClientToStart ( ) : Promise < void > {
223220 await this . proofProvider ?. waitToBeReady ( ) ;
224221 }
225222
226223 public setOptimisticHeaderHook ( handler : ( newHeader : allForks . LightClientHeader ) => void ) {
227224 this . proofProvider ?. lightClient ! . emitter . on ( LightclientEvent . lightClientOptimisticHeader , handler ) ;
228225 }
229226
230- private async fetchAndVerifyAccount ( address : string , erc20Contracts : ERC20Contract [ ] ) {
231- await this . waitForClientStarted ( ) ;
227+ private async fetchAndVerifyAccount ( address : string , erc20Contracts : string [ ] ) {
228+ await this . waitForClientToStart ( ) ;
232229 const verifiedAccount = await this . fetchAndVerifyAddressBalances ( {
233230 address,
234231 erc20Contracts,
@@ -242,25 +239,83 @@ export class LightClientVerifier {
242239 erc20Contracts,
243240 } : {
244241 address : string ;
245- erc20Contracts : ERC20Contract [ ] ;
242+ erc20Contracts : string [ ] ;
246243 } ) : Promise < VerifiedAccount > {
247- const ethBalance = await this . web3 ! . eth . getBalance ( address ) ;
244+ let ethBalance ;
245+ try {
246+ let balance = await this . web3 ! . eth . getBalance ( address ) ;
247+ console . log ( 'eth' , balance ) ;
248+ ethBalance = {
249+ balance : Number ( this . web3 ! . utils . fromWei ( balance , 'ether' ) ) ,
250+ verified : true ,
251+ } ;
252+ } catch ( e ) {
253+ console . log ( 'ERROR eth balance' , e ) ;
254+ ethBalance = {
255+ balance : 0 ,
256+ verified : false ,
257+ } ;
258+ }
259+ const multicall = new Multicall ( {
260+ web3Instance : this . web3 ,
261+ tryAggregate : true ,
262+ multicallCustomContractAddress : '0xca11bde05977b3631167028862be2a173976ca11' , // Getting multicall address from network fails
263+ } ) ;
264+
265+ const contractCallContext : ContractCallContext [ ] = erc20Contracts . map ( ( erc20ContractAddress ) => {
266+ return {
267+ reference : erc20ContractAddress ,
268+ contractAddress : erc20ContractAddress ,
269+ abi : ERC20ABI ,
270+ calls : [
271+ { reference : 'balanceOf' , methodName : 'balanceOf' , methodParameters : [ address ] } ,
272+ { reference : 'decimals' , methodName : 'decimals' , methodParameters : [ ] } ,
273+ ] ,
274+ } ;
275+ } ) ;
248276 const erc20Balances : Record < string , VerifiedBalance > = { } ;
249- for ( const erc20Contract of erc20Contracts ) {
250- const contract = new this . web3 ! . eth . Contract ( balanceOfABI , erc20Contract . contractAddress , { dataInputFill : 'data' } ) ;
251- console . log ( contract . methods . balanceOf ) ;
252- let balance = await contract . methods . balanceOf ( ) . call ( ) ;
253- console . log ( balance ) ;
254- // balance = parseFloat(formatUnits(hexToNumberString(contractBalance, erc20Contract.decimals)));
255- erc20Balances [ erc20Contract . contractAddress ] = { balance : 0 , verified : true } ;
277+ const results : ContractCallResults = await multicall . call ( contractCallContext ) ;
278+
279+ for ( const key of Object . keys ( results . results ) ) {
280+ const result = results . results [ key ] ;
281+ const erc20ContractAddress = key ;
282+ console . log ( erc20ContractAddress , result ) ;
283+ if ( result && result . callsReturnContext ) {
284+ // @ts -ignore
285+ const balanceResult = result . callsReturnContext . find ( ( call ) => call . reference === 'balanceOf' ) ;
286+ // @ts -ignore
287+ const decimalsResult = result . callsReturnContext . find ( ( call ) => call . reference === 'decimals' ) ;
288+
289+ if ( balanceResult ?. success && decimalsResult ?. success ) {
290+ const decimals = decimalsResult ?. returnValues [ 0 ] ;
291+ const balance = parseFloat ( formatUnits ( hexToNumberString ( balanceResult ?. returnValues [ 0 ] . hex ) , decimals ) ) ;
292+ console . log ( 'contract' , erc20ContractAddress , balance ) ;
293+ erc20Balances [ erc20ContractAddress ] = { balance : balance , verified : true } ;
294+ } else {
295+ erc20Balances [ erc20ContractAddress ] = { balance : 0 , verified : false } ;
296+ }
297+ }
256298 }
257299
300+ // const erc20Balances: Record<string, VerifiedBalance> = {};
301+ // for (const erc20ContractAddress of erc20Contracts) {
302+ // const contract = new this.web3!.eth.Contract(ERC20ABI, erc20ContractAddress, { dataInputFill: 'data' });
303+ // try {
304+ // // @ts -ignore
305+ // let balance = (await contract.methods.balanceOf(address).call()) as BigNumberish;
306+ // const decimals = (await contract.methods.decimals().call()) as Numeric;
307+ // console.log('contract', erc20ContractAddress, balance);
308+ // balance = parseFloat(formatUnits(balance, decimals));
309+ // erc20Balances[erc20ContractAddress] = { balance: balance, verified: true };
310+ // } catch (e) {
311+ // console.log('ERROR erc20 balance', e);
312+ // erc20Balances[erc20ContractAddress] = { balance: 0, verified: false };
313+ // }
314+ // }
315+
258316 return {
259317 balance : {
260- ethBalance : {
261- balance : Number ( this . web3 ! . utils . fromWei ( ethBalance , 'ether' ) ) ,
262- verified : true ,
263- } ,
318+ ethBalance,
264319 erc20Balances,
265320 } ,
266321 blockNumber : this . proofProvider ?. getStatus ( ) . latest ! ,
0 commit comments