switch @0x/contract-wrappers to generated wrappers (#2037)

* switch @0x/contract-wrappers to generated wrappers

- remove TransactionEncoder
- move TokenUtils to @0x/dev-utils
- detailed changes in #2040
This commit is contained in:
Xianny 2019-08-08 07:29:30 -07:00 committed by GitHub
parent 5ac7ff7084
commit 8ce390be3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
112 changed files with 37252 additions and 7181 deletions

1
.gitattributes vendored
View File

@ -4,4 +4,5 @@
*.svg linguist-generated=true
packages/contract-artifacts/artifacts/*json linguist-generated=true
packages/abi-gen-wrappers/src/generated-wrappers/*.ts linguist-generated=true
packages/contract-wrappers/src/generated-wrappers/*.ts linguist-generated=true

View File

@ -48,7 +48,6 @@
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/dev-utils/README.md",
"devDependencies": {
"@0x/abi-gen": "^3.1.2",
"@0x/contract-wrappers": "^10.1.0",
"@0x/contracts-gen": "^1.0.12",
"@0x/contracts-test-utils": "^3.1.12",
"@0x/dev-utils": "^2.2.6",

View File

@ -47,7 +47,6 @@
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^3.1.2",
"@0x/contract-wrappers": "^10.1.0",
"@0x/contracts-gen": "^1.0.12",
"@0x/contracts-test-utils": "^3.1.12",
"@0x/dev-utils": "^2.2.6",

View File

@ -48,7 +48,7 @@
"homepage": "https://github.com/0xProject/0x-monorepo/contracts/extensions/README.md",
"devDependencies": {
"@0x/abi-gen": "^3.1.2",
"@0x/contract-wrappers": "^10.1.0",
"@0x/contract-wrappers": "10.1.0",
"@0x/contracts-gen": "^1.0.12",
"@0x/contracts-test-utils": "^3.1.12",
"@0x/dev-utils": "^2.2.6",

View File

@ -50,7 +50,7 @@
"config": {
"contractsPackages": "@0x/contracts-asset-proxy @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-exchange @0x/contracts-exchange-forwarder @0x/contracts-exchange-libs @0x/contracts-extensions @0x/contracts-multisig @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-coordinator @0x/contracts-dev-utils",
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
"packagesWithDocPages": "0x.js connect json-schemas subproviders web3-wrapper contract-wrappers order-utils order-watcher sol-compiler sol-coverage sol-profiler sol-trace ethereum-types asset-buyer migrations",
"packagesWithDocPages": "0x.js connect json-schemas subproviders web3-wrapper order-utils order-watcher sol-compiler sol-coverage sol-profiler sol-trace ethereum-types asset-buyer migrations",
"ignoreDependencyVersions": "@types/styled-components @types/node",
"ignoreDependencyVersionsForPackage": "website instant dev-tools-pages"
},

View File

@ -76,7 +76,7 @@
"@0x/assert": "^2.1.2",
"@0x/asset-swapper": "^1.0.0",
"@0x/base-contract": "^5.3.0",
"@0x/contract-wrappers": "^10.1.0",
"@0x/contract-wrappers": "10.1.0",
"@0x/order-utils": "^8.2.4",
"@0x/order-watcher": "^4.0.16",
"@0x/subproviders": "^5.0.0",

View File

@ -40,6 +40,13 @@ export {
} from '@0x/contract-wrappers';
export {
DutchAuctionContract,
ERC20ProxyContract,
ERC721ProxyContract,
ExchangeContract,
ForwarderContract,
OrderValidatorContract,
WETH9Contract,
WETH9Events,
WETH9WithdrawalEventArgs,
WETH9ApprovalEventArgs,
@ -102,15 +109,12 @@ export {
ObjectMap,
OrderRelevantState,
Stats,
DutchAuctionDetails,
ZeroExTransaction,
SignedZeroExTransaction,
} from '@0x/types';
export {
BlockParamLiteral,
ContractAbi,
BlockParam,
LogWithDecodedArgs,
ContractEventArg,
SupportedProvider,

View File

@ -83,8 +83,10 @@ export class {{contractName}}Contract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return {{contractName}}Contract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly, {{> params inputs=ctor.inputs}});
}

View File

@ -3119,8 +3119,10 @@ export class AssetProxyOwnerContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return AssetProxyOwnerContract.deployAsync(
bytecode,

View File

@ -1005,8 +1005,10 @@ export class CoordinatorContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return CoordinatorContract.deployAsync(
bytecode,

View File

@ -298,8 +298,10 @@ export class CoordinatorRegistryContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return CoordinatorRegistryContract.deployAsync(
bytecode,

View File

@ -1660,8 +1660,10 @@ export class DummyERC20TokenContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return DummyERC20TokenContract.deployAsync(
bytecode,

View File

@ -2116,8 +2116,10 @@ export class DummyERC721TokenContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return DummyERC721TokenContract.deployAsync(
bytecode,

View File

@ -795,8 +795,10 @@ export class DutchAuctionContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return DutchAuctionContract.deployAsync(
bytecode,

View File

@ -1043,8 +1043,10 @@ export class ERC20ProxyContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return ERC20ProxyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -866,8 +866,10 @@ export class ERC20TokenContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return ERC20TokenContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -1043,8 +1043,10 @@ export class ERC721ProxyContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return ERC721ProxyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -1428,8 +1428,10 @@ export class ERC721TokenContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return ERC721TokenContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -119,8 +119,10 @@ export class EthBalanceCheckerContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return EthBalanceCheckerContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -6498,8 +6498,10 @@ export class ExchangeContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return ExchangeContract.deployAsync(
bytecode,

View File

@ -1501,8 +1501,10 @@ export class ForwarderContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return ForwarderContract.deployAsync(
bytecode,

View File

@ -1041,8 +1041,10 @@ export class IAssetProxyContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return IAssetProxyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -137,8 +137,10 @@ export class IValidatorContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return IValidatorContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -127,8 +127,10 @@ export class IWalletContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return IWalletContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -1345,8 +1345,10 @@ export class MultiAssetProxyContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return MultiAssetProxyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -1014,8 +1014,10 @@ export class OrderValidatorContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return OrderValidatorContract.deployAsync(
bytecode,

View File

@ -1265,8 +1265,10 @@ export class WETH9Contract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return WETH9Contract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -1009,8 +1009,10 @@ export class ZRXTokenContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return ZRXTokenContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -2372,8 +2372,10 @@ export class AbiGenDummyContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return AbiGenDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -45,8 +45,10 @@ export class LibDummyContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return LibDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -173,8 +173,10 @@ export class TestLibDummyContract extends BaseContract {
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return TestLibDummyContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}

View File

@ -39,7 +39,7 @@
"dependencies": {
"@0x/assert": "^2.1.2",
"@0x/connect": "^5.0.15",
"@0x/contract-wrappers": "^10.1.0",
"@0x/contract-wrappers": "10.1.0",
"@0x/json-schemas": "^3.1.12",
"@0x/order-utils": "^8.2.4",
"@0x/subproviders": "^5.0.0",

View File

@ -40,7 +40,7 @@
"@0x/assert": "^2.1.2",
"@0x/connect": "^5.0.15",
"@0x/contract-addresses": "^3.0.2",
"@0x/contract-wrappers": "^10.1.0",
"@0x/contract-wrappers": "10.1.0",
"@0x/dev-utils": "^2.2.6",
"@0x/fill-scenarios": "^3.0.15",
"@0x/json-schemas": "^3.1.12",

View File

@ -1,6 +1,5 @@
import { ContractAddresses, ContractWrappers } from '@0x/contract-wrappers';
import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils } from '@0x/order-utils';
import { MarketOperation, SignedOrder } from '@0x/types';

View File

@ -1,6 +1,5 @@
import { ContractAddresses, ContractWrappers } from '@0x/contract-wrappers';
import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { MarketOperation, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';

View File

@ -1,6 +1,5 @@
import { ContractAddresses, ContractWrappers } from '@0x/contract-wrappers';
import { tokenUtils } from '@0x/contract-wrappers/lib/test/utils/token_utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { MarketOperation, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';

View File

@ -1,4 +1,13 @@
[
{
"version": "11.0.0",
"changes": [
{
"note": "Use @0x/abi-gen to generate wrappers. For a full list of changes, see https://github.com/0xProject/0x-monorepo/issues/2040",
"pr": 2037
}
]
},
{
"version": "10.1.0",
"changes": [

View File

@ -1,8 +1,6 @@
## @0x/contract-wrappers
Smart TS wrappers for 0x smart contracts. The wrappers have simplified interfaces, perform client-side validation on transactions and throw helpful error messages.
### Read the [Documentation](https://0xproject.com/docs/0x.js).
Wrappers for 0x smart contracts, generated using @0x/abi-gen.
## Installation
@ -14,7 +12,7 @@ npm install @0x/contract-wrappers --save
**Import**
```javascript
```typescript
import { ContractWrappers } from '@0x/contract-wrappers';
```
@ -28,7 +26,7 @@ If your project is in [TypeScript](https://www.typescriptlang.org/), add the fol
## Contributing
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
We welcome improvements and fixes from the wider community! To report bugs within this package, please create an issue in this repository.
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
@ -54,22 +52,6 @@ To build this package and all other monorepo packages that it depends on, run th
PKG=@0x/contract-wrappers yarn build
```
Or continuously rebuild on change:
```bash
PKG=@0x/contract-wrappers yarn watch
```
```bash
yarn build
```
or continuously rebuild on change:
```bash
yarn watch
```
### Clean
```bash
@ -87,3 +69,9 @@ yarn lint
```bash
yarn test
```
### Documentation
Documentation for this package is generated by TypeDoc, using the Solidity source code for 0x contracts. Each contract corresponds to one global-level module, which contains relevant enums and interfaces for its events and structs. Most significantly, each module exports a class, `<ContractName>Contract`, e.g. `ExchangeContract`, which implements helper methods for all the functions defined in the corresponding contract.
A convention to note is that these contract-specific helper methods are defined as _object literals_, which are separated from methods in the generated documentation. Each contract method has a number of sub-methods, e.g. `sendTransactionAsync`, or `estimateGasAsync`, which are documented separately. This is an example of an expected method call signature: `exchangeContractInstance.fillOrder.sendTransactionAsync(...arguments)`.

View File

@ -1,7 +1,10 @@
{
"name": "@0x/contract-wrappers",
"version": "10.1.0",
"description": "Smart TS wrappers for 0x smart contracts",
"version": "11.0.0",
"engines": {
"node": ">=6.12"
},
"description": "Wrappers for 0x smart contract wrappers generated using @0x/abi-gen",
"keywords": [
"0xproject",
"ethereum",
@ -10,34 +13,43 @@
],
"main": "lib/src/index.js",
"types": "lib/src/index.d.ts",
"directories": {
"test": "test"
},
"scripts": {
"build": "tsc -b",
"build": "yarn pre_build && tsc -b",
"build:ci": "yarn build",
"lint": "tslint --format stylish --project . --exclude **/lib/**/*",
"fix": "tslint --fix --format stylish --project . --exclude **/lib/**/*",
"fix": "tslint --fix --format stylish --project .--exclude **/lib/**/*",
"test:circleci": "run-s test:coverage",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"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",
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
"clean": "shx rm -rf _bundles lib test_temp generated_docs",
"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",
"docs:json": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
"pre_build": "yarn generate_contract_wrappers && yarn prettier_contract_wrappers",
"prettier": "prettier --write **/* --config ../../.prettierrc",
"prettier_contract_wrappers": "prettier --write src/generated-wrappers/* --config ../../.prettierrc",
"clean": "shx rm -rf lib src/generated-wrappers",
"generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output src/generated-wrappers --backend ethers",
"docs_test": "typedoc --excludePrivate --excludeExternals --target ES5 --tsconfig typedoc-tsconfig.json --out generated_docs ./src/generated-wrappers/*"
},
"config": {
"postpublish": {
"assets": []
}
"abis": "../contract-artifacts/artifacts/@(AssetProxyOwner|DutchAuction|DummyERC20Token|DummyERC721Token|ERC20Proxy|ERC20Token|ERC721Proxy|ERC721Token|Exchange|Forwarder|IAssetProxy|IValidator|IWallet|MultiAssetProxy|OrderValidator|WETH9|ZRXToken|Coordinator|CoordinatorRegistry|EthBalanceChecker).json"
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/0x-monorepo"
"url": "https://github.com/0xProject/0x-monorepo.git"
},
"license": "Apache-2.0",
"engines": {
"node": ">=6.0.0"
"bugs": {
"url": "https://github.com/0xProject/0x-monorepo/issues"
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/contract-wrappers/README.md",
"devDependencies": {
"@0x/abi-gen": "^3.1.2",
"@0x/abi-gen-templates": "^2.4.0",
"@0x/assert": "^2.1.2",
"@0x/json-schemas": "^3.1.12",
"@0x/contracts-test-utils": "^3.1.12",
"@0x/coordinator-server": "^0.1.3",
"@0x/dev-utils": "^2.2.6",
@ -45,52 +57,34 @@
"@0x/migrations": "^4.1.11",
"@0x/subproviders": "^5.0.0",
"@0x/tslint-config": "^3.0.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/nock": "^10.0.1",
"@types/node": "*",
"@types/sinon": "^2.2.2",
"@types/uuid": "^3.4.3",
"@types/web3-provider-engine": "^14.0.0",
"@0x/types": "^2.4.1",
"@0x/utils": "^4.4.2",
"@0x/web3-wrapper": "^6.0.9",
"@types/nock": "^10.0.3",
"ethereum-types": "^2.1.4",
"ethers": "~4.0.4",
"lodash": "^4.17.11",
"shx": "^0.2.2",
"chai": "^4.0.1",
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
"nock": "^10.0.6",
"npm-run-all": "^4.1.2",
"nyc": "^11.0.1",
"opn-cli": "^3.1.0",
"shx": "^0.2.2",
"sinon": "^4.0.0",
"source-map-support": "^0.5.0",
"tslint": "5.11.0",
"typedoc": "0.13.0",
"typescript": "3.0.1",
"web3-provider-engine": "14.0.6"
"nock": "^10.0.6"
},
"dependencies": {
"@0x/abi-gen-wrappers": "^5.1.0",
"@0x/assert": "^2.1.2",
"@0x/base-contract": "^5.3.0",
"@0x/contract-addresses": "^3.0.2",
"@0x/contract-artifacts": "^2.0.3",
"@0x/json-schemas": "^3.1.12",
"@0x/contracts-test-utils": "^3.1.12",
"@0x/coordinator-server": "^0.1.3",
"@0x/dev-utils": "^2.2.6",
"@0x/fill-scenarios": "^3.0.15",
"@0x/migrations": "^4.1.11",
"@0x/order-utils": "^8.2.4",
"@0x/types": "^2.4.1",
"@0x/typescript-typings": "^4.2.4",
"@0x/utils": "^4.4.2",
"@0x/web3-wrapper": "^6.0.9",
"ethereum-types": "^2.1.4",
"ethereumjs-abi": "0.6.5",
"ethereumjs-blockstream": "6.0.0",
"ethereumjs-util": "^5.1.1",
"ethers": "~4.0.4",
"http-status-codes": "^1.3.2",
"js-sha3": "^0.7.0",
"lodash": "^4.17.11",
"uuid": "^3.3.2"
"@0x/subproviders": "^5.0.0",
"http-status-codes": "^1.3.2"
},
"publishConfig": {
"access": "public"

View File

@ -1,11 +0,0 @@
import { BigNumber } from '@0x/utils';
export abstract class AbstractBalanceAndProxyAllowanceLazyStore {
public abstract async getBalanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber>;
public abstract async getProxyAllowanceAsync(tokenAddress: string, userAddress: string): Promise<BigNumber>;
public abstract setBalance(tokenAddress: string, userAddress: string, balance: BigNumber): void;
public abstract deleteBalance(tokenAddress: string, userAddress: string): void;
public abstract setProxyAllowance(tokenAddress: string, userAddress: string, proxyAllowance: BigNumber): void;
public abstract deleteProxyAllowance(tokenAddress: string, userAddress: string): void;
public abstract deleteAll(): void;
}

View File

@ -15,20 +15,18 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
import { SupportedProvider } from 'ethereum-types';
import * as _ from 'lodash';
import { CoordinatorWrapper } from './contract_wrappers/coordinator_wrapper';
import { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper';
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 { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper';
import { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper';
import { CoordinatorWrapper } from './coordinator_wrapper';
import { DutchAuctionContract } from './generated-wrappers/dutch_auction';
import { ERC20ProxyContract } from './generated-wrappers/erc20_proxy';
import { ERC721ProxyContract } from './generated-wrappers/erc721_proxy';
import { ExchangeContract } from './generated-wrappers/exchange';
import { ForwarderContract } from './generated-wrappers/forwarder';
import { OrderValidatorContract } from './generated-wrappers/order_validator';
import { WETH9Contract } from './generated-wrappers/weth9';
import { ContractWrappersConfigSchema } from './schemas/contract_wrappers_config_schema';
import { ContractWrappersConfig } from './types';
import { assert } from './utils/assert';
import { constants } from './utils/constants';
import { _getDefaultContractAddresses } from './utils/contract_addresses';
/**
@ -36,45 +34,36 @@ import { _getDefaultContractAddresses } from './utils/contract_addresses';
*/
export class ContractWrappers {
/**
* An instance of the ExchangeWrapper class containing methods for interacting with the 0x Exchange smart contract.
* An instance of the ExchangeContract class containing methods for interacting with the 0x Exchange smart contract.
*/
public exchange: ExchangeWrapper;
public exchange: ExchangeContract;
/**
* An instance of the ERC20TokenWrapper class containing methods for interacting with any ERC20 token smart contract.
*/
public erc20Token: ERC20TokenWrapper;
/**
* An instance of the ERC721TokenWrapper class containing methods for interacting with any ERC721 token smart contract.
*/
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 ERC20ProxyWrapper class containing methods for interacting with the
* An instance of the ERC20ProxyContract class containing methods for interacting with the
* erc20Proxy smart contract.
*/
public erc20Proxy: ERC20ProxyWrapper;
public erc20Proxy: ERC20ProxyContract;
/**
* An instance of the ERC721ProxyWrapper class containing methods for interacting with the
* An instance of the ERC721ProxyContract class containing methods for interacting with the
* erc721Proxy smart contract.
*/
public erc721Proxy: ERC721ProxyWrapper;
public erc721Proxy: ERC721ProxyContract;
/**
* An instance of the ForwarderWrapper class containing methods for interacting with any Forwarder smart contract.
* An instance of the WETH9Contract class containing methods for interacting with the
* WETH9 smart contract.
*/
public forwarder: ForwarderWrapper;
public weth9: WETH9Contract;
/**
* An instance of the OrderValidatorWrapper class containing methods for interacting with any OrderValidator smart contract.
* An instance of the ForwarderContract class containing methods for interacting with any Forwarder smart contract.
*/
public orderValidator: OrderValidatorWrapper;
public forwarder: ForwarderContract;
/**
* An instance of the DutchAuctionWrapper class containing methods for interacting with any DutchAuction smart contract.
* An instance of the OrderValidatorContract class containing methods for interacting with any OrderValidator smart contract.
*/
public dutchAuction: DutchAuctionWrapper;
public orderValidator: OrderValidatorContract;
/**
* An instance of the DutchAuctionContract class containing methods for interacting with any DutchAuction smart contract.
*/
public dutchAuction: DutchAuctionContract;
/**
* An instance of the CoordinatorWrapper class containing methods for interacting with the Coordinator extension contract.
*/
@ -109,45 +98,17 @@ export class ContractWrappers {
_.forEach(artifactsArray, artifact => {
this._web3Wrapper.abiDecoder.addABI(artifact.compilerOutput.abi, artifact.contractName);
});
const blockPollingIntervalMs =
config.blockPollingIntervalMs === undefined
? constants.DEFAULT_BLOCK_POLLING_INTERVAL
: config.blockPollingIntervalMs;
const contractAddresses =
config.contractAddresses === undefined
? _getDefaultContractAddresses(config.networkId)
: config.contractAddresses;
this.erc20Proxy = new ERC20ProxyWrapper(this._web3Wrapper, config.networkId, contractAddresses.erc20Proxy);
this.erc721Proxy = new ERC721ProxyWrapper(this._web3Wrapper, config.networkId, contractAddresses.erc721Proxy);
this.erc20Token = new ERC20TokenWrapper(this._web3Wrapper, this.erc20Proxy, blockPollingIntervalMs);
this.erc721Token = new ERC721TokenWrapper(this._web3Wrapper, this.erc721Proxy, blockPollingIntervalMs);
this.etherToken = new EtherTokenWrapper(this._web3Wrapper, this.erc20Token, blockPollingIntervalMs);
this.exchange = new ExchangeWrapper(
this._web3Wrapper,
config.networkId,
this.erc20Token,
this.erc721Token,
contractAddresses.exchange,
contractAddresses.zrxToken,
blockPollingIntervalMs,
);
this.forwarder = new ForwarderWrapper(
this._web3Wrapper,
config.networkId,
contractAddresses.forwarder,
contractAddresses.zrxToken,
contractAddresses.etherToken,
);
this.orderValidator = new OrderValidatorWrapper(
this._web3Wrapper,
config.networkId,
contractAddresses.orderValidator,
);
this.dutchAuction = new DutchAuctionWrapper(
this._web3Wrapper,
config.networkId,
contractAddresses.dutchAuction,
);
this.erc20Proxy = new ERC20ProxyContract(contractAddresses.erc20Proxy, this.getProvider());
this.erc721Proxy = new ERC721ProxyContract(contractAddresses.erc721Proxy, this.getProvider());
this.weth9 = new WETH9Contract(contractAddresses.etherToken, this.getProvider());
this.exchange = new ExchangeContract(contractAddresses.exchange, this.getProvider());
this.forwarder = new ForwarderContract(contractAddresses.forwarder, this.getProvider());
this.orderValidator = new OrderValidatorContract(contractAddresses.orderValidator, this.getProvider());
this.dutchAuction = new DutchAuctionContract(contractAddresses.dutchAuction, this.getProvider());
this.coordinator = new CoordinatorWrapper(
this._web3Wrapper,
config.networkId,
@ -161,9 +122,9 @@ export class ContractWrappers {
*/
public unsubscribeAll(): void {
this.exchange.unsubscribeAll();
this.erc20Token.unsubscribeAll();
this.erc721Token.unsubscribeAll();
this.etherToken.unsubscribeAll();
this.erc20Proxy.unsubscribeAll();
this.erc721Proxy.unsubscribeAll();
this.weth9.unsubscribeAll();
}
/**
* Get the provider instance currently used by contract-wrappers

View File

@ -1,138 +0,0 @@
import { DutchAuctionContract } from '@0x/abi-gen-wrappers';
import { DutchAuction } from '@0x/contract-artifacts';
import { schemas } from '@0x/json-schemas';
import { assetDataUtils } from '@0x/order-utils';
import { DutchAuctionData, DutchAuctionDetails, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema';
import { txOptsSchema } from '../schemas/tx_opts_schema';
import { DutchAuctionWrapperError, OrderTransactionOpts } from '../types';
import { assert } from '../utils/assert';
import { _getDefaultContractAddresses } from '../utils/contract_addresses';
export class DutchAuctionWrapper {
public abi: ContractAbi = DutchAuction.compilerOutput.abi;
public address: string;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _dutchAuctionContract: DutchAuctionContract;
/**
* Dutch auction details are encoded with the asset data for a 0x order. This function produces a hex
* encoded assetData string, containing information both about the asset being traded and the
* dutch auction; which is usable in the makerAssetData or takerAssetData fields in a 0x order.
* @param assetData Hex encoded assetData string for the asset being auctioned.
* @param beginTimeSeconds Begin time of the dutch auction.
* @param beginAmount Starting amount being sold in the dutch auction.
* @return The hex encoded assetData string.
*/
public static encodeDutchAuctionAssetData(
assetData: string,
beginTimeSeconds: BigNumber,
beginAmount: BigNumber,
): string {
const dutchAuctionData = assetDataUtils.encodeDutchAuctionAssetData(assetData, beginTimeSeconds, beginAmount);
return dutchAuctionData;
}
/**
* Dutch auction details are encoded with the asset data for a 0x order. This function decodes a hex
* encoded assetData string, containing information both about the asset being traded and the
* dutch auction.
* @param dutchAuctionData Hex encoded assetData string for the asset being auctioned.
* @return An object containing the auction asset, auction begin time and auction begin amount.
*/
public static decodeDutchAuctionData(dutchAuctionData: string): DutchAuctionData {
const decoded = assetDataUtils.decodeDutchAuctionData(dutchAuctionData);
return decoded;
}
/**
* Instantiate DutchAuctionWrapper
* @param web3Wrapper Web3Wrapper instance to use.
* @param networkId Desired networkId.
* @param address The address of the Dutch Auction contract. If undefined, will
* default to the known address corresponding to the networkId.
*/
public constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) {
this.address = address === undefined ? _getDefaultContractAddresses(networkId).dutchAuction : address;
this._web3Wrapper = web3Wrapper;
this._dutchAuctionContract = new DutchAuctionContract(
this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
}
/**
* Matches the buy and sell orders at an amount given the following: the current block time, the auction
* start time and the auction begin amount. The sell order is a an order at the lowest amount
* at the end of the auction. Excess from the match is transferred to the seller.
* Over time the price moves from beginAmount to endAmount given the current block.timestamp.
* @param buyOrder The Buyer's order. This order is for the current expected price of the auction.
* @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction).
* @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
* Provider provided at instantiation.
* @return Transaction hash.
*/
public async matchOrdersAsync(
buyOrder: SignedOrder,
sellOrder: SignedOrder,
takerAddress: string,
orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true },
): Promise<string> {
// type assertions
assert.doesConformToSchema('buyOrder', buyOrder, schemas.signedOrderSchema);
assert.doesConformToSchema('sellOrder', sellOrder, schemas.signedOrderSchema);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
const normalizedTakerAddress = takerAddress.toLowerCase();
// other assertions
if (
sellOrder.makerAssetData !== buyOrder.takerAssetData ||
sellOrder.takerAssetData !== buyOrder.makerAssetData
) {
throw new Error(DutchAuctionWrapperError.AssetDataMismatch);
}
// validate transaction
if (orderTransactionOpts.shouldValidate) {
await this._dutchAuctionContract.matchOrders.callAsync(
buyOrder,
sellOrder,
buyOrder.signature,
sellOrder.signature,
{
from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice,
nonce: orderTransactionOpts.nonce,
},
);
}
// send transaction
const txHash = await this._dutchAuctionContract.matchOrders.sendTransactionAsync(
buyOrder,
sellOrder,
buyOrder.signature,
sellOrder.signature,
{
from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice,
nonce: orderTransactionOpts.nonce,
},
);
return txHash;
}
/**
* Fetches the Auction Details for the given order
* @param sellOrder The Seller's order. This order is for the lowest amount (at the end of the auction).
* @return The dutch auction details.
*/
public async getAuctionDetailsAsync(sellOrder: SignedOrder): Promise<DutchAuctionDetails> {
// type assertions
assert.doesConformToSchema('sellOrder', sellOrder, schemas.signedOrderSchema);
// call contract
const auctionDetails = await this._dutchAuctionContract.getAuctionDetails.callAsync(sellOrder);
return auctionDetails;
}
}

View File

@ -1,65 +0,0 @@
import { ERC20ProxyContract } from '@0x/abi-gen-wrappers';
import { ERC20Proxy } from '@0x/contract-artifacts';
import { AssetProxyId } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
import { assert } from '../utils/assert';
import { _getDefaultContractAddresses } from '../utils/contract_addresses';
/**
* This class includes the functionality related to interacting with the ERC20Proxy contract.
*/
export class ERC20ProxyWrapper {
public abi: ContractAbi = ERC20Proxy.compilerOutput.abi;
public address: string;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _erc20ProxyContract: ERC20ProxyContract;
/**
* Instantiate ERC20ProxyWrapper
* @param web3Wrapper Web3Wrapper instance to use
* @param networkId Desired networkId
* @param address The address of the ERC20Proxy contract. If undefined, will
* default to the known address corresponding to the networkId.
*/
constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) {
this._web3Wrapper = web3Wrapper;
this.address = address === undefined ? _getDefaultContractAddresses(networkId).erc20Proxy : address;
this._erc20ProxyContract = new ERC20ProxyContract(
this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
}
/**
* Get the 4 bytes ID of this asset proxy
* @return Proxy id
*/
public async getProxyIdAsync(): Promise<AssetProxyId> {
// Note(albrow): Below is a TSLint false positive. Code won't compile if
// you remove the type assertion.
/* tslint:disable-next-line:no-unnecessary-type-assertion */
const proxyId = (await this._erc20ProxyContract.getProxyId.callAsync()) as AssetProxyId;
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 isAuthorized = await this._erc20ProxyContract.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 authorizedAddresses = await this._erc20ProxyContract.getAuthorizedAddresses.callAsync();
return authorizedAddresses;
}
}

View File

@ -1,444 +0,0 @@
import { ERC20TokenContract, ERC20TokenEventArgs, ERC20TokenEvents } from '@0x/abi-gen-wrappers';
import { SubscriptionManager } from '@0x/base-contract';
import { ERC20Token } from '@0x/contract-artifacts';
import { schemas } from '@0x/json-schemas';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
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 { constants } from '../utils/constants';
import { utils } from '../utils/utils';
import { ERC20ProxyWrapper } from './erc20_proxy_wrapper';
/**
* 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 ERC20 Proxy smart contract.
*/
export class ERC20TokenWrapper {
public abi: ContractAbi = ERC20Token.compilerOutput.abi;
public UNLIMITED_ALLOWANCE_IN_BASE_UNITS = constants.UNLIMITED_ALLOWANCE_IN_BASE_UNITS;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _blockPollingIntervalMs?: number;
private readonly _subscriptionManager: SubscriptionManager<ERC20TokenEventArgs, ERC20TokenEvents>;
private readonly _tokenContractsByAddress: { [address: string]: ERC20TokenContract };
private readonly _erc20ProxyWrapper: ERC20ProxyWrapper;
/**
* Instantiate ERC20TokenWrapper
* @param web3Wrapper Web3Wrapper instance to use
* @param erc20ProxyWrapper The ERC20ProxyWrapper instance to use
*/
constructor(web3Wrapper: Web3Wrapper, erc20ProxyWrapper: ERC20ProxyWrapper, blockPollingIntervalMs?: number) {
this._web3Wrapper = web3Wrapper;
this._tokenContractsByAddress = {};
this._erc20ProxyWrapper = erc20ProxyWrapper;
this._blockPollingIntervalMs = blockPollingIntervalMs;
this._subscriptionManager = new SubscriptionManager<ERC20TokenEventArgs, ERC20TokenEvents>(
ERC20TokenContract.ABI(),
web3Wrapper,
);
}
/**
* Retrieves an owner's ERC20 token balance.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 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 owner's ERC20 token balance in base units.
*/
public async getBalanceAsync(
tokenAddress: string,
ownerAddress: string,
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 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;
}
/**
* Sets the spender's allowance to a specified number of baseUnits on behalf of the owner address.
* Equivalent to the ERC20 spec method `approve`.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
* for spenderAddress.
* @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
* @param amountInBaseUnits The allowance amount you would like to set.
* @param txOpts Transaction parameters.
* @return Transaction hash.
*/
public async setAllowanceAsync(
tokenAddress: string,
ownerAddress: string,
spenderAddress: string,
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
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 normalizedOwnerAddress = ownerAddress.toLowerCase();
const normalizedSpenderAddress = spenderAddress.toLowerCase();
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
const txHash = await tokenContract.approve.sendTransactionAsync(
normalizedSpenderAddress,
amountInBaseUnits,
utils.removeUndefinedProperties({
from: normalizedOwnerAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
nonce: txOpts.nonce,
}),
);
return txHash;
}
/**
* Sets the spender's allowance to an unlimited number of baseUnits on behalf of the owner address.
* Equivalent to the ERC20 spec method `approve`.
* Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
* allowances set to the max amount (e.g ZRX, WETH)
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param ownerAddress The hex encoded user Ethereum address who would like to set an allowance
* for spenderAddress.
* @param spenderAddress The hex encoded user Ethereum address who will be able to spend the set allowance.
* @param txOpts Transaction parameters.
* @return Transaction hash.
*/
public async setUnlimitedAllowanceAsync(
tokenAddress: string,
ownerAddress: string,
spenderAddress: string,
txOpts: TransactionOpts = {},
): Promise<string> {
const txHash = await this.setAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
txOpts,
);
return txHash;
}
/**
* Retrieves the owners allowance in baseUnits set to the spender's address.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param ownerAddress The hex encoded user Ethereum address whose allowance to spenderAddress
* you would like to retrieve.
* @param spenderAddress The hex encoded user Ethereum address who can spend the allowance you are fetching.
* @param methodOpts Optional arguments this method accepts.
*/
public async getAllowanceAsync(
tokenAddress: string,
ownerAddress: string,
spenderAddress: string,
methodOpts: MethodOpts = {},
): Promise<BigNumber> {
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 txData = {};
let allowanceInBaseUnits = await tokenContract.allowance.callAsync(
normalizedOwnerAddress,
normalizedSpenderAddress,
txData,
methodOpts.defaultBlock,
);
// Wrap BigNumbers returned from web3 with our own (later) version of BigNumber
allowanceInBaseUnits = new BigNumber(allowanceInBaseUnits);
return allowanceInBaseUnits;
}
/**
* Retrieves the owner's allowance in baseUnits set to the 0x proxy contract.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param ownerAddress The hex encoded user Ethereum address whose proxy contract allowance we are retrieving.
* @param methodOpts Optional arguments this method accepts.
*/
public async getProxyAllowanceAsync(
tokenAddress: string,
ownerAddress: string,
methodOpts: MethodOpts = {},
): Promise<BigNumber> {
const proxyAddress = this._erc20ProxyWrapper.address;
const allowanceInBaseUnits = await this.getAllowanceAsync(tokenAddress, ownerAddress, proxyAddress, methodOpts);
return allowanceInBaseUnits;
}
/**
* Sets the 0x proxy contract's allowance to a specified number of a tokens' baseUnits on behalf
* of an owner address.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
* for the Proxy contract.
* @param amountInBaseUnits The allowance amount specified in baseUnits.
* @param txOpts Transaction parameters.
* @return Transaction hash.
*/
public async setProxyAllowanceAsync(
tokenAddress: string,
ownerAddress: string,
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
const proxyAddress = this._erc20ProxyWrapper.address;
const txHash = await this.setAllowanceAsync(
tokenAddress,
ownerAddress,
proxyAddress,
amountInBaseUnits,
txOpts,
);
return txHash;
}
/**
* Sets the 0x proxy contract's allowance to a unlimited number of a tokens' baseUnits on behalf
* of an owner address.
* Setting an unlimited allowance will lower the gas cost for filling orders involving tokens that forego updating
* allowances set to the max amount (e.g ZRX, WETH)
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param ownerAddress The hex encoded user Ethereum address who is setting an allowance
* for the Proxy contract.
* @param txOpts Transaction parameters.
* @return Transaction hash.
*/
public async setUnlimitedProxyAllowanceAsync(
tokenAddress: string,
ownerAddress: string,
txOpts: TransactionOpts = {},
): Promise<string> {
const txHash = await this.setProxyAllowanceAsync(
tokenAddress,
ownerAddress,
this.UNLIMITED_ALLOWANCE_IN_BASE_UNITS,
txOpts,
);
return txHash;
}
/**
* Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param fromAddress The hex encoded user Ethereum address that will send the funds.
* @param toAddress The hex encoded user Ethereum address that will receive the funds.
* @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
* @param txOpts Transaction parameters.
* @return Transaction hash.
*/
public async transferAsync(
tokenAddress: string,
fromAddress: string,
toAddress: string,
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
assert.isETHAddressHex('tokenAddress', tokenAddress);
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();
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
const fromAddressBalance = await this.getBalanceAsync(normalizedTokenAddress, normalizedFromAddress);
if (fromAddressBalance.isLessThan(amountInBaseUnits)) {
throw new Error(ContractWrappersError.InsufficientBalanceForTransfer);
}
const txHash = await tokenContract.transfer.sendTransactionAsync(
normalizedToAddress,
amountInBaseUnits,
utils.removeUndefinedProperties({
from: normalizedFromAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
nonce: txOpts.nonce,
}),
);
return txHash;
}
/**
* Transfers `amountInBaseUnits` ERC20 tokens from `fromAddress` to `toAddress`.
* Requires the fromAddress to have sufficient funds and to have approved an allowance of
* `amountInBaseUnits` to `senderAddress`.
* @param tokenAddress The hex encoded contract Ethereum address where the ERC20 token is deployed.
* @param fromAddress The hex encoded user Ethereum address whose funds are being sent.
* @param toAddress The hex encoded user Ethereum address that will receive the funds.
* @param senderAddress The hex encoded user Ethereum address whose initiates the fund transfer. The
* `fromAddress` must have set an allowance to the `senderAddress`
* before this call.
* @param amountInBaseUnits The amount (specified in baseUnits) of the token to transfer.
* @param txOpts Transaction parameters.
* @return Transaction hash.
*/
public async transferFromAsync(
tokenAddress: string,
fromAddress: string,
toAddress: string,
senderAddress: string,
amountInBaseUnits: BigNumber,
txOpts: TransactionOpts = {},
): Promise<string> {
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();
const tokenContract = await this._getTokenContractAsync(normalizedTokenAddress);
const fromAddressAllowance = await this.getAllowanceAsync(
normalizedTokenAddress,
normalizedFromAddress,
normalizedSenderAddress,
);
if (fromAddressAllowance.isLessThan(amountInBaseUnits)) {
throw new Error(ContractWrappersError.InsufficientAllowanceForTransfer);
}
const fromAddressBalance = await this.getBalanceAsync(normalizedTokenAddress, normalizedFromAddress);
if (fromAddressBalance.isLessThan(amountInBaseUnits)) {
throw new Error(ContractWrappersError.InsufficientBalanceForTransfer);
}
const txHash = await tokenContract.transferFrom.sendTransactionAsync(
normalizedFromAddress,
normalizedToAddress,
amountInBaseUnits,
utils.removeUndefinedProperties({
from: normalizedSenderAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
nonce: txOpts.nonce,
}),
);
return txHash;
}
/**
* Subscribe to an event type emitted by the Token contract.
* @param tokenAddress The hex encoded address where the ERC20 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
* @param isVerbose Enable verbose subscription warnings (e.g recoverable network issues encountered)
* @return Subscription token used later to unsubscribe
*/
public subscribe<ArgsType extends ERC20TokenEventArgs>(
tokenAddress: string,
eventName: ERC20TokenEvents,
indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
isVerbose: boolean = false,
): string {
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.doesBelongToStringEnum('eventName', eventName, ERC20TokenEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback);
const normalizedTokenAddress = tokenAddress.toLowerCase();
const subscriptionToken = this._subscriptionManager.subscribe<ArgsType>(
normalizedTokenAddress,
eventName,
indexFilterValues,
ERC20Token.compilerOutput.abi,
callback,
isVerbose,
this._blockPollingIntervalMs,
);
return subscriptionToken;
}
/**
* Cancel a subscription
* @param subscriptionToken Subscription token returned by `subscribe()`
*/
public unsubscribe(subscriptionToken: string): void {
assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken);
this._subscriptionManager.unsubscribe(subscriptionToken); // doesn't matter which contract is used
}
/**
* Cancels all existing subscriptions
*/
public unsubscribeAll(): void {
this._subscriptionManager.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 ERC20TokenEventArgs>(
tokenAddress: string,
eventName: ERC20TokenEvents,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.isETHAddressHex('tokenAddress', tokenAddress);
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._subscriptionManager.getLogsAsync<ArgsType>(
normalizedTokenAddress,
eventName,
blockRange,
indexFilterValues,
ERC20Token.compilerOutput.abi,
);
return logs;
}
private async _getTokenContractAsync(tokenAddress: string): Promise<ERC20TokenContract> {
const normalizedTokenAddress = tokenAddress.toLowerCase();
let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress];
if (tokenContract !== undefined) {
return tokenContract;
}
const contractInstance = new ERC20TokenContract(
normalizedTokenAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
tokenContract = contractInstance;
this._tokenContractsByAddress[normalizedTokenAddress] = tokenContract;
return tokenContract;
}
}

View File

@ -1,65 +0,0 @@
import { ERC721ProxyContract } from '@0x/abi-gen-wrappers';
import { ERC721Proxy } from '@0x/contract-artifacts';
import { AssetProxyId } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
import { assert } from '../utils/assert';
import { _getDefaultContractAddresses } from '../utils/contract_addresses';
/**
* This class includes the functionality related to interacting with the ERC721Proxy contract.
*/
export class ERC721ProxyWrapper {
public abi: ContractAbi = ERC721Proxy.compilerOutput.abi;
public address: string;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _erc721ProxyContract: ERC721ProxyContract;
/**
* Instantiate ERC721ProxyWrapper
* @param web3Wrapper Web3Wrapper instance to use
* @param networkId Desired networkId
* @param address The address of the ERC721Proxy contract. If undefined,
* will default to the known address corresponding to the networkId.
*/
constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) {
this._web3Wrapper = web3Wrapper;
this.address = address === undefined ? _getDefaultContractAddresses(networkId).erc721Proxy : address;
this._erc721ProxyContract = new ERC721ProxyContract(
this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
}
/**
* Get the 4 bytes ID of this asset proxy
* @return Proxy id
*/
public async getProxyIdAsync(): Promise<AssetProxyId> {
// Note(albrow): Below is a TSLint false positive. Code won't compile if
// you remove the type assertion.
/* tslint:disable-next-line:no-unnecessary-type-assertion */
const proxyId = (await this._erc721ProxyContract.getProxyId.callAsync()) as AssetProxyId;
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 isAuthorized = await this._erc721ProxyContract.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 authorizedAddresses = await this._erc721ProxyContract.getAuthorizedAddresses.callAsync();
return authorizedAddresses;
}
}

View File

@ -1,471 +0,0 @@
import { ERC721TokenContract, ERC721TokenEventArgs, ERC721TokenEvents } from '@0x/abi-gen-wrappers';
import { SubscriptionManager } from '@0x/base-contract';
import { ERC721Token } from '@0x/contract-artifacts';
import { schemas } from '@0x/json-schemas';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
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 { constants } from '../utils/constants';
import { utils } from '../utils/utils';
import { ERC721ProxyWrapper } from './erc721_proxy_wrapper';
/**
* 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 {
public abi: ContractAbi = ERC721Token.compilerOutput.abi;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _subscriptionManager: SubscriptionManager<ERC721TokenEventArgs, ERC721TokenEvents>;
private readonly _blockPollingIntervalMs?: number;
private readonly _tokenContractsByAddress: { [address: string]: ERC721TokenContract };
private readonly _erc721ProxyWrapper: ERC721ProxyWrapper;
/**
* Instantiate ERC721TokenWrapper
* @param web3Wrapper Web3Wrapper instance to use
* @param erc721ProxyWrapper The ERC721ProxyWrapper instance to use
*/
constructor(web3Wrapper: Web3Wrapper, erc721ProxyWrapper: ERC721ProxyWrapper, blockPollingIntervalMs?: number) {
this._web3Wrapper = web3Wrapper;
this._tokenContractsByAddress = {};
this._erc721ProxyWrapper = erc721ProxyWrapper;
this._blockPollingIntervalMs = blockPollingIntervalMs;
this._subscriptionManager = new SubscriptionManager<ERC721TokenEventArgs, ERC721TokenEvents>(
ERC721TokenContract.ABI(),
web3Wrapper,
);
}
/**
* 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.address;
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.address;
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,
utils.removeUndefinedProperties({
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
from: normalizedOwnerAddress,
nonce: txOpts.nonce,
}),
);
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.address;
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,
utils.removeUndefinedProperties({
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
from: tokenOwnerAddress,
nonce: txOpts.nonce,
}),
);
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.address;
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);
if (normalizedSenderAddress !== ownerAddress) {
const isApprovedForAll = await this.isApprovedForAllAsync(
normalizedTokenAddress,
ownerAddress,
normalizedSenderAddress,
);
if (!isApprovedForAll) {
const approvedAddress = await this.getApprovedIfExistsAsync(normalizedTokenAddress, tokenId);
if (approvedAddress !== normalizedSenderAddress) {
throw new Error(ContractWrappersError.ERC721NoApproval);
}
}
}
const txHash = await tokenContract.transferFrom.sendTransactionAsync(
ownerAddress,
normalizedReceiverAddress,
tokenId,
utils.removeUndefinedProperties({
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
from: normalizedSenderAddress,
nonce: txOpts.nonce,
}),
);
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
* @param isVerbose Enable verbose subscription warnings (e.g recoverable network issues encountered)
* @return Subscription token used later to unsubscribe
*/
public subscribe<ArgsType extends ERC721TokenEventArgs>(
tokenAddress: string,
eventName: ERC721TokenEvents,
indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
isVerbose: boolean = false,
): 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._subscriptionManager.subscribe<ArgsType>(
normalizedTokenAddress,
eventName,
indexFilterValues,
ERC721Token.compilerOutput.abi,
callback,
isVerbose,
this._blockPollingIntervalMs,
);
return subscriptionToken;
}
/**
* Cancel a subscription
* @param subscriptionToken Subscription token returned by `subscribe()`
*/
public unsubscribe(subscriptionToken: string): void {
assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken);
this._subscriptionManager.unsubscribe(subscriptionToken);
}
/**
* Cancels all existing subscriptions
*/
public unsubscribeAll(): void {
this._subscriptionManager.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._subscriptionManager.getLogsAsync<ArgsType>(
normalizedTokenAddress,
eventName,
blockRange,
indexFilterValues,
ERC721Token.compilerOutput.abi,
);
return logs;
}
private async _getTokenContractAsync(tokenAddress: string): Promise<ERC721TokenContract> {
const normalizedTokenAddress = tokenAddress.toLowerCase();
let tokenContract = this._tokenContractsByAddress[normalizedTokenAddress];
if (tokenContract !== undefined) {
return tokenContract;
}
const contractInstance = new ERC721TokenContract(
normalizedTokenAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
tokenContract = contractInstance;
this._tokenContractsByAddress[normalizedTokenAddress] = tokenContract;
return tokenContract;
}
}

View File

@ -1,212 +0,0 @@
import { WETH9Contract, WETH9EventArgs, WETH9Events } from '@0x/abi-gen-wrappers';
import { SubscriptionManager } from '@0x/base-contract';
import { WETH9 } from '@0x/contract-artifacts';
import { schemas } from '@0x/json-schemas';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi, LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import { BlockRange, ContractWrappersError, EventCallback, IndexedFilterValues, TransactionOpts } from '../types';
import { assert } from '../utils/assert';
import { utils } from '../utils/utils';
import { ERC20TokenWrapper } from './erc20_token_wrapper';
/**
* 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 {
public abi: ContractAbi = WETH9.compilerOutput.abi;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _subscriptionManager: SubscriptionManager<WETH9EventArgs, WETH9Events>;
private readonly _blockPollingIntervalMs?: number;
private readonly _etherTokenContractsByAddress: {
[address: string]: WETH9Contract;
} = {};
private readonly _erc20TokenWrapper: ERC20TokenWrapper;
/**
* Instantiate EtherTokenWrapper.
* @param web3Wrapper Web3Wrapper instance to use
* @param erc20TokenWrapper The ERC20TokenWrapper instance to use
*/
constructor(web3Wrapper: Web3Wrapper, erc20TokenWrapper: ERC20TokenWrapper, blockPollingIntervalMs?: number) {
this._web3Wrapper = web3Wrapper;
this._erc20TokenWrapper = erc20TokenWrapper;
this._blockPollingIntervalMs = blockPollingIntervalMs;
this._subscriptionManager = new SubscriptionManager<WETH9EventArgs, WETH9Events>(
WETH9Contract.ABI(),
web3Wrapper,
);
}
/**
* Deposit ETH into the Wrapped ETH smart contract and issues the equivalent number of wrapped ETH tokens
* to the depositor address. These wrapped ETH tokens can be used in 0x trades and are redeemable for 1-to-1
* for ETH.
* @param etherTokenAddress EtherToken address you wish to deposit into.
* @param amountInWei Amount of ETH in Wei the caller wishes to deposit.
* @param depositor The hex encoded user Ethereum address that would like to make the deposit.
* @param txOpts Transaction parameters.
* @return Transaction hash.
*/
public async depositAsync(
etherTokenAddress: string,
amountInWei: BigNumber,
depositor: string,
txOpts: TransactionOpts = {},
): Promise<string> {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
assert.isValidBaseUnitAmount('amountInWei', amountInWei);
await assert.isSenderAddressAsync('depositor', depositor, this._web3Wrapper);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
const normalizedDepositorAddress = depositor.toLowerCase();
const ethBalanceInWei = await this._web3Wrapper.getBalanceInWeiAsync(normalizedDepositorAddress);
assert.assert(ethBalanceInWei.gte(amountInWei), ContractWrappersError.InsufficientEthBalanceForDeposit);
const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
const txHash = await wethContract.deposit.sendTransactionAsync(
utils.removeUndefinedProperties({
from: normalizedDepositorAddress,
value: amountInWei,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
nonce: txOpts.nonce,
}),
);
return txHash;
}
/**
* Withdraw ETH to the withdrawer's address from the wrapped ETH smart contract in exchange for the
* equivalent number of wrapped ETH tokens.
* @param etherTokenAddress EtherToken address you wish to withdraw from.
* @param amountInWei Amount of ETH in Wei the caller wishes to withdraw.
* @param withdrawer The hex encoded user Ethereum address that would like to make the withdrawal.
* @param txOpts Transaction parameters.
* @return Transaction hash.
*/
public async withdrawAsync(
etherTokenAddress: string,
amountInWei: BigNumber,
withdrawer: string,
txOpts: TransactionOpts = {},
): Promise<string> {
assert.isValidBaseUnitAmount('amountInWei', amountInWei);
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
await assert.isSenderAddressAsync('withdrawer', withdrawer, this._web3Wrapper);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
const normalizedWithdrawerAddress = withdrawer.toLowerCase();
const WETHBalanceInBaseUnits = await this._erc20TokenWrapper.getBalanceAsync(
normalizedEtherTokenAddress,
normalizedWithdrawerAddress,
);
assert.assert(
WETHBalanceInBaseUnits.gte(amountInWei),
ContractWrappersError.InsufficientWEthBalanceForWithdrawal,
);
const wethContract = await this._getEtherTokenContractAsync(normalizedEtherTokenAddress);
const txHash = await wethContract.withdraw.sendTransactionAsync(
amountInWei,
utils.removeUndefinedProperties({
from: normalizedWithdrawerAddress,
gas: txOpts.gasLimit,
gasPrice: txOpts.gasPrice,
nonce: txOpts.nonce,
}),
);
return txHash;
}
/**
* Gets historical logs without creating a subscription
* @param etherTokenAddress An address of the ether token that emitted the logs.
* @param eventName The ether 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 `{_owner: aUserAddressHex}`
* @return Array of logs that match the parameters
*/
public async getLogsAsync<ArgsType extends WETH9EventArgs>(
etherTokenAddress: string,
eventName: WETH9Events,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
assert.doesBelongToStringEnum('eventName', eventName, WETH9Events);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const logs = await this._subscriptionManager.getLogsAsync<ArgsType>(
normalizedEtherTokenAddress,
eventName,
blockRange,
indexFilterValues,
WETH9.compilerOutput.abi,
);
return logs;
}
/**
* Subscribe to an event type emitted by the Token contract.
* @param etherTokenAddress The hex encoded address where the ether token is deployed.
* @param eventName The ether 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 `{_owner: aUserAddressHex}`
* @param callback Callback that gets called when a log is added/removed
* @param isVerbose Enable verbose subscription warnings (e.g recoverable network issues encountered)
* @return Subscription token used later to unsubscribe
*/
public subscribe<ArgsType extends WETH9EventArgs>(
etherTokenAddress: string,
eventName: WETH9Events,
indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
isVerbose: boolean = false,
): string {
assert.isETHAddressHex('etherTokenAddress', etherTokenAddress);
const normalizedEtherTokenAddress = etherTokenAddress.toLowerCase();
assert.doesBelongToStringEnum('eventName', eventName, WETH9Events);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback);
const subscriptionToken = this._subscriptionManager.subscribe<ArgsType>(
normalizedEtherTokenAddress,
eventName,
indexFilterValues,
WETH9.compilerOutput.abi,
callback,
isVerbose,
this._blockPollingIntervalMs,
);
return subscriptionToken;
}
/**
* Cancel a subscription
* @param subscriptionToken Subscription token returned by `subscribe()`
*/
public unsubscribe(subscriptionToken: string): void {
assert.isValidSubscriptionToken('subscriptionToken', subscriptionToken);
this._subscriptionManager.unsubscribe(subscriptionToken);
}
/**
* Cancels all existing subscriptions
*/
public unsubscribeAll(): void {
this._subscriptionManager.unsubscribeAll();
}
private async _getEtherTokenContractAsync(etherTokenAddress: string): Promise<WETH9Contract> {
let etherTokenContract = this._etherTokenContractsByAddress[etherTokenAddress];
if (etherTokenContract !== undefined) {
return etherTokenContract;
}
const contractInstance = new WETH9Contract(
etherTokenAddress,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
etherTokenContract = contractInstance;
this._etherTokenContractsByAddress[etherTokenAddress] = etherTokenContract;
return etherTokenContract;
}
}

View File

@ -1,239 +0,0 @@
import { ForwarderContract } from '@0x/abi-gen-wrappers';
import { Forwarder } from '@0x/contract-artifacts';
import { schemas } from '@0x/json-schemas';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema';
import { txOptsSchema } from '../schemas/tx_opts_schema';
import { OrderTransactionOpts } from '../types';
import { assert } from '../utils/assert';
import { calldataOptimizationUtils } from '../utils/calldata_optimization_utils';
import { constants } from '../utils/constants';
import { _getDefaultContractAddresses } from '../utils/contract_addresses';
import { decorators } from '../utils/decorators';
import { utils } from '../utils/utils';
/**
* This class includes the functionality related to interacting with the Forwarder contract.
*/
export class ForwarderWrapper {
public abi: ContractAbi = Forwarder.compilerOutput.abi;
public address: string;
public zrxTokenAddress: string;
public etherTokenAddress: string;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _forwarderContract: ForwarderContract;
/**
* Instantiate ForwarderWrapper
* @param web3Wrapper Web3Wrapper instance to use.
* @param networkId Desired networkId.
* @param address The address of the Exchange contract. If undefined, will
* default to the known address corresponding to the networkId.
* @param zrxTokenAddress The address of the ZRXToken contract. If
* undefined, will default to the known address corresponding to the
* networkId.
* @param etherTokenAddress The address of a WETH (Ether token) contract. If
* undefined, will default to the known address corresponding to the
* networkId.
*/
constructor(
web3Wrapper: Web3Wrapper,
networkId: number,
address?: string,
zrxTokenAddress?: string,
etherTokenAddress?: string,
) {
this._web3Wrapper = web3Wrapper;
this.address = address === undefined ? _getDefaultContractAddresses(networkId).exchange : address;
this.zrxTokenAddress =
zrxTokenAddress === undefined ? _getDefaultContractAddresses(networkId).zrxToken : zrxTokenAddress;
this.etherTokenAddress =
etherTokenAddress === undefined ? _getDefaultContractAddresses(networkId).etherToken : etherTokenAddress;
this._forwarderContract = new ForwarderContract(
this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
}
/**
* Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value.
* Any ZRX required to pay fees for primary orders will automatically be purchased by this contract.
* 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH).
* Any ETH not spent will be refunded to sender.
* @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset.
* All orders must specify WETH as the takerAsset
* @param takerAddress The user Ethereum address who would like to fill this order. Must be available via the supplied
* Provider provided at instantiation.
* @param ethAmount The amount of eth to send with the transaction (in wei).
* @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset.
* Used to purchase ZRX for primary order fees.
* @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
* Defaults to 0.
* @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled.
* @param orderTransactionOpts Transaction parameters.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async marketSellOrdersWithEthAsync(
signedOrders: SignedOrder[],
takerAddress: string,
ethAmount: BigNumber,
signedFeeOrders: SignedOrder[] = [],
feePercentage: number = 0,
feeRecipientAddress: string = constants.NULL_ADDRESS,
orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true },
): Promise<string> {
// type assertions
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
assert.isBigNumber('ethAmount', ethAmount);
assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema);
assert.isNumber('feePercentage', feePercentage);
assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress);
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
// other assertions
assert.ordersCanBeUsedForForwarderContract(signedOrders, this.etherTokenAddress);
assert.feeOrdersCanBeUsedForForwarderContract(signedFeeOrders, this.zrxTokenAddress, this.etherTokenAddress);
// format feePercentage
const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage);
// lowercase input addresses
const normalizedTakerAddress = takerAddress.toLowerCase();
const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase();
// optimize orders
const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders);
const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders);
// compile signatures
const signatures = _.map(optimizedMarketOrders, order => order.signature);
const feeSignatures = _.map(optimizedFeeOrders, order => order.signature);
// validate transaction
if (orderTransactionOpts.shouldValidate) {
await this._forwarderContract.marketSellOrdersWithEth.callAsync(
optimizedMarketOrders,
signatures,
optimizedFeeOrders,
feeSignatures,
formattedFeePercentage,
normalizedFeeRecipientAddress,
{
value: ethAmount,
from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice,
nonce: orderTransactionOpts.nonce,
},
);
}
// send transaction
const txHash = await this._forwarderContract.marketSellOrdersWithEth.sendTransactionAsync(
optimizedMarketOrders,
signatures,
optimizedFeeOrders,
feeSignatures,
formattedFeePercentage,
feeRecipientAddress,
{
value: ethAmount,
from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice,
nonce: orderTransactionOpts.nonce,
},
);
return txHash;
}
/**
* Attempt to purchase makerAssetFillAmount of makerAsset by selling ethAmount provided with transaction.
* Any ZRX required to pay fees for primary orders will automatically be purchased by the contract.
* Any ETH not spent will be refunded to sender.
* @param signedOrders An array of objects that conform to the SignedOrder interface. All orders must specify the same makerAsset.
* All orders must specify WETH as the takerAsset
* @param makerAssetFillAmount 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 ethAmount The amount of eth to send with the transaction (in wei).
* @param signedFeeOrders An array of objects that conform to the SignedOrder interface. All orders must specify ZRX as makerAsset and WETH as takerAsset.
* Used to purchase ZRX for primary order fees.
* @param feePercentage The percentage of WETH sold that will payed as fee to forwarding contract feeRecipient.
* Defaults to 0.
* @param feeRecipientAddress The address that will receive ETH when signedFeeOrders are filled.
* @param orderTransactionOpts Transaction parameters.
* @return Transaction hash.
*/
@decorators.asyncZeroExErrorHandler
public async marketBuyOrdersWithEthAsync(
signedOrders: SignedOrder[],
makerAssetFillAmount: BigNumber,
takerAddress: string,
ethAmount: BigNumber,
signedFeeOrders: SignedOrder[] = [],
feePercentage: number = 0,
feeRecipientAddress: string = constants.NULL_ADDRESS,
orderTransactionOpts: OrderTransactionOpts = { shouldValidate: true },
): Promise<string> {
// type assertions
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
assert.isBigNumber('makerAssetFillAmount', makerAssetFillAmount);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
assert.isBigNumber('ethAmount', ethAmount);
assert.doesConformToSchema('signedFeeOrders', signedFeeOrders, schemas.signedOrdersSchema);
assert.isNumber('feePercentage', feePercentage);
assert.isETHAddressHex('feeRecipientAddress', feeRecipientAddress);
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
// other assertions
assert.ordersCanBeUsedForForwarderContract(signedOrders, this.etherTokenAddress);
assert.feeOrdersCanBeUsedForForwarderContract(signedFeeOrders, this.zrxTokenAddress, this.etherTokenAddress);
// format feePercentage
const formattedFeePercentage = utils.numberPercentageToEtherTokenAmountPercentage(feePercentage);
// lowercase input addresses
const normalizedTakerAddress = takerAddress.toLowerCase();
const normalizedFeeRecipientAddress = feeRecipientAddress.toLowerCase();
// optimize orders
const optimizedMarketOrders = calldataOptimizationUtils.optimizeForwarderOrders(signedOrders);
const optimizedFeeOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(signedFeeOrders);
// compile signatures
const signatures = _.map(optimizedMarketOrders, order => order.signature);
const feeSignatures = _.map(optimizedFeeOrders, order => order.signature);
// validate transaction
if (orderTransactionOpts.shouldValidate) {
await this._forwarderContract.marketBuyOrdersWithEth.callAsync(
optimizedMarketOrders,
makerAssetFillAmount,
signatures,
optimizedFeeOrders,
feeSignatures,
formattedFeePercentage,
normalizedFeeRecipientAddress,
{
value: ethAmount,
from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice,
nonce: orderTransactionOpts.nonce,
},
);
}
// send transaction
const txHash = await this._forwarderContract.marketBuyOrdersWithEth.sendTransactionAsync(
optimizedMarketOrders,
makerAssetFillAmount,
signatures,
optimizedFeeOrders,
feeSignatures,
formattedFeePercentage,
feeRecipientAddress,
{
value: ethAmount,
from: normalizedTakerAddress,
gas: orderTransactionOpts.gasLimit,
gasPrice: orderTransactionOpts.gasPrice,
nonce: orderTransactionOpts.nonce,
},
);
return txHash;
}
}

View File

@ -1,169 +0,0 @@
import { OrderValidatorContract } from '@0x/abi-gen-wrappers';
import { OrderValidator } from '@0x/contract-artifacts';
import { schemas } from '@0x/json-schemas';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { ContractAbi } from 'ethereum-types';
import * as _ from 'lodash';
import { BalanceAndAllowance, OrderAndTraderInfo, TraderInfo } from '../types';
import { assert } from '../utils/assert';
import { _getDefaultContractAddresses } from '../utils/contract_addresses';
/**
* This class includes the functionality related to interacting with the OrderValidator contract.
*/
export class OrderValidatorWrapper {
public abi: ContractAbi = OrderValidator.compilerOutput.abi;
public address: string;
private readonly _web3Wrapper: Web3Wrapper;
private readonly _orderValidatorContract: OrderValidatorContract;
/**
* Instantiate OrderValidatorWrapper
* @param web3Wrapper Web3Wrapper instance to use.
* @param networkId Desired networkId.
* @param address The address of the OrderValidator contract. If undefined,
* will default to the known address corresponding to the networkId.
*/
constructor(web3Wrapper: Web3Wrapper, networkId: number, address?: string) {
this._web3Wrapper = web3Wrapper;
this.address = address === undefined ? _getDefaultContractAddresses(networkId).orderValidator : address;
this._orderValidatorContract = new OrderValidatorContract(
this.address,
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
}
/**
* Get an object conforming to OrderAndTraderInfo containing on-chain information of the provided order and address
* @param order An object conforming to SignedOrder
* @param takerAddress An ethereum address
* @return OrderAndTraderInfo
*/
public async getOrderAndTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise<OrderAndTraderInfo> {
assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
assert.isETHAddressHex('takerAddress', takerAddress);
const orderAndTraderInfo = await this._orderValidatorContract.getOrderAndTraderInfo.callAsync(
order,
takerAddress,
);
const result = {
orderInfo: orderAndTraderInfo[0],
traderInfo: orderAndTraderInfo[1],
};
return result;
}
/**
* Get an array of objects conforming to OrderAndTraderInfo containing on-chain information of the provided orders and addresses
* @param orders An array of objects conforming to SignedOrder
* @param takerAddresses An array of ethereum addresses
* @return array of OrderAndTraderInfo
*/
public async getOrdersAndTradersInfoAsync(
orders: SignedOrder[],
takerAddresses: string[],
): Promise<OrderAndTraderInfo[]> {
assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
_.forEach(takerAddresses, (takerAddress, index) =>
assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress),
);
assert.assert(orders.length === takerAddresses.length, 'Expected orders.length to equal takerAddresses.length');
const ordersAndTradersInfo = await this._orderValidatorContract.getOrdersAndTradersInfo.callAsync(
orders,
takerAddresses,
);
const orderInfos = ordersAndTradersInfo[0];
const traderInfos = ordersAndTradersInfo[1];
const result = _.map(orderInfos, (orderInfo, index) => {
const traderInfo = traderInfos[index];
return {
orderInfo,
traderInfo,
};
});
return result;
}
/**
* Get an object conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order
* @param order An object conforming to SignedOrder
* @param takerAddress An ethereum address
* @return TraderInfo
*/
public async getTraderInfoAsync(order: SignedOrder, takerAddress: string): Promise<TraderInfo> {
assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
assert.isETHAddressHex('takerAddress', takerAddress);
const result = await this._orderValidatorContract.getTraderInfo.callAsync(order, takerAddress);
return result;
}
/**
* Get an array of objects conforming to TraderInfo containing on-chain balance and allowances for maker and taker of order
* @param orders An array of objects conforming to SignedOrder
* @param takerAddresses An array of ethereum addresses
* @return array of TraderInfo
*/
public async getTradersInfoAsync(orders: SignedOrder[], takerAddresses: string[]): Promise<TraderInfo[]> {
assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
_.forEach(takerAddresses, (takerAddress, index) =>
assert.isETHAddressHex(`takerAddresses[${index}]`, takerAddress),
);
assert.assert(orders.length === takerAddresses.length, 'Expected orders.length to equal takerAddresses.length');
const result = await this._orderValidatorContract.getTradersInfo.callAsync(orders, takerAddresses);
return result;
}
/**
* Get an object conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and assetData
* @param address An ethereum address
* @param assetData An encoded string that can be decoded by a specified proxy contract
* @return BalanceAndAllowance
*/
public async getBalanceAndAllowanceAsync(address: string, assetData: string): Promise<BalanceAndAllowance> {
assert.isETHAddressHex('address', address);
assert.isHexString('assetData', assetData);
const balanceAndAllowance = await this._orderValidatorContract.getBalanceAndAllowance.callAsync(
address,
assetData,
);
const result = {
balance: balanceAndAllowance[0],
allowance: balanceAndAllowance[1],
};
return result;
}
/**
* Get an array of objects conforming to BalanceAndAllowance containing on-chain balance and allowance for some address and array of assetDatas
* @param address An ethereum address
* @param assetDatas An array of encoded strings that can be decoded by a specified proxy contract
* @return BalanceAndAllowance
*/
public async getBalancesAndAllowancesAsync(address: string, assetDatas: string[]): Promise<BalanceAndAllowance[]> {
assert.isETHAddressHex('address', address);
_.forEach(assetDatas, (assetData, index) => assert.isHexString(`assetDatas[${index}]`, assetData));
const balancesAndAllowances = await this._orderValidatorContract.getBalancesAndAllowances.callAsync(
address,
assetDatas,
);
const balances = balancesAndAllowances[0];
const allowances = balancesAndAllowances[1];
const result = _.map(balances, (balance, index) => {
const allowance = allowances[index];
return {
balance,
allowance,
};
});
return result;
}
/**
* Get owner address of tokenId by calling `token.ownerOf(tokenId)`, but returns a null owner instead of reverting on an unowned token.
* @param tokenAddress An ethereum address
* @param tokenId An ERC721 tokenId
* @return Owner of tokenId or null address if unowned
*/
public async getERC721TokenOwnerAsync(tokenAddress: string, tokenId: BigNumber): Promise<string | undefined> {
assert.isETHAddressHex('tokenAddress', tokenAddress);
assert.isBigNumber('tokenId', tokenId);
const result = await this._orderValidatorContract.getERC721TokenOwner.callAsync(tokenAddress, tokenId);
return result;
}
}

View File

@ -1,4 +1,3 @@
import { CoordinatorContract, CoordinatorRegistryContract, ExchangeContract } from '@0x/abi-gen-wrappers';
import { getContractAddressesForNetworkOrThrow } from '@0x/contract-addresses';
import { Coordinator } from '@0x/contract-artifacts';
import { schemas } from '@0x/json-schemas';
@ -10,10 +9,12 @@ import { ContractAbi } from 'ethereum-types';
import * as HttpStatus from 'http-status-codes';
import { flatten } from 'lodash';
import { orderTxOptsSchema } from '../schemas/order_tx_opts_schema';
import { txOptsSchema } from '../schemas/tx_opts_schema';
import { CoordinatorTransaction, OrderTransactionOpts } from '../types';
import { assert } from '../utils/assert';
import { CoordinatorContract, CoordinatorRegistryContract, ExchangeContract } from './index';
import { orderTxOptsSchema } from './schemas/order_tx_opts_schema';
import { txOptsSchema } from './schemas/tx_opts_schema';
import { CoordinatorTransaction, OrderTransactionOpts } from './types';
import { assert } from './utils/assert';
import {
CoordinatorServerApprovalRawResponse,
CoordinatorServerApprovalResponse,
@ -21,9 +22,9 @@ import {
CoordinatorServerError,
CoordinatorServerErrorMsg,
CoordinatorServerResponse,
} from '../utils/coordinator_server_types';
import { decorators } from '../utils/decorators';
import { TransactionEncoder } from '../utils/transaction_encoder';
} from './utils/coordinator_server_types';
import { decorators } from './utils/decorators';
import { getAbiEncodedTransactionData } from './utils/getAbiEncodedTransactionData';
/**
* This class includes all the functionality related to filling or cancelling orders through
@ -39,7 +40,6 @@ export class CoordinatorWrapper {
private readonly _contractInstance: CoordinatorContract;
private readonly _registryInstance: CoordinatorRegistryContract;
private readonly _exchangeInstance: ExchangeContract;
private readonly _transactionEncoder: TransactionEncoder;
private readonly _feeRecipientToEndpoint: { [feeRecipient: string]: string } = {};
/**
@ -82,8 +82,6 @@ export class CoordinatorWrapper {
this._web3Wrapper.getProvider(),
this._web3Wrapper.getContractDefaults(),
);
this._transactionEncoder = new TransactionEncoder(this._exchangeInstance);
}
/**
@ -112,7 +110,12 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.fillOrderTx(signedOrder, takerAssetFillAmount);
const data = this._getAbiEncodedTransactionData(
'fillOrder',
signedOrder,
takerAssetFillAmount,
signedOrder.signature,
);
const txHash = await this._handleFillsAsync(data, takerAddress, [signedOrder], orderTransactionOpts);
return txHash;
}
@ -139,7 +142,12 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.fillOrderNoThrowTx(signedOrder, takerAssetFillAmount);
const data = this._getAbiEncodedTransactionData(
'fillOrderNoThrow',
signedOrder,
takerAssetFillAmount,
signedOrder.signature,
);
const txHash = await this._handleFillsAsync(data, takerAddress, [signedOrder], orderTransactionOpts);
return txHash;
}
@ -167,7 +175,12 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.fillOrKillOrderTx(signedOrder, takerAssetFillAmount);
const data = this._getAbiEncodedTransactionData(
'fillOrKillOrder',
signedOrder,
takerAssetFillAmount,
signedOrder.signature,
);
const txHash = await this._handleFillsAsync(data, takerAddress, [signedOrder], orderTransactionOpts);
return txHash;
}
@ -201,7 +214,13 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.batchFillOrdersTx(signedOrders, takerAssetFillAmounts);
const signatures = signedOrders.map(o => o.signature);
const data = this._getAbiEncodedTransactionData(
'batchFillOrders',
signedOrders,
takerAssetFillAmounts,
signatures,
);
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
return txHash;
}
@ -230,7 +249,13 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.batchFillOrdersNoThrowTx(signedOrders, takerAssetFillAmounts);
const signatures = signedOrders.map(o => o.signature);
const data = this._getAbiEncodedTransactionData(
'batchFillOrdersNoThrow',
signedOrders,
takerAssetFillAmounts,
signatures,
);
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
return txHash;
}
@ -259,7 +284,13 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.batchFillOrKillOrdersTx(signedOrders, takerAssetFillAmounts);
const signatures = signedOrders.map(o => o.signature);
const data = this._getAbiEncodedTransactionData(
'batchFillOrKillOrders',
signedOrders,
takerAssetFillAmounts,
signatures,
);
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
return txHash;
}
@ -291,7 +322,13 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.marketBuyOrdersTx(signedOrders, makerAssetFillAmount);
const signatures = signedOrders.map(o => o.signature);
const data = this._getAbiEncodedTransactionData(
'marketBuyOrders',
signedOrders,
makerAssetFillAmount,
signatures,
);
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
return txHash;
}
@ -323,7 +360,13 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.marketSellOrdersTx(signedOrders, takerAssetFillAmount);
const signatures = signedOrders.map(o => o.signature);
const data = this._getAbiEncodedTransactionData(
'marketSellOrders',
signedOrders,
takerAssetFillAmount,
signatures,
);
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
return txHash;
}
@ -350,7 +393,13 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.marketBuyOrdersNoThrowTx(signedOrders, makerAssetFillAmount);
const signatures = signedOrders.map(o => o.signature);
const data = this._getAbiEncodedTransactionData(
'marketBuyOrdersNoThrow',
signedOrders,
makerAssetFillAmount,
signatures,
);
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
return txHash;
}
@ -377,7 +426,13 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('takerAddress', takerAddress, this._web3Wrapper);
const data = this._transactionEncoder.marketSellOrdersNoThrowTx(signedOrders, takerAssetFillAmount);
const signatures = signedOrders.map(o => o.signature);
const data = this._getAbiEncodedTransactionData(
'marketSellOrdersNoThrow',
signedOrders,
takerAssetFillAmount,
signatures,
);
const txHash = await this._handleFillsAsync(data, takerAddress, signedOrders, orderTransactionOpts);
return txHash;
}
@ -394,7 +449,7 @@ export class CoordinatorWrapper {
assert.isETHAddressHex('feeRecipientAddress', order.feeRecipientAddress);
assert.isSenderAddressAsync('makerAddress', order.makerAddress, this._web3Wrapper);
const data = this._transactionEncoder.cancelOrderTx(order);
const data = this._getAbiEncodedTransactionData('cancelOrder', order);
const transaction = await this._generateSignedZeroExTransactionAsync(data, order.makerAddress);
const endpoint = await this._getServerEndpointOrThrowAsync(order.feeRecipientAddress);
@ -428,7 +483,7 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orders', orders, schemas.ordersSchema);
const makerAddress = getMakerAddressOrThrow(orders);
assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper);
const data = this._transactionEncoder.batchCancelOrdersTx(orders);
const data = this._getAbiEncodedTransactionData('batchCancelOrders', orders);
const serverEndpointsToOrders = await this._mapServerEndpointsToOrdersAsync(orders);
@ -486,7 +541,7 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('makerAddress', order.makerAddress, this._web3Wrapper);
const data = this._transactionEncoder.cancelOrderTx(order);
const data = this._getAbiEncodedTransactionData('cancelOrder', order);
const transaction = await this._generateSignedZeroExTransactionAsync(data, order.makerAddress);
const approvalSignatures = new Array();
@ -519,7 +574,7 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('makerAddress', makerAddress, this._web3Wrapper);
const data = this._transactionEncoder.batchCancelOrdersTx(orders);
const data = this._getAbiEncodedTransactionData('batchCancelOrders', orders);
const transaction = await this._generateSignedZeroExTransactionAsync(data, makerAddress);
const approvalSignatures = new Array();
@ -554,7 +609,7 @@ export class CoordinatorWrapper {
assert.doesConformToSchema('orderTransactionOpts', orderTransactionOpts, orderTxOptsSchema, [txOptsSchema]);
await assert.isSenderAddressAsync('senderAddress', senderAddress, this._web3Wrapper);
const data = this._transactionEncoder.cancelOrdersUpToTx(targetOrderEpoch);
const data = this._getAbiEncodedTransactionData('cancelOrdersUpTo', targetOrderEpoch);
const transaction = await this._generateSignedZeroExTransactionAsync(data, senderAddress);
const approvalSignatures = new Array();
@ -618,6 +673,10 @@ export class CoordinatorWrapper {
return signerAddress;
}
private _getAbiEncodedTransactionData<K extends keyof ExchangeContract>(methodName: K, ...args: any[]): string {
return getAbiEncodedTransactionData(this._exchangeInstance, methodName, ...args);
}
private async _handleFillsAsync(
data: string,
takerAddress: string,

View File

@ -1,91 +0,0 @@
import { AbstractBalanceAndProxyAllowanceFetcher, assetDataUtils } from '@0x/order-utils';
import { BigNumber } from '@0x/utils';
import { BlockParamLiteral } from 'ethereum-types';
import * as _ from 'lodash';
import { ERC20TokenWrapper } from '../contract_wrappers/erc20_token_wrapper';
import { ERC721TokenWrapper } from '../contract_wrappers/erc721_token_wrapper';
export class AssetBalanceAndProxyAllowanceFetcher implements AbstractBalanceAndProxyAllowanceFetcher {
private readonly _erc20Token: ERC20TokenWrapper;
private readonly _erc721Token: ERC721TokenWrapper;
private readonly _stateLayer: BlockParamLiteral;
constructor(erc20Token: ERC20TokenWrapper, erc721Token: ERC721TokenWrapper, stateLayer: BlockParamLiteral) {
this._erc20Token = erc20Token;
this._erc721Token = erc721Token;
this._stateLayer = stateLayer;
}
public async getBalanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
let balance: BigNumber | undefined;
if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
balance = await this._erc20Token.getBalanceAsync(decodedAssetData.tokenAddress, userAddress, {
defaultBlock: this._stateLayer,
});
} else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
const tokenOwner = await this._erc721Token.getOwnerOfAsync(
decodedAssetData.tokenAddress,
decodedAssetData.tokenId,
{
defaultBlock: this._stateLayer,
},
);
balance = tokenOwner === userAddress ? new BigNumber(1) : new BigNumber(0);
} else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
// The `balance` for MultiAssetData is the total units of the entire `assetData` that are held by the `userAddress`.
for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
const nestedAmountElement = decodedAssetData.amounts[index];
const nestedAssetBalance = (await this.getBalanceAsync(
nestedAssetDataElement,
userAddress,
)).dividedToIntegerBy(nestedAmountElement);
if (balance === undefined || nestedAssetBalance.isLessThan(balance)) {
balance = nestedAssetBalance;
}
}
}
return balance as BigNumber;
}
public async getProxyAllowanceAsync(assetData: string, userAddress: string): Promise<BigNumber> {
const decodedAssetData = assetDataUtils.decodeAssetDataOrThrow(assetData);
let proxyAllowance: BigNumber | undefined;
if (assetDataUtils.isERC20AssetData(decodedAssetData)) {
proxyAllowance = await this._erc20Token.getProxyAllowanceAsync(decodedAssetData.tokenAddress, userAddress, {
defaultBlock: this._stateLayer,
});
} else if (assetDataUtils.isERC721AssetData(decodedAssetData)) {
const isApprovedForAll = await this._erc721Token.isProxyApprovedForAllAsync(
decodedAssetData.tokenAddress,
userAddress,
{
defaultBlock: this._stateLayer,
},
);
if (isApprovedForAll) {
return new BigNumber(this._erc20Token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS);
} else {
const isApproved = await this._erc721Token.isProxyApprovedAsync(
decodedAssetData.tokenAddress,
decodedAssetData.tokenId,
{
defaultBlock: this._stateLayer,
},
);
proxyAllowance = isApproved ? new BigNumber(1) : new BigNumber(0);
}
} else if (assetDataUtils.isMultiAssetData(decodedAssetData)) {
// The `proxyAllowance` for MultiAssetData is the total units of the entire `assetData` that the proxies have been approved to spend by the `userAddress`.
for (const [index, nestedAssetDataElement] of decodedAssetData.nestedAssetData.entries()) {
const nestedAmountElement = decodedAssetData.amounts[index];
const nestedAssetAllowance = (await this.getProxyAllowanceAsync(
nestedAssetDataElement,
userAddress,
)).dividedToIntegerBy(nestedAmountElement);
if (proxyAllowance === undefined || nestedAssetAllowance.isLessThan(proxyAllowance)) {
proxyAllowance = nestedAssetAllowance;
}
}
}
return proxyAllowance as BigNumber;
}
}

View File

@ -1,39 +0,0 @@
// tslint:disable:no-unnecessary-type-assertion
import { AbstractOrderFilledCancelledFetcher, orderHashUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BlockParamLiteral } from 'ethereum-types';
import { ExchangeWrapper } from '../contract_wrappers/exchange_wrapper';
export class OrderFilledCancelledFetcher implements AbstractOrderFilledCancelledFetcher {
private readonly _exchange: ExchangeWrapper;
private readonly _stateLayer: BlockParamLiteral;
constructor(exchange: ExchangeWrapper, stateLayer: BlockParamLiteral) {
this._exchange = exchange;
this._stateLayer = stateLayer;
}
public async getFilledTakerAmountAsync(orderHash: string): Promise<BigNumber> {
const filledTakerAmount = this._exchange.getFilledTakerAssetAmountAsync(orderHash, {
defaultBlock: this._stateLayer,
});
return filledTakerAmount;
}
public async isOrderCancelledAsync(signedOrder: SignedOrder): Promise<boolean> {
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const isCancelled = await this._exchange.isCancelledAsync(orderHash);
const orderEpoch = await this._exchange.getOrderEpochAsync(
signedOrder.makerAddress,
signedOrder.senderAddress,
{
defaultBlock: this._stateLayer,
},
);
const isCancelledByOrderEpoch = orderEpoch > signedOrder.salt;
return isCancelled || isCancelledByOrderEpoch;
}
public getZRXAssetData(): string {
const zrxAssetData = this._exchange.getZRXAssetData();
return zrxAssetData;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,511 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
// tslint:disable:no-unused-variable
import {
BaseContract,
BlockRange,
EventCallback,
IndexedFilterValues,
SubscriptionManager,
PromiseWithTransactionHash,
} from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
BlockParam,
BlockParamLiteral,
CallData,
ContractAbi,
ContractArtifact,
DecodedLogArgs,
LogWithDecodedArgs,
MethodAbi,
TransactionReceiptWithDecodedLogs,
TxData,
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
export type CoordinatorRegistryEventArgs = CoordinatorRegistryCoordinatorEndpointSetEventArgs;
export enum CoordinatorRegistryEvents {
CoordinatorEndpointSet = 'CoordinatorEndpointSet',
}
export interface CoordinatorRegistryCoordinatorEndpointSetEventArgs extends DecodedLogArgs {
coordinatorOperator: string;
coordinatorEndpoint: string;
}
/* istanbul ignore next */
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class CoordinatorRegistryContract extends BaseContract {
/**
* Called by a Coordinator operator to set the endpoint of their Coordinator.
*/
public setCoordinatorEndpoint = {
/**
* Sends an Ethereum transaction executing this method with the supplied parameters. This is a read/write
* Ethereum operation and will cost gas.
* @param coordinatorEndpoint endpoint of the Coordinator.
* @param txData Additional data for transaction
* @returns The hash of the transaction
*/
async sendTransactionAsync(coordinatorEndpoint: string, txData?: Partial<TxData> | undefined): Promise<string> {
assert.isString('coordinatorEndpoint', coordinatorEndpoint);
const self = (this as any) as CoordinatorRegistryContract;
const encodedData = self._strictEncodeArguments('setCoordinatorEndpoint(string)', [coordinatorEndpoint]);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...txData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
self.setCoordinatorEndpoint.estimateGasAsync.bind(self, coordinatorEndpoint),
);
if (txDataWithDefaults.from !== undefined) {
txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase();
}
const txHash = await self._web3Wrapper.sendTransactionAsync(txDataWithDefaults);
return txHash;
},
/**
* Sends an Ethereum transaction and waits until the transaction has been successfully mined without reverting.
* If the transaction was mined, but reverted, an error is thrown.
* @param coordinatorEndpoint endpoint of the Coordinator.
* @param txData Additional data for transaction
* @param pollingIntervalMs Interval at which to poll for success
* @returns A promise that resolves when the transaction is successful
*/
awaitTransactionSuccessAsync(
coordinatorEndpoint: string,
txData?: Partial<TxData>,
pollingIntervalMs?: number,
timeoutMs?: number,
): PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs> {
assert.isString('coordinatorEndpoint', coordinatorEndpoint);
const self = (this as any) as CoordinatorRegistryContract;
const txHashPromise = self.setCoordinatorEndpoint.sendTransactionAsync(coordinatorEndpoint, txData);
return new PromiseWithTransactionHash<TransactionReceiptWithDecodedLogs>(
txHashPromise,
(async (): Promise<TransactionReceiptWithDecodedLogs> => {
// When the transaction hash resolves, wait for it to be mined.
return self._web3Wrapper.awaitTransactionSuccessAsync(
await txHashPromise,
pollingIntervalMs,
timeoutMs,
);
})(),
);
},
/**
* Estimates the gas cost of sending an Ethereum transaction calling this method with these arguments.
* @param coordinatorEndpoint endpoint of the Coordinator.
* @param txData Additional data for transaction
* @returns The hash of the transaction
*/
async estimateGasAsync(coordinatorEndpoint: string, txData?: Partial<TxData> | undefined): Promise<number> {
assert.isString('coordinatorEndpoint', coordinatorEndpoint);
const self = (this as any) as CoordinatorRegistryContract;
const encodedData = self._strictEncodeArguments('setCoordinatorEndpoint(string)', [coordinatorEndpoint]);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...txData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
if (txDataWithDefaults.from !== undefined) {
txDataWithDefaults.from = txDataWithDefaults.from.toLowerCase();
}
const gas = await self._web3Wrapper.estimateGasAsync(txDataWithDefaults);
return gas;
},
/**
* Sends a read-only call to the contract method. Returns the result that would happen if one were to send an
* Ethereum transaction to this method, given the current state of the blockchain. Calls do not cost gas
* since they don't modify state.
* @param coordinatorEndpoint endpoint of the Coordinator.
*/
async callAsync(
coordinatorEndpoint: string,
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<void> {
assert.isString('coordinatorEndpoint', coordinatorEndpoint);
assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (defaultBlock !== undefined) {
assert.isBlockParam('defaultBlock', defaultBlock);
}
const self = (this as any) as CoordinatorRegistryContract;
const encodedData = self._strictEncodeArguments('setCoordinatorEndpoint(string)', [coordinatorEndpoint]);
const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...callData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
callDataWithDefaults.from = callDataWithDefaults.from
? callDataWithDefaults.from.toLowerCase()
: callDataWithDefaults.from;
const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock);
BaseContract._throwIfRevertWithReasonCallResult(rawCallResult);
const abiEncoder = self._lookupAbiEncoder('setCoordinatorEndpoint(string)');
// tslint:disable boolean-naming
const result = abiEncoder.strictDecodeReturnValue<void>(rawCallResult);
// tslint:enable boolean-naming
return result;
},
/**
* Returns the ABI encoded transaction data needed to send an Ethereum transaction calling this method. Before
* sending the Ethereum tx, this encoded tx data can first be sent to a separate signing service or can be used
* to create a 0x transaction (see protocol spec for more details).
* @param coordinatorEndpoint endpoint of the Coordinator.
*/
getABIEncodedTransactionData(coordinatorEndpoint: string): string {
assert.isString('coordinatorEndpoint', coordinatorEndpoint);
const self = (this as any) as CoordinatorRegistryContract;
const abiEncodedTransactionData = self._strictEncodeArguments('setCoordinatorEndpoint(string)', [
coordinatorEndpoint,
]);
return abiEncodedTransactionData;
},
getABIDecodedTransactionData(callData: string): void {
const self = (this as any) as CoordinatorRegistryContract;
const abiEncoder = self._lookupAbiEncoder('setCoordinatorEndpoint(string)');
// tslint:disable boolean-naming
const abiDecodedCallData = abiEncoder.strictDecode<void>(callData);
return abiDecodedCallData;
},
getABIDecodedReturnData(returnData: string): void {
const self = (this as any) as CoordinatorRegistryContract;
const abiEncoder = self._lookupAbiEncoder('setCoordinatorEndpoint(string)');
// tslint:disable boolean-naming
const abiDecodedReturnData = abiEncoder.strictDecodeReturnValue<void>(returnData);
return abiDecodedReturnData;
},
};
/**
* Gets the endpoint for a Coordinator.
*/
public getCoordinatorEndpoint = {
/**
* Sends a read-only call to the contract method. Returns the result that would happen if one were to send an
* Ethereum transaction to this method, given the current state of the blockchain. Calls do not cost gas
* since they don't modify state.
* @param coordinatorOperator operator of the Coordinator endpoint.
*/
async callAsync(
coordinatorOperator: string,
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<string> {
assert.isString('coordinatorOperator', coordinatorOperator);
assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (defaultBlock !== undefined) {
assert.isBlockParam('defaultBlock', defaultBlock);
}
const self = (this as any) as CoordinatorRegistryContract;
const encodedData = self._strictEncodeArguments('getCoordinatorEndpoint(address)', [
coordinatorOperator.toLowerCase(),
]);
const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...callData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
callDataWithDefaults.from = callDataWithDefaults.from
? callDataWithDefaults.from.toLowerCase()
: callDataWithDefaults.from;
const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock);
BaseContract._throwIfRevertWithReasonCallResult(rawCallResult);
const abiEncoder = self._lookupAbiEncoder('getCoordinatorEndpoint(address)');
// tslint:disable boolean-naming
const result = abiEncoder.strictDecodeReturnValue<string>(rawCallResult);
// tslint:enable boolean-naming
return result;
},
/**
* Returns the ABI encoded transaction data needed to send an Ethereum transaction calling this method. Before
* sending the Ethereum tx, this encoded tx data can first be sent to a separate signing service or can be used
* to create a 0x transaction (see protocol spec for more details).
* @param coordinatorOperator operator of the Coordinator endpoint.
*/
getABIEncodedTransactionData(coordinatorOperator: string): string {
assert.isString('coordinatorOperator', coordinatorOperator);
const self = (this as any) as CoordinatorRegistryContract;
const abiEncodedTransactionData = self._strictEncodeArguments('getCoordinatorEndpoint(address)', [
coordinatorOperator.toLowerCase(),
]);
return abiEncodedTransactionData;
},
getABIDecodedTransactionData(callData: string): string {
const self = (this as any) as CoordinatorRegistryContract;
const abiEncoder = self._lookupAbiEncoder('getCoordinatorEndpoint(address)');
// tslint:disable boolean-naming
const abiDecodedCallData = abiEncoder.strictDecode<string>(callData);
return abiDecodedCallData;
},
getABIDecodedReturnData(returnData: string): string {
const self = (this as any) as CoordinatorRegistryContract;
const abiEncoder = self._lookupAbiEncoder('getCoordinatorEndpoint(address)');
// tslint:disable boolean-naming
const abiDecodedReturnData = abiEncoder.strictDecodeReturnValue<string>(returnData);
return abiDecodedReturnData;
},
};
private readonly _subscriptionManager: SubscriptionManager<CoordinatorRegistryEventArgs, CoordinatorRegistryEvents>;
public static async deployFrom0xArtifactAsync(
artifact: ContractArtifact | SimpleContractArtifact,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<CoordinatorRegistryContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return CoordinatorRegistryContract.deployAsync(
bytecode,
abi,
provider,
txDefaults,
logDecodeDependenciesAbiOnly,
);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractAbi },
): Promise<CoordinatorRegistryContract> {
assert.isHexString('bytecode', bytecode);
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
const iface = new ethers.utils.Interface(abi);
const deployInfo = iface.deployFunction;
const txData = deployInfo.encode(bytecode, []);
const web3Wrapper = new Web3Wrapper(provider);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{ data: txData },
txDefaults,
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`CoordinatorRegistry successfully deployed at ${txReceipt.contractAddress}`);
const contractInstance = new CoordinatorRegistryContract(
txReceipt.contractAddress as string,
provider,
txDefaults,
logDecodeDependencies,
);
contractInstance.constructorArgs = [];
return contractInstance;
}
/**
* @returns The contract ABI
*/
public static ABI(): ContractAbi {
const abi = [
{
constant: false,
inputs: [
{
name: 'coordinatorEndpoint',
type: 'string',
},
],
name: 'setCoordinatorEndpoint',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
constant: true,
inputs: [
{
name: 'coordinatorOperator',
type: 'address',
},
],
name: 'getCoordinatorEndpoint',
outputs: [
{
name: 'coordinatorEndpoint',
type: 'string',
},
],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'constructor',
},
{
anonymous: false,
inputs: [
{
name: 'coordinatorOperator',
type: 'address',
indexed: false,
},
{
name: 'coordinatorEndpoint',
type: 'string',
indexed: false,
},
],
name: 'CoordinatorEndpointSet',
outputs: [],
type: 'event',
},
] as ContractAbi;
return abi;
}
/**
* Subscribe to an event type emitted by the CoordinatorRegistry contract.
* @param eventName The CoordinatorRegistry 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
* @param isVerbose Enable verbose subscription warnings (e.g recoverable network issues encountered)
* @return Subscription token used later to unsubscribe
*/
public subscribe<ArgsType extends CoordinatorRegistryEventArgs>(
eventName: CoordinatorRegistryEvents,
indexFilterValues: IndexedFilterValues,
callback: EventCallback<ArgsType>,
isVerbose: boolean = false,
blockPollingIntervalMs?: number,
): string {
assert.doesBelongToStringEnum('eventName', eventName, CoordinatorRegistryEvents);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
assert.isFunction('callback', callback);
const subscriptionToken = this._subscriptionManager.subscribe<ArgsType>(
this.address,
eventName,
indexFilterValues,
CoordinatorRegistryContract.ABI(),
callback,
isVerbose,
blockPollingIntervalMs,
);
return subscriptionToken;
}
/**
* Cancel a subscription
* @param subscriptionToken Subscription token returned by `subscribe()`
*/
public unsubscribe(subscriptionToken: string): void {
this._subscriptionManager.unsubscribe(subscriptionToken);
}
/**
* Cancels all existing subscriptions
*/
public unsubscribeAll(): void {
this._subscriptionManager.unsubscribeAll();
}
/**
* Gets historical logs without creating a subscription
* @param eventName The CoordinatorRegistry 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 CoordinatorRegistryEventArgs>(
eventName: CoordinatorRegistryEvents,
blockRange: BlockRange,
indexFilterValues: IndexedFilterValues,
): Promise<Array<LogWithDecodedArgs<ArgsType>>> {
assert.doesBelongToStringEnum('eventName', eventName, CoordinatorRegistryEvents);
assert.doesConformToSchema('blockRange', blockRange, schemas.blockRangeSchema);
assert.doesConformToSchema('indexFilterValues', indexFilterValues, schemas.indexFilterValuesSchema);
const logs = await this._subscriptionManager.getLogsAsync<ArgsType>(
this.address,
eventName,
blockRange,
indexFilterValues,
CoordinatorRegistryContract.ABI(),
);
return logs;
}
constructor(
address: string,
supportedProvider: SupportedProvider,
txDefaults?: Partial<TxData>,
logDecodeDependencies?: { [contractName: string]: ContractAbi },
) {
super(
'CoordinatorRegistry',
CoordinatorRegistryContract.ABI(),
address,
supportedProvider,
txDefaults,
logDecodeDependencies,
);
classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', '_web3Wrapper']);
this._subscriptionManager = new SubscriptionManager<CoordinatorRegistryEventArgs, CoordinatorRegistryEvents>(
CoordinatorRegistryContract.ABI(),
this._web3Wrapper,
);
}
}
// tslint:disable:max-file-line-count
// tslint:enable:no-unbound-method no-parameter-reassignment no-consecutive-blank-lines ordered-imports align
// tslint:enable:trailing-comma whitespace no-trailing-whitespace

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,215 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
// tslint:disable:no-unused-variable
import { BaseContract, PromiseWithTransactionHash } from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
BlockParam,
BlockParamLiteral,
CallData,
ContractAbi,
ContractArtifact,
DecodedLogArgs,
MethodAbi,
TransactionReceiptWithDecodedLogs,
TxData,
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
/* istanbul ignore next */
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class EthBalanceCheckerContract extends BaseContract {
/**
* Batch fetches ETH balances
*/
public getEthBalances = {
/**
* Sends a read-only call to the contract method. Returns the result that would happen if one were to send an
* Ethereum transaction to this method, given the current state of the blockchain. Calls do not cost gas
* since they don't modify state.
* @param addresses Array of addresses.
* @returns Array of ETH balances.
*/
async callAsync(
addresses: string[],
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<BigNumber[]> {
assert.isArray('addresses', addresses);
assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (defaultBlock !== undefined) {
assert.isBlockParam('defaultBlock', defaultBlock);
}
const self = (this as any) as EthBalanceCheckerContract;
const encodedData = self._strictEncodeArguments('getEthBalances(address[])', [addresses]);
const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...callData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
callDataWithDefaults.from = callDataWithDefaults.from
? callDataWithDefaults.from.toLowerCase()
: callDataWithDefaults.from;
const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock);
BaseContract._throwIfRevertWithReasonCallResult(rawCallResult);
const abiEncoder = self._lookupAbiEncoder('getEthBalances(address[])');
// tslint:disable boolean-naming
const result = abiEncoder.strictDecodeReturnValue<BigNumber[]>(rawCallResult);
// tslint:enable boolean-naming
return result;
},
/**
* Returns the ABI encoded transaction data needed to send an Ethereum transaction calling this method. Before
* sending the Ethereum tx, this encoded tx data can first be sent to a separate signing service or can be used
* to create a 0x transaction (see protocol spec for more details).
* @param addresses Array of addresses.
*/
getABIEncodedTransactionData(addresses: string[]): string {
assert.isArray('addresses', addresses);
const self = (this as any) as EthBalanceCheckerContract;
const abiEncodedTransactionData = self._strictEncodeArguments('getEthBalances(address[])', [addresses]);
return abiEncodedTransactionData;
},
getABIDecodedTransactionData(callData: string): BigNumber[] {
const self = (this as any) as EthBalanceCheckerContract;
const abiEncoder = self._lookupAbiEncoder('getEthBalances(address[])');
// tslint:disable boolean-naming
const abiDecodedCallData = abiEncoder.strictDecode<BigNumber[]>(callData);
return abiDecodedCallData;
},
getABIDecodedReturnData(returnData: string): BigNumber[] {
const self = (this as any) as EthBalanceCheckerContract;
const abiEncoder = self._lookupAbiEncoder('getEthBalances(address[])');
// tslint:disable boolean-naming
const abiDecodedReturnData = abiEncoder.strictDecodeReturnValue<BigNumber[]>(returnData);
return abiDecodedReturnData;
},
};
public static async deployFrom0xArtifactAsync(
artifact: ContractArtifact | SimpleContractArtifact,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<EthBalanceCheckerContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return EthBalanceCheckerContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractAbi },
): Promise<EthBalanceCheckerContract> {
assert.isHexString('bytecode', bytecode);
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
const iface = new ethers.utils.Interface(abi);
const deployInfo = iface.deployFunction;
const txData = deployInfo.encode(bytecode, []);
const web3Wrapper = new Web3Wrapper(provider);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{ data: txData },
txDefaults,
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`EthBalanceChecker successfully deployed at ${txReceipt.contractAddress}`);
const contractInstance = new EthBalanceCheckerContract(
txReceipt.contractAddress as string,
provider,
txDefaults,
logDecodeDependencies,
);
contractInstance.constructorArgs = [];
return contractInstance;
}
/**
* @returns The contract ABI
*/
public static ABI(): ContractAbi {
const abi = [
{
constant: true,
inputs: [
{
name: 'addresses',
type: 'address[]',
},
],
name: 'getEthBalances',
outputs: [
{
name: '',
type: 'uint256[]',
},
],
payable: false,
stateMutability: 'view',
type: 'function',
},
] as ContractAbi;
return abi;
}
constructor(
address: string,
supportedProvider: SupportedProvider,
txDefaults?: Partial<TxData>,
logDecodeDependencies?: { [contractName: string]: ContractAbi },
) {
super(
'EthBalanceChecker',
EthBalanceCheckerContract.ABI(),
address,
supportedProvider,
txDefaults,
logDecodeDependencies,
);
classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', '_web3Wrapper']);
}
}
// tslint:disable:max-file-line-count
// tslint:enable:no-unbound-method no-parameter-reassignment no-consecutive-blank-lines ordered-imports align
// tslint:enable:trailing-comma whitespace no-trailing-whitespace

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,234 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
// tslint:disable:no-unused-variable
import { BaseContract, PromiseWithTransactionHash } from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
BlockParam,
BlockParamLiteral,
CallData,
ContractAbi,
ContractArtifact,
DecodedLogArgs,
MethodAbi,
TransactionReceiptWithDecodedLogs,
TxData,
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
/* istanbul ignore next */
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class IValidatorContract extends BaseContract {
/**
* Verifies that a signature is valid.
*/
public isValidSignature = {
/**
* Sends a read-only call to the contract method. Returns the result that would happen if one were to send an
* Ethereum transaction to this method, given the current state of the blockchain. Calls do not cost gas
* since they don't modify state.
* @param hash Message hash that is signed.
* @param signerAddress Address that should have signed the given hash.
* @param signature Proof of signing.
* @returns Validity of order signature.
*/
async callAsync(
hash: string,
signerAddress: string,
signature: string,
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<boolean> {
assert.isString('hash', hash);
assert.isString('signerAddress', signerAddress);
assert.isString('signature', signature);
assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (defaultBlock !== undefined) {
assert.isBlockParam('defaultBlock', defaultBlock);
}
const self = (this as any) as IValidatorContract;
const encodedData = self._strictEncodeArguments('isValidSignature(bytes32,address,bytes)', [
hash,
signerAddress.toLowerCase(),
signature,
]);
const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...callData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
callDataWithDefaults.from = callDataWithDefaults.from
? callDataWithDefaults.from.toLowerCase()
: callDataWithDefaults.from;
const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock);
BaseContract._throwIfRevertWithReasonCallResult(rawCallResult);
const abiEncoder = self._lookupAbiEncoder('isValidSignature(bytes32,address,bytes)');
// tslint:disable boolean-naming
const result = abiEncoder.strictDecodeReturnValue<boolean>(rawCallResult);
// tslint:enable boolean-naming
return result;
},
/**
* Returns the ABI encoded transaction data needed to send an Ethereum transaction calling this method. Before
* sending the Ethereum tx, this encoded tx data can first be sent to a separate signing service or can be used
* to create a 0x transaction (see protocol spec for more details).
* @param hash Message hash that is signed.
* @param signerAddress Address that should have signed the given hash.
* @param signature Proof of signing.
*/
getABIEncodedTransactionData(hash: string, signerAddress: string, signature: string): string {
assert.isString('hash', hash);
assert.isString('signerAddress', signerAddress);
assert.isString('signature', signature);
const self = (this as any) as IValidatorContract;
const abiEncodedTransactionData = self._strictEncodeArguments('isValidSignature(bytes32,address,bytes)', [
hash,
signerAddress.toLowerCase(),
signature,
]);
return abiEncodedTransactionData;
},
getABIDecodedTransactionData(callData: string): boolean {
const self = (this as any) as IValidatorContract;
const abiEncoder = self._lookupAbiEncoder('isValidSignature(bytes32,address,bytes)');
// tslint:disable boolean-naming
const abiDecodedCallData = abiEncoder.strictDecode<boolean>(callData);
return abiDecodedCallData;
},
getABIDecodedReturnData(returnData: string): boolean {
const self = (this as any) as IValidatorContract;
const abiEncoder = self._lookupAbiEncoder('isValidSignature(bytes32,address,bytes)');
// tslint:disable boolean-naming
const abiDecodedReturnData = abiEncoder.strictDecodeReturnValue<boolean>(returnData);
return abiDecodedReturnData;
},
};
public static async deployFrom0xArtifactAsync(
artifact: ContractArtifact | SimpleContractArtifact,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<IValidatorContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return IValidatorContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractAbi },
): Promise<IValidatorContract> {
assert.isHexString('bytecode', bytecode);
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
const iface = new ethers.utils.Interface(abi);
const deployInfo = iface.deployFunction;
const txData = deployInfo.encode(bytecode, []);
const web3Wrapper = new Web3Wrapper(provider);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{ data: txData },
txDefaults,
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`IValidator successfully deployed at ${txReceipt.contractAddress}`);
const contractInstance = new IValidatorContract(
txReceipt.contractAddress as string,
provider,
txDefaults,
logDecodeDependencies,
);
contractInstance.constructorArgs = [];
return contractInstance;
}
/**
* @returns The contract ABI
*/
public static ABI(): ContractAbi {
const abi = [
{
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',
},
] as ContractAbi;
return abi;
}
constructor(
address: string,
supportedProvider: SupportedProvider,
txDefaults?: Partial<TxData>,
logDecodeDependencies?: { [contractName: string]: ContractAbi },
) {
super('IValidator', IValidatorContract.ABI(), address, supportedProvider, txDefaults, logDecodeDependencies);
classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', '_web3Wrapper']);
}
}
// tslint:disable:max-file-line-count
// tslint:enable:no-unbound-method no-parameter-reassignment no-consecutive-blank-lines ordered-imports align
// tslint:enable:trailing-comma whitespace no-trailing-whitespace

