Skip to content

Golang library and utility for extracting erc20 token balances from EVM storage proofs

License

Notifications You must be signed in to change notification settings

vocdoni/storage-proofs-eth-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ethereum storage proofs for GoLang

A storage trie is where all of the contract data lives. Each Ethereum account has its own storage trie. A 256-bit hash of the storage trie’s root node is stored as the storageRoot value in the global state trie.

A storage proof is a Merkle Proof on this storage trie. For the scope of this proposal, the storage proof demonstrates the balance of a token holder for a specific State Root Hash (Ethereum block). This proof can be verified offchain.

storage trie

EIP1186 introduces the web3 call eth_getProof which is a convenient method for obtaining the storage proof.

More info regarding storage proofs:

Status

This GoLang module is WIP.

TODO list:

  • create an interface around token
  • add support for MiniMe tokens
  • add support for EIP721
  • explore adding support for other token standard rather than ERC20
  • write tests

Usage

This repository brings all required GoLang packages for generate and verify Ethereum storage proofs for ERC20 contracts.

Create a token.ERC20Token{} type variable, and initialize it with contract 0xdac17f958d2ee523a2206206994597c13d831ec7 (Tether Stablecoin).

    web3 := "https://web3.dappnode.net" // do not abuse please
    ts := token.ERC20Token{}
    ts.Init(context.Background(), web3, "0xdac17f958d2ee523a2206206994597c13d831ec7")

Fetch the basic token data (decimals, name, etc.) and the balance for the token holder 0x5041ed759dd4afc3a72b8192c143f72f4724081a.

	tokenData, err := td.GetTokenData()
	if err != nil {
		panic(err)
	}
	holderAddr := common.HexToAddress("0x5041ed759dd4afc3a72b8192c143f72f4724081a")

	balance, err := td.Balance(holderAddr)
	if err != nil {
		panic(err)
	}
	fmt.Printf("balance from ERC20 ABI call balanceOf() is %s\n", balance.String())

For each contract we need to find the storage index slot. It depends on the contract implementation, in which storage position the balance is stored. For a map based balances ERC20 map(address)=>uint256, the storage slot for a specific token holder will be equal to keccack256( tokenHolder + indexSlot ).

	tk, err := token.NewToken(token.TokenTypeMapbased, contract, web3)
	if err != nil {
		log.Fatal(err)
	}
	slot, balance, err := tk.DiscoverSlot(common.HexToAddress(holders[0]))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("index slot for the contract is %d\n", slot)
	fmt.Printf("balance found on the EVM storage is %s\n"), balance.String())

Now lets get the storage proof for the previous token holder on the last Ethereum block, this is obtained using EIP1186 web3 method eth_getProof for the last Ethereum block.

	sproof, err := tk.GetProof(holderAddr, nil)
	if err != nil {
		panic(err)
	}

Finally, for a key, a value (balance) and a Storage Hash Merkle Root, verify the Merkle Storage Proof is valid.

	if pv, err := ethstorageproof.VerifyEIP1186(sproof); pv {
		fmt.Println("account proof and storage proofs are valid!")
	} else {
		fmt.Printf("account proof and storage proofs are invalid (err %s)\n", err)
	}

Proofs of non existing values can also be generated and verified using the same procedure with the only difference of value equal to 0x0.


Example of proof obtained with the eth_getProof method

