abi-gen/Py: fix incorrect method return types and other small issues (#2345)
* .gitignore gen'd Python staking contract wrappers * abi-gen/test-cli: check Python type hints in lint * sra_client.py: Update doc for replicating examples * abi-gen/Py: fix call() return type incl. tx hash Previously, generated wrappers for contract methods were including type hints that suggested that a call() (as opposed to a send_transaction()) might return either the underlying return type or a transaction hash. This doesn't make sense because a call() will never return a TX hash. Now, the type hint just has the return type of the underlying method. * abi-gen: fix test_cli:lint checking wrong code test_cli:lint is meant to be a rudimentary test of the code generated by abi-gen. However, previously, this script was incorporated into `yarn lint`, and in CircleCI `static-tests` runs independently of `build`. Consequently, the runs of test_cli:lint were checking the OLD code, which was previously generated and checked in to git, NOT the code generated with the version of abi-gen represented by the git repo. Now, test_cli:lint happens during `yarn test` rather than `yarn lint`, because `yarn test` IS dependent on `yarn build`. * contract_wrappers.py: fix misplaced doc Previously, the routines `order_to_jsdict()` and `jsdict_to_order()` were moved from contract_wrappers.exchange.types to contract_wrappers.order_conversions. However, the module-level docstring describing those routines was accidentally left behind in exchange.types. * abi-gen/Py: stop documenting return types for TXs Previously the send_transaction() interface included docstring documentation for the return types of the contract method, but that doesn't make any sense because send_transaction() returns a transaction hash rather than any actual return values. * abi-gen/Py: stop gen'ing send_tx for const methods * abi-gen/Py: add build_tx to contract methods * abi-gen/Py: fix incorrect method return types Fixes #2298 . * abi-gen/Py: rm validator arg to no-input methods * abi-gen: mv Py Handlebars helpers to own module Move all existing Python-related Handlebars helpers to the newly created python_handlebars_helpers module. * abi-gen: refactor internal interface No functionality is changed. Sole purpose of this commit is to facilitate an upcoming commit. * abi-gen: refactor internal interface No functionality is changed. Sole purpose of this commit is to facilitate an upcoming commit. * abi-gen/Py: name tuples w/internalType, not hash Use the new `internalType` field on the `DataItem`s in the contract artifact to give generated tuple classes a better name than just hashing their component field names. * Fix CI errors * abi-gen/Py/wrapper: make internal member private * Update CHANGELOGs
This commit is contained in:
parent
9e3cc379ed
commit
df97b20913
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -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",
|
||||
|
@ -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') {
|
||||
|
205
packages/abi-gen/src/python_handlebars_helpers.ts
Normal file
205
packages/abi-gen/src/python_handlebars_helpers.ts
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
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);
|
||||
|
1
packages/abi-gen/stubs/eth_account/__init__.pyi
Normal file
1
packages/abi-gen/stubs/eth_account/__init__.pyi
Normal file
@ -0,0 +1 @@
|
||||
class Account: ...
|
3
packages/abi-gen/stubs/eth_account/local.pyi
Normal file
3
packages/abi-gen/stubs/eth_account/local.pyi
Normal file
@ -0,0 +1,3 @@
|
||||
class LocalAccount:
|
||||
address: str
|
||||
...
|
1
packages/abi-gen/stubs/hexbytes/__init__.pyi
Normal file
1
packages/abi-gen/stubs/hexbytes/__init__.pyi
Normal file
@ -0,0 +1 @@
|
||||
class HexBytes: ...
|
67
packages/abi-gen/stubs/web3/__init__.pyi
Normal file
67
packages/abi-gen/stubs/web3/__init__.pyi
Normal file
@ -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
|
||||
|
||||
...
|
17
packages/abi-gen/stubs/web3/contract.pyi
Normal file
17
packages/abi-gen/stubs/web3/contract.pyi
Normal file
@ -0,0 +1,17 @@
|
||||
from typing import Any
|
||||
|
||||
|
||||
class Contract:
|
||||
def call(self): ...
|
||||
|
||||
functions: Any
|
||||
|
||||
events: Any
|
||||
...
|
||||
|
||||
|
||||
class ContractFunction:
|
||||
def __call__(self, *args, **kwargs):
|
||||
...
|
||||
|
||||
...
|
5
packages/abi-gen/stubs/web3/datastructures.pyi
Normal file
5
packages/abi-gen/stubs/web3/datastructures.pyi
Normal file
@ -0,0 +1,5 @@
|
||||
class NamedElementOnion:
|
||||
...
|
||||
|
||||
class AttributeDict:
|
||||
...
|
2
packages/abi-gen/stubs/web3/exceptions.pyi
Normal file
2
packages/abi-gen/stubs/web3/exceptions.pyi
Normal file
@ -0,0 +1,2 @@
|
||||
class BadFunctionCallOutput(Exception):
|
||||
...
|
0
packages/abi-gen/stubs/web3/providers/__init__.pyi
Normal file
0
packages/abi-gen/stubs/web3/providers/__init__.pyi
Normal file
2
packages/abi-gen/stubs/web3/providers/base.pyi
Normal file
2
packages/abi-gen/stubs/web3/providers/base.pyi
Normal file
@ -0,0 +1,2 @@
|
||||
class BaseProvider:
|
||||
...
|
@ -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}}
|
||||
|
@ -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~}}
|
@ -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())
|
||||
|
@ -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~}}
|
||||
|
@ -1,3 +1,3 @@
|
||||
{{#each inputs}}
|
||||
{{toPythonIdentifier name}}: {{#parameterType type components}}{{/parameterType}}{{^if @last}}, {{/if~}}
|
||||
{{toPythonIdentifier name}}: {{#parameterType this}}{{/parameterType}}{{^if @last}}, {{/if~}}
|
||||
{{/each~}}
|
||||
|
@ -1,6 +1,8 @@
|
||||
black
|
||||
eth_utils
|
||||
hexbytes
|
||||
mypy
|
||||
mypy_extensions
|
||||
pylint
|
||||
web3
|
||||
../../python-packages/contract_wrappers
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -134,6 +134,7 @@ export interface EventAbi {
|
||||
export interface DataItem {
|
||||
name: string;
|
||||
type: string;
|
||||
internalType?: string;
|
||||
components?: DataItem[];
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 <https://0x.org/docs/contracts#structs-Order>`_.
|
||||
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x735c43e3
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.LibFillResultsFillResults
|
||||
|
||||
This is the generated class representing `the FillResults struct <https://0x.org/docs/contracts#structs-FillResults>`_.
|
||||
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0x4c5ca29b
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.LibFillResultsMatchedFillResults
|
||||
|
||||
This is the generated class representing `the MatchedFillResults struct <https://0x.org/docs/contracts#structs-MatchedFillResults>`_.
|
||||
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0xb1e4a1ae
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.LibOrderOrderInfo
|
||||
|
||||
This is the generated class representing `the OrderInfo struct <https://0x.org/docs/contracts#structs-OrderInfo>`_.
|
||||
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.Tuple0xdabc15fe
|
||||
.. autoclass:: zero_ex.contract_wrappers.exchange.LibZeroExTransactionZeroExTransaction
|
||||
|
||||
This is the generated class representing `the ZeroExTransaction struct <https://0x.org/docs/contracts#structs-ZeroExTransaction>`_.
|
||||
|
||||
|
@ -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"])
|
||||
<OrderStatus.FILLABLE: 3>
|
||||
|
||||
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...',
|
||||
|
@ -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:.
|
||||
"""
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 <https://github.com/0xProject/0x-launch-kit-backend>`_ 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
|
||||
<https://github.com/0xProject/0x-monorepo/blob/development/python-packages/sra_client/test/relayer/docker-compose.yml>`_.
|
||||
(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
|
||||
-------------------------------------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user