View File

@ -0,0 +1,220 @@
// tslint:disable:no-consecutive-blank-lines ordered-imports align trailing-comma
// tslint:disable:whitespace no-unbound-method no-trailing-whitespace
// tslint:disable:no-unused-variable
import { BaseContract, PromiseWithTransactionHash } from '@0x/base-contract';
import { schemas } from '@0x/json-schemas';
import {
BlockParam,
BlockParamLiteral,
CallData,
ContractAbi,
ContractArtifact,
DecodedLogArgs,
MethodAbi,
TransactionReceiptWithDecodedLogs,
TxData,
TxDataPayable,
SupportedProvider,
} from 'ethereum-types';
import { BigNumber, classUtils, logUtils, providerUtils } from '@0x/utils';
import { SimpleContractArtifact } from '@0x/types';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { assert } from '@0x/assert';
import * as ethers from 'ethers';
// tslint:enable:no-unused-variable
/* istanbul ignore next */
// tslint:disable:no-parameter-reassignment
// tslint:disable-next-line:class-name
export class IWalletContract extends BaseContract {
/**
* Verifies that a signature is valid.
*/
public isValidSignature = {
/**
* Sends a read-only call to the contract method. Returns the result that would happen if one were to send an
* Ethereum transaction to this method, given the current state of the blockchain. Calls do not cost gas
* since they don't modify state.
* @param hash Message hash that is signed.
* @param signature Proof of signing.
* @returns Validity of order signature.
*/
async callAsync(
hash: string,
signature: string,
callData: Partial<CallData> = {},
defaultBlock?: BlockParam,
): Promise<boolean> {
assert.isString('hash', hash);
assert.isString('signature', signature);
assert.doesConformToSchema('callData', callData, schemas.callDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (defaultBlock !== undefined) {
assert.isBlockParam('defaultBlock', defaultBlock);
}
const self = (this as any) as IWalletContract;
const encodedData = self._strictEncodeArguments('isValidSignature(bytes32,bytes)', [hash, signature]);
const callDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{
to: self.address,
...callData,
data: encodedData,
},
self._web3Wrapper.getContractDefaults(),
);
callDataWithDefaults.from = callDataWithDefaults.from
? callDataWithDefaults.from.toLowerCase()
: callDataWithDefaults.from;
const rawCallResult = await self._web3Wrapper.callAsync(callDataWithDefaults, defaultBlock);
BaseContract._throwIfRevertWithReasonCallResult(rawCallResult);
const abiEncoder = self._lookupAbiEncoder('isValidSignature(bytes32,bytes)');
// tslint:disable boolean-naming
const result = abiEncoder.strictDecodeReturnValue<boolean>(rawCallResult);
// tslint:enable boolean-naming
return result;
},
/**
* Returns the ABI encoded transaction data needed to send an Ethereum transaction calling this method. Before
* sending the Ethereum tx, this encoded tx data can first be sent to a separate signing service or can be used
* to create a 0x transaction (see protocol spec for more details).
* @param hash Message hash that is signed.
* @param signature Proof of signing.
*/
getABIEncodedTransactionData(hash: string, signature: string): string {
assert.isString('hash', hash);
assert.isString('signature', signature);
const self = (this as any) as IWalletContract;
const abiEncodedTransactionData = self._strictEncodeArguments('isValidSignature(bytes32,bytes)', [
hash,
signature,
]);
return abiEncodedTransactionData;
},
getABIDecodedTransactionData(callData: string): boolean {
const self = (this as any) as IWalletContract;
const abiEncoder = self._lookupAbiEncoder('isValidSignature(bytes32,bytes)');
// tslint:disable boolean-naming
const abiDecodedCallData = abiEncoder.strictDecode<boolean>(callData);
return abiDecodedCallData;
},
getABIDecodedReturnData(returnData: string): boolean {
const self = (this as any) as IWalletContract;
const abiEncoder = self._lookupAbiEncoder('isValidSignature(bytes32,bytes)');
// tslint:disable boolean-naming
const abiDecodedReturnData = abiEncoder.strictDecodeReturnValue<boolean>(returnData);
return abiDecodedReturnData;
},
};
public static async deployFrom0xArtifactAsync(
artifact: ContractArtifact | SimpleContractArtifact,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractArtifact | SimpleContractArtifact },
): Promise<IWalletContract> {
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
if (artifact.compilerOutput === undefined) {
throw new Error('Compiler output not found in the artifact file');
}
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const bytecode = artifact.compilerOutput.evm.bytecode.object;
const abi = artifact.compilerOutput.abi;
const logDecodeDependenciesAbiOnly: { [contractName: string]: ContractAbi } = {};
if (Object.keys(logDecodeDependencies) !== undefined) {
for (const key of Object.keys(logDecodeDependencies)) {
logDecodeDependenciesAbiOnly[key] = logDecodeDependencies[key].compilerOutput.abi;
}
}
return IWalletContract.deployAsync(bytecode, abi, provider, txDefaults, logDecodeDependenciesAbiOnly);
}
public static async deployAsync(
bytecode: string,
abi: ContractAbi,
supportedProvider: SupportedProvider,
txDefaults: Partial<TxData>,
logDecodeDependencies: { [contractName: string]: ContractAbi },
): Promise<IWalletContract> {
assert.isHexString('bytecode', bytecode);
assert.doesConformToSchema('txDefaults', txDefaults, schemas.txDataSchema, [
schemas.addressSchema,
schemas.numberSchema,
schemas.jsNumber,
]);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
const constructorAbi = BaseContract._lookupConstructorAbi(abi);
[] = BaseContract._formatABIDataItemList(constructorAbi.inputs, [], BaseContract._bigNumberToString);
const iface = new ethers.utils.Interface(abi);
const deployInfo = iface.deployFunction;
const txData = deployInfo.encode(bytecode, []);
const web3Wrapper = new Web3Wrapper(provider);
const txDataWithDefaults = await BaseContract._applyDefaultsToTxDataAsync(
{ data: txData },
txDefaults,
web3Wrapper.estimateGasAsync.bind(web3Wrapper),
);
const txHash = await web3Wrapper.sendTransactionAsync(txDataWithDefaults);
logUtils.log(`transactionHash: ${txHash}`);
const txReceipt = await web3Wrapper.awaitTransactionSuccessAsync(txHash);
logUtils.log(`IWallet successfully deployed at ${txReceipt.contractAddress}`);
const contractInstance = new IWalletContract(
txReceipt.contractAddress as string,
provider,
txDefaults,
logDecodeDependencies,
);
contractInstance.constructorArgs = [];
return contractInstance;
}
/**
* @returns The contract ABI
*/
public static ABI(): ContractAbi {
const abi = [
{
constant: true,
inputs: [
{
name: 'hash',
type: 'bytes32',
},
{
name: 'signature',
type: 'bytes',
},
],
name: 'isValidSignature',
outputs: [
{
name: 'isValid',
type: 'bool',
},
],
payable: false,
stateMutability: 'view',
type: 'function',
},
] as ContractAbi;
return abi;
}
constructor(
address: string,
supportedProvider: SupportedProvider,
txDefaults?: Partial<TxData>,
logDecodeDependencies?: { [contractName: string]: ContractAbi },
) {
super('IWallet', IWalletContract.ABI(), address, supportedProvider, txDefaults, logDecodeDependencies);
classUtils.bindAll(this, ['_abiEncoderByFunctionSignature', 'address', '_web3Wrapper']);
}
}
// tslint:disable:max-file-line-count
// tslint:enable:no-unbound-method no-parameter-reassignment no-consecutive-blank-lines ordered-imports align
// tslint:enable:trailing-comma whitespace no-trailing-whitespace

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,124 +1,26 @@
export { ContractAddresses } from '@0x/contract-addresses';
export * from './generated-wrappers/asset_proxy_owner';
export * from './generated-wrappers/dummy_erc20_token';
export * from './generated-wrappers/dummy_erc721_token';
export * from './generated-wrappers/dutch_auction';
export * from './generated-wrappers/erc20_proxy';
export * from './generated-wrappers/erc20_token';
export * from './generated-wrappers/erc721_proxy';
export * from './generated-wrappers/erc721_token';
export * from './generated-wrappers/exchange';
export * from './generated-wrappers/forwarder';
export * from './generated-wrappers/i_asset_proxy';
export * from './generated-wrappers/i_validator';
export * from './generated-wrappers/i_wallet';
export * from './generated-wrappers/multi_asset_proxy';
export * from './generated-wrappers/order_validator';
export * from './generated-wrappers/weth9';
export * from './generated-wrappers/zrx_token';
export * from './generated-wrappers/coordinator';
export * from './generated-wrappers/coordinator_registry';
export * from './generated-wrappers/eth_balance_checker';
export {
WETH9Events,
WETH9WithdrawalEventArgs,
WETH9ApprovalEventArgs,
WETH9EventArgs,
WETH9DepositEventArgs,
WETH9TransferEventArgs,
ERC20TokenTransferEventArgs,
ERC20TokenApprovalEventArgs,
ERC20TokenEvents,
ERC20TokenEventArgs,
ERC721TokenApprovalEventArgs,
ERC721TokenApprovalForAllEventArgs,
ERC721TokenTransferEventArgs,
ERC721TokenEvents,
ERC721TokenEventArgs,
ExchangeCancelUpToEventArgs,
ExchangeAssetProxyRegisteredEventArgs,
ExchangeSignatureValidatorApprovalEventArgs,
ExchangeFillEventArgs,
ExchangeCancelEventArgs,
ExchangeEventArgs,
ExchangeEvents,
} from '@0x/abi-gen-wrappers';
export * from '@0x/contract-addresses';
export { ContractWrappers } from './contract_wrappers';
export { CoordinatorWrapper } from './contract_wrappers/coordinator_wrapper';
export { ERC20TokenWrapper } from './contract_wrappers/erc20_token_wrapper';
export { ERC721TokenWrapper } from './contract_wrappers/erc721_token_wrapper';
export { EtherTokenWrapper } from './contract_wrappers/ether_token_wrapper';
export { ExchangeWrapper } from './contract_wrappers/exchange_wrapper';
export { ERC20ProxyWrapper } from './contract_wrappers/erc20_proxy_wrapper';
export { ERC721ProxyWrapper } from './contract_wrappers/erc721_proxy_wrapper';
export { ForwarderWrapper } from './contract_wrappers/forwarder_wrapper';
export { OrderValidatorWrapper } from './contract_wrappers/order_validator_wrapper';
export { DutchAuctionWrapper } from './contract_wrappers/dutch_auction_wrapper';
export { TransactionEncoder } from './utils/transaction_encoder';
export { AbiDecoder, DecodedCalldata } from '@0x/utils';
export {
ContractWrappersError,
ForwarderWrapperError,
IndexedFilterValues,
BlockRange,
ContractWrappersConfig,
MethodOpts,
OrderTransactionOpts,
TransactionOpts,
OrderStatus,
OrderInfo,
EventCallback,
DecodedLogEvent,
BalanceAndAllowance,
OrderAndTraderInfo,
TraderInfo,
ValidateOrderFillableOpts,
CoordinatorServerCancellationResponse,
CoordinatorServerError,
} from './types';
export {
AssetData,
ERC20AssetData,
ERC721AssetData,
ERC1155AssetData,
SingleAssetData,
MultiAssetData,
StaticCallAssetData,
MultiAssetDataWithRecursiveDecoding,
DutchAuctionDetails,
DutchAuctionData,
Order,
SignedOrder,
AssetProxyId,
SignedZeroExTransaction,
ZeroExTransaction,
} from '@0x/types';
export {
BlockParamLiteral,
BlockParam,
ContractEventArg,
SupportedProvider,
ContractAbi,
JSONRPCRequestPayload,
JSONRPCResponsePayload,
JSONRPCErrorCallback,
JSONRPCResponseError,
AbiDefinition,
LogWithDecodedArgs,
LogEntry,
DecodedLogEntry,
DecodedLogEntryEvent,
LogEntryEvent,
RawLog,
FunctionAbi,
EventAbi,
EventParameter,
DecodedLogArgs,
MethodAbi,
ConstructorAbi,
FallbackAbi,
DataItem,
TupleDataItem,
ConstructorStateMutability,
StateMutability,
Web3JsProvider,
GanacheProvider,
EIP1193Provider,
ZeroExProvider,
EIP1193Event,
Web3JsV1Provider,
Web3JsV2Provider,
Web3JsV3Provider,
} from 'ethereum-types';
export { AbstractBalanceAndProxyAllowanceFetcher, AbstractOrderFilledCancelledFetcher } from '@0x/order-utils';
export { AssetBalanceAndProxyAllowanceFetcher } from './fetchers/asset_balance_and_proxy_allowance_fetcher';
export { OrderFilledCancelledFetcher } from './fetchers/order_filled_cancelled_fetcher';
export { CoordinatorWrapper } from './coordinator_wrapper';
export { OrderStatus } from './types';

