diff options
Diffstat (limited to 'packages/contract-wrappers')
57 files changed, 6642 insertions, 5534 deletions
diff --git a/packages/contract-wrappers/CHANGELOG.json b/packages/contract-wrappers/CHANGELOG.json index e34b50c1a..bee5a4de9 100644 --- a/packages/contract-wrappers/CHANGELOG.json +++ b/packages/contract-wrappers/CHANGELOG.json @@ -1,9 +1,13 @@ [ { - "version": "0.0.6", + "version": "1.0.0", "changes": [ { "note": "Update blockstream to v5.0 and propogate up caught errors to active subscriptions" + }, + { + "note": "Update to v2 of 0x rpotocol", + "pr": 822 } ] }, diff --git a/packages/contract-wrappers/package.json b/packages/contract-wrappers/package.json index 373e023bd..8c65c8cca 100644 --- a/packages/contract-wrappers/package.json +++ b/packages/contract-wrappers/package.json @@ -1,6 +1,6 @@ { "name": "@0xproject/contract-wrappers", - "version": "0.0.5", + "version": "0.0.6", "description": "Smart TS wrappers for 0x smart contracts", "keywords": [ "0xproject", @@ -13,23 +13,20 @@ "scripts": { "watch_without_deps": "yarn pre_build && tsc -w", "build": "yarn pre_build && tsc && copyfiles -u 3 './lib/src/monorepo_scripts/**/*' ./scripts", - "pre_build": "run-s generate_contract_wrappers update_test_artifacts update_compact_artifacts", - "generate_contract_wrappers": "abi-gen --abis 'src/compact_artifacts/@(Exchange|Token|TokenTransferProxy|EtherToken|TokenRegistry|DummyToken).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'", + "pre_build": "run-s generate_contract_wrappers update_artifacts", + "generate_contract_wrappers": "abi-gen --abis 'src/artifacts/@(Exchange|DummyERC20Token|DummyERC721Token|ZRXToken|ERC20Token|ERC721Token|WETH9|ERC20Proxy|ERC721Proxy).json' --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers", "lint": "tslint --project . --exclude **/src/contract_wrappers/**/* --exclude **/lib/**/*", "test:circleci": "run-s test:coverage", "test": "yarn run_mocha", "rebuild_and_test": "run-s build test", "test:coverage": "nyc npm run test --all && yarn coverage:report:lcov", "coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info", - "update_compact_artifacts": "copyfiles -u 2 './src/compact_artifacts/**/*.json' ./lib/src/compact_artifacts", - "update_test_artifacts": "for i in ${npm_package_config_contracts}; do copyfiles -u 4 ../migrations/artifacts/1.0.0/$i.json test/artifacts; done;", + "update_artifacts": "copyfiles -u 2 './src/artifacts/**/*.json' ./lib/src/artifacts", "clean": "shx rm -rf _bundles lib test_temp scripts test/artifacts src/contract_wrappers/generated", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js lib/test/global_hooks.js --timeout 10000 --bail --exit", "manual:postpublish": "yarn build; node ./scripts/postpublish.js" }, "config": { - "compact_artifacts": "Exchange DummyToken ZRXToken Token EtherToken TokenTransferProxy TokenRegistry", - "contracts": "Exchange DummyToken ZRXToken Token WETH9 TokenTransferProxy_v1 MultiSigWallet MultiSigWalletWithTimeLock MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress MaliciousToken TokenRegistry Arbitrage EtherDelta AccountLevels", "postpublish": { "assets": [ "packages/contract-wrappers/_bundles/index.js", @@ -77,15 +74,15 @@ "web3-provider-engine": "14.0.6" }, "dependencies": { - "@0xproject/assert": "^0.2.12", + "@0xproject/assert": "^0.3.0", "@0xproject/base-contract": "^0.3.4", - "@0xproject/fill-scenarios": "^0.0.4", - "@0xproject/json-schemas": "0.8.1", - "@0xproject/order-utils": "^0.0.7", - "@0xproject/types": "^0.8.1", + "@0xproject/order-utils": "^1.0.0", "@0xproject/typescript-typings": "^0.4.1", "@0xproject/utils": "^0.7.1", "@0xproject/web3-wrapper": "^0.7.1", + "@0xproject/fill-scenarios": "^1.0.0", + "@0xproject/json-schemas": "^1.0.0", + "@0xproject/types": "^1.0.0", "ethereum-types": "^0.0.2", "ethereumjs-blockstream": "5.0.0", "ethereumjs-util": "^5.1.1", diff --git a/packages/contract-wrappers/src/artifacts.ts b/packages/contract-wrappers/src/artifacts.ts index 13587984c..742d0e1b2 100644 --- a/packages/contract-wrappers/src/artifacts.ts +++ b/packages/contract-wrappers/src/artifacts.ts @@ -1,18 +1,23 @@ -import { Artifact } from '@0xproject/types'; +import { ContractArtifact } from '@0xproject/sol-compiler'; + +import * as DummyERC20Token from './artifacts/DummyERC20Token.json'; +import * as DummyERC721Token from './artifacts/DummyERC721Token.json'; +import * as ERC20Proxy from './artifacts/ERC20Proxy.json'; +import * as ERC20Token from './artifacts/ERC20Token.json'; +import * as ERC721Proxy from './artifacts/ERC721Proxy.json'; +import * as ERC721Token from './artifacts/ERC721Token.json'; +import * as Exchange from './artifacts/Exchange.json'; +import * as EtherToken from './artifacts/WETH9.json'; +import * as ZRXToken from './artifacts/ZRXToken.json'; -import * as DummyToken from './compact_artifacts/DummyToken.json'; -import * as EtherToken from './compact_artifacts/EtherToken.json'; -import * as Exchange from './compact_artifacts/Exchange.json'; -import * as Token from './compact_artifacts/Token.json'; -import * as TokenRegistry from './compact_artifacts/TokenRegistry.json'; -import * as TokenTransferProxy from './compact_artifacts/TokenTransferProxy.json'; -import * as ZRX from './compact_artifacts/ZRX.json'; export const artifacts = { - ZRX: (ZRX as any) as Artifact, - DummyToken: (DummyToken as any) as Artifact, - Token: (Token as any) as Artifact, - Exchange: (Exchange as any) as Artifact, - EtherToken: (EtherToken as any) as Artifact, - TokenRegistry: (TokenRegistry as any) as Artifact, - TokenTransferProxy: (TokenTransferProxy as any) as Artifact, + ZRXToken: (ZRXToken as any) as ContractArtifact, + DummyERC20Token: (DummyERC20Token as any) as ContractArtifact, + DummyERC721Token: (DummyERC721Token as any) as ContractArtifact, + ERC20Token: (ERC20Token as any) as ContractArtifact, + ERC721Token: (ERC721Token as any) as ContractArtifact, + Exchange: (Exchange as any) as ContractArtifact, + EtherToken: (EtherToken as any) as ContractArtifact, + ERC20Proxy: (ERC20Proxy as any) as ContractArtifact, + ERC721Proxy: (ERC721Proxy as any) as ContractArtifact, }; diff --git a/packages/contract-wrappers/src/artifacts/DummyERC20Token.json b/packages/contract-wrappers/src/artifacts/DummyERC20Token.json new file mode 100644 index 000000000..0fe22d7f9 --- /dev/null +++ b/packages/contract-wrappers/src/artifacts/DummyERC20Token.json @@ -0,0 +1,306 @@ +{ + "schemaVersion": "2.0.0", + "contractName": "DummyERC20Token", + "compilerOutput": { + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_value", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_target", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "setBalance", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "_name", + "type": "string" + }, + { + "name": "_symbol", + "type": "string" + }, + { + "name": "_decimals", + "type": "uint8" + }, + { + "name": "_totalSupply", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_spender", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + } + ] + } +} diff --git a/packages/contract-wrappers/src/artifacts/DummyERC721Token.json b/packages/contract-wrappers/src/artifacts/DummyERC721Token.json new file mode 100644 index 000000000..4a75b9aaf --- /dev/null +++ b/packages/contract-wrappers/src/artifacts/DummyERC721Token.json @@ -0,0 +1,368 @@ +{ + "schemaVersion": "2.0.0", + "contractName": "DummyERC721Token", + "compilerOutput": { + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "tokenId", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "exists", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_tokenId", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_approved", + "type": "address" + }, + { + "indexed": false, + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_operator", + "type": "address" + }, + { + "indexed": false, + "name": "_approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + } + ] + } +} diff --git a/packages/contract-wrappers/src/artifacts/ERC20Proxy.json b/packages/contract-wrappers/src/artifacts/ERC20Proxy.json new file mode 100644 index 000000000..82a08e23d --- /dev/null +++ b/packages/contract-wrappers/src/artifacts/ERC20Proxy.json @@ -0,0 +1,210 @@ +{ + "contractName": "ERC20Proxy", + "compilerOutput": { + "abi": [ + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + } + ], + "name": "addAuthorizedAddress", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "authorities", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + } + ], + "name": "removeAuthorizedAddress", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + }, + { + "name": "index", + "type": "uint256" + } + ], + "name": "removeAuthorizedAddressAtIndex", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "assetData", + "type": "bytes" + }, + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getProxyId", + "outputs": [ + { + "name": "", + "type": "bytes4" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "authorized", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getAuthorizedAddresses", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": true, + "name": "caller", + "type": "address" + } + ], + "name": "AuthorizedAddressAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": true, + "name": "caller", + "type": "address" + } + ], + "name": "AuthorizedAddressRemoved", + "type": "event" + } + ] + }, + "networks": { + "50": { "address": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48" } + } +} diff --git a/packages/contract-wrappers/src/artifacts/ERC20Token.json b/packages/contract-wrappers/src/artifacts/ERC20Token.json new file mode 100644 index 000000000..46f18b4e5 --- /dev/null +++ b/packages/contract-wrappers/src/artifacts/ERC20Token.json @@ -0,0 +1,174 @@ +{ + "contractName": "ERC20Token", + "compilerOutput": { + "abi": [ + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "supply", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "success", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "remaining", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_spender", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + } + ] + } +} diff --git a/packages/contract-wrappers/src/artifacts/ERC721Proxy.json b/packages/contract-wrappers/src/artifacts/ERC721Proxy.json new file mode 100644 index 000000000..a31f5b858 --- /dev/null +++ b/packages/contract-wrappers/src/artifacts/ERC721Proxy.json @@ -0,0 +1,210 @@ +{ + "contractName": "ERC20Proxy", + "compilerOutput": { + "abi": [ + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + } + ], + "name": "addAuthorizedAddress", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "authorities", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + } + ], + "name": "removeAuthorizedAddress", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "target", + "type": "address" + }, + { + "name": "index", + "type": "uint256" + } + ], + "name": "removeAuthorizedAddressAtIndex", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "assetData", + "type": "bytes" + }, + { + "name": "from", + "type": "address" + }, + { + "name": "to", + "type": "address" + }, + { + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getProxyId", + "outputs": [ + { + "name": "", + "type": "bytes4" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "authorized", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getAuthorizedAddresses", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": true, + "name": "caller", + "type": "address" + } + ], + "name": "AuthorizedAddressAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "target", + "type": "address" + }, + { + "indexed": true, + "name": "caller", + "type": "address" + } + ], + "name": "AuthorizedAddressRemoved", + "type": "event" + } + ] + }, + "networks": { + "50": { "address": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401" } + } +} diff --git a/packages/contract-wrappers/src/artifacts/ERC721Token.json b/packages/contract-wrappers/src/artifacts/ERC721Token.json new file mode 100644 index 000000000..d3208e197 --- /dev/null +++ b/packages/contract-wrappers/src/artifacts/ERC721Token.json @@ -0,0 +1,322 @@ +{ + "schemaVersion": "2.0.0", + "contractName": "ERC721Token", + "compilerOutput": { + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "exists", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "ownerOf", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_tokenId", + "type": "uint256" + }, + { + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "_name", + "type": "string" + }, + { + "name": "_symbol", + "type": "string" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_approved", + "type": "address" + }, + { + "indexed": false, + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_operator", + "type": "address" + }, + { + "indexed": false, + "name": "_approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + } + ] + } +} diff --git a/packages/contract-wrappers/src/artifacts/Exchange.json b/packages/contract-wrappers/src/artifacts/Exchange.json new file mode 100644 index 000000000..350f521df --- /dev/null +++ b/packages/contract-wrappers/src/artifacts/Exchange.json @@ -0,0 +1,1908 @@ +{ + "contractName": "Exchange", + "compilerOutput": { + "abi": [ + { + "constant": true, + "inputs": [], + "name": "EIP712_DOMAIN_SEPARATOR_SCHEMA_HASH", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "filled", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmounts", + "type": "uint256[]" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "batchFillOrders", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "cancelled", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "hash", + "type": "bytes32" + }, + { + "name": "signerAddress", + "type": "address" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "preSign", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "leftOrder", + "type": "tuple" + }, + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "rightOrder", + "type": "tuple" + }, + { + "name": "leftSignature", + "type": "bytes" + }, + { + "name": "rightSignature", + "type": "bytes" + } + ], + "name": "matchOrders", + "outputs": [ + { + "components": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "left", + "type": "tuple" + }, + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "right", + "type": "tuple" + }, + { + "name": "leftMakerAssetSpreadAmount", + "type": "uint256" + } + ], + "name": "matchedFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "fillOrderNoThrow", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "fillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes4" + } + ], + "name": "assetProxies", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + } + ], + "name": "batchCancelOrders", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmounts", + "type": "uint256[]" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "batchFillOrKillOrders", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "targetOrderEpoch", + "type": "uint256" + } + ], + "name": "cancelOrdersUpTo", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmounts", + "type": "uint256[]" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "batchFillOrdersNoThrow", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "assetProxyId", + "type": "bytes4" + } + ], + "name": "getAssetProxy", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "name": "transactions", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "fillOrKillOrder", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "fillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "validatorAddress", + "type": "address" + }, + { + "name": "approval", + "type": "bool" + } + ], + "name": "setSignatureValidatorApproval", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowedValidators", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "marketSellOrders", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "bytes32" + }, + { + "name": "", + "type": "address" + } + ], + "name": "preSigned", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "hash", + "type": "bytes32" + }, + { + "name": "signerAddress", + "type": "address" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "isValidSignature", + "outputs": [ + { + "name": "isValid", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "makerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "marketBuyOrdersNoThrow", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "fillOrder", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "fillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "salt", + "type": "uint256" + }, + { + "name": "signerAddress", + "type": "address" + }, + { + "name": "data", + "type": "bytes" + }, + { + "name": "signature", + "type": "bytes" + } + ], + "name": "executeTransaction", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "assetProxy", + "type": "address" + } + ], + "name": "registerAssetProxy", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + } + ], + "name": "getOrderInfo", + "outputs": [ + { + "components": [ + { + "name": "orderStatus", + "type": "uint8" + }, + { + "name": "orderHash", + "type": "bytes32" + }, + { + "name": "orderTakerAssetFilledAmount", + "type": "uint256" + } + ], + "name": "orderInfo", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "order", + "type": "tuple" + } + ], + "name": "cancelOrder", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "orderEpoch", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "ZRX_ASSET_DATA", + "outputs": [ + { + "name": "", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "takerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "marketSellOrdersNoThrow", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "EIP712_DOMAIN_HASH", + "outputs": [ + { + "name": "", + "type": "bytes32" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "components": [ + { + "name": "makerAddress", + "type": "address" + }, + { + "name": "takerAddress", + "type": "address" + }, + { + "name": "feeRecipientAddress", + "type": "address" + }, + { + "name": "senderAddress", + "type": "address" + }, + { + "name": "makerAssetAmount", + "type": "uint256" + }, + { + "name": "takerAssetAmount", + "type": "uint256" + }, + { + "name": "makerFee", + "type": "uint256" + }, + { + "name": "takerFee", + "type": "uint256" + }, + { + "name": "expirationTimeSeconds", + "type": "uint256" + }, + { + "name": "salt", + "type": "uint256" + }, + { + "name": "makerAssetData", + "type": "bytes" + }, + { + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "orders", + "type": "tuple[]" + }, + { + "name": "makerAssetFillAmount", + "type": "uint256" + }, + { + "name": "signatures", + "type": "bytes[]" + } + ], + "name": "marketBuyOrders", + "outputs": [ + { + "components": [ + { + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "name": "makerFeePaid", + "type": "uint256" + }, + { + "name": "takerFeePaid", + "type": "uint256" + } + ], + "name": "totalFillResults", + "type": "tuple" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "currentContextAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "VERSION", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "_zrxAssetData", + "type": "bytes" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "signerAddress", + "type": "address" + }, + { + "indexed": true, + "name": "validatorAddress", + "type": "address" + }, + { + "indexed": false, + "name": "approved", + "type": "bool" + } + ], + "name": "SignatureValidatorApproval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "makerAddress", + "type": "address" + }, + { + "indexed": true, + "name": "feeRecipientAddress", + "type": "address" + }, + { + "indexed": false, + "name": "takerAddress", + "type": "address" + }, + { + "indexed": false, + "name": "senderAddress", + "type": "address" + }, + { + "indexed": false, + "name": "makerAssetFilledAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "takerAssetFilledAmount", + "type": "uint256" + }, + { + "indexed": false, + "name": "makerFeePaid", + "type": "uint256" + }, + { + "indexed": false, + "name": "takerFeePaid", + "type": "uint256" + }, + { + "indexed": true, + "name": "orderHash", + "type": "bytes32" + }, + { + "indexed": false, + "name": "makerAssetData", + "type": "bytes" + }, + { + "indexed": false, + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "Fill", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "makerAddress", + "type": "address" + }, + { + "indexed": true, + "name": "feeRecipientAddress", + "type": "address" + }, + { + "indexed": false, + "name": "senderAddress", + "type": "address" + }, + { + "indexed": true, + "name": "orderHash", + "type": "bytes32" + }, + { + "indexed": false, + "name": "makerAssetData", + "type": "bytes" + }, + { + "indexed": false, + "name": "takerAssetData", + "type": "bytes" + } + ], + "name": "Cancel", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "makerAddress", + "type": "address" + }, + { + "indexed": true, + "name": "senderAddress", + "type": "address" + }, + { + "indexed": false, + "name": "orderEpoch", + "type": "uint256" + } + ], + "name": "CancelUpTo", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "name": "id", + "type": "bytes4" + }, + { + "indexed": false, + "name": "assetProxy", + "type": "address" + } + ], + "name": "AssetProxyRegistered", + "type": "event" + } + ] + }, + "networks": { + "50": { + "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788" + } + } +} diff --git a/packages/contract-wrappers/src/artifacts/WETH9.json b/packages/contract-wrappers/src/artifacts/WETH9.json new file mode 100644 index 000000000..ba2c6372c --- /dev/null +++ b/packages/contract-wrappers/src/artifacts/WETH9.json @@ -0,0 +1,301 @@ +{ + "contractName": "WETH9", + "compilerOutput": { + "abi": [ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "guy", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "wad", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "deposit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": true, + "name": "_spender", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_from", + "type": "address" + }, + { + "indexed": true, + "name": "_to", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_owner", + "type": "address" + }, + { + "indexed": false, + "name": "_value", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + } + ] + }, + "networks": { + "1": { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + }, + "3": { + "address": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a" + }, + "4": { + "address": "0xc778417e063141139fce010982780140aa0cd5ab" + }, + "42": { + "address": "0x653e49e301e508a13237c0ddc98ae7d4cd2667a1" + }, + "50": { + "address": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082" + } + } +} diff --git a/packages/contract-wrappers/src/compact_artifacts/ZRX.json b/packages/contract-wrappers/src/artifacts/ZRXToken.json index e40b8f268..bb574be7c 100644 --- a/packages/contract-wrappers/src/compact_artifacts/ZRX.json +++ b/packages/contract-wrappers/src/artifacts/ZRXToken.json @@ -1,5 +1,8 @@ { - "contract_name": "ZRX", + "contractName": "ZRXToken", + "compilerOutput": { + "abi": [] + }, "networks": { "1": { "address": "0xe41d2489571d322189246dafa5ebde1f4699f498" @@ -14,7 +17,7 @@ "address": "0x6ff6c0ff1d68b964901f986d4c9fa3ac68346570" }, "50": { - "address": "0x1d7022f5b17d2f8b695918fb48fa1089c9f85401" + "address": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c" } } } diff --git a/packages/contract-wrappers/src/compact_artifacts/DummyToken.json b/packages/contract-wrappers/src/compact_artifacts/DummyToken.json deleted file mode 100644 index f64a8cd3d..000000000 --- a/packages/contract-wrappers/src/compact_artifacts/DummyToken.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "contract_name": "DummyToken", - "abi": [ - { - "constant": false, - "inputs": [ - { - "name": "_target", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "setBalance", - "outputs": [], - "payable": false, - "type": "function" - } - ] -} diff --git a/packages/contract-wrappers/src/compact_artifacts/EtherToken.json b/packages/contract-wrappers/src/compact_artifacts/EtherToken.json deleted file mode 100644 index 26cca57cd..000000000 --- a/packages/contract-wrappers/src/compact_artifacts/EtherToken.json +++ /dev/null @@ -1,287 +0,0 @@ -{ - "contract_name": "EtherToken", - "abi": [ - { - "constant": true, - "inputs": [], - "name": "name", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_spender", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_from", - "type": "address" - }, - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "amount", - "type": "uint256" - } - ], - "name": "withdraw", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "decimals", - "outputs": [ - { - "name": "", - "type": "uint8" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "symbol", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [], - "name": "deposit", - "outputs": [], - "payable": true, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - }, - { - "name": "_spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "payable": true, - "type": "fallback" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "_from", - "type": "address" - }, - { - "indexed": true, - "name": "_to", - "type": "address" - }, - { - "indexed": false, - "name": "_value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "_owner", - "type": "address" - }, - { - "indexed": true, - "name": "_spender", - "type": "address" - }, - { - "indexed": false, - "name": "_value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "_owner", - "type": "address" - }, - { - "indexed": false, - "name": "_value", - "type": "uint256" - } - ], - "name": "Deposit", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "_owner", - "type": "address" - }, - { - "indexed": false, - "name": "_value", - "type": "uint256" - } - ], - "name": "Withdrawal", - "type": "event" - } - ], - "networks": { - "1": { - "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" - }, - "3": { - "address": "0xc00fd9820cd2898cc4c054b7bf142de637ad129a" - }, - "4": { - "address": "0xc778417e063141139fce010982780140aa0cd5ab" - }, - "42": { - "address": "0x653e49e301e508a13237c0ddc98ae7d4cd2667a1" - }, - "50": { - "address": "0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c" - } - } -} diff --git a/packages/contract-wrappers/src/compact_artifacts/Exchange.json b/packages/contract-wrappers/src/compact_artifacts/Exchange.json deleted file mode 100644 index af8db7360..000000000 --- a/packages/contract-wrappers/src/compact_artifacts/Exchange.json +++ /dev/null @@ -1,610 +0,0 @@ -{ - "contract_name": "Exchange", - "abi": [ - { - "constant": true, - "inputs": [ - { - "name": "numerator", - "type": "uint256" - }, - { - "name": "denominator", - "type": "uint256" - }, - { - "name": "target", - "type": "uint256" - } - ], - "name": "isRoundingError", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "filled", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "name": "cancelled", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5][]" - }, - { - "name": "orderValues", - "type": "uint256[6][]" - }, - { - "name": "fillTakerTokenAmount", - "type": "uint256" - }, - { - "name": "shouldThrowOnInsufficientBalanceOrAllowance", - "type": "bool" - }, - { - "name": "v", - "type": "uint8[]" - }, - { - "name": "r", - "type": "bytes32[]" - }, - { - "name": "s", - "type": "bytes32[]" - } - ], - "name": "fillOrdersUpTo", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5]" - }, - { - "name": "orderValues", - "type": "uint256[6]" - }, - { - "name": "cancelTakerTokenAmount", - "type": "uint256" - } - ], - "name": "cancelOrder", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "ZRX_TOKEN_CONTRACT", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5][]" - }, - { - "name": "orderValues", - "type": "uint256[6][]" - }, - { - "name": "fillTakerTokenAmounts", - "type": "uint256[]" - }, - { - "name": "v", - "type": "uint8[]" - }, - { - "name": "r", - "type": "bytes32[]" - }, - { - "name": "s", - "type": "bytes32[]" - } - ], - "name": "batchFillOrKillOrders", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5]" - }, - { - "name": "orderValues", - "type": "uint256[6]" - }, - { - "name": "fillTakerTokenAmount", - "type": "uint256" - }, - { - "name": "v", - "type": "uint8" - }, - { - "name": "r", - "type": "bytes32" - }, - { - "name": "s", - "type": "bytes32" - } - ], - "name": "fillOrKillOrder", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "orderHash", - "type": "bytes32" - } - ], - "name": "getUnavailableTakerTokenAmount", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "signer", - "type": "address" - }, - { - "name": "hash", - "type": "bytes32" - }, - { - "name": "v", - "type": "uint8" - }, - { - "name": "r", - "type": "bytes32" - }, - { - "name": "s", - "type": "bytes32" - } - ], - "name": "isValidSignature", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "numerator", - "type": "uint256" - }, - { - "name": "denominator", - "type": "uint256" - }, - { - "name": "target", - "type": "uint256" - } - ], - "name": "getPartialAmount", - "outputs": [ - { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "TOKEN_TRANSFER_PROXY_CONTRACT", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5][]" - }, - { - "name": "orderValues", - "type": "uint256[6][]" - }, - { - "name": "fillTakerTokenAmounts", - "type": "uint256[]" - }, - { - "name": "shouldThrowOnInsufficientBalanceOrAllowance", - "type": "bool" - }, - { - "name": "v", - "type": "uint8[]" - }, - { - "name": "r", - "type": "bytes32[]" - }, - { - "name": "s", - "type": "bytes32[]" - } - ], - "name": "batchFillOrders", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5][]" - }, - { - "name": "orderValues", - "type": "uint256[6][]" - }, - { - "name": "cancelTakerTokenAmounts", - "type": "uint256[]" - } - ], - "name": "batchCancelOrders", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5]" - }, - { - "name": "orderValues", - "type": "uint256[6]" - }, - { - "name": "fillTakerTokenAmount", - "type": "uint256" - }, - { - "name": "shouldThrowOnInsufficientBalanceOrAllowance", - "type": "bool" - }, - { - "name": "v", - "type": "uint8" - }, - { - "name": "r", - "type": "bytes32" - }, - { - "name": "s", - "type": "bytes32" - } - ], - "name": "fillOrder", - "outputs": [ - { - "name": "filledTakerTokenAmount", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "orderAddresses", - "type": "address[5]" - }, - { - "name": "orderValues", - "type": "uint256[6]" - } - ], - "name": "getOrderHash", - "outputs": [ - { - "name": "", - "type": "bytes32" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "EXTERNAL_QUERY_GAS_LIMIT", - "outputs": [ - { - "name": "", - "type": "uint16" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "VERSION", - "outputs": [ - { - "name": "", - "type": "string" - } - ], - "payable": false, - "type": "function" - }, - { - "inputs": [ - { - "name": "_zrxToken", - "type": "address" - }, - { - "name": "_tokenTransferProxy", - "type": "address" - } - ], - "payable": false, - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "maker", - "type": "address" - }, - { - "indexed": false, - "name": "taker", - "type": "address" - }, - { - "indexed": true, - "name": "feeRecipient", - "type": "address" - }, - { - "indexed": false, - "name": "makerToken", - "type": "address" - }, - { - "indexed": false, - "name": "takerToken", - "type": "address" - }, - { - "indexed": false, - "name": "filledMakerTokenAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "filledTakerTokenAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "paidMakerFee", - "type": "uint256" - }, - { - "indexed": false, - "name": "paidTakerFee", - "type": "uint256" - }, - { - "indexed": true, - "name": "tokens", - "type": "bytes32" - }, - { - "indexed": false, - "name": "orderHash", - "type": "bytes32" - } - ], - "name": "LogFill", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "maker", - "type": "address" - }, - { - "indexed": true, - "name": "feeRecipient", - "type": "address" - }, - { - "indexed": false, - "name": "makerToken", - "type": "address" - }, - { - "indexed": false, - "name": "takerToken", - "type": "address" - }, - { - "indexed": false, - "name": "cancelledMakerTokenAmount", - "type": "uint256" - }, - { - "indexed": false, - "name": "cancelledTakerTokenAmount", - "type": "uint256" - }, - { - "indexed": true, - "name": "tokens", - "type": "bytes32" - }, - { - "indexed": false, - "name": "orderHash", - "type": "bytes32" - } - ], - "name": "LogCancel", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "errorId", - "type": "uint8" - }, - { - "indexed": true, - "name": "orderHash", - "type": "bytes32" - } - ], - "name": "LogError", - "type": "event" - } - ], - "networks": { - "1": { - "address": "0x12459c951127e0c374ff9105dda097662a027093" - }, - "3": { - "address": "0x479cc461fecd078f766ecc58533d6f69580cf3ac" - }, - "4": { - "address": "0x1d16ef40fac01cec8adac2ac49427b9384192c05" - }, - "42": { - "address": "0x90fe2af704b34e0224bf2299c838e04d4dcf1364" - }, - "50": { - "address": "0x48bacb9266a570d521063ef5dd96e61686dbe788" - } - } -} diff --git a/packages/contract-wrappers/src/compact_artifacts/Token.json b/packages/contract-wrappers/src/compact_artifacts/Token.json deleted file mode 100644 index 3b5a86ae0..000000000 --- a/packages/contract-wrappers/src/compact_artifacts/Token.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "contract_name": "Token", - "abi": [ - { - "constant": false, - "inputs": [ - { - "name": "_spender", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "name": "success", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "name": "supply", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_from", - "type": "address" - }, - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "name": "success", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "name": "balance", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_to", - "type": "address" - }, - { - "name": "_value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "name": "success", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_owner", - "type": "address" - }, - { - "name": "_spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "name": "remaining", - "type": "uint256" - } - ], - "payable": false, - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "_from", - "type": "address" - }, - { - "indexed": true, - "name": "_to", - "type": "address" - }, - { - "indexed": false, - "name": "_value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "_owner", - "type": "address" - }, - { - "indexed": true, - "name": "_spender", - "type": "address" - }, - { - "indexed": false, - "name": "_value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - } - ] -} diff --git a/packages/contract-wrappers/src/compact_artifacts/TokenRegistry.json b/packages/contract-wrappers/src/compact_artifacts/TokenRegistry.json deleted file mode 100644 index 0f583628c..000000000 --- a/packages/contract-wrappers/src/compact_artifacts/TokenRegistry.json +++ /dev/null @@ -1,547 +0,0 @@ -{ - "contract_name": "TokenRegistry", - "abi": [ - { - "constant": false, - "inputs": [ - { - "name": "_token", - "type": "address" - }, - { - "name": "_index", - "type": "uint256" - } - ], - "name": "removeToken", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_name", - "type": "string" - } - ], - "name": "getTokenAddressByName", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_symbol", - "type": "string" - } - ], - "name": "getTokenAddressBySymbol", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_token", - "type": "address" - }, - { - "name": "_swarmHash", - "type": "bytes" - } - ], - "name": "setTokenSwarmHash", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_token", - "type": "address" - } - ], - "name": "getTokenMetaData", - "outputs": [ - { - "name": "", - "type": "address" - }, - { - "name": "", - "type": "string" - }, - { - "name": "", - "type": "string" - }, - { - "name": "", - "type": "uint8" - }, - { - "name": "", - "type": "bytes" - }, - { - "name": "", - "type": "bytes" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "owner", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_token", - "type": "address" - }, - { - "name": "_name", - "type": "string" - }, - { - "name": "_symbol", - "type": "string" - }, - { - "name": "_decimals", - "type": "uint8" - }, - { - "name": "_ipfsHash", - "type": "bytes" - }, - { - "name": "_swarmHash", - "type": "bytes" - } - ], - "name": "addToken", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_token", - "type": "address" - }, - { - "name": "_name", - "type": "string" - } - ], - "name": "setTokenName", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - } - ], - "name": "tokens", - "outputs": [ - { - "name": "token", - "type": "address" - }, - { - "name": "name", - "type": "string" - }, - { - "name": "symbol", - "type": "string" - }, - { - "name": "decimals", - "type": "uint8" - }, - { - "name": "ipfsHash", - "type": "bytes" - }, - { - "name": "swarmHash", - "type": "bytes" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "tokenAddresses", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_name", - "type": "string" - } - ], - "name": "getTokenByName", - "outputs": [ - { - "name": "", - "type": "address" - }, - { - "name": "", - "type": "string" - }, - { - "name": "", - "type": "string" - }, - { - "name": "", - "type": "uint8" - }, - { - "name": "", - "type": "bytes" - }, - { - "name": "", - "type": "bytes" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getTokenAddresses", - "outputs": [ - { - "name": "", - "type": "address[]" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_token", - "type": "address" - }, - { - "name": "_ipfsHash", - "type": "bytes" - } - ], - "name": "setTokenIpfsHash", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "_symbol", - "type": "string" - } - ], - "name": "getTokenBySymbol", - "outputs": [ - { - "name": "", - "type": "address" - }, - { - "name": "", - "type": "string" - }, - { - "name": "", - "type": "string" - }, - { - "name": "", - "type": "uint8" - }, - { - "name": "", - "type": "bytes" - }, - { - "name": "", - "type": "bytes" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_token", - "type": "address" - }, - { - "name": "_symbol", - "type": "string" - } - ], - "name": "setTokenSymbol", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "token", - "type": "address" - }, - { - "indexed": false, - "name": "name", - "type": "string" - }, - { - "indexed": false, - "name": "symbol", - "type": "string" - }, - { - "indexed": false, - "name": "decimals", - "type": "uint8" - }, - { - "indexed": false, - "name": "ipfsHash", - "type": "bytes" - }, - { - "indexed": false, - "name": "swarmHash", - "type": "bytes" - } - ], - "name": "LogAddToken", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "token", - "type": "address" - }, - { - "indexed": false, - "name": "name", - "type": "string" - }, - { - "indexed": false, - "name": "symbol", - "type": "string" - }, - { - "indexed": false, - "name": "decimals", - "type": "uint8" - }, - { - "indexed": false, - "name": "ipfsHash", - "type": "bytes" - }, - { - "indexed": false, - "name": "swarmHash", - "type": "bytes" - } - ], - "name": "LogRemoveToken", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "token", - "type": "address" - }, - { - "indexed": false, - "name": "oldName", - "type": "string" - }, - { - "indexed": false, - "name": "newName", - "type": "string" - } - ], - "name": "LogTokenNameChange", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "token", - "type": "address" - }, - { - "indexed": false, - "name": "oldSymbol", - "type": "string" - }, - { - "indexed": false, - "name": "newSymbol", - "type": "string" - } - ], - "name": "LogTokenSymbolChange", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "token", - "type": "address" - }, - { - "indexed": false, - "name": "oldIpfsHash", - "type": "bytes" - }, - { - "indexed": false, - "name": "newIpfsHash", - "type": "bytes" - } - ], - "name": "LogTokenIpfsHashChange", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "token", - "type": "address" - }, - { - "indexed": false, - "name": "oldSwarmHash", - "type": "bytes" - }, - { - "indexed": false, - "name": "newSwarmHash", - "type": "bytes" - } - ], - "name": "LogTokenSwarmHashChange", - "type": "event" - } - ], - "networks": { - "1": { - "address": "0x926a74c5c36adf004c87399e65f75628b0f98d2c" - }, - "3": { - "address": "0x6b1a50f0bb5a7995444bd3877b22dc89c62843ed" - }, - "4": { - "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6" - }, - "42": { - "address": "0xf18e504561f4347bea557f3d4558f559dddbae7f" - }, - "50": { - "address": "0x0b1ba0af832d7c05fd64161e0db78e85978e8082" - } - } -} diff --git a/packages/contract-wrappers/src/compact_artifacts/TokenTransferProxy.json b/packages/contract-wrappers/src/compact_artifacts/TokenTransferProxy.json deleted file mode 100644 index 8cf551ddb..000000000 --- a/packages/contract-wrappers/src/compact_artifacts/TokenTransferProxy.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "contract_name": "TokenTransferProxy", - "abi": [ - { - "constant": false, - "inputs": [ - { - "name": "token", - "type": "address" - }, - { - "name": "from", - "type": "address" - }, - { - "name": "to", - "type": "address" - }, - { - "name": "value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "target", - "type": "address" - } - ], - "name": "addAuthorizedAddress", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "uint256" - } - ], - "name": "authorities", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "target", - "type": "address" - } - ], - "name": "removeAuthorizedAddress", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "owner", - "outputs": [ - { - "name": "", - "type": "address" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [ - { - "name": "", - "type": "address" - } - ], - "name": "authorized", - "outputs": [ - { - "name": "", - "type": "bool" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getAuthorizedAddresses", - "outputs": [ - { - "name": "", - "type": "address[]" - } - ], - "payable": false, - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "payable": false, - "type": "function" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "target", - "type": "address" - }, - { - "indexed": true, - "name": "caller", - "type": "address" - } - ], - "name": "LogAuthorizedAddressAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "target", - "type": "address" - }, - { - "indexed": true, - "name": "caller", - "type": "address" - } - ], - "name": "LogAuthorizedAddressRemoved", - "type": "event" - } - ], - "networks": { - "1": { - "address": "0x8da0d80f5007ef1e431dd2127178d224e32c2ef4" - }, - "3": { - "address": "0x4e9aad8184de8833365fea970cd9149372fdf1e6" - }, - "4": { - "address": "0xa8e9fa8f91e5ae138c74648c9c304f1c75003a8d" - }, - "42": { - "address": "0x087eed4bc1ee3de49befbd66c662b434b15d49d4" - }, - "50": { - "address": "0x1dc4c1cefef38a777b15aa20260a54e584b16c48" - } - } -} diff --git a/packages/contract-wrappers/src/contract_wrappers.ts b/packages/contract-wrappers/src/contract_wrappers.ts index 2ef050f27..8010242c5 100644 --- a/packages/contract-wrappers/src/contract_wrappers.ts +++ b/packages/contract-wrappers/src/contract_wrappers.ts @@ -1,13 +1,16 @@ -import { Provider } from '@0xproject/types'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Provider } from 'ethereum-types'; import * as _ from 'lodash'; +import { constants } from './utils/constants'; + import { artifacts } from './artifacts'; +import { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper'; +import { ERC20TokenWrapper } from './contract_wrappers/erc20_token_wrapper'; +import { ERC721ProxyWrapper } from './contract_wrappers/erc721_proxy_wrapper'; +import { ERC721TokenWrapper } from './contract_wrappers/erc721_token_wrapper'; import { EtherTokenWrapper } from './contract_wrappers/ether_token_wrapper'; import { ExchangeWrapper } from './contract_wrappers/exchange_wrapper'; -import { TokenRegistryWrapper } from './contract_wrappers/token_registry_wrapper'; -import { TokenTransferProxyWrapper } from './contract_wrappers/token_transfer_proxy_wrapper'; -import { TokenWrapper } from './contract_wrappers/token_wrapper'; import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema'; import { contractWrappersPrivateNetworkConfigSchema } from './schemas/contract_wrappers_private_network_config_schema'; import { contractWrappersPublicNetworkConfigSchema } from './schemas/contract_wrappers_public_network_config_schema'; @@ -22,24 +25,28 @@ export class ContractWrappers { */ public exchange: ExchangeWrapper; /** - * An instance of the TokenRegistryWrapper class containing methods for interacting with the 0x - * TokenRegistry smart contract. + * An instance of the ERC20TokenWrapper class containing methods for interacting with any ERC20 token smart contract. */ - public tokenRegistry: TokenRegistryWrapper; + public erc20Token: ERC20TokenWrapper; /** - * An instance of the TokenWrapper class containing methods for interacting with any ERC20 token smart contract. + * An instance of the ERC721TokenWrapper class containing methods for interacting with any ERC721 token smart contract. */ - public token: TokenWrapper; + public erc721Token: ERC721TokenWrapper; /** * An instance of the EtherTokenWrapper class containing methods for interacting with the * wrapped ETH ERC20 token smart contract. */ public etherToken: EtherTokenWrapper; /** - * An instance of the TokenTransferProxyWrapper class containing methods for interacting with the - * tokenTransferProxy smart contract. + * An instance of the ERC20ProxyWrapper class containing methods for interacting with the + * erc20Proxy smart contract. + */ + public erc20Proxy: ERC20ProxyWrapper; + /** + * An instance of the ERC721ProxyWrapper class containing methods for interacting with the + * erc721Proxy smart contract. */ - public proxy: TokenTransferProxyWrapper; + public erc721Proxy: ERC721ProxyWrapper; private _web3Wrapper: Web3Wrapper; /** * Instantiates a new ContractWrappers instance. @@ -55,7 +62,7 @@ export class ContractWrappers { contractWrappersPublicNetworkConfigSchema, ]); const artifactJSONs = _.values(artifacts); - const abiArrays = _.map(artifactJSONs, artifact => artifact.abi); + const abiArrays = _.map(artifactJSONs, artifact => artifact.compilerOutput.abi); const txDefaults = { gasPrice: config.gasPrice, }; @@ -63,25 +70,40 @@ export class ContractWrappers { _.forEach(abiArrays, abi => { this._web3Wrapper.abiDecoder.addABI(abi); }); - this.proxy = new TokenTransferProxyWrapper( + const blockPollingIntervalMs = _.isUndefined(config.blockPollingIntervalMs) + ? constants.DEFAULT_BLOCK_POLLING_INTERVAL + : config.blockPollingIntervalMs; + this.erc20Proxy = new ERC20ProxyWrapper(this._web3Wrapper, config.networkId, config.erc20ProxyContractAddress); + this.erc721Proxy = new ERC721ProxyWrapper( this._web3Wrapper, config.networkId, - config.tokenTransferProxyContractAddress, + config.erc721ProxyContractAddress, ); - this.token = new TokenWrapper(this._web3Wrapper, config.networkId, this.proxy); - this.exchange = new ExchangeWrapper( + this.erc20Token = new ERC20TokenWrapper( this._web3Wrapper, config.networkId, - this.token, - config.exchangeContractAddress, - config.zrxContractAddress, + this.erc20Proxy, + blockPollingIntervalMs, ); - this.tokenRegistry = new TokenRegistryWrapper( + this.erc721Token = new ERC721TokenWrapper( this._web3Wrapper, config.networkId, - config.tokenRegistryContractAddress, + this.erc721Proxy, + blockPollingIntervalMs, + ); + this.etherToken = new EtherTokenWrapper( + this._web3Wrapper, + config.networkId, + this.erc20Token, + blockPollingIntervalMs, + ); + this.exchange = new ExchangeWrapper( + this._web3Wrapper, + config.networkId, + config.exchangeContractAddress, + config.zrxContractAddress, + blockPollingIntervalMs, ); - this.etherToken = new EtherTokenWrapper(this._web3Wrapper, config.networkId, this.token); } /** * Sets a new web3 provider for 0x.js. Updating the provider will stop all @@ -93,12 +115,10 @@ export class ContractWrappers { this._web3Wrapper.setProvider(provider); (this.exchange as any)._invalidateContractInstances(); (this.exchange as any)._setNetworkId(networkId); - (this.tokenRegistry as any)._invalidateContractInstance(); - (this.tokenRegistry as any)._setNetworkId(networkId); - (this.token as any)._invalidateContractInstances(); - (this.token as any)._setNetworkId(networkId); - (this.proxy as any)._invalidateContractInstance(); - (this.proxy as any)._setNetworkId(networkId); + (this.erc20Token as any)._invalidateContractInstances(); + (this.erc20Token as any)._setNetworkId(networkId); + (this.erc20Proxy as any)._invalidateContractInstance(); + (this.erc20Proxy as any)._setNetworkId(networkId); (this.etherToken as any)._invalidateContractInstance(); (this.etherToken as any)._setNetworkId(networkId); } diff --git a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts index 9cc661080..9688edce2 100644 --- a/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/contract_wrapper.ts @@ -1,14 +1,7 @@ -import { - Artifact, - BlockParamLiteral, - ContractAbi, - FilterObject, - LogEntry, - LogWithDecodedArgs, - RawLog, -} from '@0xproject/types'; -import { intervalUtils } from '@0xproject/utils'; +import { ContractArtifact } from '@0xproject/sol-compiler'; +import { AbiDecoder, intervalUtils } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { BlockParamLiteral, ContractAbi, FilterObject, LogEntry, LogWithDecodedArgs, RawLog } from 'ethereum-types'; import { Block, BlockAndLogStreamer, Log } from 'ethereumjs-blockstream'; import * as _ from 'lodash'; @@ -19,7 +12,6 @@ import { ContractWrappersError, EventCallback, IndexedFilterValues, - InternalContractWrappersError, } from '../types'; import { constants } from '../utils/constants'; import { filterUtils } from '../utils/filter_utils'; @@ -29,9 +21,10 @@ const CONTRACT_NAME_TO_NOT_FOUND_ERROR: { } = { ZRX: ContractWrappersError.ZRXContractDoesNotExist, EtherToken: ContractWrappersError.EtherTokenContractDoesNotExist, - Token: ContractWrappersError.TokenContractDoesNotExist, - TokenRegistry: ContractWrappersError.TokenRegistryContractDoesNotExist, - TokenTransferProxy: ContractWrappersError.TokenTransferProxyContractDoesNotExist, + ERC20Token: ContractWrappersError.ERC20TokenContractDoesNotExist, + ERC20Proxy: ContractWrappersError.ERC20ProxyContractDoesNotExist, + ERC721Token: ContractWrappersError.ERC721TokenContractDoesNotExist, + ERC721Proxy: ContractWrappersError.ERC721ProxyContractDoesNotExist, Exchange: ContractWrappersError.ExchangeContractDoesNotExist, }; @@ -39,7 +32,8 @@ export abstract class ContractWrapper { public abstract abi: ContractAbi; protected _web3Wrapper: Web3Wrapper; protected _networkId: number; - private _blockAndLogStreamerIfExists: BlockAndLogStreamer<Block, Log> | undefined; + private _blockAndLogStreamerIfExists: BlockAndLogStreamer | undefined; + private _blockPollingIntervalMs: number; private _blockAndLogStreamIntervalIfExists?: NodeJS.Timer; private _filters: { [filterToken: string]: FilterObject }; private _filterCallbacks: { @@ -47,9 +41,12 @@ export abstract class ContractWrapper { }; private _onLogAddedSubscriptionToken: string | undefined; private _onLogRemovedSubscriptionToken: string | undefined; - constructor(web3Wrapper: Web3Wrapper, networkId: number) { + constructor(web3Wrapper: Web3Wrapper, networkId: number, blockPollingIntervalMs?: number) { this._web3Wrapper = web3Wrapper; this._networkId = networkId; + this._blockPollingIntervalMs = _.isUndefined(blockPollingIntervalMs) + ? constants.DEFAULT_BLOCK_POLLING_INTERVAL + : blockPollingIntervalMs; this._filters = {}; this._filterCallbacks = {}; this._blockAndLogStreamerIfExists = undefined; @@ -107,14 +104,12 @@ export abstract class ContractWrapper { protected _tryToDecodeLogOrNoop<ArgsType extends ContractEventArgs>( log: LogEntry, ): LogWithDecodedArgs<ArgsType> | RawLog { - if (_.isUndefined(this._web3Wrapper.abiDecoder)) { - throw new Error(InternalContractWrappersError.NoAbiDecoder); - } - const logWithDecodedArgs = this._web3Wrapper.abiDecoder.tryToDecodeLogOrNoop(log); + const abiDecoder = new AbiDecoder([this.abi]); + const logWithDecodedArgs = abiDecoder.tryToDecodeLogOrNoop(log); return logWithDecodedArgs; } protected async _getContractAbiAndAddressFromArtifactsAsync( - artifact: Artifact, + artifact: ContractArtifact, addressIfExists?: string, ): Promise<[ContractAbi, string]> { let contractAddress: string; @@ -128,12 +123,12 @@ export abstract class ContractWrapper { } const doesContractExist = await this._web3Wrapper.doesContractExistAtAddressAsync(contractAddress); if (!doesContractExist) { - throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contract_name]); + throw new Error(CONTRACT_NAME_TO_NOT_FOUND_ERROR[artifact.contractName]); } - const abiAndAddress: [ContractAbi, string] = [artifact.abi, contractAddress]; + const abiAndAddress: [ContractAbi, string] = [artifact.compilerOutput.abi, contractAddress]; return abiAndAddress; } - protected _getContractAddress(artifact: Artifact, addressIfExists?: string): string { + protected _getContractAddress(artifact: ContractArtifact, addressIfExists?: string): string { if (_.isUndefined(addressIfExists)) { const contractAddress = artifact.networks[this._networkId].address; if (_.isUndefined(contractAddress)) { @@ -169,7 +164,7 @@ export abstract class ContractWrapper { this._blockAndLogStreamerIfExists.addLogFilter(catchAllLogFilter); this._blockAndLogStreamIntervalIfExists = intervalUtils.setAsyncExcludingInterval( this._reconcileBlockAsync.bind(this), - constants.DEFAULT_BLOCK_POLLING_INTERVAL, + this._blockPollingIntervalMs, this._onReconcileBlockError.bind(this), ); let isRemoved = false; diff --git a/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts new file mode 100644 index 000000000..883d7c4d6 --- /dev/null +++ b/packages/contract-wrappers/src/contract_wrappers/erc20_proxy_wrapper.ts @@ -0,0 +1,84 @@ +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ContractAbi } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { artifacts } from '../artifacts'; +import { assert } from '../utils/assert'; + +import { ContractWrapper } from './contract_wrapper'; +import { ERC20ProxyContract } from './generated/erc20_proxy'; + +/** + * This class includes the functionality related to interacting with the ERC20Proxy contract. + */ +export class ERC20ProxyWrapper extends ContractWrapper { + public abi: ContractAbi = artifacts.ERC20Proxy.compilerOutput.abi; + private _erc20ProxyContractIfExists?: ERC20ProxyContract; + private _contractAddressIfExists?: string; + constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) { + super(web3Wrapper, networkId); + this._contractAddressIfExists = contractAddressIfExists; + } + /** + * Get the 4 bytes ID of this asset proxy + * @return Proxy id + */ + public async getProxyIdAsync(): Promise<string> { + const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync(); + const proxyId = await ERC20ProxyContractInstance.getProxyId.callAsync(); + return proxyId; + } + /** + * Check if the Exchange contract address is authorized by the ERC20Proxy contract. + * @param exchangeContractAddress The hex encoded address of the Exchange contract to call. + * @return Whether the exchangeContractAddress is authorized. + */ + public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> { + assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress); + const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase(); + const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync(); + const isAuthorized = await ERC20ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress); + return isAuthorized; + } + /** + * Get the list of all Exchange contract addresses authorized by the ERC20Proxy contract. + * @return The list of authorized addresses. + */ + public async getAuthorizedAddressesAsync(): Promise<string[]> { + const ERC20ProxyContractInstance = await this._getERC20ProxyContractAsync(); + const authorizedAddresses = await ERC20ProxyContractInstance.getAuthorizedAddresses.callAsync(); + return authorizedAddresses; + } + /** + * Retrieves the Ethereum address of the ERC20Proxy contract deployed on the network + * that the user-passed web3 provider is connected to. + * @returns The Ethereum address of the ERC20Proxy contract being used. + */ + public getContractAddress(): string { + const contractAddress = this._getContractAddress(artifacts.ERC20Proxy, this._contractAddressIfExists); + return contractAddress; + } + // HACK: We don't want this method to be visible to the other units within that package but not to the end user. + // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. + // tslint:disable-next-line:no-unused-variable + private _invalidateContractInstance(): void { + delete this._erc20ProxyContractIfExists; + } + private async _getERC20ProxyContractAsync(): Promise<ERC20ProxyContract> { + if (!_.isUndefined(this._erc20ProxyContractIfExists)) { + return this._erc20ProxyContractIfExists; + } + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( + artifacts.ERC20Proxy, + this._contractAddressIfExists, + ); + const contractInstance = new ERC20ProxyContract( + abi, + address, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + this._erc20ProxyContractIfExists = contractInstance; + return this._erc20ProxyContractIfExists; + } +} diff --git a/packages/contract-wrappers/src/contract_wrappers/token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts index d9364715f..29c63564e 100644 --- a/packages/contract-wrappers/src/contract_wrappers/token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/erc20_token_wrapper.ts @@ -1,10 +1,12 @@ import { schemas } from '@0xproject/json-schemas'; -import { ContractAbi, LogWithDecodedArgs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; import { artifacts } from '../artifacts'; +import { methodOptsSchema } from '../schemas/method_opts_schema'; +import { txOptsSchema } from '../schemas/tx_opts_schema'; import { BlockRange, ContractWrappersError, @@ -17,23 +19,30 @@ import { assert } from '../utils/assert'; import { constants } from '../utils/constants'; import { ContractWrapper } from './contract_wrapper'; -import { TokenContract, TokenContractEventArgs, TokenEvents } from './generated/token'; -import { TokenTransferProxyWrapper } from './token_transfer_proxy_wrapper'; +import { ERC20ProxyWrapper } from './erc20_proxy_wrapper'; +import { ERC20TokenContract, ERC20TokenEventArgs, ERC20TokenEvents } from './generated/erc20_token'; + +const removeUndefinedProperties = _.pickBy; /** * This class includes all the functionality related to interacting with ERC20 token contracts. * All ERC20 method calls are supported, along with some convenience methods for getting/setting allowances - * to the 0x Proxy smart contract. + * to the 0x ERC20 Proxy smart contract. */ -export class TokenWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.Token.abi; +export class ERC20TokenWrapper extends ContractWrapper { + public abi: ContractAbi = artifacts.ERC20Token.compilerOutput.abi; public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - private _tokenContractsByAddress: { [address: string]: TokenContract }; - private _tokenTransferProxyWrapper: TokenTransferProxyWrapper; - constructor(web3Wrapper: Web3Wrapper, networkId: number, tokenTransferProxyWrapper: TokenTransferProxyWrapper) { - super(web3Wrapper, networkId); + private _tokenContractsByAddress: { [address: string]: ERC20TokenContract }; + private _erc20ProxyWrapper: ERC20ProxyWrapper; + constructor( + web3Wrapper: Web3Wrapper, + networkId: number, + erc20ProxyWrapper: ERC20ProxyWrapper, + blockPollingIntervalMs?: number, + ) { + super(web3Wrapper, networkId, blockPollingIntervalMs); this._tokenContractsByAddress = {}; - this._tokenTransferProxyWrapper = tokenTransferProxyWrapper; + this._erc20ProxyWrapper = erc20ProxyWrapper; } /** * Retrieves an owner's ERC20 token balance. @@ -45,17 +54,18 @@ export class TokenWrapper extends ContractWrapper { public async getBalanceAsync( tokenAddress: string, ownerAddress: string, - methodOpts?: MethodOpts, + methodOpts: MethodOpts = {}, ): Promise<BigNumber> { assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); const normalizedTokenAddress = tokenAddress.toLowerCase(); const normalizedOwnerAddress = ownerAddress.toLowerCase(); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); - const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; + const txData = {}; - let balance = await tokenContract.balanceOf.callAsync(normalizedOwnerAddress, txData, defaultBlock); + let balance = await tokenContract.balanceOf.callAsync(normalizedOwnerAddress, txData, methodOpts.defaultBlock); // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber balance = new BigNumber(balance); return balance; @@ -78,20 +88,25 @@ export class TokenWrapper extends ContractWrapper { amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}, ): Promise<string> { - assert.isETHAddressHex('spenderAddress', spenderAddress); assert.isETHAddressHex('tokenAddress', tokenAddress); await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper); + assert.isETHAddressHex('spenderAddress', spenderAddress); + assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); + assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); const normalizedTokenAddress = tokenAddress.toLowerCase(); - const normalizedSpenderAddress = spenderAddress.toLowerCase(); const normalizedOwnerAddress = ownerAddress.toLowerCase(); - assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); + const normalizedSpenderAddress = spenderAddress.toLowerCase(); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); - const txHash = await tokenContract.approve.sendTransactionAsync(normalizedSpenderAddress, amountInBaseUnits, { - from: normalizedOwnerAddress, - gas: txOpts.gasLimit, - gasPrice: txOpts.gasPrice, - }); + const txHash = await tokenContract.approve.sendTransactionAsync( + normalizedSpenderAddress, + amountInBaseUnits, + removeUndefinedProperties({ + from: normalizedOwnerAddress, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, + }), + ); return txHash; } /** @@ -112,16 +127,10 @@ export class TokenWrapper extends ContractWrapper { spenderAddress: string, txOpts: TransactionOpts = {}, ): Promise<string> { - assert.isETHAddressHex('ownerAddress', ownerAddress); - assert.isETHAddressHex('tokenAddress', tokenAddress); - assert.isETHAddressHex('spenderAddress', spenderAddress); - const normalizedTokenAddress = tokenAddress.toLowerCase(); - const normalizedOwnerAddress = ownerAddress.toLowerCase(); - const normalizedSpenderAddress = spenderAddress.toLowerCase(); const txHash = await this.setAllowanceAsync( - normalizedTokenAddress, - normalizedOwnerAddress, - normalizedSpenderAddress, + tokenAddress, + ownerAddress, + spenderAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, txOpts, ); @@ -139,23 +148,24 @@ export class TokenWrapper extends ContractWrapper { tokenAddress: string, ownerAddress: string, spenderAddress: string, - methodOpts?: MethodOpts, + methodOpts: MethodOpts = {}, ): Promise<BigNumber> { - assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isETHAddressHex('ownerAddress', ownerAddress); assert.isETHAddressHex('spenderAddress', spenderAddress); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); const normalizedTokenAddress = tokenAddress.toLowerCase(); const normalizedOwnerAddress = ownerAddress.toLowerCase(); const normalizedSpenderAddress = spenderAddress.toLowerCase(); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); - const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; + const txData = {}; let allowanceInBaseUnits = await tokenContract.allowance.callAsync( normalizedOwnerAddress, normalizedSpenderAddress, txData, - defaultBlock, + methodOpts.defaultBlock, ); // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits); @@ -170,20 +180,10 @@ export class TokenWrapper extends ContractWrapper { public async getProxyAllowanceAsync( tokenAddress: string, ownerAddress: string, - methodOpts?: MethodOpts, + methodOpts: MethodOpts = {}, ): Promise<BigNumber> { - assert.isETHAddressHex('ownerAddress', ownerAddress); - assert.isETHAddressHex('tokenAddress', tokenAddress); - const normalizedTokenAddress = tokenAddress.toLowerCase(); - const normalizedOwnerAddress = ownerAddress.toLowerCase(); - - const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress(); - const allowanceInBaseUnits = await this.getAllowanceAsync( - normalizedTokenAddress, - normalizedOwnerAddress, - proxyAddress, - methodOpts, - ); + const proxyAddress = this._erc20ProxyWrapper.getContractAddress(); + const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts); return allowanceInBaseUnits; } /** @@ -202,16 +202,10 @@ export class TokenWrapper extends ContractWrapper { amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}, ): Promise<string> { - assert.isETHAddressHex('ownerAddress', ownerAddress); - assert.isETHAddressHex('tokenAddress', tokenAddress); - const normalizedTokenAddress = tokenAddress.toLowerCase(); - const normalizedOwnerAddress = ownerAddress.toLowerCase(); - assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); - - const proxyAddress = this._tokenTransferProxyWrapper.getContractAddress(); + const proxyAddress = this._erc20ProxyWrapper.getContractAddress(); const txHash = await this.setAllowanceAsync( - normalizedTokenAddress, - normalizedOwnerAddress, + tokenAddress, + ownerAddress, proxyAddress, amountInBaseUnits, txOpts, @@ -234,13 +228,9 @@ export class TokenWrapper extends ContractWrapper { ownerAddress: string, txOpts: TransactionOpts = {}, ): Promise<string> { - assert.isETHAddressHex('ownerAddress', ownerAddress); - assert.isETHAddressHex('tokenAddress', tokenAddress); - const normalizedTokenAddress = tokenAddress.toLowerCase(); - const normalizedOwnerAddress = ownerAddress.toLowerCase(); const txHash = await this.setProxyAllowanceAsync( - normalizedTokenAddress, - normalizedOwnerAddress, + tokenAddress, + ownerAddress, this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, txOpts, ); @@ -263,12 +253,13 @@ export class TokenWrapper extends ContractWrapper { txOpts: TransactionOpts = {}, ): Promise<string> { assert.isETHAddressHex('tokenAddress', tokenAddress); - assert.isETHAddressHex('toAddress', toAddress); await assert.isSenderAddressAsync('fromAddress', fromAddress, this._web3Wrapper); + assert.isETHAddressHex('toAddress', toAddress); + assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); + assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); const normalizedTokenAddress = tokenAddress.toLowerCase(); const normalizedFromAddress = fromAddress.toLowerCase(); const normalizedToAddress = toAddress.toLowerCase(); - assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); @@ -277,11 +268,15 @@ export class TokenWrapper extends ContractWrapper { throw new Error(ContractWrappersError.InsufficientBalanceForTransfer); } - const txHash = await tokenContract.transfer.sendTransactionAsync(normalizedToAddress, amountInBaseUnits, { - from: normalizedFromAddress, - gas: txOpts.gasLimit, - gasPrice: txOpts.gasPrice, - }); + const txHash = await tokenContract.transfer.sendTransactionAsync( + normalizedToAddress, + amountInBaseUnits, + removeUndefinedProperties({ + from: normalizedFromAddress, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, + }), + ); return txHash; } /** @@ -306,15 +301,16 @@ export class TokenWrapper extends ContractWrapper { amountInBaseUnits: BigNumber, txOpts: TransactionOpts = {}, ): Promise<string> { - assert.isETHAddressHex('toAddress', toAddress); - assert.isETHAddressHex('fromAddress', fromAddress); assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isETHAddressHex('fromAddress', fromAddress); + assert.isETHAddressHex('toAddress', toAddress); await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper); + assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); + assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); const normalizedToAddress = toAddress.toLowerCase(); const normalizedFromAddress = fromAddress.toLowerCase(); const normalizedTokenAddress = tokenAddress.toLowerCase(); const normalizedSenderAddress = senderAddress.toLowerCase(); - assert.isValidBaseUnitAmount('amountInBaseUnits', amountInBaseUnits); const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); @@ -336,11 +332,11 @@ export class TokenWrapper extends ContractWrapper { normalizedFromAddress, normalizedToAddress, amountInBaseUnits, - { + removeUndefinedProperties({ from: normalizedSenderAddress, gas: txOpts.gasLimit, gasPrice: txOpts.gasPrice, - }, + }), ); return txHash; } @@ -353,22 +349,22 @@ export class TokenWrapper extends ContractWrapper { * @param callback Callback that gets called when a log is added/removed * @return Subscription token used later to unsubscribe */ - public subscribe<ArgsType extends TokenContractEventArgs>( + public subscribe<ArgsType extends ERC20TokenEventArgs>( tokenAddress: string, - eventName: TokenEvents, + eventName: ERC20TokenEvents, indexFilterValues: IndexedFilterValues, callback: EventCallback<ArgsType>, ): string { assert.isETHAddressHex('tokenAddress', tokenAddress); - const normalizedTokenAddress = tokenAddress.toLowerCase(); - assert.doesBelongToStringEnum('eventName', eventName, TokenEvents); + assert.doesBelongToStringEnum('eventName', eventName, ERC20TokenEvents); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.isFunction('callback', callback); + const normalizedTokenAddress = tokenAddress.toLowerCase(); const subscriptionToken = this._subscribe<ArgsType>( normalizedTokenAddress, eventName, indexFilterValues, - artifacts.Token.abi, + artifacts.ERC20Token.compilerOutput.abi, callback, ); return subscriptionToken; @@ -378,6 +374,7 @@ export class TokenWrapper extends ContractWrapper { * @param subscriptionToken Subscription token returned by `subscribe()` */ public unsubscribe(subscriptionToken: string): void { + assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken); this._unsubscribe(subscriptionToken); } /** @@ -395,42 +392,44 @@ export class TokenWrapper extends ContractWrapper { * the value is the value you are interested in. E.g `{_from: aUserAddressHex}` * @return Array of logs that match the parameters */ - public async getLogsAsync<ArgsType extends TokenContractEventArgs>( + public async getLogsAsync<ArgsType extends ERC20TokenEventArgs>( tokenAddress: string, - eventName: TokenEvents, + eventName: ERC20TokenEvents, blockRange: BlockRange, indexFilterValues: IndexedFilterValues, ): Promise<Array<LogWithDecodedArgs<ArgsType>>> { assert.isETHAddressHex('tokenAddress', tokenAddress); - const normalizedTokenAddress = tokenAddress.toLowerCase(); - assert.doesBelongToStringEnum('eventName', eventName, TokenEvents); + assert.doesBelongToStringEnum('eventName', eventName, ERC20TokenEvents); assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); const logs = await this._getLogsAsync<ArgsType>( normalizedTokenAddress, eventName, blockRange, indexFilterValues, - artifacts.Token.abi, + artifacts.ERC20Token.compilerOutput.abi, ); return logs; } + // HACK: We don't want this method to be visible to the other units within that package but not to the end user. + // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. // tslint:disable-next-line:no-unused-variable private _invalidateContractInstances(): void { this.unsubscribeAll(); this._tokenContractsByAddress = {}; } - private async _getTokenContractAsync(tokenAddress: string): Promise<TokenContract> { + private async _getTokenContractAsync(tokenAddress: string): Promise<ERC20TokenContract> { const normalizedTokenAddress = tokenAddress.toLowerCase(); let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress]; if (!_.isUndefined(tokenContract)) { return tokenContract; } const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.Token, + artifacts.ERC20Token, normalizedTokenAddress, ); - const contractInstance = new TokenContract( + const contractInstance = new ERC20TokenContract( abi, address, this._web3Wrapper.getProvider(), diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts new file mode 100644 index 000000000..648c150df --- /dev/null +++ b/packages/contract-wrappers/src/contract_wrappers/erc721_proxy_wrapper.ts @@ -0,0 +1,84 @@ +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ContractAbi } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { artifacts } from '../artifacts'; +import { assert } from '../utils/assert'; + +import { ContractWrapper } from './contract_wrapper'; +import { ERC721ProxyContract } from './generated/erc721_proxy'; + +/** + * This class includes the functionality related to interacting with the ERC721Proxy contract. + */ +export class ERC721ProxyWrapper extends ContractWrapper { + public abi: ContractAbi = artifacts.ERC20Proxy.compilerOutput.abi; + private _erc721ProxyContractIfExists?: ERC721ProxyContract; + private _contractAddressIfExists?: string; + constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) { + super(web3Wrapper, networkId); + this._contractAddressIfExists = contractAddressIfExists; + } + /** + * Get the 4 bytes ID of this asset proxy + * @return Proxy id + */ + public async getProxyIdAsync(): Promise<string> { + const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync(); + const proxyId = await ERC721ProxyContractInstance.getProxyId.callAsync(); + return proxyId; + } + /** + * Check if the Exchange contract address is authorized by the ERC721Proxy contract. + * @param exchangeContractAddress The hex encoded address of the Exchange contract to call. + * @return Whether the exchangeContractAddress is authorized. + */ + public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> { + assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress); + const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase(); + const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync(); + const isAuthorized = await ERC721ProxyContractInstance.authorized.callAsync(normalizedExchangeContractAddress); + return isAuthorized; + } + /** + * Get the list of all Exchange contract addresses authorized by the ERC721Proxy contract. + * @return The list of authorized addresses. + */ + public async getAuthorizedAddressesAsync(): Promise<string[]> { + const ERC721ProxyContractInstance = await this._getERC721ProxyContractAsync(); + const authorizedAddresses = await ERC721ProxyContractInstance.getAuthorizedAddresses.callAsync(); + return authorizedAddresses; + } + /** + * Retrieves the Ethereum address of the ERC721Proxy contract deployed on the network + * that the user-passed web3 provider is connected to. + * @returns The Ethereum address of the ERC721Proxy contract being used. + */ + public getContractAddress(): string { + const contractAddress = this._getContractAddress(artifacts.ERC721Proxy, this._contractAddressIfExists); + return contractAddress; + } + // HACK: We don't want this method to be visible to the other units within that package but not to the end user. + // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. + // tslint:disable-next-line:no-unused-variable + private _invalidateContractInstance(): void { + delete this._erc721ProxyContractIfExists; + } + private async _getERC721ProxyContractAsync(): Promise<ERC721ProxyContract> { + if (!_.isUndefined(this._erc721ProxyContractIfExists)) { + return this._erc721ProxyContractIfExists; + } + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( + artifacts.ERC721Proxy, + this._contractAddressIfExists, + ); + const contractInstance = new ERC721ProxyContract( + abi, + address, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + this._erc721ProxyContractIfExists = contractInstance; + return this._erc721ProxyContractIfExists; + } +} diff --git a/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts new file mode 100644 index 000000000..b7e5519c4 --- /dev/null +++ b/packages/contract-wrappers/src/contract_wrappers/erc721_token_wrapper.ts @@ -0,0 +1,467 @@ +import { schemas } from '@0xproject/json-schemas'; +import { BigNumber } from '@0xproject/utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { constants } from '../../test/utils/constants'; +import { artifacts } from '../artifacts'; +import { methodOptsSchema } from '../schemas/method_opts_schema'; +import { txOptsSchema } from '../schemas/tx_opts_schema'; +import { + BlockRange, + ContractWrappersError, + EventCallback, + IndexedFilterValues, + MethodOpts, + TransactionOpts, +} from '../types'; +import { assert } from '../utils/assert'; + +import { ContractWrapper } from './contract_wrapper'; +import { ERC721ProxyWrapper } from './erc721_proxy_wrapper'; +import { ERC721TokenContract, ERC721TokenEventArgs, ERC721TokenEvents } from './generated/erc721_token'; + +const removeUndefinedProperties = _.pickBy; + +/** + * This class includes all the functionality related to interacting with ERC721 token contracts. + * All ERC721 method calls are supported, along with some convenience methods for getting/setting allowances + * to the 0x ERC721 Proxy smart contract. + */ +export class ERC721TokenWrapper extends ContractWrapper { + public abi: ContractAbi = artifacts.ERC721Token.compilerOutput.abi; + private _tokenContractsByAddress: { [address: string]: ERC721TokenContract }; + private _erc721ProxyWrapper: ERC721ProxyWrapper; + constructor( + web3Wrapper: Web3Wrapper, + networkId: number, + erc721ProxyWrapper: ERC721ProxyWrapper, + blockPollingIntervalMs?: number, + ) { + super(web3Wrapper, networkId, blockPollingIntervalMs); + this._tokenContractsByAddress = {}; + this._erc721ProxyWrapper = erc721ProxyWrapper; + } + /** + * Count all NFTs assigned to an owner + * NFTs assigned to the zero address are considered invalid, and this function throws for queries about the zero address. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param ownerAddress The hex encoded user Ethereum address whose balance you would like to check. + * @param methodOpts Optional arguments this method accepts. + * @return The number of NFTs owned by `ownerAddress`, possibly zero + */ + public async getTokenCountAsync( + tokenAddress: string, + ownerAddress: string, + methodOpts: MethodOpts = {}, + ): Promise<BigNumber> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isETHAddressHex('ownerAddress', ownerAddress); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + const normalizedOwnerAddress = ownerAddress.toLowerCase(); + + const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); + + const txData = {}; + let balance = await tokenContract.balanceOf.callAsync(normalizedOwnerAddress, txData, methodOpts.defaultBlock); + // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber + balance = new BigNumber(balance); + return balance; + } + /** + * Find the owner of an NFT + * NFTs assigned to zero address are considered invalid, and queries about them do throw. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param tokenId The identifier for an NFT + * @param methodOpts Optional arguments this method accepts. + * @return The address of the owner of the NFT + */ + public async getOwnerOfAsync( + tokenAddress: string, + tokenId: BigNumber, + methodOpts: MethodOpts = {}, + ): Promise<string> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isBigNumber('tokenId', tokenId); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + + const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); + + const txData = {}; + try { + const tokenOwner = await tokenContract.ownerOf.callAsync(tokenId, txData, methodOpts.defaultBlock); + return tokenOwner; + } catch (err) { + throw new Error(ContractWrappersError.ERC721OwnerNotFound); + } + } + /** + * Query if an address is an authorized operator for all NFT's of `ownerAddress` + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param ownerAddress The hex encoded user Ethereum address of the token owner. + * @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to check if approved. + * @param methodOpts Optional arguments this method accepts. + * @return True if `operatorAddress` is an approved operator for `ownerAddress`, false otherwise + */ + public async isApprovedForAllAsync( + tokenAddress: string, + ownerAddress: string, + operatorAddress: string, + methodOpts: MethodOpts = {}, + ): Promise<boolean> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isETHAddressHex('ownerAddress', ownerAddress); + assert.isETHAddressHex('operatorAddress', operatorAddress); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + const normalizedOwnerAddress = ownerAddress.toLowerCase(); + const normalizedOperatorAddress = operatorAddress.toLowerCase(); + + const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); + + const txData = {}; + const isApprovedForAll = await tokenContract.isApprovedForAll.callAsync( + normalizedOwnerAddress, + normalizedOperatorAddress, + txData, + methodOpts.defaultBlock, + ); + return isApprovedForAll; + } + /** + * Query if 0x proxy is an authorized operator for all NFT's of `ownerAddress` + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param ownerAddress The hex encoded user Ethereum address of the token owner. + * @param methodOpts Optional arguments this method accepts. + * @return True if `operatorAddress` is an approved operator for `ownerAddress`, false otherwise + */ + public async isProxyApprovedForAllAsync( + tokenAddress: string, + ownerAddress: string, + methodOpts: MethodOpts = {}, + ): Promise<boolean> { + const proxyAddress = this._erc721ProxyWrapper.getContractAddress(); + const isProxyApprovedForAll = await this.isApprovedForAllAsync( + tokenAddress, + ownerAddress, + proxyAddress, + methodOpts, + ); + return isProxyApprovedForAll; + } + /** + * Get the approved address for a single NFT. Returns undefined if no approval was set + * Throws if `_tokenId` is not a valid NFT + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param tokenId The identifier for an NFT + * @param methodOpts Optional arguments this method accepts. + * @return The approved address for this NFT, or the undefined if there is none + */ + public async getApprovedIfExistsAsync( + tokenAddress: string, + tokenId: BigNumber, + methodOpts: MethodOpts = {}, + ): Promise<string | undefined> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isBigNumber('tokenId', tokenId); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + + const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); + + const txData = {}; + const approvedAddress = await tokenContract.getApproved.callAsync(tokenId, txData, methodOpts.defaultBlock); + if (approvedAddress === constants.NULL_ADDRESS) { + return undefined; + } + return approvedAddress; + } + /** + * Checks if 0x proxy is approved for a single NFT + * Throws if `_tokenId` is not a valid NFT + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param tokenId The identifier for an NFT + * @param methodOpts Optional arguments this method accepts. + * @return True if 0x proxy is approved + */ + public async isProxyApprovedAsync( + tokenAddress: string, + tokenId: BigNumber, + methodOpts: MethodOpts = {}, + ): Promise<boolean> { + const proxyAddress = this._erc721ProxyWrapper.getContractAddress(); + const approvedAddress = await this.getApprovedIfExistsAsync(tokenAddress, tokenId, methodOpts); + const isProxyApproved = approvedAddress === proxyAddress; + return isProxyApproved; + } + /** + * Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets. + * Throws if `_tokenId` is not a valid NFT + * Emits the ApprovalForAll event. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param ownerAddress The hex encoded user Ethereum address of the token owner. + * @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to set approval for. + * @param isApproved The boolean variable to set the approval to. + * @param txOpts Transaction parameters. + * @return Transaction hash. + */ + public async setApprovalForAllAsync( + tokenAddress: string, + ownerAddress: string, + operatorAddress: string, + isApproved: boolean, + txOpts: TransactionOpts = {}, + ): Promise<string> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + await assert.isSenderAddressAsync('ownerAddress', ownerAddress, this._web3Wrapper); + assert.isETHAddressHex('operatorAddress', operatorAddress); + assert.isBoolean('isApproved', isApproved); + assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + const normalizedOwnerAddress = ownerAddress.toLowerCase(); + const normalizedOperatorAddress = operatorAddress.toLowerCase(); + + const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); + const txHash = await tokenContract.setApprovalForAll.sendTransactionAsync( + normalizedOperatorAddress, + isApproved, + removeUndefinedProperties({ + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, + from: normalizedOwnerAddress, + }), + ); + return txHash; + } + /** + * Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets. + * Throws if `_tokenId` is not a valid NFT + * Emits the ApprovalForAll event. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param ownerAddress The hex encoded user Ethereum address of the token owner. + * @param operatorAddress The hex encoded user Ethereum address of the operator you'd like to set approval for. + * @param isApproved The boolean variable to set the approval to. + * @param txOpts Transaction parameters. + * @return Transaction hash. + */ + public async setProxyApprovalForAllAsync( + tokenAddress: string, + ownerAddress: string, + isApproved: boolean, + txOpts: TransactionOpts = {}, + ): Promise<string> { + const proxyAddress = this._erc721ProxyWrapper.getContractAddress(); + const txHash = await this.setApprovalForAllAsync(tokenAddress, ownerAddress, proxyAddress, isApproved, txOpts); + return txHash; + } + /** + * Set or reaffirm the approved address for an NFT + * The zero address indicates there is no approved address. Throws unless `msg.sender` is the current NFT owner, + * or an authorized operator of the current owner. + * Throws if `_tokenId` is not a valid NFT + * Emits the Approval event. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param approvedAddress The hex encoded user Ethereum address you'd like to set approval for. + * @param tokenId The identifier for an NFT + * @param txOpts Transaction parameters. + * @return Transaction hash. + */ + public async setApprovalAsync( + tokenAddress: string, + approvedAddress: string, + tokenId: BigNumber, + txOpts: TransactionOpts = {}, + ): Promise<string> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isETHAddressHex('approvedAddress', approvedAddress); + assert.isBigNumber('tokenId', tokenId); + assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + const normalizedApprovedAddress = approvedAddress.toLowerCase(); + + const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); + const tokenOwnerAddress = await tokenContract.ownerOf.callAsync(tokenId); + await assert.isSenderAddressAsync('tokenOwnerAddress', tokenOwnerAddress, this._web3Wrapper); + const txHash = await tokenContract.approve.sendTransactionAsync( + normalizedApprovedAddress, + tokenId, + removeUndefinedProperties({ + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, + from: tokenOwnerAddress, + }), + ); + return txHash; + } + /** + * Set or reaffirm 0x proxy as an approved address for an NFT + * Throws unless `msg.sender` is the current NFT owner, or an authorized operator of the current owner. + * Throws if `_tokenId` is not a valid NFT + * Emits the Approval event. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param tokenId The identifier for an NFT + * @param txOpts Transaction parameters. + * @return Transaction hash. + */ + public async setProxyApprovalAsync( + tokenAddress: string, + tokenId: BigNumber, + txOpts: TransactionOpts = {}, + ): Promise<string> { + const proxyAddress = this._erc721ProxyWrapper.getContractAddress(); + const txHash = await this.setApprovalAsync(tokenAddress, proxyAddress, tokenId, txOpts); + return txHash; + } + /** + * Enable or disable approval for a third party ("operator") to manage all of `ownerAddress`'s assets. + * Throws if `_tokenId` is not a valid NFT + * Emits the ApprovalForAll event. + * @param tokenAddress The hex encoded contract Ethereum address where the ERC721 token is deployed. + * @param receiverAddress The hex encoded Ethereum address of the user to send the NFT to. + * @param senderAddress The hex encoded Ethereum address of the user to send the NFT to. + * @param tokenId The identifier for an NFT + * @param txOpts Transaction parameters. + * @return Transaction hash. + */ + public async transferFromAsync( + tokenAddress: string, + receiverAddress: string, + senderAddress: string, + tokenId: BigNumber, + txOpts: TransactionOpts = {}, + ): Promise<string> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.isETHAddressHex('receiverAddress', receiverAddress); + await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper); + assert.doesConformToSchema('txOpts', txOpts, txOptsSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + const normalizedReceiverAddress = receiverAddress.toLowerCase(); + const normalizedSenderAddress = senderAddress.toLowerCase(); + const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress); + const ownerAddress = await this.getOwnerOfAsync(tokenAddress, tokenId); + const isApprovedForAll = await this.isApprovedForAllAsync( + normalizedTokenAddress, + ownerAddress, + normalizedSenderAddress, + ); + if (!isApprovedForAll) { + const approvedAddress = await this.getApprovedIfExistsAsync(normalizedTokenAddress, tokenId); + if (approvedAddress !== senderAddress) { + throw new Error(ContractWrappersError.ERC721NoApproval); + } + } + const txHash = await tokenContract.transferFrom.sendTransactionAsync( + ownerAddress, + normalizedReceiverAddress, + tokenId, + removeUndefinedProperties({ + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, + from: normalizedSenderAddress, + }), + ); + return txHash; + } + /** + * Subscribe to an event type emitted by the Token contract. + * @param tokenAddress The hex encoded address where the ERC721 token is deployed. + * @param eventName The token contract event you would like to subscribe to. + * @param indexFilterValues An object where the keys are indexed args returned by the event and + * the value is the value you are interested in. E.g `{maker: aUserAddressHex}` + * @param callback Callback that gets called when a log is added/removed + * @return Subscription token used later to unsubscribe + */ + public subscribe<ArgsType extends ERC721TokenEventArgs>( + tokenAddress: string, + eventName: ERC721TokenEvents, + indexFilterValues: IndexedFilterValues, + callback: EventCallback<ArgsType>, + ): string { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.doesBelongToStringEnum('eventName', eventName, ERC721TokenEvents); + assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); + assert.isFunction('callback', callback); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + const subscriptionToken = this._subscribe<ArgsType>( + normalizedTokenAddress, + eventName, + indexFilterValues, + artifacts.ERC721Token.compilerOutput.abi, + callback, + ); + return subscriptionToken; + } + /** + * Cancel a subscription + * @param subscriptionToken Subscription token returned by `subscribe()` + */ + public unsubscribe(subscriptionToken: string): void { + assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken); + this._unsubscribe(subscriptionToken); + } + /** + * Cancels all existing subscriptions + */ + public unsubscribeAll(): void { + super._unsubscribeAll(); + } + /** + * Gets historical logs without creating a subscription + * @param tokenAddress An address of the token that emitted the logs. + * @param eventName The token contract event you would like to subscribe to. + * @param blockRange Block range to get logs from. + * @param indexFilterValues An object where the keys are indexed args returned by the event and + * the value is the value you are interested in. E.g `{_from: aUserAddressHex}` + * @return Array of logs that match the parameters + */ + public async getLogsAsync<ArgsType extends ERC721TokenEventArgs>( + tokenAddress: string, + eventName: ERC721TokenEvents, + blockRange: BlockRange, + indexFilterValues: IndexedFilterValues, + ): Promise<Array<LogWithDecodedArgs<ArgsType>>> { + assert.isETHAddressHex('tokenAddress', tokenAddress); + assert.doesBelongToStringEnum('eventName', eventName, ERC721TokenEvents); + assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); + assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); + const normalizedTokenAddress = tokenAddress.toLowerCase(); + const logs = await this._getLogsAsync<ArgsType>( + normalizedTokenAddress, + eventName, + blockRange, + indexFilterValues, + artifacts.ERC721Token.compilerOutput.abi, + ); + return logs; + } + // HACK: We don't want this method to be visible to the other units within that package but not to the end user. + // TS doesn't give that possibility and therefore we make it private and access it over an any cast. Because of that tslint sees it as unused. + // tslint:disable-next-line:no-unused-variable + private _invalidateContractInstances(): void { + this.unsubscribeAll(); + this._tokenContractsByAddress = {}; + } + private async _getTokenContractAsync(tokenAddress: string): Promise<ERC721TokenContract> { + const normalizedTokenAddress = tokenAddress.toLowerCase(); + let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress]; + if (!_.isUndefined(tokenContract)) { + return tokenContract; + } + const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( + artifacts.ERC721Token, + normalizedTokenAddress, + ); + const contractInstance = new ERC721TokenContract( + abi, + address, + this._web3Wrapper.getProvider(), + this._web3Wrapper.getContractDefaults(), + ); + tokenContract = contractInstance; + this._tokenContractsByAddress[normalizedTokenAddress] = tokenContract; + return tokenContract; + } +} diff --git a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts index 36b7a234a..01440a5e1 100644 --- a/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/ether_token_wrapper.ts @@ -1,7 +1,7 @@ import { schemas } from '@0xproject/json-schemas'; -import { ContractAbi, LogWithDecodedArgs } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; import { artifacts } from '../artifacts'; @@ -9,22 +9,29 @@ import { BlockRange, ContractWrappersError, EventCallback, IndexedFilterValues, import { assert } from '../utils/assert'; import { ContractWrapper } from './contract_wrapper'; -import { EtherTokenContract, EtherTokenContractEventArgs, EtherTokenEvents } from './generated/ether_token'; -import { TokenWrapper } from './token_wrapper'; +import { ERC20TokenWrapper } from './erc20_token_wrapper'; +import { WETH9Contract, WETH9EventArgs, WETH9Events } from './generated/weth9'; + +const removeUndefinedProperties = _.pickBy; /** * This class includes all the functionality related to interacting with a wrapped Ether ERC20 token contract. * The caller can convert ETH into the equivalent number of wrapped ETH ERC20 tokens and back. */ export class EtherTokenWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.EtherToken.abi; + public abi: ContractAbi = artifacts.EtherToken.compilerOutput.abi; private _etherTokenContractsByAddress: { - [address: string]: EtherTokenContract; + [address: string]: WETH9Contract; } = {}; - private _tokenWrapper: TokenWrapper; - constructor(web3Wrapper: Web3Wrapper, networkId: number, tokenWrapper: TokenWrapper) { - super(web3Wrapper, networkId); - this._tokenWrapper = tokenWrapper; + private _erc20TokenWrapper: ERC20TokenWrapper; + constructor( + web3Wrapper: Web3Wrapper, + networkId: number, + erc20TokenWrapper: ERC20TokenWrapper, + blockPollingIntervalMs?: number, + ) { + super(web3Wrapper, networkId, blockPollingIntervalMs); + this._erc20TokenWrapper = erc20TokenWrapper; } /** * Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens @@ -52,12 +59,14 @@ export class EtherTokenWrapper extends ContractWrapper { assert.assert(ethBalanceInWei.gte(amountInWei), ContractWrappersError.InsufficientEthBalanceForDeposit); const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress); - const txHash = await wethContract.deposit.sendTransactionAsync({ - from: normalizedDepositorAddress, - value: amountInWei, - gas: txOpts.gasLimit, - gasPrice: txOpts.gasPrice, - }); + const txHash = await wethContract.deposit.sendTransactionAsync( + removeUndefinedProperties({ + from: normalizedDepositorAddress, + value: amountInWei, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, + }), + ); return txHash; } /** @@ -81,7 +90,7 @@ export class EtherTokenWrapper extends ContractWrapper { const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase(); const normalizedWithdrawerAddress = withdrawer.toLowerCase(); - const WETHBalanceInBaseUnits = await this._tokenWrapper.getBalanceAsync( + const WETHBalanceInBaseUnits = await this._erc20TokenWrapper.getBalanceAsync( normalizedEtherTokenAddress, normalizedWithdrawerAddress, ); @@ -91,11 +100,14 @@ export class EtherTokenWrapper extends ContractWrapper { ); const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress); - const txHash = await wethContract.withdraw.sendTransactionAsync(amountInWei, { - from: normalizedWithdrawerAddress, - gas: txOpts.gasLimit, - gasPrice: txOpts.gasPrice, - }); + const txHash = await wethContract.withdraw.sendTransactionAsync( + amountInWei, + removeUndefinedProperties({ + from: normalizedWithdrawerAddress, + gas: txOpts.gasLimit, + gasPrice: txOpts.gasPrice, + }), + ); return txHash; } /** @@ -107,15 +119,15 @@ export class EtherTokenWrapper extends ContractWrapper { * the value is the value you are interested in. E.g `{_owner: aUserAddressHex}` * @return Array of logs that match the parameters */ - public async getLogsAsync<ArgsType extends EtherTokenContractEventArgs>( + public async getLogsAsync<ArgsType extends WETH9EventArgs>( etherTokenAddress: string, - eventName: EtherTokenEvents, + eventName: WETH9Events, blockRange: BlockRange, indexFilterValues: IndexedFilterValues, ): Promise<Array<LogWithDecodedArgs<ArgsType>>> { assert.isETHAddressHex('etherTokenAddress', etherTokenAddress); const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase(); - assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents); + assert.doesBelongToStringEnum('eventName', eventName, WETH9Events); assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); const logs = await this._getLogsAsync<ArgsType>( @@ -123,7 +135,7 @@ export class EtherTokenWrapper extends ContractWrapper { eventName, blockRange, indexFilterValues, - artifacts.EtherToken.abi, + artifacts.EtherToken.compilerOutput.abi, ); return logs; } @@ -136,22 +148,22 @@ export class EtherTokenWrapper extends ContractWrapper { * @param callback Callback that gets called when a log is added/removed * @return Subscription token used later to unsubscribe */ - public subscribe<ArgsType extends EtherTokenContractEventArgs>( + public subscribe<ArgsType extends WETH9EventArgs>( etherTokenAddress: string, - eventName: EtherTokenEvents, + eventName: WETH9Events, indexFilterValues: IndexedFilterValues, callback: EventCallback<ArgsType>, ): string { assert.isETHAddressHex('etherTokenAddress', etherTokenAddress); const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase(); - assert.doesBelongToStringEnum('eventName', eventName, EtherTokenEvents); + assert.doesBelongToStringEnum('eventName', eventName, WETH9Events); assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema); assert.isFunction('callback', callback); const subscriptionToken = this._subscribe<ArgsType>( normalizedEtherTokenAddress, eventName, indexFilterValues, - artifacts.EtherToken.abi, + artifacts.EtherToken.compilerOutput.abi, callback, ); return subscriptionToken; @@ -161,6 +173,7 @@ export class EtherTokenWrapper extends ContractWrapper { * @param subscriptionToken Subscription token returned by `subscribe()` */ public unsubscribe(subscriptionToken: string): void { + assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken); this._unsubscribe(subscriptionToken); } /** @@ -187,7 +200,7 @@ export class EtherTokenWrapper extends ContractWrapper { this.unsubscribeAll(); this._etherTokenContractsByAddress = {}; } - private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<EtherTokenContract> { + private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<WETH9Contract> { let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress]; if (!_.isUndefined(etherTokenContract)) { return etherTokenContract; @@ -196,7 +209,7 @@ export class EtherTokenWrapper extends ContractWrapper { artifacts.EtherToken, etherTokenAddress, ); - const contractInstance = new EtherTokenContract( + const contractInstance = new WETH9Contract( abi, address, this._web3Wrapper.getProvider(), diff --git a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts index 8548a06b6..0e8664cc9 100644 --- a/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts +++ b/packages/contract-wrappers/src/contract_wrappers/exchange_wrapper.ts @@ -1,209 +1,162 @@ import { schemas } from '@0xproject/json-schemas'; -import { formatters, getOrderHashHex, OrderStateUtils } from '@0xproject/order-utils'; -import { - BlockParamLiteral, - ContractAbi, - DecodedLogArgs, - ECSignature, - ExchangeContractErrs, - LogEntry, - LogWithDecodedArgs, - Order, - OrderState, - SignedOrder, -} from '@0xproject/types'; +import { Order, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; import { artifacts } from '../artifacts'; -import { SimpleBalanceAndProxyAllowanceFetcher } from '../fetchers/simple_balance_and_proxy_allowance_fetcher'; -import { SimpleOrderFilledCancelledFetcher } from '../fetchers/simple_order_filled_cancelled_fetcher'; -import { BalanceAndProxyAllowanceLazyStore } from '../stores/balance_proxy_allowance_lazy_store'; -import { - BlockRange, - EventCallback, - ExchangeContractErrCodes, - IndexedFilterValues, - MethodOpts, - OrderCancellationRequest, - OrderFillRequest, - OrderTransactionOpts, - ValidateOrderFillableOpts, -} from '../types'; +import { methodOptsSchema } from '../schemas/method_opts_schema'; +import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema'; +import { txOptsSchema } from '../schemas/tx_opts_schema'; +import { BlockRange, EventCallback, IndexedFilterValues, MethodOpts, OrderInfo, OrderTransactionOpts } from '../types'; import { assert } from '../utils/assert'; import { decorators } from '../utils/decorators'; -import { ExchangeTransferSimulator } from '../utils/exchange_transfer_simulator'; -import { OrderValidationUtils } from '../utils/order_validation_utils'; import { ContractWrapper } from './contract_wrapper'; -import { - ExchangeContract, - ExchangeContractEventArgs, - ExchangeEvents, - LogErrorContractEventArgs, -} from './generated/exchange'; -import { TokenWrapper } from './token_wrapper'; -const SHOULD_VALIDATE_BY_DEFAULT = true; - -interface ExchangeContractErrCodesToMsgs { - [exchangeContractErrCodes: number]: string; -} +import { ExchangeContract, ExchangeEventArgs, ExchangeEvents } from './generated/exchange'; /** - * This class includes all the functionality related to calling methods and subscribing to - * events of the 0x Exchange smart contract. + * This class includes all the functionality related to calling methods, sending transactions and subscribing to + * events of the 0x V2 Exchange smart contract. */ export class ExchangeWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.Exchange.abi; + public abi: ContractAbi = artifacts.Exchange.compilerOutput.abi; private _exchangeContractIfExists?: ExchangeContract; - private _orderValidationUtilsIfExists?: OrderValidationUtils; - private _tokenWrapper: TokenWrapper; - private _exchangeContractErrCodesToMsg: ExchangeContractErrCodesToMsgs = { - [ExchangeContractErrCodes.ERROR_FILL_EXPIRED]: ExchangeContractErrs.OrderFillExpired, - [ExchangeContractErrCodes.ERROR_CANCEL_EXPIRED]: ExchangeContractErrs.OrderFillExpired, - [ExchangeContractErrCodes.ERROR_FILL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero, - [ExchangeContractErrCodes.ERROR_CANCEL_NO_VALUE]: ExchangeContractErrs.OrderRemainingFillAmountZero, - [ExchangeContractErrCodes.ERROR_FILL_TRUNCATION]: ExchangeContractErrs.OrderFillRoundingError, - [ExchangeContractErrCodes.ERROR_FILL_BALANCE_ALLOWANCE]: ExchangeContractErrs.FillBalanceAllowanceError, - }; private _contractAddressIfExists?: string; private _zrxContractAddressIfExists?: string; constructor( web3Wrapper: Web3Wrapper, networkId: number, - tokenWrapper: TokenWrapper, contractAddressIfExists?: string, zrxContractAddressIfExists?: string, + blockPollingIntervalMs?: number, ) { - super(web3Wrapper, networkId); - this._tokenWrapper = tokenWrapper; + super(web3Wrapper, networkId, blockPollingIntervalMs); this._contractAddressIfExists = contractAddressIfExists; this._zrxContractAddressIfExists = zrxContractAddressIfExists; } /** - * Returns the unavailable takerAmount of an order. Unavailable amount is defined as the total - * amount that has been filled or cancelled. The remaining takerAmount can be calculated by - * subtracting the unavailable amount from the total order takerAmount. - * @param orderHash The hex encoded orderHash for which you would like to retrieve the - * unavailable takerAmount. - * @param methodOpts Optional arguments this method accepts. - * @return The amount of the order (in taker tokens) that has either been filled or cancelled. + * Retrieve the address of an asset proxy by signature. + * @param proxySignature The 4 bytes signature of an asset proxy + * @param methodOpts Optional arguments this method accepts. + * @return The address of an asset proxy for a given signature */ - public async getUnavailableTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> { - assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); + public async getAssetProxyBySignatureAsync(proxySignature: string, methodOpts: MethodOpts = {}): Promise<string> { + assert.isHexString('proxySignature', proxySignature); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + const exchangeContract = await this._getExchangeContractAsync(); + const txData = {}; + const assetProxy = await exchangeContract.getAssetProxy.callAsync( + proxySignature, + txData, + methodOpts.defaultBlock, + ); + return assetProxy; + } + /** + * Retrieve the takerAssetAmount of an order that has already been filled. + * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAssetAmount. + * @param methodOpts Optional arguments this method accepts. + * @return The amount of the order (in taker asset base units) that has already been filled. + */ + public async getFilledTakerAssetAmountAsync(orderHash: string, methodOpts: MethodOpts = {}): Promise<BigNumber> { + assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); const exchangeContract = await this._getExchangeContractAsync(); - const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; + const txData = {}; - let unavailableTakerTokenAmount = await exchangeContract.getUnavailableTakerTokenAmount.callAsync( + const filledTakerAssetAmountInBaseUnits = await exchangeContract.filled.callAsync( orderHash, txData, - defaultBlock, + methodOpts.defaultBlock, ); - // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber - unavailableTakerTokenAmount = new BigNumber(unavailableTakerTokenAmount); - return unavailableTakerTokenAmount; + return filledTakerAssetAmountInBaseUnits; } /** - * Retrieve the takerAmount of an order that has already been filled. - * @param orderHash The hex encoded orderHash for which you would like to retrieve the filled takerAmount. + * Retrieve the exchange contract version * @param methodOpts Optional arguments this method accepts. - * @return The amount of the order (in taker tokens) that has already been filled. + * @return Version */ - public async getFilledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> { - assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); + public async getVersionAsync(methodOpts: MethodOpts = {}): Promise<string> { + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + const exchangeContract = await this._getExchangeContractAsync(); + const txData = {}; + const version = await exchangeContract.VERSION.callAsync(txData, methodOpts.defaultBlock); + return version; + } + /** + * Retrieve the set order epoch for a given makerAddress & senderAddress pair. + * Orders can be bulk cancelled by setting the order epoch to a value lower then the salt value of orders one wishes to cancel. + * @param makerAddress Maker address + * @param senderAddress Sender address + * @param methodOpts Optional arguments this method accepts. + * @return Order epoch. Defaults to 0. + */ + public async getOrderEpochAsync( + makerAddress: string, + senderAddress: string, + methodOpts: MethodOpts = {}, + ): Promise<BigNumber> { + assert.isETHAddressHex('makerAddress', makerAddress); + assert.isETHAddressHex('senderAddress', senderAddress); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); const exchangeContract = await this._getExchangeContractAsync(); - const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; + const txData = {}; - let fillAmountInBaseUnits = await exchangeContract.filled.callAsync(orderHash, txData, defaultBlock); - // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber - fillAmountInBaseUnits = new BigNumber(fillAmountInBaseUnits); - return fillAmountInBaseUnits; + const orderEpoch = await exchangeContract.orderEpoch.callAsync( + makerAddress, + senderAddress, + txData, + methodOpts.defaultBlock, + ); + return orderEpoch; } /** - * Retrieve the takerAmount of an order that has been cancelled. - * @param orderHash The hex encoded orderHash for which you would like to retrieve the - * cancelled takerAmount. + * Check if an order has been cancelled. Order cancellations are binary + * @param orderHash The hex encoded orderHash for which you would like to retrieve the cancelled takerAmount. * @param methodOpts Optional arguments this method accepts. - * @return The amount of the order (in taker tokens) that has been cancelled. + * @return Whether the order has been cancelled. */ - public async getCancelledTakerAmountAsync(orderHash: string, methodOpts?: MethodOpts): Promise<BigNumber> { + public async isCancelledAsync(orderHash: string, methodOpts: MethodOpts = {}): Promise<boolean> { assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema); - + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); const exchangeContract = await this._getExchangeContractAsync(); - const defaultBlock = _.isUndefined(methodOpts) ? undefined : methodOpts.defaultBlock; + const txData = {}; - let cancelledAmountInBaseUnits = await exchangeContract.cancelled.callAsync(orderHash, txData, defaultBlock); - // Wrap BigNumbers returned from web3 with our own (later) version of BigNumber - cancelledAmountInBaseUnits = new BigNumber(cancelledAmountInBaseUnits); - return cancelledAmountInBaseUnits; - } - /** - * Fills a signed order with an amount denominated in baseUnits of the taker token. - * Since the order in which transactions are included in the next block is indeterminate, race-conditions - * could arise where a users balance or allowance changes before the fillOrder executes. Because of this, - * we allow you to specify `shouldThrowOnInsufficientBalanceOrAllowance`. - * If false, the smart contract will not throw if the parties - * do not have sufficient balances/allowances, preserving gas costs. Setting it to true forgoes this check - * and causes the smart contract to throw (using all the gas supplied) instead. - * @param signedOrder An object that conforms to the SignedOrder interface. - * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that - * you wish to fill. - * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw - * if upon execution the tokens cannot be transferred. - * @param takerAddress The user Ethereum address who would like to fill this order. - * Must be available via the supplied Provider - * passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. + const isCancelled = await exchangeContract.cancelled.callAsync(orderHash, txData, methodOpts.defaultBlock); + return isCancelled; + } + /** + * Fills a signed order with an amount denominated in baseUnits of the taker asset. + * @param signedOrder An object that conforms to the SignedOrder interface. + * @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. + * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. * @return Transaction hash. */ @decorators.asyncZeroExErrorHandler public async fillOrderAsync( signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - shouldThrowOnInsufficientBalanceOrAllowance: boolean, + takerAssetFillAmount: BigNumber, takerAddress: string, orderTransactionOpts: OrderTransactionOpts = {}, ): Promise<string> { assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); + assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); const normalizedTakerAddress = takerAddress.toLowerCase(); const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(signedOrder); - - const txHash: string = await exchangeInstance.fillOrder.sendTransactionAsync( - orderAddresses, - orderValues, - fillTakerTokenAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, + const txHash = await exchangeInstance.fillOrder.sendTransactionAsync( + signedOrder, + takerAssetFillAmount, + signedOrder.signature, { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, @@ -213,99 +166,70 @@ export class ExchangeWrapper extends ContractWrapper { return txHash; } /** - * Sequentially and atomically fills signedOrders up to the specified takerTokenFillAmount. - * If the fill amount is reached - it succeeds and does not fill the rest of the orders. - * If fill amount is not reached - it fills as much of the fill amount as possible and succeeds. - * @param signedOrders The array of signedOrders that you would like to fill until - * takerTokenFillAmount is reached. - * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. - * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw if - * upon execution any of the tokens cannot be transferred. - * If set to false, the call will continue to fill subsequent - * signedOrders even when some cannot be filled. - * @param takerAddress The user Ethereum address who would like to fill these - * orders. Must be available via the supplied Provider - * passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. + * No-throw version of fillOrderAsync. This version will not throw if the fill fails. This allows the caller to save gas at the expense of not knowing the reason the fill failed. + * @param signedOrder An object that conforms to the SignedOrder interface. + * @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. + * @param takerAddress The user Ethereum address who would like to fill this order. + * Must be available via the supplied Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. * @return Transaction hash. */ @decorators.asyncZeroExErrorHandler - public async fillOrdersUpToAsync( - signedOrders: SignedOrder[], - fillTakerTokenAmount: BigNumber, - shouldThrowOnInsufficientBalanceOrAllowance: boolean, + public async fillOrderNoThrowAsync( + signedOrder: SignedOrder, + takerAssetFillAmount: BigNumber, takerAddress: string, orderTransactionOpts: OrderTransactionOpts = {}, ): Promise<string> { - assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); - const takerTokenAddresses = _.map(signedOrders, signedOrder => signedOrder.takerTokenAddress); - assert.hasAtMostOneUniqueValue( - takerTokenAddresses, - ExchangeContractErrs.MultipleTakerTokensInFillUpToDisallowed, - ); - const exchangeContractAddresses = _.map(signedOrders, signedOrder => signedOrder.exchangeContractAddress); - assert.hasAtMostOneUniqueValue( - exchangeContractAddresses, - ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, - ); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); + assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); + assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); const normalizedTakerAddress = takerAddress.toLowerCase(); - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - let filledTakerTokenAmount = new BigNumber(0); - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - for (const signedOrder of signedOrders) { - const singleFilledTakerTokenAmount = await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount.minus(filledTakerTokenAmount), - normalizedTakerAddress, - zrxTokenAddress, - ); - filledTakerTokenAmount = filledTakerTokenAmount.plus(singleFilledTakerTokenAmount); - if (filledTakerTokenAmount.eq(fillTakerTokenAmount)) { - break; - } - } - } - - if (_.isEmpty(signedOrders)) { - throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - } + const exchangeInstance = await this._getExchangeContractAsync(); - const orderAddressesValuesAndSignatureArray = _.map(signedOrders, signedOrder => { - return [ - ...formatters.getOrderAddressesAndValues(signedOrder), - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, - ]; - }); - // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'( - const [orderAddressesArray, orderValuesArray, vArray, rArray, sArray] = _.unzip<any>( - orderAddressesValuesAndSignatureArray, + const txHash = await exchangeInstance.fillOrderNoThrow.sendTransactionAsync( + signedOrder, + takerAssetFillAmount, + signedOrder.signature, + { + from: normalizedTakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }, ); + return txHash; + } + /** + * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled, + * the fill order is abandoned. + * @param signedOrder An object that conforms to the SignedOrder interface. + * @param takerAssetFillAmount The amount of the order (in taker asset baseUnits) that you wish to fill. + * @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async fillOrKillOrderAsync( + signedOrder: SignedOrder, + takerAssetFillAmount: BigNumber, + takerAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); + assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount); + await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedTakerAddress = takerAddress.toLowerCase(); const exchangeInstance = await this._getExchangeContractAsync(); - const txHash = await exchangeInstance.fillOrdersUpTo.sendTransactionAsync( - orderAddressesArray, - orderValuesArray, - fillTakerTokenAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - vArray, - rArray, - sArray, + + const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync( + signedOrder, + takerAssetFillAmount, + signedOrder.signature, { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, @@ -315,91 +239,79 @@ export class ExchangeWrapper extends ContractWrapper { return txHash; } /** - * Batch version of fillOrderAsync. - * Executes multiple fills atomically in a single transaction. - * If shouldThrowOnInsufficientBalanceOrAllowance is set to false, it will continue filling subsequent orders even - * when earlier ones fail. - * When shouldThrowOnInsufficientBalanceOrAllowance is set to true, if any fill fails, the entire batch fails. - * @param orderFillRequests An array of objects that conform to the - * OrderFillRequest interface. - * @param shouldThrowOnInsufficientBalanceOrAllowance Whether or not you wish for the contract call to throw - * if upon execution any of the tokens cannot be - * transferred. If set to false, the call will continue to - * fill subsequent signedOrders even when some - * cannot be filled. - * @param takerAddress The user Ethereum address who would like to fill - * these orders. Must be available via the supplied - * Provider passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. + * Executes a 0x transaction. Transaction messages exist for the purpose of calling methods on the Exchange contract + * in the context of another address (see [ZEIP18](https://github.com/0xProject/ZEIPs/issues/18)). + * This is especially useful for implementing filter contracts. + * @param salt Salt + * @param signerAddress Signer address + * @param data Transaction data + * @param signature Signature + * @param senderAddress Sender address + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async executeTransactionAsync( + salt: BigNumber, + signerAddress: string, + data: string, + signature: string, + senderAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.isBigNumber('salt', salt); + assert.isETHAddressHex('signerAddress', signerAddress); + assert.isHexString('data', data); + assert.isHexString('signature', signature); + await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedSenderAddress = senderAddress.toLowerCase(); + + const exchangeInstance = await this._getExchangeContractAsync(); + + const txHash = await exchangeInstance.executeTransaction.sendTransactionAsync( + salt, + signerAddress, + data, + signature, + { + from: normalizedSenderAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }, + ); + return txHash; + } + /** + * Batch version of fillOrderAsync. Executes multiple fills atomically in a single transaction. + * @param signedOrders An array of signed orders to fill. + * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. + * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. * @return Transaction hash. */ @decorators.asyncZeroExErrorHandler public async batchFillOrdersAsync( - orderFillRequests: OrderFillRequest[], - shouldThrowOnInsufficientBalanceOrAllowance: boolean, + signedOrders: SignedOrder[], + takerAssetFillAmounts: BigNumber[], takerAddress: string, orderTransactionOpts: OrderTransactionOpts = {}, ): Promise<string> { - assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); - const exchangeContractAddresses = _.map( - orderFillRequests, - orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress, - ); - assert.hasAtMostOneUniqueValue( - exchangeContractAddresses, - ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + _.forEach(takerAssetFillAmounts, takerAssetFillAmount => + assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount), ); - assert.isBoolean('shouldThrowOnInsufficientBalanceOrAllowance', shouldThrowOnInsufficientBalanceOrAllowance); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); const normalizedTakerAddress = takerAddress.toLowerCase(); - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - for (const orderFillRequest of orderFillRequests) { - await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - orderFillRequest.signedOrder, - orderFillRequest.takerTokenFillAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - } - if (_.isEmpty(orderFillRequests)) { - throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - } - - const orderAddressesValuesAmountsAndSignatureArray = _.map(orderFillRequests, orderFillRequest => { - return [ - ...formatters.getOrderAddressesAndValues(orderFillRequest.signedOrder), - orderFillRequest.takerTokenFillAmount, - orderFillRequest.signedOrder.ecSignature.v, - orderFillRequest.signedOrder.ecSignature.r, - orderFillRequest.signedOrder.ecSignature.s, - ]; - }); - // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'( - const [orderAddressesArray, orderValuesArray, fillTakerTokenAmounts, vArray, rArray, sArray] = _.unzip<any>( - orderAddressesValuesAmountsAndSignatureArray, - ); const exchangeInstance = await this._getExchangeContractAsync(); + const signatures = _.map(signedOrders, signedOrder => signedOrder.signature); const txHash = await exchangeInstance.batchFillOrders.sendTransactionAsync( - orderAddressesArray, - orderValuesArray, - fillTakerTokenAmounts, - shouldThrowOnInsufficientBalanceOrAllowance, - vArray, - rArray, - sArray, + signedOrders, + takerAssetFillAmounts, + signatures, { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, @@ -409,58 +321,105 @@ export class ExchangeWrapper extends ContractWrapper { return txHash; } /** - * Attempts to fill a specific amount of an order. If the entire amount specified cannot be filled, - * the fill order is abandoned. - * @param signedOrder An object that conforms to the SignedOrder interface. The - * signedOrder you wish to fill. - * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. - * @param takerAddress The user Ethereum address who would like to fill this order. - * Must be available via the supplied Provider passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. + * Synchronously executes multiple calls to fillOrder until total amount of makerAsset is bought by taker. + * @param signedOrders An array of signed orders to fill. + * @param makerAssetFillAmount Maker asset fill amount. + * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. * @return Transaction hash. */ @decorators.asyncZeroExErrorHandler - public async fillOrKillOrderAsync( - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, + public async marketBuyOrdersAsync( + signedOrders: SignedOrder[], + makerAssetFillAmount: BigNumber, takerAddress: string, orderTransactionOpts: OrderTransactionOpts = {}, ): Promise<string> { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.isBigNumber('makerAssetFillAmount', makerAssetFillAmount); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); const normalizedTakerAddress = takerAddress.toLowerCase(); const exchangeInstance = await this._getExchangeContractAsync(); + const signatures = _.map(signedOrders, signedOrder => signedOrder.signature); + const txHash = await exchangeInstance.marketBuyOrders.sendTransactionAsync( + signedOrders, + makerAssetFillAmount, + signatures, + { + from: normalizedTakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }, + ); + return txHash; + } + /** + * Synchronously executes multiple calls to fillOrder until total amount of makerAsset is bought by taker. + * @param signedOrders An array of signed orders to fill. + * @param takerAssetFillAmount Taker asset fill amount. + * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async marketSellOrdersAsync( + signedOrders: SignedOrder[], + takerAssetFillAmount: BigNumber, + takerAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount); + await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedTakerAddress = takerAddress.toLowerCase(); - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } + const exchangeInstance = await this._getExchangeContractAsync(); + const signatures = _.map(signedOrders, signedOrder => signedOrder.signature); + const txHash = await exchangeInstance.marketSellOrders.sendTransactionAsync( + signedOrders, + takerAssetFillAmount, + signatures, + { + from: normalizedTakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }, + ); + return txHash; + } + /** + * No throw version of marketBuyOrdersAsync + * @param signedOrders An array of signed orders to fill. + * @param makerAssetFillAmount Maker asset fill amount. + * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async marketBuyOrdersNoThrowAsync( + signedOrders: SignedOrder[], + makerAssetFillAmount: BigNumber, + takerAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.isBigNumber('makerAssetFillAmount', makerAssetFillAmount); + await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedTakerAddress = takerAddress.toLowerCase(); - const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(signedOrder); - const txHash = await exchangeInstance.fillOrKillOrder.sendTransactionAsync( - orderAddresses, - orderValues, - fillTakerTokenAmount, - signedOrder.ecSignature.v, - signedOrder.ecSignature.r, - signedOrder.ecSignature.s, + const exchangeInstance = await this._getExchangeContractAsync(); + const signatures = _.map(signedOrders, signedOrder => signedOrder.signature); + const txHash = await exchangeInstance.marketBuyOrdersNoThrow.sendTransactionAsync( + signedOrders, + makerAssetFillAmount, + signatures, { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, @@ -470,79 +429,71 @@ export class ExchangeWrapper extends ContractWrapper { return txHash; } /** - * Batch version of fillOrKill. Allows a taker to specify a batch of orders that will either be atomically - * filled (each to the specified fillAmount) or aborted. - * @param orderFillRequests An array of objects that conform to the OrderFillRequest interface. - * @param takerAddress The user Ethereum address who would like to fill there orders. - * Must be available via the supplied Provider passed to 0x.js. - * @param orderTransactionOpts Optional arguments this method accepts. + * No throw version of marketSellOrdersAsync + * @param signedOrders An array of signed orders to fill. + * @param takerAssetFillAmount Taker asset fill amount. + * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. * @return Transaction hash. */ @decorators.asyncZeroExErrorHandler - public async batchFillOrKillAsync( - orderFillRequests: OrderFillRequest[], + public async marketSellOrdersNoThrowAsync( + signedOrders: SignedOrder[], + takerAssetFillAmount: BigNumber, takerAddress: string, orderTransactionOpts: OrderTransactionOpts = {}, ): Promise<string> { - assert.doesConformToSchema('orderFillRequests', orderFillRequests, schemas.orderFillRequestsSchema); - const exchangeContractAddresses = _.map( - orderFillRequests, - orderFillRequest => orderFillRequest.signedOrder.exchangeContractAddress, + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount); + await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedTakerAddress = takerAddress.toLowerCase(); + + const exchangeInstance = await this._getExchangeContractAsync(); + const signatures = _.map(signedOrders, signedOrder => signedOrder.signature); + const txHash = await exchangeInstance.marketSellOrdersNoThrow.sendTransactionAsync( + signedOrders, + takerAssetFillAmount, + signatures, + { + from: normalizedTakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }, ); - assert.hasAtMostOneUniqueValue( - exchangeContractAddresses, - ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, + return txHash; + } + /** + * No throw version of batchFillOrdersAsync + * @param signedOrders An array of signed orders to fill. + * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. + * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async batchFillOrdersNoThrowAsync( + signedOrders: SignedOrder[], + takerAssetFillAmounts: BigNumber[], + takerAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + _.forEach(takerAssetFillAmounts, takerAssetFillAmount => + assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount), ); await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); const normalizedTakerAddress = takerAddress.toLowerCase(); - if (_.isEmpty(orderFillRequests)) { - throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - } - const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - for (const orderFillRequest of orderFillRequests) { - await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - orderFillRequest.signedOrder, - orderFillRequest.takerTokenFillAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - } - - const orderAddressesValuesAndTakerTokenFillAmounts = _.map(orderFillRequests, request => { - return [ - ...formatters.getOrderAddressesAndValues(request.signedOrder), - request.takerTokenFillAmount, - request.signedOrder.ecSignature.v, - request.signedOrder.ecSignature.r, - request.signedOrder.ecSignature.s, - ]; - }); - - // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'( - const [orderAddresses, orderValues, fillTakerTokenAmounts, vParams, rParams, sParams] = _.unzip<any>( - orderAddressesValuesAndTakerTokenFillAmounts, - ); - const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync( - orderAddresses, - orderValues, - fillTakerTokenAmounts, - vParams, - rParams, - sParams, + const exchangeInstance = await this._getExchangeContractAsync(); + const signatures = _.map(signedOrders, signedOrder => signedOrder.signature); + const txHash = await exchangeInstance.batchFillOrdersNoThrow.sendTransactionAsync( + signedOrders, + takerAssetFillAmounts, + signatures, { from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, @@ -552,46 +503,37 @@ export class ExchangeWrapper extends ContractWrapper { return txHash; } /** - * Cancel a given fill amount of an order. Cancellations are cumulative. - * @param order An object that conforms to the Order or SignedOrder interface. - * The order you would like to cancel. - * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel. - * @param transactionOpts Optional arguments this method accepts. + * Batch version of fillOrKillOrderAsync. Executes multiple fills atomically in a single transaction. + * @param signedOrders An array of signed orders to fill. + * @param takerAssetFillAmounts The amounts of the orders (in taker asset baseUnits) that you wish to fill. + * @param takerAddress The user Ethereum address who would like to fill these orders. Must be available via the supplied + * Provider provided at instantiation. + * @param orderTransactionOpts Optional arguments this method accepts. * @return Transaction hash. */ @decorators.asyncZeroExErrorHandler - public async cancelOrderAsync( - order: Order | SignedOrder, - cancelTakerTokenAmount: BigNumber, + public async batchFillOrKillOrdersAsync( + signedOrders: SignedOrder[], + takerAssetFillAmounts: BigNumber[], + takerAddress: string, orderTransactionOpts: OrderTransactionOpts = {}, ): Promise<string> { - assert.doesConformToSchema('order', order, schemas.orderSchema); - assert.isValidBaseUnitAmount('takerTokenCancelAmount', cancelTakerTokenAmount); - await assert.isSenderAddressAsync('order.maker', order.maker, this._web3Wrapper); - const normalizedMakerAddress = order.maker.toLowerCase(); + assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema); + _.forEach(takerAssetFillAmounts, takerAssetFillAmount => + assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount), + ); + await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedTakerAddress = takerAddress.toLowerCase(); const exchangeInstance = await this._getExchangeContractAsync(); - - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - const orderHash = getOrderHashHex(order); - const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); - OrderValidationUtils.validateCancelOrderThrowIfInvalid( - order, - cancelTakerTokenAmount, - unavailableTakerTokenAmount, - ); - } - - const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(order); - const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync( - orderAddresses, - orderValues, - cancelTakerTokenAmount, + const signatures = _.map(signedOrders, signedOrder => signedOrder.signature); + const txHash = await exchangeInstance.batchFillOrKillOrders.sendTransactionAsync( + signedOrders, + takerAssetFillAmounts, + signatures, { - from: normalizedMakerAddress, + from: normalizedTakerAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, }, @@ -599,71 +541,276 @@ export class ExchangeWrapper extends ContractWrapper { return txHash; } /** - * Batch version of cancelOrderAsync. Atomically cancels multiple orders in a single transaction. - * All orders must be from the same maker. - * @param orderCancellationRequests An array of objects that conform to the OrderCancellationRequest - * interface. - * @param transactionOpts Optional arguments this method accepts. + * Batch version of cancelOrderAsync. Executes multiple cancels atomically in a single transaction. + * @param orders An array of orders to cancel. + * @param orderTransactionOpts Optional arguments this method accepts. * @return Transaction hash. */ @decorators.asyncZeroExErrorHandler public async batchCancelOrdersAsync( - orderCancellationRequests: OrderCancellationRequest[], + orders: Array<Order | SignedOrder>, orderTransactionOpts: OrderTransactionOpts = {}, ): Promise<string> { - assert.doesConformToSchema( - 'orderCancellationRequests', - orderCancellationRequests, - schemas.orderCancellationRequestsSchema, + assert.doesConformToSchema('orders', orders, schemas.ordersSchema); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const makerAddresses = _.map(orders, order => order.makerAddress); + const makerAddress = makerAddresses[0]; + await assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper); + const normalizedMakerAddress = makerAddress.toLowerCase(); + + const exchangeInstance = await this._getExchangeContractAsync(); + const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync(orders, { + from: normalizedMakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }); + return txHash; + } + /** + * Match two complementary orders that have a profitable spread. + * Each order is filled at their respective price point. However, the calculations are carried out as though + * the orders are both being filled at the right order's price point. + * The profit made by the left order goes to the taker (whoever matched the two orders). + * @param leftSignedOrder First order to match. + * @param rightSignedOrder Second order to match. + * @param takerAddress The address that sends the transaction and gets the spread. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async matchOrdersAsync( + leftSignedOrder: SignedOrder, + rightSignedOrder: SignedOrder, + takerAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.doesConformToSchema('leftSignedOrder', leftSignedOrder, schemas.signedOrderSchema); + assert.doesConformToSchema('rightSignedOrder', rightSignedOrder, schemas.signedOrderSchema); + await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedTakerAddress = takerAddress.toLowerCase(); + // TODO(logvinov): Check that: + // rightOrder.makerAssetData === leftOrder.takerAssetData; + // rightOrder.takerAssetData === leftOrder.makerAssetData; + const exchangeInstance = await this._getExchangeContractAsync(); + const txHash = await exchangeInstance.matchOrders.sendTransactionAsync( + leftSignedOrder, + rightSignedOrder, + leftSignedOrder.signature, + rightSignedOrder.signature, + { + from: normalizedTakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }, ); - const exchangeContractAddresses = _.map( - orderCancellationRequests, - orderCancellationRequest => orderCancellationRequest.order.exchangeContractAddress, + return txHash; + } + /** + * Approves a hash on-chain using any valid signature type. + * After presigning a hash, the preSign signature type will become valid for that hash and signer. + * @param hash Hash to pre-sign + * @param signerAddress Address that should have signed the given hash. + * @param signature Proof that the hash has been signed by signer. + * @param senderAddress Address that should send the transaction. + * @returns Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async preSignAsync( + hash: string, + signerAddress: string, + signature: string, + senderAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.isHexString('hash', hash); + assert.isETHAddressHex('signerAddress', signerAddress); + assert.isHexString('signature', signature); + await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedTakerAddress = senderAddress.toLowerCase(); + const exchangeInstance = await this._getExchangeContractAsync(); + const txHash = await exchangeInstance.preSign.sendTransactionAsync(hash, signerAddress, signature, { + from: normalizedTakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }); + return txHash; + } + /** + * Checks if the signature is valid. + * @param hash Hash to pre-sign + * @param signerAddress Address that should have signed the given hash. + * @param signature Proof that the hash has been signed by signer. + * @param methodOpts Optional arguments this method accepts. + * @returns If the signature is valid + */ + @decorators.asyncZeroExErrorHandler + public async isValidSignatureAsync( + hash: string, + signerAddress: string, + signature: string, + methodOpts: MethodOpts = {}, + ): Promise<boolean> { + assert.isHexString('hash', hash); + assert.isETHAddressHex('signerAddress', signerAddress); + assert.isHexString('signature', signature); + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + const exchangeInstance = await this._getExchangeContractAsync(); + const txData = {}; + const isValidSignature = await exchangeInstance.isValidSignature.callAsync( + hash, + signerAddress, + signature, + txData, + methodOpts.defaultBlock, ); - assert.hasAtMostOneUniqueValue( - exchangeContractAddresses, - ExchangeContractErrs.BatchOrdersMustHaveSameExchangeAddress, + return isValidSignature; + } + /** + * Checks if the validator is allowed by the signer. + * @param validatorAddress Address of a validator + * @param signerAddress Address of a signer + * @param methodOpts Optional arguments this method accepts. + * @returns If the validator is allowed + */ + @decorators.asyncZeroExErrorHandler + public async isAllowedValidatorAsync( + signerAddress: string, + validatorAddress: string, + methodOpts: MethodOpts = {}, + ): Promise<boolean> { + assert.isETHAddressHex('signerAddress', signerAddress); + assert.isETHAddressHex('validatorAddress', validatorAddress); + if (!_.isUndefined(methodOpts)) { + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + } + const normalizedSignerAddress = signerAddress.toLowerCase(); + const normalizedValidatorAddress = validatorAddress.toLowerCase(); + const exchangeInstance = await this._getExchangeContractAsync(); + const txData = {}; + const isValidSignature = await exchangeInstance.allowedValidators.callAsync( + normalizedSignerAddress, + normalizedValidatorAddress, + txData, + methodOpts.defaultBlock, ); - const makers = _.map(orderCancellationRequests, cancellationRequest => cancellationRequest.order.maker); - assert.hasAtMostOneUniqueValue(makers, ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); - const maker = makers[0]; - await assert.isSenderAddressAsync('maker', maker, this._web3Wrapper); - const normalizedMakerAddress = maker.toLowerCase(); + return isValidSignature; + } + /** + * Check whether the hash is pre-signed on-chain. + * @param hash Hash to check if pre-signed + * @param signerAddress Address that should have signed the given hash. + * @param methodOpts Optional arguments this method accepts. + * @returns Whether the hash is pre-signed. + */ + @decorators.asyncZeroExErrorHandler + public async isPreSignedAsync(hash: string, signerAddress: string, methodOpts: MethodOpts = {}): Promise<boolean> { + assert.isHexString('hash', hash); + assert.isETHAddressHex('signerAddress', signerAddress); + if (!_.isUndefined(methodOpts)) { + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); + } + const exchangeInstance = await this._getExchangeContractAsync(); - const shouldValidate = _.isUndefined(orderTransactionOpts.shouldValidate) - ? SHOULD_VALIDATE_BY_DEFAULT - : orderTransactionOpts.shouldValidate; - if (shouldValidate) { - for (const orderCancellationRequest of orderCancellationRequests) { - const orderHash = getOrderHashHex(orderCancellationRequest.order); - const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); - OrderValidationUtils.validateCancelOrderThrowIfInvalid( - orderCancellationRequest.order, - orderCancellationRequest.takerTokenCancelAmount, - unavailableTakerTokenAmount, - ); - } + const txData = {}; + const isPreSigned = await exchangeInstance.preSigned.callAsync( + hash, + signerAddress, + txData, + methodOpts.defaultBlock, + ); + return isPreSigned; + } + /** + * Checks if transaction is already executed. + * @param transactionHash Transaction hash to check + * @param signerAddress Address that should have signed the given hash. + * @param methodOpts Optional arguments this method accepts. + * @returns If transaction is already executed. + */ + @decorators.asyncZeroExErrorHandler + public async isTransactionExecutedAsync(transactionHash: string, methodOpts: MethodOpts = {}): Promise<boolean> { + assert.isHexString('transactionHash', transactionHash); + if (!_.isUndefined(methodOpts)) { + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); } - if (_.isEmpty(orderCancellationRequests)) { - throw new Error(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); + const exchangeInstance = await this._getExchangeContractAsync(); + const txData = {}; + const isExecuted = await exchangeInstance.transactions.callAsync( + transactionHash, + txData, + methodOpts.defaultBlock, + ); + return isExecuted; + } + /** + * Get order info + * @param order Order + * @param methodOpts Optional arguments this method accepts. + * @returns Order info + */ + @decorators.asyncZeroExErrorHandler + public async getOrderInfoAsync(order: Order | SignedOrder, methodOpts: MethodOpts = {}): Promise<OrderInfo> { + if (!_.isUndefined(methodOpts)) { + assert.doesConformToSchema('methodOpts', methodOpts, methodOptsSchema); } const exchangeInstance = await this._getExchangeContractAsync(); - const orderAddressesValuesAndTakerTokenCancelAmounts = _.map(orderCancellationRequests, cancellationRequest => { - return [ - ...formatters.getOrderAddressesAndValues(cancellationRequest.order), - cancellationRequest.takerTokenCancelAmount, - ]; + + const txData = {}; + const orderInfo = await exchangeInstance.getOrderInfo.callAsync(order, txData, methodOpts.defaultBlock); + return orderInfo; + } + /** + * Cancel a given order. + * @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel. + * @param transactionOpts Optional arguments this method accepts. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async cancelOrderAsync( + order: Order | SignedOrder, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.doesConformToSchema('order', order, schemas.orderSchema); + await assert.isSenderAddressAsync('order.maker', order.makerAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedMakerAddress = order.makerAddress.toLowerCase(); + + const exchangeInstance = await this._getExchangeContractAsync(); + const txHash = await exchangeInstance.cancelOrder.sendTransactionAsync(order, { + from: normalizedMakerAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, }); - // We use _.unzip<any> because _.unzip doesn't type check if values have different types :'( - const [orderAddresses, orderValues, cancelTakerTokenAmounts] = _.unzip<any>( - orderAddressesValuesAndTakerTokenCancelAmounts, - ); - const txHash = await exchangeInstance.batchCancelOrders.sendTransactionAsync( - orderAddresses, - orderValues, - cancelTakerTokenAmounts, + return txHash; + } + /** + * Sets the signature validator approval + * @param validatorAddress Validator contract address. + * @param isApproved Boolean value to set approval to. + * @param senderAddress Sender address. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async setSignatureValidatorApprovalAsync( + validatorAddress: string, + isApproved: boolean, + senderAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.isETHAddressHex('validatorAddress', validatorAddress); + assert.isBoolean('isApproved', isApproved); + await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedSenderAddress = senderAddress.toLowerCase(); + + const exchangeInstance = await this._getExchangeContractAsync(); + const txHash = await exchangeInstance.setSignatureValidatorApproval.sendTransactionAsync( + validatorAddress, + isApproved, { - from: normalizedMakerAddress, + from: normalizedSenderAddress, gas: orderTransactionOpts.gasLimit, gasPrice: orderTransactionOpts.gasPrice, }, @@ -671,6 +818,33 @@ export class ExchangeWrapper extends ContractWrapper { return txHash; } /** + * Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch + * and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress). + * @param targetOrderEpoch Target order epoch. + * @param senderAddress Address that should send the transaction. + * @param orderTransactionOpts Optional arguments this method accepts. + * @return Transaction hash. + */ + @decorators.asyncZeroExErrorHandler + public async cancelOrdersUpToAsync( + targetOrderEpoch: BigNumber, + senderAddress: string, + orderTransactionOpts: OrderTransactionOpts = {}, + ): Promise<string> { + assert.isBigNumber('targetOrderEpoch', targetOrderEpoch); + await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper); + assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]); + const normalizedSenderAddress = senderAddress.toLowerCase(); + + const exchangeInstance = await this._getExchangeContractAsync(); + const txHash = await exchangeInstance.cancelOrdersUpTo.sendTransactionAsync(targetOrderEpoch, { + from: normalizedSenderAddress, + gas: orderTransactionOpts.gasLimit, + gasPrice: orderTransactionOpts.gasPrice, + }); + return txHash; + } + /** * Subscribe to an event type emitted by the Exchange contract. * @param eventName The exchange contract event you would like to subscribe to. * @param indexFilterValues An object where the keys are indexed args returned by the event and @@ -678,7 +852,7 @@ export class ExchangeWrapper extends ContractWrapper { * @param callback Callback that gets called when a log is added/removed * @return Subscription token used later to unsubscribe */ - public subscribe<ArgsType extends ExchangeContractEventArgs>( + public subscribe<ArgsType extends ExchangeEventArgs>( eventName: ExchangeEvents, indexFilterValues: IndexedFilterValues, callback: EventCallback<ArgsType>, @@ -691,7 +865,7 @@ export class ExchangeWrapper extends ContractWrapper { exchangeContractAddress, eventName, indexFilterValues, - artifacts.Exchange.abi, + artifacts.Exchange.compilerOutput.abi, callback, ); return subscriptionToken; @@ -717,7 +891,7 @@ export class ExchangeWrapper extends ContractWrapper { * the value is the value you are interested in. E.g `{_from: aUserAddressHex}` * @return Array of logs that match the parameters */ - public async getLogsAsync<ArgsType extends ExchangeContractEventArgs>( + public async getLogsAsync<ArgsType extends ExchangeEventArgs>( eventName: ExchangeEvents, blockRange: BlockRange, indexFilterValues: IndexedFilterValues, @@ -731,7 +905,7 @@ export class ExchangeWrapper extends ContractWrapper { eventName, blockRange, indexFilterValues, - artifacts.Exchange.abi, + artifacts.Exchange.compilerOutput.abi, ); return logs; } @@ -745,228 +919,27 @@ export class ExchangeWrapper extends ContractWrapper { return contractAddress; } /** - * Checks if order is still fillable and throws an error otherwise. Useful for orderbook - * pruning where you want to remove stale orders without knowing who the taker will be. - * @param signedOrder An object that conforms to the SignedOrder interface. The - * signedOrder you wish to validate. - * @param opts An object that conforms to the ValidateOrderFillableOpts - * interface. Allows specifying a specific fillTakerTokenAmount - * to validate for. - */ - public async validateOrderFillableOrThrowAsync( - signedOrder: SignedOrder, - opts?: ValidateOrderFillableOpts, - ): Promise<void> { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - const zrxTokenAddress = this.getZRXTokenAddress(); - const expectedFillTakerTokenAmount = !_.isUndefined(opts) ? opts.expectedFillTakerTokenAmount : undefined; - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateOrderFillableOrThrowAsync( - exchangeTradeEmulator, - signedOrder, - zrxTokenAddress, - expectedFillTakerTokenAmount, - ); - } - /** - * Checks if order fill will succeed and throws an error otherwise. - * @param signedOrder An object that conforms to the SignedOrder interface. The - * signedOrder you wish to fill. - * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. - * @param takerAddress The user Ethereum address who would like to fill this order. - * Must be available via the supplied Provider passed to 0x.js. - */ - public async validateFillOrderThrowIfInvalidAsync( - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - ): Promise<void> { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - /** - * Checks if cancelling a given order will succeed and throws an informative error if it won't. - * @param order An object that conforms to the Order or SignedOrder interface. - * The order you would like to cancel. - * @param cancelTakerTokenAmount The amount (specified in taker tokens) that you would like to cancel. - */ - public async validateCancelOrderThrowIfInvalidAsync( - order: Order, - cancelTakerTokenAmount: BigNumber, - ): Promise<void> { - assert.doesConformToSchema('order', order, schemas.orderSchema); - assert.isValidBaseUnitAmount('cancelTakerTokenAmount', cancelTakerTokenAmount); - const orderHash = getOrderHashHex(order); - const unavailableTakerTokenAmount = await this.getUnavailableTakerAmountAsync(orderHash); - OrderValidationUtils.validateCancelOrderThrowIfInvalid( - order, - cancelTakerTokenAmount, - unavailableTakerTokenAmount, - ); - } - /** - * Checks if calling fillOrKill on a given order will succeed and throws an informative error if it won't. - * @param signedOrder An object that conforms to the SignedOrder interface. The - * signedOrder you wish to fill. - * @param fillTakerTokenAmount The total amount of the takerTokens you would like to fill. - * @param takerAddress The user Ethereum address who would like to fill this order. - * Must be available via the supplied Provider passed to 0x.js. - */ - public async validateFillOrKillOrderThrowIfInvalidAsync( - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - ): Promise<void> { - assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema); - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper); - const normalizedTakerAddress = takerAddress.toLowerCase(); - const zrxTokenAddress = this.getZRXTokenAddress(); - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - this._tokenWrapper, - BlockParamLiteral.Latest, - ); - const exchangeTradeEmulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - const orderValidationUtils = await this._getOrderValidationUtilsAsync(); - await orderValidationUtils.validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - normalizedTakerAddress, - zrxTokenAddress, - ); - } - /** - * Checks if rounding error will be > 0.1% when computing makerTokenAmount by doing: - * `(fillTakerTokenAmount * makerTokenAmount) / takerTokenAmount`. - * 0x Protocol does not accept any trades that result in large rounding errors. This means that tokens with few or - * no decimals can only be filled in quantities and ratios that avoid large rounding errors. - * @param fillTakerTokenAmount The amount of the order (in taker tokens baseUnits) that you wish to fill. - * @param takerTokenAmount The order size on the taker side - * @param makerTokenAmount The order size on the maker side - */ - public async isRoundingErrorAsync( - fillTakerTokenAmount: BigNumber, - takerTokenAmount: BigNumber, - makerTokenAmount: BigNumber, - ): Promise<boolean> { - assert.isValidBaseUnitAmount('fillTakerTokenAmount', fillTakerTokenAmount); - assert.isValidBaseUnitAmount('takerTokenAmount', takerTokenAmount); - assert.isValidBaseUnitAmount('makerTokenAmount', makerTokenAmount); - const exchangeInstance = await this._getExchangeContractAsync(); - const isRoundingError = await exchangeInstance.isRoundingError.callAsync( - fillTakerTokenAmount, - takerTokenAmount, - makerTokenAmount, - ); - return isRoundingError; - } - /** - * Checks if logs contain LogError, which is emitted by Exchange contract on transaction failure. - * @param logs Transaction logs as returned by `zeroEx.awaitTransactionMinedAsync` - */ - public throwLogErrorsAsErrors(logs: Array<LogWithDecodedArgs<DecodedLogArgs> | LogEntry>): void { - const errLog = _.find(logs, { - event: ExchangeEvents.LogError, - }); - if (!_.isUndefined(errLog)) { - const logArgs = (errLog as LogWithDecodedArgs<LogErrorContractEventArgs>).args; - const errCode = logArgs.errorId; - const errMessage = this._exchangeContractErrCodesToMsg[errCode]; - throw new Error(errMessage); - } - } - /** - * Gets the latest OrderState of a signedOrder - * @param signedOrder The signedOrder - * @param stateLayer Optional, desired blockchain state layer (defaults to latest). - * @return OrderState of the signedOrder - */ - public async getOrderStateAsync( - signedOrder: SignedOrder, - stateLayer: BlockParamLiteral = BlockParamLiteral.Latest, - ): Promise<OrderState> { - const simpleBalanceAndProxyAllowanceFetcher = new SimpleBalanceAndProxyAllowanceFetcher( - this._tokenWrapper, - stateLayer, - ); - const simpleOrderFilledCancelledFetcher = new SimpleOrderFilledCancelledFetcher(this, stateLayer); - const orderStateUtils = new OrderStateUtils( - simpleBalanceAndProxyAllowanceFetcher, - simpleOrderFilledCancelledFetcher, - ); - const orderState = orderStateUtils.getOrderStateAsync(signedOrder); - return orderState; - } - /** * Returns the ZRX token address used by the exchange contract. * @return Address of ZRX token */ public getZRXTokenAddress(): string { - const contractAddress = this._getContractAddress(artifacts.ZRX, this._zrxContractAddressIfExists); + const contractAddress = this._getContractAddress(artifacts.ZRXToken, this._zrxContractAddressIfExists); return contractAddress; } + /** + * Returns the ZRX asset data used by the exchange contract. + * @return ZRX asset data + */ + public async getZRXAssetDataAsync(): Promise<string> { + const exchangeInstance = await this._getExchangeContractAsync(); + const zrxAssetData = exchangeInstance.ZRX_ASSET_DATA.callAsync(); + return zrxAssetData; + } // tslint:disable:no-unused-variable private _invalidateContractInstances(): void { this.unsubscribeAll(); delete this._exchangeContractIfExists; } - private async _isValidSignatureUsingContractCallAsync( - dataHex: string, - ecSignature: ECSignature, - signerAddressHex: string, - ): Promise<boolean> { - assert.isHexString('dataHex', dataHex); - assert.doesConformToSchema('ecSignature', ecSignature, schemas.ecSignatureSchema); - assert.isETHAddressHex('signerAddressHex', signerAddressHex); - const normalizedSignerAddress = signerAddressHex.toLowerCase(); - - const exchangeInstance = await this._getExchangeContractAsync(); - - const isValidSignature = await exchangeInstance.isValidSignature.callAsync( - normalizedSignerAddress, - dataHex, - ecSignature.v, - ecSignature.r, - ecSignature.s, - ); - return isValidSignature; - } - private async _getOrderHashHexUsingContractCallAsync(order: Order | SignedOrder): Promise<string> { - const exchangeInstance = await this._getExchangeContractAsync(); - const [orderAddresses, orderValues] = formatters.getOrderAddressesAndValues(order); - const orderHashHex = await exchangeInstance.getOrderHash.callAsync(orderAddresses, orderValues); - return orderHashHex; - } - private async _getOrderValidationUtilsAsync(): Promise<OrderValidationUtils> { - if (!_.isUndefined(this._orderValidationUtilsIfExists)) { - return this._orderValidationUtilsIfExists; - } - const exchangeContract = await this._getExchangeContractAsync(); - this._orderValidationUtilsIfExists = new OrderValidationUtils(exchangeContract); - return this._orderValidationUtilsIfExists; - } // tslint:enable:no-unused-variable private async _getExchangeContractAsync(): Promise<ExchangeContract> { if (!_.isUndefined(this._exchangeContractIfExists)) { diff --git a/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts deleted file mode 100644 index 7b558ed69..000000000 --- a/packages/contract-wrappers/src/contract_wrappers/token_registry_wrapper.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { ContractAbi, Token } from '@0xproject/types'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import * as _ from 'lodash'; - -import { artifacts } from '../artifacts'; -import { TokenMetadata } from '../types'; -import { assert } from '../utils/assert'; -import { constants } from '../utils/constants'; - -import { ContractWrapper } from './contract_wrapper'; -import { TokenRegistryContract } from './generated/token_registry'; - -/** - * This class includes all the functionality related to interacting with the 0x Token Registry smart contract. - */ -export class TokenRegistryWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.TokenRegistry.abi; - private _tokenRegistryContractIfExists?: TokenRegistryContract; - private _contractAddressIfExists?: string; - private static _createTokenFromMetadata(metadata: TokenMetadata): Token | undefined { - if (metadata[0] === constants.NULL_ADDRESS) { - return undefined; - } - const token = { - address: metadata[0], - name: metadata[1], - symbol: metadata[2], - decimals: metadata[3], - }; - return token; - } - constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) { - super(web3Wrapper, networkId); - this._contractAddressIfExists = contractAddressIfExists; - } - /** - * Retrieves all the tokens currently listed in the Token Registry smart contract - * @return An array of objects that conform to the Token interface. - */ - public async getTokensAsync(): Promise<Token[]> { - const addresses = await this.getTokenAddressesAsync(); - const tokenPromises: Array<Promise<Token | undefined>> = _.map(addresses, async (address: string) => - this.getTokenIfExistsAsync(address), - ); - const tokens = await Promise.all(tokenPromises); - return tokens as Token[]; - } - /** - * Retrieves all the addresses of the tokens currently listed in the Token Registry smart contract - * @return An array of token addresses. - */ - public async getTokenAddressesAsync(): Promise<string[]> { - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const addresses = await tokenRegistryContract.getTokenAddresses.callAsync(); - const lowerCaseAddresses = _.map(addresses, address => address.toLowerCase()); - return lowerCaseAddresses; - } - /** - * Retrieves a token by address currently listed in the Token Registry smart contract - * @return An object that conforms to the Token interface or undefined if token not found. - */ - public async getTokenIfExistsAsync(address: string): Promise<Token | undefined> { - assert.isETHAddressHex('address', address); - const normalizedAddress = address.toLowerCase(); - - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const metadata = await tokenRegistryContract.getTokenMetaData.callAsync(normalizedAddress); - const token = TokenRegistryWrapper._createTokenFromMetadata(metadata); - return token; - } - public async getTokenAddressBySymbolIfExistsAsync(symbol: string): Promise<string | undefined> { - assert.isString('symbol', symbol); - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const addressIfExists = await tokenRegistryContract.getTokenAddressBySymbol.callAsync(symbol); - if (addressIfExists === constants.NULL_ADDRESS) { - return undefined; - } - return addressIfExists; - } - public async getTokenAddressByNameIfExistsAsync(name: string): Promise<string | undefined> { - assert.isString('name', name); - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const addressIfExists = await tokenRegistryContract.getTokenAddressByName.callAsync(name); - if (addressIfExists === constants.NULL_ADDRESS) { - return undefined; - } - return addressIfExists; - } - public async getTokenBySymbolIfExistsAsync(symbol: string): Promise<Token | undefined> { - assert.isString('symbol', symbol); - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const metadata = await tokenRegistryContract.getTokenBySymbol.callAsync(symbol); - const token = TokenRegistryWrapper._createTokenFromMetadata(metadata); - return token; - } - public async getTokenByNameIfExistsAsync(name: string): Promise<Token | undefined> { - assert.isString('name', name); - const tokenRegistryContract = await this._getTokenRegistryContractAsync(); - const metadata = await tokenRegistryContract.getTokenByName.callAsync(name); - const token = TokenRegistryWrapper._createTokenFromMetadata(metadata); - return token; - } - /** - * Retrieves the Ethereum address of the TokenRegistry contract deployed on the network - * that the user-passed web3 provider is connected to. - * @returns The Ethereum address of the TokenRegistry contract being used. - */ - public getContractAddress(): string { - const contractAddress = this._getContractAddress(artifacts.TokenRegistry, this._contractAddressIfExists); - return contractAddress; - } - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstance(): void { - delete this._tokenRegistryContractIfExists; - } - private async _getTokenRegistryContractAsync(): Promise<TokenRegistryContract> { - if (!_.isUndefined(this._tokenRegistryContractIfExists)) { - return this._tokenRegistryContractIfExists; - } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.TokenRegistry, - this._contractAddressIfExists, - ); - const contractInstance = new TokenRegistryContract( - abi, - address, - this._web3Wrapper.getProvider(), - this._web3Wrapper.getContractDefaults(), - ); - this._tokenRegistryContractIfExists = contractInstance; - return this._tokenRegistryContractIfExists; - } -} diff --git a/packages/contract-wrappers/src/contract_wrappers/token_transfer_proxy_wrapper.ts b/packages/contract-wrappers/src/contract_wrappers/token_transfer_proxy_wrapper.ts deleted file mode 100644 index 5194931d7..000000000 --- a/packages/contract-wrappers/src/contract_wrappers/token_transfer_proxy_wrapper.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import { ContractAbi } from '@0xproject/types'; -import * as _ from 'lodash'; - -import { artifacts } from '../artifacts'; -import { assert } from '../utils/assert'; - -import { ContractWrapper } from './contract_wrapper'; -import { TokenTransferProxyContract } from './generated/token_transfer_proxy'; - -/** - * This class includes the functionality related to interacting with the TokenTransferProxy contract. - */ -export class TokenTransferProxyWrapper extends ContractWrapper { - public abi: ContractAbi = artifacts.TokenTransferProxy.abi; - private _tokenTransferProxyContractIfExists?: TokenTransferProxyContract; - private _contractAddressIfExists?: string; - constructor(web3Wrapper: Web3Wrapper, networkId: number, contractAddressIfExists?: string) { - super(web3Wrapper, networkId); - this._contractAddressIfExists = contractAddressIfExists; - } - /** - * Check if the Exchange contract address is authorized by the TokenTransferProxy contract. - * @param exchangeContractAddress The hex encoded address of the Exchange contract to call. - * @return Whether the exchangeContractAddress is authorized. - */ - public async isAuthorizedAsync(exchangeContractAddress: string): Promise<boolean> { - assert.isETHAddressHex('exchangeContractAddress', exchangeContractAddress); - const normalizedExchangeContractAddress = exchangeContractAddress.toLowerCase(); - const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync(); - const isAuthorized = await tokenTransferProxyContractInstance.authorized.callAsync( - normalizedExchangeContractAddress, - ); - return isAuthorized; - } - /** - * Get the list of all Exchange contract addresses authorized by the TokenTransferProxy contract. - * @return The list of authorized addresses. - */ - public async getAuthorizedAddressesAsync(): Promise<string[]> { - const tokenTransferProxyContractInstance = await this._getTokenTransferProxyContractAsync(); - const authorizedAddresses = await tokenTransferProxyContractInstance.getAuthorizedAddresses.callAsync(); - return authorizedAddresses; - } - /** - * Retrieves the Ethereum address of the TokenTransferProxy contract deployed on the network - * that the user-passed web3 provider is connected to. - * @returns The Ethereum address of the TokenTransferProxy contract being used. - */ - public getContractAddress(): string { - const contractAddress = this._getContractAddress(artifacts.TokenTransferProxy, this._contractAddressIfExists); - return contractAddress; - } - // tslint:disable-next-line:no-unused-variable - private _invalidateContractInstance(): void { - delete this._tokenTransferProxyContractIfExists; - } - private async _getTokenTransferProxyContractAsync(): Promise<TokenTransferProxyContract> { - if (!_.isUndefined(this._tokenTransferProxyContractIfExists)) { - return this._tokenTransferProxyContractIfExists; - } - const [abi, address] = await this._getContractAbiAndAddressFromArtifactsAsync( - artifacts.TokenTransferProxy, - this._contractAddressIfExists, - ); - const contractInstance = new TokenTransferProxyContract( - abi, - address, - this._web3Wrapper.getProvider(), - this._web3Wrapper.getContractDefaults(), - ); - this._tokenTransferProxyContractIfExists = contractInstance; - return this._tokenTransferProxyContractIfExists; - } -} diff --git a/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts b/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts deleted file mode 100644 index 5017d879a..000000000 --- a/packages/contract-wrappers/src/fetchers/simple_balance_and_proxy_allowance_fetcher.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { AbstractBalanceAndProxyAllowanceFetcher } from '@0xproject/order-utils'; -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; - -import { TokenWrapper } from '../contract_wrappers/token_wrapper'; - -export class SimpleBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher { - private _tokenWrapper: TokenWrapper; - private _defaultBlock: BlockParamLiteral; - constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) { - this._tokenWrapper = token; - this._defaultBlock = defaultBlock; - } - public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const balance = this._tokenWrapper.getBalanceAsync(tokenAddress, userAddress, methodOpts); - return balance; - } - public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const proxyAllowance = this._tokenWrapper.getProxyAllowanceAsync(tokenAddress, userAddress, methodOpts); - return proxyAllowance; - } -} diff --git a/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts b/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts deleted file mode 100644 index 52d0a100c..000000000 --- a/packages/contract-wrappers/src/fetchers/simple_order_filled_cancelled_fetcher.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; - -import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper'; - -export class SimpleOrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher { - private _exchangeWrapper: ExchangeWrapper; - private _defaultBlock: BlockParamLiteral; - constructor(exchange: ExchangeWrapper, defaultBlock: BlockParamLiteral) { - this._exchangeWrapper = exchange; - this._defaultBlock = defaultBlock; - } - public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const filledTakerAmount = this._exchangeWrapper.getFilledTakerAmountAsync(orderHash, methodOpts); - return filledTakerAmount; - } - public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const cancelledTakerAmount = this._exchangeWrapper.getCancelledTakerAmountAsync(orderHash, methodOpts); - return cancelledTakerAmount; - } - public async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber> { - const unavailableTakerAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); - return unavailableTakerAmount; - } - public getZRXTokenAddress(): string { - const zrxToken = this._exchangeWrapper.getZRXTokenAddress(); - return zrxToken; - } -} diff --git a/packages/contract-wrappers/src/index.ts b/packages/contract-wrappers/src/index.ts index 45af8a707..b3c4b2c60 100644 --- a/packages/contract-wrappers/src/index.ts +++ b/packages/contract-wrappers/src/index.ts @@ -1,9 +1,7 @@ export { ContractWrappers } from './contract_wrappers'; -export { ExchangeWrapper } from './contract_wrappers/exchange_wrapper'; -export { TokenWrapper } from './contract_wrappers/token_wrapper'; -export { TokenRegistryWrapper } from './contract_wrappers/token_registry_wrapper'; +export { ERC20TokenWrapper } from './contract_wrappers/erc20_token_wrapper'; export { EtherTokenWrapper } from './contract_wrappers/ether_token_wrapper'; -export { TokenTransferProxyWrapper } from './contract_wrappers/token_transfer_proxy_wrapper'; +export { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper'; export { ContractWrappersError, @@ -12,7 +10,6 @@ export { Token, IndexedFilterValues, BlockRange, - OrderCancellationRequest, OrderFillRequest, ContractEventArgs, ContractWrappersConfig, @@ -22,47 +19,51 @@ export { LogEvent, DecodedLogEvent, OnOrderStateChangeCallback, + OrderStatus, + OrderInfo, } from './types'; +export { Order, SignedOrder, ECSignature, OrderStateValid, OrderStateInvalid, OrderState } from '@0xproject/types'; + export { BlockParamLiteral, FilterObject, BlockParam, ContractEventArg, - ExchangeContractErrs, LogWithDecodedArgs, - Order, Provider, - SignedOrder, - ECSignature, - OrderStateValid, - OrderStateInvalid, - OrderState, TransactionReceipt, TransactionReceiptWithDecodedLogs, -} from '@0xproject/types'; +} from 'ethereum-types'; + +export { + WETH9Events, + WETH9WithdrawalEventArgs, + WETH9ApprovalEventArgs, + WETH9EventArgs, + WETH9DepositEventArgs, + WETH9TransferEventArgs, +} from './contract_wrappers/generated/weth9'; export { - EtherTokenContractEventArgs, - WithdrawalContractEventArgs, - DepositContractEventArgs, - EtherTokenEvents, -} from './contract_wrappers/generated/ether_token'; + ERC20TokenTransferEventArgs, + ERC20TokenApprovalEventArgs, + ERC20TokenEvents, + ERC20TokenEventArgs, +} from './contract_wrappers/generated/erc20_token'; export { - TransferContractEventArgs, - ApprovalContractEventArgs, - TokenContractEventArgs, - TokenEvents, -} from './contract_wrappers/generated/token'; + ERC721TokenApprovalEventArgs, + ERC721TokenApprovalForAllEventArgs, + ERC721TokenTransferEventArgs, + ERC721TokenEvents, +} from './contract_wrappers/generated/erc721_token'; export { - LogErrorContractEventArgs, - LogCancelContractEventArgs, - LogFillContractEventArgs, - ExchangeContractEventArgs, + ExchangeCancelUpToEventArgs, + ExchangeAssetProxyRegisteredEventArgs, + ExchangeFillEventArgs, + ExchangeCancelEventArgs, + ExchangeEventArgs, ExchangeEvents, } from './contract_wrappers/generated/exchange'; - -export { BalanceAndProxyAllowanceLazyStore } from './stores/balance_proxy_allowance_lazy_store'; -export { OrderFilledCancelledLazyStore } from './stores/order_filled_cancelled_lazy_store'; diff --git a/packages/contract-wrappers/src/schemas/contract_wrappers_private_network_config_schema.ts b/packages/contract-wrappers/src/schemas/contract_wrappers_private_network_config_schema.ts index 759078feb..4b68daf1a 100644 --- a/packages/contract-wrappers/src/schemas/contract_wrappers_private_network_config_schema.ts +++ b/packages/contract-wrappers/src/schemas/contract_wrappers_private_network_config_schema.ts @@ -10,6 +10,7 @@ export const contractWrappersPrivateNetworkConfigSchema = { exchangeContractAddress: { $ref: '/Address' }, tokenRegistryContractAddress: { $ref: '/Address' }, tokenTransferProxyContractAddress: { $ref: '/Address' }, + blockPollingIntervalMs: { type: 'number' }, orderWatcherConfig: { type: 'object', properties: { diff --git a/packages/contract-wrappers/src/schemas/contract_wrappers_public_network_config_schema.ts b/packages/contract-wrappers/src/schemas/contract_wrappers_public_network_config_schema.ts index 304cd100b..988872376 100644 --- a/packages/contract-wrappers/src/schemas/contract_wrappers_public_network_config_schema.ts +++ b/packages/contract-wrappers/src/schemas/contract_wrappers_public_network_config_schema.ts @@ -24,6 +24,7 @@ export const contractWrappersPublicNetworkConfigSchema = { exchangeContractAddress: { $ref: '/Address' }, tokenRegistryContractAddress: { $ref: '/Address' }, tokenTransferProxyContractAddress: { $ref: '/Address' }, + blockPollingIntervalMs: { type: 'number' }, orderWatcherConfig: { type: 'object', properties: { diff --git a/packages/contract-wrappers/src/schemas/method_opts_schema.ts b/packages/contract-wrappers/src/schemas/method_opts_schema.ts new file mode 100644 index 000000000..ef434070a --- /dev/null +++ b/packages/contract-wrappers/src/schemas/method_opts_schema.ts @@ -0,0 +1,7 @@ +export const methodOptsSchema = { + id: '/MethodOpts', + properties: { + defaultBlock: { $ref: '/BlockParam' }, + }, + type: 'object', +}; diff --git a/packages/contract-wrappers/src/schemas/order_tx_opts_schema.ts b/packages/contract-wrappers/src/schemas/order_tx_opts_schema.ts new file mode 100644 index 000000000..31ad759d5 --- /dev/null +++ b/packages/contract-wrappers/src/schemas/order_tx_opts_schema.ts @@ -0,0 +1,8 @@ +export const orderTxOptsSchema = { + id: '/OrderTxOpts', + allOf: [{ $ref: '/TxOpts' }], + properties: { + shouldValidate: { type: 'boolean' }, + }, + type: 'object', +}; diff --git a/packages/contract-wrappers/src/schemas/tx_opts_schema.ts b/packages/contract-wrappers/src/schemas/tx_opts_schema.ts new file mode 100644 index 000000000..bddc33b6c --- /dev/null +++ b/packages/contract-wrappers/src/schemas/tx_opts_schema.ts @@ -0,0 +1,8 @@ +export const txOptsSchema = { + id: '/TxOpts', + properties: { + gasPrice: { $ref: '/Number' }, + gasLimit: { type: 'number' }, + }, + type: 'object', +}; diff --git a/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts b/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts deleted file mode 100644 index c0250ce7c..000000000 --- a/packages/contract-wrappers/src/stores/balance_proxy_allowance_lazy_store.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; - -import { AbstractBalanceAndProxyAllowanceLazyStore } from '../abstract/abstract_balance_and_proxy_allowance_lazy_store'; -import { TokenWrapper } from '../contract_wrappers/token_wrapper'; - -/** - * Copy on read store for balances/proxyAllowances of tokens/accounts - */ -export class BalanceAndProxyAllowanceLazyStore implements AbstractBalanceAndProxyAllowanceLazyStore { - private _tokenWrapper: TokenWrapper; - private _defaultBlock: BlockParamLiteral; - private _balance: { - [tokenAddress: string]: { - [userAddress: string]: BigNumber; - }; - }; - private _proxyAllowance: { - [tokenAddress: string]: { - [userAddress: string]: BigNumber; - }; - }; - constructor(token: TokenWrapper, defaultBlock: BlockParamLiteral) { - this._tokenWrapper = token; - this._defaultBlock = defaultBlock; - this._balance = {}; - this._proxyAllowance = {}; - } - public async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> { - if (_.isUndefined(this._balance[tokenAddress]) || _.isUndefined(this._balance[tokenAddress][userAddress])) { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const balance = await this._tokenWrapper.getBalanceAsync(tokenAddress, userAddress, methodOpts); - this.setBalance(tokenAddress, userAddress, balance); - } - const cachedBalance = this._balance[tokenAddress][userAddress]; - return cachedBalance; - } - public setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void { - if (_.isUndefined(this._balance[tokenAddress])) { - this._balance[tokenAddress] = {}; - } - this._balance[tokenAddress][userAddress] = balance; - } - public deleteBalance(tokenAddress: string, userAddress: string): void { - if (!_.isUndefined(this._balance[tokenAddress])) { - delete this._balance[tokenAddress][userAddress]; - if (_.isEmpty(this._balance[tokenAddress])) { - delete this._balance[tokenAddress]; - } - } - } - public async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber> { - if ( - _.isUndefined(this._proxyAllowance[tokenAddress]) || - _.isUndefined(this._proxyAllowance[tokenAddress][userAddress]) - ) { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const proxyAllowance = await this._tokenWrapper.getProxyAllowanceAsync( - tokenAddress, - userAddress, - methodOpts, - ); - this.setProxyAllowance(tokenAddress, userAddress, proxyAllowance); - } - const cachedProxyAllowance = this._proxyAllowance[tokenAddress][userAddress]; - return cachedProxyAllowance; - } - public setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void { - if (_.isUndefined(this._proxyAllowance[tokenAddress])) { - this._proxyAllowance[tokenAddress] = {}; - } - this._proxyAllowance[tokenAddress][userAddress] = proxyAllowance; - } - public deleteProxyAllowance(tokenAddress: string, userAddress: string): void { - if (!_.isUndefined(this._proxyAllowance[tokenAddress])) { - delete this._proxyAllowance[tokenAddress][userAddress]; - if (_.isEmpty(this._proxyAllowance[tokenAddress])) { - delete this._proxyAllowance[tokenAddress]; - } - } - } - public deleteAll(): void { - this._balance = {}; - this._proxyAllowance = {}; - } -} diff --git a/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts b/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts deleted file mode 100644 index 3d96a95f3..000000000 --- a/packages/contract-wrappers/src/stores/order_filled_cancelled_lazy_store.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { AbstractOrderFilledCancelledFetcher } from '@0xproject/order-utils'; -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; - -import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper'; - -/** - * Copy on read store for filled/cancelled taker amounts - */ -export class OrderFilledCancelledLazyStore implements AbstractOrderFilledCancelledFetcher { - private _exchangeWrapper: ExchangeWrapper; - private _defaultBlock: BlockParamLiteral; - private _filledTakerAmount: { - [orderHash: string]: BigNumber; - }; - private _cancelledTakerAmount: { - [orderHash: string]: BigNumber; - }; - constructor(exchange: ExchangeWrapper, defaultBlock: BlockParamLiteral) { - this._exchangeWrapper = exchange; - this._defaultBlock = defaultBlock; - this._filledTakerAmount = {}; - this._cancelledTakerAmount = {}; - } - public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> { - if (_.isUndefined(this._filledTakerAmount[orderHash])) { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const filledTakerAmount = await this._exchangeWrapper.getFilledTakerAmountAsync(orderHash, methodOpts); - this.setFilledTakerAmount(orderHash, filledTakerAmount); - } - const cachedFilled = this._filledTakerAmount[orderHash]; - return cachedFilled; - } - public setFilledTakerAmount(orderHash: string, filledTakerAmount: BigNumber): void { - this._filledTakerAmount[orderHash] = filledTakerAmount; - } - public deleteFilledTakerAmount(orderHash: string): void { - delete this._filledTakerAmount[orderHash]; - } - public async getCancelledTakerAmountAsync(orderHash: string): Promise<BigNumber> { - if (_.isUndefined(this._cancelledTakerAmount[orderHash])) { - const methodOpts = { - defaultBlock: this._defaultBlock, - }; - const cancelledTakerAmount = await this._exchangeWrapper.getCancelledTakerAmountAsync( - orderHash, - methodOpts, - ); - this.setCancelledTakerAmount(orderHash, cancelledTakerAmount); - } - const cachedCancelled = this._cancelledTakerAmount[orderHash]; - return cachedCancelled; - } - public setCancelledTakerAmount(orderHash: string, cancelledTakerAmount: BigNumber): void { - this._cancelledTakerAmount[orderHash] = cancelledTakerAmount; - } - public deleteCancelledTakerAmount(orderHash: string): void { - delete this._cancelledTakerAmount[orderHash]; - } - public deleteAll(): void { - this._filledTakerAmount = {}; - this._cancelledTakerAmount = {}; - } - public async getUnavailableTakerAmountAsync(orderHash: string): Promise<BigNumber> { - const unavailableTakerAmount = await this._exchangeWrapper.getUnavailableTakerAmountAsync(orderHash); - return unavailableTakerAmount; - } - public getZRXTokenAddress(): string { - const zrxToken = this._exchangeWrapper.getZRXTokenAddress(); - return zrxToken; - } -} diff --git a/packages/contract-wrappers/src/types.ts b/packages/contract-wrappers/src/types.ts index 0c1c58486..8d19d085f 100644 --- a/packages/contract-wrappers/src/types.ts +++ b/packages/contract-wrappers/src/types.ts @@ -1,26 +1,21 @@ import { BigNumber } from '@0xproject/utils'; -import { - BlockParam, - ContractEventArg, - LogEntryEvent, - LogWithDecodedArgs, - Order, - OrderState, - SignedOrder, -} from '@0xproject/types'; - -import { EtherTokenContractEventArgs, EtherTokenEvents } from './contract_wrappers/generated/ether_token'; -import { ExchangeContractEventArgs, ExchangeEvents } from './contract_wrappers/generated/exchange'; -import { TokenContractEventArgs, TokenEvents } from './contract_wrappers/generated/token'; +import { OrderState, SignedOrder } from '@0xproject/types'; +import { BlockParam, ContractEventArg, DecodedLogArgs, LogEntryEvent, LogWithDecodedArgs } from 'ethereum-types'; + +import { ERC20TokenEventArgs, ERC20TokenEvents } from './contract_wrappers/generated/erc20_token'; +import { ERC721TokenEventArgs, ERC721TokenEvents } from './contract_wrappers/generated/erc721_token'; +import { ExchangeEventArgs, ExchangeEvents } from './contract_wrappers/generated/exchange'; +import { WETH9EventArgs, WETH9Events } from './contract_wrappers/generated/weth9'; export enum ContractWrappersError { ExchangeContractDoesNotExist = 'EXCHANGE_CONTRACT_DOES_NOT_EXIST', ZRXContractDoesNotExist = 'ZRX_CONTRACT_DOES_NOT_EXIST', EtherTokenContractDoesNotExist = 'ETHER_TOKEN_CONTRACT_DOES_NOT_EXIST', - TokenTransferProxyContractDoesNotExist = 'TOKEN_TRANSFER_PROXY_CONTRACT_DOES_NOT_EXIST', - TokenRegistryContractDoesNotExist = 'TOKEN_REGISTRY_CONTRACT_DOES_NOT_EXIST', - TokenContractDoesNotExist = 'TOKEN_CONTRACT_DOES_NOT_EXIST', + ERC20ProxyContractDoesNotExist = 'ERC20_PROXY_CONTRACT_DOES_NOT_EXIST', + ERC721ProxyContractDoesNotExist = 'ERC721_PROXY_CONTRACT_DOES_NOT_EXIST', + ERC20TokenContractDoesNotExist = 'ERC20_TOKEN_CONTRACT_DOES_NOT_EXIST', + ERC721TokenContractDoesNotExist = 'ERC721_TOKEN_CONTRACT_DOES_NOT_EXIST', ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK', InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER', InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER', @@ -30,6 +25,8 @@ export enum ContractWrappersError { OutOfGas = 'OUT_OF_GAS', SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND', SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT', + ERC721OwnerNotFound = 'ERC_721_OWNER_NOT_FOUND', + ERC721NoApproval = 'ERC_721_NO_APPROVAL', } export enum InternalContractWrappersError { @@ -39,21 +36,15 @@ export enum InternalContractWrappersError { } export type LogEvent = LogEntryEvent; -export interface DecodedLogEvent<ArgsType> { +export interface DecodedLogEvent<ArgsType extends DecodedLogArgs> { isRemoved: boolean; log: LogWithDecodedArgs<ArgsType>; } -export type EventCallback<ArgsType> = (err: null | Error, log?: DecodedLogEvent<ArgsType>) => void; - -export enum ExchangeContractErrCodes { - ERROR_FILL_EXPIRED, // Order has already expired - ERROR_FILL_NO_VALUE, // Order has already been fully filled or cancelled - ERROR_FILL_TRUNCATION, // Rounding error too large - ERROR_FILL_BALANCE_ALLOWANCE, // Insufficient balance or allowance for token transfer - ERROR_CANCEL_EXPIRED, // Order has already expired - ERROR_CANCEL_NO_VALUE, // Order has already been fully filled or cancelled -} +export type EventCallback<ArgsType extends DecodedLogArgs> = ( + err: null | Error, + log?: DecodedLogEvent<ArgsType>, +) => void; export interface ContractEvent { logIndex: number; @@ -67,7 +58,7 @@ export interface ContractEvent { args: ContractEventArgs; } -export type ContractEventArgs = ExchangeContractEventArgs | TokenContractEventArgs | EtherTokenContractEventArgs; +export type ContractEventArgs = ExchangeEventArgs | ERC20TokenEventArgs | ERC721TokenEventArgs | WETH9EventArgs; // [address, name, symbol, decimals, ipfsHash, swarmHash] export type TokenMetadata = [string, string, string, number, string, string]; @@ -90,7 +81,7 @@ export interface TokenAddressBySymbol { [symbol: string]: string; } -export type ContractEvents = TokenEvents | ExchangeEvents | EtherTokenEvents; +export type ContractEvents = ERC20TokenEvents | ERC721TokenEvents | ExchangeEvents | WETH9Events; export interface IndexedFilterValues { [index: string]: ContractEventArg; @@ -101,14 +92,9 @@ export interface BlockRange { toBlock: BlockParam; } -export interface OrderCancellationRequest { - order: Order | SignedOrder; - takerTokenCancelAmount: BigNumber; -} - export interface OrderFillRequest { signedOrder: SignedOrder; - takerTokenFillAmount: BigNumber; + takerAssetFillAmount: BigNumber; } export type AsyncMethod = (...args: any[]) => Promise<any>; @@ -119,17 +105,19 @@ export type SyncMethod = (...args: any[]) => any; * gasPrice: Gas price to use with every transaction * exchangeContractAddress: The address of an exchange contract to use * zrxContractAddress: The address of the ZRX contract to use - * tokenRegistryContractAddress: The address of a token registry contract to use - * tokenTransferProxyContractAddress: The address of the token transfer proxy contract to use + * erc20ProxyContractAddress: The address of the erc20 token transfer proxy contract to use + * erc721ProxyContractAddress: The address of the erc721 token transfer proxy contract to use * orderWatcherConfig: All the configs related to the orderWatcher + * blockPollingIntervalMs: The interval to use for block polling in event watching methods (defaults to 1000) */ export interface ContractWrappersConfig { networkId: number; gasPrice?: BigNumber; exchangeContractAddress?: string; zrxContractAddress?: string; - tokenRegistryContractAddress?: string; - tokenTransferProxyContractAddress?: string; + erc20ProxyContractAddress?: string; + erc721ProxyContractAddress?: string; + blockPollingIntervalMs?: number; } /** @@ -180,3 +168,19 @@ export enum TransferType { } export type OnOrderStateChangeCallback = (err: Error | null, orderState?: OrderState) => void; + +export interface OrderInfo { + orderStatus: number; + orderHash: string; + orderTakerAssetFilledAmount: BigNumber; +} + +export enum OrderStatus { + INVALID, + INVALID_MAKER_ASSET_AMOUNT, + INVALID_TAKER_ASSET_AMOUNT, + FILLABLE, + EXPIRED, + FULLY_FILLED, + CANCELLED, +} diff --git a/packages/contract-wrappers/src/utils/assert.ts b/packages/contract-wrappers/src/utils/assert.ts index 140979e7e..da6697b08 100644 --- a/packages/contract-wrappers/src/utils/assert.ts +++ b/packages/contract-wrappers/src/utils/assert.ts @@ -1,20 +1,28 @@ import { assert as sharedAssert } from '@0xproject/assert'; // We need those two unused imports because they're actually used by sharedAssert which gets injected here -// tslint:disable:no-unused-variable -import { Schema } from '@0xproject/json-schemas'; -import { ECSignature } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; +import { Schema } from '@0xproject/json-schemas'; // tslint:disable-line:no-unused-variable +import { isValidSignatureAsync } from '@0xproject/order-utils'; +import { ECSignature } from '@0xproject/types'; // tslint:disable-line:no-unused-variable +import { BigNumber } from '@0xproject/utils'; // tslint:disable-line:no-unused-variable import { Web3Wrapper } from '@0xproject/web3-wrapper'; -// tslint:enable:no-unused-variable - -import { isValidSignature } from '@0xproject/order-utils'; +import { Provider } from 'ethereum-types'; export const assert = { ...sharedAssert, - isValidSignature(orderHash: string, ecSignature: ECSignature, signerAddress: string): void { - const isValid = isValidSignature(orderHash, ecSignature, signerAddress); + async isValidSignatureAsync( + provider: Provider, + orderHash: string, + signature: string, + signerAddress: string, + ): Promise<void> { + const isValid = await isValidSignatureAsync(provider, orderHash, signature, signerAddress); this.assert(isValid, `Expected order with hash '${orderHash}' to have a valid signature`); }, + isValidSubscriptionToken(variableName: string, subscriptionToken: string): void { + const uuidRegex = new RegExp('^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$'); + const isValid = uuidRegex.test(subscriptionToken); + this.assert(isValid, `Expected ${variableName} to be a valid subscription token`); + }, async isSenderAddressAsync( variableName: string, senderAddressHex: string, diff --git a/packages/contract-wrappers/src/utils/filter_utils.ts b/packages/contract-wrappers/src/utils/filter_utils.ts index f96dc573f..0e73987f7 100644 --- a/packages/contract-wrappers/src/utils/filter_utils.ts +++ b/packages/contract-wrappers/src/utils/filter_utils.ts @@ -1,14 +1,4 @@ -// tslint:disable:no-unused-variable -import { - ConstructorAbi, - ContractAbi, - EventAbi, - FallbackAbi, - FilterObject, - LogEntry, - MethodAbi, -} from '@0xproject/types'; -// tslint:enable:no-unused-variable +import { ConstructorAbi, ContractAbi, EventAbi, FallbackAbi, FilterObject, LogEntry, MethodAbi } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as jsSHA3 from 'js-sha3'; import * as _ from 'lodash'; diff --git a/packages/contract-wrappers/src/utils/order_validation_utils.ts b/packages/contract-wrappers/src/utils/order_validation_utils.ts deleted file mode 100644 index c6ef26275..000000000 --- a/packages/contract-wrappers/src/utils/order_validation_utils.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { getOrderHashHex, isValidSignature, OrderError } from '@0xproject/order-utils'; -import { ExchangeContractErrs, Order, SignedOrder } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as _ from 'lodash'; - -import { ExchangeContract } from '../contract_wrappers/generated/exchange'; -import { TradeSide, TransferType } from '../types'; -import { constants } from '../utils/constants'; -import { utils } from '../utils/utils'; - -import { ExchangeTransferSimulator } from './exchange_transfer_simulator'; - -export class OrderValidationUtils { - private _exchangeContract: ExchangeContract; - public static validateCancelOrderThrowIfInvalid( - order: Order, - cancelTakerTokenAmount: BigNumber, - unavailableTakerTokenAmount: BigNumber, - ): void { - if (cancelTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderCancelAmountZero); - } - if (order.takerTokenAmount.eq(unavailableTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); - } - const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); - if (order.expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderCancelExpired); - } - } - public static async validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - senderAddress: string, - zrxTokenAddress: string, - ): Promise<void> { - const fillMakerTokenAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerTokenAmount, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.makerTokenAddress, - signedOrder.maker, - senderAddress, - fillMakerTokenAmount, - TradeSide.Maker, - TransferType.Trade, - ); - await exchangeTradeEmulator.transferFromAsync( - signedOrder.takerTokenAddress, - senderAddress, - signedOrder.maker, - fillTakerTokenAmount, - TradeSide.Taker, - TransferType.Trade, - ); - const makerFeeAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, - signedOrder.maker, - signedOrder.feeRecipient, - makerFeeAmount, - TradeSide.Maker, - TransferType.Fee, - ); - const takerFeeAmount = OrderValidationUtils._getPartialAmount( - fillTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.takerFee, - ); - await exchangeTradeEmulator.transferFromAsync( - zrxTokenAddress, - senderAddress, - signedOrder.feeRecipient, - takerFeeAmount, - TradeSide.Taker, - TransferType.Fee, - ); - } - private static _validateRemainingFillAmountNotZeroOrThrow( - takerTokenAmount: BigNumber, - unavailableTakerTokenAmount: BigNumber, - ): void { - if (takerTokenAmount.eq(unavailableTakerTokenAmount)) { - throw new Error(ExchangeContractErrs.OrderRemainingFillAmountZero); - } - } - private static _validateOrderNotExpiredOrThrow(expirationUnixTimestampSec: BigNumber): void { - const currentUnixTimestampSec = utils.getCurrentUnixTimestampSec(); - if (expirationUnixTimestampSec.lessThan(currentUnixTimestampSec)) { - throw new Error(ExchangeContractErrs.OrderFillExpired); - } - } - private static _getPartialAmount(numerator: BigNumber, denominator: BigNumber, target: BigNumber): BigNumber { - const fillMakerTokenAmount = numerator - .mul(target) - .div(denominator) - .round(0); - return fillMakerTokenAmount; - } - constructor(exchangeContract: ExchangeContract) { - this._exchangeContract = exchangeContract; - } - public async validateOrderFillableOrThrowAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - zrxTokenAddress: string, - expectedFillTakerTokenAmount?: BigNumber, - ): Promise<void> { - const orderHash = getOrderHashHex(signedOrder); - const unavailableTakerTokenAmount = await this._exchangeContract.getUnavailableTakerTokenAmount.callAsync( - orderHash, - ); - OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( - signedOrder.takerTokenAmount, - unavailableTakerTokenAmount, - ); - OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); - let fillTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - if (!_.isUndefined(expectedFillTakerTokenAmount)) { - fillTakerTokenAmount = expectedFillTakerTokenAmount; - } - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - signedOrder.taker, - zrxTokenAddress, - ); - } - public async validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - zrxTokenAddress: string, - ): Promise<BigNumber> { - if (fillTakerTokenAmount.eq(0)) { - throw new Error(ExchangeContractErrs.OrderFillAmountZero); - } - const orderHash = getOrderHashHex(signedOrder); - if (!isValidSignature(orderHash, signedOrder.ecSignature, signedOrder.maker)) { - throw new Error(OrderError.InvalidSignature); - } - const unavailableTakerTokenAmount = await this._exchangeContract.getUnavailableTakerTokenAmount.callAsync( - orderHash, - ); - OrderValidationUtils._validateRemainingFillAmountNotZeroOrThrow( - signedOrder.takerTokenAmount, - unavailableTakerTokenAmount, - ); - if (signedOrder.taker !== constants.NULL_ADDRESS && signedOrder.taker !== takerAddress) { - throw new Error(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); - } - OrderValidationUtils._validateOrderNotExpiredOrThrow(signedOrder.expirationUnixTimestampSec); - const remainingTakerTokenAmount = signedOrder.takerTokenAmount.minus(unavailableTakerTokenAmount); - const filledTakerTokenAmount = remainingTakerTokenAmount.lessThan(fillTakerTokenAmount) - ? remainingTakerTokenAmount - : fillTakerTokenAmount; - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - filledTakerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - - const wouldRoundingErrorOccur = await this._exchangeContract.isRoundingError.callAsync( - filledTakerTokenAmount, - signedOrder.takerTokenAmount, - signedOrder.makerTokenAmount, - ); - if (wouldRoundingErrorOccur) { - throw new Error(ExchangeContractErrs.OrderFillRoundingError); - } - return filledTakerTokenAmount; - } - public async validateFillOrKillOrderThrowIfInvalidAsync( - exchangeTradeEmulator: ExchangeTransferSimulator, - signedOrder: SignedOrder, - fillTakerTokenAmount: BigNumber, - takerAddress: string, - zrxTokenAddress: string, - ): Promise<void> { - const filledTakerTokenAmount = await this.validateFillOrderThrowIfInvalidAsync( - exchangeTradeEmulator, - signedOrder, - fillTakerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - if (filledTakerTokenAmount !== fillTakerTokenAmount) { - throw new Error(ExchangeContractErrs.InsufficientRemainingFillAmount); - } - } -} diff --git a/packages/contract-wrappers/test/artifacts_test.ts b/packages/contract-wrappers/test/artifacts_test.ts index 39801df35..c05d513b3 100644 --- a/packages/contract-wrappers/test/artifacts_test.ts +++ b/packages/contract-wrappers/test/artifacts_test.ts @@ -10,7 +10,8 @@ chaiSetup.configure(); // Those tests are slower cause they're talking to a remote node const TIMEOUT = 10000; -describe('Artifacts', () => { +// TODO: Re-enable those tests after final kovan and ropsten deployments are done. +describe.skip('Artifacts', () => { describe('contracts are deployed on kovan', () => { const kovanRpcUrl = constants.KOVAN_RPC_URL; const provider = web3Factory.getRpcProvider({ rpcUrl: kovanRpcUrl }); @@ -18,14 +19,11 @@ describe('Artifacts', () => { networkId: constants.KOVAN_NETWORK_ID, }; const contractWrappers = new ContractWrappers(provider, config); - it('token registry contract is deployed', async () => { - await (contractWrappers.tokenRegistry as any)._getTokenRegistryContractAsync(); + it('erc20 proxy contract is deployed', async () => { + await (contractWrappers.erc20Proxy as any)._getTokenTransferProxyContractAsync(); }).timeout(TIMEOUT); - it('proxy contract is deployed', async () => { - await (contractWrappers.proxy as any)._getTokenTransferProxyContractAsync(); - }).timeout(TIMEOUT); - it('exchange contract is deployed', async () => { - await (contractWrappers.exchange as any)._getExchangeContractAsync(); + it('erc721 proxy contract is deployed', async () => { + await (contractWrappers.erc721Proxy as any)._getTokenTransferProxyContractAsync(); }).timeout(TIMEOUT); }); describe('contracts are deployed on ropsten', () => { @@ -35,14 +33,11 @@ describe('Artifacts', () => { networkId: constants.ROPSTEN_NETWORK_ID, }; const contractWrappers = new ContractWrappers(provider, config); - it('token registry contract is deployed', async () => { - await (contractWrappers.tokenRegistry as any)._getTokenRegistryContractAsync(); - }).timeout(TIMEOUT); - it('proxy contract is deployed', async () => { - await (contractWrappers.proxy as any)._getTokenTransferProxyContractAsync(); + it('erc20 proxy contract is deployed', async () => { + await (contractWrappers.erc20Proxy as any)._getTokenTransferProxyContractAsync(); }).timeout(TIMEOUT); - it('exchange contract is deployed', async () => { - await (contractWrappers.exchange as any)._getExchangeContractAsync(); + it('erc721 proxy contract is deployed', async () => { + await (contractWrappers.erc721Proxy as any)._getTokenTransferProxyContractAsync(); }).timeout(TIMEOUT); }); }); diff --git a/packages/contract-wrappers/test/token_transfer_proxy_wrapper_test.ts b/packages/contract-wrappers/test/erc20_proxy_wrapper_test.ts index 0b66985aa..6bf9f1e25 100644 --- a/packages/contract-wrappers/test/token_transfer_proxy_wrapper_test.ts +++ b/packages/contract-wrappers/test/erc20_proxy_wrapper_test.ts @@ -9,7 +9,7 @@ import { provider } from './utils/web3_wrapper'; chaiSetup.configure(); const expect = chai.expect; -describe('TokenTransferProxyWrapper', () => { +describe('ERC20ProxyWrapper', () => { let contractWrappers: ContractWrappers; const config = { networkId: constants.TESTRPC_NETWORK_ID, @@ -19,15 +19,15 @@ describe('TokenTransferProxyWrapper', () => { }); describe('#isAuthorizedAsync', () => { it('should return false if the address is not authorized', async () => { - const isAuthorized = await contractWrappers.proxy.isAuthorizedAsync(constants.NULL_ADDRESS); + const isAuthorized = await contractWrappers.erc20Proxy.isAuthorizedAsync(constants.NULL_ADDRESS); expect(isAuthorized).to.be.false(); }); }); describe('#getAuthorizedAddressesAsync', () => { it('should return the list of authorized addresses', async () => { - const authorizedAddresses = await contractWrappers.proxy.getAuthorizedAddressesAsync(); + const authorizedAddresses = await contractWrappers.erc20Proxy.getAuthorizedAddressesAsync(); for (const authorizedAddress of authorizedAddresses) { - const isAuthorized = await contractWrappers.proxy.isAuthorizedAsync(authorizedAddress); + const isAuthorized = await contractWrappers.erc20Proxy.isAuthorizedAsync(authorizedAddress); expect(isAuthorized).to.be.true(); } }); diff --git a/packages/contract-wrappers/test/token_wrapper_test.ts b/packages/contract-wrappers/test/erc20_wrapper_test.ts index 86b93b3f9..0011508e0 100644 --- a/packages/contract-wrappers/test/token_wrapper_test.ts +++ b/packages/contract-wrappers/test/erc20_wrapper_test.ts @@ -1,37 +1,36 @@ import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils'; import { EmptyWalletSubprovider } from '@0xproject/subproviders'; -import { DoneCallback, Provider } from '@0xproject/types'; +import { DoneCallback } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; import * as chai from 'chai'; +import { Provider } from 'ethereum-types'; import 'mocha'; import Web3ProviderEngine = require('web3-provider-engine'); import { - ApprovalContractEventArgs, BlockParamLiteral, BlockRange, ContractWrappers, ContractWrappersError, DecodedLogEvent, - Token, - TokenEvents, - TransferContractEventArgs, + ERC20TokenApprovalEventArgs, + ERC20TokenEvents, + ERC20TokenTransferEventArgs, } from '../src'; import { chaiSetup } from './utils/chai_setup'; import { constants } from './utils/constants'; -import { TokenUtils } from './utils/token_utils'; +import { tokenUtils } from './utils/token_utils'; import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -describe('TokenWrapper', () => { +describe('ERC20Wrapper', () => { let contractWrappers: ContractWrappers; let userAddresses: string[]; - let tokens: Token[]; - let tokenUtils: TokenUtils; + let tokens: string[]; let coinbase: string; let addressWithoutFunds: string; const config = { @@ -40,8 +39,7 @@ describe('TokenWrapper', () => { before(async () => { contractWrappers = new ContractWrappers(provider, config); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - tokenUtils = new TokenUtils(tokens); + tokens = tokenUtils.getDummyERC20TokenAddresses(); coinbase = userAddresses[0]; addressWithoutFunds = userAddresses[1]; }); @@ -52,26 +50,26 @@ describe('TokenWrapper', () => { await blockchainLifecycle.revertAsync(); }); describe('#transferAsync', () => { - let token: Token; + let tokenAddress: string; let transferAmount: BigNumber; before(() => { - token = tokens[0]; + tokenAddress = tokens[0]; transferAmount = new BigNumber(42); }); it('should successfully transfer tokens', async () => { const fromAddress = coinbase; const toAddress = addressWithoutFunds; - const preBalance = await contractWrappers.token.getBalanceAsync(token.address, toAddress); + const preBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress); expect(preBalance).to.be.bignumber.equal(0); - await contractWrappers.token.transferAsync(token.address, fromAddress, toAddress, transferAmount); - const postBalance = await contractWrappers.token.getBalanceAsync(token.address, toAddress); + await contractWrappers.erc20Token.transferAsync(tokenAddress, fromAddress, toAddress, transferAmount); + const postBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress); return expect(postBalance).to.be.bignumber.equal(transferAmount); }); it('should fail to transfer tokens if fromAddress has an insufficient balance', async () => { const fromAddress = addressWithoutFunds; const toAddress = coinbase; return expect( - contractWrappers.token.transferAsync(token.address, fromAddress, toAddress, transferAmount), + contractWrappers.erc20Token.transferAsync(tokenAddress, fromAddress, toAddress, transferAmount), ).to.be.rejectedWith(ContractWrappersError.InsufficientBalanceForTransfer); }); it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { @@ -79,16 +77,21 @@ describe('TokenWrapper', () => { const fromAddress = coinbase; const toAddress = coinbase; return expect( - contractWrappers.token.transferAsync(nonExistentTokenAddress, fromAddress, toAddress, transferAmount), - ).to.be.rejectedWith(ContractWrappersError.TokenContractDoesNotExist); + contractWrappers.erc20Token.transferAsync( + nonExistentTokenAddress, + fromAddress, + toAddress, + transferAmount, + ), + ).to.be.rejectedWith(ContractWrappersError.ERC20TokenContractDoesNotExist); }); }); describe('#transferFromAsync', () => { - let token: Token; + let tokenAddress: string; let toAddress: string; let senderAddress: string; before(async () => { - token = tokens[0]; + tokenAddress = tokens[0]; toAddress = addressWithoutFunds; senderAddress = userAddresses[2]; }); @@ -96,19 +99,19 @@ describe('TokenWrapper', () => { const fromAddress = coinbase; const transferAmount = new BigNumber(42); - const fromAddressBalance = await contractWrappers.token.getBalanceAsync(token.address, fromAddress); + const fromAddressBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, fromAddress); expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount); - const fromAddressAllowance = await contractWrappers.token.getAllowanceAsync( - token.address, + const fromAddressAllowance = await contractWrappers.erc20Token.getAllowanceAsync( + tokenAddress, fromAddress, toAddress, ); expect(fromAddressAllowance).to.be.bignumber.equal(0); return expect( - contractWrappers.token.transferFromAsync( - token.address, + contractWrappers.erc20Token.transferFromAsync( + tokenAddress, fromAddress, toAddress, senderAddress, @@ -120,11 +123,11 @@ describe('TokenWrapper', () => { const fromAddress = coinbase; const transferAmount = new BigNumber(42); - await contractWrappers.token.setAllowanceAsync(token.address, fromAddress, toAddress, transferAmount); + await contractWrappers.erc20Token.setAllowanceAsync(tokenAddress, fromAddress, toAddress, transferAmount); return expect( - contractWrappers.token.transferFromAsync( - token.address, + contractWrappers.erc20Token.transferFromAsync( + tokenAddress, fromAddress, toAddress, senderAddress, @@ -136,20 +139,25 @@ describe('TokenWrapper', () => { const fromAddress = addressWithoutFunds; const transferAmount = new BigNumber(42); - const fromAddressBalance = await contractWrappers.token.getBalanceAsync(token.address, fromAddress); + const fromAddressBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, fromAddress); expect(fromAddressBalance).to.be.bignumber.equal(0); - await contractWrappers.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount); - const fromAddressAllowance = await contractWrappers.token.getAllowanceAsync( - token.address, + await contractWrappers.erc20Token.setAllowanceAsync( + tokenAddress, + fromAddress, + senderAddress, + transferAmount, + ); + const fromAddressAllowance = await contractWrappers.erc20Token.getAllowanceAsync( + tokenAddress, fromAddress, senderAddress, ); expect(fromAddressAllowance).to.be.bignumber.equal(transferAmount); return expect( - contractWrappers.token.transferFromAsync( - token.address, + contractWrappers.erc20Token.transferFromAsync( + tokenAddress, fromAddress, toAddress, senderAddress, @@ -160,42 +168,47 @@ describe('TokenWrapper', () => { it('should successfully transfer tokens', async () => { const fromAddress = coinbase; - const preBalance = await contractWrappers.token.getBalanceAsync(token.address, toAddress); + const preBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress); expect(preBalance).to.be.bignumber.equal(0); const transferAmount = new BigNumber(42); - await contractWrappers.token.setAllowanceAsync(token.address, fromAddress, senderAddress, transferAmount); + await contractWrappers.erc20Token.setAllowanceAsync( + tokenAddress, + fromAddress, + senderAddress, + transferAmount, + ); - await contractWrappers.token.transferFromAsync( - token.address, + await contractWrappers.erc20Token.transferFromAsync( + tokenAddress, fromAddress, toAddress, senderAddress, transferAmount, ); - const postBalance = await contractWrappers.token.getBalanceAsync(token.address, toAddress); + const postBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress); return expect(postBalance).to.be.bignumber.equal(transferAmount); }); it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { const fromAddress = coinbase; const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; return expect( - contractWrappers.token.transferFromAsync( + contractWrappers.erc20Token.transferFromAsync( nonExistentTokenAddress, fromAddress, toAddress, senderAddress, new BigNumber(42), ), - ).to.be.rejectedWith(ContractWrappersError.TokenContractDoesNotExist); + ).to.be.rejectedWith(ContractWrappersError.ERC20TokenContractDoesNotExist); }); }); describe('#getBalanceAsync', () => { describe('With provider with accounts', () => { it('should return the balance for an existing ERC20 token', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; - const balance = await contractWrappers.token.getBalanceAsync(token.address, ownerAddress); + const balance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, ownerAddress); const expectedBalance = new BigNumber('1000000000000000000000000000'); return expect(balance).to.be.bignumber.equal(expectedBalance); }); @@ -203,13 +216,13 @@ describe('TokenWrapper', () => { const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; const ownerAddress = coinbase; return expect( - contractWrappers.token.getBalanceAsync(nonExistentTokenAddress, ownerAddress), - ).to.be.rejectedWith(ContractWrappersError.TokenContractDoesNotExist); + contractWrappers.erc20Token.getBalanceAsync(nonExistentTokenAddress, ownerAddress), + ).to.be.rejectedWith(ContractWrappersError.ERC20TokenContractDoesNotExist); }); it('should return a balance of 0 for a non-existent owner address', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593'; - const balance = await contractWrappers.token.getBalanceAsync(token.address, nonExistentOwner); + const balance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, nonExistentOwner); const expectedBalance = new BigNumber(0); return expect(balance).to.be.bignumber.equal(expectedBalance); }); @@ -221,9 +234,12 @@ describe('TokenWrapper', () => { zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config); }); it('should return balance even when called with provider instance without addresses', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; - const balance = await zeroExContractWithoutAccounts.token.getBalanceAsync(token.address, ownerAddress); + const balance = await zeroExContractWithoutAccounts.erc20Token.getBalanceAsync( + tokenAddress, + ownerAddress, + ); const expectedBalance = new BigNumber('1000000000000000000000000000'); return expect(balance).to.be.bignumber.equal(expectedBalance); }); @@ -231,12 +247,12 @@ describe('TokenWrapper', () => { }); describe('#setAllowanceAsync', () => { it("should set the spender's allowance", async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; const spenderAddress = addressWithoutFunds; - const allowanceBeforeSet = await contractWrappers.token.getAllowanceAsync( - token.address, + const allowanceBeforeSet = await contractWrappers.erc20Token.getAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, ); @@ -244,15 +260,15 @@ describe('TokenWrapper', () => { expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet); const amountInBaseUnits = new BigNumber(50); - await contractWrappers.token.setAllowanceAsync( - token.address, + await contractWrappers.erc20Token.setAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, amountInBaseUnits, ); - const allowanceAfterSet = await contractWrappers.token.getAllowanceAsync( - token.address, + const allowanceAfterSet = await contractWrappers.erc20Token.getAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, ); @@ -262,44 +278,50 @@ describe('TokenWrapper', () => { }); describe('#setUnlimitedAllowanceAsync', () => { it("should set the unlimited spender's allowance", async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; const spenderAddress = addressWithoutFunds; - await contractWrappers.token.setUnlimitedAllowanceAsync(token.address, ownerAddress, spenderAddress); - const allowance = await contractWrappers.token.getAllowanceAsync( - token.address, + await contractWrappers.erc20Token.setUnlimitedAllowanceAsync(tokenAddress, ownerAddress, spenderAddress); + const allowance = await contractWrappers.erc20Token.getAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, ); - return expect(allowance).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + return expect(allowance).to.be.bignumber.equal( + contractWrappers.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + ); }); it('should reduce the gas cost for transfers including tokens with unlimited allowance support', async () => { const transferAmount = new BigNumber(5); - const zrx = tokenUtils.getProtocolTokenOrThrow(); + const zrxAddress = tokenUtils.getProtocolTokenAddress(); const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses; - await contractWrappers.token.setAllowanceAsync( - zrx.address, + await contractWrappers.erc20Token.setAllowanceAsync( + zrxAddress, coinbase, userWithNormalAllowance, transferAmount, ); - await contractWrappers.token.setUnlimitedAllowanceAsync(zrx.address, coinbase, userWithUnlimitedAllowance); + await contractWrappers.erc20Token.setUnlimitedAllowanceAsync( + zrxAddress, + coinbase, + userWithUnlimitedAllowance, + ); const initBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance); const initBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync( userWithUnlimitedAllowance, ); - await contractWrappers.token.transferFromAsync( - zrx.address, + await contractWrappers.erc20Token.transferFromAsync( + zrxAddress, coinbase, userWithNormalAllowance, userWithNormalAllowance, transferAmount, ); - await contractWrappers.token.transferFromAsync( - zrx.address, + await contractWrappers.erc20Token.transferFromAsync( + zrxAddress, coinbase, userWithUnlimitedAllowance, userWithUnlimitedAllowance, @@ -323,20 +345,20 @@ describe('TokenWrapper', () => { describe('#getAllowanceAsync', () => { describe('With provider with accounts', () => { it('should get the proxy allowance', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; const spenderAddress = addressWithoutFunds; const amountInBaseUnits = new BigNumber(50); - await contractWrappers.token.setAllowanceAsync( - token.address, + await contractWrappers.erc20Token.setAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, amountInBaseUnits, ); - const allowance = await contractWrappers.token.getAllowanceAsync( - token.address, + const allowance = await contractWrappers.erc20Token.getAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, ); @@ -344,11 +366,11 @@ describe('TokenWrapper', () => { return expect(allowance).to.be.bignumber.equal(expectedAllowance); }); it('should return 0 if no allowance set yet', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; const spenderAddress = addressWithoutFunds; - const allowance = await contractWrappers.token.getAllowanceAsync( - token.address, + const allowance = await contractWrappers.erc20Token.getAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, ); @@ -363,20 +385,20 @@ describe('TokenWrapper', () => { zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config); }); it('should get the proxy allowance', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; const spenderAddress = addressWithoutFunds; const amountInBaseUnits = new BigNumber(50); - await contractWrappers.token.setAllowanceAsync( - token.address, + await contractWrappers.erc20Token.setAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, amountInBaseUnits, ); - const allowance = await zeroExContractWithoutAccounts.token.getAllowanceAsync( - token.address, + const allowance = await zeroExContractWithoutAccounts.erc20Token.getAllowanceAsync( + tokenAddress, ownerAddress, spenderAddress, ); @@ -387,42 +409,50 @@ describe('TokenWrapper', () => { }); describe('#getProxyAllowanceAsync', () => { it('should get the proxy allowance', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; const amountInBaseUnits = new BigNumber(50); - await contractWrappers.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits); + await contractWrappers.erc20Token.setProxyAllowanceAsync(tokenAddress, ownerAddress, amountInBaseUnits); - const allowance = await contractWrappers.token.getProxyAllowanceAsync(token.address, ownerAddress); + const allowance = await contractWrappers.erc20Token.getProxyAllowanceAsync(tokenAddress, ownerAddress); const expectedAllowance = amountInBaseUnits; return expect(allowance).to.be.bignumber.equal(expectedAllowance); }); }); describe('#setProxyAllowanceAsync', () => { it('should set the proxy allowance', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; - const allowanceBeforeSet = await contractWrappers.token.getProxyAllowanceAsync(token.address, ownerAddress); + const allowanceBeforeSet = await contractWrappers.erc20Token.getProxyAllowanceAsync( + tokenAddress, + ownerAddress, + ); const expectedAllowanceBeforeAllowanceSet = new BigNumber(0); expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet); const amountInBaseUnits = new BigNumber(50); - await contractWrappers.token.setProxyAllowanceAsync(token.address, ownerAddress, amountInBaseUnits); + await contractWrappers.erc20Token.setProxyAllowanceAsync(tokenAddress, ownerAddress, amountInBaseUnits); - const allowanceAfterSet = await contractWrappers.token.getProxyAllowanceAsync(token.address, ownerAddress); + const allowanceAfterSet = await contractWrappers.erc20Token.getProxyAllowanceAsync( + tokenAddress, + ownerAddress, + ); const expectedAllowanceAfterAllowanceSet = amountInBaseUnits; return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet); }); }); describe('#setUnlimitedProxyAllowanceAsync', () => { it('should set the unlimited proxy allowance', async () => { - const token = tokens[0]; + const tokenAddress = tokens[0]; const ownerAddress = coinbase; - await contractWrappers.token.setUnlimitedProxyAllowanceAsync(token.address, ownerAddress); - const allowance = await contractWrappers.token.getProxyAllowanceAsync(token.address, ownerAddress); - return expect(allowance).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, ownerAddress); + const allowance = await contractWrappers.erc20Token.getProxyAllowanceAsync(tokenAddress, ownerAddress); + return expect(allowance).to.be.bignumber.equal( + contractWrappers.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, + ); }); }); describe('#subscribe', () => { @@ -431,11 +461,10 @@ describe('TokenWrapper', () => { const transferAmount = new BigNumber(42); const allowanceAmount = new BigNumber(42); before(() => { - const token = tokens[0]; - tokenAddress = token.address; + tokenAddress = tokens[0]; }); afterEach(() => { - contractWrappers.token.unsubscribeAll(); + contractWrappers.erc20Token.unsubscribeAll(); }); // Hack: Mocha does not allow a test to be both async and have a `done` callback // Since we need to await the receipt of the event in the `subscribe` callback, @@ -445,7 +474,7 @@ describe('TokenWrapper', () => { it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => { (async () => { const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<TransferContractEventArgs>) => { + (logEvent: DecodedLogEvent<ERC20TokenTransferEventArgs>) => { expect(logEvent.isRemoved).to.be.false(); expect(logEvent.log.logIndex).to.be.equal(0); expect(logEvent.log.transactionIndex).to.be.equal(0); @@ -456,14 +485,24 @@ describe('TokenWrapper', () => { expect(args._value).to.be.bignumber.equal(transferAmount); }, ); - contractWrappers.token.subscribe(tokenAddress, TokenEvents.Transfer, indexFilterValues, callback); - await contractWrappers.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); + contractWrappers.erc20Token.subscribe( + tokenAddress, + ERC20TokenEvents.Transfer, + indexFilterValues, + callback, + ); + await contractWrappers.erc20Token.transferAsync( + tokenAddress, + coinbase, + addressWithoutFunds, + transferAmount, + ); })().catch(done); }); it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => { (async () => { const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + (logEvent: DecodedLogEvent<ERC20TokenApprovalEventArgs>) => { expect(logEvent).to.not.be.undefined(); expect(logEvent.isRemoved).to.be.false(); const args = logEvent.log.args; @@ -472,8 +511,13 @@ describe('TokenWrapper', () => { expect(args._value).to.be.bignumber.equal(allowanceAmount); }, ); - contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); - await contractWrappers.token.setAllowanceAsync( + contractWrappers.erc20Token.subscribe( + tokenAddress, + ERC20TokenEvents.Approval, + indexFilterValues, + callback, + ); + await contractWrappers.erc20Token.setAllowanceAsync( tokenAddress, coinbase, addressWithoutFunds, @@ -484,42 +528,52 @@ describe('TokenWrapper', () => { it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (_logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + (_logEvent: DecodedLogEvent<ERC20TokenApprovalEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }, ); - contractWrappers.token.subscribe( + contractWrappers.erc20Token.subscribe( tokenAddress, - TokenEvents.Transfer, + ERC20TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled, ); const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(); contractWrappers.setProvider(provider, constants.TESTRPC_NETWORK_ID); - contractWrappers.token.subscribe( + contractWrappers.erc20Token.subscribe( tokenAddress, - TokenEvents.Transfer, + ERC20TokenEvents.Transfer, indexFilterValues, callbackToBeCalled, ); - await contractWrappers.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); + await contractWrappers.erc20Token.transferAsync( + tokenAddress, + coinbase, + addressWithoutFunds, + transferAmount, + ); })().catch(done); }); it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (_logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + (_logEvent: DecodedLogEvent<ERC20TokenApprovalEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }, ); - const subscriptionToken = contractWrappers.token.subscribe( + const subscriptionToken = contractWrappers.erc20Token.subscribe( tokenAddress, - TokenEvents.Transfer, + ERC20TokenEvents.Transfer, indexFilterValues, callbackNeverToBeCalled, ); - contractWrappers.token.unsubscribe(subscriptionToken); - await contractWrappers.token.transferAsync(tokenAddress, coinbase, addressWithoutFunds, transferAmount); + contractWrappers.erc20Token.unsubscribe(subscriptionToken); + await contractWrappers.erc20Token.transferAsync( + tokenAddress, + coinbase, + addressWithoutFunds, + transferAmount, + ); done(); })().catch(done); }); @@ -533,16 +587,15 @@ describe('TokenWrapper', () => { }; let txHash: string; before(() => { - const token = tokens[0]; - tokenAddress = token.address; - tokenTransferProxyAddress = contractWrappers.proxy.getContractAddress(); + tokenAddress = tokens[0]; + tokenTransferProxyAddress = contractWrappers.erc20Proxy.getContractAddress(); }); it('should get logs with decoded args emitted by Approval', async () => { - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const eventName = TokenEvents.Approval; + txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const eventName = ERC20TokenEvents.Approval; const indexFilterValues = {}; - const logs = await contractWrappers.token.getLogsAsync<ApprovalContractEventArgs>( + const logs = await contractWrappers.erc20Token.getLogsAsync<ERC20TokenApprovalEventArgs>( tokenAddress, eventName, blockRange, @@ -553,14 +606,14 @@ describe('TokenWrapper', () => { expect(logs[0].event).to.be.equal(eventName); expect(args._owner).to.be.equal(coinbase); expect(args._spender).to.be.equal(tokenTransferProxyAddress); - expect(args._value).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + expect(args._value).to.be.bignumber.equal(contractWrappers.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); }); it('should only get the logs with the correct event name', async () => { - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const differentEventName = TokenEvents.Transfer; + txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const differentEventName = ERC20TokenEvents.Transfer; const indexFilterValues = {}; - const logs = await contractWrappers.token.getLogsAsync( + const logs = await contractWrappers.erc20Token.getLogsAsync( tokenAddress, differentEventName, blockRange, @@ -569,15 +622,18 @@ describe('TokenWrapper', () => { expect(logs).to.have.length(0); }); it('should only get the logs with the correct indexed fields', async () => { - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(tokenAddress, addressWithoutFunds); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const eventName = TokenEvents.Approval; + txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync( + tokenAddress, + addressWithoutFunds, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const eventName = ERC20TokenEvents.Approval; const indexFilterValues = { _owner: coinbase, }; - const logs = await contractWrappers.token.getLogsAsync<ApprovalContractEventArgs>( + const logs = await contractWrappers.erc20Token.getLogsAsync<ERC20TokenApprovalEventArgs>( tokenAddress, eventName, blockRange, diff --git a/packages/contract-wrappers/test/erc721_proxy_wrapper_test.ts b/packages/contract-wrappers/test/erc721_proxy_wrapper_test.ts new file mode 100644 index 000000000..9473d930b --- /dev/null +++ b/packages/contract-wrappers/test/erc721_proxy_wrapper_test.ts @@ -0,0 +1,35 @@ +import * as chai from 'chai'; + +import { ContractWrappers } from '../src'; + +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { provider } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; + +describe('ERC721ProxyWrapper', () => { + let contractWrappers: ContractWrappers; + const config = { + networkId: constants.TESTRPC_NETWORK_ID, + }; + before(async () => { + contractWrappers = new ContractWrappers(provider, config); + }); + describe('#isAuthorizedAsync', () => { + it('should return false if the address is not authorized', async () => { + const isAuthorized = await contractWrappers.erc721Proxy.isAuthorizedAsync(constants.NULL_ADDRESS); + expect(isAuthorized).to.be.false(); + }); + }); + describe('#getAuthorizedAddressesAsync', () => { + it('should return the list of authorized addresses', async () => { + const authorizedAddresses = await contractWrappers.erc721Proxy.getAuthorizedAddressesAsync(); + for (const authorizedAddress of authorizedAddresses) { + const isAuthorized = await contractWrappers.erc721Proxy.isAuthorizedAsync(authorizedAddress); + expect(isAuthorized).to.be.true(); + } + }); + }); +}); diff --git a/packages/contract-wrappers/test/erc721_wrapper_test.ts b/packages/contract-wrappers/test/erc721_wrapper_test.ts new file mode 100644 index 000000000..96b8fcf1d --- /dev/null +++ b/packages/contract-wrappers/test/erc721_wrapper_test.ts @@ -0,0 +1,472 @@ +import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils'; +import { EmptyWalletSubprovider } from '@0xproject/subproviders'; +import { DoneCallback } from '@0xproject/types'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import { Provider } from 'ethereum-types'; +import 'mocha'; +import Web3ProviderEngine = require('web3-provider-engine'); + +import { + BlockParamLiteral, + BlockRange, + ContractWrappers, + ContractWrappersError, + DecodedLogEvent, + ERC721TokenApprovalEventArgs, + ERC721TokenApprovalForAllEventArgs, + ERC721TokenEvents, + ERC721TokenTransferEventArgs, +} from '../src'; + +import { chaiSetup } from './utils/chai_setup'; +import { constants } from './utils/constants'; +import { tokenUtils } from './utils/token_utils'; +import { provider, web3Wrapper } from './utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('ERC721Wrapper', () => { + let contractWrappers: ContractWrappers; + let userAddresses: string[]; + let tokens: string[]; + let ownerAddress: string; + let tokenAddress: string; + let anotherOwnerAddress: string; + let operatorAddress: string; + let approvedAddress: string; + let receiverAddress: string; + const config = { + networkId: constants.TESTRPC_NETWORK_ID, + }; + before(async () => { + contractWrappers = new ContractWrappers(provider, config); + userAddresses = await web3Wrapper.getAvailableAddressesAsync(); + tokens = tokenUtils.getDummyERC721TokenAddresses(); + tokenAddress = tokens[0]; + [ownerAddress, operatorAddress, anotherOwnerAddress, approvedAddress, receiverAddress] = userAddresses; + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('#transferFromAsync', () => { + it('should fail to transfer NFT if fromAddress has no approvals set', async () => { + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + return expect( + contractWrappers.erc721Token.transferFromAsync(tokenAddress, receiverAddress, approvedAddress, tokenId), + ).to.be.rejectedWith(ContractWrappersError.ERC721NoApproval); + }); + it('should successfully transfer tokens when sender is an approved address', async () => { + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + let txHash = await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const owner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId); + expect(owner).to.be.equal(ownerAddress); + txHash = await contractWrappers.erc721Token.transferFromAsync( + tokenAddress, + receiverAddress, + approvedAddress, + tokenId, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const newOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId); + expect(newOwner).to.be.equal(receiverAddress); + }); + it('should successfully transfer tokens when sender is an approved operator', async () => { + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + const isApprovedForAll = true; + let txHash = await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + isApprovedForAll, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const owner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId); + expect(owner).to.be.equal(ownerAddress); + txHash = await contractWrappers.erc721Token.transferFromAsync( + tokenAddress, + receiverAddress, + operatorAddress, + tokenId, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const newOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId); + expect(newOwner).to.be.equal(receiverAddress); + }); + }); + describe('#getTokenCountAsync', () => { + describe('With provider with accounts', () => { + it('should return the count for an existing ERC721 token', async () => { + let tokenCount = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, ownerAddress); + expect(tokenCount).to.be.bignumber.equal(0); + await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + tokenCount = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, ownerAddress); + expect(tokenCount).to.be.bignumber.equal(1); + }); + it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { + const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; + return expect( + contractWrappers.erc721Token.getTokenCountAsync(nonExistentTokenAddress, ownerAddress), + ).to.be.rejectedWith(ContractWrappersError.ERC721TokenContractDoesNotExist); + }); + it('should return a balance of 0 for a non-existent owner address', async () => { + const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593'; + const balance = await contractWrappers.erc721Token.getTokenCountAsync(tokenAddress, nonExistentOwner); + const expectedBalance = new BigNumber(0); + return expect(balance).to.be.bignumber.equal(expectedBalance); + }); + }); + describe('With provider without accounts', () => { + let zeroExContractWithoutAccounts: ContractWrappers; + before(async () => { + const emptyWalletProvider = addEmptyWalletSubprovider(provider); + zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config); + }); + it('should return balance even when called with provider instance without addresses', async () => { + const balance = await zeroExContractWithoutAccounts.erc721Token.getTokenCountAsync( + tokenAddress, + ownerAddress, + ); + return expect(balance).to.be.bignumber.equal(0); + }); + }); + }); + describe('#getOwnerOfAsync', () => { + it('should return the owner for an existing ERC721 token', async () => { + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + const tokenOwner = await contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, tokenId); + expect(tokenOwner).to.be.bignumber.equal(ownerAddress); + }); + it('should throw a CONTRACT_DOES_NOT_EXIST error for a non-existent token contract', async () => { + const nonExistentTokenAddress = '0x9dd402f14d67e001d8efbe6583e51bf9706aa065'; + const fakeTokenId = new BigNumber(42); + return expect( + contractWrappers.erc721Token.getOwnerOfAsync(nonExistentTokenAddress, fakeTokenId), + ).to.be.rejectedWith(ContractWrappersError.ERC721TokenContractDoesNotExist); + }); + it('should return undefined not 0 for a non-existent ERC721', async () => { + const fakeTokenId = new BigNumber(42); + return expect(contractWrappers.erc721Token.getOwnerOfAsync(tokenAddress, fakeTokenId)).to.be.rejectedWith( + ContractWrappersError.ERC721OwnerNotFound, + ); + }); + }); + describe('#setApprovalForAllAsync/isApprovedForAllAsync', () => { + it('should check if operator address is approved', async () => { + let isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + ); + expect(isApprovedForAll).to.be.false(); + // set + isApprovedForAll = true; + let txHash = await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + isApprovedForAll, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + ); + expect(isApprovedForAll).to.be.true(); + // unset + txHash = await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + false, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + isApprovedForAll = await contractWrappers.erc721Token.isApprovedForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + ); + expect(isApprovedForAll).to.be.false(); + }); + }); + describe('#setProxyApprovalForAllAsync/isProxyApprovedForAllAsync', () => { + it('should check if proxy address is approved', async () => { + let isApprovedForAll = true; + const txHash = await contractWrappers.erc721Token.setProxyApprovalForAllAsync( + tokenAddress, + ownerAddress, + isApprovedForAll, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + isApprovedForAll = await contractWrappers.erc721Token.isProxyApprovedForAllAsync( + tokenAddress, + ownerAddress, + ); + expect(isApprovedForAll).to.be.true(); + }); + }); + describe('#setApprovalAsync/getApprovedIfExistsAsync', () => { + it("should set the spender's approval", async () => { + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + + const approvalBeforeSet = await contractWrappers.erc721Token.getApprovedIfExistsAsync( + tokenAddress, + tokenId, + ); + expect(approvalBeforeSet).to.be.undefined(); + await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId); + const approvalAfterSet = await contractWrappers.erc721Token.getApprovedIfExistsAsync(tokenAddress, tokenId); + expect(approvalAfterSet).to.be.equal(approvedAddress); + }); + }); + describe('#setProxyApprovalAsync/isProxyApprovedAsync', () => { + it('should set the proxy approval', async () => { + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + + const approvalBeforeSet = await contractWrappers.erc721Token.isProxyApprovedAsync(tokenAddress, tokenId); + expect(approvalBeforeSet).to.be.false(); + await contractWrappers.erc721Token.setProxyApprovalAsync(tokenAddress, tokenId); + const approvalAfterSet = await contractWrappers.erc721Token.isProxyApprovedAsync(tokenAddress, tokenId); + expect(approvalAfterSet).to.be.true(); + }); + }); + describe('#subscribe', () => { + const indexFilterValues = {}; + afterEach(() => { + contractWrappers.erc721Token.unsubscribeAll(); + }); + // Hack: Mocha does not allow a test to be both async and have a `done` callback + // Since we need to await the receipt of the event in the `subscribe` callback, + // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then + // wrap the rest of the test in an async block + // Source: https://github.com/mochajs/mocha/issues/2407 + it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => { + (async () => { + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ERC721TokenTransferEventArgs>) => { + expect(logEvent.isRemoved).to.be.false(); + expect(logEvent.log.logIndex).to.be.equal(0); + expect(logEvent.log.transactionIndex).to.be.equal(0); + expect(logEvent.log.blockNumber).to.be.a('number'); + const args = logEvent.log.args; + expect(args._from).to.be.equal(ownerAddress); + expect(args._to).to.be.equal(receiverAddress); + expect(args._tokenId).to.be.bignumber.equal(tokenId); + }, + ); + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + const isApprovedForAll = true; + await web3Wrapper.awaitTransactionSuccessAsync( + await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + isApprovedForAll, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + contractWrappers.erc721Token.subscribe( + tokenAddress, + ERC721TokenEvents.Transfer, + indexFilterValues, + callback, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await contractWrappers.erc721Token.transferFromAsync( + tokenAddress, + receiverAddress, + operatorAddress, + tokenId, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + })().catch(done); + }); + it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => { + (async () => { + const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ERC721TokenApprovalEventArgs>) => { + expect(logEvent).to.not.be.undefined(); + expect(logEvent.isRemoved).to.be.false(); + const args = logEvent.log.args; + expect(args._owner).to.be.equal(ownerAddress); + expect(args._approved).to.be.equal(approvedAddress); + expect(args._tokenId).to.be.bignumber.equal(tokenId); + }, + ); + contractWrappers.erc721Token.subscribe( + tokenAddress, + ERC721TokenEvents.Approval, + indexFilterValues, + callback, + ); + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + await web3Wrapper.awaitTransactionSuccessAsync( + await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + })().catch(done); + }); + it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => { + (async () => { + const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ERC721TokenApprovalEventArgs>) => { + done(new Error('Expected this subscription to have been cancelled')); + }, + ); + contractWrappers.erc721Token.subscribe( + tokenAddress, + ERC721TokenEvents.Transfer, + indexFilterValues, + callbackNeverToBeCalled, + ); + const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(); + contractWrappers.setProvider(provider, constants.TESTRPC_NETWORK_ID); + contractWrappers.erc721Token.subscribe( + tokenAddress, + ERC721TokenEvents.Approval, + indexFilterValues, + callbackToBeCalled, + ); + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + await web3Wrapper.awaitTransactionSuccessAsync( + await contractWrappers.erc721Token.setApprovalAsync(tokenAddress, approvedAddress, tokenId), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + done(); + })().catch(done); + }); + it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { + (async () => { + const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( + (logEvent: DecodedLogEvent<ERC721TokenApprovalForAllEventArgs>) => { + done(new Error('Expected this subscription to have been cancelled')); + }, + ); + const subscriptionToken = contractWrappers.erc721Token.subscribe( + tokenAddress, + ERC721TokenEvents.ApprovalForAll, + indexFilterValues, + callbackNeverToBeCalled, + ); + contractWrappers.erc721Token.unsubscribe(subscriptionToken); + + const tokenId = await tokenUtils.mintDummyERC721Async(tokenAddress, ownerAddress); + const isApproved = true; + await web3Wrapper.awaitTransactionSuccessAsync( + await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + isApproved, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + done(); + })().catch(done); + }); + }); + describe('#getLogsAsync', () => { + let tokenTransferProxyAddress: string; + const blockRange: BlockRange = { + fromBlock: 0, + toBlock: BlockParamLiteral.Latest, + }; + let txHash: string; + before(() => { + tokenTransferProxyAddress = contractWrappers.erc721Proxy.getContractAddress(); + }); + it('should get logs with decoded args emitted by ApprovalForAll', async () => { + const isApprovedForAll = true; + txHash = await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + isApprovedForAll, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const eventName = ERC721TokenEvents.ApprovalForAll; + const indexFilterValues = {}; + const logs = await contractWrappers.erc721Token.getLogsAsync<ERC721TokenApprovalForAllEventArgs>( + tokenAddress, + eventName, + blockRange, + indexFilterValues, + ); + expect(logs).to.have.length(1); + const args = logs[0].args; + expect(logs[0].event).to.be.equal(eventName); + expect(args._owner).to.be.equal(ownerAddress); + expect(args._operator).to.be.equal(operatorAddress); + expect(args._approved).to.be.equal(isApprovedForAll); + }); + it('should only get the logs with the correct event name', async () => { + const isApprovedForAll = true; + txHash = await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + isApprovedForAll, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const differentEventName = ERC721TokenEvents.Transfer; + const indexFilterValues = {}; + const logs = await contractWrappers.erc721Token.getLogsAsync( + tokenAddress, + differentEventName, + blockRange, + indexFilterValues, + ); + expect(logs).to.have.length(0); + }); + it('should only get the logs with the correct indexed fields', async () => { + const isApprovedForAll = true; + txHash = await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + ownerAddress, + operatorAddress, + isApprovedForAll, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + txHash = await contractWrappers.erc721Token.setApprovalForAllAsync( + tokenAddress, + anotherOwnerAddress, + operatorAddress, + isApprovedForAll, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const eventName = ERC721TokenEvents.ApprovalForAll; + const indexFilterValues = { + _owner: anotherOwnerAddress, + }; + const logs = await contractWrappers.erc721Token.getLogsAsync<ERC721TokenApprovalForAllEventArgs>( + tokenAddress, + eventName, + blockRange, + indexFilterValues, + ); + expect(logs).to.have.length(1); + const args = logs[0].args; + expect(args._owner).to.be.equal(anotherOwnerAddress); + }); + }); +}); +// tslint:disable:max-file-line-count + +function addEmptyWalletSubprovider(p: Provider): Provider { + const providerEngine = new Web3ProviderEngine(); + providerEngine.addProvider(new EmptyWalletSubprovider()); + const currentSubproviders = (p as any)._providers; + for (const subprovider of currentSubproviders) { + providerEngine.addProvider(subprovider); + } + providerEngine.start(); + return providerEngine; +} diff --git a/packages/contract-wrappers/test/ether_token_wrapper_test.ts b/packages/contract-wrappers/test/ether_token_wrapper_test.ts index f401ef90a..0a860884a 100644 --- a/packages/contract-wrappers/test/ether_token_wrapper_test.ts +++ b/packages/contract-wrappers/test/ether_token_wrapper_test.ts @@ -6,22 +6,21 @@ import * as chai from 'chai'; import 'mocha'; import { - ApprovalContractEventArgs, BlockParamLiteral, BlockRange, ContractWrappers, ContractWrappersError, DecodedLogEvent, - DepositContractEventArgs, - EtherTokenEvents, - Token, - TransferContractEventArgs, - WithdrawalContractEventArgs, + WETH9ApprovalEventArgs, + WETH9DepositEventArgs, + WETH9Events, + WETH9TransferEventArgs, + WETH9WithdrawalEventArgs, } from '../src'; import { chaiSetup } from './utils/chai_setup'; import { constants } from './utils/constants'; -import { TokenUtils } from './utils/token_utils'; +import { tokenUtils } from './utils/token_utils'; import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); @@ -36,7 +35,6 @@ const MAX_REASONABLE_GAS_COST_IN_WEI = 62517; describe('EtherTokenWrapper', () => { let contractWrappers: ContractWrappers; - let tokens: Token[]; let userAddresses: string[]; let addressWithETH: string; let wethContractAddress: string; @@ -54,7 +52,6 @@ describe('EtherTokenWrapper', () => { const withdrawalAmount = new BigNumber(42); before(async () => { contractWrappers = new ContractWrappers(provider, zeroExConfig); - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); addressWithETH = userAddresses[0]; wethContractAddress = contractWrappers.etherToken.getContractAddressIfExists() as string; @@ -85,7 +82,10 @@ describe('EtherTokenWrapper', () => { describe('#depositAsync', () => { it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => { const preETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH); - const preWETHBalance = await contractWrappers.token.getBalanceAsync(wethContractAddress, addressWithETH); + const preWETHBalance = await contractWrappers.erc20Token.getBalanceAsync( + wethContractAddress, + addressWithETH, + ); expect(preETHBalance).to.be.bignumber.gt(0); expect(preWETHBalance).to.be.bignumber.equal(0); @@ -94,10 +94,10 @@ describe('EtherTokenWrapper', () => { depositWeiAmount, addressWithETH, ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); const postETHBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(addressWithETH); - const postWETHBalanceInBaseUnits = await contractWrappers.token.getBalanceAsync( + const postWETHBalanceInBaseUnits = await contractWrappers.erc20Token.getBalanceAsync( wethContractAddress, addressWithETH, ); @@ -126,7 +126,10 @@ describe('EtherTokenWrapper', () => { const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount); const preETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH); - const preWETHBalance = await contractWrappers.token.getBalanceAsync(wethContractAddress, addressWithETH); + const preWETHBalance = await contractWrappers.erc20Token.getBalanceAsync( + wethContractAddress, + addressWithETH, + ); let gasCost = expectedPreETHBalance.minus(preETHBalance); expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI); expect(preWETHBalance).to.be.bignumber.equal(depositWeiAmount); @@ -136,10 +139,10 @@ describe('EtherTokenWrapper', () => { depositWeiAmount, addressWithETH, ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); const postETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH); - const postWETHBalanceInBaseUnits = await contractWrappers.token.getBalanceAsync( + const postWETHBalanceInBaseUnits = await contractWrappers.erc20Token.getBalanceAsync( wethContractAddress, addressWithETH, ); @@ -150,7 +153,10 @@ describe('EtherTokenWrapper', () => { expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI); }); it('should throw if user has insufficient WETH balance for withdrawal', async () => { - const preWETHBalance = await contractWrappers.token.getBalanceAsync(wethContractAddress, addressWithETH); + const preWETHBalance = await contractWrappers.erc20Token.getBalanceAsync( + wethContractAddress, + addressWithETH, + ); expect(preWETHBalance).to.be.bignumber.equal(0); // tslint:disable-next-line:custom-no-magic-numbers @@ -164,10 +170,8 @@ describe('EtherTokenWrapper', () => { describe('#subscribe', () => { const indexFilterValues = {}; let etherTokenAddress: string; - before(() => { - const tokenUtils = new TokenUtils(tokens); - const etherToken = tokenUtils.getWethTokenOrThrow(); - etherTokenAddress = etherToken.address; + before(async () => { + etherTokenAddress = tokenUtils.getWethTokenAddress(); }); afterEach(() => { contractWrappers.etherToken.unsubscribeAll(); @@ -180,7 +184,7 @@ describe('EtherTokenWrapper', () => { it('Should receive the Transfer event when tokens are transfered', (done: DoneCallback) => { (async () => { const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<TransferContractEventArgs>) => { + (logEvent: DecodedLogEvent<WETH9TransferEventArgs>) => { expect(logEvent).to.not.be.undefined(); expect(logEvent.isRemoved).to.be.false(); expect(logEvent.log.logIndex).to.be.equal(0); @@ -195,11 +199,11 @@ describe('EtherTokenWrapper', () => { await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); contractWrappers.etherToken.subscribe( etherTokenAddress, - EtherTokenEvents.Transfer, + WETH9Events.Transfer, indexFilterValues, callback, ); - await contractWrappers.token.transferAsync( + await contractWrappers.erc20Token.transferAsync( etherTokenAddress, addressWithETH, addressWithoutFunds, @@ -210,7 +214,7 @@ describe('EtherTokenWrapper', () => { it('Should receive the Approval event when allowance is being set', (done: DoneCallback) => { (async () => { const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + (logEvent: DecodedLogEvent<WETH9ApprovalEventArgs>) => { expect(logEvent).to.not.be.undefined(); expect(logEvent.isRemoved).to.be.false(); const args = logEvent.log.args; @@ -221,11 +225,11 @@ describe('EtherTokenWrapper', () => { ); contractWrappers.etherToken.subscribe( etherTokenAddress, - EtherTokenEvents.Approval, + WETH9Events.Approval, indexFilterValues, callback, ); - await contractWrappers.token.setAllowanceAsync( + await contractWrappers.erc20Token.setAllowanceAsync( etherTokenAddress, addressWithETH, addressWithoutFunds, @@ -236,7 +240,7 @@ describe('EtherTokenWrapper', () => { it('Should receive the Deposit event when ether is being deposited', (done: DoneCallback) => { (async () => { const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<DepositContractEventArgs>) => { + (logEvent: DecodedLogEvent<WETH9DepositEventArgs>) => { expect(logEvent).to.not.be.undefined(); expect(logEvent.isRemoved).to.be.false(); const args = logEvent.log.args; @@ -246,7 +250,7 @@ describe('EtherTokenWrapper', () => { ); contractWrappers.etherToken.subscribe( etherTokenAddress, - EtherTokenEvents.Deposit, + WETH9Events.Deposit, indexFilterValues, callback, ); @@ -256,7 +260,7 @@ describe('EtherTokenWrapper', () => { it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => { (async () => { const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<WithdrawalContractEventArgs>) => { + (logEvent: DecodedLogEvent<WETH9WithdrawalEventArgs>) => { expect(logEvent).to.not.be.undefined(); expect(logEvent.isRemoved).to.be.false(); const args = logEvent.log.args; @@ -267,7 +271,7 @@ describe('EtherTokenWrapper', () => { await contractWrappers.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH); contractWrappers.etherToken.subscribe( etherTokenAddress, - EtherTokenEvents.Withdrawal, + WETH9Events.Withdrawal, indexFilterValues, callback, ); @@ -277,13 +281,13 @@ describe('EtherTokenWrapper', () => { it('should cancel outstanding subscriptions when ZeroEx.setProvider is called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (_logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + (_logEvent: DecodedLogEvent<WETH9ApprovalEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }, ); contractWrappers.etherToken.subscribe( etherTokenAddress, - EtherTokenEvents.Transfer, + WETH9Events.Transfer, indexFilterValues, callbackNeverToBeCalled, ); @@ -292,11 +296,11 @@ describe('EtherTokenWrapper', () => { await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); contractWrappers.etherToken.subscribe( etherTokenAddress, - EtherTokenEvents.Transfer, + WETH9Events.Transfer, indexFilterValues, callbackToBeCalled, ); - await contractWrappers.token.transferAsync( + await contractWrappers.erc20Token.transferAsync( etherTokenAddress, addressWithETH, addressWithoutFunds, @@ -307,19 +311,19 @@ describe('EtherTokenWrapper', () => { it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (_logEvent: DecodedLogEvent<ApprovalContractEventArgs>) => { + (_logEvent: DecodedLogEvent<WETH9ApprovalEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }, ); await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH); const subscriptionToken = contractWrappers.etherToken.subscribe( etherTokenAddress, - EtherTokenEvents.Transfer, + WETH9Events.Transfer, indexFilterValues, callbackNeverToBeCalled, ); contractWrappers.etherToken.unsubscribe(subscriptionToken); - await contractWrappers.token.transferAsync( + await contractWrappers.erc20Token.transferAsync( etherTokenAddress, addressWithETH, addressWithoutFunds, @@ -331,7 +335,7 @@ describe('EtherTokenWrapper', () => { }); describe('#getLogsAsync', () => { let etherTokenAddress: string; - let tokenTransferProxyAddress: string; + let erc20ProxyAddress: string; const blockRange: BlockRange = { fromBlock: 0, toBlock: BlockParamLiteral.Latest, @@ -339,17 +343,18 @@ describe('EtherTokenWrapper', () => { let txHash: string; before(() => { addressWithETH = userAddresses[0]; - const tokenUtils = new TokenUtils(tokens); - const etherToken = tokenUtils.getWethTokenOrThrow(); - etherTokenAddress = etherToken.address; - tokenTransferProxyAddress = contractWrappers.proxy.getContractAddress(); + etherTokenAddress = tokenUtils.getWethTokenAddress(); + erc20ProxyAddress = contractWrappers.erc20Proxy.getContractAddress(); }); it('should get logs with decoded args emitted by Approval', async () => { - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const eventName = EtherTokenEvents.Approval; + txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync( + etherTokenAddress, + addressWithETH, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const eventName = WETH9Events.Approval; const indexFilterValues = {}; - const logs = await contractWrappers.etherToken.getLogsAsync<ApprovalContractEventArgs>( + const logs = await contractWrappers.etherToken.getLogsAsync<WETH9ApprovalEventArgs>( etherTokenAddress, eventName, blockRange, @@ -359,14 +364,14 @@ describe('EtherTokenWrapper', () => { const args = logs[0].args; expect(logs[0].event).to.be.equal(eventName); expect(args._owner).to.be.equal(addressWithETH); - expect(args._spender).to.be.equal(tokenTransferProxyAddress); - expect(args._value).to.be.bignumber.equal(contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); + expect(args._spender).to.be.equal(erc20ProxyAddress); + expect(args._value).to.be.bignumber.equal(contractWrappers.erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS); }); it('should get logs with decoded args emitted by Deposit', async () => { await contractWrappers.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH); - const eventName = EtherTokenEvents.Deposit; + const eventName = WETH9Events.Deposit; const indexFilterValues = {}; - const logs = await contractWrappers.etherToken.getLogsAsync<DepositContractEventArgs>( + const logs = await contractWrappers.etherToken.getLogsAsync<WETH9DepositEventArgs>( etherTokenAddress, eventName, blockRange, @@ -379,9 +384,12 @@ describe('EtherTokenWrapper', () => { expect(args._value).to.be.bignumber.equal(depositAmount); }); it('should only get the logs with the correct event name', async () => { - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const differentEventName = EtherTokenEvents.Transfer; + txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync( + etherTokenAddress, + addressWithETH, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const differentEventName = WETH9Events.Transfer; const indexFilterValues = {}; const logs = await contractWrappers.etherToken.getLogsAsync( etherTokenAddress, @@ -392,18 +400,21 @@ describe('EtherTokenWrapper', () => { expect(logs).to.have.length(0); }); it('should only get the logs with the correct indexed fields', async () => { - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(etherTokenAddress, addressWithETH); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync( + txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync( + etherTokenAddress, + addressWithETH, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + txHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync( etherTokenAddress, addressWithoutFunds, ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const eventName = EtherTokenEvents.Approval; + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const eventName = WETH9Events.Approval; const indexFilterValues = { _owner: addressWithETH, }; - const logs = await contractWrappers.etherToken.getLogsAsync<ApprovalContractEventArgs>( + const logs = await contractWrappers.etherToken.getLogsAsync<WETH9ApprovalEventArgs>( etherTokenAddress, eventName, blockRange, diff --git a/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts b/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts deleted file mode 100644 index cbc52df7f..000000000 --- a/packages/contract-wrappers/test/exchange_transfer_simulator_test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { BlockParamLiteral, Token } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; - -import { ContractWrappers, ExchangeContractErrs } from '../src'; -import { BalanceAndProxyAllowanceLazyStore } from '../src/stores/balance_proxy_allowance_lazy_store'; -import { TradeSide, TransferType } from '../src/types'; -import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator'; - -import { chaiSetup } from './utils/chai_setup'; -import { constants } from './utils/constants'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('ExchangeTransferSimulator', () => { - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - const contractWrappers = new ContractWrappers(provider, config); - const transferAmount = new BigNumber(5); - let userAddresses: string[]; - let tokens: Token[]; - let coinbase: string; - let sender: string; - let recipient: string; - let exampleTokenAddress: string; - let exchangeTransferSimulator: ExchangeTransferSimulator; - let txHash: string; - before(async () => { - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - [coinbase, sender, recipient] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - exampleTokenAddress = tokens[0].address; - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('#transferFromAsync', () => { - beforeEach(() => { - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - contractWrappers.token, - BlockParamLiteral.Latest, - ); - exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - }); - it("throws if the user doesn't have enough allowance", async () => { - return expect( - exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, - sender, - recipient, - transferAmount, - TradeSide.Taker, - TransferType.Trade, - ), - ).to.be.rejectedWith(ExchangeContractErrs.InsufficientTakerAllowance); - }); - it("throws if the user doesn't have enough balance", async () => { - txHash = await contractWrappers.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - return expect( - exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, - sender, - recipient, - transferAmount, - TradeSide.Maker, - TransferType.Trade, - ), - ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); - }); - it('updates balances and proxyAllowance after transfer', async () => { - txHash = await contractWrappers.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - txHash = await contractWrappers.token.setProxyAllowanceAsync(exampleTokenAddress, sender, transferAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - await exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, - sender, - recipient, - transferAmount, - TradeSide.Taker, - TransferType.Trade, - ); - const store = (exchangeTransferSimulator as any)._store; - const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); - const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient); - const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender); - expect(senderBalance).to.be.bignumber.equal(0); - expect(recipientBalance).to.be.bignumber.equal(transferAmount); - expect(senderProxyAllowance).to.be.bignumber.equal(0); - }); - it("doesn't update proxyAllowance after transfer if unlimited", async () => { - txHash = await contractWrappers.token.transferAsync(exampleTokenAddress, coinbase, sender, transferAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - txHash = await contractWrappers.token.setUnlimitedProxyAllowanceAsync(exampleTokenAddress, sender); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - await exchangeTransferSimulator.transferFromAsync( - exampleTokenAddress, - sender, - recipient, - transferAmount, - TradeSide.Taker, - TransferType.Trade, - ); - const store = (exchangeTransferSimulator as any)._store; - const senderBalance = await store.getBalanceAsync(exampleTokenAddress, sender); - const recipientBalance = await store.getBalanceAsync(exampleTokenAddress, recipient); - const senderProxyAllowance = await store.getProxyAllowanceAsync(exampleTokenAddress, sender); - expect(senderBalance).to.be.bignumber.equal(0); - expect(recipientBalance).to.be.bignumber.equal(transferAmount); - expect(senderProxyAllowance).to.be.bignumber.equal( - contractWrappers.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS, - ); - }); - }); -}); diff --git a/packages/contract-wrappers/test/exchange_wrapper_test.ts b/packages/contract-wrappers/test/exchange_wrapper_test.ts index c945b4c70..b2c716008 100644 --- a/packages/contract-wrappers/test/exchange_wrapper_test.ts +++ b/packages/contract-wrappers/test/exchange_wrapper_test.ts @@ -1,57 +1,91 @@ import { BlockchainLifecycle, callbackErrorReporter } from '@0xproject/dev-utils'; import { FillScenarios } from '@0xproject/fill-scenarios'; -import { getOrderHashHex } from '@0xproject/order-utils'; -import { BlockParamLiteral, DoneCallback, OrderState } from '@0xproject/types'; +import { assetProxyUtils, orderHashUtils } from '@0xproject/order-utils'; +import { DoneCallback, SignedOrder } from '@0xproject/types'; import { BigNumber } from '@0xproject/utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; import * as chai from 'chai'; +import { BlockParamLiteral } from 'ethereum-types'; import 'mocha'; import { - BlockRange, ContractWrappers, DecodedLogEvent, - ExchangeContractErrs, + ExchangeCancelEventArgs, ExchangeEvents, - LogCancelContractEventArgs, - LogFillContractEventArgs, - OrderCancellationRequest, - OrderFillRequest, - SignedOrder, - Token, + ExchangeFillEventArgs, + OrderStatus, } from '../src'; import { chaiSetup } from './utils/chai_setup'; import { constants } from './utils/constants'; -import { TokenUtils } from './utils/token_utils'; +import { tokenUtils } from './utils/token_utils'; import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -const NON_EXISTENT_ORDER_HASH = '0x79370342234e7acd6bbeac335bd3bb1d368383294b64b8160a00f4060e4d3777'; - describe('ExchangeWrapper', () => { let contractWrappers: ContractWrappers; - let tokenUtils: TokenUtils; - let tokens: Token[]; let userAddresses: string[]; let zrxTokenAddress: string; let fillScenarios: FillScenarios; let exchangeContractAddress: string; + let makerTokenAddress: string; + let takerTokenAddress: string; + let coinbase: string; + let makerAddress: string; + let anotherMakerAddress: string; + let takerAddress: string; + let makerAssetData: string; + let takerAssetData: string; + let feeRecipient: string; + let txHash: string; + const fillableAmount = new BigNumber(5); + const takerTokenFillAmount = new BigNumber(5); + let signedOrder: SignedOrder; + let anotherSignedOrder: SignedOrder; const config = { networkId: constants.TESTRPC_NETWORK_ID, + blockPollingIntervalMs: 0, }; before(async () => { + await blockchainLifecycle.startAsync(); contractWrappers = new ContractWrappers(provider, config); exchangeContractAddress = contractWrappers.exchange.getContractAddress(); + const erc20ProxyAddress = contractWrappers.erc20Proxy.getContractAddress(); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - tokenUtils = new TokenUtils(tokens); - zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address; - fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); - await fillScenarios.initTokenBalancesAsync(); + zrxTokenAddress = tokenUtils.getProtocolTokenAddress(); + fillScenarios = new FillScenarios( + provider, + userAddresses, + zrxTokenAddress, + exchangeContractAddress, + erc20ProxyAddress, + ); + [coinbase, makerAddress, takerAddress, feeRecipient, anotherMakerAddress] = userAddresses; + [makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses(); + [makerAssetData, takerAssetData] = [ + assetProxyUtils.encodeERC20AssetData(makerTokenAddress), + assetProxyUtils.encodeERC20AssetData(takerTokenAddress), + ]; + signedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + ); + anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( + makerAssetData, + takerAssetData, + makerAddress, + takerAddress, + fillableAmount, + ); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); }); beforeEach(async () => { await blockchainLifecycle.startAsync(); @@ -59,903 +93,279 @@ describe('ExchangeWrapper', () => { afterEach(async () => { await blockchainLifecycle.revertAsync(); }); - describe('fillOrKill order(s)', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let coinbase: string; - let makerAddress: string; - let takerAddress: string; - let feeRecipient: string; - const takerTokenFillAmount = new BigNumber(5); - before(async () => { - [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - describe('#batchFillOrKillAsync', () => { - it('successfully batch fillOrKill', async () => { - const fillableAmount = new BigNumber(5); - const partialFillTakerAmount = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, + describe('fill order(s)', () => { + describe('#fillOrderAsync', () => { + it('should fill a valid order', async () => { + txHash = await contractWrappers.exchange.fillOrderAsync( + signedOrder, + takerTokenFillAmount, takerAddress, - fillableAmount, ); - const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + }); + }); + describe('#fillOrderNoThrowAsync', () => { + it('should fill a valid order', async () => { + txHash = await contractWrappers.exchange.fillOrderNoThrowAsync( + signedOrder, + takerTokenFillAmount, takerAddress, - fillableAmount, ); - const orderFillRequests = [ - { - signedOrder, - takerTokenFillAmount: partialFillTakerAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount: partialFillTakerAmount, - }, - ]; - await contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress); - }); - describe('order transaction options', () => { - let signedOrder: SignedOrder; - let orderFillRequests: OrderFillRequest[]; - const fillableAmount = new BigNumber(5); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - orderFillRequests = [ - { - signedOrder, - takerTokenFillAmount: new BigNumber(0), - }, - ]; - }); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { - shouldValidate: true, - }), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.batchFillOrKillAsync(orderFillRequests, takerAddress, { - shouldValidate: false, - }), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder); + expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FULLY_FILLED); }); }); describe('#fillOrKillOrderAsync', () => { - let signedOrder: SignedOrder; - const fillableAmount = new BigNumber(5); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, + it('should fill or kill a valid order', async () => { + txHash = await contractWrappers.exchange.fillOrKillOrderAsync( + signedOrder, + takerTokenFillAmount, takerAddress, - fillableAmount, ); - }); - describe('successful fills', () => { - it('should fill a valid order', async () => { - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(0); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(0); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount); - await contractWrappers.exchange.fillOrKillOrderAsync( - signedOrder, - takerTokenFillAmount, - takerAddress, - ); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(takerTokenFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(takerTokenFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - }); - it('should partially fill a valid order', async () => { - const partialFillAmount = new BigNumber(3); - await contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, partialFillAmount, takerAddress); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(partialFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(partialFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - }); - }); - describe('order transaction options', () => { - const emptyFillableAmount = new BigNumber(0); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { - shouldValidate: true, - }), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.fillOrKillOrderAsync(signedOrder, emptyFillableAmount, takerAddress, { - shouldValidate: false, - }), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); }); }); - }); - describe('fill order(s)', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let coinbase: string; - let makerAddress: string; - let takerAddress: string; - let feeRecipient: string; - const fillableAmount = new BigNumber(5); - const takerTokenFillAmount = new BigNumber(5); - const shouldThrowOnInsufficientBalanceOrAllowance = true; - before(async () => { - [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - describe('#fillOrderAsync', () => { - describe('successful fills', () => { - it('should fill a valid order', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(0); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(0); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount); - const txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - takerTokenFillAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(takerTokenFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(takerTokenFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(takerTokenFillAmount)); - }); - it('should partially fill the valid order', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const partialFillAmount = new BigNumber(3); - const txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - partialFillAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, makerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, makerAddress), - ).to.be.bignumber.equal(partialFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(makerTokenAddress, takerAddress), - ).to.be.bignumber.equal(partialFillAmount); - expect( - await contractWrappers.token.getBalanceAsync(takerTokenAddress, takerAddress), - ).to.be.bignumber.equal(fillableAmount.minus(partialFillAmount)); - }); - it('should fill the valid orders with fees', async () => { - const makerFee = new BigNumber(1); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - takerTokenAddress, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - feeRecipient, - ); - const txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - takerTokenFillAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - expect( - await contractWrappers.token.getBalanceAsync(zrxTokenAddress, feeRecipient), - ).to.be.bignumber.equal(makerFee.plus(takerFee)); - }); - }); - describe('order transaction options', () => { - let signedOrder: SignedOrder; - const emptyFillTakerAmount = new BigNumber(0); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - }); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.fillOrderAsync( - signedOrder, - emptyFillTakerAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.fillOrderAsync( - signedOrder, - emptyFillTakerAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: true, - }, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.fillOrderAsync( - signedOrder, - emptyFillTakerAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: false, - }, - ), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); + describe('#batchFillOrdersAsync', () => { + it('should fill a batch of valid orders', async () => { + const signedOrders = [signedOrder, anotherSignedOrder]; + const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount]; + txHash = await contractWrappers.exchange.batchFillOrdersAsync( + signedOrders, + takerAssetFillAmounts, + takerAddress, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); }); - describe('negative fill amount', async () => { - let signedOrder: SignedOrder; - const negativeFillTakerAmount = new BigNumber(-100); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - }); - it('should not allow the exchange wrapper to fill if amount is negative', async () => { - return expect( - contractWrappers.exchange.fillOrderAsync( - signedOrder, - negativeFillTakerAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejected(); - }); + }); + describe('#marketBuyOrdersAsync', () => { + it('should maker buy', async () => { + const signedOrders = [signedOrder, anotherSignedOrder]; + const makerAssetFillAmount = takerTokenFillAmount; + txHash = await contractWrappers.exchange.marketBuyOrdersAsync( + signedOrders, + makerAssetFillAmount, + takerAddress, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); }); }); - describe('#batchFillOrdersAsync', () => { - let signedOrder: SignedOrder; - let signedOrderHashHex: string; - let anotherSignedOrder: SignedOrder; - let anotherOrderHashHex: string; - let orderFillBatch: OrderFillRequest[]; - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, + describe('#marketBuyOrdersNoThrowAsync', () => { + it('should no throw maker buy', async () => { + const signedOrders = [signedOrder, anotherSignedOrder]; + const makerAssetFillAmount = takerTokenFillAmount; + txHash = await contractWrappers.exchange.marketBuyOrdersNoThrowAsync( + signedOrders, + makerAssetFillAmount, takerAddress, - fillableAmount, ); - signedOrderHashHex = getOrderHashHex(signedOrder); - anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder); + expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + }); + }); + describe('#marketSellOrdersAsync', () => { + it('should maker sell', async () => { + const signedOrders = [signedOrder, anotherSignedOrder]; + const takerAssetFillAmount = takerTokenFillAmount; + txHash = await contractWrappers.exchange.marketSellOrdersAsync( + signedOrders, + takerAssetFillAmount, takerAddress, - fillableAmount, ); - anotherOrderHashHex = getOrderHashHex(anotherSignedOrder); - }); - describe('successful batch fills', () => { - beforeEach(() => { - orderFillBatch = [ - { - signedOrder, - takerTokenFillAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount, - }, - ]; - }); - it('should throw if a batch is empty', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - [], - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - }); - it('should successfully fill multiple orders', async () => { - const txHash = await contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex); - const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync( - anotherOrderHashHex, - ); - expect(filledAmount).to.be.bignumber.equal(takerTokenFillAmount); - expect(anotherFilledAmount).to.be.bignumber.equal(takerTokenFillAmount); - }); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); }); - describe('order transaction options', () => { - beforeEach(async () => { - const emptyFillTakerAmount = new BigNumber(0); - orderFillBatch = [ - { - signedOrder, - takerTokenFillAmount: emptyFillTakerAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount: emptyFillTakerAmount, - }, - ]; - }); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: true, - }, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: false, - }, - ), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - }); - describe('negative batch fill amount', async () => { - beforeEach(async () => { - const negativeFillTakerAmount = new BigNumber(-100); - orderFillBatch = [ - { - signedOrder, - takerTokenFillAmount, - }, - { - signedOrder: anotherSignedOrder, - takerTokenFillAmount: negativeFillTakerAmount, - }, - ]; - }); - it('should not allow the exchange wrapper to batch fill if any amount is negative', async () => { - return expect( - contractWrappers.exchange.batchFillOrdersAsync( - orderFillBatch, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejected(); - }); + }); + describe('#marketSellOrdersNoThrowAsync', () => { + it('should no throw maker sell', async () => { + const signedOrders = [signedOrder, anotherSignedOrder]; + const takerAssetFillAmount = takerTokenFillAmount; + txHash = await contractWrappers.exchange.marketSellOrdersNoThrowAsync( + signedOrders, + takerAssetFillAmount, + takerAddress, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder); + expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + }); + }); + describe('#batchFillOrdersNoThrowAsync', () => { + it('should fill a batch of valid orders', async () => { + const signedOrders = [signedOrder, anotherSignedOrder]; + const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount]; + txHash = await contractWrappers.exchange.batchFillOrdersNoThrowAsync( + signedOrders, + takerAssetFillAmounts, + takerAddress, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + let orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder); + expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + orderInfo = await contractWrappers.exchange.getOrderInfoAsync(anotherSignedOrder); + expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FULLY_FILLED); + }); + }); + describe('#batchFillOrKillOrdersAsync', () => { + it('should fill or kill a batch of valid orders', async () => { + const signedOrders = [signedOrder, anotherSignedOrder]; + const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount]; + txHash = await contractWrappers.exchange.batchFillOrKillOrdersAsync( + signedOrders, + takerAssetFillAmounts, + takerAddress, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); }); }); - describe('#fillOrdersUpTo', () => { - let signedOrder: SignedOrder; - let signedOrderHashHex: string; - let anotherSignedOrder: SignedOrder; - let anotherOrderHashHex: string; - let signedOrders: SignedOrder[]; - const fillUpToAmount = fillableAmount.plus(fillableAmount).minus(1); - beforeEach(async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, + describe('#matchOrdersAsync', () => { + it('should match two valid ordersr', async () => { + const matchingSignedOrder = await fillScenarios.createFillableSignedOrderAsync( + takerAssetData, + makerAssetData, makerAddress, takerAddress, fillableAmount, ); - signedOrderHashHex = getOrderHashHex(signedOrder); - anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, + txHash = await contractWrappers.exchange.matchOrdersAsync( + signedOrder, + matchingSignedOrder, takerAddress, - fillableAmount, ); - anotherOrderHashHex = getOrderHashHex(anotherSignedOrder); - signedOrders = [signedOrder, anotherSignedOrder]; - }); - describe('successful batch fills', () => { - it('should throw if a batch is empty', async () => { - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - [], - fillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.BatchOrdersMustHaveAtLeastOneItem); - }); - it('should successfully fill up to specified amount when all orders are fully funded', async () => { - const txHash = await contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - fillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex); - const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync( - anotherOrderHashHex, - ); - expect(filledAmount).to.be.bignumber.equal(fillableAmount); - const remainingFillAmount = fillableAmount.minus(1); - expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount); - }); - it('should successfully fill up to specified amount and leave the rest of the orders untouched', async () => { - const txHash = await contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex); - const zeroAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(anotherOrderHashHex); - expect(filledAmount).to.be.bignumber.equal(fillableAmount); - expect(zeroAmount).to.be.bignumber.equal(0); - }); - it('should successfully fill up to specified amount even if filling all orders would fail', async () => { - const missingBalance = new BigNumber(1); // User will still have enough balance to fill up to 9, - // but won't have 10 to fully fill all orders in a batch. - await contractWrappers.token.transferAsync( - makerTokenAddress, - makerAddress, - coinbase, - missingBalance, - ); - const txHash = await contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - fillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const filledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync(signedOrderHashHex); - const anotherFilledAmount = await contractWrappers.exchange.getFilledTakerAmountAsync( - anotherOrderHashHex, - ); - expect(filledAmount).to.be.bignumber.equal(fillableAmount); - const remainingFillAmount = fillableAmount.minus(1); - expect(anotherFilledAmount).to.be.bignumber.equal(remainingFillAmount); - }); - }); - describe('failed batch fills', () => { - it("should fail validation if user doesn't have enough balance without fill up to", async () => { - const missingBalance = new BigNumber(2); // User will only have enough balance to fill up to 8 - await contractWrappers.token.transferAsync( - makerTokenAddress, - makerAddress, - coinbase, - missingBalance, - ); - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - fillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.InsufficientMakerBalance); - }); - }); - describe('order transaction options', () => { - const emptyFillUpToAmount = new BigNumber(0); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - emptyFillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - emptyFillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: true, - }, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.fillOrdersUpToAsync( - signedOrders, - emptyFillUpToAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - { - shouldValidate: false, - }, - ), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); }); }); }); describe('cancel order(s)', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let coinbase: string; - let makerAddress: string; - let takerAddress: string; - const fillableAmount = new BigNumber(5); - let signedOrder: SignedOrder; - let orderHashHex: string; - const cancelAmount = new BigNumber(3); - beforeEach(async () => { - [coinbase, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - orderHashHex = getOrderHashHex(signedOrder); - }); describe('#cancelOrderAsync', () => { - describe('successful cancels', () => { - it('should cancel an order', async () => { - const txHash = await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelAmount); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const cancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHashHex); - expect(cancelledAmount).to.be.bignumber.equal(cancelAmount); - }); - }); - describe('order transaction options', () => { - const emptyCancelTakerTokenAmount = new BigNumber(0); - it('should validate when orderTransactionOptions are not present', async () => { - return expect( - contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { - shouldValidate: true, - }), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.cancelOrderAsync(signedOrder, emptyCancelTakerTokenAmount, { - shouldValidate: false, - }), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); + it('should cancel a valid order', async () => { + txHash = await contractWrappers.exchange.cancelOrderAsync(signedOrder); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); }); }); describe('#batchCancelOrdersAsync', () => { - let anotherSignedOrder: SignedOrder; - let anotherOrderHashHex: string; - let cancelBatch: OrderCancellationRequest[]; - beforeEach(async () => { - anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, + it('should cancel a batch of valid orders', async () => { + const orders = [signedOrder, anotherSignedOrder]; + txHash = await contractWrappers.exchange.batchCancelOrdersAsync(orders); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + }); + }); + describe('#cancelOrdersUpTo/getOrderEpochAsync', () => { + it('should cancel orders up to target order epoch', async () => { + const targetOrderEpoch = new BigNumber(42); + txHash = await contractWrappers.exchange.cancelOrdersUpToAsync(targetOrderEpoch, makerAddress); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const orderEpoch = await contractWrappers.exchange.getOrderEpochAsync( makerAddress, - takerAddress, - fillableAmount, + constants.NULL_ADDRESS, ); - anotherOrderHashHex = getOrderHashHex(anotherSignedOrder); - cancelBatch = [ - { - order: signedOrder, - takerTokenCancelAmount: cancelAmount, - }, - { - order: anotherSignedOrder, - takerTokenCancelAmount: cancelAmount, - }, - ]; - }); - describe('failed batch cancels', () => { - it('should throw when orders have different makers', async () => { - const signedOrderWithDifferentMaker = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - takerAddress, - takerAddress, - fillableAmount, - ); - return expect( - contractWrappers.exchange.batchCancelOrdersAsync([ - cancelBatch[0], - { - order: signedOrderWithDifferentMaker, - takerTokenCancelAmount: cancelAmount, - }, - ]), - ).to.be.rejectedWith(ExchangeContractErrs.MultipleMakersInSingleCancelBatchDisallowed); - }); - }); - describe('successful batch cancels', () => { - it('should cancel a batch of orders', async () => { - await contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch); - const cancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHashHex); - const anotherCancelledAmount = await contractWrappers.exchange.getCancelledTakerAmountAsync( - anotherOrderHashHex, - ); - expect(cancelledAmount).to.be.bignumber.equal(cancelAmount); - expect(anotherCancelledAmount).to.be.bignumber.equal(cancelAmount); - }); - }); - describe('order transaction options', () => { - beforeEach(async () => { - const emptyTakerTokenCancelAmount = new BigNumber(0); - cancelBatch = [ - { - order: signedOrder, - takerTokenCancelAmount: emptyTakerTokenCancelAmount, - }, - { - order: anotherSignedOrder, - takerTokenCancelAmount: emptyTakerTokenCancelAmount, - }, - ]; - }); - it('should validate when orderTransactionOptions are not present', async () => { - return expect(contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch)).to.be.rejectedWith( - ExchangeContractErrs.OrderCancelAmountZero, - ); - }); - it('should validate when orderTransactionOptions specify to validate', async () => { - return expect( - contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch, { - shouldValidate: true, - }), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - it('should not validate when orderTransactionOptions specify not to validate', async () => { - return expect( - contractWrappers.exchange.batchCancelOrdersAsync(cancelBatch, { - shouldValidate: false, - }), - ).to.not.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); + expect(orderEpoch).to.be.bignumber.equal(targetOrderEpoch.plus(1)); }); }); }); - describe('tests that require partially filled order', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let takerAddress: string; - let fillableAmount: BigNumber; - let partialFillAmount: BigNumber; - let signedOrder: SignedOrder; - let orderHash: string; - before(() => { - takerAddress = userAddresses[1]; - tokenUtils = new TokenUtils(tokens); - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; + describe('#getZRXAssetData', () => { + it('should get the asset data', async () => { + const ZRX_ASSET_DATA = await contractWrappers.exchange.getZRXAssetDataAsync(); + const ASSET_DATA_HEX_LENGTH = 74; + expect(ZRX_ASSET_DATA).to.have.length(ASSET_DATA_HEX_LENGTH); }); - beforeEach(async () => { - fillableAmount = new BigNumber(5); - partialFillAmount = new BigNumber(2); - signedOrder = await fillScenarios.createPartiallyFilledSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - takerAddress, - fillableAmount, - partialFillAmount, + }); + describe('#getOrderInfoAsync', () => { + it('should get the order info', async () => { + const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder); + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + expect(orderInfo.orderHash).to.be.equal(orderHash); + }); + }); + describe('#isValidSignature', () => { + it('should check if the signature is valid', async () => { + const orderHash = orderHashUtils.getOrderHashHex(signedOrder); + let isValid = await contractWrappers.exchange.isValidSignatureAsync( + orderHash, + signedOrder.makerAddress, + signedOrder.signature, + ); + expect(isValid).to.be.true(); + isValid = await contractWrappers.exchange.isValidSignatureAsync( + orderHash, + signedOrder.takerAddress, + signedOrder.signature, ); - orderHash = getOrderHashHex(signedOrder); + expect(isValid).to.be.false(); }); - describe('#getUnavailableTakerAmountAsync', () => { - it('should throw if passed an invalid orderHash', async () => { - const invalidOrderHashHex = '0x123'; - return expect( - contractWrappers.exchange.getUnavailableTakerAmountAsync(invalidOrderHashHex), - ).to.be.rejected(); - }); - it('should return zero if passed a valid but non-existent orderHash', async () => { - const unavailableValueT = await contractWrappers.exchange.getUnavailableTakerAmountAsync( - NON_EXISTENT_ORDER_HASH, - ); - expect(unavailableValueT).to.be.bignumber.equal(0); - }); - it('should return the unavailableValueT for a valid and partially filled orderHash', async () => { - const unavailableValueT = await contractWrappers.exchange.getUnavailableTakerAmountAsync(orderHash); - expect(unavailableValueT).to.be.bignumber.equal(partialFillAmount); - }); + }); + describe('#isAllowedValidatorAsync', () => { + it('should check if the validator is alllowed', async () => { + const signerAddress = makerAddress; + const validatorAddress = constants.NULL_ADDRESS; + const isAllowed = await contractWrappers.exchange.isAllowedValidatorAsync(signerAddress, validatorAddress); + expect(isAllowed).to.be.false(); }); - describe('#getFilledTakerAmountAsync', () => { - it('should throw if passed an invalid orderHash', async () => { - const invalidOrderHashHex = '0x123'; - return expect( - contractWrappers.exchange.getFilledTakerAmountAsync(invalidOrderHashHex), - ).to.be.rejected(); - }); - it('should return zero if passed a valid but non-existent orderHash', async () => { - const filledValueT = await contractWrappers.exchange.getFilledTakerAmountAsync(NON_EXISTENT_ORDER_HASH); - expect(filledValueT).to.be.bignumber.equal(0); - }); - it('should return the filledValueT for a valid and partially filled orderHash', async () => { - const filledValueT = await contractWrappers.exchange.getFilledTakerAmountAsync(orderHash); - expect(filledValueT).to.be.bignumber.equal(partialFillAmount); - }); + }); + describe('#setSignatureValidatorApproval', () => { + it('should set signature validator approval', async () => { + const validatorAddress = constants.NULL_ADDRESS; + const isApproved = true; + const senderAddress = makerAddress; + txHash = await contractWrappers.exchange.setSignatureValidatorApprovalAsync( + validatorAddress, + isApproved, + senderAddress, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); }); - describe('#getCancelledTakerAmountAsync', () => { - it('should throw if passed an invalid orderHash', async () => { - const invalidOrderHashHex = '0x123'; - return expect( - contractWrappers.exchange.getCancelledTakerAmountAsync(invalidOrderHashHex), - ).to.be.rejected(); - }); - it('should return zero if passed a valid but non-existent orderHash', async () => { - const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync( - NON_EXISTENT_ORDER_HASH, - ); - expect(cancelledValueT).to.be.bignumber.equal(0); - }); - it('should return the cancelledValueT for a valid and partially filled orderHash', async () => { - const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHash); - expect(cancelledValueT).to.be.bignumber.equal(0); - }); - it('should return the cancelledValueT for a valid and cancelled orderHash', async () => { - const cancelAmount = fillableAmount.minus(partialFillAmount); - await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelAmount); - const cancelledValueT = await contractWrappers.exchange.getCancelledTakerAmountAsync(orderHash); - expect(cancelledValueT).to.be.bignumber.equal(cancelAmount); - }); + }); + describe('#isTransactionExecutedAsync', () => { + it('should check if the transaction is executed', async () => { + const transactionHash = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const isExecuted = await contractWrappers.exchange.isTransactionExecutedAsync(transactionHash); + expect(isExecuted).to.be.false(); + }); + }); + describe('#getAssetProxyBySignatureAsync', () => { + it('should fill or kill a valid order', async () => { + const erc20ProxyId = await contractWrappers.erc20Proxy.getProxyIdAsync(); + const erc20ProxyAddressById = await contractWrappers.exchange.getAssetProxyBySignatureAsync(erc20ProxyId); + const erc20ProxyAddress = contractWrappers.erc20Proxy.getContractAddress(); + expect(erc20ProxyAddressById).to.be.equal(erc20ProxyAddress); + const erc721ProxyId = await contractWrappers.erc721Proxy.getProxyIdAsync(); + const erc721ProxyAddressById = await contractWrappers.exchange.getAssetProxyBySignatureAsync(erc721ProxyId); + const erc721ProxyAddress = contractWrappers.erc721Proxy.getContractAddress(); + expect(erc721ProxyAddressById).to.be.equal(erc721ProxyAddress); + }); + }); + describe('#preSignAsync/isPreSignedAsync', () => { + it('should preSign the hash', async () => { + const senderAddress = takerAddress; + const hash = orderHashUtils.getOrderHashHex(signedOrder); + const signerAddress = signedOrder.makerAddress; + let isPreSigned = await contractWrappers.exchange.isPreSignedAsync(hash, signerAddress); + expect(isPreSigned).to.be.false(); + txHash = await contractWrappers.exchange.preSignAsync( + hash, + signerAddress, + signedOrder.signature, + senderAddress, + ); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + isPreSigned = await contractWrappers.exchange.isPreSignedAsync(hash, signerAddress); + expect(isPreSigned).to.be.true(); + }); + }); + describe('#getVersionAsync', () => { + it('should return version the hash', async () => { + const version = await contractWrappers.exchange.getVersionAsync(); + const VERSION = '2.0.1-alpha'; + expect(version).to.be.equal(VERSION); }); }); describe('#subscribe', () => { const indexFilterValues = {}; - const shouldThrowOnInsufficientBalanceOrAllowance = true; - let makerTokenAddress: string; - let takerTokenAddress: string; - let coinbase: string; - let takerAddress: string; - let makerAddress: string; - let fillableAmount: BigNumber; - let signedOrder: SignedOrder; const takerTokenFillAmountInBaseUnits = new BigNumber(1); - const cancelTakerAmountInBaseUnits = new BigNumber(1); - before(() => { - [coinbase, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); beforeEach(async () => { - fillableAmount = new BigNumber(5); signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, + makerAssetData, + takerAssetData, makerAddress, takerAddress, fillableAmount, @@ -969,18 +379,17 @@ describe('ExchangeWrapper', () => { // we do need both. A hack is to make the top-level a sync fn w/ a done callback and then // wrap the rest of the test in an async block // Source: https://github.com/mochajs/mocha/issues/2407 - it('Should receive the LogFill event when an order is filled', (done: DoneCallback) => { + it('Should receive the Fill event when an order is filled', (done: DoneCallback) => { (async () => { const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); + (logEvent: DecodedLogEvent<ExchangeFillEventArgs>) => { + expect(logEvent.log.event).to.be.equal(ExchangeEvents.Fill); }, ); - contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback); + contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callback); await contractWrappers.exchange.fillOrderAsync( signedOrder, takerTokenFillAmountInBaseUnits, - shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, ); })().catch(done); @@ -988,35 +397,34 @@ describe('ExchangeWrapper', () => { it('Should receive the LogCancel event when an order is cancelled', (done: DoneCallback) => { (async () => { const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<LogCancelContractEventArgs>) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogCancel); + (logEvent: DecodedLogEvent<ExchangeCancelEventArgs>) => { + expect(logEvent.log.event).to.be.equal(ExchangeEvents.Cancel); }, ); - contractWrappers.exchange.subscribe(ExchangeEvents.LogCancel, indexFilterValues, callback); - await contractWrappers.exchange.cancelOrderAsync(signedOrder, cancelTakerAmountInBaseUnits); + contractWrappers.exchange.subscribe(ExchangeEvents.Cancel, indexFilterValues, callback); + await contractWrappers.exchange.cancelOrderAsync(signedOrder); })().catch(done); }); it('Outstanding subscriptions are cancelled when contractWrappers.setProvider called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (_logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { + (logEvent: DecodedLogEvent<ExchangeFillEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }, ); - contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callbackNeverToBeCalled); + contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callbackNeverToBeCalled); contractWrappers.setProvider(provider, constants.TESTRPC_NETWORK_ID); const callback = callbackErrorReporter.reportNodeCallbackErrors(done)( - (logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { - expect(logEvent.log.event).to.be.equal(ExchangeEvents.LogFill); + (logEvent: DecodedLogEvent<ExchangeFillEventArgs>) => { + expect(logEvent.log.event).to.be.equal(ExchangeEvents.Fill); }, ); - contractWrappers.exchange.subscribe(ExchangeEvents.LogFill, indexFilterValues, callback); + contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callback); await contractWrappers.exchange.fillOrderAsync( signedOrder, takerTokenFillAmountInBaseUnits, - shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, ); })().catch(done); @@ -1024,12 +432,12 @@ describe('ExchangeWrapper', () => { it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => { (async () => { const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)( - (_logEvent: DecodedLogEvent<LogFillContractEventArgs>) => { + (_logEvent: DecodedLogEvent<ExchangeFillEventArgs>) => { done(new Error('Expected this subscription to have been cancelled')); }, ); const subscriptionToken = contractWrappers.exchange.subscribe( - ExchangeEvents.LogFill, + ExchangeEvents.Fill, indexFilterValues, callbackNeverToBeCalled, ); @@ -1037,102 +445,36 @@ describe('ExchangeWrapper', () => { await contractWrappers.exchange.fillOrderAsync( signedOrder, takerTokenFillAmountInBaseUnits, - shouldThrowOnInsufficientBalanceOrAllowance, takerAddress, ); done(); })().catch(done); }); }); - describe('#getOrderHashHexUsingContractCallAsync', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let makerAddress: string; - let takerAddress: string; - const fillableAmount = new BigNumber(5); - before(async () => { - [, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - it("get's the same hash as the local function", async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const orderHash = getOrderHashHex(signedOrder); - const orderHashFromContract = await (contractWrappers.exchange as any)._getOrderHashHexUsingContractCallAsync( - signedOrder, - ); - expect(orderHash).to.equal(orderHashFromContract); - }); - }); describe('#getZRXTokenAddressAsync', () => { it('gets the same token as is in token registry', () => { - const zrxAddress = contractWrappers.exchange.getZRXTokenAddress(); - const zrxToken = tokenUtils.getProtocolTokenOrThrow(); - expect(zrxAddress).to.equal(zrxToken.address); + const zrxAddressFromExchangeWrapper = contractWrappers.exchange.getZRXTokenAddress(); + const zrxAddress = tokenUtils.getProtocolTokenAddress(); + expect(zrxAddressFromExchangeWrapper).to.equal(zrxAddress); }); }); describe('#getLogsAsync', () => { - let makerTokenAddress: string; - let takerTokenAddress: string; - let makerAddress: string; - let takerAddress: string; - const fillableAmount = new BigNumber(5); - const shouldThrowOnInsufficientBalanceOrAllowance = true; - const blockRange: BlockRange = { + const blockRange = { fromBlock: 0, toBlock: BlockParamLiteral.Latest, }; - let txHash: string; - before(async () => { - [, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - it('should get logs with decoded args emitted by LogFill', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const eventName = ExchangeEvents.LogFill; + it('should get logs with decoded args emitted by Fill', async () => { + txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress); + const eventName = ExchangeEvents.Fill; const indexFilterValues = {}; const logs = await contractWrappers.exchange.getLogsAsync(eventName, blockRange, indexFilterValues); expect(logs).to.have.length(1); expect(logs[0].event).to.be.equal(eventName); }); it('should only get the logs with the correct event name', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - const differentEventName = ExchangeEvents.LogCancel; + txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const differentEventName = ExchangeEvents.Cancel; const indexFilterValues = {}; const logs = await contractWrappers.exchange.getLogsAsync( differentEventName, @@ -1142,86 +484,34 @@ describe('ExchangeWrapper', () => { expect(logs).to.have.length(0); }); it('should only get the logs with the correct indexed fields', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - txHash = await contractWrappers.exchange.fillOrderAsync( - signedOrder, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, - takerAddress, - ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); - - const differentMakerAddress = userAddresses[2]; - const anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - differentMakerAddress, + txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); + const signedOrderWithAnotherMakerAddress = await fillScenarios.createFillableSignedOrderAsync( + makerAssetData, + takerAssetData, + anotherMakerAddress, takerAddress, fillableAmount, ); txHash = await contractWrappers.exchange.fillOrderAsync( - anotherSignedOrder, - fillableAmount, - shouldThrowOnInsufficientBalanceOrAllowance, + signedOrderWithAnotherMakerAddress, + takerTokenFillAmount, takerAddress, ); - await web3Wrapper.awaitTransactionSuccessAsync(txHash); + await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS); - const eventName = ExchangeEvents.LogFill; + const eventName = ExchangeEvents.Fill; const indexFilterValues = { - maker: differentMakerAddress, + makerAddress: anotherMakerAddress, }; - const logs = await contractWrappers.exchange.getLogsAsync<LogFillContractEventArgs>( + const logs = await contractWrappers.exchange.getLogsAsync<ExchangeFillEventArgs>( eventName, blockRange, indexFilterValues, ); expect(logs).to.have.length(1); const args = logs[0].args; - expect(args.maker).to.be.equal(differentMakerAddress); - }); - }); - describe('#getOrderStateAsync', () => { - let maker: string; - let taker: string; - let makerToken: Token; - let takerToken: Token; - let signedOrder: SignedOrder; - let orderState: OrderState; - const fillableAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), constants.ZRX_DECIMALS); - before(async () => { - [, maker, taker] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - [makerToken, takerToken] = tokenUtils.getDummyTokens(); - }); - it('should report orderStateValid when order is fillable', async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, - takerToken.address, - maker, - taker, - fillableAmount, - ); - orderState = await contractWrappers.exchange.getOrderStateAsync(signedOrder); - expect(orderState.isValid).to.be.true(); - }); - it('should report orderStateInvalid when maker allowance set to 0', async () => { - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerToken.address, - takerToken.address, - maker, - taker, - fillableAmount, - ); - await contractWrappers.token.setProxyAllowanceAsync(makerToken.address, maker, new BigNumber(0)); - orderState = await contractWrappers.exchange.getOrderStateAsync(signedOrder); - expect(orderState.isValid).to.be.false(); + expect(args.makerAddress).to.be.equal(anotherMakerAddress); }); }); }); // tslint:disable:max-file-line-count diff --git a/packages/contract-wrappers/test/global_hooks.ts b/packages/contract-wrappers/test/global_hooks.ts index 52a384ada..bf80e0908 100644 --- a/packages/contract-wrappers/test/global_hooks.ts +++ b/packages/contract-wrappers/test/global_hooks.ts @@ -1,5 +1,5 @@ import { devConstants } from '@0xproject/dev-utils'; -import { runV1MigrationsAsync } from '@0xproject/migrations'; +import { runV2MigrationsAsync } from '@0xproject/migrations'; import { provider } from './utils/web3_wrapper'; @@ -12,6 +12,6 @@ before('migrate contracts', async function(): Promise<void> { gas: devConstants.GAS_LIMIT, from: devConstants.TESTRPC_FIRST_ADDRESS, }; - const artifactsDir = `../migrations/artifacts/1.0.0`; - await runV1MigrationsAsync(provider, artifactsDir, txDefaults); + const artifactsDir = `../migrations/artifacts/2.0.0`; + await runV2MigrationsAsync(provider, artifactsDir, txDefaults); }); diff --git a/packages/contract-wrappers/test/order_validation_test.ts b/packages/contract-wrappers/test/order_validation_test.ts deleted file mode 100644 index 2afea2d5f..000000000 --- a/packages/contract-wrappers/test/order_validation_test.ts +++ /dev/null @@ -1,539 +0,0 @@ -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { FillScenarios } from '@0xproject/fill-scenarios'; -import { OrderError } from '@0xproject/order-utils'; -import { BlockParamLiteral } from '@0xproject/types'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import * as Sinon from 'sinon'; - -import { ContractWrappers, ExchangeContractErrs, SignedOrder, Token } from '../src'; -import { BalanceAndProxyAllowanceLazyStore } from '../src/stores/balance_proxy_allowance_lazy_store'; -import { TradeSide, TransferType } from '../src/types'; -import { ExchangeTransferSimulator } from '../src/utils/exchange_transfer_simulator'; -import { OrderValidationUtils } from '../src/utils/order_validation_utils'; - -import { chaiSetup } from './utils/chai_setup'; -import { constants } from './utils/constants'; -import { TokenUtils } from './utils/token_utils'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('OrderValidation', () => { - let contractWrappers: ContractWrappers; - let userAddresses: string[]; - let tokens: Token[]; - let tokenUtils: TokenUtils; - let exchangeContractAddress: string; - let zrxTokenAddress: string; - let fillScenarios: FillScenarios; - let makerTokenAddress: string; - let takerTokenAddress: string; - let coinbase: string; - let makerAddress: string; - let takerAddress: string; - let feeRecipient: string; - const fillableAmount = new BigNumber(5); - const fillTakerAmount = new BigNumber(5); - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - before(async () => { - contractWrappers = new ContractWrappers(provider, config); - exchangeContractAddress = contractWrappers.exchange.getContractAddress(); - userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - [coinbase, makerAddress, takerAddress, feeRecipient] = userAddresses; - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - tokenUtils = new TokenUtils(tokens); - zrxTokenAddress = tokenUtils.getProtocolTokenOrThrow().address; - fillScenarios = new FillScenarios(provider, userAddresses, tokens, zrxTokenAddress, exchangeContractAddress); - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('validateOrderFillableOrThrowAsync', () => { - it('should succeed if the order is fillable', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder); - }); - it('should succeed if the maker is buying ZRX and has no ZRX balance', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - zrxTokenAddress, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - feeRecipient, - ); - const zrxMakerBalance = await contractWrappers.token.getBalanceAsync(zrxTokenAddress, makerAddress); - await contractWrappers.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance); - await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder); - }); - it('should succeed if the maker is buying ZRX and has no ZRX balance and there is no specified taker', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - zrxTokenAddress, - makerFee, - takerFee, - makerAddress, - constants.NULL_ADDRESS, - fillableAmount, - feeRecipient, - ); - const zrxMakerBalance = await contractWrappers.token.getBalanceAsync(zrxTokenAddress, makerAddress); - await contractWrappers.token.transferAsync(zrxTokenAddress, makerAddress, takerAddress, zrxMakerBalance); - await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder); - }); - it('should succeed if the order is asymmetric and fillable', async () => { - const makerFillableAmount = fillableAmount; - // tslint:disable-next-line:custom-no-magic-numbers - const takerFillableAmount = fillableAmount.minus(4); - const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - makerFillableAmount, - takerFillableAmount, - ); - await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder); - }); - it('should throw when the order is fully filled or cancelled', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect(contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith( - ExchangeContractErrs.OrderRemainingFillAmountZero, - ); - }); - it('should throw when order is expired', async () => { - const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - expirationInPast, - ); - return expect(contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder)).to.be.rejectedWith( - ExchangeContractErrs.OrderFillExpired, - ); - }); - }); - describe('validateFillOrderAndThrowIfInvalidAsync', () => { - it('should throw when the fill amount is zero', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const zeroFillAmount = new BigNumber(0); - return expect( - contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - zeroFillAmount, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillAmountZero); - }); - it('should throw when the signature is invalid', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - // 27 <--> 28 - // tslint:disable-next-line:custom-no-magic-numbers - signedOrder.ecSignature.v = 28 - signedOrder.ecSignature.v + 27; - return expect( - contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillableAmount, - takerAddress, - ), - ).to.be.rejectedWith(OrderError.InvalidSignature); - }); - it('should throw when the order is fully filled or cancelled', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect( - contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillableAmount, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderRemainingFillAmountZero); - }); - it('should throw when sender is not a taker', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - // tslint:disable-next-line:custom-no-magic-numbers - const nonTakerAddress = userAddresses[6]; - return expect( - contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillTakerAmount, - nonTakerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.TransactionSenderIsNotFillOrderTaker); - }); - it('should throw when order is expired', async () => { - const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - expirationInPast, - ); - return expect( - contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillTakerAmount, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillExpired); - }); - it('should throw when there a rounding error would have occurred', async () => { - const makerAmount = new BigNumber(3); - const takerAmount = new BigNumber(5); - const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - makerAmount, - takerAmount, - ); - const fillTakerAmountThatCausesRoundingError = new BigNumber(3); - return expect( - contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync( - signedOrder, - fillTakerAmountThatCausesRoundingError, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.OrderFillRoundingError); - }); - }); - describe('#validateFillOrKillOrderAndThrowIfInvalidAsync', () => { - it('should throw if remaining fillAmount is less then the desired fillAmount', async () => { - const signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - const tooLargeFillAmount = new BigNumber(7); - const fillAmountDifference = tooLargeFillAmount.minus(fillableAmount); - await contractWrappers.token.transferAsync(takerTokenAddress, coinbase, takerAddress, fillAmountDifference); - await contractWrappers.token.setProxyAllowanceAsync(takerTokenAddress, takerAddress, tooLargeFillAmount); - await contractWrappers.token.transferAsync(makerTokenAddress, coinbase, makerAddress, fillAmountDifference); - await contractWrappers.token.setProxyAllowanceAsync(makerTokenAddress, makerAddress, tooLargeFillAmount); - - return expect( - contractWrappers.exchange.validateFillOrKillOrderThrowIfInvalidAsync( - signedOrder, - tooLargeFillAmount, - takerAddress, - ), - ).to.be.rejectedWith(ExchangeContractErrs.InsufficientRemainingFillAmount); - }); - }); - describe('validateCancelOrderAndThrowIfInvalidAsync', () => { - let signedOrder: SignedOrder; - const cancelAmount = new BigNumber(3); - beforeEach(async () => { - [coinbase, makerAddress, takerAddress] = userAddresses; - const [makerToken, takerToken] = tokenUtils.getDummyTokens(); - makerTokenAddress = makerToken.address; - takerTokenAddress = takerToken.address; - signedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - ); - }); - it('should throw when cancel amount is zero', async () => { - const zeroCancelAmount = new BigNumber(0); - return expect( - contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, zeroCancelAmount), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelAmountZero); - }); - it('should throw when order is expired', async () => { - const expirationInPast = new BigNumber(1496826058); // 7th Jun 2017 - const expiredSignedOrder = await fillScenarios.createFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - fillableAmount, - expirationInPast, - ); - return expect( - contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(expiredSignedOrder, cancelAmount), - ).to.be.rejectedWith(ExchangeContractErrs.OrderCancelExpired); - }); - it('should throw when order is already cancelled or filled', async () => { - await contractWrappers.exchange.cancelOrderAsync(signedOrder, fillableAmount); - return expect( - contractWrappers.exchange.validateCancelOrderThrowIfInvalidAsync(signedOrder, fillableAmount), - ).to.be.rejectedWith(ExchangeContractErrs.OrderAlreadyCancelledOrFilled); - }); - }); - describe('#validateFillOrderBalancesAllowancesThrowIfInvalidAsync', () => { - let exchangeTransferSimulator: ExchangeTransferSimulator; - let transferFromAsync: Sinon.SinonSpy; - const bigNumberMatch = (expected: BigNumber) => { - return Sinon.match((value: BigNumber) => value.eq(expected)); - }; - beforeEach('create exchangeTransferSimulator', async () => { - const balanceAndProxyAllowanceLazyStore = new BalanceAndProxyAllowanceLazyStore( - contractWrappers.token, - BlockParamLiteral.Latest, - ); - exchangeTransferSimulator = new ExchangeTransferSimulator(balanceAndProxyAllowanceLazyStore); - transferFromAsync = Sinon.spy(); - exchangeTransferSimulator.transferFromAsync = transferFromAsync as any; - }); - it('should call exchangeTransferSimulator.transferFrom in a correct order', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - takerTokenAddress, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - feeRecipient, - ); - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, - signedOrder, - fillableAmount, - takerAddress, - zrxTokenAddress, - ); - // tslint:disable-next-line:custom-no-magic-numbers - expect(transferFromAsync.callCount).to.be.equal(4); - expect( - transferFromAsync - .getCall(0) - .calledWith( - makerTokenAddress, - makerAddress, - takerAddress, - bigNumberMatch(fillableAmount), - TradeSide.Maker, - TransferType.Trade, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(1) - .calledWith( - takerTokenAddress, - takerAddress, - makerAddress, - bigNumberMatch(fillableAmount), - TradeSide.Taker, - TransferType.Trade, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(2) - .calledWith( - zrxTokenAddress, - makerAddress, - feeRecipient, - bigNumberMatch(makerFee), - TradeSide.Maker, - TransferType.Fee, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(3) - .calledWith( - zrxTokenAddress, - takerAddress, - feeRecipient, - bigNumberMatch(takerFee), - TradeSide.Taker, - TransferType.Fee, - ), - ).to.be.true(); - }); - it('should call exchangeTransferSimulator.transferFrom with correct values for an open order', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(2); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - takerTokenAddress, - makerFee, - takerFee, - makerAddress, - constants.NULL_ADDRESS, - fillableAmount, - feeRecipient, - ); - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, - signedOrder, - fillableAmount, - takerAddress, - zrxTokenAddress, - ); - // tslint:disable-next-line:custom-no-magic-numbers - expect(transferFromAsync.callCount).to.be.equal(4); - expect( - transferFromAsync - .getCall(0) - .calledWith( - makerTokenAddress, - makerAddress, - takerAddress, - bigNumberMatch(fillableAmount), - TradeSide.Maker, - TransferType.Trade, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(1) - .calledWith( - takerTokenAddress, - takerAddress, - makerAddress, - bigNumberMatch(fillableAmount), - TradeSide.Taker, - TransferType.Trade, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(2) - .calledWith( - zrxTokenAddress, - makerAddress, - feeRecipient, - bigNumberMatch(makerFee), - TradeSide.Maker, - TransferType.Fee, - ), - ).to.be.true(); - expect( - transferFromAsync - .getCall(3) - .calledWith( - zrxTokenAddress, - takerAddress, - feeRecipient, - bigNumberMatch(takerFee), - TradeSide.Taker, - TransferType.Fee, - ), - ).to.be.true(); - }); - it('should correctly round the fillMakerTokenAmount', async () => { - const makerTokenAmount = new BigNumber(3); - const takerTokenAmount = new BigNumber(1); - const signedOrder = await fillScenarios.createAsymmetricFillableSignedOrderAsync( - makerTokenAddress, - takerTokenAddress, - makerAddress, - takerAddress, - makerTokenAmount, - takerTokenAmount, - ); - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, - signedOrder, - takerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - // tslint:disable-next-line:custom-no-magic-numbers - expect(transferFromAsync.callCount).to.be.equal(4); - const makerFillAmount = transferFromAsync.getCall(0).args[3]; - expect(makerFillAmount).to.be.bignumber.equal(makerTokenAmount); - }); - it('should correctly round the makerFeeAmount', async () => { - const makerFee = new BigNumber(2); - const takerFee = new BigNumber(4); - const signedOrder = await fillScenarios.createFillableSignedOrderWithFeesAsync( - makerTokenAddress, - takerTokenAddress, - makerFee, - takerFee, - makerAddress, - takerAddress, - fillableAmount, - constants.NULL_ADDRESS, - ); - const fillTakerTokenAmount = fillableAmount.div(2).round(0); - await OrderValidationUtils.validateFillOrderBalancesAllowancesThrowIfInvalidAsync( - exchangeTransferSimulator, - signedOrder, - fillTakerTokenAmount, - takerAddress, - zrxTokenAddress, - ); - const makerPartialFee = makerFee.div(2); - const takerPartialFee = takerFee.div(2); - // tslint:disable-next-line:custom-no-magic-numbers - expect(transferFromAsync.callCount).to.be.equal(4); - const partialMakerFee = transferFromAsync.getCall(2).args[3]; - expect(partialMakerFee).to.be.bignumber.equal(makerPartialFee); - const partialTakerFee = transferFromAsync.getCall(3).args[3]; - expect(partialTakerFee).to.be.bignumber.equal(takerPartialFee); - }); - }); -}); // tslint:disable-line:max-file-line-count diff --git a/packages/contract-wrappers/test/subscription_test.ts b/packages/contract-wrappers/test/subscription_test.ts index b9417ca3d..adda4ab78 100644 --- a/packages/contract-wrappers/test/subscription_test.ts +++ b/packages/contract-wrappers/test/subscription_test.ts @@ -5,10 +5,11 @@ import * as _ from 'lodash'; import 'mocha'; import * as Sinon from 'sinon'; -import { ApprovalContractEventArgs, ContractWrappers, DecodedLogEvent, Token, TokenEvents } from '../src'; +import { ContractWrappers, DecodedLogEvent, ERC20TokenApprovalEventArgs, ERC20TokenEvents, Token } from '../src'; import { chaiSetup } from './utils/chai_setup'; import { constants } from './utils/constants'; +import { tokenUtils } from './utils/token_utils'; import { provider, web3Wrapper } from './utils/web3_wrapper'; chaiSetup.configure(); @@ -17,7 +18,6 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); describe('SubscriptionTest', () => { let contractWrappers: ContractWrappers; let userAddresses: string[]; - let tokens: Token[]; let coinbase: string; let addressWithoutFunds: string; const config = { @@ -26,7 +26,6 @@ describe('SubscriptionTest', () => { before(async () => { contractWrappers = new ContractWrappers(provider, config); userAddresses = await web3Wrapper.getAvailableAddressesAsync(); - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); coinbase = userAddresses[0]; addressWithoutFunds = userAddresses[1]; }); @@ -42,11 +41,11 @@ describe('SubscriptionTest', () => { const allowanceAmount = new BigNumber(42); let stubs: Sinon.SinonStub[] = []; before(() => { - const token = tokens[0]; - tokenAddress = token.address; + const tokenAddresses = tokenUtils.getDummyERC20TokenAddresses(); + tokenAddress = tokenAddresses[0]; }); afterEach(() => { - contractWrappers.token.unsubscribeAll(); + contractWrappers.erc20Token.unsubscribeAll(); _.each(stubs, s => s.restore()); stubs = []; }); @@ -55,8 +54,13 @@ describe('SubscriptionTest', () => { const errMsg = 'Error fetching block'; const callback = callbackErrorReporter.assertNodeCallbackError(done, errMsg); stubs = [Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockAsync').throws(new Error(errMsg))]; - contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); - await contractWrappers.token.setAllowanceAsync( + contractWrappers.erc20Token.subscribe( + tokenAddress, + ERC20TokenEvents.Approval, + indexFilterValues, + callback, + ); + await contractWrappers.erc20Token.setAllowanceAsync( tokenAddress, coinbase, addressWithoutFunds, @@ -69,8 +73,13 @@ describe('SubscriptionTest', () => { const errMsg = 'Error fetching logs'; const callback = callbackErrorReporter.assertNodeCallbackError(done, errMsg); stubs = [Sinon.stub((contractWrappers as any)._web3Wrapper, 'getLogsAsync').throws(new Error(errMsg))]; - contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); - await contractWrappers.token.setAllowanceAsync( + contractWrappers.erc20Token.subscribe( + tokenAddress, + ERC20TokenEvents.Approval, + indexFilterValues, + callback, + ); + await contractWrappers.erc20Token.setAllowanceAsync( tokenAddress, coinbase, addressWithoutFunds, @@ -80,14 +89,20 @@ describe('SubscriptionTest', () => { }); it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => { (async () => { - const callback = (_err: Error | null, _logEvent?: DecodedLogEvent<ApprovalContractEventArgs>) => _.noop; - contractWrappers.token.subscribe(tokenAddress, TokenEvents.Approval, indexFilterValues, callback); + const callback = (err: Error | null, _logEvent?: DecodedLogEvent<ERC20TokenApprovalEventArgs>) => + _.noop; + contractWrappers.erc20Token.subscribe( + tokenAddress, + ERC20TokenEvents.Approval, + indexFilterValues, + callback, + ); stubs = [ Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockAsync').throws( new Error('JSON RPC error'), ), ]; - contractWrappers.token.unsubscribeAll(); + contractWrappers.erc20Token.unsubscribeAll(); done(); })().catch(done); }); diff --git a/packages/contract-wrappers/test/token_registry_wrapper_test.ts b/packages/contract-wrappers/test/token_registry_wrapper_test.ts deleted file mode 100644 index 6576d789d..000000000 --- a/packages/contract-wrappers/test/token_registry_wrapper_test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { schemas, SchemaValidator } from '@0xproject/json-schemas'; -import * as chai from 'chai'; -import * as _ from 'lodash'; -import 'mocha'; - -import { ContractWrappers, Token } from '../src'; - -import { chaiSetup } from './utils/chai_setup'; -import { constants } from './utils/constants'; -import { provider, web3Wrapper } from './utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -const TOKEN_REGISTRY_SIZE_AFTER_MIGRATION = 7; - -describe('TokenRegistryWrapper', () => { - let contractWrappers: ContractWrappers; - let tokens: Token[]; - const tokenAddressBySymbol: { [symbol: string]: string } = {}; - const tokenAddressByName: { [symbol: string]: string } = {}; - const tokenBySymbol: { [symbol: string]: Token } = {}; - const tokenByName: { [symbol: string]: Token } = {}; - const registeredSymbol = 'ZRX'; - const registeredName = '0x Protocol Token'; - const unregisteredSymbol = 'MAL'; - const unregisteredName = 'Malicious Token'; - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - before(async () => { - contractWrappers = new ContractWrappers(provider, config); - tokens = await contractWrappers.tokenRegistry.getTokensAsync(); - _.map(tokens, token => { - tokenAddressBySymbol[token.symbol] = token.address; - tokenAddressByName[token.name] = token.address; - tokenBySymbol[token.symbol] = token; - tokenByName[token.name] = token; - }); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('#getTokensAsync', () => { - it('should return all the tokens added to the tokenRegistry during the migration', async () => { - expect(tokens).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION); - - const schemaValidator = new SchemaValidator(); - _.each(tokens, token => { - const validationResult = schemaValidator.validate(token, schemas.tokenSchema); - expect(validationResult.errors).to.have.lengthOf(0); - }); - }); - }); - describe('#getTokenAddressesAsync', () => { - it('should return all the token addresses added to the tokenRegistry during the migration', async () => { - const tokenAddresses = await contractWrappers.tokenRegistry.getTokenAddressesAsync(); - expect(tokenAddresses).to.have.lengthOf(TOKEN_REGISTRY_SIZE_AFTER_MIGRATION); - - const schemaValidator = new SchemaValidator(); - _.each(tokenAddresses, tokenAddress => { - const validationResult = schemaValidator.validate(tokenAddress, schemas.addressSchema); - expect(validationResult.errors).to.have.lengthOf(0); - expect(tokenAddress).to.not.be.equal(constants.NULL_ADDRESS); - }); - }); - }); - describe('#getTokenAddressBySymbol', () => { - it('should return correct address for a token in the registry', async () => { - const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressBySymbolIfExistsAsync( - registeredSymbol, - ); - expect(tokenAddress).to.be.equal(tokenAddressBySymbol[registeredSymbol]); - }); - it('should return undefined for a token out of registry', async () => { - const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressBySymbolIfExistsAsync( - unregisteredSymbol, - ); - expect(tokenAddress).to.be.undefined(); - }); - }); - describe('#getTokenAddressByName', () => { - it('should return correct address for a token in the registry', async () => { - const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressByNameIfExistsAsync( - registeredName, - ); - expect(tokenAddress).to.be.equal(tokenAddressByName[registeredName]); - }); - it('should return undefined for a token out of registry', async () => { - const tokenAddress = await contractWrappers.tokenRegistry.getTokenAddressByNameIfExistsAsync( - unregisteredName, - ); - expect(tokenAddress).to.be.undefined(); - }); - }); - describe('#getTokenBySymbol', () => { - it('should return correct token for a token in the registry', async () => { - const token = await contractWrappers.tokenRegistry.getTokenBySymbolIfExistsAsync(registeredSymbol); - expect(token).to.be.deep.equal(tokenBySymbol[registeredSymbol]); - }); - it('should return undefined for a token out of registry', async () => { - const token = await contractWrappers.tokenRegistry.getTokenBySymbolIfExistsAsync(unregisteredSymbol); - expect(token).to.be.undefined(); - }); - }); - describe('#getTokenByName', () => { - it('should return correct token for a token in the registry', async () => { - const token = await contractWrappers.tokenRegistry.getTokenByNameIfExistsAsync(registeredName); - expect(token).to.be.deep.equal(tokenByName[registeredName]); - }); - it('should return undefined for a token out of registry', async () => { - const token = await contractWrappers.tokenRegistry.getTokenByNameIfExistsAsync(unregisteredName); - expect(token).to.be.undefined(); - }); - }); - describe('#getTokenIfExistsAsync', () => { - it('should return the token added to the tokenRegistry during the migration', async () => { - const aToken = tokens[0]; - - const token = await contractWrappers.tokenRegistry.getTokenIfExistsAsync(aToken.address); - const schemaValidator = new SchemaValidator(); - const validationResult = schemaValidator.validate(token, schemas.tokenSchema); - expect(validationResult.errors).to.have.lengthOf(0); - }); - it('should return return undefined when passed a token address not in the tokenRegistry', async () => { - const unregisteredTokenAddress = '0x5409ed021d9299bf6814279a6a1411a7e866a631'; - const tokenIfExists = await contractWrappers.tokenRegistry.getTokenIfExistsAsync(unregisteredTokenAddress); - expect(tokenIfExists).to.be.undefined(); - }); - }); -}); diff --git a/packages/contract-wrappers/test/utils/constants.ts b/packages/contract-wrappers/test/utils/constants.ts index cf030259c..60c3370a2 100644 --- a/packages/contract-wrappers/test/utils/constants.ts +++ b/packages/contract-wrappers/test/utils/constants.ts @@ -1,9 +1,18 @@ +import { BigNumber } from '@0xproject/utils'; + export const constants = { NULL_ADDRESS: '0x0000000000000000000000000000000000000000', ROPSTEN_NETWORK_ID: 3, KOVAN_NETWORK_ID: 42, TESTRPC_NETWORK_ID: 50, + AWAIT_TRANSACTION_MINED_MS: 0, KOVAN_RPC_URL: 'https://kovan.infura.io/', ROPSTEN_RPC_URL: 'https://ropsten.infura.io/', ZRX_DECIMALS: 18, + DUMMY_TOKEN_NAME: '', + DUMMY_TOKEN_SYMBOL: '', + DUMMY_TOKEN_DECIMALS: 18, + DUMMY_TOKEN_TOTAL_SUPPLY: new BigNumber(10 ** 27), // tslint:disable-line:custom-no-magic-numbers + NUM_DUMMY_ERC20_TO_DEPLOY: 3, + NUM_DUMMY_ERC721_TO_DEPLOY: 1, }; diff --git a/packages/contract-wrappers/test/utils/token_utils.ts b/packages/contract-wrappers/test/utils/token_utils.ts index fe85de085..06a82ff6e 100644 --- a/packages/contract-wrappers/test/utils/token_utils.ts +++ b/packages/contract-wrappers/test/utils/token_utils.ts @@ -1,33 +1,47 @@ -import * as _ from 'lodash'; +import { generatePseudoRandomSalt } from '@0xproject/order-utils'; +import { BigNumber } from '@0xproject/utils'; -import { InternalContractWrappersError, Token } from '../../src/types'; +import { artifacts } from '../../src/artifacts'; +import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_erc721_token'; -const PROTOCOL_TOKEN_SYMBOL = 'ZRX'; -const WETH_TOKEN_SYMBOL = 'WETH'; +import { constants } from './constants'; +import { provider, txDefaults, web3Wrapper } from './web3_wrapper'; -export class TokenUtils { - private _tokens: Token[]; - constructor(tokens: Token[]) { - this._tokens = tokens; - } - public getProtocolTokenOrThrow(): Token { - const zrxToken = _.find(this._tokens, { symbol: PROTOCOL_TOKEN_SYMBOL }); - if (_.isUndefined(zrxToken)) { - throw new Error(InternalContractWrappersError.ZrxNotInTokenRegistry); - } - return zrxToken; - } - public getWethTokenOrThrow(): Token { - const wethToken = _.find(this._tokens, { symbol: WETH_TOKEN_SYMBOL }); - if (_.isUndefined(wethToken)) { - throw new Error(InternalContractWrappersError.WethNotInTokenRegistry); - } - return wethToken; - } - public getDummyTokens(): Token[] { - const dummyTokens = _.filter(this._tokens, token => { - return !_.includes([PROTOCOL_TOKEN_SYMBOL, WETH_TOKEN_SYMBOL], token.symbol); - }); - return dummyTokens; - } -} +// Those addresses come from migrations. They're deterministic so it's relatively safe to hard-code them here. +// Before we were fetching them from the TokenRegistry but now we can't as it's deprecated and removed. +const DUMMY_ERC_20_ADRESSES = [ + '0x6dfff22588be9b3ef8cf0ad6dc9b84796f9fb45f', + '0xcfc18cec799fbd1793b5c43e773c98d4d61cc2db', + '0xf22469f31527adc53284441bae1665a7b9214dba', + '0x10add991de718a69dec2117cb6aa28098836511b', + '0x8d61158a366019ac78db4149d75fff9dda51160d', +]; + +const DUMMY_ERC_721_ADRESSES = ['0x131855dda0aaff096f6854854c55a4debf61077a']; + +export const tokenUtils = { + getProtocolTokenAddress(): string { + return artifacts.ZRXToken.networks[constants.TESTRPC_NETWORK_ID].address; + }, + getWethTokenAddress(): string { + return artifacts.EtherToken.networks[constants.TESTRPC_NETWORK_ID].address; + }, + getDummyERC20TokenAddresses(): string[] { + return DUMMY_ERC_20_ADRESSES; + }, + getDummyERC721TokenAddresses(): string[] { + return DUMMY_ERC_721_ADRESSES; + }, + async mintDummyERC721Async(address: string, tokenOwner: string): Promise<BigNumber> { + const erc721 = new DummyERC721TokenContract( + artifacts.DummyERC721Token.compilerOutput.abi, + address, + provider, + txDefaults, + ); + const tokenId = generatePseudoRandomSalt(); + const txHash = await erc721.mint.sendTransactionAsync(tokenOwner, tokenId); + web3Wrapper.awaitTransactionSuccessAsync(txHash); + return tokenId; + }, +}; diff --git a/packages/contract-wrappers/test/utils/web3_wrapper.ts b/packages/contract-wrappers/test/utils/web3_wrapper.ts index f7d11f138..02c8c5918 100644 --- a/packages/contract-wrappers/test/utils/web3_wrapper.ts +++ b/packages/contract-wrappers/test/utils/web3_wrapper.ts @@ -1,8 +1,12 @@ -import { web3Factory } from '@0xproject/dev-utils'; -import { Provider } from '@0xproject/types'; +import { devConstants, web3Factory } from '@0xproject/dev-utils'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import { Provider } from 'ethereum-types'; +const txDefaults = { + from: devConstants.TESTRPC_FIRST_ADDRESS, + gas: devConstants.GAS_LIMIT, +}; const provider: Provider = web3Factory.getRpcProvider({ shouldUseInProcessGanache: true }); const web3Wrapper = new Web3Wrapper(provider); -export { provider, web3Wrapper }; +export { provider, web3Wrapper, txDefaults }; |