{
   "address":"0xdac17f958d2ee523a2206206994597c13d831ec7",
   "accountProof":[
      "0xf90211a0384dea0e04cdf33c7972d6fda16170b9b04bb0b7f19907e1376343cb46757225a0a767ccd497f2515220a3f5667fe738f4ee669792c380a1894aec4be0ece5aa81a09713acc380ec317fdc5b827dc23c2f63f0a4b9923242c99bee7f186e47545ceea02e80a0af7fa0445833fd58b3240545d9b17b844a5715369e4ec0c76751a6aa27a05cbf7446065dae784d8275ab38a91567a63dc38b6c6ab47f34fbc27b112511aaa0252f860b05658694d03e82122b2b53c0a2aa27b3cdd6c6b506f6ed78611b8ff0a04c0ac4a26e60b425a8d5f93202334efedd06ff3277e5619ea69e9bcba8d2808ba0a30e5359136d5f00f5f4742aadf0c892e7ad5e3e4eafb4d0ff81e47b68b629dea09d21f0a6ffa5ca1505a6f441bbff23d00cd2d7e4eb3e53d2cad40925e898cd21a036b087a125f6731a255962bb1e3d968b9b17cf3906fc4165b8c0358c60d55dbfa05c5a1d68f48766dc9759fd68c53037c772c46bfc7c17d8a436cd94bbe713b295a0dec84fd1a29839d1f1753c6bea4411bb52ed321a6e635bb73feecd784a884d41a0e2c512fed45b79beda8d69490b125a491319d96de26b66dafcc402fb4f924d33a06c3ba8686212011a60318df596eea333836ec0d1b6f6c19c2bcea94370fda9bba0f13806aec144b2f2686c6097945610e127b35449021409425b43cb01699db911a04cf068b6dbd041b1ac3417262503cdd7370a100f98d202a918888d610b825eec80",
      "0xf90211a017fc8a237f709a9d525259665f6c44fe95ac2c1563a1275f8d6f040a6e6bd712a064e92c041d4010a9fc8184cf0d8ce5ec35b29a6f6eb9d28aa5d261e831b0a247a06803e1c46d87cfac1b18db2a887829759847ebfdf9ff536da170b6883e890f2ca001c238a230844b25f75c7fce273e51030d4a68faaecc90cc0e0cbcf75e990b02a08dcd9431d8553e35753a4059d6aaceda195714a601fbb1fe4eb82b3291175c71a0b5ddf286be094782da6a3f35c2a1fc2dc9d5daa542e02343f85f5b611cc38d35a039d1b06d8f2df8f4497ffd12ed032a37cb776463e9b9bd6dc58da780c6c4bcd4a054fb4472575537badd15ef021be027f7726d761ccb3088da4014bed1ccd0d936a0bde473565f25d95ba83355ef61179db0e5f09b167ecde2c3b2ff38af387547d1a048253d7af577733f9d77146f25e1939a6dc9b1b7b4df7e92a5377e0c21bad6cea05cbf482b66d59074e021ea08582a5c34ff360f2a548238541da819779f27f09ea09acc6af56b43137efafe6caee4923a50e531a026597d5b93bde87b74389ace96a068ea1192ecf9f0c2a8b5e40bebc76215f87b5fc9f3f9958eaea7fe64b34da398a082c17e65c7e205247575cc0a543ca5d43da2bdfbfd68de21abb7aa32267c31ada0686aa8f993fb794a2bdf469b4196c8fd31603d9012f16287a06106dfe5874d3ba0e78109090bca2991159eb726ed0b125a5542b6249563a339a2fc1c8b6fa1889d80",
      "0xf90211a0ac9b57ddb96081b19763e007c420b9166ea3e3cb81a92ec6779fa5012c4a00cea0a2f3d95f6f878ed41c2211e079afabecbb40fbbd382714bfc63c032ee3499f24a0925708c42cda17afe109862da1be30f4531fea0c57b160331bfe91893b3b71fba02ef945d4edc9de0a76e0901ebd61398672a7bd422a6fd5840d7be4be5c65a232a068c67517f4aab7a038aae55a15e1513009f7919305d086b413fb0e801be943f0a05e8613d39e40c546aad1961961ce4dc04387bac1a77b46b065779604f0e41606a062cbff30606d51cd45f458ce61e8ada839895a7a747ea261db30eea7a6be330ca00958eab204a900567e44cf47298dc3cb34333a3e45d657674e54f8625c578662a06257c48f08e7526274385077b00d87d99fc210a703c90fe35def31bcc11cefc3a02abc60edeed5399cfb676ebe7426a57c85b5e018eab10812f89a5b16c76cea68a0ffba8197099393d36377cc4b9a5a1346c925e2a6d1fc14e9ca56f5795102db12a0e9384096699720f9db7e5e66ff0c65874033cd8aa26f5f4b2841c44308e17117a0b30af72e6077c838b9e1dff5fe711d3a75759d22224258a3846449940bf545daa0c2eb8df2bd709ad10e4051155079b277ed20b33e9781d45451ebbc968d8e3c4ba0aafed1e2706d2f38feb683ff3c93bf264b9edddae4716e985a0844c2e3380899a08ae727a23403c25e37135e9a180be1a34c2ebe9981f59929fdc65f98837d003a80",
      "0xf90211a0e1bbf50f7cee3f3da069c14ce664ed374f53951dc21696972eee12c1dc50839da0b6e6c5c3078a1ccb7e08d2ffadd1a000bb6a84fd70d32be60ea780da5f4c15f9a07235156a477b1dc99dfb801a73af75d6ecca8d8500ae645aaabcd4f64fd0fa2ea041c4133ef628f5698f17676e491c34355a3c345c4092108f3eba73eb145459bda0709f062e88f4a2e47a5eb6c7e10bbcf7a64891bb0b924d8f860673f7b44f1bbfa05b1b503e5f6bd3115314e767ced96aed64ff2a4a83b801f34846b87985ca3155a02da2e53cc3a97780e98fe16433e0976f7ae7268531b7e5ee9141c790812e200ca06016c3678dc803061ac5de8c0a21a994c4f98f2e7717f5ef26c1d1ad7afc6af5a09cdfb998edffbcdb10424b6079fa49c6474f1786ebd6aa3d53a868ee94868f42a0669eb33ebf4ef9420273f1464b946b893758ccdb4742da5385d22aa76a896291a015e29157a0242ffd57a2895cb5d72c00321a418cf51d8ec16c69d529e4504aaea0678382e1bd36c2318a4244b8ac38225c065c77c024a262aa5d6bd667acc529f0a081d7411b485a5288436cc91da7838f10fb5c4229677ffc7dfb59473e5dbc4e5ca043937cb4b99b1984b10d3167814e460a05de339f06c19755d440df7bda40fe57a006d96232e235fc308b5bb302a69793cd38738df18864f0d1671bb53d48d3841ba0cec792dc460edfd37e647c560c53f2edb85d978bf4c254e512b823f7e890db8880",
      "0xf90211a0fd4dc179175e5af393ab1d7551bd3a9c5760ed9679df54d34cce97daca2949aaa03baaa57f4b7492162639d4154d45ae5792c2b58065c37fd87b10b75ce897fd80a097474dfc4ee2d38407026dc70abfc9a74ba999328ccd3cd0715f5f2f58dcb154a02f8effca6153706f073550808745fcb5c2333e2374ab1aab0f0c5b7616aebc5aa0067034da9840d6ce9bc8bed341805b38d2f66930278cb0d106cf49b8a353157ca070e069dfffb9e1281493c929825673cf27eeddd39b139561cda7afb4a09c9726a08eb7abd6b39e4ec53b3a63d16c84f98bd2e54a6443078a76386851d8c660aceca0481ca3da2b45dcd27087f4dce07a5776dd7f041b06daf586929fb6a184697472a0ec394b987b3e586e27bc33dbda68b68230abd5a6bb43bd3274eb83b6f88fc6b8a058cbedf165bc9886ce3f3358b276c6d64fe8326743d09e29927e64b915b96bcba0a8ce82444f7b90bd648ed79a07f5473de5ac574f226dc2837398c79c22403397a00ef90fd591599ffd78bb63d02688692c6b8290d63b8d3d6a05ec3e914ff13d87a010fbb6a665a92bd47ecda2f2f7f0f6b0bcec460e0bfee79639564de05c35c65aa0a0d80741d491df5b1703b29fe2ff7b7cf1688a96ae9f82ea4b7ad14f075dd65ca089a0d75785af96ded82b990c18a055c33b6de178dca530eb4f6d686327e62dfba003f6fc685410b06341c89ebe59b5d334c2b592b105da3456e120fa5946772a0080",
      "0xf90211a0667022a2e9d08e61ab0e84eba409a3faa1f117b1448cab10b08a4b3157c12779a0f060906007684bba15aca67b8b8de5d52359d79ee8e0d5427c9816cc1f89b403a09770f1dc4fcca0eb269cbb9065375d88f79d57581d0d2928e8565c9cac03ffb1a0c02c1caec46022380688a901ff39f44a69b24b6f5e8c7423bdc81ab7a0842ae5a03ef6974fb165ccedc4d1272e8cef18fc7c7dd158051fa4fcbe50cf52309289e9a0ea722726bf8c8d8a9351a1d4e530c428f582ab6cab8cc9e33ead52983898cb1ea09fd533ae1eba26257907b7e5ddd6bf710ca5b08ab066cdaeb80c805a44727543a0cfb0452dd7d9a5ac8f1a5ac7c33105285117a80bfa9ffaea4ef232126dbc2f31a0ab9112bb96477a58a2245426f4a4e79a655bb1faace978a3719883dc9c67f347a0e836d2a68e3dc58f9cb0dc770a2aa9321e6beb5747c36ca1aa28796ec151b0cea0a13881e2f4a05e8aa92f20077efe09186164dee35e20ba1d0bfc7c7b26a92e70a0945f49ad82c981890faae5f2551ed7d6d7dce4eddd2bb0bedf2119fa731bf14ba09b28663cea3b6c37a8e07e1a588a381cc3001a11f21add5cc038a17a4397d30ea04dff355675e2f31c9c2de55d16b9f8ca53382f5c024213a5b7936acfb54656cba061a0bb68122afdfae0fb4f556dc3a65d79a690f0e1918004a93f996467ddd550a04b767f6b38c7a6439a4b7b6066207638b9656c416f9ad09a3ee52c600e681c4780",
      "0xf8f18080a0149f0c3b727be267847fe12d583df188a307bc85b5adeb7577450367ef875894a00b3a26a05b5494fb3ff6f0b3897688a5581066b20b07ebab9252d169d928717f80a01e2a1ed3d1572b872bbf09ee44d2ed737da31f01de3c0f4b4e1f0467400664618080a01fb693867275623bcbc46e13db58ddcd0ceda37cd8c5d39ebc56d15b535d40fca07aad8ea34d91339abdfdc55b0d5e0aa4fc3c506f56fd2518b6f8c7c5d2ed254880a0e9864fdfaf3693b2602f56cd938ccd494b8634b1f91800ef02203a3609ca4c21a05904860db3383d925f9a36578fcea05e0ef02ee49d21ce50d0df7056df00b0f880808080",
      "0xf8669d3802a763f7db875346d03fbf86f137de55814b191c069e721f47474733b846f8440101a03310913fe74cfb66dbde8fe8557b48e8e65617a17c2375a581c32d49f812cde4a0b44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55"
   ],
   "balance":"0x1",
   "codeHash":"0xb44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55",
   "nonce":"0x1",
   "storageHash":"0x3310913fe74cfb66dbde8fe8557b48e8e65617a17c2375a581c32d49f812cde4",
   "storageProof":[
      {
         "key":"95eea00c49d14a895954837cd876ffa8cfad96cbaacc40fc31d6df2c902528a8",
         "value":"0xd647b234389e",
         "proof":[
            "0xf90211a03697534056039e03300557bd69fe16e18ce4a6ccd5522db4dfa97dfe1fad3d3aa0b1bf1f230b98b9034738d599177ae817c08143b9395a47f300636b0dd2fb3c5ea0aa04a4966751d4c50063fe13a96a6c7924f665819733f556849b5eb9fa1d6839a0e162e080d1c12c59dc984fb2246d8ad61209264bee40d3fdd07c4ea4ff411b6aa0e5c3f2dde71bf303423f34674748567dcdf8379129653b8213f698468738d492a068a3e3059b6e7115a055a7874f81c5a1e84ddc1967527973f8c78cd86a1c9f8fa0d734bd63b7be8e8471091b792f5bbcbc7b0ce582f6d985b7a15a3c0155242c56a00143c06f57a65c8485dbae750aa51df5dff1bf7bdf28060129a20de9e51364eda07b416f79b3f4e39d0159efff351009d44002d9e83530fb5a5778eb55f5f4432ca036706b52196fa0b73feb2e7ff8f1379c7176d427dd44ad63c7b65e66693904a1a0fd6c8b815e2769ce379a20eaccdba1f145fb11f77c280553f15ee4f1ee135375a02f5233009f082177e5ed2bfa6e180bf1a7310e6bc3c079cb85a4ac6fee4ae379a03f07f1bb33fa26ebd772fa874914dc7a08581095e5159fdcf9221be6cbeb6648a097557eec1ac08c3bfe45ce8e34cd329164a33928ac83fef1009656536ef6907fa028196bfb31aa7f14a0a8000b00b0aa5d09450c32d537e45eebee70b14313ff1ca0126ce265ca7bbb0e0b01f068d1edef1544cbeb2f048c99829713c18d7abc049a80",
            "0xf90211a01f7c019858f447dbff8ed8e4329e88600bab8f17fec9594c664e25acc95da3dba00916866833439bc250b5e08edc2b7c041634ca3b33a013f79eb299a3e33a056fa0a8fae921061f3bc81154b5d2c149d3860a3cdef00d02173ee3b5837de6b26f56a097583621a54e74994619a97cf82f823005a35ad1bf4795047726619eccd11ad4a0be39789c4abdb2a185cce40f2c77575eee2a1eab38d3168395560da15307614aa0c46a7fcea5501656d70508178f0731460edcce1c01e32ed08c1468f2593db277a0460f030038b09b8461d834ded79d77fd3ed25c4e248775752bf1c830a530ef2ba03d887abec623c6b4d93be75d1608dcacb291bf30406fbbc944a94aa4203fb1eba0475eeb07d471044af74313093cfbb0201e17a405d7af13a7ae6b245e18915515a0ea794035230f90e14f4f601d0e9f04217ed02710df363417bc3dd7dda5a84608a08aa9f0c44e5a9e359b65d4f0e40937662f07af10642a626e19ca7bc56c329706a05677c9b342c1cbcfd491cd491800d44423d1bdc08ab00eb59376a7118f7c4e23a0b761f22d67328e6c90caf8be65affb701b045f4f1f581472fc5f724e0c61328da0b6727240643009e59bba78249918a83ca8faeaf1d5b47fe0b41c356e8ae300dda0d7cbeb12faf439126bdb86f94b6262c3a70e88e4d8a47b49735f0b9d632a5df8a0428fe930556ce5ea94bcc4d092fe0f05a2a9b360175857711729ab3a7092a81780",
            "0xf90211a0a81ff74945250ba9925753e379aa8815a3fe77926aad2d02db4d78a3da7ecb48a0e795c64d2b738b34ebb77a3307df800c9f1fe324b442cd71ffa5fd268ec12ffca020a5f968a1c8292d08135cb451daed999a44eb0cdd04526a89c38e753398c50ca0cc7165f6b984f2f2569d101d70f72af94eb79b18c126895da2d3cf557b5aca51a00a10f4dc5851e71f195cca5bd7a2a62a6c3ca03d95e91c7dc55e55f4cb726903a09ecaaf877b18a55ca4001fe46cc10389c94104abc2994cdfe5843f28814b119ca099f59b2b52ee9d9b44ab7123a86775dcfe6c50301dd7cf9c6fc6ea968c1d2a01a0fccda5c1489dd3268fcf2471f6a372fc3afe5eebecc0ba9fc3c023d85da26aeda0d730ea7aabdad2e5451e826726e53c86e6e805e46220bc7edd80bf3f7e467f96a074f3d767a84557aa9559b08b2d1f93d8205827c042d1ac616b8cf37e22de2beca03ca1fe479ea1e64c3bfeae9c603ef4f55c270de0bb4c79d045f67d3481ca2852a0bdcbc3d154db40b5faa1f6875f46d485b96bdfffb4513da631f9b302768faae7a096923f4f559c7b7f912587292cc865aba5934e9e75c9aa88f20de73e928c6c51a0f52ca9710977327dd407ba9d9f00b0075d7e312ee19686f1b53579585ff50132a076adb4cf98f9af261d1b2a147b9b2cbace1647eab537ca1a55e8d40d35775b74a0723fa2f72d6a983939bc9bf9ee22e05fc142437726450e5707c60155bc34ec0580",
            "0xf90211a039a64fd9b31f3ea3bf9a991ca828eadb67cf9ae0ebbcdd195d297454699580e8a0c95d85a63beb9a02b56d032116d86fdf9a64065dd5d44e33acef674ae3ceb6f9a0d83ef07a99302abccdce1289d353a20494713f45d8edbd3c2e87f08788a878d9a063a19aee41fec40a98edcf4d60c759c509b512bfc5d9feae7de50c8c2d00eee7a029ce5c8c1ac9939cbf481274b8e6f5f24a430136dc5aab7deb489a9ce7db5a95a03b45c53d2e4f54e49a53eb298aee828f4803581ee60ca52940533b77c3e3fadfa082b8084dfc0337a49fd5d53ce107fa0bdcdf25ac7bbac017d7ac250b77c8764da0d4dab90004fd3bb36b2fa5f6e914a7c90820d119e5ba3ba8439270c6cfbd0a18a0bd9286c9248ae8a953d00aa9906f06b6f0364d0bb9fb615de04c9f8d5b5fd346a00955eccaa41a17fafb0ed66272b5183b3a30a973af7a43e0f25f0b640fd5df0ca0a7ba773602ace05991211770fc5555dd9c55e2518bcb005d1db022584a89132da0450b066588b44701992ab4a926d5dd185dd465abf1e51a3f60ab4a9f924cf85aa05eda0687636512339d0db99eade61c0d44358bb8027185296ad59b4cedd2935aa0cd18604dee296e5443b598e470a04efde04fbb0213c0fda8cc3af20dae2a1c34a01c0474c1bf15e4732d1c22a1303573c7ec8643f711354e853ab26038b2eebe25a081a0b2d329a9519375f71384a7130faafc3a1379db59bfb7db7135c9d27d9cc080",
            "0xf901f1a0e14d9caa85464966f0371f427250e7bf2d86f6d41535b08ea79044391d6f0fe7a08bea233c92e4c1d05bd03d62cd93974cc29f6df54e6fa2574bf8dfb3af936a85a0dd4f4af9ab72d1bbbf59c286a731614f48bf3cdcef572cd819426fc2a30ae5d9a058465ea8e97b2f3a873646f9504aff111ab967abaea8ebb2fe91bf058ea162f3a06466c41eb770bae5c07c26cd692540e7f9af70fee2bc164e12f29083ebd2cca1a0fe0925da033ffb967ca1e1d6505c2ca740553916c3b9a205131d719b7921fb00a0973ecee958d1305f1b6f8159a9732e47f5fbd4121f60254ea44a8f028308150780a0f81717c7f8702ed39d89a34fc86827aab31db26b22d22ee399667e4a44081c95a0e276ec24ee74ee61bdbe92e8afa57813d59f26e0ad34f24d71f0627719f8a11ea00a8ec2f6922480890f9e67c62c1654dcccf1710a01a378f614e02a3785d42d7ea08c43cc0c6c690dd87cf42691e9ead799c5ddbcfc9458ebd054a46acecedbe9f7a0d3727ed077c60ed6ff1d9d5d20fdf2912a513942d0efee9ec2d97d33ab9b7f56a03f293b0c0f25b9aa2735c4e42b132008d8af2ecfaaefdc940dc94cf312f5a1dda04860bc5f19829bb277554927c5b6dbc0af93025aca49aed3be020a3080996a26a0e47475bf4f62364d8a0dd87f2c21d0b64ef4d9aaf5fc46d9b1e3af5b83f56d4580",
            "0xf89180a0dd3524693059f48ce47c5dd82d0462953a5141c7abc5a63981637b71e0100bcf80a033ca98826ea65c1c4be16e1df12e78142d992bf1fc0189401632ceff3fcbbe7880a0e5daf803b0890d164e287fa43b51332286cddeb9b62280426037fc02dc2b4d5f8080a0bfa907c2e30720a07347d23cfa573dec8c9b3afa51a4a0e0783a481da0fcb1b38080808080808080",
            "0xe79e208246cec5810061f4ff7efe1dcd6cb407d59abc3478830df04484584c868786d647b234389e"
         ]
      }
   ]
}

About

Golang library and utility for extracting erc20 token balances from EVM storage proofs

Resources

License

Stars

Watchers

Forks

Packages

No packages published