View File

@ -1,7 +0,0 @@
export const methodOptsSchema = {
id: '/MethodOpts',
properties: {
defaultBlock: { $ref: '/blockParamSchema' },
},
type: 'object',
};

View File

@ -1,7 +0,0 @@
export const validateOrderFillableOptsSchema = {
id: '/ValidateOrderFillableOpts',
properties: {
expectedFillTakerTokenAmount: { $ref: '/wholeNumberSchema' },
},
type: 'object',
};

View File

@ -1,47 +1,8 @@
import {
ERC20TokenEventArgs,
ERC20TokenEvents,
ERC721TokenEventArgs,
ERC721TokenEvents,
ExchangeEventArgs,
ExchangeEvents,
WETH9EventArgs,
WETH9Events,
} from '@0x/abi-gen-wrappers';
import { ContractAddresses } from '@0x/contract-addresses';
import { OrderState, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { BlockParam, ContractEventArg, DecodedLogArgs, LogEntryEvent, LogWithDecodedArgs } from 'ethereum-types';
import { ContractEventArg, DecodedLogArgs, LogWithDecodedArgs } from 'ethereum-types';
export enum ExchangeWrapperError {
AssetDataMismatch = 'ASSET_DATA_MISMATCH',
}
export enum ForwarderWrapperError {
CompleteFillFailed = 'COMPLETE_FILL_FAILED',
}
export enum ContractWrappersError {
ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
InvalidJump = 'INVALID_JUMP',
OutOfGas = 'OUT_OF_GAS',
SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT',
ERC721OwnerNotFound = 'ERC_721_OWNER_NOT_FOUND',
ERC721NoApproval = 'ERC_721_NO_APPROVAL',
SignatureRequestDenied = 'SIGNATURE_REQUEST_DENIED',
}
export enum InternalContractWrappersError {
NoAbiDecoder = 'NO_ABI_DECODER',
}
export type LogEvent = LogEntryEvent;
export interface DecodedLogEvent<ArgsType extends DecodedLogArgs> {
isRemoved: boolean;
log: LogWithDecodedArgs<ArgsType>;
@ -52,30 +13,6 @@ export type EventCallback<ArgsType extends DecodedLogArgs> = (
log?: DecodedLogEvent<ArgsType>,
) => void;
export interface ContractEvent {
logIndex: number;
transactionIndex: number;
transactionHash: string;
blockHash: string;
blockNumber: number;
address: string;
type: string;
event: string;
args: ContractEventArgs;
}
export type ContractEventArgs = ExchangeEventArgs | ERC20TokenEventArgs | ERC721TokenEventArgs | WETH9EventArgs;
// [address, name, symbol, decimals, ipfsHash, swarmHash]
export type TokenMetadata = [string, string, string, number, string, string];
export interface Token {
name: string;
address: string;
symbol: string;
decimals: number;
}
export interface TxOpts {
from: string;
gas?: number;
@ -83,29 +20,10 @@ export interface TxOpts {
gasPrice?: BigNumber;
}
export interface TokenAddressBySymbol {
[symbol: string]: string;
}
export type ContractEvents = ERC20TokenEvents | ERC721TokenEvents | ExchangeEvents | WETH9Events;
export interface IndexedFilterValues {
[index: string]: ContractEventArg;
}
export interface BlockRange {
fromBlock: BlockParam;
toBlock: BlockParam;
}
export interface OrderFillRequest {
signedOrder: SignedOrder;
takerAssetFillAmount: BigNumber;
}
export type AsyncMethod = (...args: any[]) => Promise<any>;
export type SyncMethod = (...args: any[]) => any;
/**
* networkId: The id of the underlying ethereum network your provider is connected to. (1-mainnet, 3-ropsten, 4-rinkeby, 42-kovan, 50-testrpc)
* gasPrice: Gas price to use with every transaction
@ -119,35 +37,6 @@ export interface ContractWrappersConfig {
blockPollingIntervalMs?: number;
}
/**
* `expectedFillTakerTokenAmount`: If specified, the validation method will ensure that the supplied order maker has a sufficient
* allowance/balance to fill this amount of the order's takerTokenAmount.
*
* `validateRemainingOrderAmountIsFillable`: The validation method ensures that the maker has sufficient allowance/balance to fill
* the entire remaining order amount. If this option is set to false, the balances
* and allowances are calculated to determine the order is fillable for a
* non-zero amount (some value less than or equal to the order remaining amount).
* We call such orders "partially fillable orders". Default is `true`.
*
* `simulationTakerAddress`: During the maker transfer simulation, tokens are sent from the maker to the `simulationTakerAddress`. This defaults
* to the `takerAddress` specified in the order. Some tokens prevent transfer to the NULL address so this address can be specified.
*/
export interface ValidateOrderFillableOpts {
expectedFillTakerTokenAmount?: BigNumber;
validateRemainingOrderAmountIsFillable?: boolean;
simulationTakerAddress?: string;
}
/**
* defaultBlock: The block up to which to query the blockchain state. Setting this to a historical block number
* let's the user query the blockchain's state at an arbitrary point in time. In order for this to work, the
* backing Ethereum node must keep the entire historical state of the chain (e.g setting `--pruning=archive`
* flag when running Parity).
*/
export interface MethodOpts {
defaultBlock?: BlockParam;
}
/**
* gasPrice: Gas price in Wei to use for a transaction
* gasLimit: The amount of gas to send with a transaction (in Gwei)
@ -167,18 +56,6 @@ export interface OrderTransactionOpts extends TransactionOpts {
shouldValidate?: boolean;
}
export enum TradeSide {
Maker = 'maker',
Taker = 'taker',
}
export enum TransferType {
Trade = 'trade',
Fee = 'fee',
}
export type OnOrderStateChangeCallback = (err: Error | null, orderState?: OrderState) => void;
export interface OrderInfo {
orderStatus: OrderStatus;
orderHash: string;
@ -195,31 +72,6 @@ export enum OrderStatus {
Cancelled,
}
export interface TraderInfo {
makerBalance: BigNumber;
makerAllowance: BigNumber;
takerBalance: BigNumber;
takerAllowance: BigNumber;
makerZrxBalance: BigNumber;
makerZrxAllowance: BigNumber;
takerZrxBalance: BigNumber;
takerZrxAllowance: BigNumber;
}
export interface OrderAndTraderInfo {
orderInfo: OrderInfo;
traderInfo: TraderInfo;
}
export interface BalanceAndAllowance {
balance: BigNumber;
allowance: BigNumber;
}
export enum DutchAuctionWrapperError {
AssetDataMismatch = 'ASSET_DATA_MISMATCH',
}
export { CoordinatorServerCancellationResponse, CoordinatorServerError } from './utils/coordinator_server_types';
export interface CoordinatorTransaction {

View File

@ -8,7 +8,7 @@ import { Web3Wrapper } from '@0x/web3-wrapper';
import { SupportedProvider } from 'ethereum-types';
import * as _ from 'lodash';
import { constants } from './constants';
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
export const assert = {
...sharedAssert,
@ -56,7 +56,7 @@ export const assert = {
}
},
allTakerAddressesAreNull(orders: Order[]): void {
assert.ordersHaveAtMostOneUniqueValueForProperty(orders, 'takerAddress', constants.NULL_ADDRESS);
assert.ordersHaveAtMostOneUniqueValueForProperty(orders, 'takerAddress', NULL_ADDRESS);
},
allMakerAssetDatasAreErc20Token(orders: Order[], tokenAddress: string): void {
assert.ordersHaveAtMostOneUniqueValueForProperty(

View File

@ -1,44 +0,0 @@
import { SignedOrder } from '@0x/types';
import * as _ from 'lodash';
import { constants } from './constants';
export const calldataOptimizationUtils = {
/**
* Takes an array of orders and outputs an array of equivalent orders where all takerAssetData are '0x' and
* all makerAssetData are '0x' except for that of the first order, which retains its original value
* @param orders An array of SignedOrder objects
* @returns optimized orders
*/
optimizeForwarderOrders(orders: SignedOrder[]): SignedOrder[] {
const optimizedOrders = _.map(orders, (order, index) =>
transformOrder(order, {
makerAssetData: index === 0 ? order.makerAssetData : constants.NULL_BYTES,
takerAssetData: constants.NULL_BYTES,
}),
);
return optimizedOrders;
},
/**
* Takes an array of orders and outputs an array of equivalent orders where all takerAssetData are '0x' and
* all makerAssetData are '0x'
* @param orders An array of SignedOrder objects
* @returns optimized orders
*/
optimizeForwarderFeeOrders(orders: SignedOrder[]): SignedOrder[] {
const optimizedOrders = _.map(orders, (order, index) =>
transformOrder(order, {
makerAssetData: constants.NULL_BYTES,
takerAssetData: constants.NULL_BYTES,
}),
);
return optimizedOrders;
},
};
const transformOrder = (order: SignedOrder, partialOrder: Partial<SignedOrder>) => {
return {
...order,
...partialOrder,
};
};

View File

@ -1,19 +0,0 @@
import { BigNumber } from '@0x/utils';
export const constants = {
NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
NULL_BYTES: '0x',
TESTRPC_NETWORK_ID: 50,
INVALID_JUMP_PATTERN: 'invalid JUMP at',
REVERT: 'revert',
OUT_OF_GAS_PATTERN: 'out of gas',
INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string',
// tslint:disable-next-line:custom-no-magic-numbers
UNLIMITED_ALLOWANCE_IN_BASE_UNITS: new BigNumber(2).pow(256).minus(1),
DEFAULT_BLOCK_POLLING_INTERVAL: 1000,
ZERO_AMOUNT: new BigNumber(0),
ONE_AMOUNT: new BigNumber(1),
ETHER_TOKEN_DECIMALS: 18,
METAMASK_USER_DENIED_SIGNATURE_PATTERN: 'User denied transaction signature',
TRUST_WALLET_USER_DENIED_SIGNATURE_PATTERN: 'cancelled',
};

View File

@ -1,8 +1,31 @@
import * as _ from 'lodash';
import { AsyncMethod, ContractWrappersError, SyncMethod } from '../types';
export enum ContractWrappersError {
ContractNotDeployedOnNetwork = 'CONTRACT_NOT_DEPLOYED_ON_NETWORK',
InsufficientAllowanceForTransfer = 'INSUFFICIENT_ALLOWANCE_FOR_TRANSFER',
InsufficientBalanceForTransfer = 'INSUFFICIENT_BALANCE_FOR_TRANSFER',
InsufficientEthBalanceForDeposit = 'INSUFFICIENT_ETH_BALANCE_FOR_DEPOSIT',
InsufficientWEthBalanceForWithdrawal = 'INSUFFICIENT_WETH_BALANCE_FOR_WITHDRAWAL',
InvalidJump = 'INVALID_JUMP',
OutOfGas = 'OUT_OF_GAS',
SubscriptionNotFound = 'SUBSCRIPTION_NOT_FOUND',
SubscriptionAlreadyPresent = 'SUBSCRIPTION_ALREADY_PRESENT',
ERC721OwnerNotFound = 'ERC_721_OWNER_NOT_FOUND',
ERC721NoApproval = 'ERC_721_NO_APPROVAL',
SignatureRequestDenied = 'SIGNATURE_REQUEST_DENIED',
}
import { constants } from './constants';
export type AsyncMethod = (...args: any[]) => Promise<any>;
export type SyncMethod = (...args: any[]) => any;
const constants = {
INVALID_JUMP_PATTERN: 'invalid JUMP at',
REVERT: 'revert',
OUT_OF_GAS_PATTERN: 'out of gas',
INVALID_TAKER_FORMAT: 'instance.taker is not of a type(s) string',
METAMASK_USER_DENIED_SIGNATURE_PATTERN: 'User denied transaction signature',
TRUST_WALLET_USER_DENIED_SIGNATURE_PATTERN: 'cancelled',
};
type ErrorTransformer = (err: Error) => Error;

View File

@ -1,87 +0,0 @@
import { ContractAbi, EventAbi, FilterObject, LogEntry } from 'ethereum-types';
import * as ethUtil from 'ethereumjs-util';
import * as jsSHA3 from 'js-sha3';
import * as _ from 'lodash';
import * as uuid from 'uuid/v4';
import { BlockRange, ContractEvents, IndexedFilterValues } from '../types';
const TOPIC_LENGTH = 32;
export const filterUtils = {
generateUUID(): string {
return uuid();
},
getFilter(
address: string,
eventName: ContractEvents,
indexFilterValues: IndexedFilterValues,
abi: ContractAbi,
blockRange?: BlockRange,
): FilterObject {
const eventAbi = _.find(abi, { name: eventName }) as EventAbi;
const eventSignature = filterUtils.getEventSignatureFromAbiByName(eventAbi);
const topicForEventSignature = ethUtil.addHexPrefix(jsSHA3.keccak256(eventSignature));
const topicsForIndexedArgs = filterUtils.getTopicsForIndexedArgs(eventAbi, indexFilterValues);
const topics = [topicForEventSignature, ...topicsForIndexedArgs];
let filter: FilterObject = {
address,
topics,
};
if (blockRange !== undefined) {
filter = {
...blockRange,
...filter,
};
}
return filter;
},
getEventSignatureFromAbiByName(eventAbi: EventAbi): string {
const types = _.map(eventAbi.inputs, 'type');
const signature = `${eventAbi.name}(${types.join(',')})`;
return signature;
},
getTopicsForIndexedArgs(abi: EventAbi, indexFilterValues: IndexedFilterValues): Array<string | null> {
const topics: Array<string | null> = [];
for (const eventInput of abi.inputs) {
if (!eventInput.indexed) {
continue;
}
if (indexFilterValues[eventInput.name] === undefined) {
// Null is a wildcard topic in a JSON-RPC call
topics.push(null);
} else {
const value = indexFilterValues[eventInput.name] as string;
const buffer = ethUtil.toBuffer(value);
const paddedBuffer = ethUtil.setLengthLeft(buffer, TOPIC_LENGTH);
const topic = ethUtil.bufferToHex(paddedBuffer);
topics.push(topic);
}
}
return topics;
},
matchesFilter(log: LogEntry, filter: FilterObject): boolean {
if (filter.address !== undefined && log.address !== filter.address) {
return false;
}
if (filter.topics !== undefined) {
return filterUtils.doesMatchTopics(log.topics, filter.topics);
}
return true;
},
doesMatchTopics(logTopics: string[], filterTopics: Array<string[] | string | null>): boolean {
const matchesTopic = _.zipWith(logTopics, filterTopics, filterUtils.matchesTopic.bind(filterUtils));
const doesMatchTopics = _.every(matchesTopic);
return doesMatchTopics;
},
matchesTopic(logTopic: string, filterTopic: string[] | string | null): boolean {
if (_.isArray(filterTopic)) {
return _.includes(filterTopic, logTopic);
}
if (_.isString(filterTopic)) {
return filterTopic === logTopic;
}
// null topic is a wildcard
return true;
},
};

View File

@ -0,0 +1,24 @@
import { ExchangeContract } from '../index';
/**
* Returns the ABI encoded transaction hash for a given method and arguments
* @param methodName Must be a valid name of a function in the Solidity Exchange Contract
* @param params The appropriate arguments for the method given, in order
*/
export function getAbiEncodedTransactionData<K extends keyof ExchangeContract>(
contractInstance: ExchangeContract,
methodName: K,
...params: any[] // tslint:disable-line:trailing-comma
): string {
// HACK (xianny): we haven't formalised contract method functions into a type interface, and would have to
// differentiate contract method members from other class members to get this to work non-hackily
const method = (contractInstance[methodName] as any) as {
getABIEncodedTransactionData: (...args: any[]) => string;
};
if (method.getABIEncodedTransactionData) {
const abiEncodedData = method.getABIEncodedTransactionData(...params);
return abiEncodedData;
} else {
return '';
}
}

View File

@ -1,297 +0,0 @@
import { ExchangeContract } from '@0x/abi-gen-wrappers';
import { schemas } from '@0x/json-schemas';
import { transactionHashUtils } from '@0x/order-utils';
import { Order, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import _ = require('lodash');
import { assert } from './assert';
/**
* Transaction Encoder. Transaction messages exist for the purpose of calling methods on the Exchange contract
* in the context of another address. For example, UserA can encode and sign a fillOrder transaction and UserB
* can submit this to the blockchain. The Exchange context executes as if UserA had directly submitted this transaction.
*/
export class TransactionEncoder {
private readonly _exchangeInstance: ExchangeContract;
constructor(exchangeInstance: ExchangeContract) {
this._exchangeInstance = exchangeInstance;
}
/**
* Hashes the transaction data for use with the Exchange contract.
* @param data The ABI Encoded 0x Exchange method. I.e fillOrder
* @param salt A random value to provide uniqueness and prevent replay attacks.
* @param signerAddress The address which will sign this transaction.
* @return The hash of the 0x transaction.
*/
public getTransactionHashHex(data: string, salt: BigNumber, signerAddress: string): string {
const exchangeAddress = this._getExchangeContract().address;
const transaction = {
verifyingContractAddress: exchangeAddress,
salt,
signerAddress,
data,
};
const hashHex = transactionHashUtils.getTransactionHashHex(transaction);
return hashHex;
}
/**
* Encodes a fillOrder transaction.
* @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.
* @return Hex encoded abi of the function call.
*/
public fillOrderTx(signedOrder: SignedOrder, takerAssetFillAmount: BigNumber): string {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
const abiEncodedData = this._getExchangeContract().fillOrder.getABIEncodedTransactionData(
signedOrder,
takerAssetFillAmount,
signedOrder.signature,
);
return abiEncodedData;
}
/**
* Encodes a fillOrderNoThrow transaction.
* @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.
* @return Hex encoded abi of the function call.
*/
public fillOrderNoThrowTx(signedOrder: SignedOrder, takerAssetFillAmount: BigNumber): string {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
const abiEncodedData = this._getExchangeContract().fillOrderNoThrow.getABIEncodedTransactionData(
signedOrder,
takerAssetFillAmount,
signedOrder.signature,
);
return abiEncodedData;
}
/**
* Encodes a fillOrKillOrder transaction.
* @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.
* @return Hex encoded abi of the function call.
*/
public fillOrKillOrderTx(signedOrder: SignedOrder, takerAssetFillAmount: BigNumber): string {
assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
assert.isValidBaseUnitAmount('takerAssetFillAmount', takerAssetFillAmount);
const abiEncodedData = this._getExchangeContract().fillOrKillOrder.getABIEncodedTransactionData(
signedOrder,
takerAssetFillAmount,
signedOrder.signature,
);
return abiEncodedData;
}
/**
* Encodes a batchFillOrders 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.
* @return Hex encoded abi of the function call.
*/
public batchFillOrdersTx(signedOrders: SignedOrder[], takerAssetFillAmounts: BigNumber[]): string {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
_.forEach(takerAssetFillAmounts, takerAssetFillAmount =>
assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount),
);
const signatures = _.map(signedOrders, signedOrder => signedOrder.signature);
const abiEncodedData = this._getExchangeContract().batchFillOrders.getABIEncodedTransactionData(
signedOrders,
takerAssetFillAmounts,
signatures,
);
return abiEncodedData;
}
/**
* Encodes a batchFillOrKillOrders 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.
* @return Hex encoded abi of the function call.
*/
public batchFillOrKillOrdersTx(signedOrders: SignedOrder[], takerAssetFillAmounts: BigNumber[]): string {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
_.forEach(takerAssetFillAmounts, takerAssetFillAmount =>
assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount),
);
const signatures = _.map(signedOrders, signedOrder => signedOrder.signature);
const abiEncodedData = this._getExchangeContract().batchFillOrKillOrders.getABIEncodedTransactionData(
signedOrders,
takerAssetFillAmounts,
signatures,
);
return abiEncodedData;
}
/**
* Encodes a batchFillOrdersNoThrow 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.
* @return Hex encoded abi of the function call.
*/
public batchFillOrdersNoThrowTx(signedOrders: SignedOrder[], takerAssetFillAmounts: BigNumber[]): string {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
_.forEach(takerAssetFillAmounts, takerAssetFillAmount =>
assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount),
);
const signatures = _.map(signedOrders, signedOrder => signedOrder.signature);
const abiEncodedData = this._getExchangeContract().batchFillOrdersNoThrow.getABIEncodedTransactionData(
signedOrders,
takerAssetFillAmounts,
signatures,
);
return abiEncodedData;
}
/**
* Encodes a batchCancelOrders transaction.
* @param signedOrders An array of orders to cancel.
* @return Hex encoded abi of the function call.
*/
public batchCancelOrdersTx(signedOrders: SignedOrder[]): string {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
const abiEncodedData = this._getExchangeContract().batchCancelOrders.getABIEncodedTransactionData(signedOrders);
return abiEncodedData;
}
/**
* Encodes a cancelOrdersUpTo transaction.
* @param targetOrderEpoch Target order epoch.
* @return Hex encoded abi of the function call.
*/
public cancelOrdersUpToTx(targetOrderEpoch: BigNumber): string {
assert.isBigNumber('targetOrderEpoch', targetOrderEpoch);
const abiEncodedData = this._getExchangeContract().cancelOrdersUpTo.getABIEncodedTransactionData(
targetOrderEpoch,
);
return abiEncodedData;
}
/**
* Encodes a cancelOrder transaction.
* @param order An object that conforms to the Order or SignedOrder interface. The order you would like to cancel.
* @return Hex encoded abi of the function call.
*/
public cancelOrderTx(order: Order | SignedOrder): string {
assert.doesConformToSchema('order', order, schemas.orderSchema);
const abiEncodedData = this._getExchangeContract().cancelOrder.getABIEncodedTransactionData(order);
return abiEncodedData;
}
/**
* Encodes a marketSellOrders transaction.
* @param signedOrders An array of signed orders to fill.
* @param takerAssetFillAmount Taker asset fill amount.
* @return Hex encoded abi of the function call.
*/
public marketSellOrdersTx(signedOrders: SignedOrder[], takerAssetFillAmount: BigNumber): string {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount);
const signatures = _.map(signedOrders, signedOrder => signedOrder.signature);
const abiEncodedData = this._getExchangeContract().marketSellOrders.getABIEncodedTransactionData(
signedOrders,
takerAssetFillAmount,
signatures,
);
return abiEncodedData;
}
/**
* Encodes a marketSellOrdersNoThrow transaction.
* @param signedOrders An array of signed orders to fill.
* @param takerAssetFillAmount Taker asset fill amount.
* @return Hex encoded abi of the function call.
*/
public marketSellOrdersNoThrowTx(signedOrders: SignedOrder[], takerAssetFillAmount: BigNumber): string {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
assert.isBigNumber('takerAssetFillAmount', takerAssetFillAmount);
const signatures = _.map(signedOrders, signedOrder => signedOrder.signature);
const abiEncodedData = this._getExchangeContract().marketSellOrdersNoThrow.getABIEncodedTransactionData(
signedOrders,
takerAssetFillAmount,
signatures,
);
return abiEncodedData;
}
/**
* Encodes a maketBuyOrders transaction.
* @param signedOrders An array of signed orders to fill.
* @param makerAssetFillAmount Maker asset fill amount.
* @return Hex encoded abi of the function call.
*/
public marketBuyOrdersTx(signedOrders: SignedOrder[], makerAssetFillAmount: BigNumber): string {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
assert.isBigNumber('makerAssetFillAmount', makerAssetFillAmount);
const signatures = _.map(signedOrders, signedOrder => signedOrder.signature);
const abiEncodedData = this._getExchangeContract().marketBuyOrders.getABIEncodedTransactionData(
signedOrders,
makerAssetFillAmount,
signatures,
);
return abiEncodedData;
}
/**
* Encodes a maketBuyOrdersNoThrow transaction.
* @param signedOrders An array of signed orders to fill.
* @param makerAssetFillAmount Maker asset fill amount.
* @return Hex encoded abi of the function call.
*/
public marketBuyOrdersNoThrowTx(signedOrders: SignedOrder[], makerAssetFillAmount: BigNumber): string {
assert.doesConformToSchema('signedOrders', signedOrders, schemas.signedOrdersSchema);
assert.isBigNumber('makerAssetFillAmount', makerAssetFillAmount);
const signatures = _.map(signedOrders, signedOrder => signedOrder.signature);
const abiEncodedData = this._getExchangeContract().marketBuyOrdersNoThrow.getABIEncodedTransactionData(
signedOrders,
makerAssetFillAmount,
signatures,
);
return abiEncodedData;
}
/**
* Encodes a matchOrders transaction.
* @param leftOrder First order to match.
* @param rightOrder Second order to match.
* @return Hex encoded abi of the function call.
*/
public matchOrdersTx(leftOrder: SignedOrder, rightOrder: SignedOrder): string {
assert.doesConformToSchema('leftOrder', leftOrder, schemas.orderSchema);
assert.doesConformToSchema('rightOrder', rightOrder, schemas.orderSchema);
const abiEncodedData = this._getExchangeContract().matchOrders.getABIEncodedTransactionData(
leftOrder,
rightOrder,
leftOrder.signature,
rightOrder.signature,
);
return abiEncodedData;
}
/**
* Encodes a preSign transaction.
* @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.
* @return Hex encoded abi of the function call.
*/
public preSignTx(hash: string, signerAddress: string, signature: string): string {
assert.isHexString('hash', hash);
assert.isETHAddressHex('signerAddress', signerAddress);
assert.isHexString('signature', signature);
const abiEncodedData = this._getExchangeContract().preSign.getABIEncodedTransactionData(
hash,
signerAddress,
signature,
);
return abiEncodedData;
}
/**
* Encodes a setSignatureValidatorApproval transaction.
* @param validatorAddress Validator contract address.
* @param isApproved Boolean value to set approval to.
* @return Hex encoded abi of the function call.
*/
public setSignatureValidatorApprovalTx(validatorAddress: string, isApproved: boolean): string {
assert.isETHAddressHex('validatorAddress', validatorAddress);
assert.isBoolean('isApproved', isApproved);
const abiEncodedData = this._getExchangeContract().setSignatureValidatorApproval.getABIEncodedTransactionData(
validatorAddress,
isApproved,
);
return abiEncodedData;
}
private _getExchangeContract(): ExchangeContract {
return this._exchangeInstance;
}
}

