From 4eb07678343fc78392c32c545d1cba646d0e6a5b Mon Sep 17 00:00:00 2001 From: fabioberger Date: Thu, 1 Aug 2019 14:56:23 +0200 Subject: [PATCH 1/2] Update deps in private packages --- packages/instant/package.json | 10 +- packages/monorepo-scripts/package.json | 2 +- .../python-contract-wrappers/package.json | 2 +- packages/testnet-faucets/package.json | 6 +- packages/website/package.json | 6 +- yarn.lock | 131 +++++++++--------- 6 files changed, 82 insertions(+), 75 deletions(-) diff --git a/packages/instant/package.json b/packages/instant/package.json index 6e783a5451..ea16a2ef1a 100644 --- a/packages/instant/package.json +++ b/packages/instant/package.json @@ -44,14 +44,14 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/instant/README.md", "dependencies": { "@0x/assert": "^2.1.0", - "@0x/asset-buyer": "6.1.4", + "@0x/asset-buyer": "6.1.8", "@0x/json-schemas": "^3.0.11", "@0x/order-utils": "^8.2.2", - "@0x/subproviders": "^4.1.2", + "@0x/subproviders": "^5.0.0", "@0x/types": "^2.4.1", - "@0x/typescript-typings": "^4.2.3", - "@0x/utils": "^4.4.1", - "@0x/web3-wrapper": "^6.0.8", + "@0x/typescript-typings": "^4.2.4", + "@0x/utils": "^4.4.2", + "@0x/web3-wrapper": "^6.0.9", "babel-runtime": "^6.26.0", "bowser": "^1.9.4", "copy-to-clipboard": "^3.0.8", diff --git a/packages/monorepo-scripts/package.json b/packages/monorepo-scripts/package.json index 352d2fa59a..69fe1babe8 100644 --- a/packages/monorepo-scripts/package.json +++ b/packages/monorepo-scripts/package.json @@ -49,7 +49,7 @@ }, "dependencies": { "@0x/types": "^2.4.1", - "@0x/utils": "^4.4.1", + "@0x/utils": "^4.4.2", "@lerna/batch-packages": "^3.0.0-beta.18", "@types/depcheck": "^0.6.0", "async-child-process": "^1.1.1", diff --git a/packages/python-contract-wrappers/package.json b/packages/python-contract-wrappers/package.json index 4d27dcd5ce..7773577725 100644 --- a/packages/python-contract-wrappers/package.json +++ b/packages/python-contract-wrappers/package.json @@ -25,7 +25,7 @@ }, "homepage": "https://github.com/0xProject/0x-monorepo/packages/python-contract-wrappers/README.md", "devDependencies": { - "@0x/abi-gen": "^3.1.1", + "@0x/abi-gen": "^3.1.2", "shx": "^0.2.2" }, "publishConfig": { diff --git a/packages/testnet-faucets/package.json b/packages/testnet-faucets/package.json index 944941f560..2aab54c149 100644 --- a/packages/testnet-faucets/package.json +++ b/packages/testnet-faucets/package.json @@ -20,10 +20,10 @@ "license": "Apache-2.0", "dependencies": { "0x.js": "^6.0.12", - "@0x/subproviders": "^4.1.2", + "@0x/subproviders": "^5.0.0", "@0x/typescript-typings": "^4.2.4", - "@0x/utils": "^4.4.1", - "@0x/web3-wrapper": "^6.0.8", + "@0x/utils": "^4.4.2", + "@0x/web3-wrapper": "^6.0.9", "body-parser": "^1.17.1", "ethereum-types": "^2.1.4", "ethereumjs-tx": "^1.3.5", diff --git a/packages/website/package.json b/packages/website/package.json index 57ffc3cf48..4d952453d2 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -26,11 +26,11 @@ "@0x/contract-wrappers": "^9.1.7", "@0x/json-schemas": "^3.0.11", "@0x/order-utils": "^8.2.2", - "@0x/subproviders": "^4.1.2", + "@0x/subproviders": "^5.0.0", "@0x/types": "^2.4.1", "@0x/typescript-typings": "^4.2.4", - "@0x/utils": "^4.4.1", - "@0x/web3-wrapper": "^6.0.8", + "@0x/utils": "^4.4.2", + "@0x/web3-wrapper": "^6.0.9", "@reach/dialog": "^0.1.2", "@types/react-lazyload": "^2.3.1", "@types/react-loadable": "^5.4.2", diff --git a/yarn.lock b/yarn.lock index 20920898ad..0652d54366 100644 --- a/yarn.lock +++ b/yarn.lock @@ -655,21 +655,22 @@ dependencies: "@0x/base-contract" "^5.1.0" -"@0x/asset-buyer@6.1.4": - version "6.1.4" - resolved "https://registry.yarnpkg.com/@0x/asset-buyer/-/asset-buyer-6.1.4.tgz#ea863b860fbae6f633846bdcf23cacbb345aefd1" +"@0x/asset-buyer@6.1.8": + version "6.1.8" + resolved "https://registry.yarnpkg.com/@0x/asset-buyer/-/asset-buyer-6.1.8.tgz#71f6abb366e89e62457c256644edb37e12113e94" + integrity sha512-JOMu38EsQexDVyzOGYJamlLDigVoGOBNHAVYIkPn4r3SLY75Shcxlb9AhUfKEMDPv3kjk70r0Iy1DmIh4HBjdA== dependencies: - "@0x/assert" "^2.0.10" - "@0x/connect" "^5.0.9" - "@0x/contract-wrappers" "^9.1.3" - "@0x/json-schemas" "^3.0.10" - "@0x/order-utils" "^8.1.0" - "@0x/subproviders" "^4.0.6" - "@0x/types" "^2.2.2" - "@0x/typescript-typings" "^4.2.2" - "@0x/utils" "^4.3.3" - "@0x/web3-wrapper" "^6.0.6" - ethereum-types "^2.1.2" + "@0x/assert" "^2.1.0" + "@0x/connect" "^5.0.13" + "@0x/contract-wrappers" "^9.1.7" + "@0x/json-schemas" "^3.0.11" + "@0x/order-utils" "^8.2.2" + "@0x/subproviders" "^4.1.1" + "@0x/types" "^2.4.0" + "@0x/typescript-typings" "^4.2.3" + "@0x/utils" "^4.4.0" + "@0x/web3-wrapper" "^6.0.7" + ethereum-types "^2.1.3" lodash "^4.17.11" "@0x/base-contract@^4.0.1", "@0x/base-contract@^4.0.3": @@ -716,7 +717,7 @@ lodash "^4.17.11" uuid "^3.3.2" -"@0x/contract-wrappers@^9.1.3", "@0x/contract-wrappers@^9.1.7": +"@0x/contract-wrappers@^9.1.7": version "9.1.8" resolved "https://registry.yarnpkg.com/@0x/contract-wrappers/-/contract-wrappers-9.1.8.tgz#5923d35af3e4b442a57d02f74e02620b2d5b1356" integrity sha512-+qx0lwDAuTitP8sXA4bxRS+W81i5TaXWziD/Fcraj40XGeuHFnRXVY5FEl9V7TC0sEDyvnnF2VFc5Y+v2CnYVQ== @@ -741,42 +742,6 @@ lodash "^4.17.11" uuid "^3.3.2" -"@0x/contracts-asset-proxy@^2.2.4": - version "2.2.3" - dependencies: - "@0x/base-contract" "^5.3.0" - "@0x/contracts-erc1155" "^1.1.11" - "@0x/contracts-erc20" "^2.2.10" - "@0x/contracts-erc721" "^2.1.11" - "@0x/contracts-utils" "^3.2.0" - "@0x/order-utils" "^8.2.4" - "@0x/types" "^2.4.1" - "@0x/typescript-typings" "^4.2.4" - "@0x/utils" "^4.4.2" - "@0x/web3-wrapper" "^6.0.9" - ethereum-types "^2.1.4" - ethereumjs-util "^5.1.1" - lodash "^4.17.11" - -"@0x/contracts-exchange@^2.1.10": - version "2.1.9" - dependencies: - "@0x/base-contract" "^5.3.0" - "@0x/contracts-asset-proxy" "^2.2.4" - "@0x/contracts-erc1155" "^1.1.11" - "@0x/contracts-erc20" "^2.2.10" - "@0x/contracts-erc721" "^2.1.11" - "@0x/contracts-exchange-libs" "^3.0.4" - "@0x/contracts-utils" "^3.2.0" - "@0x/order-utils" "^8.2.4" - "@0x/types" "^2.4.1" - "@0x/typescript-typings" "^4.2.4" - "@0x/utils" "^4.4.2" - "@0x/web3-wrapper" "^6.0.9" - ethereum-types "^2.1.4" - ethereumjs-util "^5.1.1" - lodash "^4.17.11" - "@0x/contracts-utils@2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@0x/contracts-utils/-/contracts-utils-2.0.1.tgz#32e298ab5e6edb045c37294063ff928b629db0a4" @@ -865,7 +830,7 @@ ethers "~4.0.4" lodash "^4.17.11" -"@0x/subproviders@^4.0.4", "@0x/subproviders@^4.0.6", "@0x/subproviders@^4.1.2": +"@0x/subproviders@^4.0.4", "@0x/subproviders@^4.1.1": version "4.1.2" resolved "https://registry.yarnpkg.com/@0x/subproviders/-/subproviders-4.1.2.tgz#ab7bb0f482b11ccb4615fb5dd8ca85199cd0ae23" integrity sha512-PaK/+cC6+o3glVITnBdb/AN/ej7ulfr49KGftNRATB8Y/yI6Xa3adqgFvDh7jiKBoB/auTRFQ/TabQTcieKl6g== @@ -2040,8 +2005,9 @@ "@types/react" "*" "@types/react-dom@^16.0.6": - version "16.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.11.tgz#bd10ccb0d9260343f4b9a49d4f7a8330a5c1f081" + version "16.8.5" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.5.tgz#3e3f4d99199391a7fb40aa3a155c8dd99b899cbd" + integrity sha512-idCEjROZ2cqh29+trmTmZhsBAUNQuYrF92JHKzZ5+aiFM1mlSk3bb23CK7HhYuOY75Apgap5y2jTyHzaM2AJGA== dependencies: "@types/react" "*" @@ -2080,6 +2046,7 @@ "@types/react-redux@^4.4.37": version "4.4.47" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-4.4.47.tgz#12af1677116e08d413fe2620d0a85560c8a0536e" + integrity sha512-wyFTmLtEymHCjOmVVvsbNqJaGM9Q0x6sZTQfz4XkDj06P8Xe+ys9wKSQHx2Jt9J5Mi7HZnGcJaMFktn60sXluw== dependencies: "@types/react" "*" redux "^3.6.0" @@ -4317,7 +4284,7 @@ buffer-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" -buffer-from@1.x, buffer-from@^1.1.0: +buffer-from@1.x, buffer-from@>=1.1, buffer-from@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -9258,6 +9225,13 @@ hoist-non-react-statics@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" +hoist-non-react-statics@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" + integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== + dependencies: + react-is "^16.7.0" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -11389,10 +11363,15 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -lodash-es@^4.17.5, lodash-es@^4.2.1: +lodash-es@^4.17.5: version "4.17.8" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.8.tgz#6fa8c8c5d337481df0bdf1c0d899d42473121e45" +lodash-es@^4.2.1: + version "4.17.15" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" + integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== + lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" @@ -14856,11 +14835,16 @@ react-is@^16.6.0: version "16.6.3" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.6.3.tgz#d2d7462fcfcbe6ec0da56ad69047e47e56e7eac0" +react-is@^16.7.0: + version "16.8.6" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" + integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== + react-lazyload@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/react-lazyload/-/react-lazyload-2.3.0.tgz#ccb134223012447074a96543954f44b055dc6185" -react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.2, react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" @@ -14900,7 +14884,20 @@ react-popper@^1.0.0-beta.6: typed-styles "^0.0.5" warning "^3.0.0" -react-redux@^5.0.3, react-redux@^5.0.7: +react-redux@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f" + integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg== + dependencies: + "@babel/runtime" "^7.1.2" + hoist-non-react-statics "^3.1.0" + invariant "^2.2.4" + loose-envify "^1.1.0" + prop-types "^15.6.1" + react-is "^16.6.0" + react-lifecycles-compat "^3.0.0" + +react-redux@^5.0.7: version "5.0.7" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" dependencies: @@ -15304,8 +15301,9 @@ reduce-function-call@^1.0.1: balanced-match "^0.4.2" redux-devtools-extension@^2.13.2: - version "2.13.2" - resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.2.tgz#e0f9a8e8dfca7c17be92c7124958a3b94eb2911d" + version "2.13.8" + resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" + integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== redux-devtools-extension@^2.13.5: version "2.13.5" @@ -15321,6 +15319,7 @@ redux@*, redux@^4.0.0: redux@^3.6.0: version "3.7.2" resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" + integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== dependencies: lodash "^4.2.1" lodash-es "^4.2.1" @@ -15806,16 +15805,19 @@ rollbar-sourcemap-webpack-plugin@^2.4.0: verror "^1.6.1" rollbar@^2.4.7: - version "2.4.7" - resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.4.7.tgz#9b1de1a0fab6b6e63fcfcd322c26081a1d8242e8" + version "2.10.0" + resolved "https://registry.yarnpkg.com/rollbar/-/rollbar-2.10.0.tgz#a18c333eb45ebd21be128afdf9bf3a0093be7218" + integrity sha512-Ifr+KB8cXsaE9Lae3o2X7jx+WAAHMxul44Bj+P+0uGxbTO4TcHFRloUWgPVtmHxuFjpDchHbhwMwMicTseEb1Q== dependencies: async "~1.2.1" + buffer-from ">=1.1" console-polyfill "0.3.0" debug "2.6.9" error-stack-parser "1.3.3" json-stringify-safe "~5.0.0" lru-cache "~2.2.1" request-ip "~2.0.1" + source-map ">=0.5.0" uuid "3.0.x" optionalDependencies: decache "^3.0.5" @@ -16584,6 +16586,11 @@ source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" +source-map@>=0.5.0: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" From 57318c00416e53872a4ba485fe3968424a1d8b6c Mon Sep 17 00:00:00 2001 From: "F. Eugene Aumson" Date: Thu, 1 Aug 2019 12:47:52 -0400 Subject: [PATCH 2/2] Python nested wrapper methods & estimate_gas (#1996) * git rm unnecessary .gitkeep file * After all Pytest runs, show short test summary * abi-gen/Py: facilitate inlining of parameter lists Effectively, stopped new-lines from being introduced by calls to the `params` and `typed_params` partials. * abi-gen: simple Py wrapper test for local dev'ment * abi-gen/Py: stop gen'ing ValidatorBase * abi-gen/Py: declare abi() wrapper method in Base * abi-gen/Py: methods as classes to ease call/sendTx Represent methods as classes in order to faciliate access to a method's different operations (call, send_transaction, etc). * contract_wrappers.py: make Base methods public Changed some methods on BaseContractWrapper to be public. * contract_wrappers.py: remove unused method * contract_wrappers.py: extract method * abi-gen/Py: inline method * contract_wrappers.py: fix bug in call() We were passing transaction parameters through to sendTransaction() invocations, but not to call() invocations. * abi-gen/Py: remove `view_only` param to call/tx Formerly, in the BaseContractWrapper, there was just one function used for both eth_call and eth_sendTransaction, and you would distinguish between the two by specifying `view_only=True` when you wanted a call. This commit defines a method dedicated to executing an eth_call, and leaves the old method behind, with the `view_only` param removed, to be used for eth_sendTransaction. * abi-gen/Py: rename method * contract_wrappers/Py: simplify web3 func handling Pass web3 function instance into generated wrapper method class constructor, rather than having that class obtain it upon each method call. Really this is just an elimination of a call to BaseContractWrapper.contract_instance(), which will be removed completely in a shortly-upcoming commit. * contract_wrappers.py: inline method Inline and remove method BaseContractWrapper.contract_instance(). * contract_wrappers.py: pass Validator to *Method Pass a ValidatorBase instance into construction of the contract method classes, *Method, to eliminate another dependency on the containing contract object, which will be eliminated completely in a shortly-upcoming commit. * abi-gen/Py: BaseContractWrapper -> ContractMethod Change the fundamental thing-to-be-wrapped from the contract to the method. Since the named method classes were introduced (in a previous commit), and since the operations contained within the Base are predominantly focused on supporting method calls more than anything else, it makes more intuitive sense to provide a base for the methods than for the contract. With this change, the method classes no longer require a contract object to be passed to their constructors. The contract members that the methods were utilizing are now passed directly to the method constructor. * contract_wrappers.py: rename module to bases... ...from _base_contract_wrapper. The old name hasn't made sense since ValidatorBase was moved into that module, and definitely doesn't make sense now that the fundamental thing-to-be-wrapped has changed from the contract to the method. Also renamed to make it public (removed the leading underscore) since we're generating code that will depend on it. * abi-gen/Py: clarify call/sendTx docstrings * abi-gen/Py: adjust whitespace * contract_wrappers.py: inline method * abi-gen/Py: rename class ValidatorBase... ...to just Validator. It's in the "bases" module, which provides the context needed in order to know it's a base class * python-packages: fix silent failures of ./parallel * contract_wrappers.py: remove private_key support Having this present was overcomplicating interfaces. And it was untested (and not readily working when testing was attempted). And it only provided a thin layer of convenience, which a client could easily code up themselves. * contract_wrappers.py: inline method * contract_wrappers.py: rm unused member variables * contract_wrappers.py: rm unnecessary instance var * abi-gen/Py: add estimate_gas to gen'd methods * update CHANGELOG.json --- .../Python/contract.handlebars | 73 +- .../Python/partials/call.handlebars | 54 - .../Python/partials/event.handlebars | 6 +- .../Python/partials/method_class.handlebars | 76 ++ .../Python/partials/params.handlebars | 4 +- .../Python/partials/typed_params.handlebars | 4 +- packages/abi-gen/CHANGELOG.json | 25 +- packages/abi-gen/package.json | 1 + packages/abi-gen/src/index.ts | 4 + .../python/abi_gen_dummy/__init__.py | 1021 ++++++++++------- .../python/lib_dummy/__init__.py | 58 +- .../python/test_lib_dummy/__init__.py | 204 ++-- python-packages/contract_addresses/setup.py | 3 +- python-packages/contract_artifacts/setup.py | 3 +- python-packages/contract_wrappers/setup.cfg | 5 + python-packages/contract_wrappers/setup.py | 3 +- .../src/zero_ex/contract_wrappers/__init__.py | 38 +- .../_base_contract_wrapper.py | 136 --- .../src/zero_ex/contract_wrappers/bases.py | 66 ++ .../contract_wrappers/exchange/.gitkeep | 0 .../contract_wrappers/exchange/validator.py | 13 +- .../stubs/eth_utils/__init__.pyi | 6 +- .../contract_wrappers/stubs/web3/contract.pyi | 5 + .../stubs/web3/utils/datatypes.pyi | 7 + .../test/test_base_contract_method.py | 15 + .../test/test_base_contract_wrapper.py | 48 - .../test/test_erc20_wrapper.py | 16 +- .../test/test_exchange_wrapper.py | 4 +- python-packages/json_schemas/setup.py | 3 +- python-packages/middlewares/setup.py | 3 +- python-packages/order_utils/setup.py | 3 +- python-packages/parallel | 21 +- python-packages/sra_client/setup.py | 3 +- .../src/zero_ex/sra_client/__init__.py | 4 +- 34 files changed, 1070 insertions(+), 865 deletions(-) delete mode 100644 packages/abi-gen-templates/Python/partials/call.handlebars create mode 100644 packages/abi-gen-templates/Python/partials/method_class.handlebars create mode 100644 python-packages/contract_wrappers/setup.cfg delete mode 100644 python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py create mode 100644 python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py delete mode 100644 python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/.gitkeep create mode 100644 python-packages/contract_wrappers/stubs/web3/contract.pyi create mode 100644 python-packages/contract_wrappers/test/test_base_contract_method.py delete mode 100644 python-packages/contract_wrappers/test/test_base_contract_wrapper.py diff --git a/packages/abi-gen-templates/Python/contract.handlebars b/packages/abi-gen-templates/Python/contract.handlebars index 25eaa2980b..a71adfd9e1 100644 --- a/packages/abi-gen-templates/Python/contract.handlebars +++ b/packages/abi-gen-templates/Python/contract.handlebars @@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import Union, ) +from eth_utils import to_checksum_address from mypy_extensions import TypedDict # pylint: disable=unused-import from hexbytes import HexBytes +from web3 import Web3 +from web3.contract import ContractFunction from web3.datastructures import AttributeDict from web3.providers.base import BaseProvider -from zero_ex.contract_wrappers._base_contract_wrapper import BaseContractWrapper +from zero_ex.contract_wrappers.bases import ContractMethod, Validator from zero_ex.contract_wrappers.tx_params import TxParams -class {{contractName}}ValidatorBase: - """Base class for validating inputs to {{contractName}} methods.""" - def __init__( - self, - provider: BaseProvider, - contract_address: str, - private_key: str = None, - ): - """Initialize the instance.""" - - def assert_valid( - self, method_name: str, parameter_name: str, argument_value: Any - ): - """Raise an exception if method input is not valid. - - :param method_name: Name of the method whose input is to be validated. - :param parameter_name: Name of the parameter whose input is to be - validated. - :param argument_value: Value of argument to parameter to be validated. - """ - - # Try to import a custom validator class definition; if there isn't one, # declare one that we can instantiate for the default argument to the # constructor for {{contractName}} below. @@ -53,56 +34,52 @@ try: ) except ImportError: - class {{contractName}}Validator({{contractName}}ValidatorBase): # type: ignore + class {{contractName}}Validator(Validator): # type: ignore """No-op input validator.""" {{tupleDefinitions ABIString}} +{{#each methods}} +{{> method_class contractName=../contractName}} +{{/each}} -# pylint: disable=too-many-public-methods -class {{contractName}}(BaseContractWrapper): +# pylint: disable=too-many-public-methods,too-many-instance-attributes +class {{contractName}}: """Wrapper class for {{contractName}} Solidity contract.{{docBytesIfNecessary ABIString}}""" +{{#each methods}} + {{toPythonIdentifier this.name}}: {{toPythonClassname this.name}}Method +{{/each}} def __init__( self, provider: BaseProvider, contract_address: str, validator: {{contractName}}Validator = None, - private_key: str = None, ): """Get an instance of wrapper for smart contract. :param provider: instance of :class:`web3.providers.base.BaseProvider` :param contract_address: where the contract has been deployed - :param private_key: If specified, transactions will be signed locally, - via Web3.py's `eth.account.signTransaction()`:code:, before being - sent via `eth.sendRawTransaction()`:code:. + :param validator: for validation of method inputs. """ - super().__init__( - provider=provider, - contract_address=contract_address, - private_key=private_key, - ) + self.contract_address = contract_address if not validator: - validator = {{contractName}}Validator(provider, contract_address, private_key) + validator = {{contractName}}Validator(provider, contract_address) - self.validator = validator + self._web3_eth = Web3( # type: ignore # pylint: disable=no-member + provider + ).eth - def _get_contract_instance(self, token_address): - """Get an instance of the smart contract at a specific address. + functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions - :returns: contract object - """ - return self._contract_instance( - address=token_address, abi={{contractName}}.abi() - ) -{{#each methods}} -{{> call contractName=../contractName}} -{{/each}} + {{#each methods}} + self.{{toPythonIdentifier this.name}} = {{toPythonClassname this.name}}Method(provider, contract_address, functions.{{this.name}}, validator) + + {{/each}} {{#each events}} -{{> event}} +{{> event contractName=../contractName}} {{/each}} @staticmethod diff --git a/packages/abi-gen-templates/Python/partials/call.handlebars b/packages/abi-gen-templates/Python/partials/call.handlebars deleted file mode 100644 index f0756937df..0000000000 --- a/packages/abi-gen-templates/Python/partials/call.handlebars +++ /dev/null @@ -1,54 +0,0 @@ - - def {{this.languageSpecificName}}( - self, - {{> typed_params inputs=inputs}} - tx_params: Optional[TxParams] = None, - {{^this.constant}} - view_only: bool = False, - {{/this.constant}} - ) -> {{> return_type outputs=outputs~}}: - """Execute underlying, same-named contract method. -{{sanitizeDevdocDetails this.name this.devdoc.details 8}}{{~#if this.devdoc.params~}}{{#each this.devdoc.params}} -{{makeParameterDocstringRole @key this 8}}{{/each}}{{/if}} - :param tx_params: transaction parameters - {{#if this.constant~}} - {{#if this.devdoc.return}} -{{makeReturnDocstringRole this.devdoc.return 8}}{{/if}} - {{else}} - :param view_only: whether to use transact() or call() - - :returns: if param `view_only`:code: is `True`:code:, then returns the - value returned from the underlying function; else returns the - transaction hash. - {{/if}} - """ - {{#each this.inputs}} - self.validator.assert_valid( - method_name='{{../name}}', - parameter_name='{{name}}', - argument_value={{toPythonIdentifier name}}, - ) - {{#if (equal type 'address')}} - {{toPythonIdentifier this.name}} = self._validate_and_checksum_address({{toPythonIdentifier this.name}}) - {{else if (equal type 'uint256')}} - # safeguard against fractional inputs - {{toPythonIdentifier this.name}} = int({{toPythonIdentifier this.name}}) - {{else if (equal type 'bytes')}} - {{toPythonIdentifier this.name}} = bytes.fromhex({{toPythonIdentifier this.name}}.decode("utf-8")) - {{else if (equal type 'bytes[]')}} - {{toPythonIdentifier this.name}} = [ - bytes.fromhex({{toPythonIdentifier this.name}}_element.decode("utf-8")) - for {{toPythonIdentifier this.name}}_element in {{toPythonIdentifier this.name}} - ] -{{/if}} -{{/each}} - func = self._get_contract_instance( - self.contract_address - ).functions.{{this.name}}( - {{> params}} - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only={{#if this.constant}}True{{else}}view_only{{/if}} - ) diff --git a/packages/abi-gen-templates/Python/partials/event.handlebars b/packages/abi-gen-templates/Python/partials/event.handlebars index 461f38b3ec..c33d9ce1b2 100644 --- a/packages/abi-gen-templates/Python/partials/event.handlebars +++ b/packages/abi-gen-templates/Python/partials/event.handlebars @@ -6,8 +6,4 @@ {{makeEventParameterDocstringRole name 8}} """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) - return ( - self._get_contract_instance(self.contract_address) - .events.{{name}}() - .processReceipt(tx_receipt) - ) + return self._web3_eth.contract(address=to_checksum_address(self.contract_address), abi={{contractName}}.abi()).events.{{name}}().processReceipt(tx_receipt) diff --git a/packages/abi-gen-templates/Python/partials/method_class.handlebars b/packages/abi-gen-templates/Python/partials/method_class.handlebars new file mode 100644 index 0000000000..e2abdf1958 --- /dev/null +++ b/packages/abi-gen-templates/Python/partials/method_class.handlebars @@ -0,0 +1,76 @@ + +class {{toPythonClassname this.name}}Method(ContractMethod): + """Various interfaces to the {{this.name}} method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + {{#if inputs}} + def validate_and_normalize_inputs(self, {{> typed_params inputs=inputs}}): + """Validate the inputs to the {{this.name}} method.""" + {{#each this.inputs}} + self.validator.assert_valid( + method_name='{{../name}}', + parameter_name='{{name}}', + argument_value={{toPythonIdentifier name}}, + ) + {{#if (equal type 'address')}} + {{toPythonIdentifier this.name}} = self.validate_and_checksum_address({{toPythonIdentifier this.name}}) + {{else if (equal type 'uint256')}} + # safeguard against fractional inputs + {{toPythonIdentifier this.name}} = int({{toPythonIdentifier this.name}}) + {{else if (equal type 'bytes')}} + {{toPythonIdentifier this.name}} = bytes.fromhex({{toPythonIdentifier this.name}}.decode("utf-8")) + {{else if (equal type 'bytes[]')}} + {{toPythonIdentifier this.name}} = [ + bytes.fromhex({{toPythonIdentifier this.name}}_element.decode("utf-8")) + for {{toPythonIdentifier this.name}}_element in {{toPythonIdentifier this.name}} + ] +{{/if}} +{{/each}} + return ({{> params }}) + + {{/if}} + def call(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> {{> return_type outputs=outputs type='call'~}}: + """Execute underlying contract method via eth_call. +{{sanitizeDevdocDetails this.name this.devdoc.details 8}}{{~#if this.devdoc.params~}}{{#each this.devdoc.params}} +{{makeParameterDocstringRole @key this 8}}{{/each}}{{/if}} + :param tx_params: transaction parameters + {{#if this.constant~}} + {{#if this.devdoc.return}} +{{makeReturnDocstringRole this.devdoc.return 8}}{{/if}} + {{else}} + :returns: the return value of the underlying method. + {{/if}} + """ + {{#if inputs}} + ({{> params }}) = self.validate_and_normalize_inputs({{> params}}) + {{/if}} + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method({{> params}}).call(tx_params.as_dict()) + + def send_transaction(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. +{{sanitizeDevdocDetails this.name this.devdoc.details 8}}{{~#if this.devdoc.params~}}{{#each this.devdoc.params}} +{{makeParameterDocstringRole @key this 8}}{{/each}}{{/if}} + :param tx_params: transaction parameters + {{#if this.constant~}} + {{#if this.devdoc.return}} +{{makeReturnDocstringRole this.devdoc.return 8}}{{/if}} + {{/if}} + """ + {{#if inputs}} + ({{> params }}) = self.validate_and_normalize_inputs({{> params}}) + {{/if}} + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method({{> params}}).transact(tx_params.as_dict()) + + def estimate_gas(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + {{#if inputs}} + ({{> params }}) = self.validate_and_normalize_inputs({{> params}}) + {{/if}} + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method({{> params}}).estimateGas(tx_params.as_dict()) diff --git a/packages/abi-gen-templates/Python/partials/params.handlebars b/packages/abi-gen-templates/Python/partials/params.handlebars index 1b2b86e6a5..98b3dfa702 100644 --- a/packages/abi-gen-templates/Python/partials/params.handlebars +++ b/packages/abi-gen-templates/Python/partials/params.handlebars @@ -1,3 +1,3 @@ {{#each inputs}} -{{toPythonIdentifier name}}{{#if @last}}{{else}},{{/if}} -{{/each}} +{{toPythonIdentifier name}}{{#if @last}}{{else}}, {{/if~}} +{{/each~}} diff --git a/packages/abi-gen-templates/Python/partials/typed_params.handlebars b/packages/abi-gen-templates/Python/partials/typed_params.handlebars index 640d1188e5..a67cd0c201 100644 --- a/packages/abi-gen-templates/Python/partials/typed_params.handlebars +++ b/packages/abi-gen-templates/Python/partials/typed_params.handlebars @@ -1,3 +1,3 @@ {{#each inputs}} -{{toPythonIdentifier name}}: {{#parameterType type components}}{{/parameterType}}, -{{/each}} +{{toPythonIdentifier name}}: {{#parameterType type components}}{{/parameterType}}{{^if @last}}, {{/if~}} +{{/each~}} diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index 4ea1b6c6ce..5ba78c5a37 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -1,12 +1,33 @@ [ { - "timestamp": 1564604963, + "version": "4.0.0", + "changes": [ + { + "note": "whitespace changes to generated Python code", + "pr": 1996 + }, + { + "note": "move Python Validator base class from generated code to common package", + "pr": 1996 + }, + { + "note": "Changed fundamental thing-to-be-wrapped from the contract to the contract method. That is, now there is a base contract method wrapper class rather than a base contract wrapper class, and individual contract methods are represented by named classes inheriting from that base, and the different operations on a method are now represented by a nested-object dot notation, ie, WrappedContract.ContractMethod.call() and WrappedContract.ContractMethod.send_transaction().", + "pr": 1996 + }, + { + "note": "added gas estimation functionality to contract methods", + "pr": 1996 + } + ] + }, + { "version": "3.1.2", "changes": [ { "note": "Dependencies updated" } - ] + ], + "timestamp": 1564604963 }, { "version": "3.1.1", diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index be33f6a38d..56a62d4cf5 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -23,6 +23,7 @@ "test_cli:clean": "rm -rf test-cli/output && rm -rf test-cli/test_typescript/lib", "test_cli:build": "tsc --project test-cli/tsconfig.json", "test_cli:run_mocha": "mocha --require source-map-support/register --require make-promises-safe test-cli/test_typescript/lib/**/*_test.js --timeout 100000 --bail --exit", + "test_cli:test_python": "black --check test-cli/output/python/**/__init__.py; test $? -le 1 # just make sure black can parse the output", "rebuild_and_test": "run-s build test", "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index f650c3553c..b103f6a805 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -225,6 +225,10 @@ function registerPythonHelpers(): void { } return ''; }); + Handlebars.registerHelper( + 'toPythonClassname', + (sourceName: string) => new Handlebars.SafeString(changeCase.pascal(sourceName)), + ); } if (args.language === 'TypeScript') { registerTypeScriptHelpers(); diff --git a/packages/abi-gen/test-cli/expected-output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/expected-output/python/abi_gen_dummy/__init__.py index 0b33e533be..79e6e79406 100644 --- a/packages/abi-gen/test-cli/expected-output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/expected-output/python/abi_gen_dummy/__init__.py @@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import Union, ) +from eth_utils import to_checksum_address from mypy_extensions import TypedDict # pylint: disable=unused-import from hexbytes import HexBytes +from web3 import Web3 +from web3.contract import ContractFunction from web3.datastructures import AttributeDict from web3.providers.base import BaseProvider -from zero_ex.contract_wrappers._base_contract_wrapper import BaseContractWrapper +from zero_ex.contract_wrappers.bases import ContractMethod, Validator from zero_ex.contract_wrappers.tx_params import TxParams -class AbiGenDummyValidatorBase: - """Base class for validating inputs to AbiGenDummy methods.""" - def __init__( - self, - provider: BaseProvider, - contract_address: str, - private_key: str = None, - ): - """Initialize the instance.""" - - def assert_valid( - self, method_name: str, parameter_name: str, argument_value: Any - ): - """Raise an exception if method input is not valid. - - :param method_name: Name of the method whose input is to be validated. - :param parameter_name: Name of the parameter whose input is to be - validated. - :param argument_value: Value of argument to parameter to be validated. - """ - - # Try to import a custom validator class definition; if there isn't one, # declare one that we can instantiate for the default argument to the # constructor for AbiGenDummy below. @@ -53,7 +34,7 @@ try: ) except ImportError: - class AbiGenDummyValidator(AbiGenDummyValidatorBase): # type: ignore + class AbiGenDummyValidator(Validator): # type: ignore """No-op input validator.""" @@ -102,81 +83,47 @@ class Tuple0xcf8ad995(TypedDict): aString: str -# pylint: disable=too-many-public-methods -class AbiGenDummy(BaseContractWrapper): - """Wrapper class for AbiGenDummy Solidity contract. +class SimpleRequireMethod(ContractMethod): + """Various interfaces to the simpleRequire method.""" - All method parameters of type `bytes`:code: should be encoded as UTF-8, - which can be accomplished via `str.encode("utf_8")`:code:. - """ + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function - def __init__( - self, - provider: BaseProvider, - contract_address: str, - validator: AbiGenDummyValidator = None, - private_key: str = None, - ): - """Get an instance of wrapper for smart contract. - - :param provider: instance of :class:`web3.providers.base.BaseProvider` - :param contract_address: where the contract has been deployed - :param private_key: If specified, transactions will be signed locally, - via Web3.py's `eth.account.signTransaction()`:code:, before being - sent via `eth.sendRawTransaction()`:code:. - """ - super().__init__( - provider=provider, - contract_address=contract_address, - private_key=private_key, - ) - - if not validator: - validator = AbiGenDummyValidator(provider, contract_address, private_key) - - self.validator = validator - - def _get_contract_instance(self, token_address): - """Get an instance of the smart contract at a specific address. - - :returns: contract object - """ - return self._contract_instance( - address=token_address, abi=AbiGenDummy.abi() - ) - - def simple_require( - self, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def call(self, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.simpleRequire( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) - def accepts_an_array_of_bytes( - self, - a: List[bytes], - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. - a method that accepts an array of bytes - - :param a: the array of bytes being accepted :param tx_params: transaction parameters """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class AcceptsAnArrayOfBytesMethod(ContractMethod): + """Various interfaces to the acceptsAnArrayOfBytes method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, a: List[bytes]): + """Validate the inputs to the acceptsAnArrayOfBytes method.""" self.validator.assert_valid( method_name='acceptsAnArrayOfBytes', parameter_name='a', @@ -186,32 +133,50 @@ class AbiGenDummy(BaseContractWrapper): bytes.fromhex(a_element.decode("utf-8")) for a_element in a ] - func = self._get_contract_instance( - self.contract_address - ).functions.acceptsAnArrayOfBytes( - a - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (a) - def withdraw( - self, - wad: int, - tx_params: Optional[TxParams] = None, - view_only: bool = False, - ) -> Union[None, Union[HexBytes, bytes]]: - """Execute underlying, same-named contract method. + def call(self, a: List[bytes], tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. + a method that accepts an array of bytes + + :param a: the array of bytes being accepted :param tx_params: transaction parameters - :param view_only: whether to use transact() or call() - :returns: if param `view_only`:code: is `True`:code:, then returns the - value returned from the underlying function; else returns the - transaction hash. """ + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).call(tx_params.as_dict()) + + def send_transaction(self, a: List[bytes], tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + a method that accepts an array of bytes + + :param a: the array of bytes being accepted + :param tx_params: transaction parameters + + """ + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).transact(tx_params.as_dict()) + + def estimate_gas(self, a: List[bytes], tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).estimateGas(tx_params.as_dict()) + +class WithdrawMethod(ContractMethod): + """Various interfaces to the withdraw method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, wad: int): + """Validate the inputs to the withdraw method.""" self.validator.assert_valid( method_name='withdraw', parameter_name='wad', @@ -219,30 +184,43 @@ class AbiGenDummy(BaseContractWrapper): ) # safeguard against fractional inputs wad = int(wad) - func = self._get_contract_instance( - self.contract_address - ).functions.withdraw( - wad - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=view_only - ) + return (wad) - def ecrecover_fn( - self, - _hash: bytes, - v: int, - r: bytes, - s: bytes, - tx_params: Optional[TxParams] = None, - ) -> str: - """Execute underlying, same-named contract method. + def call(self, wad: int, tx_params: Optional[TxParams] = None) -> Union[None, Union[HexBytes, bytes]]: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters - + :returns: the return value of the underlying method. """ + (wad) = self.validate_and_normalize_inputs(wad) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(wad).call(tx_params.as_dict()) + + def send_transaction(self, wad: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + """ + (wad) = self.validate_and_normalize_inputs(wad) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(wad).transact(tx_params.as_dict()) + + def estimate_gas(self, wad: int, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (wad) = self.validate_and_normalize_inputs(wad) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(wad).estimateGas(tx_params.as_dict()) + +class EcrecoverFnMethod(ContractMethod): + """Various interfaces to the ecrecoverFn method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, _hash: bytes, v: int, r: bytes, s: bytes): + """Validate the inputs to the ecrecoverFn method.""" self.validator.assert_valid( method_name='ecrecoverFn', parameter_name='hash', @@ -263,143 +241,218 @@ class AbiGenDummy(BaseContractWrapper): parameter_name='s', argument_value=s, ) - func = self._get_contract_instance( - self.contract_address - ).functions.ecrecoverFn( - _hash, - v, - r, - s - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (_hash, v, r, s) - def accepts_bytes( - self, - a: bytes, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def call(self, _hash: bytes, v: int, r: bytes, s: bytes, tx_params: Optional[TxParams] = None) -> str: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + (_hash, v, r, s) = self.validate_and_normalize_inputs(_hash, v, r, s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(_hash, v, r, s).call(tx_params.as_dict()) + + def send_transaction(self, _hash: bytes, v: int, r: bytes, s: bytes, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (_hash, v, r, s) = self.validate_and_normalize_inputs(_hash, v, r, s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(_hash, v, r, s).transact(tx_params.as_dict()) + + def estimate_gas(self, _hash: bytes, v: int, r: bytes, s: bytes, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (_hash, v, r, s) = self.validate_and_normalize_inputs(_hash, v, r, s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(_hash, v, r, s).estimateGas(tx_params.as_dict()) + +class AcceptsBytesMethod(ContractMethod): + """Various interfaces to the acceptsBytes method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, a: bytes): + """Validate the inputs to the acceptsBytes method.""" self.validator.assert_valid( method_name='acceptsBytes', parameter_name='a', argument_value=a, ) a = bytes.fromhex(a.decode("utf-8")) - func = self._get_contract_instance( - self.contract_address - ).functions.acceptsBytes( - a - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (a) - def revert_with_constant( - self, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def call(self, a: bytes, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.revertWithConstant( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).call(tx_params.as_dict()) - def simple_revert( - self, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def send_transaction(self, a: bytes, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.simpleRevert( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).transact(tx_params.as_dict()) - def nested_struct_output( - self, - tx_params: Optional[TxParams] = None, - ) -> Tuple0xc9bdd2d5: - """Execute underlying, same-named contract method. + def estimate_gas(self, a: bytes, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (a) = self.validate_and_normalize_inputs(a) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(a).estimateGas(tx_params.as_dict()) + +class RevertWithConstantMethod(ContractMethod): + """Various interfaces to the revertWithConstant method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.nestedStructOutput( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) - def require_with_constant( - self, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.requireWithConstant( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) - def with_address_input( - self, - x: str, - a: int, - b: int, - y: str, - c: int, - tx_params: Optional[TxParams] = None, - ) -> str: - """Execute underlying, same-named contract method. + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class SimpleRevertMethod(ContractMethod): + """Various interfaces to the simpleRevert method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class NestedStructOutputMethod(ContractMethod): + """Various interfaces to the nestedStructOutput method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xc9bdd2d5: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class RequireWithConstantMethod(ContractMethod): + """Various interfaces to the requireWithConstant method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class WithAddressInputMethod(ContractMethod): + """Various interfaces to the withAddressInput method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, x: str, a: int, b: int, y: str, c: int): + """Validate the inputs to the withAddressInput method.""" self.validator.assert_valid( method_name='withAddressInput', parameter_name='x', argument_value=x, ) - x = self._validate_and_checksum_address(x) + x = self.validate_and_checksum_address(x) self.validator.assert_valid( method_name='withAddressInput', parameter_name='a', @@ -419,7 +472,7 @@ class AbiGenDummy(BaseContractWrapper): parameter_name='y', argument_value=y, ) - y = self._validate_and_checksum_address(y) + y = self.validate_and_checksum_address(y) self.validator.assert_valid( method_name='withAddressInput', parameter_name='c', @@ -427,81 +480,117 @@ class AbiGenDummy(BaseContractWrapper): ) # safeguard against fractional inputs c = int(c) - func = self._get_contract_instance( - self.contract_address - ).functions.withAddressInput( - x, - a, - b, - y, - c - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (x, a, b, y, c) - def struct_input( - self, - s: Tuple0xcf8ad995, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def call(self, x: str, a: int, b: int, y: str, c: int, tx_params: Optional[TxParams] = None) -> str: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + (x, a, b, y, c) = self.validate_and_normalize_inputs(x, a, b, y, c) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x, a, b, y, c).call(tx_params.as_dict()) + + def send_transaction(self, x: str, a: int, b: int, y: str, c: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (x, a, b, y, c) = self.validate_and_normalize_inputs(x, a, b, y, c) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x, a, b, y, c).transact(tx_params.as_dict()) + + def estimate_gas(self, x: str, a: int, b: int, y: str, c: int, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (x, a, b, y, c) = self.validate_and_normalize_inputs(x, a, b, y, c) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x, a, b, y, c).estimateGas(tx_params.as_dict()) + +class StructInputMethod(ContractMethod): + """Various interfaces to the structInput method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, s: Tuple0xcf8ad995): + """Validate the inputs to the structInput method.""" self.validator.assert_valid( method_name='structInput', parameter_name='s', argument_value=s, ) - func = self._get_contract_instance( - self.contract_address - ).functions.structInput( - s - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (s) - def non_pure_method( - self, - tx_params: Optional[TxParams] = None, - view_only: bool = False, - ) -> Union[int, Union[HexBytes, bytes]]: - """Execute underlying, same-named contract method. - - :param tx_params: transaction parameters - :param view_only: whether to use transact() or call() - - :returns: if param `view_only`:code: is `True`:code:, then returns the - value returned from the underlying function; else returns the - transaction hash. - """ - func = self._get_contract_instance( - self.contract_address - ).functions.nonPureMethod( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=view_only - ) - - def simple_pure_function_with_input( - self, - x: int, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. + def call(self, s: Tuple0xcf8ad995, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + (s) = self.validate_and_normalize_inputs(s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(s).call(tx_params.as_dict()) + + def send_transaction(self, s: Tuple0xcf8ad995, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (s) = self.validate_and_normalize_inputs(s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(s).transact(tx_params.as_dict()) + + def estimate_gas(self, s: Tuple0xcf8ad995, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (s) = self.validate_and_normalize_inputs(s) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(s).estimateGas(tx_params.as_dict()) + +class NonPureMethodMethod(ContractMethod): + """Various interfaces to the nonPureMethod method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> Union[int, Union[HexBytes, bytes]]: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + :returns: the return value of the underlying method. + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class SimplePureFunctionWithInputMethod(ContractMethod): + """Various interfaces to the simplePureFunctionWithInput method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, x: int): + """Validate the inputs to the simplePureFunctionWithInput method.""" self.validator.assert_valid( method_name='simplePureFunctionWithInput', parameter_name='x', @@ -509,125 +598,289 @@ class AbiGenDummy(BaseContractWrapper): ) # safeguard against fractional inputs x = int(x) - func = self._get_contract_instance( - self.contract_address - ).functions.simplePureFunctionWithInput( - x - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (x) - def non_pure_method_that_returns_nothing( - self, - tx_params: Optional[TxParams] = None, - view_only: bool = False, - ) -> Union[None, Union[HexBytes, bytes]]: - """Execute underlying, same-named contract method. - - :param tx_params: transaction parameters - :param view_only: whether to use transact() or call() - - :returns: if param `view_only`:code: is `True`:code:, then returns the - value returned from the underlying function; else returns the - transaction hash. - """ - func = self._get_contract_instance( - self.contract_address - ).functions.nonPureMethodThatReturnsNothing( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=view_only - ) - - def simple_pure_function( - self, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. + def call(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.simplePureFunction( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).call(tx_params.as_dict()) - def nested_struct_input( - self, - n: Tuple0xc9bdd2d5, - tx_params: Optional[TxParams] = None, - ) -> None: - """Execute underlying, same-named contract method. + def send_transaction(self, x: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. :param tx_params: transaction parameters """ + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).transact(tx_params.as_dict()) + + def estimate_gas(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).estimateGas(tx_params.as_dict()) + +class NonPureMethodThatReturnsNothingMethod(ContractMethod): + """Various interfaces to the nonPureMethodThatReturnsNothing method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> Union[None, Union[HexBytes, bytes]]: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + :returns: the return value of the underlying method. + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class SimplePureFunctionMethod(ContractMethod): + """Various interfaces to the simplePureFunction method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class NestedStructInputMethod(ContractMethod): + """Various interfaces to the nestedStructInput method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, n: Tuple0xc9bdd2d5): + """Validate the inputs to the nestedStructInput method.""" self.validator.assert_valid( method_name='nestedStructInput', parameter_name='n', argument_value=n, ) - func = self._get_contract_instance( - self.contract_address - ).functions.nestedStructInput( - n - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (n) - def struct_output( - self, - tx_params: Optional[TxParams] = None, - ) -> Tuple0xcf8ad995: - """Execute underlying, same-named contract method. + def call(self, n: Tuple0xc9bdd2d5, tx_params: Optional[TxParams] = None) -> None: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + (n) = self.validate_and_normalize_inputs(n) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(n).call(tx_params.as_dict()) + + def send_transaction(self, n: Tuple0xc9bdd2d5, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (n) = self.validate_and_normalize_inputs(n) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(n).transact(tx_params.as_dict()) + + def estimate_gas(self, n: Tuple0xc9bdd2d5, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (n) = self.validate_and_normalize_inputs(n) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(n).estimateGas(tx_params.as_dict()) + +class StructOutputMethod(ContractMethod): + """Various interfaces to the structOutput method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xcf8ad995: + """Execute underlying contract method via eth_call. a method that returns a struct :param tx_params: transaction parameters :returns: a Struct struct """ - func = self._get_contract_instance( - self.contract_address - ).functions.structOutput( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) - def pure_function_with_constant( - self, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + a method that returns a struct + + :param tx_params: transaction parameters + :returns: a Struct struct + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +class PureFunctionWithConstantMethod(ContractMethod): + """Various interfaces to the pureFunctionWithConstant method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def call(self, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ - func = self._get_contract_instance( - self.contract_address - ).functions.pureFunctionWithConstant( - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().call(tx_params.as_dict()) + + def send_transaction(self, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().transact(tx_params.as_dict()) + + def estimate_gas(self, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method().estimateGas(tx_params.as_dict()) + +# pylint: disable=too-many-public-methods,too-many-instance-attributes +class AbiGenDummy: + """Wrapper class for AbiGenDummy Solidity contract. + + All method parameters of type `bytes`:code: should be encoded as UTF-8, + which can be accomplished via `str.encode("utf_8")`:code:. + """ + simple_require: SimpleRequireMethod + accepts_an_array_of_bytes: AcceptsAnArrayOfBytesMethod + withdraw: WithdrawMethod + ecrecover_fn: EcrecoverFnMethod + accepts_bytes: AcceptsBytesMethod + revert_with_constant: RevertWithConstantMethod + simple_revert: SimpleRevertMethod + nested_struct_output: NestedStructOutputMethod + require_with_constant: RequireWithConstantMethod + with_address_input: WithAddressInputMethod + struct_input: StructInputMethod + non_pure_method: NonPureMethodMethod + simple_pure_function_with_input: SimplePureFunctionWithInputMethod + non_pure_method_that_returns_nothing: NonPureMethodThatReturnsNothingMethod + simple_pure_function: SimplePureFunctionMethod + nested_struct_input: NestedStructInputMethod + struct_output: StructOutputMethod + pure_function_with_constant: PureFunctionWithConstantMethod + + def __init__( + self, + provider: BaseProvider, + contract_address: str, + validator: AbiGenDummyValidator = None, + ): + """Get an instance of wrapper for smart contract. + + :param provider: instance of :class:`web3.providers.base.BaseProvider` + :param contract_address: where the contract has been deployed + :param validator: for validation of method inputs. + """ + self.contract_address = contract_address + + if not validator: + validator = AbiGenDummyValidator(provider, contract_address) + + self._web3_eth = Web3( # type: ignore # pylint: disable=no-member + provider + ).eth + + functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi=AbiGenDummy.abi()).functions + + self.simple_require = SimpleRequireMethod(provider, contract_address, functions.simpleRequire, validator) + + self.accepts_an_array_of_bytes = AcceptsAnArrayOfBytesMethod(provider, contract_address, functions.acceptsAnArrayOfBytes, validator) + + self.withdraw = WithdrawMethod(provider, contract_address, functions.withdraw, validator) + + self.ecrecover_fn = EcrecoverFnMethod(provider, contract_address, functions.ecrecoverFn, validator) + + self.accepts_bytes = AcceptsBytesMethod(provider, contract_address, functions.acceptsBytes, validator) + + self.revert_with_constant = RevertWithConstantMethod(provider, contract_address, functions.revertWithConstant, validator) + + self.simple_revert = SimpleRevertMethod(provider, contract_address, functions.simpleRevert, validator) + + self.nested_struct_output = NestedStructOutputMethod(provider, contract_address, functions.nestedStructOutput, validator) + + self.require_with_constant = RequireWithConstantMethod(provider, contract_address, functions.requireWithConstant, validator) + + self.with_address_input = WithAddressInputMethod(provider, contract_address, functions.withAddressInput, validator) + + self.struct_input = StructInputMethod(provider, contract_address, functions.structInput, validator) + + self.non_pure_method = NonPureMethodMethod(provider, contract_address, functions.nonPureMethod, validator) + + self.simple_pure_function_with_input = SimplePureFunctionWithInputMethod(provider, contract_address, functions.simplePureFunctionWithInput, validator) + + self.non_pure_method_that_returns_nothing = NonPureMethodThatReturnsNothingMethod(provider, contract_address, functions.nonPureMethodThatReturnsNothing, validator) + + self.simple_pure_function = SimplePureFunctionMethod(provider, contract_address, functions.simplePureFunction, validator) + + self.nested_struct_input = NestedStructInputMethod(provider, contract_address, functions.nestedStructInput, validator) + + self.struct_output = StructOutputMethod(provider, contract_address, functions.structOutput, validator) + + self.pure_function_with_constant = PureFunctionWithConstantMethod(provider, contract_address, functions.pureFunctionWithConstant, validator) + def get_withdrawal_event( self, tx_hash: Union[HexBytes, bytes] ) -> Tuple[AttributeDict]: @@ -636,11 +889,7 @@ class AbiGenDummy(BaseContractWrapper): :param tx_hash: hash of transaction emitting Withdrawal event """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) - return ( - self._get_contract_instance(self.contract_address) - .events.Withdrawal() - .processReceipt(tx_receipt) - ) + return self._web3_eth.contract(address=to_checksum_address(self.contract_address), abi=AbiGenDummy.abi()).events.Withdrawal().processReceipt(tx_receipt) def get_an_event_event( self, tx_hash: Union[HexBytes, bytes] ) -> Tuple[AttributeDict]: @@ -649,11 +898,7 @@ class AbiGenDummy(BaseContractWrapper): :param tx_hash: hash of transaction emitting AnEvent event """ tx_receipt = self._web3_eth.getTransactionReceipt(tx_hash) - return ( - self._get_contract_instance(self.contract_address) - .events.AnEvent() - .processReceipt(tx_receipt) - ) + return self._web3_eth.contract(address=to_checksum_address(self.contract_address), abi=AbiGenDummy.abi()).events.AnEvent().processReceipt(tx_receipt) @staticmethod def abi(): diff --git a/packages/abi-gen/test-cli/expected-output/python/lib_dummy/__init__.py b/packages/abi-gen/test-cli/expected-output/python/lib_dummy/__init__.py index 66bb91e528..fee8d1da38 100644 --- a/packages/abi-gen/test-cli/expected-output/python/lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/expected-output/python/lib_dummy/__init__.py @@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import Union, ) +from eth_utils import to_checksum_address from mypy_extensions import TypedDict # pylint: disable=unused-import from hexbytes import HexBytes +from web3 import Web3 +from web3.contract import ContractFunction from web3.datastructures import AttributeDict from web3.providers.base import BaseProvider -from zero_ex.contract_wrappers._base_contract_wrapper import BaseContractWrapper +from zero_ex.contract_wrappers.bases import ContractMethod, Validator from zero_ex.contract_wrappers.tx_params import TxParams -class LibDummyValidatorBase: - """Base class for validating inputs to LibDummy methods.""" - def __init__( - self, - provider: BaseProvider, - contract_address: str, - private_key: str = None, - ): - """Initialize the instance.""" - - def assert_valid( - self, method_name: str, parameter_name: str, argument_value: Any - ): - """Raise an exception if method input is not valid. - - :param method_name: Name of the method whose input is to be validated. - :param parameter_name: Name of the parameter whose input is to be - validated. - :param argument_value: Value of argument to parameter to be validated. - """ - - # Try to import a custom validator class definition; if there isn't one, # declare one that we can instantiate for the default argument to the # constructor for LibDummy below. @@ -53,15 +34,15 @@ try: ) except ImportError: - class LibDummyValidator(LibDummyValidatorBase): # type: ignore + class LibDummyValidator(Validator): # type: ignore """No-op input validator.""" -# pylint: disable=too-many-public-methods -class LibDummy(BaseContractWrapper): +# pylint: disable=too-many-public-methods,too-many-instance-attributes +class LibDummy: """Wrapper class for LibDummy Solidity contract.""" def __init__( @@ -69,35 +50,24 @@ class LibDummy(BaseContractWrapper): provider: BaseProvider, contract_address: str, validator: LibDummyValidator = None, - private_key: str = None, ): """Get an instance of wrapper for smart contract. :param provider: instance of :class:`web3.providers.base.BaseProvider` :param contract_address: where the contract has been deployed - :param private_key: If specified, transactions will be signed locally, - via Web3.py's `eth.account.signTransaction()`:code:, before being - sent via `eth.sendRawTransaction()`:code:. + :param validator: for validation of method inputs. """ - super().__init__( - provider=provider, - contract_address=contract_address, - private_key=private_key, - ) + self.contract_address = contract_address if not validator: - validator = LibDummyValidator(provider, contract_address, private_key) + validator = LibDummyValidator(provider, contract_address) - self.validator = validator + self._web3_eth = Web3( # type: ignore # pylint: disable=no-member + provider + ).eth - def _get_contract_instance(self, token_address): - """Get an instance of the smart contract at a specific address. + functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi=LibDummy.abi()).functions - :returns: contract object - """ - return self._contract_instance( - address=token_address, abi=LibDummy.abi() - ) @staticmethod def abi(): diff --git a/packages/abi-gen/test-cli/expected-output/python/test_lib_dummy/__init__.py b/packages/abi-gen/test-cli/expected-output/python/test_lib_dummy/__init__.py index 5c4cab81d8..2f40a16476 100644 --- a/packages/abi-gen/test-cli/expected-output/python/test_lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/expected-output/python/test_lib_dummy/__init__.py @@ -11,37 +11,18 @@ from typing import ( # pylint: disable=unused-import Union, ) +from eth_utils import to_checksum_address from mypy_extensions import TypedDict # pylint: disable=unused-import from hexbytes import HexBytes +from web3 import Web3 +from web3.contract import ContractFunction from web3.datastructures import AttributeDict from web3.providers.base import BaseProvider -from zero_ex.contract_wrappers._base_contract_wrapper import BaseContractWrapper +from zero_ex.contract_wrappers.bases import ContractMethod, Validator from zero_ex.contract_wrappers.tx_params import TxParams -class TestLibDummyValidatorBase: - """Base class for validating inputs to TestLibDummy methods.""" - def __init__( - self, - provider: BaseProvider, - contract_address: str, - private_key: str = None, - ): - """Initialize the instance.""" - - def assert_valid( - self, method_name: str, parameter_name: str, argument_value: Any - ): - """Raise an exception if method input is not valid. - - :param method_name: Name of the method whose input is to be validated. - :param parameter_name: Name of the parameter whose input is to be - validated. - :param argument_value: Value of argument to parameter to be validated. - """ - - # Try to import a custom validator class definition; if there isn't one, # declare one that we can instantiate for the default argument to the # constructor for TestLibDummy below. @@ -53,62 +34,23 @@ try: ) except ImportError: - class TestLibDummyValidator(TestLibDummyValidatorBase): # type: ignore + class TestLibDummyValidator(Validator): # type: ignore """No-op input validator.""" -# pylint: disable=too-many-public-methods -class TestLibDummy(BaseContractWrapper): - """Wrapper class for TestLibDummy Solidity contract.""" +class PublicAddConstantMethod(ContractMethod): + """Various interfaces to the publicAddConstant method.""" - def __init__( - self, - provider: BaseProvider, - contract_address: str, - validator: TestLibDummyValidator = None, - private_key: str = None, - ): - """Get an instance of wrapper for smart contract. + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function - :param provider: instance of :class:`web3.providers.base.BaseProvider` - :param contract_address: where the contract has been deployed - :param private_key: If specified, transactions will be signed locally, - via Web3.py's `eth.account.signTransaction()`:code:, before being - sent via `eth.sendRawTransaction()`:code:. - """ - super().__init__( - provider=provider, - contract_address=contract_address, - private_key=private_key, - ) - - if not validator: - validator = TestLibDummyValidator(provider, contract_address, private_key) - - self.validator = validator - - def _get_contract_instance(self, token_address): - """Get an instance of the smart contract at a specific address. - - :returns: contract object - """ - return self._contract_instance( - address=token_address, abi=TestLibDummy.abi() - ) - - def public_add_constant( - self, - x: int, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. - - :param tx_params: transaction parameters - - """ + def validate_and_normalize_inputs(self, x: int): + """Validate the inputs to the publicAddConstant method.""" self.validator.assert_valid( method_name='publicAddConstant', parameter_name='x', @@ -116,27 +58,44 @@ class TestLibDummy(BaseContractWrapper): ) # safeguard against fractional inputs x = int(x) - func = self._get_contract_instance( - self.contract_address - ).functions.publicAddConstant( - x - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (x) - def public_add_one( - self, - x: int, - tx_params: Optional[TxParams] = None, - ) -> int: - """Execute underlying, same-named contract method. + def call(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. :param tx_params: transaction parameters """ + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).call(tx_params.as_dict()) + + def send_transaction(self, x: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).transact(tx_params.as_dict()) + + def estimate_gas(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).estimateGas(tx_params.as_dict()) + +class PublicAddOneMethod(ContractMethod): + """Various interfaces to the publicAddOne method.""" + + def __init__(self, provider: BaseProvider, contract_address: str, contract_function: ContractFunction, validator: Validator=None): + """Persist instance data.""" + super().__init__(provider, contract_address, validator) + self.underlying_method = contract_function + + def validate_and_normalize_inputs(self, x: int): + """Validate the inputs to the publicAddOne method.""" self.validator.assert_valid( method_name='publicAddOne', parameter_name='x', @@ -144,16 +103,67 @@ class TestLibDummy(BaseContractWrapper): ) # safeguard against fractional inputs x = int(x) - func = self._get_contract_instance( - self.contract_address - ).functions.publicAddOne( - x - ) - return self._invoke_function_call( - func=func, - tx_params=tx_params, - view_only=True - ) + return (x) + + def call(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Execute underlying contract method via eth_call. + + :param tx_params: transaction parameters + + """ + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).call(tx_params.as_dict()) + + def send_transaction(self, x: int, tx_params: Optional[TxParams] = None) -> Union[HexBytes, bytes]: + """Execute underlying contract method via eth_sendTransaction. + + :param tx_params: transaction parameters + + """ + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).transact(tx_params.as_dict()) + + def estimate_gas(self, x: int, tx_params: Optional[TxParams] = None) -> int: + """Estimate gas consumption of method call.""" + (x) = self.validate_and_normalize_inputs(x) + tx_params = super().normalize_tx_params(tx_params) + return self.underlying_method(x).estimateGas(tx_params.as_dict()) + +# pylint: disable=too-many-public-methods,too-many-instance-attributes +class TestLibDummy: + """Wrapper class for TestLibDummy Solidity contract.""" + public_add_constant: PublicAddConstantMethod + public_add_one: PublicAddOneMethod + + def __init__( + self, + provider: BaseProvider, + contract_address: str, + validator: TestLibDummyValidator = None, + ): + """Get an instance of wrapper for smart contract. + + :param provider: instance of :class:`web3.providers.base.BaseProvider` + :param contract_address: where the contract has been deployed + :param validator: for validation of method inputs. + """ + self.contract_address = contract_address + + if not validator: + validator = TestLibDummyValidator(provider, contract_address) + + self._web3_eth = Web3( # type: ignore # pylint: disable=no-member + provider + ).eth + + functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi=TestLibDummy.abi()).functions + + self.public_add_constant = PublicAddConstantMethod(provider, contract_address, functions.publicAddConstant, validator) + + self.public_add_one = PublicAddOneMethod(provider, contract_address, functions.publicAddOne, validator) + @staticmethod def abi(): diff --git a/python-packages/contract_addresses/setup.py b/python-packages/contract_addresses/setup.py index f197122930..1aa2231097 100755 --- a/python-packages/contract_addresses/setup.py +++ b/python-packages/contract_addresses/setup.py @@ -109,7 +109,8 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - exit(pytest.main(["--doctest-modules"])) + exit(pytest.main(["--doctest-modules", "-rapP"])) + # show short test summary at end ^ with open("README.md", "r") as file_handle: diff --git a/python-packages/contract_artifacts/setup.py b/python-packages/contract_artifacts/setup.py index 14db18fcb3..101fb2fc41 100755 --- a/python-packages/contract_artifacts/setup.py +++ b/python-packages/contract_artifacts/setup.py @@ -138,7 +138,8 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - exit(pytest.main(["--doctest-modules"])) + exit(pytest.main(["--doctest-modules", "-rapP"])) + # show short test summary at end ^ with open("README.md", "r") as file_handle: diff --git a/python-packages/contract_wrappers/setup.cfg b/python-packages/contract_wrappers/setup.cfg new file mode 100644 index 0000000000..e37dc107e7 --- /dev/null +++ b/python-packages/contract_wrappers/setup.cfg @@ -0,0 +1,5 @@ +[pycodestyle] +ignore = E501, W503 +# E501 = line too long +# W503 = line break occurred before a binary operator +# we let black handle these things diff --git a/python-packages/contract_wrappers/setup.py b/python-packages/contract_wrappers/setup.py index 7d7da3da6c..cf50679cc7 100755 --- a/python-packages/contract_wrappers/setup.py +++ b/python-packages/contract_wrappers/setup.py @@ -74,7 +74,8 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - exit(pytest.main(["--doctest-modules"])) + exit(pytest.main(["--doctest-modules", "-rapP"])) + # show short test summary at end ^ class LintCommand(distutils.command.build_py.build_py): diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py index a92a6d8653..059273bd53 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/__init__.py @@ -102,13 +102,13 @@ balance: >>> erc20_proxy_addr = NETWORK_TO_ADDRESSES[NetworkId.GANACHE].erc20_proxy ->>> tx = zrx_token.approve( +>>> tx = zrx_token.approve.send_transaction( ... erc20_proxy_addr, ... to_wei(100, 'ether'), ... tx_params=TxParams(from_=maker_address), ... ) ->>> tx = weth_token.approve( +>>> tx = weth_token.approve.send_transaction( ... erc20_proxy_addr, ... to_wei(100, 'ether'), ... tx_params=TxParams(from_=taker_address), @@ -166,7 +166,7 @@ too. ... provider=ganache, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange, ... ) ->>> tx_hash = exchange.fill_order( +>>> tx_hash = exchange.fill_order.send_transaction( ... order=order, ... taker_asset_fill_amount=order["takerAssetAmount"], ... signature=maker_signature, @@ -217,7 +217,7 @@ A Maker can cancel an order that has yet to be filled. ... ) ... ) ->>> tx_hash = exchange.cancel_order( +>>> tx_hash = exchange.cancel_order.send_transaction( ... order=order, tx_params=TxParams(from_=maker_address) ... ) @@ -287,12 +287,40 @@ is an example where the taker fills two orders in one transaction: Fill order_1 and order_2 together: ->>> exchange.batch_fill_orders( +>>> exchange.batch_fill_orders.send_transaction( ... orders=[order_1, order_2], ... taker_asset_fill_amounts=[1, 2], ... signatures=[signature_1, signature_2], ... tx_params=TxParams(from_=taker_address)) HexBytes('0x...') + +Estimating gas consumption +-------------------------- + +Before executing a transaction, you may want to get an estimate of how much gas +will be consumed. + +>>> exchange.cancel_order.estimate_gas( +... order=Order( +... makerAddress=maker_address, +... takerAddress='0x0000000000000000000000000000000000000000', +... exchangeAddress=exchange_address, +... senderAddress='0x0000000000000000000000000000000000000000', +... feeRecipientAddress='0x0000000000000000000000000000000000000000', +... makerAssetData=asset_data_utils.encode_erc20(weth_address), +... takerAssetData=asset_data_utils.encode_erc20(weth_address), +... salt=random.randint(1, 100000000000000000), +... makerFee=0, +... takerFee=0, +... makerAssetAmount=1000000000000000000, +... takerAssetAmount=500000000000000000000, +... expirationTimeSeconds=round( +... (datetime.utcnow() + timedelta(days=1)).timestamp() +... ) +... ), +... tx_params=TxParams(from_=maker_address), +... ) +73825 """ from .tx_params import TxParams diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py deleted file mode 100644 index 0ac4847f8b..0000000000 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/_base_contract_wrapper.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Base wrapper class for accessing ethereum smart contracts.""" - -from typing import Optional, Union - -from eth_utils import to_checksum_address -from web3 import Web3 -from web3.providers.base import BaseProvider - -from .tx_params import TxParams - - -class BaseContractWrapper: - """Base class for wrapping ethereum smart contracts. - - It provides functionality for instantiating a contract instance, - calling view functions, and calling functions which require - transactions. - """ - - def __init__( - self, - provider: BaseProvider, - contract_address: str, - private_key: str = None, - ): - """Create an instance of BaseContractWrapper. - - :param provider: instance of :class:`web3.providers.base.BaseProvider` - :param private_key: If specified, transactions will be signed locally, - via Web3.py's `eth.account.signTransaction()`:code:, before being - sent via `eth.sendRawTransaction()`:code:. - """ - self._provider = provider - self._private_key = private_key - self._web3 = Web3(provider) - self._web3_eth = self._web3.eth # pylint: disable=no-member - self.contract_address = self._validate_and_checksum_address( - contract_address - ) - - self._can_send_tx = False - if self._web3_eth.defaultAccount or self._web3_eth.accounts: - self._can_send_tx = True - else: - middleware_stack = getattr(self._web3, "middleware_stack") - if middleware_stack.get("sign_and_send_raw_middleware"): - self._can_send_tx = True - elif private_key: - self._private_key = private_key - self._web3_eth.defaultAccount = to_checksum_address( - self._web3_eth.account.privateKeyToAccount( - private_key - ).address - ) - self._can_send_tx = True - - def _contract_instance(self, address: str, abi: dict): - """Get a contract instance. - - :param address: string address of contract - :param abi: dict contract ABI - - :returns: instance of contract - """ - return self._web3_eth.contract( - address=to_checksum_address(address), abi=abi - ) - - def _validate_and_checksum_address(self, address: str): - if not self._web3.isAddress(address): - raise TypeError("Invalid address provided: {}".format(address)) - return to_checksum_address(address) - - def _invoke_function_call(self, func, tx_params, view_only): - if view_only: - return func.call() - if not self._can_send_tx: - raise Exception( - "Cannot send transaction because no local private_key" - " or account found." - ) - if not tx_params: - tx_params = TxParams() - if not tx_params.from_: - tx_params.from_ = ( - self._web3_eth.defaultAccount or self._web3_eth.accounts[0] - ) - tx_params.from_ = self._validate_and_checksum_address(tx_params.from_) - if self._private_key: - res = self._sign_and_send_raw_direct(func, tx_params) - else: - res = func.transact(tx_params.as_dict()) - return res - - def _sign_and_send_raw_direct(self, func, tx_params): - transaction = func.buildTransaction(tx_params.as_dict()) - signed_tx = self._web3_eth.account.signTransaction( - transaction, private_key=self._private_key - ) - return self._web3_eth.sendRawTransaction(signed_tx.rawTransaction) - - # pylint: disable=too-many-arguments - def execute_method( - self, - abi: dict, - method: str, - args: Optional[Union[list, tuple]] = None, - tx_params: Optional[TxParams] = None, - view_only: bool = False, - ) -> str: - """Execute the method on a contract instance. - - :param abi: dict of contract ABI - :param method: string name of method to call - :param args: default None, list or tuple of arguments for the method - :param tx_params: default None, :class:`TxParams` transaction params - :param view_only: default False, boolean of whether the transaction - should only be validated. - - :returns: str of transaction hash - """ - contract_instance = self._contract_instance( - address=self.contract_address, abi=abi - ) - if args is None: - args = [] - if hasattr(contract_instance.functions, method): - func = getattr(contract_instance.functions, method)(*args) - return self._invoke_function_call( - func=func, tx_params=tx_params, view_only=view_only - ) - raise Exception( - "No method {} found on contract {}.".format( - self.contract_address, method - ) - ) diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py new file mode 100644 index 0000000000..39280f4efe --- /dev/null +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/bases.py @@ -0,0 +1,66 @@ +"""Base wrapper class for accessing ethereum smart contracts.""" + +from typing import Any + +from eth_utils import is_address, to_checksum_address +from web3 import Web3 +from web3.providers.base import BaseProvider + +from .tx_params import TxParams + + +class Validator: + """Base class for validating inputs to methods.""" + + def __init__(self, provider: BaseProvider, contract_address: str): + """Initialize the instance.""" + + def assert_valid( + self, method_name: str, parameter_name: str, argument_value: Any + ): + """Raise an exception if method input is not valid. + + :param method_name: Name of the method whose input is to be validated. + :param parameter_name: Name of the parameter whose input is to be + validated. + :param argument_value: Value of argument to parameter to be validated. + """ + + +class ContractMethod: + """Base class for wrapping an Ethereum smart contract method.""" + + def __init__( + self, + provider: BaseProvider, + contract_address: str, + validator: Validator = None, + ): + """Instantiate the object. + + :param provider: Instance of :class:`web3.providers.base.BaseProvider` + :param contract_address: Where the contract has been deployed to. + :param validator: Used to validate method inputs. + """ + self._web3_eth = Web3(provider).eth # pylint: disable=no-member + if validator is None: + validator = Validator(provider, contract_address) + self.validator = validator + + @staticmethod + def validate_and_checksum_address(address: str): + """Validate the given address, and return it's checksum address.""" + if not is_address(address): + raise TypeError("Invalid address provided: {}".format(address)) + return to_checksum_address(address) + + def normalize_tx_params(self, tx_params) -> TxParams: + """Normalize and return the given transaction parameters.""" + if not tx_params: + tx_params = TxParams() + if not tx_params.from_: + tx_params.from_ = ( + self._web3_eth.defaultAccount or self._web3_eth.accounts[0] + ) + tx_params.from_ = self.validate_and_checksum_address(tx_params.from_) + return tx_params diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/.gitkeep b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py index 27932db2bd..2479ee8179 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/validator.py @@ -6,21 +6,16 @@ from web3.providers.base import BaseProvider from zero_ex import json_schemas -from . import ExchangeValidatorBase +from ..bases import Validator from .types import order_to_jsdict -class ExchangeValidator(ExchangeValidatorBase): +class ExchangeValidator(Validator): """Validate inputs to Exchange methods.""" - def __init__( - self, - provider: BaseProvider, - contract_address: str, - private_key: str = None, - ): + def __init__(self, provider: BaseProvider, contract_address: str): """Initialize the class.""" - super().__init__(provider, contract_address, private_key) + super().__init__(provider, contract_address) self.contract_address = contract_address def assert_valid( diff --git a/python-packages/contract_wrappers/stubs/eth_utils/__init__.pyi b/python-packages/contract_wrappers/stubs/eth_utils/__init__.pyi index 90e1277eb0..c541dc5e42 100644 --- a/python-packages/contract_wrappers/stubs/eth_utils/__init__.pyi +++ b/python-packages/contract_wrappers/stubs/eth_utils/__init__.pyi @@ -1,3 +1,7 @@ +from typing import Union + def to_checksum_address(address: str) -> str: ... -def remove_0x_prefix(hex_string: str) -> str: ... \ No newline at end of file +def remove_0x_prefix(hex_string: str) -> str: ... + +def is_address(address: Union[str, bytes]) -> bool: ... diff --git a/python-packages/contract_wrappers/stubs/web3/contract.pyi b/python-packages/contract_wrappers/stubs/web3/contract.pyi new file mode 100644 index 0000000000..3078722ba2 --- /dev/null +++ b/python-packages/contract_wrappers/stubs/web3/contract.pyi @@ -0,0 +1,5 @@ +class ContractFunction: + def __call__(self, *args, **kwargs): + ... + + ... diff --git a/python-packages/contract_wrappers/stubs/web3/utils/datatypes.pyi b/python-packages/contract_wrappers/stubs/web3/utils/datatypes.pyi index 70baff3728..f4d350d897 100644 --- a/python-packages/contract_wrappers/stubs/web3/utils/datatypes.pyi +++ b/python-packages/contract_wrappers/stubs/web3/utils/datatypes.pyi @@ -1,3 +1,10 @@ +from typing import Any + + class Contract: def call(self): ... + + functions: Any + + events: Any ... diff --git a/python-packages/contract_wrappers/test/test_base_contract_method.py b/python-packages/contract_wrappers/test/test_base_contract_method.py new file mode 100644 index 0000000000..6b558b9fad --- /dev/null +++ b/python-packages/contract_wrappers/test/test_base_contract_method.py @@ -0,0 +1,15 @@ +"""Tests for :class:`ContractMethod`.""" + +import pytest + +from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId +from zero_ex.contract_wrappers.bases import ContractMethod + + +@pytest.fixture(scope="module") +def contract_wrapper(ganache_provider): + """Get a ContractMethod instance for testing.""" + return ContractMethod( + provider=ganache_provider, + contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token, + ) diff --git a/python-packages/contract_wrappers/test/test_base_contract_wrapper.py b/python-packages/contract_wrappers/test/test_base_contract_wrapper.py deleted file mode 100644 index a26e200cd3..0000000000 --- a/python-packages/contract_wrappers/test/test_base_contract_wrapper.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Tests for :class:`BaseContractWrapper`.""" - -import pytest -from eth_utils import to_checksum_address - -from zero_ex.contract_addresses import NETWORK_TO_ADDRESSES, NetworkId -from zero_ex.contract_artifacts import abi_by_name -from zero_ex.contract_wrappers._base_contract_wrapper import ( - BaseContractWrapper, -) - - -@pytest.fixture(scope="module") -def contract_wrapper(ganache_provider): - """Get a BaseContractWrapper instance for testing.""" - return BaseContractWrapper( - provider=ganache_provider, - contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].ether_token, - ) - - -def test_contract_wrapper__execute_method( - accounts, - contract_wrapper, # pylint: disable=redefined-outer-name - erc20_proxy_address, -): - """Test :function:`BaseContractWrapper.execute` method.""" - acc1_allowance = contract_wrapper.execute_method( - abi=abi_by_name("WETH9"), - method="allowance", - view_only=True, - args=( - to_checksum_address(accounts[3]), - to_checksum_address(erc20_proxy_address), - ), - ) - assert acc1_allowance == 0 - - with pytest.raises(Exception): - contract_wrapper.execute_method( - abi=abi_by_name("WETH9"), - method="send", - view_only=True, - args=[ - to_checksum_address(accounts[3]), - to_checksum_address(erc20_proxy_address), - ], - ) diff --git a/python-packages/contract_wrappers/test/test_erc20_wrapper.py b/python-packages/contract_wrappers/test/test_erc20_wrapper.py index 565c15aa4e..7f3d42fb2b 100644 --- a/python-packages/contract_wrappers/test/test_erc20_wrapper.py +++ b/python-packages/contract_wrappers/test/test_erc20_wrapper.py @@ -25,8 +25,8 @@ def test_erc20_wrapper__balance_of( weth_instance, # pylint: disable=redefined-outer-name ): """Test getting baance of an account for an ERC20 token.""" - acc1_original_weth_balance = erc20_wrapper.balance_of(accounts[0]) - acc2_original_weth_balance = erc20_wrapper.balance_of(accounts[1]) + acc1_original_weth_balance = erc20_wrapper.balance_of.call(accounts[0]) + acc2_original_weth_balance = erc20_wrapper.balance_of.call(accounts[1]) expected_difference = 1 * 10 ** 18 @@ -36,8 +36,8 @@ def test_erc20_wrapper__balance_of( weth_instance.functions.deposit().transact( {"from": accounts[1], "value": expected_difference} ) - acc1_weth_balance = erc20_wrapper.balance_of(accounts[0]) - acc2_weth_balance = erc20_wrapper.balance_of(accounts[1]) + acc1_weth_balance = erc20_wrapper.balance_of.call(accounts[0]) + acc2_weth_balance = erc20_wrapper.balance_of.call(accounts[1]) assert ( acc1_weth_balance - acc1_original_weth_balance == expected_difference @@ -53,21 +53,21 @@ def test_erc20_wrapper__approve( erc20_wrapper, # pylint: disable=redefined-outer-name ): """Test approving one account to spend balance from another account.""" - erc20_wrapper.approve( + erc20_wrapper.approve.send_transaction( erc20_proxy_address, MAX_ALLOWANCE, tx_params=TxParams(from_=accounts[0]), ) - erc20_wrapper.approve( + erc20_wrapper.approve.send_transaction( erc20_proxy_address, MAX_ALLOWANCE, tx_params=TxParams(from_=accounts[1]), ) - acc_1_weth_allowance = erc20_wrapper.allowance( + acc_1_weth_allowance = erc20_wrapper.allowance.call( accounts[0], erc20_proxy_address ) - acc_2_weth_allowance = erc20_wrapper.allowance( + acc_2_weth_allowance = erc20_wrapper.allowance.call( accounts[1], erc20_proxy_address ) diff --git a/python-packages/contract_wrappers/test/test_exchange_wrapper.py b/python-packages/contract_wrappers/test/test_exchange_wrapper.py index 64896234c4..4f2b70a079 100644 --- a/python-packages/contract_wrappers/test/test_exchange_wrapper.py +++ b/python-packages/contract_wrappers/test/test_exchange_wrapper.py @@ -77,7 +77,7 @@ def test_exchange_wrapper__fill_order( ) order_signature = sign_hash_to_bytes(ganache_provider, maker, order_hash) - tx_hash = exchange_wrapper.fill_order( + tx_hash = exchange_wrapper.fill_order.send_transaction( order=order, taker_asset_fill_amount=order["takerAssetAmount"], signature=order_signature, @@ -114,7 +114,7 @@ def test_exchange_wrapper__batch_fill_orders( for order_hash in order_hashes ] taker_amounts = [order["takerAssetAmount"] for order in orders] - tx_hash = exchange_wrapper.batch_fill_orders( + tx_hash = exchange_wrapper.batch_fill_orders.send_transaction( orders=orders, taker_asset_fill_amounts=taker_amounts, signatures=order_signatures, diff --git a/python-packages/json_schemas/setup.py b/python-packages/json_schemas/setup.py index 03a57cdbed..f18cc596ed 100755 --- a/python-packages/json_schemas/setup.py +++ b/python-packages/json_schemas/setup.py @@ -40,7 +40,8 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - exit(pytest.main(["--doctest-modules"])) + exit(pytest.main(["--doctest-modules", "-rapP"])) + # show short test summary at end ^ class LintCommand(distutils.command.build_py.build_py): diff --git a/python-packages/middlewares/setup.py b/python-packages/middlewares/setup.py index 79dc36f64e..572f29c02f 100755 --- a/python-packages/middlewares/setup.py +++ b/python-packages/middlewares/setup.py @@ -20,7 +20,8 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - exit(pytest.main(["--doctest-modules"])) + exit(pytest.main(["--doctest-modules", "-rapP"])) + # show short test summary at end ^ class LintCommand(distutils.command.build_py.build_py): diff --git a/python-packages/order_utils/setup.py b/python-packages/order_utils/setup.py index 38ab5f00b6..8496a88efd 100755 --- a/python-packages/order_utils/setup.py +++ b/python-packages/order_utils/setup.py @@ -21,7 +21,8 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - exit(pytest.main(["--doctest-modules"])) + exit(pytest.main(["--doctest-modules", "-rapP"])) + # show short test summary at end ^ class LintCommand(distutils.command.build_py.build_py): diff --git a/python-packages/parallel b/python-packages/parallel index 12f385baaf..c127d3cf5f 100755 --- a/python-packages/parallel +++ b/python-packages/parallel @@ -20,9 +20,9 @@ $ ./parallel pip uninstall $(basename $(pwd)) >>>""" -from concurrent.futures import ProcessPoolExecutor +from concurrent.futures import ProcessPoolExecutor, wait from os import chdir -from subprocess import check_call +from subprocess import CalledProcessError, check_output from sys import argv PACKAGES = [ @@ -38,7 +38,18 @@ PACKAGES = [ def run_cmd_on_package(package: str): """cd to the package dir, ./setup.py lint, cd ..""" chdir(package) - check_call(f"{' '.join(argv[1:])}".split()) - chdir("..") + try: + check_output(f"{' '.join(argv[1:])}".split()) + except CalledProcessError as error: + print(f"standard output from command:\n{error.output.decode('utf-8')}") + raise RuntimeError(f"Above exception raised in {package}, ") from error + finally: + chdir("..") -ProcessPoolExecutor().map(run_cmd_on_package, PACKAGES) +with ProcessPoolExecutor() as executor: + for future in executor.map(run_cmd_on_package, PACKAGES): + # iterate over map()'s return value, to resolve the futures. + # but we don't actually care what the return values are, so just `pass`. + # if any exceptions were raised by the underlying task, they'll be + # raised as the iteration encounters them. + pass diff --git a/python-packages/sra_client/setup.py b/python-packages/sra_client/setup.py index b61c6e7d92..87636337ba 100755 --- a/python-packages/sra_client/setup.py +++ b/python-packages/sra_client/setup.py @@ -33,7 +33,8 @@ class TestCommandExtension(TestCommand): """Invoke pytest.""" import pytest - exit(pytest.main(["--doctest-modules"])) + exit(pytest.main(["--doctest-modules", "-rapP"])) + # show short test summary at end ^ class TestPublishCommand(distutils.command.build_py.build_py): diff --git a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py index de7e254ade..9b8b8c7a13 100644 --- a/python-packages/sra_client/src/zero_ex/sra_client/__init__.py +++ b/python-packages/sra_client/src/zero_ex/sra_client/__init__.py @@ -319,7 +319,7 @@ book. Now let's have the taker fill it: ... provider=eth_node, ... contract_address=NETWORK_TO_ADDRESSES[NetworkId.GANACHE].exchange ... ) ->>> exchange.fill_order( +>>> exchange.fill_order.send_transaction( ... order=order, ... taker_asset_fill_amount=order['makerAssetAmount']/2, # note the half fill ... signature=order['signature'].replace('0x', '').encode('utf-8'), @@ -333,7 +333,7 @@ Cancelling Note that the above fill was partial: it only filled half of the order. Now we'll have our maker cancel the remaining order: ->>> exchange.cancel_order( +>>> exchange.cancel_order.send_transaction( ... order=order, ... tx_params=TxParams(from_=maker_address) ... )