From 4f9d42fa45f82738f708d8934b770ca799ca9c4b Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 31 Jan 2024 13:47:17 -0500 Subject: [PATCH 1/9] add plumbing to add errors to the contract --- example/abis/Example.json | 32 ++++++++++-- example/contracts/Example.sol | 7 ++- pypechain/render/contract.py | 14 +++++- .../templates/contract.py/base.py.jinja2 | 2 + .../templates/contract.py/contract.py.jinja2 | 7 +++ .../templates/contract.py/errors.py.jinja2 | 50 +++++++++++++++++++ pypechain/utilities/abi.py | 9 ++-- 7 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 pypechain/templates/contract.py/errors.py.jinja2 diff --git a/example/abis/Example.json b/example/abis/Example.json index cce14d07..23181ea0 100644 --- a/example/abis/Example.json +++ b/example/abis/Example.json @@ -29,6 +29,32 @@ "name": "WrongChoice", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "flip", + "type": "uint256" + } + ], + "name": "Flip", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "flop", + "type": "uint256" + } + ], + "name": "Flop", + "type": "event" + }, { "inputs": [ { @@ -55,7 +81,7 @@ "type": "uint256" } ], - "stateMutability": "pure", + "stateMutability": "nonpayable", "type": "function" }, { @@ -426,8 +452,8 @@ "type": "function" } ], - "bin": "608060405234801562000010575f80fd5b5060405162001535380380620015358339818101604052810190620000369190620001d3565b805f908162000046919062000459565b50506200053d565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620000af8262000067565b810181811067ffffffffffffffff82111715620000d157620000d062000077565b5b80604052505050565b5f620000e56200004e565b9050620000f38282620000a4565b919050565b5f67ffffffffffffffff82111562000115576200011462000077565b5b620001208262000067565b9050602081019050919050565b5f5b838110156200014c5780820151818401526020810190506200012f565b5f8484015250505050565b5f6200016d6200016784620000f8565b620000da565b9050828152602081018484840111156200018c576200018b62000063565b5b620001998482856200012d565b509392505050565b5f82601f830112620001b857620001b76200005f565b5b8151620001ca84826020860162000157565b91505092915050565b5f60208284031215620001eb57620001ea62000057565b5b5f82015167ffffffffffffffff8111156200020b576200020a6200005b565b5b6200021984828501620001a1565b91505092915050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200027157607f821691505b6020821081036200028757620002866200022c565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620002eb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002ae565b620002f78683620002ae565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620003416200033b62000335846200030f565b62000318565b6200030f565b9050919050565b5f819050919050565b6200035c8362000321565b620003746200036b8262000348565b848454620002ba565b825550505050565b5f90565b6200038a6200037c565b6200039781848462000351565b505050565b5b81811015620003be57620003b25f8262000380565b6001810190506200039d565b5050565b601f8211156200040d57620003d7816200028d565b620003e2846200029f565b81016020851015620003f2578190505b6200040a62000401856200029f565b8301826200039c565b50505b505050565b5f82821c905092915050565b5f6200042f5f198460080262000412565b1980831691505092915050565b5f6200044983836200041e565b9150826002028217905092915050565b620004648262000222565b67ffffffffffffffff81111562000480576200047f62000077565b5b6200048c825462000259565b62000499828285620003c2565b5f60209050601f831160018114620004cf575f8415620004ba578287015190505b620004c685826200043c565b86555062000535565b601f198416620004df866200028d565b5f5b828110156200050857848901518255600182019150602085019450602081019050620004e1565b8683101562000528578489015162000524601f8916826200041e565b8355505b6001600288020188555050505b505050505050565b610fea806200054b5f395ff3fe608060405234801561000f575f80fd5b5060043610610091575f3560e01c8063811d9aa311610064578063811d9aa31461011157806386a5a4c31461012f578063cf71512c1461015f578063d332644614610190578063e0f7c604146101c057610091565b806326da1c9e1461009557806340e27b10146100b157806353fd1043146100d057806373b10c0f146100ef575b5f80fd5b6100af60048036038101906100aa91906106df565b6101df565b005b6100b961025f565b6040516100c79291906107e6565b60405180910390f35b6100d8610317565b6040516100e692919061089c565b60405180910390f35b6100f76103e6565b604051610108959493929190610937565b60405180910390f35b6101196104fa565b604051610126919061099d565b60405180910390f35b610149600480360381019061014491906109df565b610552565b604051610156919061099d565b60405180910390f35b61017960048036038101906101749190610a50565b61056b565b604051610187929190610a8e565b60405180910390f35b6101aa60048036038101906101a59190610ad3565b61057a565b6040516101b79190610b1a565b60405180910390f35b6101c8610593565b6040516101d692919061089c565b60405180910390f35b5f600360018360028111156101f7576101f6610b3a565b5b6102019190610ba0565b61020b9190610c01565b60ff1660028111156102205761021f610b3a565b5b9050806040517fc13b30d40000000000000000000000000000000000000000000000000000000081526004016102569190610ce7565b60405180910390fd5b610267610658565b61026f610658565b5f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525090505f6040518060400160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d626572203200000000000000000000000000000000815250815250905081819350935050509091565b61031f610658565b610327610671565b5f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525090505f6040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d6265722032000000000000000000000000000000008152508152602001604051806020016040528060011515815250815250905081819350935050509091565b6103ee610658565b6103f6610671565b5f60605f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525094505f6040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d62657220320000000000000000000000000000000081525081526020016040518060200160405280600115158152508152509050858160015f6040518060400160405280601381526020017f52657475726e5479706573436f6e7472616374000000000000000000000000008152509095509550955095509550509091929394565b610502610658565b6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d626572203100000000000000000000000000000000815250815250905090565b61055a610658565b8161056490610eb0565b9050919050565b5f808284915091509250929050565b610582610671565b8161058c90610fa2565b9050919050565b61059b610658565b6105a3610671565b6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525091506040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d626572203200000000000000000000000000000000815250815260200160405180602001604052806001151581525081525090509091565b60405180604001604052805f8152602001606081525090565b60405180606001604052805f815260200160608152602001610691610697565b81525090565b60405180602001604052805f151581525090565b5f604051905090565b5f80fd5b5f80fd5b600381106106c8575f80fd5b50565b5f813590506106d9816106bc565b92915050565b5f602082840312156106f4576106f36106b4565b5b5f610701848285016106cb565b91505092915050565b5f819050919050565b61071c8161070a565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561075957808201518184015260208101905061073e565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61077e82610722565b610788818561072c565b935061079881856020860161073c565b6107a181610764565b840191505092915050565b5f604083015f8301516107c15f860182610713565b50602083015184820360208601526107d98282610774565b9150508091505092915050565b5f6040820190508181035f8301526107fe81856107ac565b9050818103602083015261081281846107ac565b90509392505050565b5f8115159050919050565b61082f8161081b565b82525050565b602082015f8201516108495f850182610826565b50505050565b5f606083015f8301516108645f860182610713565b506020830151848203602086015261087c8282610774565b91505060408301516108916040860182610835565b508091505092915050565b5f6040820190508181035f8301526108b481856107ac565b905081810360208301526108c8818461084f565b90509392505050565b6108da8161070a565b82525050565b5f82825260208201905092915050565b5f6108fa82610722565b61090481856108e0565b935061091481856020860161073c565b61091d81610764565b840191505092915050565b6109318161081b565b82525050565b5f60a0820190508181035f83015261094f81886107ac565b90508181036020830152610963818761084f565b905061097260408301866108d1565b818103606083015261098481856108f0565b90506109936080830184610928565b9695505050505050565b5f6020820190508181035f8301526109b581846107ac565b905092915050565b5f80fd5b5f604082840312156109d6576109d56109bd565b5b81905092915050565b5f602082840312156109f4576109f36106b4565b5b5f82013567ffffffffffffffff811115610a1157610a106106b8565b5b610a1d848285016109c1565b91505092915050565b610a2f8161070a565b8114610a39575f80fd5b50565b5f81359050610a4a81610a26565b92915050565b5f8060408385031215610a6657610a656106b4565b5b5f610a7385828601610a3c565b9250506020610a8485828601610a3c565b9150509250929050565b5f604082019050610aa15f8301856108d1565b610aae60208301846108d1565b9392505050565b5f60608284031215610aca57610ac96109bd565b5b81905092915050565b5f60208284031215610ae857610ae76106b4565b5b5f82013567ffffffffffffffff811115610b0557610b046106b8565b5b610b1184828501610ab5565b91505092915050565b5f6020820190508181035f830152610b32818461084f565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f60ff82169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610baa82610b67565b9150610bb583610b67565b9250828201905060ff811115610bce57610bcd610b73565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f610c0b82610b67565b9150610c1683610b67565b925082610c2657610c25610bd4565b5b828206905092915050565b60038110610c4257610c41610b3a565b5b50565b5f819050610c5282610c31565b919050565b5f610c6182610c45565b9050919050565b610c7181610c57565b82525050565b7f5468616e6b20796f7520666f7220706c6179696e672c2062757420796f7520635f8201527f686f7365207468652077726f6e67206c65747465720000000000000000000000602082015250565b5f610cd16035836108e0565b9150610cdc82610c77565b604082019050919050565b5f604082019050610cfa5f830184610c68565b8181036020830152610d0b81610cc5565b905092915050565b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610d4d82610764565b810181811067ffffffffffffffff82111715610d6c57610d6b610d17565b5b80604052505050565b5f610d7e6106ab565b9050610d8a8282610d44565b919050565b5f80fd5b5f80fd5b5f80fd5b5f67ffffffffffffffff821115610db557610db4610d17565b5b610dbe82610764565b9050602081019050919050565b828183375f83830152505050565b5f610deb610de684610d9b565b610d75565b905082815260208101848484011115610e0757610e06610d97565b5b610e12848285610dcb565b509392505050565b5f82601f830112610e2e57610e2d610d93565b5b8135610e3e848260208601610dd9565b91505092915050565b5f60408284031215610e5c57610e5b610d13565b5b610e666040610d75565b90505f610e7584828501610a3c565b5f83015250602082013567ffffffffffffffff811115610e9857610e97610d8f565b5b610ea484828501610e1a565b60208301525092915050565b5f610ebb3683610e47565b9050919050565b610ecb8161081b565b8114610ed5575f80fd5b50565b5f81359050610ee681610ec2565b92915050565b5f60208284031215610f0157610f00610d13565b5b610f0b6020610d75565b90505f610f1a84828501610ed8565b5f8301525092915050565b5f60608284031215610f3a57610f39610d13565b5b610f446060610d75565b90505f610f5384828501610a3c565b5f83015250602082013567ffffffffffffffff811115610f7657610f75610d8f565b5b610f8284828501610e1a565b6020830152506040610f9684828501610eec565b60408301525092915050565b5f610fad3683610f25565b905091905056fea264697066735822122062992a1f0d910c18b3e4158e6a13a45fecfa4e04259349bd3f28b56da17dcf2664736f6c63430008170033", - "metadata": "{\"compiler\":{\"version\":\"0.8.23+commit.f704f362\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"enum Example.Letters\",\"name\":\"answer\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"errorMessage\",\"type\":\"string\"}],\"name\":\"WrongChoice\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"flip\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"flop\",\"type\":\"uint256\"}],\"name\":\"flipFlop\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_flop\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_flip\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enum Example.Letters\",\"name\":\"guess\",\"type\":\"uint8\"}],\"name\":\"guessALetter\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"mixStructsAndPrimitives\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"simpleStruct\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"_name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"YesOrNo\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"namedSingleStruct\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"struct1\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"namedTwoMixedStructs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"simpleStruct\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"singleNestedStruct\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"simpleStruct\",\"type\":\"tuple\"}],\"name\":\"singleSimpleStruct\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"twoMixedStructs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"twoSimpleStructs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"example/contracts/Example.sol\":\"Example\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"example/contracts/Example.sol\":{\"keccak256\":\"0x28a76e7b5bd966214d423e43b99106a304cde0433438a79fb35735afa201b77f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://850c49423cfeca51fef36b8282dafbfe6edd4580466af46e4504eeffceae3bfb\",\"dweb:/ipfs/QmbHb92tHdpYy5oqZrjYnJdnvpqYxizMpNkVxhDRvVLhC6\"]}},\"version\":1}" + "bin": "608060405234801562000010575f80fd5b50604051620015bc380380620015bc8339818101604052810190620000369190620001d3565b805f908162000046919062000459565b50506200053d565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620000af8262000067565b810181811067ffffffffffffffff82111715620000d157620000d062000077565b5b80604052505050565b5f620000e56200004e565b9050620000f38282620000a4565b919050565b5f67ffffffffffffffff82111562000115576200011462000077565b5b620001208262000067565b9050602081019050919050565b5f5b838110156200014c5780820151818401526020810190506200012f565b5f8484015250505050565b5f6200016d6200016784620000f8565b620000da565b9050828152602081018484840111156200018c576200018b62000063565b5b620001998482856200012d565b509392505050565b5f82601f830112620001b857620001b76200005f565b5b8151620001ca84826020860162000157565b91505092915050565b5f60208284031215620001eb57620001ea62000057565b5b5f82015167ffffffffffffffff8111156200020b576200020a6200005b565b5b6200021984828501620001a1565b91505092915050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200027157607f821691505b6020821081036200028757620002866200022c565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620002eb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002ae565b620002f78683620002ae565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620003416200033b62000335846200030f565b62000318565b6200030f565b9050919050565b5f819050919050565b6200035c8362000321565b620003746200036b8262000348565b848454620002ba565b825550505050565b5f90565b6200038a6200037c565b6200039781848462000351565b505050565b5b81811015620003be57620003b25f8262000380565b6001810190506200039d565b5050565b601f8211156200040d57620003d7816200028d565b620003e2846200029f565b81016020851015620003f2578190505b6200040a62000401856200029f565b8301826200039c565b50505b505050565b5f82821c905092915050565b5f6200042f5f198460080262000412565b1980831691505092915050565b5f6200044983836200041e565b9150826002028217905092915050565b620004648262000222565b67ffffffffffffffff81111562000480576200047f62000077565b5b6200048c825462000259565b62000499828285620003c2565b5f60209050601f831160018114620004cf575f8415620004ba578287015190505b620004c685826200043c565b86555062000535565b601f198416620004df866200028d565b5f5b828110156200050857848901518255600182019150602085019450602081019050620004e1565b8683101562000528578489015162000524601f8916826200041e565b8355505b6001600288020188555050505b505050505050565b611071806200054b5f395ff3fe608060405234801561000f575f80fd5b5060043610610091575f3560e01c8063811d9aa311610064578063811d9aa31461011157806386a5a4c31461012f578063cf71512c1461015f578063d332644614610190578063e0f7c604146101c057610091565b806326da1c9e1461009557806340e27b10146100b157806353fd1043146100d057806373b10c0f146100ef575b5f80fd5b6100af60048036038101906100aa919061074d565b6101df565b005b6100b961025f565b6040516100c7929190610854565b60405180910390f35b6100d8610317565b6040516100e692919061090a565b60405180910390f35b6100f76103e6565b6040516101089594939291906109a5565b60405180910390f35b6101196104fa565b6040516101269190610a0b565b60405180910390f35b61014960048036038101906101449190610a4d565b610552565b6040516101569190610a0b565b60405180910390f35b61017960048036038101906101749190610abe565b61056b565b604051610187929190610afc565b60405180910390f35b6101aa60048036038101906101a59190610b41565b6105e8565b6040516101b79190610b88565b60405180910390f35b6101c8610601565b6040516101d692919061090a565b60405180910390f35b5f600360018360028111156101f7576101f6610ba8565b5b6102019190610c0e565b61020b9190610c6f565b60ff1660028111156102205761021f610ba8565b5b9050806040517fc13b30d40000000000000000000000000000000000000000000000000000000081526004016102569190610d55565b60405180910390fd5b6102676106c6565b61026f6106c6565b5f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525090505f6040518060400160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d626572203200000000000000000000000000000000815250815250905081819350935050509091565b61031f6106c6565b6103276106df565b5f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525090505f6040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d6265722032000000000000000000000000000000008152508152602001604051806020016040528060011515815250815250905081819350935050509091565b6103ee6106c6565b6103f66106df565b5f60605f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525094505f6040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d62657220320000000000000000000000000000000081525081526020016040518060200160405280600115158152508152509050858160015f6040518060400160405280601381526020017f52657475726e5479706573436f6e7472616374000000000000000000000000008152509095509550955095509550509091929394565b6105026106c6565b6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d626572203100000000000000000000000000000000815250815250905090565b61055a6106c6565b8161056490610f1e565b9050919050565b5f807ffc25ee6cef1e5b8c4e7a2f8d3bbb15ca76bb693b8b5644b3b481e1831c09530b8460405161059c9190610f30565b60405180910390a17ffa3d951cbf852d2a9cc2dfc9fc6b57914afbf57597ecf432c403ed2d74124b2c836040516105d39190610f30565b60405180910390a18284915091509250929050565b6105f06106df565b816105fa90611029565b9050919050565b6106096106c6565b6106116106df565b6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525091506040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d626572203200000000000000000000000000000000815250815260200160405180602001604052806001151581525081525090509091565b60405180604001604052805f8152602001606081525090565b60405180606001604052805f8152602001606081526020016106ff610705565b81525090565b60405180602001604052805f151581525090565b5f604051905090565b5f80fd5b5f80fd5b60038110610736575f80fd5b50565b5f813590506107478161072a565b92915050565b5f6020828403121561076257610761610722565b5b5f61076f84828501610739565b91505092915050565b5f819050919050565b61078a81610778565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156107c75780820151818401526020810190506107ac565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6107ec82610790565b6107f6818561079a565b93506108068185602086016107aa565b61080f816107d2565b840191505092915050565b5f604083015f83015161082f5f860182610781565b506020830151848203602086015261084782826107e2565b9150508091505092915050565b5f6040820190508181035f83015261086c818561081a565b90508181036020830152610880818461081a565b90509392505050565b5f8115159050919050565b61089d81610889565b82525050565b602082015f8201516108b75f850182610894565b50505050565b5f606083015f8301516108d25f860182610781565b50602083015184820360208601526108ea82826107e2565b91505060408301516108ff60408601826108a3565b508091505092915050565b5f6040820190508181035f830152610922818561081a565b9050818103602083015261093681846108bd565b90509392505050565b61094881610778565b82525050565b5f82825260208201905092915050565b5f61096882610790565b610972818561094e565b93506109828185602086016107aa565b61098b816107d2565b840191505092915050565b61099f81610889565b82525050565b5f60a0820190508181035f8301526109bd818861081a565b905081810360208301526109d181876108bd565b90506109e0604083018661093f565b81810360608301526109f2818561095e565b9050610a016080830184610996565b9695505050505050565b5f6020820190508181035f830152610a23818461081a565b905092915050565b5f80fd5b5f60408284031215610a4457610a43610a2b565b5b81905092915050565b5f60208284031215610a6257610a61610722565b5b5f82013567ffffffffffffffff811115610a7f57610a7e610726565b5b610a8b84828501610a2f565b91505092915050565b610a9d81610778565b8114610aa7575f80fd5b50565b5f81359050610ab881610a94565b92915050565b5f8060408385031215610ad457610ad3610722565b5b5f610ae185828601610aaa565b9250506020610af285828601610aaa565b9150509250929050565b5f604082019050610b0f5f83018561093f565b610b1c602083018461093f565b9392505050565b5f60608284031215610b3857610b37610a2b565b5b81905092915050565b5f60208284031215610b5657610b55610722565b5b5f82013567ffffffffffffffff811115610b7357610b72610726565b5b610b7f84828501610b23565b91505092915050565b5f6020820190508181035f830152610ba081846108bd565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f60ff82169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610c1882610bd5565b9150610c2383610bd5565b9250828201905060ff811115610c3c57610c3b610be1565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f610c7982610bd5565b9150610c8483610bd5565b925082610c9457610c93610c42565b5b828206905092915050565b60038110610cb057610caf610ba8565b5b50565b5f819050610cc082610c9f565b919050565b5f610ccf82610cb3565b9050919050565b610cdf81610cc5565b82525050565b7f5468616e6b20796f7520666f7220706c6179696e672c2062757420796f7520635f8201527f686f7365207468652077726f6e67206c65747465720000000000000000000000602082015250565b5f610d3f60358361094e565b9150610d4a82610ce5565b604082019050919050565b5f604082019050610d685f830184610cd6565b8181036020830152610d7981610d33565b905092915050565b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610dbb826107d2565b810181811067ffffffffffffffff82111715610dda57610dd9610d85565b5b80604052505050565b5f610dec610719565b9050610df88282610db2565b919050565b5f80fd5b5f80fd5b5f80fd5b5f67ffffffffffffffff821115610e2357610e22610d85565b5b610e2c826107d2565b9050602081019050919050565b828183375f83830152505050565b5f610e59610e5484610e09565b610de3565b905082815260208101848484011115610e7557610e74610e05565b5b610e80848285610e39565b509392505050565b5f82601f830112610e9c57610e9b610e01565b5b8135610eac848260208601610e47565b91505092915050565b5f60408284031215610eca57610ec9610d81565b5b610ed46040610de3565b90505f610ee384828501610aaa565b5f83015250602082013567ffffffffffffffff811115610f0657610f05610dfd565b5b610f1284828501610e88565b60208301525092915050565b5f610f293683610eb5565b9050919050565b5f602082019050610f435f83018461093f565b92915050565b610f5281610889565b8114610f5c575f80fd5b50565b5f81359050610f6d81610f49565b92915050565b5f60208284031215610f8857610f87610d81565b5b610f926020610de3565b90505f610fa184828501610f5f565b5f8301525092915050565b5f60608284031215610fc157610fc0610d81565b5b610fcb6060610de3565b90505f610fda84828501610aaa565b5f83015250602082013567ffffffffffffffff811115610ffd57610ffc610dfd565b5b61100984828501610e88565b602083015250604061101d84828501610f73565b60408301525092915050565b5f6110343683610fac565b905091905056fea264697066735822122072cb644782cb5404d6b2629e03389cd6529e6d20b9301e18b3bb2e96a9ba4f1364736f6c63430008170033", + "metadata": "{\"compiler\":{\"version\":\"0.8.23+commit.f704f362\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"enum Example.Letters\",\"name\":\"answer\",\"type\":\"uint8\"},{\"internalType\":\"string\",\"name\":\"errorMessage\",\"type\":\"string\"}],\"name\":\"WrongChoice\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"flip\",\"type\":\"uint256\"}],\"name\":\"Flip\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"flop\",\"type\":\"uint256\"}],\"name\":\"Flop\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"flip\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"flop\",\"type\":\"uint256\"}],\"name\":\"flipFlop\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"_flop\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_flip\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enum Example.Letters\",\"name\":\"guess\",\"type\":\"uint8\"}],\"name\":\"guessALetter\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"mixStructsAndPrimitives\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"simpleStruct\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"\",\"type\":\"tuple\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"_name\",\"type\":\"string\"},{\"internalType\":\"bool\",\"name\":\"YesOrNo\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"namedSingleStruct\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"struct1\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"namedTwoMixedStructs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"simpleStruct\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"singleNestedStruct\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"simpleStruct\",\"type\":\"tuple\"}],\"name\":\"singleSimpleStruct\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"twoMixedStructs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"bool\",\"name\":\"boolVal\",\"type\":\"bool\"}],\"internalType\":\"struct Example.InnerStruct\",\"name\":\"innerStruct\",\"type\":\"tuple\"}],\"internalType\":\"struct Example.NestedStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"twoSimpleStructs\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"intVal\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"strVal\",\"type\":\"string\"}],\"internalType\":\"struct Example.SimpleStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"example/contracts/Example.sol\":\"Example\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"example/contracts/Example.sol\":{\"keccak256\":\"0x338978c673dd29904bd9ef3f9b4761decbd39e25821da47c26b2175095cf0166\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://287018c5c351a74e55aead22b72943600338ea5410291c47c5a90f92abe24aef\",\"dweb:/ipfs/QmPfDBywaAoczvbS8p6gqjoZ29oBY44uffgSoQdTBvDX4u\"]}},\"version\":1}" } }, "version": "0.8.23+commit.f704f362.Darwin.appleclang" diff --git a/example/contracts/Example.sol b/example/contracts/Example.sol index 9d8de63a..b524b4bd 100644 --- a/example/contracts/Example.sol +++ b/example/contracts/Example.sol @@ -3,6 +3,9 @@ pragma solidity ^0.8.0; contract Example { + event Flip(uint flip); + event Flop(uint flop); + string contractName; enum Letters { A, B, C } @@ -34,7 +37,9 @@ contract Example { revert WrongChoice(answer, "Thank you for playing, but you chose the wrong letter"); } - function flipFlop(uint flip, uint flop) public pure returns (uint _flop, uint _flip) { + function flipFlop(uint flip, uint flop) public returns (uint _flop, uint _flip) { + emit Flip(flip); + emit Flop(flop); return (flop,flip); } diff --git a/pypechain/render/contract.py b/pypechain/render/contract.py index a3e237ac..1a76b1e7 100644 --- a/pypechain/render/contract.py +++ b/pypechain/render/contract.py @@ -206,6 +206,7 @@ def render_contract_file(contract_info: ContractInfo) -> str | None: function_datas, constructor_data = get_function_datas(contract_info.abi) event_datas = get_event_datas(contract_info.abi) + error_infos = contract_info.errors.values() has_bytecode = bool(contract_info.bytecode) has_events = bool(len(event_datas.values())) @@ -227,6 +228,12 @@ def render_contract_file(contract_info: ContractInfo) -> str | None: events=event_datas, ) + has_errors = bool(len(error_infos)) + errors_block = templates.errors_template.render( + contract_name=contract_info.contract_name, + errors=error_infos, + ) + abi_block = templates.abi_template.render( abi=contract_info.abi, bytecode=contract_info.bytecode, @@ -236,6 +243,7 @@ def render_contract_file(contract_info: ContractInfo) -> str | None: contract_block = templates.contract_template.render( has_bytecode=has_bytecode, has_events=has_events, + has_errors=has_errors, contract_name=contract_info.contract_name, constructor=constructor_data, functions=function_datas, @@ -254,9 +262,11 @@ def render_contract_file(contract_info: ContractInfo) -> str | None: has_overloading=has_overloading, has_multiple_return_values=has_multiple_return_values, has_bytecode=has_bytecode, - has_events=has_events, functions_block=functions_block, + has_events=has_events, events_block=events_block, + has_errors=has_errors, + errors_block=errors_block, abi_block=abi_block, contract_block=contract_block, # TODO: use this data to add a typed constructor @@ -320,6 +330,7 @@ class ContractTemplates(NamedTuple): base_template: Any functions_template: Any events_template: Any + errors_template: Any abi_template: Any contract_template: Any @@ -330,6 +341,7 @@ def get_templates_for_contract_file(env): base_template=env.get_template("contract.py/base.py.jinja2"), functions_template=env.get_template("contract.py/functions.py.jinja2"), events_template=env.get_template("contract.py/events.py.jinja2"), + errors_template=env.get_template("contract.py/errors.py.jinja2"), abi_template=env.get_template("contract.py/abi.py.jinja2"), contract_template=env.get_template("contract.py/contract.py.jinja2"), ) diff --git a/pypechain/templates/contract.py/base.py.jinja2 b/pypechain/templates/contract.py/base.py.jinja2 index f4cd5326..ead04cce 100644 --- a/pypechain/templates/contract.py/base.py.jinja2 +++ b/pypechain/templates/contract.py/base.py.jinja2 @@ -53,6 +53,8 @@ structs = { {% if has_events %}{{ events_block }}{% endif %} +{% if has_errors %}{{ errors_block }}{% endif %} + {{abi_block}} {{contract_block}} \ No newline at end of file diff --git a/pypechain/templates/contract.py/contract.py.jinja2 b/pypechain/templates/contract.py/contract.py.jinja2 index 2ac24f95..af92b64a 100644 --- a/pypechain/templates/contract.py/contract.py.jinja2 +++ b/pypechain/templates/contract.py/contract.py.jinja2 @@ -14,6 +14,9 @@ class {{contract_name}}Contract(Contract): {% if has_events -%} self.events = {{contract_name}}ContractEvents({{contract_name | lower}}_abi, self.w3, address) # type: ignore {%- endif %} + {% if has_errors -%} + self.errors = {{contract_name}}ContractErrors() + {%- endif %} except FallbackNotFound: print("Fallback function not found. Continuing...") @@ -22,6 +25,10 @@ class {{contract_name}}Contract(Contract): events: {{contract_name}}ContractEvents {%- endif %} + {% if has_errors -%} + errors: {{contract_name}}ContractErrors = {{contract_name}}ContractErrors() + {%- endif %} + functions: {{contract_name}}ContractFunctions {% set has_constructor_args = constructor.input_names_and_types|length > 0 %} diff --git a/pypechain/templates/contract.py/errors.py.jinja2 b/pypechain/templates/contract.py/errors.py.jinja2 new file mode 100644 index 00000000..9d31d80a --- /dev/null +++ b/pypechain/templates/contract.py/errors.py.jinja2 @@ -0,0 +1,50 @@ +{# loop over all errors and create types for each #} +{%- for error_info in errors -%} +class {{contract_name}}{{error_info.name}}ContractError: + """ContractError for {{error_info.name}}.""" + # @combomethod destroys return types, so we are redefining functions as both class and instance + # pylint: disable=function-redefined + + # 4 byte error selector + selector: str + # error signature, i.e. CustomError(uint256,bool) + signature: str + + {# TODO: remove pylint disable when we add a type-hint for argument_names #} + # pylint: disable=useless-parent-delegation + def __init__( + self: "{{contract_name}}{{error_info.name}}ContractError", + ) -> None: + self.selector = "{{error_info.selector}}" + self.signature = "{{error_info.signature}}" + + def decode_error_data( # type: ignore + self: "{{contract_name}}{{error_info.name}}ContractError", + data: HexBytes, + # TODO: get the return type here + ) -> str: + """Decodes error data returns from a smart contract.""" + # do the decoding + return "data goes here." + + @classmethod + def decode_error_data( # type: ignore + cls: Type["{{contract_name}}{{error_info.name}}ContractError"], + data: HexBytes, + ) -> str: + """Decodes error data returns from a smart contract.""" + return "data goes here." +{% endfor %} + +class {{contract_name}}ContractErrors: + """ContractErrors for the {{contract_name}} contract.""" +{% for error_info in errors %} + {{error_info.name}}: {{contract_name}}{{error_info.name}}ContractError +{% endfor %} + + def __init__( + self, + ) -> None: + {% for error_info in errors -%} + self.{{error_info.name}} = {{contract_name}}{{error_info.name}}ContractError() + {% endfor %} \ No newline at end of file diff --git a/pypechain/utilities/abi.py b/pypechain/utilities/abi.py index 5b3769ae..a6515177 100644 --- a/pypechain/utilities/abi.py +++ b/pypechain/utilities/abi.py @@ -7,7 +7,7 @@ from pathlib import Path from typing import Any, Literal, NamedTuple, Sequence, TypedDict, TypeGuard, cast -from eth_utils.abi import collapse_if_tuple, function_abi_to_4byte_selector +from eth_utils.abi import collapse_if_tuple, function_signature_to_4byte_selector from eth_utils.hexadecimal import encode_hex from web3.types import ABI, ABIElement, ABIEvent, ABIFunction, ABIFunctionComponents, ABIFunctionParams @@ -159,7 +159,7 @@ def is_abi_event(item: ABIElement | ABIError) -> TypeGuard[ABIEvent]: return True -def is_abi_error(item: ABIElement) -> TypeGuard[ABIError]: +def is_abi_error(item: ABIElement | ABIError) -> TypeGuard[ABIError]: """Typeguard function for ABIError. Parameters @@ -497,8 +497,9 @@ def get_errors_for_abi(abi: ABI) -> list[ErrorInfo]: inputs.append(error_input) - selector = encode_hex(function_abi_to_4byte_selector(dict(item))) - signature = _abi_to_signature(dict(item)) + signature = _abi_to_signature(dict(item)).replace(" ", "") + selector = encode_hex(function_signature_to_4byte_selector(signature)) + errors.append( ErrorInfo( name=item.get("name"), From 3f8ef63b9614ea354bc92e70e2a08849387216a6 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 31 Jan 2024 13:48:07 -0500 Subject: [PATCH 2/9] regen types --- example/types/ExampleContract.py | 257 ++++++++++++++++++++++++++++++- example/types/ExampleTypes.py | 21 +++ 2 files changed, 273 insertions(+), 5 deletions(-) diff --git a/example/types/ExampleContract.py b/example/types/ExampleContract.py index 6f7976fb..b6a2910e 100644 --- a/example/types/ExampleContract.py +++ b/example/types/ExampleContract.py @@ -22,16 +22,24 @@ from __future__ import annotations -from typing import Any, NamedTuple, Type, cast +from typing import Any, Iterable, NamedTuple, Sequence, Type, cast from eth_account.signers.local import LocalAccount from eth_typing import ChecksumAddress, HexStr from hexbytes import HexBytes from typing_extensions import Self from web3 import Web3 -from web3.contract.contract import Contract, ContractConstructor, ContractFunction, ContractFunctions +from web3._utils.filters import LogFilter +from web3.contract.contract import ( + Contract, + ContractConstructor, + ContractEvent, + ContractEvents, + ContractFunction, + ContractFunctions, +) from web3.exceptions import FallbackNotFound -from web3.types import ABI, BlockIdentifier, CallOverride, TxParams +from web3.types import ABI, BlockIdentifier, CallOverride, EventData, TxParams from .ExampleTypes import InnerStruct, NestedStruct, SimpleStruct from .utilities import dataclass_to_tuple, rename_returned_types @@ -417,6 +425,227 @@ def __init__( ) +class ExampleFlipContractEvent(ContractEvent): + """ContractEvent for Flip.""" + + # super() get_logs and create_filter methods are generic, while our version adds values & types + # pylint: disable=arguments-differ + + # @combomethod destroys return types, so we are redefining functions as both class and instance + # pylint: disable=function-redefined + + # pylint: disable=useless-parent-delegation + def __init__(self, *argument_names: tuple[str]) -> None: + super().__init__(*argument_names) + + def get_logs( # type: ignore + self: "ExampleFlipContractEvent", + argument_filters: dict[str, Any] | None = None, + fromBlock: BlockIdentifier | None = None, + toBlock: BlockIdentifier | None = None, + block_hash: HexBytes | None = None, + ) -> Iterable[EventData]: + return cast( + Iterable[EventData], + super().get_logs( + argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, block_hash=block_hash + ), + ) + + @classmethod + def get_logs( # type: ignore + cls: Type["ExampleFlipContractEvent"], + argument_filters: dict[str, Any] | None = None, + fromBlock: BlockIdentifier | None = None, + toBlock: BlockIdentifier | None = None, + block_hash: HexBytes | None = None, + ) -> Iterable[EventData]: + return cast( + Iterable[EventData], + super().get_logs( + argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, block_hash=block_hash + ), + ) + + def create_filter( # type: ignore + self: "ExampleFlipContractEvent", + *, # PEP 3102 + argument_filters: dict[str, Any] | None = None, + fromBlock: BlockIdentifier | None = None, + toBlock: BlockIdentifier = "latest", + address: ChecksumAddress | None = None, + topics: Sequence[Any] | None = None, + ) -> LogFilter: + return cast( + LogFilter, + super().create_filter( + argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics + ), + ) + + @classmethod + def create_filter( # type: ignore + cls: Type["ExampleFlipContractEvent"], + *, # PEP 3102 + argument_filters: dict[str, Any] | None = None, + fromBlock: BlockIdentifier | None = None, + toBlock: BlockIdentifier = "latest", + address: ChecksumAddress | None = None, + topics: Sequence[Any] | None = None, + ) -> LogFilter: + return cast( + LogFilter, + super().create_filter( + argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics + ), + ) + + +class ExampleFlopContractEvent(ContractEvent): + """ContractEvent for Flop.""" + + # super() get_logs and create_filter methods are generic, while our version adds values & types + # pylint: disable=arguments-differ + + # @combomethod destroys return types, so we are redefining functions as both class and instance + # pylint: disable=function-redefined + + # pylint: disable=useless-parent-delegation + def __init__(self, *argument_names: tuple[str]) -> None: + super().__init__(*argument_names) + + def get_logs( # type: ignore + self: "ExampleFlopContractEvent", + argument_filters: dict[str, Any] | None = None, + fromBlock: BlockIdentifier | None = None, + toBlock: BlockIdentifier | None = None, + block_hash: HexBytes | None = None, + ) -> Iterable[EventData]: + return cast( + Iterable[EventData], + super().get_logs( + argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, block_hash=block_hash + ), + ) + + @classmethod + def get_logs( # type: ignore + cls: Type["ExampleFlopContractEvent"], + argument_filters: dict[str, Any] | None = None, + fromBlock: BlockIdentifier | None = None, + toBlock: BlockIdentifier | None = None, + block_hash: HexBytes | None = None, + ) -> Iterable[EventData]: + return cast( + Iterable[EventData], + super().get_logs( + argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, block_hash=block_hash + ), + ) + + def create_filter( # type: ignore + self: "ExampleFlopContractEvent", + *, # PEP 3102 + argument_filters: dict[str, Any] | None = None, + fromBlock: BlockIdentifier | None = None, + toBlock: BlockIdentifier = "latest", + address: ChecksumAddress | None = None, + topics: Sequence[Any] | None = None, + ) -> LogFilter: + return cast( + LogFilter, + super().create_filter( + argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics + ), + ) + + @classmethod + def create_filter( # type: ignore + cls: Type["ExampleFlopContractEvent"], + *, # PEP 3102 + argument_filters: dict[str, Any] | None = None, + fromBlock: BlockIdentifier | None = None, + toBlock: BlockIdentifier = "latest", + address: ChecksumAddress | None = None, + topics: Sequence[Any] | None = None, + ) -> LogFilter: + return cast( + LogFilter, + super().create_filter( + argument_filters=argument_filters, fromBlock=fromBlock, toBlock=toBlock, address=address, topics=topics + ), + ) + + +class ExampleContractEvents(ContractEvents): + """ContractEvents for the Example contract.""" + + Flip: ExampleFlipContractEvent + + Flop: ExampleFlopContractEvent + + def __init__( + self, + abi: ABI, + w3: "Web3", + address: ChecksumAddress | None = None, + ) -> None: + super().__init__(abi, w3, address) + self.Flip = cast( + ExampleFlipContractEvent, + ExampleFlipContractEvent.factory("Flip", w3=w3, contract_abi=abi, address=address, event_name="Flip"), + ) + self.Flop = cast( + ExampleFlopContractEvent, + ExampleFlopContractEvent.factory("Flop", w3=w3, contract_abi=abi, address=address, event_name="Flop"), + ) + + +class ExampleWrongChoiceContractError: + """ContractError for WrongChoice.""" + + # @combomethod destroys return types, so we are redefining functions as both class and instance + # pylint: disable=function-redefined + + # 4 byte error selector + selector: str + # error signature, i.e. CustomError(uint256,bool) + signature: str + + # pylint: disable=useless-parent-delegation + def __init__( + self: "ExampleWrongChoiceContractError", + ) -> None: + self.selector = "0xc13b30d4" + self.signature = "WrongChoice(uint8,string)" + + def decode_error_data( # type: ignore + self: "ExampleWrongChoiceContractError", + data: HexBytes, + # TODO: get the return type here + ) -> str: + # do the decoding + return "data goes here." + + @classmethod + def decode_error_data( # type: ignore + cls: Type["ExampleWrongChoiceContractError"], + data: HexBytes, + ) -> str: + return "data goes here." + + +class ExampleContractErrors: + """ContractErrors for the Example contract.""" + + WrongChoice: ExampleWrongChoiceContractError + + def __init__( + self, + ) -> None: + self.WrongChoice = ExampleWrongChoiceContractError() + + example_abi: ABI = cast( ABI, [ @@ -433,6 +662,18 @@ def __init__( "name": "WrongChoice", "type": "error", }, + { + "anonymous": False, + "inputs": [{"indexed": False, "internalType": "uint256", "name": "flip", "type": "uint256"}], + "name": "Flip", + "type": "event", + }, + { + "anonymous": False, + "inputs": [{"indexed": False, "internalType": "uint256", "name": "flop", "type": "uint256"}], + "name": "Flop", + "type": "event", + }, { "inputs": [ {"internalType": "uint256", "name": "flip", "type": "uint256"}, @@ -443,7 +684,7 @@ def __init__( {"internalType": "uint256", "name": "_flop", "type": "uint256"}, {"internalType": "uint256", "name": "_flip", "type": "uint256"}, ], - "stateMutability": "pure", + "stateMutability": "nonpayable", "type": "function", }, { @@ -665,7 +906,7 @@ def __init__( ) # pylint: disable=line-too-long example_bytecode = HexStr( - "0x608060405234801562000010575f80fd5b5060405162001535380380620015358339818101604052810190620000369190620001d3565b805f908162000046919062000459565b50506200053d565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620000af8262000067565b810181811067ffffffffffffffff82111715620000d157620000d062000077565b5b80604052505050565b5f620000e56200004e565b9050620000f38282620000a4565b919050565b5f67ffffffffffffffff82111562000115576200011462000077565b5b620001208262000067565b9050602081019050919050565b5f5b838110156200014c5780820151818401526020810190506200012f565b5f8484015250505050565b5f6200016d6200016784620000f8565b620000da565b9050828152602081018484840111156200018c576200018b62000063565b5b620001998482856200012d565b509392505050565b5f82601f830112620001b857620001b76200005f565b5b8151620001ca84826020860162000157565b91505092915050565b5f60208284031215620001eb57620001ea62000057565b5b5f82015167ffffffffffffffff8111156200020b576200020a6200005b565b5b6200021984828501620001a1565b91505092915050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200027157607f821691505b6020821081036200028757620002866200022c565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620002eb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002ae565b620002f78683620002ae565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620003416200033b62000335846200030f565b62000318565b6200030f565b9050919050565b5f819050919050565b6200035c8362000321565b620003746200036b8262000348565b848454620002ba565b825550505050565b5f90565b6200038a6200037c565b6200039781848462000351565b505050565b5b81811015620003be57620003b25f8262000380565b6001810190506200039d565b5050565b601f8211156200040d57620003d7816200028d565b620003e2846200029f565b81016020851015620003f2578190505b6200040a62000401856200029f565b8301826200039c565b50505b505050565b5f82821c905092915050565b5f6200042f5f198460080262000412565b1980831691505092915050565b5f6200044983836200041e565b9150826002028217905092915050565b620004648262000222565b67ffffffffffffffff81111562000480576200047f62000077565b5b6200048c825462000259565b62000499828285620003c2565b5f60209050601f831160018114620004cf575f8415620004ba578287015190505b620004c685826200043c565b86555062000535565b601f198416620004df866200028d565b5f5b828110156200050857848901518255600182019150602085019450602081019050620004e1565b8683101562000528578489015162000524601f8916826200041e565b8355505b6001600288020188555050505b505050505050565b610fea806200054b5f395ff3fe608060405234801561000f575f80fd5b5060043610610091575f3560e01c8063811d9aa311610064578063811d9aa31461011157806386a5a4c31461012f578063cf71512c1461015f578063d332644614610190578063e0f7c604146101c057610091565b806326da1c9e1461009557806340e27b10146100b157806353fd1043146100d057806373b10c0f146100ef575b5f80fd5b6100af60048036038101906100aa91906106df565b6101df565b005b6100b961025f565b6040516100c79291906107e6565b60405180910390f35b6100d8610317565b6040516100e692919061089c565b60405180910390f35b6100f76103e6565b604051610108959493929190610937565b60405180910390f35b6101196104fa565b604051610126919061099d565b60405180910390f35b610149600480360381019061014491906109df565b610552565b604051610156919061099d565b60405180910390f35b61017960048036038101906101749190610a50565b61056b565b604051610187929190610a8e565b60405180910390f35b6101aa60048036038101906101a59190610ad3565b61057a565b6040516101b79190610b1a565b60405180910390f35b6101c8610593565b6040516101d692919061089c565b60405180910390f35b5f600360018360028111156101f7576101f6610b3a565b5b6102019190610ba0565b61020b9190610c01565b60ff1660028111156102205761021f610b3a565b5b9050806040517fc13b30d40000000000000000000000000000000000000000000000000000000081526004016102569190610ce7565b60405180910390fd5b610267610658565b61026f610658565b5f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525090505f6040518060400160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d626572203200000000000000000000000000000000815250815250905081819350935050509091565b61031f610658565b610327610671565b5f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525090505f6040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d6265722032000000000000000000000000000000008152508152602001604051806020016040528060011515815250815250905081819350935050509091565b6103ee610658565b6103f6610671565b5f60605f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525094505f6040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d62657220320000000000000000000000000000000081525081526020016040518060200160405280600115158152508152509050858160015f6040518060400160405280601381526020017f52657475726e5479706573436f6e7472616374000000000000000000000000008152509095509550955095509550509091929394565b610502610658565b6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d626572203100000000000000000000000000000000815250815250905090565b61055a610658565b8161056490610eb0565b9050919050565b5f808284915091509250929050565b610582610671565b8161058c90610fa2565b9050919050565b61059b610658565b6105a3610671565b6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525091506040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d626572203200000000000000000000000000000000815250815260200160405180602001604052806001151581525081525090509091565b60405180604001604052805f8152602001606081525090565b60405180606001604052805f815260200160608152602001610691610697565b81525090565b60405180602001604052805f151581525090565b5f604051905090565b5f80fd5b5f80fd5b600381106106c8575f80fd5b50565b5f813590506106d9816106bc565b92915050565b5f602082840312156106f4576106f36106b4565b5b5f610701848285016106cb565b91505092915050565b5f819050919050565b61071c8161070a565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561075957808201518184015260208101905061073e565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61077e82610722565b610788818561072c565b935061079881856020860161073c565b6107a181610764565b840191505092915050565b5f604083015f8301516107c15f860182610713565b50602083015184820360208601526107d98282610774565b9150508091505092915050565b5f6040820190508181035f8301526107fe81856107ac565b9050818103602083015261081281846107ac565b90509392505050565b5f8115159050919050565b61082f8161081b565b82525050565b602082015f8201516108495f850182610826565b50505050565b5f606083015f8301516108645f860182610713565b506020830151848203602086015261087c8282610774565b91505060408301516108916040860182610835565b508091505092915050565b5f6040820190508181035f8301526108b481856107ac565b905081810360208301526108c8818461084f565b90509392505050565b6108da8161070a565b82525050565b5f82825260208201905092915050565b5f6108fa82610722565b61090481856108e0565b935061091481856020860161073c565b61091d81610764565b840191505092915050565b6109318161081b565b82525050565b5f60a0820190508181035f83015261094f81886107ac565b90508181036020830152610963818761084f565b905061097260408301866108d1565b818103606083015261098481856108f0565b90506109936080830184610928565b9695505050505050565b5f6020820190508181035f8301526109b581846107ac565b905092915050565b5f80fd5b5f604082840312156109d6576109d56109bd565b5b81905092915050565b5f602082840312156109f4576109f36106b4565b5b5f82013567ffffffffffffffff811115610a1157610a106106b8565b5b610a1d848285016109c1565b91505092915050565b610a2f8161070a565b8114610a39575f80fd5b50565b5f81359050610a4a81610a26565b92915050565b5f8060408385031215610a6657610a656106b4565b5b5f610a7385828601610a3c565b9250506020610a8485828601610a3c565b9150509250929050565b5f604082019050610aa15f8301856108d1565b610aae60208301846108d1565b9392505050565b5f60608284031215610aca57610ac96109bd565b5b81905092915050565b5f60208284031215610ae857610ae76106b4565b5b5f82013567ffffffffffffffff811115610b0557610b046106b8565b5b610b1184828501610ab5565b91505092915050565b5f6020820190508181035f830152610b32818461084f565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f60ff82169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610baa82610b67565b9150610bb583610b67565b9250828201905060ff811115610bce57610bcd610b73565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f610c0b82610b67565b9150610c1683610b67565b925082610c2657610c25610bd4565b5b828206905092915050565b60038110610c4257610c41610b3a565b5b50565b5f819050610c5282610c31565b919050565b5f610c6182610c45565b9050919050565b610c7181610c57565b82525050565b7f5468616e6b20796f7520666f7220706c6179696e672c2062757420796f7520635f8201527f686f7365207468652077726f6e67206c65747465720000000000000000000000602082015250565b5f610cd16035836108e0565b9150610cdc82610c77565b604082019050919050565b5f604082019050610cfa5f830184610c68565b8181036020830152610d0b81610cc5565b905092915050565b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610d4d82610764565b810181811067ffffffffffffffff82111715610d6c57610d6b610d17565b5b80604052505050565b5f610d7e6106ab565b9050610d8a8282610d44565b919050565b5f80fd5b5f80fd5b5f80fd5b5f67ffffffffffffffff821115610db557610db4610d17565b5b610dbe82610764565b9050602081019050919050565b828183375f83830152505050565b5f610deb610de684610d9b565b610d75565b905082815260208101848484011115610e0757610e06610d97565b5b610e12848285610dcb565b509392505050565b5f82601f830112610e2e57610e2d610d93565b5b8135610e3e848260208601610dd9565b91505092915050565b5f60408284031215610e5c57610e5b610d13565b5b610e666040610d75565b90505f610e7584828501610a3c565b5f83015250602082013567ffffffffffffffff811115610e9857610e97610d8f565b5b610ea484828501610e1a565b60208301525092915050565b5f610ebb3683610e47565b9050919050565b610ecb8161081b565b8114610ed5575f80fd5b50565b5f81359050610ee681610ec2565b92915050565b5f60208284031215610f0157610f00610d13565b5b610f0b6020610d75565b90505f610f1a84828501610ed8565b5f8301525092915050565b5f60608284031215610f3a57610f39610d13565b5b610f446060610d75565b90505f610f5384828501610a3c565b5f83015250602082013567ffffffffffffffff811115610f7657610f75610d8f565b5b610f8284828501610e1a565b6020830152506040610f9684828501610eec565b60408301525092915050565b5f610fad3683610f25565b905091905056fea264697066735822122062992a1f0d910c18b3e4158e6a13a45fecfa4e04259349bd3f28b56da17dcf2664736f6c63430008170033" + "0x608060405234801562000010575f80fd5b50604051620015bc380380620015bc8339818101604052810190620000369190620001d3565b805f908162000046919062000459565b50506200053d565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620000af8262000067565b810181811067ffffffffffffffff82111715620000d157620000d062000077565b5b80604052505050565b5f620000e56200004e565b9050620000f38282620000a4565b919050565b5f67ffffffffffffffff82111562000115576200011462000077565b5b620001208262000067565b9050602081019050919050565b5f5b838110156200014c5780820151818401526020810190506200012f565b5f8484015250505050565b5f6200016d6200016784620000f8565b620000da565b9050828152602081018484840111156200018c576200018b62000063565b5b620001998482856200012d565b509392505050565b5f82601f830112620001b857620001b76200005f565b5b8151620001ca84826020860162000157565b91505092915050565b5f60208284031215620001eb57620001ea62000057565b5b5f82015167ffffffffffffffff8111156200020b576200020a6200005b565b5b6200021984828501620001a1565b91505092915050565b5f81519050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b5f60028204905060018216806200027157607f821691505b6020821081036200028757620002866200022c565b5b50919050565b5f819050815f5260205f209050919050565b5f6020601f8301049050919050565b5f82821b905092915050565b5f60088302620002eb7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002ae565b620002f78683620002ae565b95508019841693508086168417925050509392505050565b5f819050919050565b5f819050919050565b5f620003416200033b62000335846200030f565b62000318565b6200030f565b9050919050565b5f819050919050565b6200035c8362000321565b620003746200036b8262000348565b848454620002ba565b825550505050565b5f90565b6200038a6200037c565b6200039781848462000351565b505050565b5b81811015620003be57620003b25f8262000380565b6001810190506200039d565b5050565b601f8211156200040d57620003d7816200028d565b620003e2846200029f565b81016020851015620003f2578190505b6200040a62000401856200029f565b8301826200039c565b50505b505050565b5f82821c905092915050565b5f6200042f5f198460080262000412565b1980831691505092915050565b5f6200044983836200041e565b9150826002028217905092915050565b620004648262000222565b67ffffffffffffffff81111562000480576200047f62000077565b5b6200048c825462000259565b62000499828285620003c2565b5f60209050601f831160018114620004cf575f8415620004ba578287015190505b620004c685826200043c565b86555062000535565b601f198416620004df866200028d565b5f5b828110156200050857848901518255600182019150602085019450602081019050620004e1565b8683101562000528578489015162000524601f8916826200041e565b8355505b6001600288020188555050505b505050505050565b611071806200054b5f395ff3fe608060405234801561000f575f80fd5b5060043610610091575f3560e01c8063811d9aa311610064578063811d9aa31461011157806386a5a4c31461012f578063cf71512c1461015f578063d332644614610190578063e0f7c604146101c057610091565b806326da1c9e1461009557806340e27b10146100b157806353fd1043146100d057806373b10c0f146100ef575b5f80fd5b6100af60048036038101906100aa919061074d565b6101df565b005b6100b961025f565b6040516100c7929190610854565b60405180910390f35b6100d8610317565b6040516100e692919061090a565b60405180910390f35b6100f76103e6565b6040516101089594939291906109a5565b60405180910390f35b6101196104fa565b6040516101269190610a0b565b60405180910390f35b61014960048036038101906101449190610a4d565b610552565b6040516101569190610a0b565b60405180910390f35b61017960048036038101906101749190610abe565b61056b565b604051610187929190610afc565b60405180910390f35b6101aa60048036038101906101a59190610b41565b6105e8565b6040516101b79190610b88565b60405180910390f35b6101c8610601565b6040516101d692919061090a565b60405180910390f35b5f600360018360028111156101f7576101f6610ba8565b5b6102019190610c0e565b61020b9190610c6f565b60ff1660028111156102205761021f610ba8565b5b9050806040517fc13b30d40000000000000000000000000000000000000000000000000000000081526004016102569190610d55565b60405180910390fd5b6102676106c6565b61026f6106c6565b5f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525090505f6040518060400160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d626572203200000000000000000000000000000000815250815250905081819350935050509091565b61031f6106c6565b6103276106df565b5f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525090505f6040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d6265722032000000000000000000000000000000008152508152602001604051806020016040528060011515815250815250905081819350935050509091565b6103ee6106c6565b6103f66106df565b5f60605f6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525094505f6040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d62657220320000000000000000000000000000000081525081526020016040518060200160405280600115158152508152509050858160015f6040518060400160405280601381526020017f52657475726e5479706573436f6e7472616374000000000000000000000000008152509095509550955095509550509091929394565b6105026106c6565b6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d626572203100000000000000000000000000000000815250815250905090565b61055a6106c6565b8161056490610f1e565b9050919050565b5f807ffc25ee6cef1e5b8c4e7a2f8d3bbb15ca76bb693b8b5644b3b481e1831c09530b8460405161059c9190610f30565b60405180910390a17ffa3d951cbf852d2a9cc2dfc9fc6b57914afbf57597ecf432c403ed2d74124b2c836040516105d39190610f30565b60405180910390a18284915091509250929050565b6105f06106df565b816105fa90611029565b9050919050565b6106096106c6565b6106116106df565b6040518060400160405280600181526020016040518060400160405280601081526020017f596f7520617265206e756d62657220310000000000000000000000000000000081525081525091506040518060600160405280600281526020016040518060400160405280601081526020017f596f7520617265206e756d626572203200000000000000000000000000000000815250815260200160405180602001604052806001151581525081525090509091565b60405180604001604052805f8152602001606081525090565b60405180606001604052805f8152602001606081526020016106ff610705565b81525090565b60405180602001604052805f151581525090565b5f604051905090565b5f80fd5b5f80fd5b60038110610736575f80fd5b50565b5f813590506107478161072a565b92915050565b5f6020828403121561076257610761610722565b5b5f61076f84828501610739565b91505092915050565b5f819050919050565b61078a81610778565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156107c75780820151818401526020810190506107ac565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6107ec82610790565b6107f6818561079a565b93506108068185602086016107aa565b61080f816107d2565b840191505092915050565b5f604083015f83015161082f5f860182610781565b506020830151848203602086015261084782826107e2565b9150508091505092915050565b5f6040820190508181035f83015261086c818561081a565b90508181036020830152610880818461081a565b90509392505050565b5f8115159050919050565b61089d81610889565b82525050565b602082015f8201516108b75f850182610894565b50505050565b5f606083015f8301516108d25f860182610781565b50602083015184820360208601526108ea82826107e2565b91505060408301516108ff60408601826108a3565b508091505092915050565b5f6040820190508181035f830152610922818561081a565b9050818103602083015261093681846108bd565b90509392505050565b61094881610778565b82525050565b5f82825260208201905092915050565b5f61096882610790565b610972818561094e565b93506109828185602086016107aa565b61098b816107d2565b840191505092915050565b61099f81610889565b82525050565b5f60a0820190508181035f8301526109bd818861081a565b905081810360208301526109d181876108bd565b90506109e0604083018661093f565b81810360608301526109f2818561095e565b9050610a016080830184610996565b9695505050505050565b5f6020820190508181035f830152610a23818461081a565b905092915050565b5f80fd5b5f60408284031215610a4457610a43610a2b565b5b81905092915050565b5f60208284031215610a6257610a61610722565b5b5f82013567ffffffffffffffff811115610a7f57610a7e610726565b5b610a8b84828501610a2f565b91505092915050565b610a9d81610778565b8114610aa7575f80fd5b50565b5f81359050610ab881610a94565b92915050565b5f8060408385031215610ad457610ad3610722565b5b5f610ae185828601610aaa565b9250506020610af285828601610aaa565b9150509250929050565b5f604082019050610b0f5f83018561093f565b610b1c602083018461093f565b9392505050565b5f60608284031215610b3857610b37610a2b565b5b81905092915050565b5f60208284031215610b5657610b55610722565b5b5f82013567ffffffffffffffff811115610b7357610b72610726565b5b610b7f84828501610b23565b91505092915050565b5f6020820190508181035f830152610ba081846108bd565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f60ff82169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f610c1882610bd5565b9150610c2383610bd5565b9250828201905060ff811115610c3c57610c3b610be1565b5b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f610c7982610bd5565b9150610c8483610bd5565b925082610c9457610c93610c42565b5b828206905092915050565b60038110610cb057610caf610ba8565b5b50565b5f819050610cc082610c9f565b919050565b5f610ccf82610cb3565b9050919050565b610cdf81610cc5565b82525050565b7f5468616e6b20796f7520666f7220706c6179696e672c2062757420796f7520635f8201527f686f7365207468652077726f6e67206c65747465720000000000000000000000602082015250565b5f610d3f60358361094e565b9150610d4a82610ce5565b604082019050919050565b5f604082019050610d685f830184610cd6565b8181036020830152610d7981610d33565b905092915050565b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610dbb826107d2565b810181811067ffffffffffffffff82111715610dda57610dd9610d85565b5b80604052505050565b5f610dec610719565b9050610df88282610db2565b919050565b5f80fd5b5f80fd5b5f80fd5b5f67ffffffffffffffff821115610e2357610e22610d85565b5b610e2c826107d2565b9050602081019050919050565b828183375f83830152505050565b5f610e59610e5484610e09565b610de3565b905082815260208101848484011115610e7557610e74610e05565b5b610e80848285610e39565b509392505050565b5f82601f830112610e9c57610e9b610e01565b5b8135610eac848260208601610e47565b91505092915050565b5f60408284031215610eca57610ec9610d81565b5b610ed46040610de3565b90505f610ee384828501610aaa565b5f83015250602082013567ffffffffffffffff811115610f0657610f05610dfd565b5b610f1284828501610e88565b60208301525092915050565b5f610f293683610eb5565b9050919050565b5f602082019050610f435f83018461093f565b92915050565b610f5281610889565b8114610f5c575f80fd5b50565b5f81359050610f6d81610f49565b92915050565b5f60208284031215610f8857610f87610d81565b5b610f926020610de3565b90505f610fa184828501610f5f565b5f8301525092915050565b5f60608284031215610fc157610fc0610d81565b5b610fcb6060610de3565b90505f610fda84828501610aaa565b5f83015250602082013567ffffffffffffffff811115610ffd57610ffc610dfd565b5b61100984828501610e88565b602083015250604061101d84828501610f73565b60408301525092915050565b5f6110343683610fac565b905091905056fea264697066735822122072cb644782cb5404d6b2629e03389cd6529e6d20b9301e18b3bb2e96a9ba4f1364736f6c63430008170033" ) @@ -680,10 +921,16 @@ def __init__(self, address: ChecksumAddress | None = None) -> None: # Initialize parent Contract class super().__init__(address=address) self.functions = ExampleContractFunctions(example_abi, self.w3, address) # type: ignore + self.events = ExampleContractEvents(example_abi, self.w3, address) # type: ignore + self.errors = ExampleContractErrors() except FallbackNotFound: print("Fallback function not found. Continuing...") + events: ExampleContractEvents + + errors: ExampleContractErrors + functions: ExampleContractFunctions class ConstructorArgs(NamedTuple): diff --git a/example/types/ExampleTypes.py b/example/types/ExampleTypes.py index ac4cc07c..052fe2fe 100644 --- a/example/types/ExampleTypes.py +++ b/example/types/ExampleTypes.py @@ -20,6 +20,8 @@ from dataclasses import dataclass +from web3.types import ABIEvent, ABIEventParams + @dataclass class SimpleStruct: @@ -45,6 +47,25 @@ class NestedStruct: innerStruct: InnerStruct +Flip = ABIEvent( + anonymous=False, + inputs=[ + ABIEventParams(indexed=False, name="flip", type="uint256"), + ], + name="Flip", + type="event", +) + +Flop = ABIEvent( + anonymous=False, + inputs=[ + ABIEventParams(indexed=False, name="flop", type="uint256"), + ], + name="Flop", + type="event", +) + + @dataclass class ErrorInfo: """Custom contract error information.""" From c1f7245de8e6086d5f51809896bbfb8deb050645 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 31 Jan 2024 18:11:04 -0500 Subject: [PATCH 3/9] add tests --- pypechain/test/errors/README.md | 11 + pypechain/test/errors/__init__.py | 0 pypechain/test/errors/abis/Errors.json | 101 +++++ pypechain/test/errors/contracts/Errors.sol | 30 ++ pypechain/test/errors/test_errors.py | 80 ++++ pypechain/test/errors/types/ErrorsContract.py | 419 ++++++++++++++++++ pypechain/test/errors/types/__init__.py | 10 + pypechain/test/errors/types/utilities.py | 110 +++++ 8 files changed, 761 insertions(+) create mode 100644 pypechain/test/errors/README.md create mode 100644 pypechain/test/errors/__init__.py create mode 100644 pypechain/test/errors/abis/Errors.json create mode 100644 pypechain/test/errors/contracts/Errors.sol create mode 100644 pypechain/test/errors/test_errors.py create mode 100644 pypechain/test/errors/types/ErrorsContract.py create mode 100644 pypechain/test/errors/types/__init__.py create mode 100644 pypechain/test/errors/types/utilities.py diff --git a/pypechain/test/errors/README.md b/pypechain/test/errors/README.md new file mode 100644 index 00000000..00026183 --- /dev/null +++ b/pypechain/test/errors/README.md @@ -0,0 +1,11 @@ +## Generate ABIs + +Install [`solc`](https://docs.soliditylang.org/en/latest/installing-solidity.html). + +If pytest fails to run while editing tests, you'll need to recompile the contracts manually. From +this directory run: + +```bash +rm abis/Errors.json +solc contracts/Errors.sol --combined-json abi,bin,metadata >> abis/Errors.json +``` diff --git a/pypechain/test/errors/__init__.py b/pypechain/test/errors/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pypechain/test/errors/abis/Errors.json b/pypechain/test/errors/abis/Errors.json new file mode 100644 index 00000000..843e83d6 --- /dev/null +++ b/pypechain/test/errors/abis/Errors.json @@ -0,0 +1,101 @@ +{ + "contracts": { + "pypechain/test/errors/contracts/Errors.sol:Errors": { + "abi": [ + { + "inputs": [], + "name": "One", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "trueOrFalse", + "type": "bool" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "bart", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lisa", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "homer", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "marge", + "type": "uint256" + } + ], + "internalType": "struct Errors.Ages", + "name": "theSimpsons", + "type": "tuple" + }, + { + "internalType": "enum Errors.Simpsons", + "name": "who", + "type": "uint8" + } + ], + "name": "Three", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "message", + "type": "string" + }, + { + "internalType": "address", + "name": "who", + "type": "address" + }, + { + "internalType": "uint8", + "name": "value", + "type": "uint8" + } + ], + "name": "Two", + "type": "error" + }, + { + "inputs": [], + "name": "revertWithErrorOne", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "revertWithErrorThree", + "outputs": [], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "revertWithErrorTwo", + "outputs": [], + "stateMutability": "pure", + "type": "function" + } + ], + "bin": "608060405234801561000f575f80fd5b506104328061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c806349cbdbf514610043578063a13e7b711461004d578063dc785aeb14610057575b5f80fd5b61004b610061565b005b6100556100ca565b005b61005f6100fc565b005b5f6040518060800160405280600181526020016002815260200160038152602001600481525090505f815f6040517f09b8b9890000000000000000000000000000000000000000000000000000000081526004016100c193929190610234565b60405180910390fd5b6040517fbe0c211000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60ff6040517f01e3e2f60000000000000000000000000000000000000000000000000000000081526004016101339291906103c2565b60405180910390fd5b5f8115159050919050565b6101508161013c565b82525050565b5f819050919050565b61016881610156565b82525050565b608082015f8201516101825f85018261015f565b506020820151610195602085018261015f565b5060408201516101a8604085018261015f565b5060608201516101bb606085018261015f565b50505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600481106101ff576101fe6101c1565b5b50565b5f81905061020f826101ee565b919050565b5f61021e82610202565b9050919050565b61022e81610214565b82525050565b5f60c0820190506102475f830186610147565b610254602083018561016e565b61026160a0830184610225565b949350505050565b5f82825260208201905092915050565b7f492077696c6c206e6f7420706c6564676520616c6c656769616e636520746f205f8201527f426172742e20492077696c6c206e6f7420706c6564676520616c6c656769616e60208201527f636520746f20426172742e20492077696c6c206e6f7420706c6564676520616c60408201527f6c656769616e636520746f20426172742e000000000000000000000000000000606082015250565b5f61031f607183610269565b915061032a82610279565b608082019050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61035e82610335565b9050919050565b61036e81610354565b82525050565b5f819050919050565b5f60ff82169050919050565b5f819050919050565b5f6103ac6103a76103a284610374565b610389565b61037d565b9050919050565b6103bc81610392565b82525050565b5f6060820190508181035f8301526103d981610313565b90506103e86020830185610365565b6103f560408301846103b3565b939250505056fea2646970667358221220d2ccb335b1907f8c97ee5b43958fd13ff523a48524e48710094939c1728f158b64736f6c63430008170033", + "metadata": "{\"compiler\":{\"version\":\"0.8.23+commit.f704f362\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"One\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bool\",\"name\":\"trueOrFalse\",\"type\":\"bool\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"bart\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lisa\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"homer\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"marge\",\"type\":\"uint256\"}],\"internalType\":\"struct Errors.Ages\",\"name\":\"theSimpsons\",\"type\":\"tuple\"},{\"internalType\":\"enum Errors.Simpsons\",\"name\":\"who\",\"type\":\"uint8\"}],\"name\":\"Three\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"who\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"value\",\"type\":\"uint8\"}],\"name\":\"Two\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"revertWithErrorOne\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revertWithErrorThree\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revertWithErrorTwo\",\"outputs\":[],\"stateMutability\":\"pure\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"pypechain/test/errors/contracts/Errors.sol\":\"Errors\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"pypechain/test/errors/contracts/Errors.sol\":{\"keccak256\":\"0x577eaff061dd738d75c5280b9531655f1e99924f76ac490784a4006b25cc30b4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2b39a1bdaffc240ce98baf5c4dc4fd5648e11959727d63e0f986db3b44508355\",\"dweb:/ipfs/QmXLqkC5db1zwf7zmwYQyRAWJgRyV69LDcsQC2cQjLF9fJ\"]}},\"version\":1}" + } + }, + "version": "0.8.23+commit.f704f362.Darwin.appleclang" +} \ No newline at end of file diff --git a/pypechain/test/errors/contracts/Errors.sol b/pypechain/test/errors/contracts/Errors.sol new file mode 100644 index 00000000..93debf54 --- /dev/null +++ b/pypechain/test/errors/contracts/Errors.sol @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +contract Errors { + struct Ages { + uint256 bart; + uint256 lisa; + uint256 homer; + uint256 marge; + } + + enum Simpsons { BART, LISA, HOMER, MARGE } + + error One(); + error Two(string message, address who, uint8 value); + error Three(bool trueOrFalse, Ages theSimpsons, Simpsons who); + + function revertWithErrorOne() public pure { + revert One(); + } + + function revertWithErrorTwo() public pure { + revert Two("I will not pledge allegiance to Bart. I will not pledge allegiance to Bart. I will not pledge allegiance to Bart.", address(0), 255); + } + + function revertWithErrorThree() public pure { + Ages memory ages = Ages(1, 2, 3, 4); + revert Three(false, ages, Simpsons.BART); + } +} diff --git a/pypechain/test/errors/test_errors.py b/pypechain/test/errors/test_errors.py new file mode 100644 index 00000000..7626088b --- /dev/null +++ b/pypechain/test/errors/test_errors.py @@ -0,0 +1,80 @@ +"""Tests for overloading methods.""" + +from __future__ import annotations + +import os + +import pytest +from eth_abi.codec import ABICodec +from eth_abi.registry import registry as default_registry +from hexbytes import HexBytes +from web3.exceptions import ContractCustomError + +from .types import ErrorsContract + +current_path = os.path.abspath(os.path.dirname(__file__)) +project_root = os.path.dirname(os.path.dirname(current_path)) + + +@pytest.mark.usefixtures("process_contracts") +class TestErrors: + """Tests events emitted from the contracts.""" + + def test_error_one(self, w3): + """Test that we can decode just the selector.""" + deployed_contract = ErrorsContract.deploy(w3=w3, account=w3.eth.accounts[0]) + try: + deployed_contract.functions.revertWithErrorOne().transact() + except ContractCustomError as err: + assert err.message + + selector = err.message[:10] + assert selector == ErrorsContract.errors.One.selector + + data = err.message[10:] + assert data == "" + + types = () + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + assert not decoded + + def test_error_two(self, w3): + """Test that we can decode strings.""" + deployed_contract = ErrorsContract.deploy(w3=w3, account=w3.eth.accounts[0]) + try: + deployed_contract.functions.revertWithErrorTwo().transact() + except ContractCustomError as err: + assert err.message + + selector = err.message[:10] + assert selector == ErrorsContract.errors.Two.selector + + data = err.message[10:] + + types = ("string", "address", "uint8") + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + assert decoded == ( + "I will not pledge allegiance to Bart. I will not pledge allegiance to Bart. I will not pledge allegiance to Bart.", + "0x0000000000000000000000000000000000000000", + 255, + ) + + def test_error_three(self, w3): + """Test that we can decode structs and enums.""" + deployed_contract = ErrorsContract.deploy(w3=w3, account=w3.eth.accounts[0]) + try: + deployed_contract.functions.revertWithErrorThree().transact() + except ContractCustomError as err: + assert err.message + + selector = err.message[:10] + assert selector == ErrorsContract.errors.Three.selector + + data = err.message[10:] + + types = ("bool", "(uint256,uint256,uint256,uint256)", "uint8") + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + assert decoded == (False, (1, 2, 3, 4), 0) diff --git a/pypechain/test/errors/types/ErrorsContract.py b/pypechain/test/errors/types/ErrorsContract.py new file mode 100644 index 00000000..25fdf507 --- /dev/null +++ b/pypechain/test/errors/types/ErrorsContract.py @@ -0,0 +1,419 @@ +"""A web3.py Contract class for the Errors contract. + +DO NOT EDIT. This file was generated by pypechain. See documentation at +https://github.com/delvtech/pypechain""" + +# contracts have PascalCase names +# pylint: disable=invalid-name + +# contracts control how many attributes and arguments we have in generated code +# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-arguments + +# we don't need else statement if the other conditionals all have return, +# but it's easier to generate +# pylint: disable=no-else-return + +# This file is bound to get very long depending on contract sizes. +# pylint: disable=too-many-lines + +# methods are overriden with specific arguments instead of generic *args, **kwargs +# pylint: disable=arguments-differ + +from __future__ import annotations + +from typing import Any, Type, cast + +from eth_account.signers.local import LocalAccount +from eth_typing import ChecksumAddress, HexStr +from hexbytes import HexBytes +from typing_extensions import Self +from web3 import Web3 +from web3.contract.contract import Contract, ContractConstructor, ContractFunction, ContractFunctions +from web3.exceptions import FallbackNotFound +from web3.types import ABI, BlockIdentifier, CallOverride, TxParams + +structs = {} + + +class ErrorsRevertWithErrorOneContractFunction(ContractFunction): + """ContractFunction for the revertWithErrorOne method.""" + + def __call__(self) -> ErrorsRevertWithErrorOneContractFunction: # type: ignore + clone = super().__call__() + self.kwargs = clone.kwargs + self.args = clone.args + return self + + def call( + self, + transaction: TxParams | None = None, + block_identifier: BlockIdentifier = "latest", + state_override: CallOverride | None = None, + ccip_read_enabled: bool | None = None, + ) -> None: + """returns None.""" + # Define the expected return types from the smart contract call + + # Call the function + + +class ErrorsRevertWithErrorThreeContractFunction(ContractFunction): + """ContractFunction for the revertWithErrorThree method.""" + + def __call__(self) -> ErrorsRevertWithErrorThreeContractFunction: # type: ignore + clone = super().__call__() + self.kwargs = clone.kwargs + self.args = clone.args + return self + + def call( + self, + transaction: TxParams | None = None, + block_identifier: BlockIdentifier = "latest", + state_override: CallOverride | None = None, + ccip_read_enabled: bool | None = None, + ) -> None: + """returns None.""" + # Define the expected return types from the smart contract call + + # Call the function + + +class ErrorsRevertWithErrorTwoContractFunction(ContractFunction): + """ContractFunction for the revertWithErrorTwo method.""" + + def __call__(self) -> ErrorsRevertWithErrorTwoContractFunction: # type: ignore + clone = super().__call__() + self.kwargs = clone.kwargs + self.args = clone.args + return self + + def call( + self, + transaction: TxParams | None = None, + block_identifier: BlockIdentifier = "latest", + state_override: CallOverride | None = None, + ccip_read_enabled: bool | None = None, + ) -> None: + """returns None.""" + # Define the expected return types from the smart contract call + + # Call the function + + +class ErrorsContractFunctions(ContractFunctions): + """ContractFunctions for the Errors contract.""" + + revertWithErrorOne: ErrorsRevertWithErrorOneContractFunction + + revertWithErrorThree: ErrorsRevertWithErrorThreeContractFunction + + revertWithErrorTwo: ErrorsRevertWithErrorTwoContractFunction + + def __init__( + self, + abi: ABI, + w3: "Web3", + address: ChecksumAddress | None = None, + decode_tuples: bool | None = False, + ) -> None: + super().__init__(abi, w3, address, decode_tuples) + self.revertWithErrorOne = ErrorsRevertWithErrorOneContractFunction.factory( + "revertWithErrorOne", + w3=w3, + contract_abi=abi, + address=address, + decode_tuples=decode_tuples, + function_identifier="revertWithErrorOne", + ) + self.revertWithErrorThree = ErrorsRevertWithErrorThreeContractFunction.factory( + "revertWithErrorThree", + w3=w3, + contract_abi=abi, + address=address, + decode_tuples=decode_tuples, + function_identifier="revertWithErrorThree", + ) + self.revertWithErrorTwo = ErrorsRevertWithErrorTwoContractFunction.factory( + "revertWithErrorTwo", + w3=w3, + contract_abi=abi, + address=address, + decode_tuples=decode_tuples, + function_identifier="revertWithErrorTwo", + ) + + +class ErrorsOneContractError: + """ContractError for One.""" + + # @combomethod destroys return types, so we are redefining functions as both class and instance + # pylint: disable=function-redefined + + # 4 byte error selector + selector: str + # error signature, i.e. CustomError(uint256,bool) + signature: str + + # pylint: disable=useless-parent-delegation + def __init__( + self: "ErrorsOneContractError", + ) -> None: + self.selector = "0xbe0c2110" + self.signature = "One()" + + def decode_error_data( # type: ignore + self: "ErrorsOneContractError", + data: HexBytes, + # TODO: get the return type here + ) -> str: + """Decodes error data returns from a smart contract.""" + # do the decoding + return "data goes here." + + @classmethod + def decode_error_data( # type: ignore + cls: Type["ErrorsOneContractError"], + data: HexBytes, + ) -> str: + """Decodes error data returns from a smart contract.""" + return "data goes here." + + +class ErrorsThreeContractError: + """ContractError for Three.""" + + # @combomethod destroys return types, so we are redefining functions as both class and instance + # pylint: disable=function-redefined + + # 4 byte error selector + selector: str + # error signature, i.e. CustomError(uint256,bool) + signature: str + + # pylint: disable=useless-parent-delegation + def __init__( + self: "ErrorsThreeContractError", + ) -> None: + self.selector = "0x09b8b989" + self.signature = "Three(bool,(uint256,uint256,uint256,uint256),uint8)" + + def decode_error_data( # type: ignore + self: "ErrorsThreeContractError", + data: HexBytes, + # TODO: get the return type here + ) -> str: + """Decodes error data returns from a smart contract.""" + # do the decoding + return "data goes here." + + @classmethod + def decode_error_data( # type: ignore + cls: Type["ErrorsThreeContractError"], + data: HexBytes, + ) -> str: + """Decodes error data returns from a smart contract.""" + return "data goes here." + + +class ErrorsTwoContractError: + """ContractError for Two.""" + + # @combomethod destroys return types, so we are redefining functions as both class and instance + # pylint: disable=function-redefined + + # 4 byte error selector + selector: str + # error signature, i.e. CustomError(uint256,bool) + signature: str + + # pylint: disable=useless-parent-delegation + def __init__( + self: "ErrorsTwoContractError", + ) -> None: + self.selector = "0x01e3e2f6" + self.signature = "Two(string,address,uint8)" + + def decode_error_data( # type: ignore + self: "ErrorsTwoContractError", + data: HexBytes, + # TODO: get the return type here + ) -> str: + """Decodes error data returns from a smart contract.""" + # do the decoding + return "data goes here." + + @classmethod + def decode_error_data( # type: ignore + cls: Type["ErrorsTwoContractError"], + data: HexBytes, + ) -> str: + """Decodes error data returns from a smart contract.""" + return "data goes here." + + +class ErrorsContractErrors: + """ContractErrors for the Errors contract.""" + + One: ErrorsOneContractError + + Three: ErrorsThreeContractError + + Two: ErrorsTwoContractError + + def __init__( + self, + ) -> None: + self.One = ErrorsOneContractError() + self.Three = ErrorsThreeContractError() + self.Two = ErrorsTwoContractError() + + +errors_abi: ABI = cast( + ABI, + [ + {"inputs": [], "name": "One", "type": "error"}, + { + "inputs": [ + {"internalType": "bool", "name": "trueOrFalse", "type": "bool"}, + { + "components": [ + {"internalType": "uint256", "name": "bart", "type": "uint256"}, + {"internalType": "uint256", "name": "lisa", "type": "uint256"}, + {"internalType": "uint256", "name": "homer", "type": "uint256"}, + {"internalType": "uint256", "name": "marge", "type": "uint256"}, + ], + "internalType": "struct Errors.Ages", + "name": "theSimpsons", + "type": "tuple", + }, + {"internalType": "enum Errors.Simpsons", "name": "who", "type": "uint8"}, + ], + "name": "Three", + "type": "error", + }, + { + "inputs": [ + {"internalType": "string", "name": "message", "type": "string"}, + {"internalType": "address", "name": "who", "type": "address"}, + {"internalType": "uint8", "name": "value", "type": "uint8"}, + ], + "name": "Two", + "type": "error", + }, + {"inputs": [], "name": "revertWithErrorOne", "outputs": [], "stateMutability": "pure", "type": "function"}, + {"inputs": [], "name": "revertWithErrorThree", "outputs": [], "stateMutability": "pure", "type": "function"}, + {"inputs": [], "name": "revertWithErrorTwo", "outputs": [], "stateMutability": "pure", "type": "function"}, + ], +) +# pylint: disable=line-too-long +errors_bytecode = HexStr( + "0x608060405234801561000f575f80fd5b506104328061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c806349cbdbf514610043578063a13e7b711461004d578063dc785aeb14610057575b5f80fd5b61004b610061565b005b6100556100ca565b005b61005f6100fc565b005b5f6040518060800160405280600181526020016002815260200160038152602001600481525090505f815f6040517f09b8b9890000000000000000000000000000000000000000000000000000000081526004016100c193929190610234565b60405180910390fd5b6040517fbe0c211000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60ff6040517f01e3e2f60000000000000000000000000000000000000000000000000000000081526004016101339291906103c2565b60405180910390fd5b5f8115159050919050565b6101508161013c565b82525050565b5f819050919050565b61016881610156565b82525050565b608082015f8201516101825f85018261015f565b506020820151610195602085018261015f565b5060408201516101a8604085018261015f565b5060608201516101bb606085018261015f565b50505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b600481106101ff576101fe6101c1565b5b50565b5f81905061020f826101ee565b919050565b5f61021e82610202565b9050919050565b61022e81610214565b82525050565b5f60c0820190506102475f830186610147565b610254602083018561016e565b61026160a0830184610225565b949350505050565b5f82825260208201905092915050565b7f492077696c6c206e6f7420706c6564676520616c6c656769616e636520746f205f8201527f426172742e20492077696c6c206e6f7420706c6564676520616c6c656769616e60208201527f636520746f20426172742e20492077696c6c206e6f7420706c6564676520616c60408201527f6c656769616e636520746f20426172742e000000000000000000000000000000606082015250565b5f61031f607183610269565b915061032a82610279565b608082019050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61035e82610335565b9050919050565b61036e81610354565b82525050565b5f819050919050565b5f60ff82169050919050565b5f819050919050565b5f6103ac6103a76103a284610374565b610389565b61037d565b9050919050565b6103bc81610392565b82525050565b5f6060820190508181035f8301526103d981610313565b90506103e86020830185610365565b6103f560408301846103b3565b939250505056fea2646970667358221220d2ccb335b1907f8c97ee5b43958fd13ff523a48524e48710094939c1728f158b64736f6c63430008170033" +) + + +class ErrorsContract(Contract): + """A web3.py Contract class for the Errors contract.""" + + abi: ABI = errors_abi + bytecode: bytes = HexBytes(errors_bytecode) + + def __init__(self, address: ChecksumAddress | None = None) -> None: + try: + # Initialize parent Contract class + super().__init__(address=address) + self.functions = ErrorsContractFunctions(errors_abi, self.w3, address) # type: ignore + + self.errors = ErrorsContractErrors() + + except FallbackNotFound: + print("Fallback function not found. Continuing...") + + errors: ErrorsContractErrors = ErrorsContractErrors() + + functions: ErrorsContractFunctions + + @classmethod + def constructor(cls) -> ContractConstructor: # type: ignore + """Creates a transaction with the contract's constructor function. + + Parameters + ---------- + + w3 : Web3 + A web3 instance. + account : LocalAccount + The account to use to deploy the contract. + + Returns + ------- + Self + A deployed instance of the contract. + + """ + + return super().constructor() + + @classmethod + def deploy(cls, w3: Web3, account: LocalAccount | ChecksumAddress) -> Self: + """Deploys and instance of the contract. + + Parameters + ---------- + w3 : Web3 + A web3 instance. + account : LocalAccount + The account to use to deploy the contract. + + Returns + ------- + Self + A deployed instance of the contract. + """ + deployer = cls.factory(w3=w3) + constructor_fn = deployer.constructor() + + # if an address is supplied, try to use a web3 default account + if isinstance(account, str): + tx_hash = constructor_fn.transact({"from": account}) + tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) + + deployed_contract = deployer(address=tx_receipt.contractAddress) # type: ignore + return deployed_contract + + # otherwise use the account provided. + deployment_tx = constructor_fn.build_transaction() + current_nonce = w3.eth.get_transaction_count(account.address) + deployment_tx.update({"nonce": current_nonce}) + + # Sign the transaction with local account private key + signed_tx = account.sign_transaction(deployment_tx) + + # Send the signed transaction and wait for receipt + tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction) + tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) + + deployed_contract = deployer(address=tx_receipt.contractAddress) # type: ignore + return deployed_contract + + @classmethod + def factory(cls, w3: Web3, class_name: str | None = None, **kwargs: Any) -> Type[Self]: + """Deploys and instance of the contract. + + Parameters + ---------- + w3 : Web3 + A web3 instance. + class_name: str | None + The instance class name. + + Returns + ------- + Self + A deployed instance of the contract. + """ + contract = super().factory(w3, class_name, **kwargs) + contract.functions = ErrorsContractFunctions(errors_abi, w3, None) + + return contract diff --git a/pypechain/test/errors/types/__init__.py b/pypechain/test/errors/types/__init__.py new file mode 100644 index 00000000..391a0f8a --- /dev/null +++ b/pypechain/test/errors/types/__init__.py @@ -0,0 +1,10 @@ +"""Export all types from generated files. + +DO NOT EDIT. This file was generated by pypechain. See documentation at +https://github.com/delvtech/pypechain""" + +# python 3.10 causes this warning when sibling/child files import from one of the files listed here. +# remove this pylint diable when we upgreade to 3.11 +# pylint: disable=import-self + +from .ErrorsContract import * diff --git a/pypechain/test/errors/types/utilities.py b/pypechain/test/errors/types/utilities.py new file mode 100644 index 00000000..6d40773e --- /dev/null +++ b/pypechain/test/errors/types/utilities.py @@ -0,0 +1,110 @@ +"""Utility functions for the pypechain generated files. + +DO NOT EDIT. This file was generated by pypechain. See documentation at +https://github.com/delvtech/pypechain""" + +from __future__ import annotations + +from dataclasses import fields, is_dataclass +from typing import Any, Tuple, TypeVar, cast + +T = TypeVar("T") + + +def tuple_to_dataclass(cls: type[T], structs: dict[str, Any], tuple_data: Any | Tuple[Any, ...]) -> T: + """ + Converts a tuple (including nested tuples) to a dataclass instance. If cls is not a dataclass, + then the data will just be passed through this function. + + Parameters + ---------- + cls: type[T] + The dataclass type to which the tuple data is to be converted. + tuple_data: Any | Tuple[Any, ...] + A tuple (or nested tuple) of values to convert into a dataclass instance. + + Returns + ------- + T + Either an instance of cls populated with data from tuple_data or tuple_data itself. + """ + if not is_dataclass(cls): + return cast(T, tuple_data) + + field_types = {field.name: field.type for field in fields(cls)} + field_values = {} + + for (field_name, field_type), value in zip(field_types.items(), tuple_data): + field_type = structs.get(field_type, field_type) + if is_dataclass(field_type): + # Recursively convert nested tuples to nested dataclasses + field_values[field_name] = tuple_to_dataclass(field_type, structs, value) + elif isinstance(value, tuple) and not getattr(field_type, "_name", None) == "Tuple": + # If it's a tuple and the field is not intended to be a tuple, assume it's a nested dataclass + field_values[field_name] = tuple_to_dataclass(field_type, structs, value) + else: + # Otherwise, set the primitive value directly + field_values[field_name] = value + + return cls(**field_values) + + +def dataclass_to_tuple(instance: Any) -> Any: + """Convert a dataclass instance to a tuple, handling nested dataclasses. + If the input is not a dataclass, return the original value. + + Parameters + ---------- + instance : Any + The dataclass instance to convert to a tuple. If it is not it passes through. + + Returns + ------- + Any + either a tuple or the orginial value + """ + if not is_dataclass(instance): + return instance + + def convert_value(value: Any) -> Any: + """Convert nested dataclasses to tuples recursively, or return the original value.""" + if is_dataclass(value): + return dataclass_to_tuple(value) + return value + + return tuple(convert_value(getattr(instance, field.name)) for field in fields(instance)) + + +def rename_returned_types( + structs: dict[str, Any], return_types: list[Any] | Any, raw_values: list[str | int | tuple] | str | int | tuple +) -> tuple: + """Convert structs in the return value to known dataclasses. + + Parameters + ---------- + return_types : list[str] | str + The type or list of types returned from a contract. + raw_values : list[str | int | tuple] | str | int | tuple + The actual returned values from the contract. + + Returns + ------- + tuple + The return types. + """ + # cover case of multiple return values + if isinstance(return_types, list): + # Ensure raw_values is a tuple for consistency + if not isinstance(raw_values, list): + raw_values = (raw_values,) + + # Convert the tuple to the dataclass instance using the utility function + converted_values = tuple( + tuple_to_dataclass(return_type, structs, value) for return_type, value in zip(return_types, raw_values) + ) + + return converted_values + + # cover case of single return value + converted_value = tuple_to_dataclass(return_types, structs, raw_values) + return converted_value From b4f7d0caabe49d309af91464bf25b7a0a1322c9e Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 31 Jan 2024 18:40:00 -0500 Subject: [PATCH 4/9] get input types dynamically --- pypechain/templates/utilities.py | 27 +++++++++++++++++++ pypechain/test/errors/test_errors.py | 34 +++++++++++++++++------- pypechain/test/errors/types/utilities.py | 10 +++++++ 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/pypechain/templates/utilities.py b/pypechain/templates/utilities.py index 6d40773e..2c9a2737 100644 --- a/pypechain/templates/utilities.py +++ b/pypechain/templates/utilities.py @@ -8,6 +8,9 @@ from dataclasses import fields, is_dataclass from typing import Any, Tuple, TypeVar, cast +from eth_utils.abi import collapse_if_tuple +from web3.types import ABIFunction + T = TypeVar("T") @@ -108,3 +111,27 @@ def rename_returned_types( # cover case of single return value converted_value = tuple_to_dataclass(return_types, structs, raw_values) return converted_value + + +def get_abi_input_types(abi: ABIFunction) -> list[str]: + """Gets all the solidity input types for a function or error. + + Cribbed from web3._utils.abi.py file. + + Parameters + ---------- + + abi: ABIFunction + The ABIFunction or ABIError that we want to get input types for. + + Returns + ------- + list[str] + A list of solidity input types. + + """ + + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): + return [] + else: + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/errors/test_errors.py b/pypechain/test/errors/test_errors.py index 7626088b..1b5243b2 100644 --- a/pypechain/test/errors/test_errors.py +++ b/pypechain/test/errors/test_errors.py @@ -3,12 +3,16 @@ from __future__ import annotations import os +from typing import cast import pytest from eth_abi.codec import ABICodec from eth_abi.registry import registry as default_registry from hexbytes import HexBytes from web3.exceptions import ContractCustomError +from web3.types import ABIFunction + +from pypechain.templates.utilities import get_abi_input_types from .types import ErrorsContract @@ -29,12 +33,15 @@ def test_error_one(self, w3): assert err.message selector = err.message[:10] - assert selector == ErrorsContract.errors.One.selector - data = err.message[10:] - assert data == "" + assert selector == ErrorsContract.errors.One.selector - types = () + error_abi = cast( + ABIFunction, + [item for item in ErrorsContract.abi if item.get("name") == "One" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + assert types == () abi_codec = ABICodec(default_registry) decoded = abi_codec.decode(types, HexBytes(data)) assert not decoded @@ -48,11 +55,15 @@ def test_error_two(self, w3): assert err.message selector = err.message[:10] - assert selector == ErrorsContract.errors.Two.selector - data = err.message[10:] + assert selector == ErrorsContract.errors.Two.selector - types = ("string", "address", "uint8") + error_abi = cast( + ABIFunction, + [item for item in ErrorsContract.abi if item.get("name") == "Two" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + assert types == ("string", "address", "uint8") abi_codec = ABICodec(default_registry) decoded = abi_codec.decode(types, HexBytes(data)) assert decoded == ( @@ -70,11 +81,16 @@ def test_error_three(self, w3): assert err.message selector = err.message[:10] + data = err.message[10:] assert selector == ErrorsContract.errors.Three.selector - data = err.message[10:] + error_abi = cast( + ABIFunction, + [item for item in ErrorsContract.abi if item.get("name") == "Three" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + assert types == ("bool", "(uint256,uint256,uint256,uint256)", "uint8") - types = ("bool", "(uint256,uint256,uint256,uint256)", "uint8") abi_codec = ABICodec(default_registry) decoded = abi_codec.decode(types, HexBytes(data)) assert decoded == (False, (1, 2, 3, 4), 0) diff --git a/pypechain/test/errors/types/utilities.py b/pypechain/test/errors/types/utilities.py index 6d40773e..e955df9c 100644 --- a/pypechain/test/errors/types/utilities.py +++ b/pypechain/test/errors/types/utilities.py @@ -8,6 +8,9 @@ from dataclasses import fields, is_dataclass from typing import Any, Tuple, TypeVar, cast +from eth_utils.abi import collapse_if_tuple +from web3.types import ABIFunction + T = TypeVar("T") @@ -108,3 +111,10 @@ def rename_returned_types( # cover case of single return value converted_value = tuple_to_dataclass(return_types, structs, raw_values) return converted_value + + +def get_abi_input_types(abi: ABIFunction) -> list[str]: + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): + return [] + else: + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] From 7bf0b0c231110577164d710ceb17658be7c097ba Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 31 Jan 2024 19:02:05 -0500 Subject: [PATCH 5/9] add decoding to contract errors --- .../templates/contract.py/base.py.jinja2 | 6 +- .../templates/contract.py/errors.py.jinja2 | 27 +++++-- pypechain/test/errors/test_errors.py | 11 ++- pypechain/test/errors/types/ErrorsContract.py | 81 ++++++++++++++----- pypechain/test/errors/types/utilities.py | 17 ++++ 5 files changed, 112 insertions(+), 30 deletions(-) diff --git a/pypechain/templates/contract.py/base.py.jinja2 b/pypechain/templates/contract.py/base.py.jinja2 index ead04cce..c14c9fcd 100644 --- a/pypechain/templates/contract.py/base.py.jinja2 +++ b/pypechain/templates/contract.py/base.py.jinja2 @@ -26,6 +26,8 @@ from dataclasses import fields, is_dataclass from typing import Any, NamedTuple, Tuple, Type, TypeVar, cast, overload from typing import Iterable, Sequence +from eth_abi.codec import ABICodec +from eth_abi.registry import registry as default_registry from eth_typing import ChecksumAddress, HexStr from eth_account.signers.local import LocalAccount from hexbytes import HexBytes @@ -34,13 +36,13 @@ from web3 import Web3 from web3.contract.contract import Contract, ContractFunction, ContractFunctions, ContractConstructor from web3.contract.contract import ContractEvent, ContractEvents from web3.exceptions import FallbackNotFound -from web3.types import ABI, BlockIdentifier, CallOverride, TxParams +from web3.types import ABI, BlockIdentifier, CallOverride, TxParams, ABIFunction from web3.types import EventData from web3._utils.filters import LogFilter {% for struct_info in structs_used %} from .{{struct_info.contract_name}}Types import {{struct_info.name}} {% endfor %} -from .utilities import tuple_to_dataclass, dataclass_to_tuple, rename_returned_types +from .utilities import dataclass_to_tuple, get_abi_input_types, rename_returned_types, tuple_to_dataclass structs = { diff --git a/pypechain/templates/contract.py/errors.py.jinja2 b/pypechain/templates/contract.py/errors.py.jinja2 index 9d31d80a..e1aad598 100644 --- a/pypechain/templates/contract.py/errors.py.jinja2 +++ b/pypechain/templates/contract.py/errors.py.jinja2 @@ -21,19 +21,34 @@ class {{contract_name}}{{error_info.name}}ContractError: def decode_error_data( # type: ignore self: "{{contract_name}}{{error_info.name}}ContractError", data: HexBytes, - # TODO: get the return type here - ) -> str: + # TODO: instead of returning a tuple, return a dataclass with the input names and types just like we do for functions + ) -> tuple[Any, ...]: """Decodes error data returns from a smart contract.""" - # do the decoding - return "data goes here." + error_abi = cast( + ABIFunction, + [item for item in {{contract_name | lower}}_abi if item.get("name") == "{{error_info.name}}" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + return decoded @classmethod def decode_error_data( # type: ignore cls: Type["{{contract_name}}{{error_info.name}}ContractError"], data: HexBytes, - ) -> str: + ) -> tuple[Any, ...]: """Decodes error data returns from a smart contract.""" - return "data goes here." + error_abi = cast( + ABIFunction, + [item for item in {{contract_name | lower}}_abi if item.get("name") == "{{error_info.name}}" and item.get("type") == "error"][0], + + + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + return decoded {% endfor %} class {{contract_name}}ContractErrors: diff --git a/pypechain/test/errors/test_errors.py b/pypechain/test/errors/test_errors.py index 1b5243b2..5125a489 100644 --- a/pypechain/test/errors/test_errors.py +++ b/pypechain/test/errors/test_errors.py @@ -41,11 +41,13 @@ def test_error_one(self, w3): [item for item in ErrorsContract.abi if item.get("name") == "One" and item.get("type") == "error"][0], ) types = get_abi_input_types(error_abi) - assert types == () + assert types == [] abi_codec = ABICodec(default_registry) decoded = abi_codec.decode(types, HexBytes(data)) assert not decoded + assert deployed_contract.errors.One.decode_error_data(HexBytes(data)) == decoded + def test_error_two(self, w3): """Test that we can decode strings.""" deployed_contract = ErrorsContract.deploy(w3=w3, account=w3.eth.accounts[0]) @@ -63,7 +65,7 @@ def test_error_two(self, w3): [item for item in ErrorsContract.abi if item.get("name") == "Two" and item.get("type") == "error"][0], ) types = get_abi_input_types(error_abi) - assert types == ("string", "address", "uint8") + assert types == ["string", "address", "uint8"] abi_codec = ABICodec(default_registry) decoded = abi_codec.decode(types, HexBytes(data)) assert decoded == ( @@ -71,6 +73,7 @@ def test_error_two(self, w3): "0x0000000000000000000000000000000000000000", 255, ) + assert deployed_contract.errors.Two.decode_error_data(HexBytes(data)) == decoded def test_error_three(self, w3): """Test that we can decode structs and enums.""" @@ -89,8 +92,10 @@ def test_error_three(self, w3): [item for item in ErrorsContract.abi if item.get("name") == "Three" and item.get("type") == "error"][0], ) types = get_abi_input_types(error_abi) - assert types == ("bool", "(uint256,uint256,uint256,uint256)", "uint8") + assert types == ["bool", "(uint256,uint256,uint256,uint256)", "uint8"] abi_codec = ABICodec(default_registry) decoded = abi_codec.decode(types, HexBytes(data)) assert decoded == (False, (1, 2, 3, 4), 0) + + assert deployed_contract.errors.Three.decode_error_data(HexBytes(data)) == decoded diff --git a/pypechain/test/errors/types/ErrorsContract.py b/pypechain/test/errors/types/ErrorsContract.py index 25fdf507..4cb20655 100644 --- a/pypechain/test/errors/types/ErrorsContract.py +++ b/pypechain/test/errors/types/ErrorsContract.py @@ -24,6 +24,8 @@ from typing import Any, Type, cast +from eth_abi.codec import ABICodec +from eth_abi.registry import registry as default_registry from eth_account.signers.local import LocalAccount from eth_typing import ChecksumAddress, HexStr from hexbytes import HexBytes @@ -31,7 +33,9 @@ from web3 import Web3 from web3.contract.contract import Contract, ContractConstructor, ContractFunction, ContractFunctions from web3.exceptions import FallbackNotFound -from web3.types import ABI, BlockIdentifier, CallOverride, TxParams +from web3.types import ABI, ABIFunction, BlockIdentifier, CallOverride, TxParams + +from .utilities import get_abi_input_types structs = {} @@ -166,19 +170,32 @@ def __init__( def decode_error_data( # type: ignore self: "ErrorsOneContractError", data: HexBytes, - # TODO: get the return type here - ) -> str: + # TODO: instead of returning a tuple, return a dataclass with the input names and types just like we do for functions + ) -> tuple[Any, ...]: """Decodes error data returns from a smart contract.""" - # do the decoding - return "data goes here." + error_abi = cast( + ABIFunction, + [item for item in errors_abi if item.get("name") == "One" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + return decoded @classmethod def decode_error_data( # type: ignore cls: Type["ErrorsOneContractError"], data: HexBytes, - ) -> str: + ) -> tuple[Any, ...]: """Decodes error data returns from a smart contract.""" - return "data goes here." + error_abi = cast( + ABIFunction, + [item for item in errors_abi if item.get("name") == "One" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + return decoded class ErrorsThreeContractError: @@ -202,19 +219,32 @@ def __init__( def decode_error_data( # type: ignore self: "ErrorsThreeContractError", data: HexBytes, - # TODO: get the return type here - ) -> str: + # TODO: instead of returning a tuple, return a dataclass with the input names and types just like we do for functions + ) -> tuple[Any, ...]: """Decodes error data returns from a smart contract.""" - # do the decoding - return "data goes here." + error_abi = cast( + ABIFunction, + [item for item in errors_abi if item.get("name") == "Three" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + return decoded @classmethod def decode_error_data( # type: ignore cls: Type["ErrorsThreeContractError"], data: HexBytes, - ) -> str: + ) -> tuple[Any, ...]: """Decodes error data returns from a smart contract.""" - return "data goes here." + error_abi = cast( + ABIFunction, + [item for item in errors_abi if item.get("name") == "Three" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + return decoded class ErrorsTwoContractError: @@ -238,19 +268,32 @@ def __init__( def decode_error_data( # type: ignore self: "ErrorsTwoContractError", data: HexBytes, - # TODO: get the return type here - ) -> str: + # TODO: instead of returning a tuple, return a dataclass with the input names and types just like we do for functions + ) -> tuple[Any, ...]: """Decodes error data returns from a smart contract.""" - # do the decoding - return "data goes here." + error_abi = cast( + ABIFunction, + [item for item in errors_abi if item.get("name") == "Two" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + return decoded @classmethod def decode_error_data( # type: ignore cls: Type["ErrorsTwoContractError"], data: HexBytes, - ) -> str: + ) -> tuple[Any, ...]: """Decodes error data returns from a smart contract.""" - return "data goes here." + error_abi = cast( + ABIFunction, + [item for item in errors_abi if item.get("name") == "Two" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, HexBytes(data)) + return decoded class ErrorsContractErrors: diff --git a/pypechain/test/errors/types/utilities.py b/pypechain/test/errors/types/utilities.py index e955df9c..2c9a2737 100644 --- a/pypechain/test/errors/types/utilities.py +++ b/pypechain/test/errors/types/utilities.py @@ -114,6 +114,23 @@ def rename_returned_types( def get_abi_input_types(abi: ABIFunction) -> list[str]: + """Gets all the solidity input types for a function or error. + + Cribbed from web3._utils.abi.py file. + + Parameters + ---------- + + abi: ABIFunction + The ABIFunction or ABIError that we want to get input types for. + + Returns + ------- + list[str] + A list of solidity input types. + + """ + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] else: From d5757bafddf3e738566bd7f2fa7286d1b5d845ce Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 31 Jan 2024 19:50:54 -0500 Subject: [PATCH 6/9] add method to decode any contract error --- .../templates/contract.py/errors.py.jinja2 | 19 +++++++++---- pypechain/test/errors/test_errors.py | 3 +++ pypechain/test/errors/types/ErrorsContract.py | 27 ++++++++++++++----- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/pypechain/templates/contract.py/errors.py.jinja2 b/pypechain/templates/contract.py/errors.py.jinja2 index e1aad598..de614ef1 100644 --- a/pypechain/templates/contract.py/errors.py.jinja2 +++ b/pypechain/templates/contract.py/errors.py.jinja2 @@ -30,7 +30,7 @@ class {{contract_name}}{{error_info.name}}ContractError: ) types = get_abi_input_types(error_abi) abi_codec = ABICodec(default_registry) - decoded = abi_codec.decode(types, HexBytes(data)) + decoded = abi_codec.decode(types, data) return decoded @classmethod @@ -42,12 +42,10 @@ class {{contract_name}}{{error_info.name}}ContractError: error_abi = cast( ABIFunction, [item for item in {{contract_name | lower}}_abi if item.get("name") == "{{error_info.name}}" and item.get("type") == "error"][0], - - ) types = get_abi_input_types(error_abi) abi_codec = ABICodec(default_registry) - decoded = abi_codec.decode(types, HexBytes(data)) + decoded = abi_codec.decode(types, data) return decoded {% endfor %} @@ -62,4 +60,15 @@ class {{contract_name}}ContractErrors: ) -> None: {% for error_info in errors -%} self.{{error_info.name}} = {{contract_name}}{{error_info.name}}ContractError() - {% endfor %} \ No newline at end of file + {% endfor %} + self._all = [{% for error_info in errors -%}self.{{error_info.name}},{%- endfor %}] + + def decode_custom_error(self, data: HexBytes) -> tuple[Any, ...]: + """Decodes a custom contract error.""" + selector = data.hex()[:10] + for err in self._all: + if err.selector == selector: + return err.decode_error_data(data) + + raise ValueError(f"{{contract_name}} does not have a selector matching {selector}") + diff --git a/pypechain/test/errors/test_errors.py b/pypechain/test/errors/test_errors.py index 5125a489..9f6066c8 100644 --- a/pypechain/test/errors/test_errors.py +++ b/pypechain/test/errors/test_errors.py @@ -47,6 +47,7 @@ def test_error_one(self, w3): assert not decoded assert deployed_contract.errors.One.decode_error_data(HexBytes(data)) == decoded + assert deployed_contract.errors.decode_custom_error(HexBytes(err.message)) == decoded def test_error_two(self, w3): """Test that we can decode strings.""" @@ -74,6 +75,7 @@ def test_error_two(self, w3): 255, ) assert deployed_contract.errors.Two.decode_error_data(HexBytes(data)) == decoded + assert deployed_contract.errors.decode_custom_error(HexBytes(err.message)) == decoded def test_error_three(self, w3): """Test that we can decode structs and enums.""" @@ -99,3 +101,4 @@ def test_error_three(self, w3): assert decoded == (False, (1, 2, 3, 4), 0) assert deployed_contract.errors.Three.decode_error_data(HexBytes(data)) == decoded + assert deployed_contract.errors.decode_custom_error(HexBytes(err.message)) == decoded diff --git a/pypechain/test/errors/types/ErrorsContract.py b/pypechain/test/errors/types/ErrorsContract.py index 4cb20655..b745f2f4 100644 --- a/pypechain/test/errors/types/ErrorsContract.py +++ b/pypechain/test/errors/types/ErrorsContract.py @@ -179,7 +179,7 @@ def decode_error_data( # type: ignore ) types = get_abi_input_types(error_abi) abi_codec = ABICodec(default_registry) - decoded = abi_codec.decode(types, HexBytes(data)) + decoded = abi_codec.decode(types, data) return decoded @classmethod @@ -194,7 +194,7 @@ def decode_error_data( # type: ignore ) types = get_abi_input_types(error_abi) abi_codec = ABICodec(default_registry) - decoded = abi_codec.decode(types, HexBytes(data)) + decoded = abi_codec.decode(types, data) return decoded @@ -228,7 +228,7 @@ def decode_error_data( # type: ignore ) types = get_abi_input_types(error_abi) abi_codec = ABICodec(default_registry) - decoded = abi_codec.decode(types, HexBytes(data)) + decoded = abi_codec.decode(types, data) return decoded @classmethod @@ -243,7 +243,7 @@ def decode_error_data( # type: ignore ) types = get_abi_input_types(error_abi) abi_codec = ABICodec(default_registry) - decoded = abi_codec.decode(types, HexBytes(data)) + decoded = abi_codec.decode(types, data) return decoded @@ -277,7 +277,7 @@ def decode_error_data( # type: ignore ) types = get_abi_input_types(error_abi) abi_codec = ABICodec(default_registry) - decoded = abi_codec.decode(types, HexBytes(data)) + decoded = abi_codec.decode(types, data) return decoded @classmethod @@ -292,7 +292,7 @@ def decode_error_data( # type: ignore ) types = get_abi_input_types(error_abi) abi_codec = ABICodec(default_registry) - decoded = abi_codec.decode(types, HexBytes(data)) + decoded = abi_codec.decode(types, data) return decoded @@ -312,6 +312,21 @@ def __init__( self.Three = ErrorsThreeContractError() self.Two = ErrorsTwoContractError() + self._all = [ + self.One, + self.Three, + self.Two, + ] + + def decode_custom_error(self, data: HexBytes) -> tuple[Any, ...]: + """Decodes a custom contract error.""" + selector = data.hex()[:10] + for err in self._all: + if err.selector == selector: + return err.decode_error_data(data) + + raise ValueError(f"Errors does not have a selector matching {selector}") + errors_abi: ABI = cast( ABI, From 8bfbcb9d61410b879b03b0de254630645804c53e Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Thu, 1 Feb 2024 13:24:28 -0500 Subject: [PATCH 7/9] fix test --- pypechain/templates/contract.py/contract.py.jinja2 | 1 + pypechain/templates/contract.py/errors.py.jinja2 | 6 +++--- pypechain/test/errors/test_errors.py | 12 ++++++------ pypechain/test/errors/types/ErrorsContract.py | 7 ++++--- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/pypechain/templates/contract.py/contract.py.jinja2 b/pypechain/templates/contract.py/contract.py.jinja2 index af92b64a..e4abc858 100644 --- a/pypechain/templates/contract.py/contract.py.jinja2 +++ b/pypechain/templates/contract.py/contract.py.jinja2 @@ -122,6 +122,7 @@ class {{contract_name}}Contract(Contract): """ contract = super().factory(w3, class_name, **kwargs) contract.functions = {{contract_name}}ContractFunctions({{contract_name | lower}}_abi, w3, None) + contract.errors = {{contract_name}}ContractErrors() return contract diff --git a/pypechain/templates/contract.py/errors.py.jinja2 b/pypechain/templates/contract.py/errors.py.jinja2 index de614ef1..f8a99a1c 100644 --- a/pypechain/templates/contract.py/errors.py.jinja2 +++ b/pypechain/templates/contract.py/errors.py.jinja2 @@ -63,12 +63,12 @@ class {{contract_name}}ContractErrors: {% endfor %} self._all = [{% for error_info in errors -%}self.{{error_info.name}},{%- endfor %}] - def decode_custom_error(self, data: HexBytes) -> tuple[Any, ...]: + def decode_custom_error(self, data: str) -> tuple[Any, ...]: """Decodes a custom contract error.""" - selector = data.hex()[:10] + selector = data[:10] for err in self._all: if err.selector == selector: - return err.decode_error_data(data) + return err.decode_error_data(HexBytes(data[10:])) raise ValueError(f"{{contract_name}} does not have a selector matching {selector}") diff --git a/pypechain/test/errors/test_errors.py b/pypechain/test/errors/test_errors.py index 9f6066c8..a03146f3 100644 --- a/pypechain/test/errors/test_errors.py +++ b/pypechain/test/errors/test_errors.py @@ -46,8 +46,8 @@ def test_error_one(self, w3): decoded = abi_codec.decode(types, HexBytes(data)) assert not decoded - assert deployed_contract.errors.One.decode_error_data(HexBytes(data)) == decoded - assert deployed_contract.errors.decode_custom_error(HexBytes(err.message)) == decoded + assert ErrorsContract.errors.One.decode_error_data(HexBytes(data)) == decoded + assert ErrorsContract.errors.decode_custom_error(err.message) == decoded def test_error_two(self, w3): """Test that we can decode strings.""" @@ -74,8 +74,8 @@ def test_error_two(self, w3): "0x0000000000000000000000000000000000000000", 255, ) - assert deployed_contract.errors.Two.decode_error_data(HexBytes(data)) == decoded - assert deployed_contract.errors.decode_custom_error(HexBytes(err.message)) == decoded + assert ErrorsContract.errors.Two.decode_error_data(HexBytes(data)) == decoded + assert ErrorsContract.errors.decode_custom_error(err.message) == decoded def test_error_three(self, w3): """Test that we can decode structs and enums.""" @@ -100,5 +100,5 @@ def test_error_three(self, w3): decoded = abi_codec.decode(types, HexBytes(data)) assert decoded == (False, (1, 2, 3, 4), 0) - assert deployed_contract.errors.Three.decode_error_data(HexBytes(data)) == decoded - assert deployed_contract.errors.decode_custom_error(HexBytes(err.message)) == decoded + assert ErrorsContract.errors.Three.decode_error_data(HexBytes(data)) == decoded + assert ErrorsContract.errors.decode_custom_error(err.message) == decoded diff --git a/pypechain/test/errors/types/ErrorsContract.py b/pypechain/test/errors/types/ErrorsContract.py index b745f2f4..9d6a1291 100644 --- a/pypechain/test/errors/types/ErrorsContract.py +++ b/pypechain/test/errors/types/ErrorsContract.py @@ -318,12 +318,12 @@ def __init__( self.Two, ] - def decode_custom_error(self, data: HexBytes) -> tuple[Any, ...]: + def decode_custom_error(self, data: str) -> tuple[Any, ...]: """Decodes a custom contract error.""" - selector = data.hex()[:10] + selector = data[:10] for err in self._all: if err.selector == selector: - return err.decode_error_data(data) + return err.decode_error_data(HexBytes(data[10:])) raise ValueError(f"Errors does not have a selector matching {selector}") @@ -473,5 +473,6 @@ def factory(cls, w3: Web3, class_name: str | None = None, **kwargs: Any) -> Type """ contract = super().factory(w3, class_name, **kwargs) contract.functions = ErrorsContractFunctions(errors_abi, w3, None) + contract.errors = ErrorsContractErrors() return contract From 314bab45fa2f6b5286ed5b13f3c3c8e24f344476 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Thu, 1 Feb 2024 15:09:29 -0500 Subject: [PATCH 8/9] lint fixes and regen --- example/types/ExampleContract.py | 53 +++++++++++++++---- example/types/utilities.py | 27 ++++++++++ pypechain/render/contract.py | 2 +- .../templates/contract.py/base.py.jinja2 | 4 ++ .../templates/contract.py/contract.py.jinja2 | 2 + .../types/ConstructorNoArgsContract.py | 4 ++ .../types/ConstructorWithArgsContract.py | 4 ++ .../ConstructorWithStructArgsContract.py | 4 ++ .../deployment/types/NoConstructorContract.py | 4 ++ pypechain/test/deployment/types/utilities.py | 27 ++++++++++ pypechain/test/errors/test_errors.py | 2 +- pypechain/test/errors/types/ErrorsContract.py | 4 ++ pypechain/test/events/types/EventsContract.py | 4 ++ pypechain/test/events/types/utilities.py | 27 ++++++++++ .../types/OverloadedMethodsContract.py | 4 ++ pypechain/test/overloading/types/utilities.py | 27 ++++++++++ .../return_types/types/ReturnTypesContract.py | 4 ++ .../test/return_types/types/utilities.py | 27 ++++++++++ .../test/structs/types/StructsAContract.py | 4 ++ .../test/structs/types/StructsBContract.py | 4 ++ .../test/structs/types/StructsCContract.py | 4 ++ pypechain/test/structs/types/utilities.py | 27 ++++++++++ 22 files changed, 258 insertions(+), 11 deletions(-) diff --git a/example/types/ExampleContract.py b/example/types/ExampleContract.py index b6a2910e..7f3c7aba 100644 --- a/example/types/ExampleContract.py +++ b/example/types/ExampleContract.py @@ -20,10 +20,16 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, Iterable, NamedTuple, Sequence, Type, cast +from eth_abi.codec import ABICodec +from eth_abi.registry import registry as default_registry from eth_account.signers.local import LocalAccount from eth_typing import ChecksumAddress, HexStr from hexbytes import HexBytes @@ -39,10 +45,10 @@ ContractFunctions, ) from web3.exceptions import FallbackNotFound -from web3.types import ABI, BlockIdentifier, CallOverride, EventData, TxParams +from web3.types import ABI, ABIFunction, BlockIdentifier, CallOverride, EventData, TxParams from .ExampleTypes import InnerStruct, NestedStruct, SimpleStruct -from .utilities import dataclass_to_tuple, rename_returned_types +from .utilities import dataclass_to_tuple, get_abi_input_types, rename_returned_types structs = { "SimpleStruct": SimpleStruct, @@ -622,17 +628,32 @@ def __init__( def decode_error_data( # type: ignore self: "ExampleWrongChoiceContractError", data: HexBytes, - # TODO: get the return type here - ) -> str: - # do the decoding - return "data goes here." + # TODO: instead of returning a tuple, return a dataclass with the input names and types just like we do for functions + ) -> tuple[Any, ...]: + """Decodes error data returns from a smart contract.""" + error_abi = cast( + ABIFunction, + [item for item in example_abi if item.get("name") == "WrongChoice" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, data) + return decoded @classmethod def decode_error_data( # type: ignore cls: Type["ExampleWrongChoiceContractError"], data: HexBytes, - ) -> str: - return "data goes here." + ) -> tuple[Any, ...]: + """Decodes error data returns from a smart contract.""" + error_abi = cast( + ABIFunction, + [item for item in example_abi if item.get("name") == "WrongChoice" and item.get("type") == "error"][0], + ) + types = get_abi_input_types(error_abi) + abi_codec = ABICodec(default_registry) + decoded = abi_codec.decode(types, data) + return decoded class ExampleContractErrors: @@ -645,6 +666,19 @@ def __init__( ) -> None: self.WrongChoice = ExampleWrongChoiceContractError() + self._all = [ + self.WrongChoice, + ] + + def decode_custom_error(self, data: str) -> tuple[Any, ...]: + """Decodes a custom contract error.""" + selector = data[:10] + for err in self._all: + if err.selector == selector: + return err.decode_error_data(HexBytes(data[10:])) + + raise ValueError(f"Example does not have a selector matching {selector}") + example_abi: ABI = cast( ABI, @@ -929,7 +963,7 @@ def __init__(self, address: ChecksumAddress | None = None) -> None: events: ExampleContractEvents - errors: ExampleContractErrors + errors: ExampleContractErrors = ExampleContractErrors() functions: ExampleContractFunctions @@ -1019,5 +1053,6 @@ def factory(cls, w3: Web3, class_name: str | None = None, **kwargs: Any) -> Type """ contract = super().factory(w3, class_name, **kwargs) contract.functions = ExampleContractFunctions(example_abi, w3, None) + contract.errors = ExampleContractErrors() return contract diff --git a/example/types/utilities.py b/example/types/utilities.py index 6d40773e..2c9a2737 100644 --- a/example/types/utilities.py +++ b/example/types/utilities.py @@ -8,6 +8,9 @@ from dataclasses import fields, is_dataclass from typing import Any, Tuple, TypeVar, cast +from eth_utils.abi import collapse_if_tuple +from web3.types import ABIFunction + T = TypeVar("T") @@ -108,3 +111,27 @@ def rename_returned_types( # cover case of single return value converted_value = tuple_to_dataclass(return_types, structs, raw_values) return converted_value + + +def get_abi_input_types(abi: ABIFunction) -> list[str]: + """Gets all the solidity input types for a function or error. + + Cribbed from web3._utils.abi.py file. + + Parameters + ---------- + + abi: ABIFunction + The ABIFunction or ABIError that we want to get input types for. + + Returns + ------- + list[str] + A list of solidity input types. + + """ + + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): + return [] + else: + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/render/contract.py b/pypechain/render/contract.py index 1a76b1e7..62c69d6e 100644 --- a/pypechain/render/contract.py +++ b/pypechain/render/contract.py @@ -194,12 +194,12 @@ def render_contract_file(contract_info: ContractInfo) -> str | None: str A serialized python file. """ + # pylint: disable=too-many-locals # if the abi is empty, then we are dealing with an interface or library so we don't want to # create a contract file for it. if contract_info.abi == []: return None # TODO: break this function up or bundle arguments to save on variables - # pylint: disable=too-many-locals env = get_jinja_env() templates = get_templates_for_contract_file(env) diff --git a/pypechain/templates/contract.py/base.py.jinja2 b/pypechain/templates/contract.py/base.py.jinja2 index c14c9fcd..8476c2f9 100644 --- a/pypechain/templates/contract.py/base.py.jinja2 +++ b/pypechain/templates/contract.py/base.py.jinja2 @@ -20,6 +20,10 @@ https://github.com/delvtech/pypechain""" # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from dataclasses import fields, is_dataclass diff --git a/pypechain/templates/contract.py/contract.py.jinja2 b/pypechain/templates/contract.py/contract.py.jinja2 index e4abc858..0098a92d 100644 --- a/pypechain/templates/contract.py/contract.py.jinja2 +++ b/pypechain/templates/contract.py/contract.py.jinja2 @@ -122,7 +122,9 @@ class {{contract_name}}Contract(Contract): """ contract = super().factory(w3, class_name, **kwargs) contract.functions = {{contract_name}}ContractFunctions({{contract_name | lower}}_abi, w3, None) + {% if has_errors -%} contract.errors = {{contract_name}}ContractErrors() + {%- endif %} return contract diff --git a/pypechain/test/deployment/types/ConstructorNoArgsContract.py b/pypechain/test/deployment/types/ConstructorNoArgsContract.py index 13b61673..c37c663b 100644 --- a/pypechain/test/deployment/types/ConstructorNoArgsContract.py +++ b/pypechain/test/deployment/types/ConstructorNoArgsContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, Type, cast diff --git a/pypechain/test/deployment/types/ConstructorWithArgsContract.py b/pypechain/test/deployment/types/ConstructorWithArgsContract.py index cbac0d52..d17f4dc2 100644 --- a/pypechain/test/deployment/types/ConstructorWithArgsContract.py +++ b/pypechain/test/deployment/types/ConstructorWithArgsContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, NamedTuple, Type, cast diff --git a/pypechain/test/deployment/types/ConstructorWithStructArgsContract.py b/pypechain/test/deployment/types/ConstructorWithStructArgsContract.py index bd5da072..0a116c00 100644 --- a/pypechain/test/deployment/types/ConstructorWithStructArgsContract.py +++ b/pypechain/test/deployment/types/ConstructorWithStructArgsContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, NamedTuple, Type, cast diff --git a/pypechain/test/deployment/types/NoConstructorContract.py b/pypechain/test/deployment/types/NoConstructorContract.py index 8df80e7f..35969f3c 100644 --- a/pypechain/test/deployment/types/NoConstructorContract.py +++ b/pypechain/test/deployment/types/NoConstructorContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, Type, cast diff --git a/pypechain/test/deployment/types/utilities.py b/pypechain/test/deployment/types/utilities.py index 6d40773e..2c9a2737 100644 --- a/pypechain/test/deployment/types/utilities.py +++ b/pypechain/test/deployment/types/utilities.py @@ -8,6 +8,9 @@ from dataclasses import fields, is_dataclass from typing import Any, Tuple, TypeVar, cast +from eth_utils.abi import collapse_if_tuple +from web3.types import ABIFunction + T = TypeVar("T") @@ -108,3 +111,27 @@ def rename_returned_types( # cover case of single return value converted_value = tuple_to_dataclass(return_types, structs, raw_values) return converted_value + + +def get_abi_input_types(abi: ABIFunction) -> list[str]: + """Gets all the solidity input types for a function or error. + + Cribbed from web3._utils.abi.py file. + + Parameters + ---------- + + abi: ABIFunction + The ABIFunction or ABIError that we want to get input types for. + + Returns + ------- + list[str] + A list of solidity input types. + + """ + + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): + return [] + else: + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/errors/test_errors.py b/pypechain/test/errors/test_errors.py index a03146f3..ace92c1f 100644 --- a/pypechain/test/errors/test_errors.py +++ b/pypechain/test/errors/test_errors.py @@ -70,7 +70,7 @@ def test_error_two(self, w3): abi_codec = ABICodec(default_registry) decoded = abi_codec.decode(types, HexBytes(data)) assert decoded == ( - "I will not pledge allegiance to Bart. I will not pledge allegiance to Bart. I will not pledge allegiance to Bart.", + "I will not pledge allegiance to Bart. I will not pledge allegiance to Bart. I will not pledge allegiance to Bart.", # pylint: disable=line-too-long "0x0000000000000000000000000000000000000000", 255, ) diff --git a/pypechain/test/errors/types/ErrorsContract.py b/pypechain/test/errors/types/ErrorsContract.py index 9d6a1291..dbc749f8 100644 --- a/pypechain/test/errors/types/ErrorsContract.py +++ b/pypechain/test/errors/types/ErrorsContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, Type, cast diff --git a/pypechain/test/events/types/EventsContract.py b/pypechain/test/events/types/EventsContract.py index 7180d13d..c6d2b0cd 100644 --- a/pypechain/test/events/types/EventsContract.py +++ b/pypechain/test/events/types/EventsContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, Iterable, Sequence, Type, cast diff --git a/pypechain/test/events/types/utilities.py b/pypechain/test/events/types/utilities.py index 6d40773e..2c9a2737 100644 --- a/pypechain/test/events/types/utilities.py +++ b/pypechain/test/events/types/utilities.py @@ -8,6 +8,9 @@ from dataclasses import fields, is_dataclass from typing import Any, Tuple, TypeVar, cast +from eth_utils.abi import collapse_if_tuple +from web3.types import ABIFunction + T = TypeVar("T") @@ -108,3 +111,27 @@ def rename_returned_types( # cover case of single return value converted_value = tuple_to_dataclass(return_types, structs, raw_values) return converted_value + + +def get_abi_input_types(abi: ABIFunction) -> list[str]: + """Gets all the solidity input types for a function or error. + + Cribbed from web3._utils.abi.py file. + + Parameters + ---------- + + abi: ABIFunction + The ABIFunction or ABIError that we want to get input types for. + + Returns + ------- + list[str] + A list of solidity input types. + + """ + + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): + return [] + else: + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/overloading/types/OverloadedMethodsContract.py b/pypechain/test/overloading/types/OverloadedMethodsContract.py index 207deaa0..27587ef1 100644 --- a/pypechain/test/overloading/types/OverloadedMethodsContract.py +++ b/pypechain/test/overloading/types/OverloadedMethodsContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, NamedTuple, Type, cast, overload diff --git a/pypechain/test/overloading/types/utilities.py b/pypechain/test/overloading/types/utilities.py index 6d40773e..2c9a2737 100644 --- a/pypechain/test/overloading/types/utilities.py +++ b/pypechain/test/overloading/types/utilities.py @@ -8,6 +8,9 @@ from dataclasses import fields, is_dataclass from typing import Any, Tuple, TypeVar, cast +from eth_utils.abi import collapse_if_tuple +from web3.types import ABIFunction + T = TypeVar("T") @@ -108,3 +111,27 @@ def rename_returned_types( # cover case of single return value converted_value = tuple_to_dataclass(return_types, structs, raw_values) return converted_value + + +def get_abi_input_types(abi: ABIFunction) -> list[str]: + """Gets all the solidity input types for a function or error. + + Cribbed from web3._utils.abi.py file. + + Parameters + ---------- + + abi: ABIFunction + The ABIFunction or ABIError that we want to get input types for. + + Returns + ------- + list[str] + A list of solidity input types. + + """ + + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): + return [] + else: + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/return_types/types/ReturnTypesContract.py b/pypechain/test/return_types/types/ReturnTypesContract.py index dfd5a2cb..6ec8a6d6 100644 --- a/pypechain/test/return_types/types/ReturnTypesContract.py +++ b/pypechain/test/return_types/types/ReturnTypesContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, NamedTuple, Type, cast diff --git a/pypechain/test/return_types/types/utilities.py b/pypechain/test/return_types/types/utilities.py index 6d40773e..2c9a2737 100644 --- a/pypechain/test/return_types/types/utilities.py +++ b/pypechain/test/return_types/types/utilities.py @@ -8,6 +8,9 @@ from dataclasses import fields, is_dataclass from typing import Any, Tuple, TypeVar, cast +from eth_utils.abi import collapse_if_tuple +from web3.types import ABIFunction + T = TypeVar("T") @@ -108,3 +111,27 @@ def rename_returned_types( # cover case of single return value converted_value = tuple_to_dataclass(return_types, structs, raw_values) return converted_value + + +def get_abi_input_types(abi: ABIFunction) -> list[str]: + """Gets all the solidity input types for a function or error. + + Cribbed from web3._utils.abi.py file. + + Parameters + ---------- + + abi: ABIFunction + The ABIFunction or ABIError that we want to get input types for. + + Returns + ------- + list[str] + A list of solidity input types. + + """ + + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): + return [] + else: + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/structs/types/StructsAContract.py b/pypechain/test/structs/types/StructsAContract.py index f47efaa6..4e7b70a6 100644 --- a/pypechain/test/structs/types/StructsAContract.py +++ b/pypechain/test/structs/types/StructsAContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, Type, cast diff --git a/pypechain/test/structs/types/StructsBContract.py b/pypechain/test/structs/types/StructsBContract.py index 772e60b6..84ab31a5 100644 --- a/pypechain/test/structs/types/StructsBContract.py +++ b/pypechain/test/structs/types/StructsBContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, Type, cast diff --git a/pypechain/test/structs/types/StructsCContract.py b/pypechain/test/structs/types/StructsCContract.py index a986da07..5a65eae9 100644 --- a/pypechain/test/structs/types/StructsCContract.py +++ b/pypechain/test/structs/types/StructsCContract.py @@ -20,6 +20,10 @@ # methods are overriden with specific arguments instead of generic *args, **kwargs # pylint: disable=arguments-differ +# consumers have too many opinions on line length +# pylint: disable=line-too-long + + from __future__ import annotations from typing import Any, Type, cast diff --git a/pypechain/test/structs/types/utilities.py b/pypechain/test/structs/types/utilities.py index 6d40773e..2c9a2737 100644 --- a/pypechain/test/structs/types/utilities.py +++ b/pypechain/test/structs/types/utilities.py @@ -8,6 +8,9 @@ from dataclasses import fields, is_dataclass from typing import Any, Tuple, TypeVar, cast +from eth_utils.abi import collapse_if_tuple +from web3.types import ABIFunction + T = TypeVar("T") @@ -108,3 +111,27 @@ def rename_returned_types( # cover case of single return value converted_value = tuple_to_dataclass(return_types, structs, raw_values) return converted_value + + +def get_abi_input_types(abi: ABIFunction) -> list[str]: + """Gets all the solidity input types for a function or error. + + Cribbed from web3._utils.abi.py file. + + Parameters + ---------- + + abi: ABIFunction + The ABIFunction or ABIError that we want to get input types for. + + Returns + ------- + list[str] + A list of solidity input types. + + """ + + if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): + return [] + else: + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] From f34bf9a821fac6cc8de4f96c203f62f12d3c77f4 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Thu, 1 Feb 2024 15:12:13 -0500 Subject: [PATCH 9/9] lint fix --- example/types/utilities.py | 3 +-- pypechain/templates/utilities.py | 3 +-- pypechain/test/deployment/types/utilities.py | 3 +-- pypechain/test/errors/types/utilities.py | 3 +-- pypechain/test/events/types/utilities.py | 3 +-- pypechain/test/overloading/types/utilities.py | 3 +-- pypechain/test/return_types/types/utilities.py | 3 +-- pypechain/test/structs/types/utilities.py | 3 +-- 8 files changed, 8 insertions(+), 16 deletions(-) diff --git a/example/types/utilities.py b/example/types/utilities.py index 2c9a2737..00b87d9d 100644 --- a/example/types/utilities.py +++ b/example/types/utilities.py @@ -133,5 +133,4 @@ def get_abi_input_types(abi: ABIFunction) -> list[str]: if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] - else: - return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/templates/utilities.py b/pypechain/templates/utilities.py index 2c9a2737..00b87d9d 100644 --- a/pypechain/templates/utilities.py +++ b/pypechain/templates/utilities.py @@ -133,5 +133,4 @@ def get_abi_input_types(abi: ABIFunction) -> list[str]: if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] - else: - return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/deployment/types/utilities.py b/pypechain/test/deployment/types/utilities.py index 2c9a2737..00b87d9d 100644 --- a/pypechain/test/deployment/types/utilities.py +++ b/pypechain/test/deployment/types/utilities.py @@ -133,5 +133,4 @@ def get_abi_input_types(abi: ABIFunction) -> list[str]: if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] - else: - return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/errors/types/utilities.py b/pypechain/test/errors/types/utilities.py index 2c9a2737..00b87d9d 100644 --- a/pypechain/test/errors/types/utilities.py +++ b/pypechain/test/errors/types/utilities.py @@ -133,5 +133,4 @@ def get_abi_input_types(abi: ABIFunction) -> list[str]: if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] - else: - return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/events/types/utilities.py b/pypechain/test/events/types/utilities.py index 2c9a2737..00b87d9d 100644 --- a/pypechain/test/events/types/utilities.py +++ b/pypechain/test/events/types/utilities.py @@ -133,5 +133,4 @@ def get_abi_input_types(abi: ABIFunction) -> list[str]: if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] - else: - return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/overloading/types/utilities.py b/pypechain/test/overloading/types/utilities.py index 2c9a2737..00b87d9d 100644 --- a/pypechain/test/overloading/types/utilities.py +++ b/pypechain/test/overloading/types/utilities.py @@ -133,5 +133,4 @@ def get_abi_input_types(abi: ABIFunction) -> list[str]: if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] - else: - return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/return_types/types/utilities.py b/pypechain/test/return_types/types/utilities.py index 2c9a2737..00b87d9d 100644 --- a/pypechain/test/return_types/types/utilities.py +++ b/pypechain/test/return_types/types/utilities.py @@ -133,5 +133,4 @@ def get_abi_input_types(abi: ABIFunction) -> list[str]: if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] - else: - return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] diff --git a/pypechain/test/structs/types/utilities.py b/pypechain/test/structs/types/utilities.py index 2c9a2737..00b87d9d 100644 --- a/pypechain/test/structs/types/utilities.py +++ b/pypechain/test/structs/types/utilities.py @@ -133,5 +133,4 @@ def get_abi_input_types(abi: ABIFunction) -> list[str]: if "inputs" not in abi and (abi.get("type") == "fallback" or abi.get("type") == "receive"): return [] - else: - return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])] + return [collapse_if_tuple(cast(dict[str, Any], arg)) for arg in abi.get("inputs", [])]