View File

@ -1,23 +0,0 @@
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { constants } from './constants';
export const utils = {
getCurrentUnixTimestampSec(): BigNumber {
const milisecondsInSecond = 1000;
return new BigNumber(Date.now() / milisecondsInSecond).integerValue();
},
getCurrentUnixTimestampMs(): BigNumber {
return new BigNumber(Date.now());
},
numberPercentageToEtherTokenAmountPercentage(percentage: number): BigNumber {
return Web3Wrapper.toBaseUnitAmount(constants.ONE_AMOUNT, constants.ETHER_TOKEN_DECIMALS).multipliedBy(
percentage,
);
},
removeUndefinedProperties<T extends object>(obj: T): Partial<T> {
return _.pickBy(obj);
},
};

View File

@ -8,6 +8,7 @@ import * as _ from 'lodash';
import 'mocha';
import { ContractAddresses, ContractWrappers } from '../src';
import { getAbiEncodedTransactionData } from '../src/utils/getAbiEncodedTransactionData';
import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
@ -80,8 +81,14 @@ describe('ABI Decoding Calldata', () => {
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
const transactionEncoder = await contractWrappers.exchange.transactionEncoderAsync();
matchOrdersTxData = transactionEncoder.matchOrdersTx(signedOrderLeft, signedOrderRight);
matchOrdersTxData = getAbiEncodedTransactionData(
contractWrappers.exchange,
'matchOrders',
signedOrderLeft,
signedOrderRight,
signedOrderLeft.signature,
signedOrderRight.signature,
);
});
describe('decode', () => {

View File

@ -1,59 +0,0 @@
import { orderFactory } from '@0x/order-utils/lib/src/order_factory';
import * as chai from 'chai';
import * as _ from 'lodash';
import 'mocha';
import { calldataOptimizationUtils } from '../src/utils/calldata_optimization_utils';
import { constants } from '../src/utils/constants';
import { chaiSetup } from './utils/chai_setup';
chaiSetup.configure();
const expect = chai.expect;
// utility for generating a set of order objects with mostly NULL values
// except for a specified makerAssetData and takerAssetData
const FAKE_ORDERS_COUNT = 5;
const generateFakeOrders = (makerAssetData: string, takerAssetData: string) =>
_.map(_.range(FAKE_ORDERS_COUNT), index => {
const order = orderFactory.createOrder(
constants.NULL_ADDRESS,
constants.ZERO_AMOUNT,
makerAssetData,
constants.ZERO_AMOUNT,
takerAssetData,
constants.NULL_ADDRESS,
);
return {
...order,
signature: 'dummy signature',
};
});
describe('calldataOptimizationUtils', () => {
const fakeMakerAssetData = 'fakeMakerAssetData';
const fakeTakerAssetData = 'fakeTakerAssetData';
const orders = generateFakeOrders(fakeMakerAssetData, fakeTakerAssetData);
describe('#optimizeForwarderOrders', () => {
it('should make makerAssetData `0x` unless first order', () => {
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderOrders(orders);
expect(optimizedOrders[0].makerAssetData).to.equal(fakeMakerAssetData);
const ordersWithoutHead = _.slice(optimizedOrders, 1);
_.forEach(ordersWithoutHead, order => expect(order.makerAssetData).to.equal(constants.NULL_BYTES));
});
it('should make all takerAssetData `0x`', () => {
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderOrders(orders);
_.forEach(optimizedOrders, order => expect(order.takerAssetData).to.equal(constants.NULL_BYTES));
});
});
describe('#optimizeForwarderFeeOrders', () => {
it('should make all makerAssetData `0x`', () => {
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(orders);
_.forEach(optimizedOrders, order => expect(order.makerAssetData).to.equal(constants.NULL_BYTES));
});
it('should make all takerAssetData `0x`', () => {
const optimizedOrders = calldataOptimizationUtils.optimizeForwarderFeeOrders(orders);
_.forEach(optimizedOrders, order => expect(order.takerAssetData).to.equal(constants.NULL_BYTES));
});
});
});

View File

@ -1,7 +1,7 @@
import { CoordinatorRegistryContract } from '@0x/abi-gen-wrappers';
import { constants } from '@0x/contracts-test-utils';
import { defaultOrmConfig, getAppAsync } from '@0x/coordinator-server';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { BlockchainLifecycle, tokenUtils } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
@ -16,7 +16,6 @@ import { CoordinatorServerErrorMsg } from '../src/utils/coordinator_server_types
import { chaiSetup } from './utils/chai_setup';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
@ -71,7 +70,7 @@ describe('CoordinatorWrapper', () => {
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
zrxTokenAddress = contractAddresses.zrxToken;
fillScenarios = new FillScenarios(
provider,
userAddresses,
@ -422,7 +421,7 @@ describe('CoordinatorWrapper', () => {
txHash = await contractWrappers.coordinator.hardCancelOrdersUpToAsync(targetOrderEpoch, makerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderEpoch = await contractWrappers.exchange.getOrderEpochAsync(
const orderEpoch = await contractWrappers.exchange.orderEpoch.callAsync(
makerAddress,
contractWrappers.coordinator.address,
);

View File

@ -1,143 +0,0 @@
import { expectTransactionFailedAsync, getLatestBlockTimestampAsync } from '@0x/contracts-test-utils';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { assetDataUtils } from '@0x/order-utils';
import { ERC20AssetData, RevertReason, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import { ContractWrappers, DutchAuctionWrapper } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { DutchAuctionUtils } from './utils/dutch_auction_utils';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:custom-no-magic-numbers
describe('DutchAuctionWrapper', () => {
const makerAssetAmount = new BigNumber(5);
const auctionEndTakerAmount = new BigNumber(10);
const auctionBeginTakerAmount = auctionEndTakerAmount.times(2);
const tenMinutesInSeconds = 10 * 60;
let contractWrappers: ContractWrappers;
let exchangeContractAddress: string;
let userAddresses: string[];
let makerAddress: string;
let takerAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let buyOrder: SignedOrder;
let sellOrder: SignedOrder;
let makerTokenAssetData: string;
let takerTokenAssetData: string;
let auctionBeginTimeSeconds: BigNumber;
let auctionEndTimeSeconds: BigNumber;
before(async () => {
// setup contract wrappers & addresses
const contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
[, makerAddress, takerAddress] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
// construct asset data for tokens being swapped
[makerTokenAssetData, takerTokenAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
// setup auction details in maker asset data
const currentBlockTimestamp: number = await getLatestBlockTimestampAsync();
auctionBeginTimeSeconds = new BigNumber(currentBlockTimestamp - tenMinutesInSeconds);
auctionEndTimeSeconds = new BigNumber(currentBlockTimestamp + tenMinutesInSeconds);
// create auction orders
const coinbase = userAddresses[0];
const dutchAuctionUtils = new DutchAuctionUtils(
web3Wrapper,
coinbase,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
);
sellOrder = await dutchAuctionUtils.createSignedSellOrderAsync(
auctionBeginTimeSeconds,
auctionEndTimeSeconds,
auctionBeginTakerAmount,
auctionEndTakerAmount,
makerAssetAmount,
makerTokenAssetData,
takerTokenAssetData,
makerAddress,
constants.NULL_ADDRESS,
);
buyOrder = await dutchAuctionUtils.createSignedBuyOrderAsync(sellOrder, takerAddress);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('.decodeDutchAuctionAssetData', () => {
it('decodes to the encoded values', async () => {
const encodedAssetData = DutchAuctionWrapper.encodeDutchAuctionAssetData(
makerTokenAssetData,
auctionBeginTimeSeconds,
makerAssetAmount,
);
const decodedAssetData = DutchAuctionWrapper.decodeDutchAuctionData(encodedAssetData);
// tslint:disable-next-line:no-unnecessary-type-assertion
const erc20AssetData = decodedAssetData.assetData as ERC20AssetData;
expect(erc20AssetData.tokenAddress).to.eq(makerTokenAddress);
expect(decodedAssetData.beginAmount).to.be.bignumber.eq(makerAssetAmount);
expect(decodedAssetData.beginTimeSeconds).to.be.bignumber.eq(auctionBeginTimeSeconds);
});
});
describe('#matchOrdersAsync', () => {
it('should match two orders', async () => {
const txHash = await contractWrappers.dutchAuction.matchOrdersAsync(buyOrder, sellOrder, takerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
it('should throw when invalid transaction and shouldValidate is true', async () => {
// request match with bad buy/sell orders
const badSellOrder = buyOrder;
const badBuyOrder = sellOrder;
return expectTransactionFailedAsync(
contractWrappers.dutchAuction.matchOrdersAsync(badBuyOrder, badSellOrder, takerAddress, {
shouldValidate: true,
}),
RevertReason.InvalidAssetData,
);
});
});
describe('#getAuctionDetailsAsync', () => {
it('should get auction details', async () => {
// get auction details
const auctionDetails = await contractWrappers.dutchAuction.getAuctionDetailsAsync(sellOrder);
// run some basic sanity checks on the return value
expect(auctionDetails.beginTimeSeconds, 'auctionDetails.beginTimeSeconds').to.be.bignumber.equal(
auctionBeginTimeSeconds,
);
expect(auctionDetails.beginAmount, 'auctionDetails.beginAmount').to.be.bignumber.equal(
auctionBeginTakerAmount,
);
expect(auctionDetails.endTimeSeconds, 'auctionDetails.endTimeSeconds').to.be.bignumber.equal(
auctionEndTimeSeconds,
);
});
});
});

View File

@ -1,39 +0,0 @@
import * as chai from 'chai';
import { ContractWrappers } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { provider } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
describe('ERC20ProxyWrapper', () => {
let contractWrappers: ContractWrappers;
before(async () => {
const contractAddresses = await migrateOnceAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
});
describe('#isAuthorizedAsync', () => {
it('should return false if the address is not authorized', async () => {
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.erc20Proxy.getAuthorizedAddressesAsync();
for (const authorizedAddress of authorizedAddresses) {
const isAuthorized = await contractWrappers.erc20Proxy.isAuthorizedAsync(authorizedAddress);
expect(isAuthorized).to.be.true();
}
});
});
});

View File

@ -1,633 +0,0 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { EmptyWalletSubprovider, Web3ProviderEngine } from '@0x/subproviders';
import { DoneCallback } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import {
BlockParamLiteral,
BlockRange,
ContractWrappers,
ContractWrappersConfig,
ContractWrappersError,
DecodedLogEvent,
ERC20TokenApprovalEventArgs,
ERC20TokenEvents,
ERC20TokenTransferEventArgs,
} from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
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('ERC20Wrapper', () => {
let contractWrappers: ContractWrappers;
let contractAddresses: ContractAddresses;
let userAddresses: string[];
let tokens: string[];
let coinbase: string;
let addressWithoutFunds: string;
let config: ContractWrappersConfig;
before(async () => {
contractAddresses = await migrateOnceAsync();
config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
tokens = tokenUtils.getDummyERC20TokenAddresses();
coinbase = userAddresses[0];
addressWithoutFunds = userAddresses[1];
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#transferAsync', () => {
let tokenAddress: string;
let transferAmount: BigNumber;
before(() => {
tokenAddress = tokens[0];
transferAmount = new BigNumber(42);
});
it('should successfully transfer tokens', async () => {
const fromAddress = coinbase;
const toAddress = addressWithoutFunds;
const preBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress);
expect(preBalance).to.be.bignumber.equal(0);
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.erc20Token.transferAsync(tokenAddress, fromAddress, toAddress, transferAmount),
).to.be.rejectedWith(ContractWrappersError.InsufficientBalanceForTransfer);
});
});
describe('#transferFromAsync', () => {
let tokenAddress: string;
let toAddress: string;
let senderAddress: string;
before(async () => {
tokenAddress = tokens[0];
toAddress = addressWithoutFunds;
senderAddress = userAddresses[2];
});
it('should fail to transfer tokens if fromAddress has insufficient allowance set', async () => {
const fromAddress = coinbase;
const transferAmount = new BigNumber(42);
const fromAddressBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, fromAddress);
expect(fromAddressBalance).to.be.bignumber.greaterThan(transferAmount);
const fromAddressAllowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
fromAddress,
toAddress,
);
expect(fromAddressAllowance).to.be.bignumber.equal(0);
return expect(
contractWrappers.erc20Token.transferFromAsync(
tokenAddress,
fromAddress,
toAddress,
senderAddress,
transferAmount,
),
).to.be.rejectedWith(ContractWrappersError.InsufficientAllowanceForTransfer);
});
it('[regression] should fail to transfer tokens if set allowance for toAddress instead of senderAddress', async () => {
const fromAddress = coinbase;
const transferAmount = new BigNumber(42);
await contractWrappers.erc20Token.setAllowanceAsync(tokenAddress, fromAddress, toAddress, transferAmount);
return expect(
contractWrappers.erc20Token.transferFromAsync(
tokenAddress,
fromAddress,
toAddress,
senderAddress,
transferAmount,
),
).to.be.rejectedWith(ContractWrappersError.InsufficientAllowanceForTransfer);
});
it('should fail to transfer tokens if fromAddress has insufficient balance', async () => {
const fromAddress = addressWithoutFunds;
const transferAmount = new BigNumber(42);
const fromAddressBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, fromAddress);
expect(fromAddressBalance).to.be.bignumber.equal(0);
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.erc20Token.transferFromAsync(
tokenAddress,
fromAddress,
toAddress,
senderAddress,
transferAmount,
),
).to.be.rejectedWith(ContractWrappersError.InsufficientBalanceForTransfer);
});
it('should successfully transfer tokens', async () => {
const fromAddress = coinbase;
const preBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress);
expect(preBalance).to.be.bignumber.equal(0);
const transferAmount = new BigNumber(42);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
fromAddress,
senderAddress,
transferAmount,
);
await contractWrappers.erc20Token.transferFromAsync(
tokenAddress,
fromAddress,
toAddress,
senderAddress,
transferAmount,
);
const postBalance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, toAddress);
return expect(postBalance).to.be.bignumber.equal(transferAmount);
});
});
describe('#getBalanceAsync', () => {
describe('With provider with accounts', () => {
it('should return the balance for an existing ERC20 token', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const balance = await contractWrappers.erc20Token.getBalanceAsync(tokenAddress, ownerAddress);
const expectedBalance = new BigNumber('1000000000000000000000000000');
return expect(balance).to.be.bignumber.equal(expectedBalance);
});
it('should return a balance of 0 for a non-existent owner address', async () => {
const tokenAddress = tokens[0];
const nonExistentOwner = '0x198c6ad858f213fb31b6fe809e25040e6b964593';
const balance = await contractWrappers.erc20Token.getBalanceAsync(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 tokenAddress = tokens[0];
const ownerAddress = coinbase;
const balance = await zeroExContractWithoutAccounts.erc20Token.getBalanceAsync(
tokenAddress,
ownerAddress,
);
const expectedBalance = new BigNumber('1000000000000000000000000000');
return expect(balance).to.be.bignumber.equal(expectedBalance);
});
});
});
describe('#setAllowanceAsync', () => {
it("should set the spender's allowance", async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const allowanceBeforeSet = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
const expectedAllowanceBeforeAllowanceSet = new BigNumber(0);
expect(allowanceBeforeSet).to.be.bignumber.equal(expectedAllowanceBeforeAllowanceSet);
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
amountInBaseUnits,
);
const allowanceAfterSet = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
const expectedAllowanceAfterAllowanceSet = amountInBaseUnits;
return expect(allowanceAfterSet).to.be.bignumber.equal(expectedAllowanceAfterAllowanceSet);
});
});
describe('#setUnlimitedAllowanceAsync', () => {
it("should set the unlimited spender's allowance", async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
await contractWrappers.erc20Token.setUnlimitedAllowanceAsync(tokenAddress, ownerAddress, spenderAddress);
const allowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
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 zrxAddress = contractAddresses.zrxToken;
const [, userWithNormalAllowance, userWithUnlimitedAllowance] = userAddresses;
await contractWrappers.erc20Token.setAllowanceAsync(
zrxAddress,
coinbase,
userWithNormalAllowance,
transferAmount,
);
await contractWrappers.erc20Token.setUnlimitedAllowanceAsync(
zrxAddress,
coinbase,
userWithUnlimitedAllowance,
);
const initBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
const initBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
userWithUnlimitedAllowance,
);
await contractWrappers.erc20Token.transferFromAsync(
zrxAddress,
coinbase,
userWithNormalAllowance,
userWithNormalAllowance,
transferAmount,
);
await contractWrappers.erc20Token.transferFromAsync(
zrxAddress,
coinbase,
userWithUnlimitedAllowance,
userWithUnlimitedAllowance,
transferAmount,
);
const finalBalanceWithNormalAllowance = await web3Wrapper.getBalanceInWeiAsync(userWithNormalAllowance);
const finalBalanceWithUnlimitedAllowance = await web3Wrapper.getBalanceInWeiAsync(
userWithUnlimitedAllowance,
);
const normalGasCost = initBalanceWithNormalAllowance.minus(finalBalanceWithNormalAllowance);
const unlimitedGasCost = initBalanceWithUnlimitedAllowance.minus(finalBalanceWithUnlimitedAllowance);
// In theory the gas cost with unlimited allowance should be smaller, but with testrpc it's actually bigger.
// This needs to be investigated in ethereumjs-vm. This test is essentially a repro.
// TODO: Make this test pass with inverted assertion.
expect(unlimitedGasCost.toNumber()).to.be.gt(normalGasCost.toNumber());
});
});
describe('#getAllowanceAsync', () => {
describe('With provider with accounts', () => {
it('should get the proxy allowance', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
amountInBaseUnits,
);
const allowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
it('should return 0 if no allowance set yet', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const allowance = await contractWrappers.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
const expectedAllowance = new BigNumber(0);
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
});
describe('With provider without accounts', () => {
let zeroExContractWithoutAccounts: ContractWrappers;
before(async () => {
const emptyWalletProvider = addEmptyWalletSubprovider(provider);
zeroExContractWithoutAccounts = new ContractWrappers(emptyWalletProvider, config);
});
it('should get the proxy allowance', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const spenderAddress = addressWithoutFunds;
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
amountInBaseUnits,
);
const allowance = await zeroExContractWithoutAccounts.erc20Token.getAllowanceAsync(
tokenAddress,
ownerAddress,
spenderAddress,
);
const expectedAllowance = amountInBaseUnits;
return expect(allowance).to.be.bignumber.equal(expectedAllowance);
});
});
});
describe('#getProxyAllowanceAsync', () => {
it('should get the proxy allowance', async () => {
const tokenAddress = tokens[0];
const ownerAddress = coinbase;
const amountInBaseUnits = new BigNumber(50);
await contractWrappers.erc20Token.setProxyAllowanceAsync(tokenAddress, ownerAddress, amountInBaseUnits);
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 tokenAddress = tokens[0];
const ownerAddress = coinbase;
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.erc20Token.setProxyAllowanceAsync(tokenAddress, ownerAddress, amountInBaseUnits);
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 tokenAddress = tokens[0];
const ownerAddress = coinbase;
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', () => {
const indexFilterValues = {};
let tokenAddress: string;
const transferAmount = new BigNumber(42);
const allowanceAmount = new BigNumber(42);
before(() => {
tokenAddress = tokens[0];
});
afterEach(() => {
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,
// 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<ERC20TokenTransferEventArgs>) => {
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(coinbase);
expect(args._to).to.be.equal(addressWithoutFunds);
expect(args._value).to.be.bignumber.equal(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<ERC20TokenApprovalEventArgs>) => {
expect(logEvent).to.not.be.undefined();
expect(logEvent.isRemoved).to.be.false();
const args = logEvent.log.args;
expect(args._owner).to.be.equal(coinbase);
expect(args._spender).to.be.equal(addressWithoutFunds);
expect(args._value).to.be.bignumber.equal(allowanceAmount);
},
);
contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Approval,
indexFilterValues,
callback,
);
await contractWrappers.erc20Token.setAllowanceAsync(
tokenAddress,
coinbase,
addressWithoutFunds,
allowanceAmount,
);
})().catch(done);
});
it('Outstanding subscriptions are cancelled when contractWrappers.unsubscribeAll called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(_logEvent: DecodedLogEvent<ERC20TokenApprovalEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)();
contractWrappers.unsubscribeAll();
contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Transfer,
indexFilterValues,
callbackToBeCalled,
);
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<ERC20TokenApprovalEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
const subscriptionToken = contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
contractWrappers.erc20Token.unsubscribe(subscriptionToken);
await contractWrappers.erc20Token.transferAsync(
tokenAddress,
coinbase,
addressWithoutFunds,
transferAmount,
);
done();
})().catch(done);
});
});
describe('#getLogsAsync', () => {
let tokenAddress: string;
let tokenTransferProxyAddress: string;
const blockRange: BlockRange = {
fromBlock: 0,
toBlock: BlockParamLiteral.Latest,
};
let txHash: string;
before(() => {
tokenAddress = tokens[0];
tokenTransferProxyAddress = contractWrappers.erc20Proxy.address;
});
it('should get logs with decoded args emitted by Approval', async () => {
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.erc20Token.getLogsAsync<ERC20TokenApprovalEventArgs>(
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(coinbase);
expect(args._spender).to.be.equal(tokenTransferProxyAddress);
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.erc20Token.setUnlimitedProxyAllowanceAsync(tokenAddress, coinbase);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const differentEventName = ERC20TokenEvents.Transfer;
const indexFilterValues = {};
const logs = await contractWrappers.erc20Token.getLogsAsync(
tokenAddress,
differentEventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(0);
});
it('should only get the logs with the correct indexed fields', async () => {
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.erc20Token.getLogsAsync<ERC20TokenApprovalEventArgs>(
tokenAddress,
eventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(1);
const args = logs[0].args;
expect(args._owner).to.be.equal(coinbase);
});
});
});
// tslint:disable:max-file-line-count
function addEmptyWalletSubprovider(p: Web3ProviderEngine): Web3ProviderEngine {
const providerEngine = new Web3ProviderEngine();
providerEngine.addProvider(new EmptyWalletSubprovider());
const currentSubproviders = (p as any)._providers;
for (const subprovider of currentSubproviders) {
providerEngine.addProvider(subprovider);
}
providerUtils.startProviderEngine(providerEngine);
return providerEngine;
}

View File

@ -1,39 +0,0 @@
import * as chai from 'chai';
import { ContractWrappers } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { provider } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
describe('ERC721ProxyWrapper', () => {
let contractWrappers: ContractWrappers;
before(async () => {
const contractAddresses = await migrateOnceAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
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();
}
});
});
});

