diff --git a/.gitignore b/.gitignore index efa2b704d0..40c0275869 100644 --- a/.gitignore +++ b/.gitignore @@ -175,6 +175,8 @@ python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_validator/__in python-packages/contract_wrappers/src/zero_ex/contract_wrappers/i_wallet/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/multi_asset_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_validator/__init__.py +python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking/__init__.py +python-packages/contract_wrappers/src/zero_ex/contract_wrappers/staking_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/static_call_proxy/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/weth9/__init__.py python-packages/contract_wrappers/src/zero_ex/contract_wrappers/zrx_token/__init__.py diff --git a/packages/abi-gen/CHANGELOG.json b/packages/abi-gen/CHANGELOG.json index fe81217934..4eeb68951b 100644 --- a/packages/abi-gen/CHANGELOG.json +++ b/packages/abi-gen/CHANGELOG.json @@ -5,6 +5,26 @@ { "note": "Refactored TS wrapper templates to result in a more succint interface. See https://github.com/0xProject/0x-monorepo/pull/2325 for details.", "pr": 2284 + }, + { + "note": "Python: Corrected return types and values for call() interface to generated method wrappers. (Fixes #2298)", + "pr": 2345 + }, + { + "note": "Python: Stopped generating send_transaction() interface for constant (view/pure) methods", + "pr": 2345 + }, + { + "note": "Python: Added a build_transaction() interface to contract method classes", + "pr": 2345 + }, + { + "note": "Python: Removed `validator` argument to contract method classes for methods that don't have any inputs", + "pr": 2345 + }, + { + "note": "Python: Changed the names of generated tuples to use the `internalType` field in the ABI, if it's present, resulting in human readable struct names rather than hashes of component field names.", + "pr": 2345 } ] }, diff --git a/packages/abi-gen/package.json b/packages/abi-gen/package.json index 06bdd91c9b..34b989c419 100644 --- a/packages/abi-gen/package.json +++ b/packages/abi-gen/package.json @@ -8,7 +8,7 @@ "main": "lib/src/index.js", "types": "lib/src/index.d.ts", "scripts": { - "lint": "tslint --format stylish --project . && yarn test_cli:lint", + "lint": "tslint --format stylish --project .", "fix": "tslint --fix --format stylish --project . && yarn lint-contracts", "clean": "shx rm -rf lib && yarn test_cli:clean", "build": "tsc -b && yarn generate_contract_wrappers && yarn prettier_contract_wrappers && yarn test_cli:build", @@ -18,15 +18,16 @@ "run_mocha": "(uname -s | grep -q Darwin && echo 'HACK! skipping mocha run due to https://github.com/0xProject/0x-monorepo/issues/2000') || mocha --require source-map-support/register --require make-promises-safe lib/test/*_test.js --timeout 100000 --bail --exit", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "test_cli": "run-s test_cli:test_typescript diff_contract_wrappers", + "test_cli": "run-p test_cli:test_typescript diff_contract_wrappers test_cli:lint", "test_cli:clean": "rm -rf test-cli/test_typescript/lib", "test_cli:build": "tsc --project test-cli/tsconfig.json", "test_cli:test_typescript": "mocha --require source-map-support/register --require make-promises-safe test-cli/test_typescript/lib/**/*_test.js --timeout 100000 --bail --exit", "test_cli:lint": "run-p test_cli:lint_solidity test_cli:lint_python # test_cli:lint_typescript # HACK: typescript lint disabled because prettier fails", "test_cli:lint_solidity": "solhint -c ../../contracts/.solhint.json test-cli/fixtures/contracts/*.sol", "test_cli:lint_typescript": "prettier --check ./test-cli/output/typescript/* --config ../../.prettierrc", - "test_cli:lint_python": "pip install -r test-cli/fixtures/python-requirements.txt && run-p test_cli:lint_python:black test_cli:lint_python:pylint", + "test_cli:lint_python": "pip install -r test-cli/fixtures/python-requirements.txt && run-p test_cli:lint_python:black test_cli:lint_python:mypy test_cli:lint_python:pylint", "test_cli:lint_python:black": "black --line-length=79 --check ./test-cli/output/python/*", + "test_cli:lint_python:mypy": "MYPYPATH=./stubs mypy ./test-cli/output/python", "test_cli:lint_python:pylint": "PYTHONPATH=../../python-packages/contract_wrappers/src pylint --rcfile=test-cli/fixtures/pylintrc ./test-cli/output/python/*", "rebuild_and_test": "run-s build test", "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", diff --git a/packages/abi-gen/src/index.ts b/packages/abi-gen/src/index.ts index 8a18eaf701..2bef521792 100644 --- a/packages/abi-gen/src/index.ts +++ b/packages/abi-gen/src/index.ts @@ -4,22 +4,14 @@ import chalk from 'chalk'; import * as changeCase from 'change-case'; import { execSync } from 'child_process'; import * as cliFormat from 'cli-format'; -import { - AbiDefinition, - ConstructorAbi, - ContractAbi, - DataItem, - DevdocOutput, - EventAbi, - MethodAbi, -} from 'ethereum-types'; +import { AbiDefinition, ConstructorAbi, ContractAbi, DevdocOutput, EventAbi, MethodAbi } from 'ethereum-types'; import { sync as globSync } from 'glob'; import * as Handlebars from 'handlebars'; import * as _ from 'lodash'; import * as mkdirp from 'mkdirp'; -import toposort = require('toposort'); import * as yargs from 'yargs'; +import { registerPythonHelpers } from './python_handlebars_helpers'; import { ContextData, ContractsBackend, ParamKind } from './types'; import { utils } from './utils'; @@ -146,154 +138,6 @@ function registerTypeScriptHelpers(): void { ); } -function registerPythonHelpers(): void { - Handlebars.registerHelper('equal', (lhs: any, rhs: any) => { - return lhs === rhs; - }); - Handlebars.registerHelper('safeString', (str: string) => new Handlebars.SafeString(str)); - Handlebars.registerHelper('parameterType', utils.solTypeToPyType.bind(utils)); - Handlebars.registerHelper('returnType', utils.solTypeToPyType.bind(utils)); - Handlebars.registerHelper('toPythonIdentifier', utils.toPythonIdentifier.bind(utils)); - Handlebars.registerHelper('sanitizeDevdocDetails', (_methodName: string, devdocDetails: string, indent: number) => { - // wrap to 80 columns, assuming given indent, so that generated - // docstrings can pass pycodestyle checks. also, replace repeated - // spaces, likely caused by leading indents in the Solidity, because - // they cause repeated spaces in the output, and in particular they may - // cause repeated spaces at the beginning of a line in the docstring, - // which leads to "unexpected indent" errors when generating - // documentation. - if (devdocDetails === undefined || devdocDetails.length === 0) { - return ''; - } - const columnsPerRow = 80; - return new Handlebars.SafeString( - `\n${cliFormat.wrap(devdocDetails.replace(/ +/g, ' ') || '', { - paddingLeft: ' '.repeat(indent), - width: columnsPerRow, - ansi: false, - })}\n`, - ); - }); - Handlebars.registerHelper('makeParameterDocstringRole', (name: string, description: string, indent: number) => { - let docstring = `:param ${name}:`; - if (description && description.length > 0) { - docstring = `${docstring} ${description}`; - } - return new Handlebars.SafeString(utils.wrapPythonDocstringRole(docstring, indent)); - }); - Handlebars.registerHelper( - 'makeReturnDocstringRole', - (description: string, indent: number) => - new Handlebars.SafeString( - utils.wrapPythonDocstringRole(`:returns: ${description.replace(/ +/g, ' ')}`, indent), - ), - ); - Handlebars.registerHelper( - 'makeEventParameterDocstringRole', - (eventName: string, indent: number) => - new Handlebars.SafeString( - utils.wrapPythonDocstringRole( - `:param tx_hash: hash of transaction emitting ${eventName} event`, - indent, - ), - ), - ); - Handlebars.registerHelper('tupleDefinitions', (abisJSON: string) => { - const abis: AbiDefinition[] = JSON.parse(abisJSON); - // build an array of objects, each of which has one key, the Python - // name of a tuple, with a string value holding the body of a Python - // class representing that tuple. Using a key-value object conveniently - // filters duplicate references to the same tuple. - const tupleBodies: { [pythonTupleName: string]: string } = {}; - // build an array of tuple dependencies, whose format conforms to the - // expected input to toposort, a function to do a topological sort, - // which will help us declare tuples in the proper order, avoiding - // references to tuples that haven't been declared yet. - const tupleDependencies: Array<[string, string]> = []; - for (const abi of abis) { - let parameters: DataItem[] = []; - if (abi.hasOwnProperty('inputs')) { - // HACK(feuGeneA): using "as MethodAbi" below, but abi - // could just as well be ConstructorAbi, EventAbi, etc. We - // just need to tell the TypeScript compiler that it's NOT - // FallbackAbi, or else it would complain, "Property - // 'inputs' does not exist on type 'AbiDefinition'. - // Property 'inputs' does not exist on type - // 'FallbackAbi'.", despite the enclosing if statement. - // tslint:disable:no-unnecessary-type-assertion - parameters = parameters.concat((abi as MethodAbi).inputs); - } - if (abi.hasOwnProperty('outputs')) { - // HACK(feuGeneA): same as described above, except here we - // KNOW that it's a MethodAbi, given the enclosing if - // statement, because that's the only AbiDefinition subtype - // that actually has an outputs field. - parameters = parameters.concat((abi as MethodAbi).outputs); - } - for (const parameter of parameters) { - utils.extractTuples(parameter, tupleBodies, tupleDependencies); - } - } - // build up a list of tuples to declare. the order they're pushed into - // this array is the order they will be declared. - const tuplesToDeclare = []; - // first push the ones that have dependencies - tuplesToDeclare.push(...toposort(tupleDependencies)); - // then push any remaining bodies (the ones that DON'T have - // dependencies) - for (const pythonTupleName in tupleBodies) { - if (!tuplesToDeclare.includes(pythonTupleName)) { - tuplesToDeclare.push(pythonTupleName); - } - } - // now iterate over those ordered tuples-to-declare, and prefix the - // corresponding class bodies with their class headers, to form full - // class declarations. - const tupleDeclarations = []; - for (const pythonTupleName of tuplesToDeclare) { - if (tupleBodies[pythonTupleName]) { - tupleDeclarations.push( - `class ${pythonTupleName}(TypedDict):\n """Python representation of a tuple or struct.\n\n Solidity compiler output does not include the names of structs that appear\n in method definitions. A tuple found in an ABI may have been written in\n Solidity as a literal, anonymous tuple, or it may have been written as a\n named \`struct\`:code:, but there is no way to tell from the compiler\n output. This class represents a tuple that appeared in a method\n definition. Its name is derived from a hash of that tuple's field names,\n and every method whose ABI refers to a tuple with that same list of field\n names will have a generated wrapper method that refers to this class.\n\n Any members of type \`bytes\`:code: should be encoded as UTF-8, which can be\n accomplished via \`str.encode("utf_8")\`:code:\n """${ - tupleBodies[pythonTupleName] - }`, - ); - } - } - // finally, join the class declarations together for the output file - return new Handlebars.SafeString(tupleDeclarations.join('\n\n\n')); - }); - Handlebars.registerHelper('docBytesIfNecessary', (abisJSON: string) => { - const abis: AbiDefinition[] = JSON.parse(abisJSON); - // see if any ABIs accept params of type bytes, and if so then emit - // explanatory documentation string. - for (const abi of abis) { - if (abi.hasOwnProperty('inputs')) { - // HACK(feuGeneA): using "as MethodAbi" below, but abi - // could just as well be ConstructorAbi, EventAbi, etc. We - // just need to tell the TypeScript compiler that it's NOT - // FallbackAbi, or else it would complain, "Property - // 'inputs' does not exist on type 'AbiDefinition'. - // Property 'inputs' does not exist on type - // 'FallbackAbi'.", despite the enclosing if statement. - // tslint:disable:no-unnecessary-type-assertion - if ((abi as MethodAbi).inputs) { - for (const input of (abi as MethodAbi).inputs) { - if (input.type === 'bytes') { - return new Handlebars.SafeString( - '\n\n All method parameters of type `bytes`:code: should be encoded as UTF-8,\n which can be accomplished via `str.encode("utf_8")`:code:.\n ', - ); - } - } - } - } - } - return ''; - }); - Handlebars.registerHelper( - 'toPythonClassname', - (sourceName: string) => new Handlebars.SafeString(changeCase.pascal(sourceName)), - ); -} if (args.language === 'TypeScript') { registerTypeScriptHelpers(); } else if (args.language === 'Python') { diff --git a/packages/abi-gen/src/python_handlebars_helpers.ts b/packages/abi-gen/src/python_handlebars_helpers.ts new file mode 100644 index 0000000000..f98cd9b62e --- /dev/null +++ b/packages/abi-gen/src/python_handlebars_helpers.ts @@ -0,0 +1,205 @@ +import * as changeCase from 'change-case'; +import * as cliFormat from 'cli-format'; +import * as Handlebars from 'handlebars'; +import toposort = require('toposort'); + +import { AbiDefinition, DataItem, MethodAbi } from 'ethereum-types'; + +import { utils } from './utils'; + +/** + * Register all Python-related Handlebars helpers + */ +export function registerPythonHelpers(): void { + Handlebars.registerHelper('equal', (lhs: any, rhs: any) => { + return lhs === rhs; + }); + Handlebars.registerHelper('safeString', (str: string) => new Handlebars.SafeString(str)); + Handlebars.registerHelper('parameterType', utils.solTypeToPyType.bind(utils)); + Handlebars.registerHelper('returnType', utils.solTypeToPyType.bind(utils)); + Handlebars.registerHelper('toPythonIdentifier', utils.toPythonIdentifier.bind(utils)); + Handlebars.registerHelper('sanitizeDevdocDetails', (_methodName: string, devdocDetails: string, indent: number) => { + // wrap to 80 columns, assuming given indent, so that generated + // docstrings can pass pycodestyle checks. also, replace repeated + // spaces, likely caused by leading indents in the Solidity, because + // they cause repeated spaces in the output, and in particular they may + // cause repeated spaces at the beginning of a line in the docstring, + // which leads to "unexpected indent" errors when generating + // documentation. + if (devdocDetails === undefined || devdocDetails.length === 0) { + return ''; + } + const columnsPerRow = 80; + return new Handlebars.SafeString( + `\n${cliFormat.wrap(devdocDetails.replace(/ +/g, ' ') || '', { + paddingLeft: ' '.repeat(indent), + width: columnsPerRow, + ansi: false, + })}\n`, + ); + }); + Handlebars.registerHelper('makeParameterDocstringRole', (name: string, description: string, indent: number) => { + let docstring = `:param ${name}:`; + if (description && description.length > 0) { + docstring = `${docstring} ${description}`; + } + return new Handlebars.SafeString(utils.wrapPythonDocstringRole(docstring, indent)); + }); + Handlebars.registerHelper( + 'makeReturnDocstringRole', + (description: string, indent: number) => + new Handlebars.SafeString( + utils.wrapPythonDocstringRole(`:returns: ${description.replace(/ +/g, ' ')}`, indent), + ), + ); + Handlebars.registerHelper( + 'makeEventParameterDocstringRole', + (eventName: string, indent: number) => + new Handlebars.SafeString( + utils.wrapPythonDocstringRole( + `:param tx_hash: hash of transaction emitting ${eventName} event`, + indent, + ), + ), + ); + Handlebars.registerHelper('tupleDefinitions', (abisJSON: string) => { + const abis: AbiDefinition[] = JSON.parse(abisJSON); + // build an array of objects, each of which has one key, the Python + // name of a tuple, with a string value holding the body of a Python + // class representing that tuple. Using a key-value object conveniently + // filters duplicate references to the same tuple. + const tupleBodies: { [pythonTupleName: string]: string } = {}; + // build an array of tuple dependencies, whose format conforms to the + // expected input to toposort, a function to do a topological sort, + // which will help us declare tuples in the proper order, avoiding + // references to tuples that haven't been declared yet. + const tupleDependencies: Array<[string, string]> = []; + for (const abi of abis) { + let parameters: DataItem[] = []; + if (abi.hasOwnProperty('inputs')) { + // HACK(feuGeneA): using "as MethodAbi" below, but abi + // could just as well be ConstructorAbi, EventAbi, etc. We + // just need to tell the TypeScript compiler that it's NOT + // FallbackAbi, or else it would complain, "Property + // 'inputs' does not exist on type 'AbiDefinition'. + // Property 'inputs' does not exist on type + // 'FallbackAbi'.", despite the enclosing if statement. + // tslint:disable:no-unnecessary-type-assertion + parameters = parameters.concat((abi as MethodAbi).inputs); + } + if (abi.hasOwnProperty('outputs')) { + // HACK(feuGeneA): same as described above, except here we + // KNOW that it's a MethodAbi, given the enclosing if + // statement, because that's the only AbiDefinition subtype + // that actually has an outputs field. + parameters = parameters.concat((abi as MethodAbi).outputs); + } + for (const parameter of parameters) { + utils.extractTuples(parameter, tupleBodies, tupleDependencies); + } + } + // build up a list of tuples to declare. the order they're pushed into + // this array is the order they will be declared. + const tuplesToDeclare = []; + // first push the ones that have dependencies + tuplesToDeclare.push(...toposort(tupleDependencies)); + // then push any remaining bodies (the ones that DON'T have + // dependencies) + for (const pythonTupleName in tupleBodies) { + if (!tuplesToDeclare.includes(pythonTupleName)) { + tuplesToDeclare.push(pythonTupleName); + } + } + // now iterate over those ordered tuples-to-declare, and prefix the + // corresponding class bodies with their class headers, to form full + // class declarations. + const tupleDeclarations = []; + for (const pythonTupleName of tuplesToDeclare) { + if (tupleBodies[pythonTupleName]) { + tupleDeclarations.push( + `class ${pythonTupleName}(TypedDict):\n """Python representation of a tuple or struct.\n\n Solidity compiler output does not include the names of structs that appear\n in method definitions. A tuple found in an ABI may have been written in\n Solidity as a literal, anonymous tuple, or it may have been written as a\n named \`struct\`:code:, but there is no way to tell from the compiler\n output. This class represents a tuple that appeared in a method\n definition. Its name is derived from a hash of that tuple's field names,\n and every method whose ABI refers to a tuple with that same list of field\n names will have a generated wrapper method that refers to this class.\n\n Any members of type \`bytes\`:code: should be encoded as UTF-8, which can be\n accomplished via \`str.encode("utf_8")\`:code:\n """${ + tupleBodies[pythonTupleName] + }`, + ); + } + } + // finally, join the class declarations together for the output file + return new Handlebars.SafeString(tupleDeclarations.join('\n\n\n')); + }); + Handlebars.registerHelper('docBytesIfNecessary', (abisJSON: string) => { + const abis: AbiDefinition[] = JSON.parse(abisJSON); + // see if any ABIs accept params of type bytes, and if so then emit + // explanatory documentation string. + for (const abi of abis) { + if (abi.hasOwnProperty('inputs')) { + // HACK(feuGeneA): using "as MethodAbi" below, but abi + // could just as well be ConstructorAbi, EventAbi, etc. We + // just need to tell the TypeScript compiler that it's NOT + // FallbackAbi, or else it would complain, "Property + // 'inputs' does not exist on type 'AbiDefinition'. + // Property 'inputs' does not exist on type + // 'FallbackAbi'.", despite the enclosing if statement. + // tslint:disable:no-unnecessary-type-assertion + if ((abi as MethodAbi).inputs) { + for (const input of (abi as MethodAbi).inputs) { + if (input.type === 'bytes') { + return new Handlebars.SafeString( + '\n\n All method parameters of type `bytes`:code: should be encoded as UTF-8,\n which can be accomplished via `str.encode("utf_8")`:code:.\n ', + ); + } + } + } + } + } + return ''; + }); + Handlebars.registerHelper( + 'toPythonClassname', + (sourceName: string) => new Handlebars.SafeString(changeCase.pascal(sourceName)), + ); + Handlebars.registerHelper( + 'makeOutputsValue', + /** + * Produces a Python expression representing the return value from a + * Solidity function. + * @param pythonVariable the name of the Python variable holding the value + * to be used to populate the output expression. + * @param abiOutputs the "outputs" object of the function's ABI. + */ + (pythonVariable: string, abiOutputs: DataItem[]) => { + if (abiOutputs.length === 1) { + return new Handlebars.SafeString(solValueToPyValue(pythonVariable, abiOutputs[0])); + } else { + let tupleValue = '('; + for (let i = 0; i < abiOutputs.length; i++) { + tupleValue += `${pythonVariable}[${i}],`; + } + tupleValue += ')'; + return new Handlebars.SafeString(tupleValue); + } + }, + ); +} + +function solValueToPyValue(pythonVariable: string, abiItem: DataItem): string { + const pythonTypeName = utils.solTypeToPyType(abiItem); + if (pythonTypeName.match(/List\[.*\]/) !== null) { + return `[${solValueToPyValue('element', { + ...abiItem, + type: abiItem.type.replace('[]', ''), + })} for element in ${pythonVariable}]`; + } else { + let pyValue = `${pythonTypeName}(`; + if (abiItem.components) { + let i = 0; + for (const component of abiItem.components) { + pyValue += `${component.name}=${pythonVariable}[${i}],`; + i++; + } + } else { + pyValue += pythonVariable; + } + pyValue += ')'; + return pyValue; + } +} diff --git a/packages/abi-gen/src/utils.ts b/packages/abi-gen/src/utils.ts index 8828ce009b..3327aca139 100644 --- a/packages/abi-gen/src/utils.ts +++ b/packages/abi-gen/src/utils.ts @@ -102,11 +102,14 @@ export const utils = { throw new Error(`Unknown Solidity type found: ${solType}`); } }, - solTypeToPyType(solType: string, components?: DataItem[]): string { + solTypeToPyType(dataItem: DataItem): string { + const solType = dataItem.type; const trailingArrayRegex = /\[\d*\]$/; if (solType.match(trailingArrayRegex)) { - const arrayItemSolType = solType.replace(trailingArrayRegex, ''); - const arrayItemPyType = utils.solTypeToPyType(arrayItemSolType, components); + const arrayItemPyType = utils.solTypeToPyType({ + ...dataItem, + type: dataItem.type.replace(trailingArrayRegex, ''), + }); const arrayPyType = `List[${arrayItemPyType}]`; return arrayPyType; } else { @@ -125,7 +128,7 @@ export const utils = { } const TUPLE_TYPE_REGEX = '^tuple$'; if (solType.match(TUPLE_TYPE_REGEX)) { - return utils.makePythonTupleName(components as DataItem[]); + return utils.makePythonTupleName(dataItem); } throw new Error(`Unknown Solidity type found: ${solType}`); } @@ -187,13 +190,21 @@ export const utils = { * simply concatenate all of the names of the components, and convert that * concatenation into PascalCase to conform to Python convention. */ - makePythonTupleName(tupleComponents: DataItem[]): string { - const lengthOfHashSuffix = 8; - return `Tuple0x${createHash('MD5') - .update(_.map(tupleComponents, component => component.name).join('_')) - .digest() - .toString('hex') - .substring(0, lengthOfHashSuffix)}`; + makePythonTupleName(tuple: DataItem): string { + if (tuple.internalType !== undefined) { + return tuple.internalType + .replace('struct ', '') + .replace('.', '') + .replace('[]', ''); + } else { + const tupleComponents = tuple.components; + const lengthOfHashSuffix = 8; + return `Tuple0x${createHash('MD5') + .update(_.map(tupleComponents, component => component.name).join('_')) + .digest() + .toString('hex') + .substring(0, lengthOfHashSuffix)}`; + } }, /** * @returns a string that is a Python code snippet that's intended to be @@ -203,10 +214,7 @@ export const utils = { makePythonTupleClassBody(tupleComponents: DataItem[]): string { let toReturn: string = ''; for (const tupleComponent of tupleComponents) { - toReturn = `${toReturn}\n\n ${tupleComponent.name}: ${utils.solTypeToPyType( - tupleComponent.type, - tupleComponent.components, - )}`; + toReturn = `${toReturn}\n\n ${tupleComponent.name}: ${utils.solTypeToPyType(tupleComponent)}`; } toReturn = `${toReturn}`; return toReturn; @@ -361,12 +369,12 @@ export const utils = { // Argument of type 'DataItem[] | undefined' is not assignable to parameter of type 'DataItem[]'. // Type 'undefined' is not assignable to type 'DataItem[]' // when the code below tries to access tupleDataItem.components. - const pythonTupleName = utils.makePythonTupleName(tupleDataItem.components); + const pythonTupleName = utils.makePythonTupleName(tupleDataItem); tupleBodies[pythonTupleName] = utils.makePythonTupleClassBody(tupleDataItem.components); for (const component of tupleDataItem.components) { if (component.type === 'tuple' || component.type === 'tuple[]') { tupleDependencies.push([ - utils.makePythonTupleName((component as TupleDataItem).components), // tslint:disable-line:no-unnecessary-type-assertion + utils.makePythonTupleName(component as TupleDataItem), // tslint:disable-line:no-unnecessary-type-assertion pythonTupleName, ]); utils.extractTuples(component, tupleBodies, tupleDependencies); diff --git a/packages/abi-gen/stubs/eth_account/__init__.pyi b/packages/abi-gen/stubs/eth_account/__init__.pyi new file mode 100644 index 0000000000..5caed9e96f --- /dev/null +++ b/packages/abi-gen/stubs/eth_account/__init__.pyi @@ -0,0 +1 @@ +class Account: ... diff --git a/packages/abi-gen/stubs/eth_account/local.pyi b/packages/abi-gen/stubs/eth_account/local.pyi new file mode 100644 index 0000000000..d0b3515699 --- /dev/null +++ b/packages/abi-gen/stubs/eth_account/local.pyi @@ -0,0 +1,3 @@ +class LocalAccount: + address: str + ... diff --git a/packages/abi-gen/stubs/hexbytes/__init__.pyi b/packages/abi-gen/stubs/hexbytes/__init__.pyi new file mode 100644 index 0000000000..bc88efe52a --- /dev/null +++ b/packages/abi-gen/stubs/hexbytes/__init__.pyi @@ -0,0 +1 @@ +class HexBytes: ... diff --git a/packages/abi-gen/stubs/web3/__init__.pyi b/packages/abi-gen/stubs/web3/__init__.pyi new file mode 100644 index 0000000000..1dfa4bb5f5 --- /dev/null +++ b/packages/abi-gen/stubs/web3/__init__.pyi @@ -0,0 +1,67 @@ +from typing import Any, Callable, Dict, List, Optional, Union + +from hexbytes import HexBytes +from eth_account.local import LocalAccount +from web3 import datastructures +from web3.contract import Contract +from web3.providers.base import BaseProvider + + +class Web3: + class HTTPProvider(BaseProvider): + ... + + def __init__(self, provider: BaseProvider) -> None: ... + + @staticmethod + def sha3( + primitive: Optional[Union[bytes, int, None]] = None, + text: Optional[str] = None, + hexstr: Optional[str] = None + ) -> bytes: ... + + @staticmethod + def isAddress(address: str) -> bool: ... + + class middleware_stack: + @staticmethod + def get(key: str) -> Callable: ... + + def inject( + self, middleware_func: object, layer: object + ) -> None: ... + + ... + + middleware_onion: middleware_stack + + class net: + version: str + ... + + + class Eth: + defaultAccount: str + accounts: List[str] + chainId: int + ... + + class account: + @staticmethod + def privateKeyToAccount(private_key: str) -> LocalAccount: ... + ... + + @staticmethod + def getTransactionReceipt(tx_hash: Union[HexBytes, bytes]) -> Any: ... + + @staticmethod + def contract(address: str, abi: Dict) -> Contract: ... + ... + + @staticmethod + def isAddress(address: str) -> bool: ... + ... + + eth: Eth + + ... diff --git a/packages/abi-gen/stubs/web3/contract.pyi b/packages/abi-gen/stubs/web3/contract.pyi new file mode 100644 index 0000000000..6f575e5b39 --- /dev/null +++ b/packages/abi-gen/stubs/web3/contract.pyi @@ -0,0 +1,17 @@ +from typing import Any + + +class Contract: + def call(self): ... + + functions: Any + + events: Any + ... + + +class ContractFunction: + def __call__(self, *args, **kwargs): + ... + + ... diff --git a/packages/abi-gen/stubs/web3/datastructures.pyi b/packages/abi-gen/stubs/web3/datastructures.pyi new file mode 100644 index 0000000000..fac8f29268 --- /dev/null +++ b/packages/abi-gen/stubs/web3/datastructures.pyi @@ -0,0 +1,5 @@ +class NamedElementOnion: + ... + +class AttributeDict: + ... \ No newline at end of file diff --git a/packages/abi-gen/stubs/web3/exceptions.pyi b/packages/abi-gen/stubs/web3/exceptions.pyi new file mode 100644 index 0000000000..83abf973d1 --- /dev/null +++ b/packages/abi-gen/stubs/web3/exceptions.pyi @@ -0,0 +1,2 @@ +class BadFunctionCallOutput(Exception): + ... diff --git a/packages/abi-gen/stubs/web3/providers/__init__.pyi b/packages/abi-gen/stubs/web3/providers/__init__.pyi new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/abi-gen/stubs/web3/providers/base.pyi b/packages/abi-gen/stubs/web3/providers/base.pyi new file mode 100644 index 0000000000..82ca9e3da1 --- /dev/null +++ b/packages/abi-gen/stubs/web3/providers/base.pyi @@ -0,0 +1,2 @@ +class BaseProvider: + ... diff --git a/packages/abi-gen/templates/Python/contract.handlebars b/packages/abi-gen/templates/Python/contract.handlebars index ab6e9682da..8e2d38231c 100644 --- a/packages/abi-gen/templates/Python/contract.handlebars +++ b/packages/abi-gen/templates/Python/contract.handlebars @@ -115,7 +115,7 @@ class {{contractName}}: functions = self._web3_eth.contract(address=to_checksum_address(contract_address), abi={{contractName}}.abi()).functions {{#each methods}} - self.{{toPythonIdentifier this.languageSpecificName}} = {{toPythonClassname this.languageSpecificName}}Method(web3_or_provider, contract_address, functions.{{this.name}}, validator) + self.{{toPythonIdentifier this.languageSpecificName}} = {{toPythonClassname this.languageSpecificName}}Method(web3_or_provider, contract_address, functions.{{this.name}}{{#if this.inputs}}, validator{{/if}}) {{/each}} {{/if}} diff --git a/packages/abi-gen/templates/Python/partials/call_return_type.handlebars b/packages/abi-gen/templates/Python/partials/call_return_type.handlebars new file mode 100644 index 0000000000..6c6f7092a4 --- /dev/null +++ b/packages/abi-gen/templates/Python/partials/call_return_type.handlebars @@ -0,0 +1,11 @@ +{{~#if outputs~}} +{{#if outputs.length}} +{{#singleReturnValue}} +{{#returnType outputs.[0]}}{{~/returnType~}} +{{/singleReturnValue}} +{{^singleReturnValue}} +Tuple[{{#each outputs}}{{#returnType this}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] +{{~/singleReturnValue}} +{{else}}None +{{/if}} +{{else}}None{{/if~}} diff --git a/packages/abi-gen/templates/Python/partials/method_class.handlebars b/packages/abi-gen/templates/Python/partials/method_class.handlebars index 2586142662..945009edb3 100644 --- a/packages/abi-gen/templates/Python/partials/method_class.handlebars +++ b/packages/abi-gen/templates/Python/partials/method_class.handlebars @@ -2,10 +2,10 @@ class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod): """Various interfaces to the {{this.name}} method.""" - def __init__(self, web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, validator: Validator=None): + def __init__(self, web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction{{#if inputs}}, validator: Validator=None{{/if}}): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address{{#if inputs}}, validator{{/if}}) + self._underlying_method = contract_function {{#if inputs}} def validate_and_normalize_inputs(self, {{> typed_params inputs=inputs}}): @@ -26,7 +26,7 @@ class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod): return ({{> params }}) {{/if}} - def call(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> {{> return_type outputs=outputs type='call'~}}: + def call(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> {{> call_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}} @@ -42,28 +42,37 @@ class {{toPythonClassname this.languageSpecificName}}Method(ContractMethod): ({{> 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()) + {{#hasReturnValue}}returned = {{/hasReturnValue}}self._underlying_method({{> params}}).call(tx_params.as_dict()) + {{#hasReturnValue}} + return {{makeOutputsValue 'returned' outputs}} + {{/hasReturnValue}} +{{^if this.constant}} 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()) + return self._underlying_method({{> params}}).transact(tx_params.as_dict()) + def build_transaction(self, {{#if inputs}}{{> typed_params inputs=inputs}}, {{/if}}tx_params: Optional[TxParams] = None) -> dict: + """Construct calldata to be used as input to the method.""" + {{#if inputs}} + ({{> params }}) = self.validate_and_normalize_inputs({{> params}}) + {{/if}} + tx_params = super().normalize_tx_params(tx_params) + return self._underlying_method({{> params}}).buildTransaction(tx_params.as_dict()) + +{{/if}} 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()) + return self._underlying_method({{> params}}).estimateGas(tx_params.as_dict()) diff --git a/packages/abi-gen/templates/Python/partials/return_type.handlebars b/packages/abi-gen/templates/Python/partials/return_type.handlebars index 7a1e833103..787121d413 100644 --- a/packages/abi-gen/templates/Python/partials/return_type.handlebars +++ b/packages/abi-gen/templates/Python/partials/return_type.handlebars @@ -4,10 +4,10 @@ Union[ {{~/if~}} {{#if outputs.length}} {{#singleReturnValue}} -{{#returnType outputs.0.type outputs.0.components}}{{~/returnType~}} +{{#returnType outputs.[0]}}{{~/returnType~}} {{/singleReturnValue}} {{^singleReturnValue}} -Tuple[{{#each outputs}}{{#returnType type components}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] +Tuple[{{#each outputs}}{{#returnType this}}{{/returnType}}{{#unless @last}}, {{/unless}}{{/each}}] {{~/singleReturnValue}} {{else}}None {{/if}}{{^if this.constant}}, Union[HexBytes, bytes]]{{/if~}} diff --git a/packages/abi-gen/templates/Python/partials/typed_params.handlebars b/packages/abi-gen/templates/Python/partials/typed_params.handlebars index a67cd0c201..d7bc663777 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}}{{^if @last}}, {{/if~}} +{{toPythonIdentifier name}}: {{#parameterType this}}{{/parameterType}}{{^if @last}}, {{/if~}} {{/each~}} diff --git a/packages/abi-gen/test-cli/fixtures/python-requirements.txt b/packages/abi-gen/test-cli/fixtures/python-requirements.txt index 8609654621..60cebd9185 100644 --- a/packages/abi-gen/test-cli/fixtures/python-requirements.txt +++ b/packages/abi-gen/test-cli/fixtures/python-requirements.txt @@ -1,6 +1,8 @@ black eth_utils hexbytes +mypy mypy_extensions pylint web3 +../../python-packages/contract_wrappers diff --git a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py index 3e0a520095..f5ad70668c 100644 --- a/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/abi_gen_dummy/__init__.py @@ -46,7 +46,7 @@ except ImportError: pass -class Tuple0xf95128ef(TypedDict): +class AbiGenDummyComplexInput(TypedDict): """Python representation of a tuple or struct. Solidity compiler output does not include the names of structs that appear @@ -69,7 +69,7 @@ class Tuple0xf95128ef(TypedDict): car: str -class Tuple0xa057bf41(TypedDict): +class AbiGenDummyComplexOutput(TypedDict): """Python representation of a tuple or struct. Solidity compiler output does not include the names of structs that appear @@ -85,7 +85,7 @@ class Tuple0xa057bf41(TypedDict): accomplished via `str.encode("utf_8")`:code: """ - input: Tuple0xf95128ef + input: AbiGenDummyComplexInput lorem: Union[bytes, str] @@ -94,7 +94,7 @@ class Tuple0xa057bf41(TypedDict): dolor: str -class Tuple0x246f9407(TypedDict): +class AbiGenDummyStructNotDirectlyUsedAnywhere(TypedDict): """Python representation of a tuple or struct. Solidity compiler output does not include the names of structs that appear @@ -113,7 +113,7 @@ class Tuple0x246f9407(TypedDict): aField: int -class Tuple0x1b9da225(TypedDict): +class AbiGenDummyNestedStructWithInnerStructNotUsedElsewhere(TypedDict): """Python representation of a tuple or struct. Solidity compiler output does not include the names of structs that appear @@ -129,10 +129,10 @@ class Tuple0x1b9da225(TypedDict): accomplished via `str.encode("utf_8")`:code: """ - innerStruct: Tuple0x246f9407 + innerStruct: AbiGenDummyStructNotDirectlyUsedAnywhere -class Tuple0xcf8ad995(TypedDict): +class AbiGenDummyStruct(TypedDict): """Python representation of a tuple or struct. Solidity compiler output does not include the names of structs that appear @@ -157,7 +157,7 @@ class Tuple0xcf8ad995(TypedDict): aString: str -class Tuple0xc9bdd2d5(TypedDict): +class AbiGenDummyNestedStruct(TypedDict): """Python representation of a tuple or struct. Solidity compiler output does not include the names of structs that appear @@ -173,7 +173,7 @@ class Tuple0xc9bdd2d5(TypedDict): accomplished via `str.encode("utf_8")`:code: """ - innerStruct: Tuple0xcf8ad995 + innerStruct: AbiGenDummyStruct description: str @@ -190,7 +190,7 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, a: List[Union[bytes, str]]): """Validate the inputs to the acceptsAnArrayOfBytes method.""" @@ -214,22 +214,7 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod): """ (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[Union[bytes, str]], 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()) + self._underlying_method(a).call(tx_params.as_dict()) def estimate_gas( self, a: List[Union[bytes, str]], tx_params: Optional[TxParams] = None @@ -237,7 +222,7 @@ class AcceptsAnArrayOfBytesMethod(ContractMethod): """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()) + return self._underlying_method(a).estimateGas(tx_params.as_dict()) class AcceptsBytesMethod(ContractMethod): @@ -252,7 +237,7 @@ class AcceptsBytesMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, a: Union[bytes, str]): """Validate the inputs to the acceptsBytes method.""" @@ -271,19 +256,7 @@ class AcceptsBytesMethod(ContractMethod): """ (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: Union[bytes, str], tx_params: Optional[TxParams] = None - ) -> Union[HexBytes, bytes]: - """Execute underlying contract method via eth_sendTransaction. - - :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()) + self._underlying_method(a).call(tx_params.as_dict()) def estimate_gas( self, a: Union[bytes, str], tx_params: Optional[TxParams] = None @@ -291,7 +264,7 @@ class AcceptsBytesMethod(ContractMethod): """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()) + return self._underlying_method(a).estimateGas(tx_params.as_dict()) class ComplexInputComplexOutputMethod(ContractMethod): @@ -306,9 +279,11 @@ class ComplexInputComplexOutputMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function - def validate_and_normalize_inputs(self, complex_input: Tuple0xf95128ef): + def validate_and_normalize_inputs( + self, complex_input: AbiGenDummyComplexInput + ): """Validate the inputs to the complexInputComplexOutput method.""" self.validator.assert_valid( method_name="complexInputComplexOutput", @@ -319,9 +294,9 @@ class ComplexInputComplexOutputMethod(ContractMethod): def call( self, - complex_input: Tuple0xf95128ef, + complex_input: AbiGenDummyComplexInput, tx_params: Optional[TxParams] = None, - ) -> Tuple0xa057bf41: + ) -> AbiGenDummyComplexOutput: """Execute underlying contract method via eth_call. Tests decoding when the input and output are complex. @@ -331,35 +306,25 @@ class ComplexInputComplexOutputMethod(ContractMethod): """ (complex_input) = self.validate_and_normalize_inputs(complex_input) tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(complex_input).call(tx_params.as_dict()) - - def send_transaction( - self, - complex_input: Tuple0xf95128ef, - tx_params: Optional[TxParams] = None, - ) -> Union[HexBytes, bytes]: - """Execute underlying contract method via eth_sendTransaction. - - Tests decoding when the input and output are complex. - - :param tx_params: transaction parameters - - """ - (complex_input) = self.validate_and_normalize_inputs(complex_input) - tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(complex_input).transact( + returned = self._underlying_method(complex_input).call( tx_params.as_dict() ) + return AbiGenDummyComplexOutput( + input=returned[0], + lorem=returned[1], + ipsum=returned[2], + dolor=returned[3], + ) def estimate_gas( self, - complex_input: Tuple0xf95128ef, + complex_input: AbiGenDummyComplexInput, tx_params: Optional[TxParams] = None, ) -> int: """Estimate gas consumption of method call.""" (complex_input) = self.validate_and_normalize_inputs(complex_input) tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(complex_input).estimateGas( + return self._underlying_method(complex_input).estimateGas( tx_params.as_dict() ) @@ -376,7 +341,7 @@ class EcrecoverFnMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs( self, @@ -428,37 +393,10 @@ class EcrecoverFnMethod(ContractMethod): """ (_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: Union[bytes, str], - v: int, - r: Union[bytes, str], - s: Union[bytes, str], - tx_params: Optional[TxParams] = None, - ) -> Union[HexBytes, bytes]: - """Execute underlying contract method via eth_sendTransaction. - - test that devdocs will be generated and that multiline devdocs will - look okay - - :param hash: description of some hash. Let's make this line super long - to demonstrate hanging indents for method params. It has to be more - than one hundred twenty columns. - :param r: ECDSA r output - :param s: ECDSA s output - :param v: some v, recovery id - :param tx_params: transaction parameters - :returns: the signerAddress that created this signature. this line too - is super long in order to demonstrate the proper hanging - indentation in generated code. - """ - (_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( + returned = self._underlying_method(_hash, v, r, s).call( tx_params.as_dict() ) + return str(returned) def estimate_gas( self, @@ -471,7 +409,7 @@ class EcrecoverFnMethod(ContractMethod): """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( + return self._underlying_method(_hash, v, r, s).estimateGas( tx_params.as_dict() ) @@ -484,22 +422,19 @@ class EmitSimpleEventMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function - def call( - self, tx_params: Optional[TxParams] = None - ) -> Union[None, Union[HexBytes, bytes]]: + def call(self, tx_params: Optional[TxParams] = None) -> None: """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()) + self._underlying_method().call(tx_params.as_dict()) def send_transaction( self, tx_params: Optional[TxParams] = None @@ -509,12 +444,17 @@ class EmitSimpleEventMethod(ContractMethod): :param tx_params: transaction parameters """ tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method().transact(tx_params.as_dict()) + return self._underlying_method().transact(tx_params.as_dict()) + + def build_transaction(self, tx_params: Optional[TxParams] = None) -> dict: + """Construct calldata to be used as input to the method.""" + tx_params = super().normalize_tx_params(tx_params) + return self._underlying_method().buildTransaction(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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class MethodReturningArrayOfStructsMethod(ContractMethod): @@ -525,38 +465,35 @@ class MethodReturningArrayOfStructsMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call( self, tx_params: Optional[TxParams] = None - ) -> List[Tuple0xcf8ad995]: + ) -> List[AbiGenDummyStruct]: """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()) + returned = self._underlying_method().call(tx_params.as_dict()) + return [ + AbiGenDummyStruct( + someBytes=element[0], + anInteger=element[1], + aDynamicArrayOfBytes=element[2], + aString=element[3], + ) + for element in returned + ] 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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class MethodReturningMultipleValuesMethod(ContractMethod): @@ -567,11 +504,10 @@ class MethodReturningMultipleValuesMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> Tuple[int, str]: """Execute underlying contract method via eth_call. @@ -580,23 +516,16 @@ class MethodReturningMultipleValuesMethod(ContractMethod): """ 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()) + returned = self._underlying_method().call(tx_params.as_dict()) + return ( + returned[0], + returned[1], + ) 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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod( @@ -609,36 +538,29 @@ class MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod( web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function - def call(self, tx_params: Optional[TxParams] = None) -> Tuple0x1b9da225: + def call( + self, tx_params: Optional[TxParams] = None + ) -> AbiGenDummyNestedStructWithInnerStructNotUsedElsewhere: """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()) + returned = self._underlying_method().call(tx_params.as_dict()) + return AbiGenDummyNestedStructWithInnerStructNotUsedElsewhere( + innerStruct=returned[0], + ) 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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class MultiInputMultiOutputMethod(ContractMethod): @@ -653,7 +575,7 @@ class MultiInputMultiOutputMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs( self, index_0: int, index_1: Union[bytes, str], index_2: str @@ -697,31 +619,13 @@ class MultiInputMultiOutputMethod(ContractMethod): index_0, index_1, index_2 ) tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0, index_1, index_2).call( + returned = self._underlying_method(index_0, index_1, index_2).call( tx_params.as_dict() ) - - def send_transaction( - self, - index_0: int, - index_1: Union[bytes, str], - index_2: str, - tx_params: Optional[TxParams] = None, - ) -> Union[HexBytes, bytes]: - """Execute underlying contract method via eth_sendTransaction. - - Tests decoding when the input and output are complex and have more than - one argument. - - :param tx_params: transaction parameters - - """ - (index_0, index_1, index_2) = self.validate_and_normalize_inputs( - index_0, index_1, index_2 - ) - tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0, index_1, index_2).transact( - tx_params.as_dict() + return ( + returned[0], + returned[1], + returned[2], ) def estimate_gas( @@ -736,7 +640,7 @@ class MultiInputMultiOutputMethod(ContractMethod): index_0, index_1, index_2 ) tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0, index_1, index_2).estimateGas( + return self._underlying_method(index_0, index_1, index_2).estimateGas( tx_params.as_dict() ) @@ -753,9 +657,9 @@ class NestedStructInputMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function - def validate_and_normalize_inputs(self, n: Tuple0xc9bdd2d5): + def validate_and_normalize_inputs(self, n: AbiGenDummyNestedStruct): """Validate the inputs to the nestedStructInput method.""" self.validator.assert_valid( method_name="nestedStructInput", @@ -765,7 +669,7 @@ class NestedStructInputMethod(ContractMethod): return n def call( - self, n: Tuple0xc9bdd2d5, tx_params: Optional[TxParams] = None + self, n: AbiGenDummyNestedStruct, tx_params: Optional[TxParams] = None ) -> None: """Execute underlying contract method via eth_call. @@ -774,27 +678,15 @@ class NestedStructInputMethod(ContractMethod): """ (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()) + self._underlying_method(n).call(tx_params.as_dict()) def estimate_gas( - self, n: Tuple0xc9bdd2d5, tx_params: Optional[TxParams] = None + self, n: AbiGenDummyNestedStruct, 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()) + return self._underlying_method(n).estimateGas(tx_params.as_dict()) class NestedStructOutputMethod(ContractMethod): @@ -805,36 +697,29 @@ class NestedStructOutputMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function - def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xc9bdd2d5: + def call( + self, tx_params: Optional[TxParams] = None + ) -> AbiGenDummyNestedStruct: """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()) + returned = self._underlying_method().call(tx_params.as_dict()) + return AbiGenDummyNestedStruct( + innerStruct=returned[0], description=returned[1], + ) 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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class NoInputNoOutputMethod(ContractMethod): @@ -845,11 +730,10 @@ class NoInputNoOutputMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: """Execute underlying contract method via eth_call. @@ -860,25 +744,12 @@ class NoInputNoOutputMethod(ContractMethod): """ 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. - - Tests decoding when both input and output are empty. - - :param tx_params: transaction parameters - - """ - tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method().transact(tx_params.as_dict()) + self._underlying_method().call(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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class NoInputSimpleOutputMethod(ContractMethod): @@ -889,11 +760,10 @@ class NoInputSimpleOutputMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> int: """Execute underlying contract method via eth_call. @@ -904,25 +774,13 @@ class NoInputSimpleOutputMethod(ContractMethod): """ 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. - - Tests decoding when input is empty and output is non-empty. - - :param tx_params: transaction parameters - - """ - tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method().transact(tx_params.as_dict()) + returned = self._underlying_method().call(tx_params.as_dict()) + return int(returned) 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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class NonPureMethodMethod(ContractMethod): @@ -933,22 +791,20 @@ class NonPureMethodMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function - def call( - self, tx_params: Optional[TxParams] = None - ) -> Union[int, Union[HexBytes, bytes]]: + def call(self, tx_params: Optional[TxParams] = None) -> int: """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()) + returned = self._underlying_method().call(tx_params.as_dict()) + return int(returned) def send_transaction( self, tx_params: Optional[TxParams] = None @@ -958,12 +814,17 @@ class NonPureMethodMethod(ContractMethod): :param tx_params: transaction parameters """ tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method().transact(tx_params.as_dict()) + return self._underlying_method().transact(tx_params.as_dict()) + + def build_transaction(self, tx_params: Optional[TxParams] = None) -> dict: + """Construct calldata to be used as input to the method.""" + tx_params = super().normalize_tx_params(tx_params) + return self._underlying_method().buildTransaction(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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class NonPureMethodThatReturnsNothingMethod(ContractMethod): @@ -974,22 +835,19 @@ class NonPureMethodThatReturnsNothingMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function - def call( - self, tx_params: Optional[TxParams] = None - ) -> Union[None, Union[HexBytes, bytes]]: + def call(self, tx_params: Optional[TxParams] = None) -> None: """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()) + self._underlying_method().call(tx_params.as_dict()) def send_transaction( self, tx_params: Optional[TxParams] = None @@ -999,12 +857,17 @@ class NonPureMethodThatReturnsNothingMethod(ContractMethod): :param tx_params: transaction parameters """ tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method().transact(tx_params.as_dict()) + return self._underlying_method().transact(tx_params.as_dict()) + + def build_transaction(self, tx_params: Optional[TxParams] = None) -> dict: + """Construct calldata to be used as input to the method.""" + tx_params = super().normalize_tx_params(tx_params) + return self._underlying_method().buildTransaction(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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class OverloadedMethod2Method(ContractMethod): @@ -1019,7 +882,7 @@ class OverloadedMethod2Method(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, a: str): """Validate the inputs to the overloadedMethod method.""" @@ -1038,19 +901,7 @@ class OverloadedMethod2Method(ContractMethod): """ (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: str, tx_params: Optional[TxParams] = None - ) -> Union[HexBytes, bytes]: - """Execute underlying contract method via eth_sendTransaction. - - :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()) + self._underlying_method(a).call(tx_params.as_dict()) def estimate_gas( self, a: str, tx_params: Optional[TxParams] = None @@ -1058,7 +909,7 @@ class OverloadedMethod2Method(ContractMethod): """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()) + return self._underlying_method(a).estimateGas(tx_params.as_dict()) class OverloadedMethod1Method(ContractMethod): @@ -1073,7 +924,7 @@ class OverloadedMethod1Method(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, a: int): """Validate the inputs to the overloadedMethod method.""" @@ -1092,19 +943,7 @@ class OverloadedMethod1Method(ContractMethod): """ (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: int, tx_params: Optional[TxParams] = None - ) -> Union[HexBytes, bytes]: - """Execute underlying contract method via eth_sendTransaction. - - :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()) + self._underlying_method(a).call(tx_params.as_dict()) def estimate_gas( self, a: int, tx_params: Optional[TxParams] = None @@ -1112,7 +951,7 @@ class OverloadedMethod1Method(ContractMethod): """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()) + return self._underlying_method(a).estimateGas(tx_params.as_dict()) class PureFunctionWithConstantMethod(ContractMethod): @@ -1123,11 +962,10 @@ class PureFunctionWithConstantMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> int: """Execute underlying contract method via eth_call. @@ -1136,23 +974,13 @@ class PureFunctionWithConstantMethod(ContractMethod): """ 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()) + returned = self._underlying_method().call(tx_params.as_dict()) + return int(returned) 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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class RequireWithConstantMethod(ContractMethod): @@ -1163,11 +991,10 @@ class RequireWithConstantMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: """Execute underlying contract method via eth_call. @@ -1176,23 +1003,12 @@ class RequireWithConstantMethod(ContractMethod): """ 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()) + self._underlying_method().call(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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class RevertWithConstantMethod(ContractMethod): @@ -1203,11 +1019,10 @@ class RevertWithConstantMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: """Execute underlying contract method via eth_call. @@ -1216,23 +1031,12 @@ class RevertWithConstantMethod(ContractMethod): """ 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()) + self._underlying_method().call(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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class SimpleInputNoOutputMethod(ContractMethod): @@ -1247,7 +1051,7 @@ class SimpleInputNoOutputMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, index_0: int): """Validate the inputs to the simpleInputNoOutput method.""" @@ -1270,21 +1074,7 @@ class SimpleInputNoOutputMethod(ContractMethod): """ (index_0) = self.validate_and_normalize_inputs(index_0) tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0).call(tx_params.as_dict()) - - def send_transaction( - self, index_0: int, tx_params: Optional[TxParams] = None - ) -> Union[HexBytes, bytes]: - """Execute underlying contract method via eth_sendTransaction. - - Tests decoding when input is not empty but output is empty. - - :param tx_params: transaction parameters - - """ - (index_0) = self.validate_and_normalize_inputs(index_0) - tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0).transact(tx_params.as_dict()) + self._underlying_method(index_0).call(tx_params.as_dict()) def estimate_gas( self, index_0: int, tx_params: Optional[TxParams] = None @@ -1292,7 +1082,9 @@ class SimpleInputNoOutputMethod(ContractMethod): """Estimate gas consumption of method call.""" (index_0) = self.validate_and_normalize_inputs(index_0) tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0).estimateGas(tx_params.as_dict()) + return self._underlying_method(index_0).estimateGas( + tx_params.as_dict() + ) class SimpleInputSimpleOutputMethod(ContractMethod): @@ -1307,7 +1099,7 @@ class SimpleInputSimpleOutputMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, index_0: int): """Validate the inputs to the simpleInputSimpleOutput method.""" @@ -1330,21 +1122,8 @@ class SimpleInputSimpleOutputMethod(ContractMethod): """ (index_0) = self.validate_and_normalize_inputs(index_0) tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0).call(tx_params.as_dict()) - - def send_transaction( - self, index_0: int, tx_params: Optional[TxParams] = None - ) -> Union[HexBytes, bytes]: - """Execute underlying contract method via eth_sendTransaction. - - Tests decoding when both input and output are non-empty. - - :param tx_params: transaction parameters - - """ - (index_0) = self.validate_and_normalize_inputs(index_0) - tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0).transact(tx_params.as_dict()) + returned = self._underlying_method(index_0).call(tx_params.as_dict()) + return int(returned) def estimate_gas( self, index_0: int, tx_params: Optional[TxParams] = None @@ -1352,7 +1131,9 @@ class SimpleInputSimpleOutputMethod(ContractMethod): """Estimate gas consumption of method call.""" (index_0) = self.validate_and_normalize_inputs(index_0) tx_params = super().normalize_tx_params(tx_params) - return self.underlying_method(index_0).estimateGas(tx_params.as_dict()) + return self._underlying_method(index_0).estimateGas( + tx_params.as_dict() + ) class SimplePureFunctionMethod(ContractMethod): @@ -1363,11 +1144,10 @@ class SimplePureFunctionMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> int: """Execute underlying contract method via eth_call. @@ -1376,23 +1156,13 @@ class SimplePureFunctionMethod(ContractMethod): """ 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()) + returned = self._underlying_method().call(tx_params.as_dict()) + return int(returned) 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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class SimplePureFunctionWithInputMethod(ContractMethod): @@ -1407,7 +1177,7 @@ class SimplePureFunctionWithInputMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, x: int): """Validate the inputs to the simplePureFunctionWithInput method.""" @@ -1428,19 +1198,8 @@ class SimplePureFunctionWithInputMethod(ContractMethod): """ (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()) + returned = self._underlying_method(x).call(tx_params.as_dict()) + return int(returned) def estimate_gas( self, x: int, tx_params: Optional[TxParams] = None @@ -1448,7 +1207,7 @@ class SimplePureFunctionWithInputMethod(ContractMethod): """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()) + return self._underlying_method(x).estimateGas(tx_params.as_dict()) class SimpleRequireMethod(ContractMethod): @@ -1459,11 +1218,10 @@ class SimpleRequireMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: """Execute underlying contract method via eth_call. @@ -1472,23 +1230,12 @@ class SimpleRequireMethod(ContractMethod): """ 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()) + self._underlying_method().call(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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class SimpleRevertMethod(ContractMethod): @@ -1499,11 +1246,10 @@ class SimpleRevertMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function def call(self, tx_params: Optional[TxParams] = None) -> None: """Execute underlying contract method via eth_call. @@ -1512,23 +1258,12 @@ class SimpleRevertMethod(ContractMethod): """ 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()) + self._underlying_method().call(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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class StructInputMethod(ContractMethod): @@ -1543,9 +1278,9 @@ class StructInputMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function - def validate_and_normalize_inputs(self, s: Tuple0xcf8ad995): + def validate_and_normalize_inputs(self, s: AbiGenDummyStruct): """Validate the inputs to the structInput method.""" self.validator.assert_valid( method_name="structInput", parameter_name="s", argument_value=s, @@ -1553,7 +1288,7 @@ class StructInputMethod(ContractMethod): return s def call( - self, s: Tuple0xcf8ad995, tx_params: Optional[TxParams] = None + self, s: AbiGenDummyStruct, tx_params: Optional[TxParams] = None ) -> None: """Execute underlying contract method via eth_call. @@ -1562,27 +1297,15 @@ class StructInputMethod(ContractMethod): """ (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()) + self._underlying_method(s).call(tx_params.as_dict()) def estimate_gas( - self, s: Tuple0xcf8ad995, tx_params: Optional[TxParams] = None + self, s: AbiGenDummyStruct, 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()) + return self._underlying_method(s).estimateGas(tx_params.as_dict()) class StructOutputMethod(ContractMethod): @@ -1593,13 +1316,12 @@ class StructOutputMethod(ContractMethod): web3_or_provider: Union[Web3, BaseProvider], contract_address: str, contract_function: ContractFunction, - validator: Validator = None, ): """Persist instance data.""" - super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + super().__init__(web3_or_provider, contract_address) + self._underlying_method = contract_function - def call(self, tx_params: Optional[TxParams] = None) -> Tuple0xcf8ad995: + def call(self, tx_params: Optional[TxParams] = None) -> AbiGenDummyStruct: """Execute underlying contract method via eth_call. a method that returns a struct @@ -1608,25 +1330,18 @@ class StructOutputMethod(ContractMethod): :returns: a Struct struct """ 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. - - 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()) + returned = self._underlying_method().call(tx_params.as_dict()) + return AbiGenDummyStruct( + someBytes=returned[0], + anInteger=returned[1], + aDynamicArrayOfBytes=returned[2], + aString=returned[3], + ) 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()) + return self._underlying_method().estimateGas(tx_params.as_dict()) class WithAddressInputMethod(ContractMethod): @@ -1641,7 +1356,7 @@ class WithAddressInputMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs( self, x: str, a: int, b: int, y: str, c: int @@ -1698,27 +1413,10 @@ class WithAddressInputMethod(ContractMethod): """ (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( + returned = self._underlying_method(x, a, b, y, c).call( tx_params.as_dict() ) + return str(returned) def estimate_gas( self, @@ -1732,7 +1430,7 @@ class WithAddressInputMethod(ContractMethod): """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( + return self._underlying_method(x, a, b, y, c).estimateGas( tx_params.as_dict() ) @@ -1749,7 +1447,7 @@ class WithdrawMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, wad: int): """Validate the inputs to the withdraw method.""" @@ -1760,9 +1458,7 @@ class WithdrawMethod(ContractMethod): wad = int(wad) return wad - def call( - self, wad: int, tx_params: Optional[TxParams] = None - ) -> Union[None, Union[HexBytes, bytes]]: + def call(self, wad: int, tx_params: Optional[TxParams] = None) -> None: """Execute underlying contract method via eth_call. :param tx_params: transaction parameters @@ -1770,7 +1466,7 @@ class WithdrawMethod(ContractMethod): """ (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()) + self._underlying_method(wad).call(tx_params.as_dict()) def send_transaction( self, wad: int, tx_params: Optional[TxParams] = None @@ -1781,7 +1477,17 @@ class WithdrawMethod(ContractMethod): """ (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()) + return self._underlying_method(wad).transact(tx_params.as_dict()) + + def build_transaction( + self, wad: int, tx_params: Optional[TxParams] = None + ) -> dict: + """Construct calldata to be used as input to the method.""" + (wad) = self.validate_and_normalize_inputs(wad) + tx_params = super().normalize_tx_params(tx_params) + return self._underlying_method(wad).buildTransaction( + tx_params.as_dict() + ) def estimate_gas( self, wad: int, tx_params: Optional[TxParams] = None @@ -1789,7 +1495,7 @@ class WithdrawMethod(ContractMethod): """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()) + return self._underlying_method(wad).estimateGas(tx_params.as_dict()) # pylint: disable=too-many-public-methods,too-many-instance-attributes @@ -2036,31 +1742,25 @@ class AbiGenDummy: ) self.emit_simple_event = EmitSimpleEventMethod( - web3_or_provider, - contract_address, - functions.emitSimpleEvent, - validator, + web3_or_provider, contract_address, functions.emitSimpleEvent ) self.method_returning_array_of_structs = MethodReturningArrayOfStructsMethod( web3_or_provider, contract_address, functions.methodReturningArrayOfStructs, - validator, ) self.method_returning_multiple_values = MethodReturningMultipleValuesMethod( web3_or_provider, contract_address, functions.methodReturningMultipleValues, - validator, ) self.method_using_nested_struct_with_inner_struct_not_used_elsewhere = MethodUsingNestedStructWithInnerStructNotUsedElsewhereMethod( web3_or_provider, contract_address, functions.methodUsingNestedStructWithInnerStructNotUsedElsewhere, - validator, ) self.multi_input_multi_output = MultiInputMultiOutputMethod( @@ -2078,38 +1778,25 @@ class AbiGenDummy: ) self.nested_struct_output = NestedStructOutputMethod( - web3_or_provider, - contract_address, - functions.nestedStructOutput, - validator, + web3_or_provider, contract_address, functions.nestedStructOutput ) self.no_input_no_output = NoInputNoOutputMethod( - web3_or_provider, - contract_address, - functions.noInputNoOutput, - validator, + web3_or_provider, contract_address, functions.noInputNoOutput ) self.no_input_simple_output = NoInputSimpleOutputMethod( - web3_or_provider, - contract_address, - functions.noInputSimpleOutput, - validator, + web3_or_provider, contract_address, functions.noInputSimpleOutput ) self.non_pure_method = NonPureMethodMethod( - web3_or_provider, - contract_address, - functions.nonPureMethod, - validator, + web3_or_provider, contract_address, functions.nonPureMethod ) self.non_pure_method_that_returns_nothing = NonPureMethodThatReturnsNothingMethod( web3_or_provider, contract_address, functions.nonPureMethodThatReturnsNothing, - validator, ) self.overloaded_method2 = OverloadedMethod2Method( @@ -2130,21 +1817,14 @@ class AbiGenDummy: web3_or_provider, contract_address, functions.pureFunctionWithConstant, - validator, ) self.require_with_constant = RequireWithConstantMethod( - web3_or_provider, - contract_address, - functions.requireWithConstant, - validator, + web3_or_provider, contract_address, functions.requireWithConstant ) self.revert_with_constant = RevertWithConstantMethod( - web3_or_provider, - contract_address, - functions.revertWithConstant, - validator, + web3_or_provider, contract_address, functions.revertWithConstant ) self.simple_input_no_output = SimpleInputNoOutputMethod( @@ -2162,10 +1842,7 @@ class AbiGenDummy: ) self.simple_pure_function = SimplePureFunctionMethod( - web3_or_provider, - contract_address, - functions.simplePureFunction, - validator, + web3_or_provider, contract_address, functions.simplePureFunction ) self.simple_pure_function_with_input = SimplePureFunctionWithInputMethod( @@ -2176,17 +1853,11 @@ class AbiGenDummy: ) self.simple_require = SimpleRequireMethod( - web3_or_provider, - contract_address, - functions.simpleRequire, - validator, + web3_or_provider, contract_address, functions.simpleRequire ) self.simple_revert = SimpleRevertMethod( - web3_or_provider, - contract_address, - functions.simpleRevert, - validator, + web3_or_provider, contract_address, functions.simpleRevert ) self.struct_input = StructInputMethod( @@ -2197,10 +1868,7 @@ class AbiGenDummy: ) self.struct_output = StructOutputMethod( - web3_or_provider, - contract_address, - functions.structOutput, - validator, + web3_or_provider, contract_address, functions.structOutput ) self.with_address_input = WithAddressInputMethod( diff --git a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py index 1b9e02f2e0..11c40fa5d5 100644 --- a/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py +++ b/packages/abi-gen/test-cli/output/python/test_lib_dummy/__init__.py @@ -58,7 +58,7 @@ class PublicAddConstantMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, x: int): """Validate the inputs to the publicAddConstant method.""" @@ -79,19 +79,8 @@ class PublicAddConstantMethod(ContractMethod): """ (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()) + returned = self._underlying_method(x).call(tx_params.as_dict()) + return int(returned) def estimate_gas( self, x: int, tx_params: Optional[TxParams] = None @@ -99,7 +88,7 @@ class PublicAddConstantMethod(ContractMethod): """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()) + return self._underlying_method(x).estimateGas(tx_params.as_dict()) class PublicAddOneMethod(ContractMethod): @@ -114,7 +103,7 @@ class PublicAddOneMethod(ContractMethod): ): """Persist instance data.""" super().__init__(web3_or_provider, contract_address, validator) - self.underlying_method = contract_function + self._underlying_method = contract_function def validate_and_normalize_inputs(self, x: int): """Validate the inputs to the publicAddOne method.""" @@ -133,19 +122,8 @@ class PublicAddOneMethod(ContractMethod): """ (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()) + returned = self._underlying_method(x).call(tx_params.as_dict()) + return int(returned) def estimate_gas( self, x: int, tx_params: Optional[TxParams] = None @@ -153,7 +131,7 @@ class PublicAddOneMethod(ContractMethod): """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()) + return self._underlying_method(x).estimateGas(tx_params.as_dict()) # pylint: disable=too-many-public-methods,too-many-instance-attributes diff --git a/packages/ethereum-types/src/index.ts b/packages/ethereum-types/src/index.ts index a23fbd5002..84509d06b4 100644 --- a/packages/ethereum-types/src/index.ts +++ b/packages/ethereum-types/src/index.ts @@ -134,6 +134,7 @@ export interface EventAbi { export interface DataItem { name: string; type: string; + internalType?: string; components?: DataItem[]; } diff --git a/python-packages/contract_wrappers/CHANGELOG.md b/python-packages/contract_wrappers/CHANGELOG.md index 83a085820e..d7ff55c21a 100644 --- a/python-packages/contract_wrappers/CHANGELOG.md +++ b/python-packages/contract_wrappers/CHANGELOG.md @@ -9,6 +9,11 @@ - Moved methods `jsdict_to_order()` and `order_to_jsdict()` from `zero_ex.contract_wrappers.exchange.types` to `zero_ex.contract_wrappers.order_conversions`. - Changed field name `zero_ex.contract_wrappers.tx_params.TxParams.gasPrice` to `.gas_price`. - Migrated to new version of 0x-contract-addresses. +- Made the `underlying_method` field on ContractMethod private by prefixing its name with an underscore. +- Corrected return types and values for call() interface to generated method wrappers. (Fixes #2298.) +- Removed `send_transaction()` method from ContractMethod instances for underlying Solidity methods that are const (view/pure). +- Added a `build_transaction()` method to instances of ContractMethod for non-const Solidity methods. +- Removed `validator` argument from ContractMethod instances for underlying Solidity methods that lack inputs. ## 1.1.0 - 2019-08-14 diff --git a/python-packages/contract_wrappers/src/index.rst b/python-packages/contract_wrappers/src/index.rst index 005b33981d..3f14908678 100644 --- a/python-packages/contract_wrappers/src/index.rst +++ b/python-packages/contract_wrappers/src/index.rst @@ -201,23 +201,23 @@ zero_ex.contract_wrappers.exchange.types zero_ex.contract_wrappers.exchange: Generated Tuples ==================================================== -.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x6ca34a6f +.. autoclass:: zero_ex.contract_wrappers.exchange.LibOrderOrder This is the generated class representing `the Order struct `_. -.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x735c43e3 +.. autoclass:: zero_ex.contract_wrappers.exchange.LibFillResultsFillResults This is the generated class representing `the FillResults struct `_. -.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x4c5ca29b +.. autoclass:: zero_ex.contract_wrappers.exchange.LibFillResultsMatchedFillResults This is the generated class representing `the MatchedFillResults struct `_. -.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0xb1e4a1ae +.. autoclass:: zero_ex.contract_wrappers.exchange.LibOrderOrderInfo This is the generated class representing `the OrderInfo struct `_. -.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0xdabc15fe +.. autoclass:: zero_ex.contract_wrappers.exchange.LibZeroExTransactionZeroExTransaction This is the generated class representing `the ZeroExTransaction struct `_. 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 a1bf3510f6..23e9f591ee 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 @@ -171,7 +171,7 @@ Now we'll have our Taker fill the order. But before filling an order, one may wish to check that it's actually fillable: >>> from zero_ex.contract_wrappers.exchange.types import OrderStatus ->>> OrderStatus(exchange.get_order_info.call(order)[0]) +>>> OrderStatus(exchange.get_order_info.call(order)["orderStatus"]) The `takerAssetAmount`:code: parameter specifies the amount of tokens (in this @@ -181,13 +181,18 @@ completely, but partial fills are possible too. One may wish to first call the method in a read-only way, to ensure that it will not revert, and to validate that the return data is as expected: ->>> exchange.fill_order.call( +>>> from pprint import pprint +>>> pprint(exchange.fill_order.call( ... order=order, ... taker_asset_fill_amount=order["takerAssetAmount"], ... signature=maker_signature, ... tx_params=TxParams(from_=taker_address) -... ) -(100000000000000000, 100000000000000000, 0, 0, 0) +... )) +{'makerAssetFilledAmount': 100000000000000000, + 'makerFeePaid': 0, + 'protocolFeePaid': 0, + 'takerAssetFilledAmount': 100000000000000000, + 'takerFeePaid': 0} Finally, submit the transaction: @@ -203,7 +208,6 @@ the exchange wrapper: >>> exchange.get_fill_event(tx_hash) (AttributeDict({'args': ...({'makerAddress': ...}), 'event': 'Fill', ...}),) ->>> from pprint import pprint >>> pprint(exchange.get_fill_event(tx_hash)[0].args.__dict__) {'feeRecipientAddress': '0x0000000000000000000000000000000000000000', 'makerAddress': '0x...', diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py index 500732d6bd..d324655f72 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/exchange/types.py @@ -5,20 +5,16 @@ encountered in the Exchange contract's ABI. However, they have weird names, containing hashes of the tuple's field names, because the name of a Solidity `struct`:code: isn't conveyed through the ABI. This module provides type aliases with human-friendly names. - -Converting between the JSON wire format and the types accepted by Web3.py (eg -`bytes` vs `str`) can be onerous. This module provides conveniences for -converting Exchange structs between JSON and Python objects. """ from enum import auto, Enum from . import ( - Tuple0x735c43e3, - Tuple0x6ca34a6f, - Tuple0x4c5ca29b, - Tuple0xdabc15fe, - Tuple0xb1e4a1ae, + LibFillResultsFillResults, + LibOrderOrder, + LibFillResultsMatchedFillResults, + LibZeroExTransactionZeroExTransaction, + LibOrderOrderInfo, ) @@ -29,43 +25,43 @@ from . import ( # of each of these classes. -class FillResults(Tuple0x735c43e3): +class FillResults(LibFillResultsFillResults): """The `FillResults`:code: Solidity struct. Also known as - `zero_ex.contract_wrappers.exchange.Tuple0x735c43e3`:py:class:. + `zero_ex.contract_wrappers.exchange.LibFillResultsFillResults`:py:class:. """ -class Order(Tuple0x6ca34a6f): +class Order(LibOrderOrder): """The `Order`:code: Solidity struct. Also known as - `zero_ex.contract_wrappers.exchange.Tuple0x6ca34a6f`:py:class:. + `zero_ex.contract_wrappers.exchange.LibOrderOrder`:py:class:. """ -class MatchedFillResults(Tuple0x4c5ca29b): +class MatchedFillResults(LibFillResultsMatchedFillResults): """The `MatchedFillResults`:code: Solidity struct. Also known as - `zero_ex.contract_wrappers.exchange.Tuple0x4c5ca29b`:py:class:. + `zero_ex.contract_wrappers.exchange.LibFillResultsMatchedFillResults`:py:class:. """ -class ZeroExTransaction(Tuple0xdabc15fe): +class ZeroExTransaction(LibZeroExTransactionZeroExTransaction): """The `ZeroExTransaction`:code: Solidity struct. Also known as - `zero_ex.contract_wrappers.exchange.Tuple0xdabc15fe`:py:class:. + `zero_ex.contract_wrappers.exchange.LibZeroExTransactionZeroExTransaction`:py:class:. """ -class OrderInfo(Tuple0xb1e4a1ae): +class OrderInfo(LibOrderOrderInfo): """The `OrderInfo`:code: Solidity struct. Also known as - `zero_ex.contract_wrappers.exchange.Tuple0xb1e4a1ae`:py:class:. + `zero_ex.contract_wrappers.exchange.LibOrderOrderInfo`:py:class:. """ diff --git a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py index 138e125eeb..a639ffe1c5 100644 --- a/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py +++ b/python-packages/contract_wrappers/src/zero_ex/contract_wrappers/order_conversions.py @@ -1,4 +1,9 @@ -"""Utilities to convert between JSON and Python-native objects.""" +"""Utilities to convert between JSON and Python-native objects. + +Converting between the JSON wire format and the types accepted by Web3.py (eg +`bytes` vs `str`) can be onerous. This module provides conveniences for +converting Exchange structs between JSON and Python objects. +""" from copy import copy from typing import cast, Dict, Union diff --git a/python-packages/contract_wrappers/test/test_exchange_wrapper.py b/python-packages/contract_wrappers/test/test_exchange_wrapper.py index 1b5e101cde..e4bc644f10 100644 --- a/python-packages/contract_wrappers/test/test_exchange_wrapper.py +++ b/python-packages/contract_wrappers/test/test_exchange_wrapper.py @@ -91,11 +91,11 @@ def test_exchange_wrapper__fill_order( signature=order_signature, tx_params=TxParams(from_=taker), ) - assert fill_results[0] == 1 - assert fill_results[1] == 1 - assert fill_results[2] == 0 - assert fill_results[3] == 0 - assert fill_results[4] == 0 + assert fill_results["makerAssetFilledAmount"] == 1 + assert fill_results["takerAssetFilledAmount"] == 1 + assert fill_results["makerFeePaid"] == 0 + assert fill_results["takerFeePaid"] == 0 + assert fill_results["protocolFeePaid"] == 0 tx_hash = exchange_wrapper.fill_order.send_transaction( order=order, 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 ab30fed4c1..a97cce2a56 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 @@ -32,37 +32,12 @@ configured to connect to any JSON-RPC endpoint, on any network. The examples below assume that Launch Kit is connected to a Ganache development network accessible at `http://localhost:8545`:code:. -To replicate this setup, one could run the following commands: - -:: - - docker run -d -p 8545:8545 0xorg/ganache-cli - - docker run -d -p 60557:60557 --network host \ - -e ETHEREUM_RPC_URL=http://localhost:8545 \ - -e ETHEREUM_NETWORK_ID=50 \ - -e ETHEREUM_CHAIN_ID=1337 \ - -e USE_BOOTSTRAP_LIST=false \ - -e VERBOSITY=3 \ - -e PRIVATE_KEY_PATH= \ - -e BLOCK_POLLING_INTERVAL=5s \ - -e P2P_LISTEN_PORT=60557 - 0xorg/mesh:6.0.0-beta-0xv3 - - docker run -d --network host \ - -e RPC_URL=http://localhost:8545 \ - -e CHAIN_ID=1337 \ - -e FEE_RECIPIENT=0x0000000000000000000000000000000000000001 \ - -e MAKER_FEE_UNIT_AMOUNT=0 \ - -e TAKER_FEE_UNIT_AMOUNT=0 - -e MESH_ENDPOINT=ws://localhost:60557 - -e WHITELIST_ALL_TOKENS=True \ - 0xorg/launch-kit-ci - -(Note: This will only work on Linux, because `--network host`:code: only works -on Linux. For other platforms one would have to clone `the 0x-launch-kit-backend -repository `_ and build and start -the server.) +These examples are automatically verified by spinning up docker images +`0xorg/ganache-cli`, `0xorg/mesh`, and `0xorg/launch-kit-backend`. You can +replicate this environment yourself by using `this docker-compose.yml file +`_. +(Note: This will only work on Linux, because it uses `network_mode: +"host"`:code:, which only works on Linux.) Configure and create an API client instance -------------------------------------------