View File

@ -1,465 +0,0 @@
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { EmptyWalletSubprovider, Web3ProviderEngine } from '@0x/subproviders';
import { DoneCallback } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import {
BlockParamLiteral,
BlockRange,
ContractWrappers,
ContractWrappersConfig,
ContractWrappersError,
DecodedLogEvent,
ERC721TokenApprovalEventArgs,
ERC721TokenApprovalForAllEventArgs,
ERC721TokenEvents,
ERC721TokenTransferEventArgs,
} from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
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;
let config: ContractWrappersConfig;
before(async () => {
const contractAddresses = await migrateOnceAsync();
config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
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 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 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 isProxyApprovedBeforeSet = await contractWrappers.erc721Token.isProxyApprovedAsync(
tokenAddress,
tokenId,
);
expect(isProxyApprovedBeforeSet).to.be.false();
await contractWrappers.erc721Token.setProxyApprovalAsync(tokenAddress, tokenId);
const isProxyApprovedAfterSet = await contractWrappers.erc721Token.isProxyApprovedAsync(
tokenAddress,
tokenId,
);
expect(isProxyApprovedAfterSet).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.unsubscribeAll 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.unsubscribeAll();
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 isApproved = true;
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc721Token.setApprovalForAllAsync(
tokenAddress,
ownerAddress,
operatorAddress,
isApproved,
),
constants.AWAIT_TRANSACTION_MINED_MS,
);
done();
})().catch(done);
});
});
describe('#getLogsAsync', () => {
const blockRange: BlockRange = {
fromBlock: 0,
toBlock: BlockParamLiteral.Latest,
};
let txHash: string;
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: Web3ProviderEngine): Web3ProviderEngine {
const providerEngine = new Web3ProviderEngine();
providerEngine.addProvider(new EmptyWalletSubprovider());
const currentSubproviders = (p as any)._providers;
for (const subprovider of currentSubproviders) {
providerEngine.addProvider(subprovider);
}
providerUtils.startProviderEngine(providerEngine);
return providerEngine;
}

View File

@ -1,437 +0,0 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { DoneCallback } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import 'mocha';
import {
BlockParamLiteral,
BlockRange,
ContractWrappers,
ContractWrappersError,
WETH9ApprovalEventArgs,
WETH9DepositEventArgs,
WETH9Events,
WETH9TransferEventArgs,
WETH9WithdrawalEventArgs,
} from '../src';
import { DecodedLogEvent } from '../src/types';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// Since the address depositing/withdrawing ETH/WETH also needs to pay gas costs for the transaction,
// a small amount of ETH will be used to pay this gas cost. We therefore check that the difference between
// the expected balance and actual balance (given the amount of ETH deposited), only deviates by the amount
// required to pay gas costs.
const MAX_REASONABLE_GAS_COST_IN_WEI = 62517;
describe('EtherTokenWrapper', () => {
let contractWrappers: ContractWrappers;
let contractAddresses: ContractAddresses;
let userAddresses: string[];
let addressWithETH: string;
let wethContractAddress: string;
let depositWeiAmount: BigNumber;
const decimalPlaces = 7;
let addressWithoutFunds: string;
const gasPrice = new BigNumber(1);
const transferAmount = new BigNumber(42);
const allowanceAmount = new BigNumber(42);
const depositAmount = new BigNumber(42);
const withdrawalAmount = new BigNumber(42);
before(async () => {
contractAddresses = await migrateOnceAsync();
const config = {
gasPrice,
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
addressWithETH = userAddresses[0];
wethContractAddress = contractAddresses.etherToken;
depositWeiAmount = Web3Wrapper.toWei(new BigNumber(5));
addressWithoutFunds = userAddresses[1];
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#getContractAddressIfExists', async () => {
it('should return contract address if connected to a known network', () => {
const contractAddressIfExists = contractAddresses.etherToken;
expect(contractAddressIfExists).to.not.be.undefined();
});
it('should throw if connected to a private network and contract addresses are not specified', () => {
const UNKNOWN_NETWORK_NETWORK_ID = 10;
expect(
() =>
new ContractWrappers(provider, {
networkId: UNKNOWN_NETWORK_NETWORK_ID,
} as any),
).to.throw();
});
});
describe('#depositAsync', () => {
it('should successfully deposit ETH and issue Wrapped ETH tokens', async () => {
const preETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const preWETHBalance = await contractWrappers.erc20Token.getBalanceAsync(
wethContractAddress,
addressWithETH,
);
expect(preETHBalance).to.be.bignumber.gt(0);
expect(preWETHBalance).to.be.bignumber.equal(0);
const txHash = await contractWrappers.etherToken.depositAsync(
wethContractAddress,
depositWeiAmount,
addressWithETH,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const postETHBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const postWETHBalanceInBaseUnits = await contractWrappers.erc20Token.getBalanceAsync(
wethContractAddress,
addressWithETH,
);
expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(depositWeiAmount);
const remainingETHInWei = preETHBalance.minus(depositWeiAmount);
const gasCost = remainingETHInWei.minus(postETHBalanceInWei);
expect(gasCost).to.be.bignumber.lte(MAX_REASONABLE_GAS_COST_IN_WEI);
});
it('should throw if user has insufficient ETH balance for deposit', async () => {
const preETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const extraETHBalance = Web3Wrapper.toWei(new BigNumber(5));
const overETHBalanceinWei = preETHBalance.plus(extraETHBalance);
return expect(
contractWrappers.etherToken.depositAsync(wethContractAddress, overETHBalanceinWei, addressWithETH),
).to.be.rejectedWith(ContractWrappersError.InsufficientEthBalanceForDeposit);
});
});
describe('#withdrawAsync', () => {
it('should successfully withdraw ETH in return for Wrapped ETH tokens', async () => {
const ETHBalanceInWei = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
await contractWrappers.etherToken.depositAsync(wethContractAddress, depositWeiAmount, addressWithETH);
const expectedPreETHBalance = ETHBalanceInWei.minus(depositWeiAmount);
const preETHBalance = await web3Wrapper.getBalanceInWeiAsync(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);
const txHash = await contractWrappers.etherToken.withdrawAsync(
wethContractAddress,
depositWeiAmount,
addressWithETH,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const postETHBalance = await web3Wrapper.getBalanceInWeiAsync(addressWithETH);
const postWETHBalanceInBaseUnits = await contractWrappers.erc20Token.getBalanceAsync(
wethContractAddress,
addressWithETH,
);
expect(postWETHBalanceInBaseUnits).to.be.bignumber.equal(0);
const expectedETHBalance = preETHBalance.plus(depositWeiAmount).integerValue(decimalPlaces);
gasCost = expectedETHBalance.minus(postETHBalance);
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.erc20Token.getBalanceAsync(
wethContractAddress,
addressWithETH,
);
expect(preWETHBalance).to.be.bignumber.equal(0);
// tslint:disable-next-line:custom-no-magic-numbers
const overWETHBalance = preWETHBalance.plus(999999999);
return expect(
contractWrappers.etherToken.withdrawAsync(wethContractAddress, overWETHBalance, addressWithETH),
).to.be.rejectedWith(ContractWrappersError.InsufficientWEthBalanceForWithdrawal);
});
});
describe('#subscribe', () => {
const indexFilterValues = {};
let etherTokenAddress: string;
before(async () => {
etherTokenAddress = contractAddresses.etherToken;
});
afterEach(() => {
contractWrappers.etherToken.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 async 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<WETH9TransferEventArgs>) => {
expect(logEvent).to.not.be.undefined();
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(addressWithETH);
expect(args._to).to.be.equal(addressWithoutFunds);
expect(args._value).to.be.bignumber.equal(transferAmount);
},
);
await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Transfer,
indexFilterValues,
callback,
);
await contractWrappers.erc20Token.transferAsync(
etherTokenAddress,
addressWithETH,
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<WETH9ApprovalEventArgs>) => {
expect(logEvent).to.not.be.undefined();
expect(logEvent.isRemoved).to.be.false();
const args = logEvent.log.args;
expect(args._owner).to.be.equal(addressWithETH);
expect(args._spender).to.be.equal(addressWithoutFunds);
expect(args._value).to.be.bignumber.equal(allowanceAmount);
},
);
contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Approval,
indexFilterValues,
callback,
);
await contractWrappers.erc20Token.setAllowanceAsync(
etherTokenAddress,
addressWithETH,
addressWithoutFunds,
allowanceAmount,
);
})().catch(done);
});
it('Should receive the Deposit event when ether is being deposited', (done: DoneCallback) => {
(async () => {
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<WETH9DepositEventArgs>) => {
expect(logEvent).to.not.be.undefined();
expect(logEvent.isRemoved).to.be.false();
const args = logEvent.log.args;
expect(args._owner).to.be.equal(addressWithETH);
expect(args._value).to.be.bignumber.equal(depositAmount);
},
);
contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Deposit,
indexFilterValues,
callback,
);
await contractWrappers.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
})().catch(done);
});
it('Should receive the Withdrawal event when ether is being withdrawn', (done: DoneCallback) => {
(async () => {
const callback = callbackErrorReporter.reportNodeCallbackErrors(done)(
(logEvent: DecodedLogEvent<WETH9WithdrawalEventArgs>) => {
expect(logEvent).to.not.be.undefined();
expect(logEvent.isRemoved).to.be.false();
const args = logEvent.log.args;
expect(args._owner).to.be.equal(addressWithETH);
expect(args._value).to.be.bignumber.equal(depositAmount);
},
);
await contractWrappers.etherToken.depositAsync(etherTokenAddress, depositAmount, addressWithETH);
contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Withdrawal,
indexFilterValues,
callback,
);
await contractWrappers.etherToken.withdrawAsync(etherTokenAddress, withdrawalAmount, addressWithETH);
})().catch(done);
});
it('should cancel outstanding subscriptions when contractWrappers.unsubscribeAll is called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(_logEvent: DecodedLogEvent<WETH9ApprovalEventArgs>) => {
done(new Error('Expected this subscription to have been cancelled'));
},
);
contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
const callbackToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)();
contractWrappers.unsubscribeAll();
await contractWrappers.etherToken.depositAsync(etherTokenAddress, transferAmount, addressWithETH);
contractWrappers.etherToken.subscribe(
etherTokenAddress,
WETH9Events.Transfer,
indexFilterValues,
callbackToBeCalled,
);
await contractWrappers.erc20Token.transferAsync(
etherTokenAddress,
addressWithETH,
addressWithoutFunds,
transferAmount,
);
})().catch(done);
});
it('Should cancel subscription when unsubscribe called', (done: DoneCallback) => {
(async () => {
const callbackNeverToBeCalled = callbackErrorReporter.reportNodeCallbackErrors(done)(
(_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,
WETH9Events.Transfer,
indexFilterValues,
callbackNeverToBeCalled,
);
contractWrappers.etherToken.unsubscribe(subscriptionToken);
await contractWrappers.erc20Token.transferAsync(
etherTokenAddress,
addressWithETH,
addressWithoutFunds,
transferAmount,
);
done();
})().catch(done);
});
});
describe('#getLogsAsync', () => {
let etherTokenAddress: string;
let erc20ProxyAddress: string;
let blockRange: BlockRange;
let txHash: string;
before(async () => {
addressWithETH = userAddresses[0];
etherTokenAddress = contractAddresses.etherToken;
erc20ProxyAddress = contractWrappers.erc20Proxy.address;
// Start the block range after all migrations to avoid unexpected logs
const currentBlock: number = await web3Wrapper.getBlockNumberAsync();
const fromBlock = currentBlock + 1;
blockRange = {
fromBlock,
toBlock: BlockParamLiteral.Latest,
};
});
it('should get logs with decoded args emitted by Approval', async () => {
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<WETH9ApprovalEventArgs>(
etherTokenAddress,
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(addressWithETH);
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 = WETH9Events.Deposit;
const indexFilterValues = {};
const logs = await contractWrappers.etherToken.getLogsAsync<WETH9DepositEventArgs>(
etherTokenAddress,
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(addressWithETH);
expect(args._value).to.be.bignumber.equal(depositAmount);
});
it('should only get the logs with the correct event name', async () => {
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,
differentEventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(0);
});
it('should only get the logs with the correct indexed fields', async () => {
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, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = WETH9Events.Approval;
const indexFilterValues = {
_owner: addressWithETH,
};
const logs = await contractWrappers.etherToken.getLogsAsync<WETH9ApprovalEventArgs>(
etherTokenAddress,
eventName,
blockRange,
indexFilterValues,
);
expect(logs).to.have.length(1);
const args = logs[0].args;
expect(args._owner).to.be.equal(addressWithETH);
});
});
});

View File

@ -1,8 +1,7 @@
import { DummyERC20TokenContract } from '@0x/abi-gen-wrappers';
import { BlockchainLifecycle, callbackErrorReporter } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils, orderHashUtils, signatureUtils } from '@0x/order-utils';
import { DoneCallback, RevertReason, SignedOrder } from '@0x/types';
import { DoneCallback, SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import { BlockParamLiteral } from 'ethereum-types';
@ -10,8 +9,8 @@ import 'mocha';
import { ContractWrappers, ExchangeCancelEventArgs, ExchangeEvents, ExchangeFillEventArgs, OrderStatus } from '../src';
import { DecodedLogEvent } from '../src/types';
import { _getDefaultContractAddresses } from '../src/utils/contract_addresses';
import { UntransferrableDummyERC20Token } from './artifacts/UntransferrableDummyERC20Token';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
@ -35,7 +34,6 @@ describe('ExchangeWrapper', () => {
let takerAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let txHash: string;
const fillableAmount = new BigNumber(5);
const takerTokenFillAmount = new BigNumber(5);
let signedOrder: SignedOrder;
@ -52,7 +50,7 @@ describe('ExchangeWrapper', () => {
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
zrxTokenAddress = contractAddresses.zrxToken;
fillScenarios = new FillScenarios(
provider,
userAddresses,
@ -94,71 +92,71 @@ describe('ExchangeWrapper', () => {
describe('fill order(s)', () => {
describe('#fillOrderAsync', () => {
it('should fill a valid order', async () => {
txHash = await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#fillOrderNoThrowAsync', () => {
it('should fill a valid order', async () => {
txHash = await contractWrappers.exchange.fillOrderNoThrowAsync(
await contractWrappers.exchange.fillOrderNoThrow.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
describe('#fillOrKillOrderAsync', () => {
it('should fill or kill a valid order', async () => {
txHash = await contractWrappers.exchange.fillOrKillOrderAsync(
await contractWrappers.exchange.fillOrKillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#batchFillOrdersAsync', () => {
it('should fill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
txHash = await contractWrappers.exchange.batchFillOrdersAsync(
await contractWrappers.exchange.batchFillOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#marketBuyOrdersAsync', () => {
it('should maker buy', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = takerTokenFillAmount;
txHash = await contractWrappers.exchange.marketBuyOrdersAsync(
await contractWrappers.exchange.marketBuyOrders.awaitTransactionSuccessAsync(
signedOrders,
makerAssetFillAmount,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#marketBuyOrdersNoThrowAsync', () => {
it('should no throw maker buy', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = takerTokenFillAmount;
txHash = await contractWrappers.exchange.marketBuyOrdersNoThrowAsync(
await contractWrappers.exchange.marketBuyOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
makerAssetFillAmount,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
@ -166,25 +164,25 @@ describe('ExchangeWrapper', () => {
it('should maker sell', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmount = takerTokenFillAmount;
txHash = await contractWrappers.exchange.marketSellOrdersAsync(
await contractWrappers.exchange.marketSellOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmount,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#marketSellOrdersNoThrowAsync', () => {
it('should no throw maker sell', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmount = takerTokenFillAmount;
txHash = await contractWrappers.exchange.marketSellOrdersNoThrowAsync(
await contractWrappers.exchange.marketSellOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmount,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
@ -192,15 +190,15 @@ describe('ExchangeWrapper', () => {
it('should fill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
txHash = await contractWrappers.exchange.batchFillOrdersNoThrowAsync(
await contractWrappers.exchange.batchFillOrdersNoThrow.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
let orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
let orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
orderInfo = await contractWrappers.exchange.getOrderInfoAsync(anotherSignedOrder);
orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(anotherSignedOrder);
expect(orderInfo.orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
});
@ -208,12 +206,12 @@ describe('ExchangeWrapper', () => {
it('should fill or kill a batch of valid orders', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount];
txHash = await contractWrappers.exchange.batchFillOrKillOrdersAsync(
await contractWrappers.exchange.batchFillOrKillOrders.awaitTransactionSuccessAsync(
signedOrders,
takerAssetFillAmounts,
takerAddress,
signedOrders.map(o => o.signature),
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#matchOrdersAsync', () => {
@ -225,35 +223,39 @@ describe('ExchangeWrapper', () => {
takerAddress,
fillableAmount,
);
txHash = await contractWrappers.exchange.matchOrdersAsync(
await contractWrappers.exchange.matchOrders.awaitTransactionSuccessAsync(
signedOrder,
matchingSignedOrder,
takerAddress,
signedOrder.signature,
matchingSignedOrder.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
});
describe('cancel order(s)', () => {
describe('#cancelOrderAsync', () => {
it('should cancel a valid order', async () => {
txHash = await contractWrappers.exchange.cancelOrderAsync(signedOrder);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
await contractWrappers.exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder, {
from: makerAddress,
});
});
});
describe('#batchCancelOrdersAsync', () => {
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);
await contractWrappers.exchange.batchCancelOrders.awaitTransactionSuccessAsync(orders, {
from: makerAddress,
});
});
});
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(
await contractWrappers.exchange.cancelOrdersUpTo.awaitTransactionSuccessAsync(targetOrderEpoch, {
from: makerAddress,
});
const orderEpoch = await contractWrappers.exchange.orderEpoch.callAsync(
makerAddress,
constants.NULL_ADDRESS,
);
@ -261,138 +263,35 @@ describe('ExchangeWrapper', () => {
});
});
});
describe('#getZRXAssetData', () => {
it('should get the asset data', () => {
const ZRX_ASSET_DATA = contractWrappers.exchange.getZRXAssetData();
const ASSET_DATA_HEX_LENGTH = 74;
expect(ZRX_ASSET_DATA).to.have.length(ASSET_DATA_HEX_LENGTH);
});
});
describe('#getOrderInfoAsync', () => {
it('should get the order info', async () => {
const orderInfo = await contractWrappers.exchange.getOrderInfoAsync(signedOrder);
const orderInfo = await contractWrappers.exchange.getOrderInfo.callAsync(signedOrder);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
expect(orderInfo.orderHash).to.be.equal(orderHash);
});
});
describe('#getOrdersInfoAsync', () => {
it('should get the orders info', async () => {
const ordersInfo = await contractWrappers.exchange.getOrdersInfoAsync([signedOrder, anotherSignedOrder]);
const ordersInfo = await contractWrappers.exchange.getOrdersInfo.callAsync([
signedOrder,
anotherSignedOrder,
]);
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
expect(ordersInfo[0].orderHash).to.be.equal(orderHash);
const anotherOrderHash = orderHashUtils.getOrderHashHex(anotherSignedOrder);
expect(ordersInfo[1].orderHash).to.be.equal(anotherOrderHash);
});
});
describe('#validateOrderFillableOrThrowAsync', () => {
it('should throw if signature is invalid', async () => {
const signedOrderWithInvalidSignature = {
...signedOrder,
signature:
'0x1b61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc3340349190569279751135161d22529dc25add4f6069af05be04cacbda2ace225403',
};
return expect(
contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrderWithInvalidSignature),
).to.eventually.to.be.rejectedWith(RevertReason.InvalidOrderSignature);
});
it('should validate the order with the current balances and allowances for the maker', async () => {
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder, {
validateRemainingOrderAmountIsFillable: false,
});
});
it('should validate the order with remaining fillable amount for the order', async () => {
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder);
});
it('should validate the order with specified amount', async () => {
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder, {
expectedFillTakerTokenAmount: signedOrder.takerAssetAmount,
});
});
it('should throw if the amount is greater than the allowance/balance', async () => {
return expect(
contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder, {
// tslint:disable-next-line:custom-no-magic-numbers
expectedFillTakerTokenAmount: new BigNumber(2).pow(256).minus(1),
}),
).to.eventually.to.be.rejected();
});
it('should throw when the maker does not have enough balance for the remaining order amount', async () => {
const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// Change maker balance to have less than the order amount
const remainingBalance = makerBalance.minus(signedOrder.makerAssetAmount.minus(1));
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc20Token.transferAsync(
makerTokenAddress,
makerAddress,
constants.NULL_ADDRESS,
remainingBalance,
),
);
return expect(
contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder),
).to.eventually.to.be.rejected();
});
it('should validate the order when remaining order amount has some fillable amount', async () => {
const makerBalance = await contractWrappers.erc20Token.getBalanceAsync(makerTokenAddress, makerAddress);
// Change maker balance to have less than the order amount
const remainingBalance = makerBalance.minus(signedOrder.makerAssetAmount.minus(1));
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc20Token.transferAsync(
makerTokenAddress,
makerAddress,
constants.NULL_ADDRESS,
remainingBalance,
),
);
// An amount is still transferrable
await contractWrappers.exchange.validateOrderFillableOrThrowAsync(signedOrder, {
validateRemainingOrderAmountIsFillable: false,
});
});
it('should throw when the ERC20 token has transfer restrictions', async () => {
const artifactDependencies = {};
const untransferrableToken = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
UntransferrableDummyERC20Token,
provider,
{ from: userAddresses[0] },
artifactDependencies,
'UntransferrableToken',
'UTT',
new BigNumber(constants.ZRX_DECIMALS),
// tslint:disable-next-line:custom-no-magic-numbers
new BigNumber(2).pow(20).minus(1),
);
const untransferrableMakerAssetData = assetDataUtils.encodeERC20AssetData(untransferrableToken.address);
const invalidSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
untransferrableMakerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
await web3Wrapper.awaitTransactionSuccessAsync(
await contractWrappers.erc20Token.setProxyAllowanceAsync(
untransferrableToken.address,
makerAddress,
signedOrder.makerAssetAmount,
),
);
return expect(
contractWrappers.exchange.validateOrderFillableOrThrowAsync(invalidSignedOrder),
).to.eventually.to.be.rejectedWith('TRANSFER_FAILED');
});
});
describe('#isValidSignature', () => {
it('should check if the signature is valid', async () => {
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
let isValid = await contractWrappers.exchange.isValidSignatureAsync(
let isValid = await contractWrappers.exchange.isValidSignature.callAsync(
orderHash,
signedOrder.makerAddress,
signedOrder.signature,
);
expect(isValid).to.be.true();
isValid = await contractWrappers.exchange.isValidSignatureAsync(
isValid = await contractWrappers.exchange.isValidSignature.callAsync(
orderHash,
signedOrder.takerAddress,
signedOrder.signature,
@ -404,7 +303,10 @@ describe('ExchangeWrapper', () => {
it('should check if the validator is allowed', async () => {
const signerAddress = makerAddress;
const validatorAddress = constants.NULL_ADDRESS;
const isAllowed = await contractWrappers.exchange.isAllowedValidatorAsync(signerAddress, validatorAddress);
const isAllowed = await contractWrappers.exchange.allowedValidators.callAsync(
signerAddress,
validatorAddress,
);
expect(isAllowed).to.be.false();
});
});
@ -413,52 +315,50 @@ describe('ExchangeWrapper', () => {
const validatorAddress = constants.NULL_ADDRESS;
const isApproved = true;
const senderAddress = makerAddress;
txHash = await contractWrappers.exchange.setSignatureValidatorApprovalAsync(
await contractWrappers.exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
validatorAddress,
isApproved,
senderAddress,
{ from: senderAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
});
});
describe('#isTransactionExecutedAsync', () => {
it('should check if the transaction is executed', async () => {
const transactionHash = '0x0000000000000000000000000000000000000000000000000000000000000000';
const isExecuted = await contractWrappers.exchange.isTransactionExecutedAsync(transactionHash);
const isExecuted = await contractWrappers.exchange.transactions.callAsync(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 erc20ProxyId = await contractWrappers.erc20Proxy.getProxyId.callAsync();
const erc20ProxyAddressById = await contractWrappers.exchange.getAssetProxy.callAsync(erc20ProxyId);
const erc20ProxyAddress = contractWrappers.erc20Proxy.address;
expect(erc20ProxyAddressById).to.be.equal(erc20ProxyAddress);
const erc721ProxyId = await contractWrappers.erc721Proxy.getProxyIdAsync();
const erc721ProxyAddressById = await contractWrappers.exchange.getAssetProxyBySignatureAsync(erc721ProxyId);
const erc721ProxyId = await contractWrappers.erc721Proxy.getProxyId.callAsync();
const erc721ProxyAddressById = await contractWrappers.exchange.getAssetProxy.callAsync(erc721ProxyId);
const erc721ProxyAddress = contractWrappers.erc721Proxy.address;
expect(erc721ProxyAddressById).to.be.equal(erc721ProxyAddress);
});
});
describe('#preSignAsync/isPreSignedAsync', () => {
describe('#preSign/isPresigned', () => {
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);
let isPreSigned = await contractWrappers.exchange.preSigned.callAsync(hash, signerAddress);
expect(isPreSigned).to.be.false();
txHash = await contractWrappers.exchange.preSignAsync(
await contractWrappers.exchange.preSign.awaitTransactionSuccessAsync(
hash,
signerAddress,
signedOrder.signature,
senderAddress,
{ from: senderAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
isPreSigned = await contractWrappers.exchange.isPreSignedAsync(hash, signerAddress);
isPreSigned = await contractWrappers.exchange.preSigned.callAsync(hash, signerAddress);
expect(isPreSigned).to.be.true();
const preSignedSignature = '0x06';
const isValidSignature = await contractWrappers.exchange.isValidSignatureAsync(
const isValidSignature = await contractWrappers.exchange.isValidSignature.callAsync(
hash,
signerAddress,
preSignedSignature,
@ -477,7 +377,7 @@ describe('ExchangeWrapper', () => {
});
describe('#getVersionAsync', () => {
it('should return version the hash', async () => {
const version = await contractWrappers.exchange.getVersionAsync();
const version = await contractWrappers.exchange.VERSION.callAsync();
const VERSION = '2.0.0';
expect(version).to.be.equal(VERSION);
});
@ -510,10 +410,11 @@ describe('ExchangeWrapper', () => {
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callback);
await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
})().catch(done);
});
@ -525,7 +426,9 @@ describe('ExchangeWrapper', () => {
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Cancel, indexFilterValues, callback);
await contractWrappers.exchange.cancelOrderAsync(signedOrder);
await contractWrappers.exchange.cancelOrder.awaitTransactionSuccessAsync(signedOrder, {
from: makerAddress,
});
})().catch(done);
});
it('Outstanding subscriptions are cancelled when contractWrappers.unsubscribeAll called', (done: DoneCallback) => {
@ -545,10 +448,11 @@ describe('ExchangeWrapper', () => {
},
);
contractWrappers.exchange.subscribe(ExchangeEvents.Fill, indexFilterValues, callback);
await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
})().catch(done);
});
@ -565,10 +469,11 @@ describe('ExchangeWrapper', () => {
callbackNeverToBeCalled,
);
contractWrappers.exchange.unsubscribe(subscriptionToken);
await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmountInBaseUnits,
takerAddress,
signedOrder.signature,
{ from: takerAddress },
);
done();
})().catch(done);
@ -580,7 +485,12 @@ describe('ExchangeWrapper', () => {
toBlock: BlockParamLiteral.Latest,
};
it('should get logs with decoded args emitted by Fill', async () => {
txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const eventName = ExchangeEvents.Fill;
const indexFilterValues = {};
const logs = await contractWrappers.exchange.getLogsAsync(eventName, blockRange, indexFilterValues);
@ -588,8 +498,12 @@ describe('ExchangeWrapper', () => {
expect(logs[0].event).to.be.equal(eventName);
});
it('should only get the logs with the correct event name', async () => {
txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const differentEventName = ExchangeEvents.Cancel;
const indexFilterValues = {};
const logs = await contractWrappers.exchange.getLogsAsync(
@ -600,8 +514,12 @@ describe('ExchangeWrapper', () => {
expect(logs).to.have.length(0);
});
it('should only get the logs with the correct indexed fields', async () => {
txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrder,
takerTokenFillAmount,
signedOrder.signature,
{ from: takerAddress },
);
const signedOrderWithAnotherMakerAddress = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
@ -609,13 +527,12 @@ describe('ExchangeWrapper', () => {
takerAddress,
fillableAmount,
);
txHash = await contractWrappers.exchange.fillOrderAsync(
await contractWrappers.exchange.fillOrder.awaitTransactionSuccessAsync(
signedOrderWithAnotherMakerAddress,
takerTokenFillAmount,
takerAddress,
signedOrderWithAnotherMakerAddress.signature,
{ from: takerAddress },
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const eventName = ExchangeEvents.Fill;
const indexFilterValues = {
makerAddress: anotherMakerAddress,

View File

@ -1,167 +0,0 @@
import { BlockchainLifecycle } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import 'mocha';
import { ContractWrappers, OrderStatus } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// tslint:disable:custom-no-magic-numbers
describe('ForwarderWrapper', () => {
const fillableAmount = new BigNumber(5);
let contractWrappers: ContractWrappers;
let fillScenarios: FillScenarios;
let exchangeContractAddress: string;
let zrxTokenAddress: string;
let userAddresses: string[];
let makerAddress: string;
let takerAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let signedOrder: SignedOrder;
let anotherSignedOrder: SignedOrder;
before(async () => {
const contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
fillScenarios = new FillScenarios(
provider,
userAddresses,
zrxTokenAddress,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
contractWrappers.erc721Proxy.address,
);
[, makerAddress, takerAddress] = userAddresses;
[makerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
takerTokenAddress = contractWrappers.forwarder.etherTokenAddress;
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#marketBuyOrdersWithEthAsync', () => {
it('should market buy orders with eth', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = signedOrder.makerAssetAmount.plus(anotherSignedOrder.makerAssetAmount);
const txHash = await contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
signedOrders,
makerAssetFillAmount,
takerAddress,
makerAssetFillAmount,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const ordersInfo = await contractWrappers.exchange.getOrdersInfoAsync([signedOrder, anotherSignedOrder]);
expect(ordersInfo[0].orderStatus).to.be.equal(OrderStatus.FullyFilled);
expect(ordersInfo[1].orderStatus).to.be.equal(OrderStatus.FullyFilled);
});
it('should throw when invalid transaction and shouldValidate is true', async () => {
const signedOrders = [signedOrder];
// request more makerAsset than what is available
const makerAssetFillAmount = signedOrder.makerAssetAmount.plus(100);
return expect(
contractWrappers.forwarder.marketBuyOrdersWithEthAsync(
signedOrders,
makerAssetFillAmount,
takerAddress,
makerAssetFillAmount,
[],
0,
constants.NULL_ADDRESS,
{
shouldValidate: true,
},
),
).to.be.rejectedWith('COMPLETE_FILL_FAILED');
});
});
describe('#marketSellOrdersWithEthAsync', () => {
it('should market sell orders with eth', async () => {
const signedOrders = [signedOrder, anotherSignedOrder];
const makerAssetFillAmount = signedOrder.makerAssetAmount.plus(anotherSignedOrder.makerAssetAmount);
const txHash = await contractWrappers.forwarder.marketSellOrdersWithEthAsync(
signedOrders,
takerAddress,
makerAssetFillAmount,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
const ordersInfo = await contractWrappers.exchange.getOrdersInfoAsync([signedOrder, anotherSignedOrder]);
expect(ordersInfo[0].orderStatus).to.be.equal(OrderStatus.FullyFilled);
expect(ordersInfo[1].orderStatus).to.be.equal(OrderStatus.Fillable);
expect(ordersInfo[1].orderTakerAssetFilledAmount).to.be.bignumber.equal(new BigNumber(4)); // only 95% of ETH is sold
});
it('should throw when invalid transaction and shouldValidate is true', async () => {
// create an order with fees, we try to fill it but we do not provide enough ETH to cover the fees
const signedOrderWithFee = await fillScenarios.createFillableSignedOrderWithFeesAsync(
makerAssetData,
takerAssetData,
constants.ZERO_AMOUNT,
new BigNumber(100),
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
constants.NULL_ADDRESS,
);
const signedOrders = [signedOrderWithFee];
const makerAssetFillAmount = signedOrder.makerAssetAmount;
return expect(
contractWrappers.forwarder.marketSellOrdersWithEthAsync(
signedOrders,
takerAddress,
makerAssetFillAmount,
[],
0,
constants.NULL_ADDRESS,
{
shouldValidate: true,
},
),
).to.be.rejectedWith('COMPLETE_FILL_FAILED');
});
});
});

View File

@ -1,146 +0,0 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { BlockchainLifecycle } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import * as chai from 'chai';
import * as _ from 'lodash';
import 'mocha';
import { ContractWrappers, OrderStatus } from '../src';
import { OrderInfo, TraderInfo } from '../src/types';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
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('OrderValidator', () => {
const fillableAmount = new BigNumber(5);
let contractWrappers: ContractWrappers;
let fillScenarios: FillScenarios;
let exchangeContractAddress: string;
let zrxTokenAddress: string;
let zrxTokenAssetData: string;
let userAddresses: string[];
let coinbase: string;
let makerAddress: string;
let takerAddress: string;
let feeRecipient: string;
let anotherMakerAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let signedOrder: SignedOrder;
let anotherSignedOrder: SignedOrder;
let contractAddresses: ContractAddresses;
before(async () => {
contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
zrxTokenAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress);
fillScenarios = new FillScenarios(
provider,
userAddresses,
zrxTokenAddress,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
contractWrappers.erc721Proxy.address,
);
[coinbase, makerAddress, takerAddress, feeRecipient, anotherMakerAddress] = userAddresses;
[makerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
takerTokenAddress = contractAddresses.etherToken;
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
anotherSignedOrder = await fillScenarios.createFillableSignedOrderAsync(
zrxTokenAssetData,
takerAssetData,
makerAddress,
constants.NULL_ADDRESS,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#getOrdersAndTradersInfoAsync', () => {
let signedOrders: SignedOrder[];
let takerAddresses: string[];
let ordersInfo: OrderInfo[];
let tradersInfo: TraderInfo[];
beforeEach(async () => {
signedOrders = [signedOrder, anotherSignedOrder];
takerAddresses = [takerAddress, takerAddress];
const ordersAndTradersInfo = await contractWrappers.orderValidator.getOrdersAndTradersInfoAsync(
signedOrders,
takerAddresses,
);
ordersInfo = _.map(ordersAndTradersInfo, orderAndTraderInfo => orderAndTraderInfo.orderInfo);
tradersInfo = _.map(ordersAndTradersInfo, orderAndTraderInfo => orderAndTraderInfo.traderInfo);
});
it('should return the same number of order infos and trader infos as input orders', async () => {
expect(ordersInfo.length).to.be.equal(signedOrders.length);
expect(tradersInfo.length).to.be.equal(takerAddresses.length);
});
it('should return correct on-chain order info for input orders', async () => {
const firstOrderInfo = ordersInfo[0];
const secondOrderInfo = ordersInfo[1];
expect(firstOrderInfo.orderStatus).to.be.equal(OrderStatus.Fillable);
expect(firstOrderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
expect(secondOrderInfo.orderStatus).to.be.equal(OrderStatus.Fillable);
expect(secondOrderInfo.orderTakerAssetFilledAmount).to.bignumber.equal(constants.ZERO_AMOUNT);
});
it('should return correct on-chain trader info for input takers', async () => {
const firstTraderInfo = tradersInfo[0];
const secondTraderInfo = tradersInfo[1];
expect(firstTraderInfo.makerBalance).to.bignumber.equal(new BigNumber(5));
expect(firstTraderInfo.makerAllowance).to.bignumber.equal(new BigNumber(5));
expect(firstTraderInfo.takerBalance).to.bignumber.equal(new BigNumber(0));
expect(firstTraderInfo.takerAllowance).to.bignumber.equal(new BigNumber(0));
expect(firstTraderInfo.makerZrxBalance).to.bignumber.equal(new BigNumber(5));
expect(firstTraderInfo.makerZrxAllowance).to.bignumber.equal(new BigNumber(5));
expect(firstTraderInfo.takerZrxBalance).to.bignumber.equal(new BigNumber(0));
expect(firstTraderInfo.takerZrxAllowance).to.bignumber.equal(new BigNumber(0));
expect(secondTraderInfo.makerBalance).to.bignumber.equal(new BigNumber(5));
expect(secondTraderInfo.makerAllowance).to.bignumber.equal(new BigNumber(5));
expect(secondTraderInfo.takerBalance).to.bignumber.equal(new BigNumber(0));
expect(secondTraderInfo.takerAllowance).to.bignumber.equal(new BigNumber(0));
expect(secondTraderInfo.makerZrxBalance).to.bignumber.equal(new BigNumber(5));
expect(secondTraderInfo.makerZrxAllowance).to.bignumber.equal(new BigNumber(5));
expect(secondTraderInfo.takerZrxBalance).to.bignumber.equal(new BigNumber(0));
expect(secondTraderInfo.takerZrxAllowance).to.bignumber.equal(new BigNumber(0));
});
});
});

View File

@ -1,115 +0,0 @@
import { BlockchainLifecycle, devConstants, web3Factory } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { runMigrationsAsync } from '@0x/migrations';
import { assetDataUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
import 'mocha';
import { ContractWrappers } from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { tokenUtils } from './utils/token_utils';
chaiSetup.configure();
const expect = chai.expect;
describe('Revert Validation ExchangeWrapper', () => {
let contractWrappers: ContractWrappers;
let userAddresses: string[];
let fillScenarios: FillScenarios;
let makerTokenAddress: string;
let takerTokenAddress: string;
let makerAddress: string;
let takerAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let txHash: string;
let blockchainLifecycle: BlockchainLifecycle;
let web3Wrapper: Web3Wrapper;
const fillableAmount = new BigNumber(5);
const takerTokenFillAmount = new BigNumber(5);
let signedOrder: SignedOrder;
before(async () => {
// vmErrorsOnRPCResponse is useful for quick feedback and testing during development
// but is not the default behaviour in production. Here we ensure our failure cases
// are handled in an environment which behaves similar to production
const provider = web3Factory.getRpcProvider({
shouldUseInProcessGanache: true,
shouldThrowErrorsOnGanacheRPCResponse: false,
});
web3Wrapper = new Web3Wrapper(provider);
blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
// Re-deploy the artifacts in this provider, rather than in the default provider exposed in
// the beforeAll hook. This is due to the fact that the default provider enabled vmErrorsOnRPCResponse
// and we are explicity testing with vmErrorsOnRPCResponse disabled.
const txDefaults = {
gas: devConstants.GAS_LIMIT,
from: devConstants.TESTRPC_FIRST_ADDRESS,
};
await blockchainLifecycle.startAsync();
const contractAddresses = await runMigrationsAsync(provider, txDefaults);
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
fillScenarios = new FillScenarios(
provider,
userAddresses,
contractAddresses.zrxToken,
contractAddresses.exchange,
contractAddresses.erc20Proxy,
contractAddresses.erc721Proxy,
);
[, makerAddress, takerAddress] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#fillOrderAsync', () => {
it('should throw the revert reason when shouldValidate is true and a fill would revert', async () => {
// Create a scenario where the fill will revert
const makerTokenBalance = await contractWrappers.erc20Token.getBalanceAsync(
makerTokenAddress,
makerAddress,
);
// Transfer all of the tokens from maker to create a failure scenario
txHash = await contractWrappers.erc20Token.transferAsync(
makerTokenAddress,
makerAddress,
takerAddress,
makerTokenBalance,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
return expect(
contractWrappers.exchange.fillOrderAsync(signedOrder, takerTokenFillAmount, takerAddress, {
shouldValidate: true,
}),
).to.be.rejectedWith('TRANSFER_FAILED');
});
});
});

View File

@ -1,75 +0,0 @@
import { BlockchainLifecycle } from '@0x/dev-utils';
import { DoneCallback } from '@0x/types';
import * as _ from 'lodash';
import 'mocha';
import * as Sinon from 'sinon';
import {
ContractWrappers,
ContractWrappersConfig,
DecodedLogEvent,
ERC20TokenApprovalEventArgs,
ERC20TokenEvents,
} from '../src';
import { chaiSetup } from './utils/chai_setup';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
chaiSetup.configure();
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('SubscriptionTest', () => {
let contractWrappers: ContractWrappers;
let config: ContractWrappersConfig;
before(async () => {
const contractAddresses = await migrateOnceAsync();
config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
};
contractWrappers = new ContractWrappers(provider, config);
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('#subscribe', () => {
const indexFilterValues = {};
let tokenAddress: string;
let stubs: Sinon.SinonStub[] = [];
before(() => {
const tokenAddresses = tokenUtils.getDummyERC20TokenAddresses();
tokenAddress = tokenAddresses[0];
});
afterEach(() => {
contractWrappers.erc20Token.unsubscribeAll();
_.each(stubs, s => s.restore());
stubs = [];
});
it('Should allow unsubscribeAll to be called successfully after an error', (done: DoneCallback) => {
(async () => {
const callback = (err: Error | null, _logEvent?: DecodedLogEvent<ERC20TokenApprovalEventArgs>) =>
_.noop.bind(_);
contractWrappers.erc20Token.subscribe(
tokenAddress,
ERC20TokenEvents.Approval,
indexFilterValues,
callback,
);
stubs = [
Sinon.stub((contractWrappers as any)._web3Wrapper, 'getBlockIfExistsAsync').throws(
new Error('JSON RPC error'),
),
];
contractWrappers.erc20Token.unsubscribeAll();
done();
})().catch(done);
});
});
});

View File

@ -1,210 +0,0 @@
import { BlockchainLifecycle } from '@0x/dev-utils';
import { FillScenarios } from '@0x/fill-scenarios';
import { assetDataUtils, generatePseudoRandomSalt, orderHashUtils, signatureUtils } from '@0x/order-utils';
import { SignedOrder } from '@0x/types';
import { BigNumber } from '@0x/utils';
import 'mocha';
import { ContractWrappers } from '../src';
import { TransactionEncoder } from '../src/utils/transaction_encoder';
import { constants } from './utils/constants';
import { migrateOnceAsync } from './utils/migrate';
import { tokenUtils } from './utils/token_utils';
import { provider, web3Wrapper } from './utils/web3_wrapper';
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('TransactionEncoder', () => {
let contractWrappers: ContractWrappers;
let userAddresses: string[];
let fillScenarios: FillScenarios;
let exchangeContractAddress: string;
let makerTokenAddress: string;
let takerTokenAddress: string;
let coinbase: string;
let makerAddress: string;
let senderAddress: string;
let takerAddress: string;
let makerAssetData: string;
let takerAssetData: string;
let txHash: string;
const fillableAmount = new BigNumber(5);
const takerTokenFillAmount = new BigNumber(5);
let signedOrder: SignedOrder;
before(async () => {
const contractAddresses = await migrateOnceAsync();
await blockchainLifecycle.startAsync();
const config = {
networkId: constants.TESTRPC_NETWORK_ID,
contractAddresses,
blockPollingIntervalMs: 10,
};
contractWrappers = new ContractWrappers(provider, config);
exchangeContractAddress = contractWrappers.exchange.address;
userAddresses = await web3Wrapper.getAvailableAddressesAsync();
const zrxTokenAddress = contractWrappers.exchange.zrxTokenAddress;
fillScenarios = new FillScenarios(
provider,
userAddresses,
zrxTokenAddress,
exchangeContractAddress,
contractWrappers.erc20Proxy.address,
contractWrappers.erc721Proxy.address,
);
[coinbase, makerAddress, takerAddress, senderAddress] = userAddresses;
[makerTokenAddress, takerTokenAddress] = tokenUtils.getDummyERC20TokenAddresses();
[makerAssetData, takerAssetData] = [
assetDataUtils.encodeERC20AssetData(makerTokenAddress),
assetDataUtils.encodeERC20AssetData(takerTokenAddress),
];
signedOrder = await fillScenarios.createFillableSignedOrderAsync(
makerAssetData,
takerAssetData,
makerAddress,
takerAddress,
fillableAmount,
);
});
after(async () => {
await blockchainLifecycle.revertAsync();
});
beforeEach(async () => {
await blockchainLifecycle.startAsync();
});
afterEach(async () => {
await blockchainLifecycle.revertAsync();
});
describe('encode and executeTransaction', () => {
const executeTransactionOrThrowAsync = async (
encoder: TransactionEncoder,
data: string,
signerAddress: string = takerAddress,
): Promise<void> => {
const salt = generatePseudoRandomSalt();
const transactionHash = encoder.getTransactionHashHex(data, salt, signerAddress);
const signature = await signatureUtils.ecSignHashAsync(provider, transactionHash, signerAddress);
txHash = await contractWrappers.exchange.executeTransactionAsync(
salt,
signerAddress,
data,
signature,
senderAddress,
);
await web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
};
describe('#fillOrderTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.fillOrderTx(signedOrder, takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#fillOrderNoThrowTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.fillOrderNoThrowTx(signedOrder, takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#fillOrKillOrderTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.fillOrKillOrderTx(signedOrder, takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#marketSellOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.marketSellOrdersTx([signedOrder], takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#marketSellOrdersNoThrowTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.marketSellOrdersNoThrowTx([signedOrder], takerTokenFillAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#marketBuyOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.marketBuyOrdersTx([signedOrder], fillableAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#marketBuyOrdersNoThrowTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.marketBuyOrdersNoThrowTx([signedOrder], fillableAmount);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#preSignTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
const signature = signedOrder.signature;
const data = encoder.preSignTx(orderHash, makerAddress, signature);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#setSignatureValidatorApprovalTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const isApproved = true;
const data = encoder.setSignatureValidatorApprovalTx(senderAddress, isApproved);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#batchFillOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.batchFillOrdersTx([signedOrder], [takerTokenFillAmount]);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#batchFillOrKillOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.batchFillOrKillOrdersTx([signedOrder], [takerTokenFillAmount]);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#batchFillOrdersNoThrowTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.batchFillOrdersNoThrowTx([signedOrder], [takerTokenFillAmount]);
await executeTransactionOrThrowAsync(encoder, data);
});
});
describe('#batchCancelOrdersTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.batchCancelOrdersTx([signedOrder]);
const signerAddress = makerAddress;
await executeTransactionOrThrowAsync(encoder, data, signerAddress);
});
});
describe('#cancelOrderTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const data = encoder.cancelOrderTx(signedOrder);
const signerAddress = makerAddress;
await executeTransactionOrThrowAsync(encoder, data, signerAddress);
});
});
describe('#cancelOrdersUpToTx', () => {
it('should successfully execute the transaction', async () => {
const encoder = await contractWrappers.exchange.transactionEncoderAsync();
const targetEpoch = signedOrder.salt;
const data = encoder.cancelOrdersUpToTx(targetEpoch);
const signerAddress = makerAddress;
await executeTransactionOrThrowAsync(encoder, data, signerAddress);
});
});
});
});

Some files were not shown because too many files have changed in this diff Show More