aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts
diff options
context:
space:
mode:
authorfragosti <francesco.agosti93@gmail.com>2018-08-21 03:01:58 +0800
committerfragosti <francesco.agosti93@gmail.com>2018-08-21 03:01:58 +0800
commitcabce8cb674e937a566e8a821e3959076390f603 (patch)
tree085b13d8d39c78ae6b5b58157ad73ca5efea544f /packages/contracts
parent44cc5e45cc3a3ed7db2691a287500e5d61a2d0c1 (diff)
parent756787c61fff9c6b463fbbd69b48210dafe62004 (diff)
downloaddexon-sol-tools-cabce8cb674e937a566e8a821e3959076390f603.tar
dexon-sol-tools-cabce8cb674e937a566e8a821e3959076390f603.tar.gz
dexon-sol-tools-cabce8cb674e937a566e8a821e3959076390f603.tar.bz2
dexon-sol-tools-cabce8cb674e937a566e8a821e3959076390f603.tar.lz
dexon-sol-tools-cabce8cb674e937a566e8a821e3959076390f603.tar.xz
dexon-sol-tools-cabce8cb674e937a566e8a821e3959076390f603.tar.zst
dexon-sol-tools-cabce8cb674e937a566e8a821e3959076390f603.zip
Merge branch 'development' of https://github.com/0xProject/0x-monorepo into feature/connect/sra-api-v2
Diffstat (limited to 'packages/contracts')
-rw-r--r--packages/contracts/compiler.json2
-rw-r--r--packages/contracts/package.json12
-rw-r--r--packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol33
-rw-r--r--packages/contracts/src/2.0.0/test/DummyERC20Token/DummyNoReturnERC20Token.sol116
-rw-r--r--packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol68
-rw-r--r--packages/contracts/src/2.0.0/test/DummyERC721Receiver/InvalidERC721Receiver.sol66
-rw-r--r--packages/contracts/src/2.0.0/test/DummyERC721Token/DummyERC721Token.sol61
-rw-r--r--packages/contracts/src/2.0.0/test/Mintable/Mintable.sol43
-rw-r--r--packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol79
-rw-r--r--packages/contracts/src/2.0.0/tokens/ERC20Token/IERC20Token.sol65
-rw-r--r--packages/contracts/src/2.0.0/tokens/ERC20Token/MintableERC20Token.sol61
-rw-r--r--packages/contracts/src/2.0.0/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol (renamed from packages/contracts/src/2.0.0/tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol)23
-rw-r--r--packages/contracts/src/2.0.0/tokens/ERC721Token/ERC721Token.sol554
-rw-r--r--packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Receiver.sol81
-rw-r--r--packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Token.sol198
-rw-r--r--packages/contracts/src/2.0.0/tokens/ERC721Token/MintableERC721Token.sol83
-rw-r--r--packages/contracts/test/asset_proxy/proxies.ts4
-rw-r--r--packages/contracts/test/exchange/core.ts133
-rw-r--r--packages/contracts/test/exchange/dispatcher.ts2
-rw-r--r--packages/contracts/test/exchange/fill_order.ts2
-rw-r--r--packages/contracts/test/exchange/signature_validator.ts2
-rw-r--r--packages/contracts/test/multisig/asset_proxy_owner.ts4
-rw-r--r--packages/contracts/test/tokens/erc721_token.ts279
-rw-r--r--packages/contracts/test/tokens/unlimited_allowance_token.ts2
-rw-r--r--packages/contracts/test/utils/artifacts.ts4
-rw-r--r--packages/contracts/test/utils/erc721_wrapper.ts3
-rw-r--r--packages/contracts/test/utils/exchange_wrapper.ts5
-rw-r--r--packages/contracts/test/utils/fill_order_combinatorial_utils.ts6
-rw-r--r--packages/contracts/test/utils/forwarder_wrapper.ts2
-rw-r--r--packages/contracts/test/utils/log_decoder.ts5
-rw-r--r--packages/contracts/test/utils/multi_sig_wrapper.ts2
31 files changed, 1345 insertions, 655 deletions
diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json
index ad35fc5b3..60605e23b 100644
--- a/packages/contracts/compiler.json
+++ b/packages/contracts/compiler.json
@@ -23,6 +23,7 @@
"DummyERC20Token",
"DummyERC721Receiver",
"DummyERC721Token",
+ "DummyNoReturnERC20Token",
"ERC20Proxy",
"ERC20Token",
"ERC721Token",
@@ -32,6 +33,7 @@
"Forwarder",
"IAssetData",
"IAssetProxy",
+ "InvalidERC721Receiver",
"IValidator",
"IWallet",
"MixinAuthorizable",
diff --git a/packages/contracts/package.json b/packages/contracts/package.json
index 8bd808a27..b25b33c20 100644
--- a/packages/contracts/package.json
+++ b/packages/contracts/package.json
@@ -20,11 +20,14 @@
"test:coverage": "SOLIDITY_COVERAGE=true run-s build run_mocha coverage:report:text coverage:report:lcov",
"test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html",
"test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha",
- "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
+ "run_mocha":
+ "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler --contracts-dir src",
"clean": "shx rm -rf lib generated_contract_wrappers",
- "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers",
- "lint": "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts",
+ "generate_contract_wrappers":
+ "abi-gen --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output generated_contract_wrappers --backend ethers",
+ "lint":
+ "tslint --project . --exclude **/src/generated_contract_wrappers/**/* --exclude **/lib/**/* && yarn lint-contracts",
"coverage:report:text": "istanbul report text",
"coverage:report:html": "istanbul report html && open coverage/index.html",
"profiler:report:html": "istanbul report html && open coverage/index.html",
@@ -33,7 +36,8 @@
"lint-contracts": "solhint src/2.0.0/**/**/**/**/*.sol"
},
"config": {
- "abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
+ "abis":
+ "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|DummyNoReturnERC20Token|ERC20Proxy|ERC721Proxy|Forwarder|Exchange|ExchangeWrapper|IAssetData|IAssetProxy|InvalidERC721Receiver|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetProxyOwner|TestAssetProxyDispatcher|TestConstants|TestExchangeInternals|TestLibBytes|TestLibs|TestSignatureValidator|Validator|Wallet|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
},
"repository": {
"type": "git",
diff --git a/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol
index 9272b18a8..412c5d1ad 100644
--- a/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol
+++ b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyERC20Token.sol
@@ -18,17 +18,18 @@
pragma solidity 0.4.24;
-import "../Mintable/Mintable.sol";
import "../../utils/Ownable/Ownable.sol";
+import "../../tokens/ERC20Token/MintableERC20Token.sol";
contract DummyERC20Token is
- Mintable,
- Ownable
+ Ownable,
+ MintableERC20Token
{
string public name;
string public symbol;
uint256 public decimals;
+ uint256 public constant MAX_MINT_AMOUNT = 10000000000000000000000;
constructor (
string _name,
@@ -41,20 +42,36 @@ contract DummyERC20Token is
name = _name;
symbol = _symbol;
decimals = _decimals;
- totalSupply = _totalSupply;
+ _totalSupply = _totalSupply;
balances[msg.sender] = _totalSupply;
}
+ /// @dev Sets the balance of target address
+ /// @param _target Address or which balance will be updated
+ /// @param _value New balance of target address
function setBalance(address _target, uint256 _value)
- public
+ external
onlyOwner
{
- uint256 currBalance = balanceOf(_target);
+ uint256 currBalance = balances[_target];
if (_value < currBalance) {
- totalSupply = safeSub(totalSupply, safeSub(currBalance, _value));
+ _totalSupply = safeSub(_totalSupply, safeSub(currBalance, _value));
} else {
- totalSupply = safeAdd(totalSupply, safeSub(_value, currBalance));
+ _totalSupply = safeAdd(_totalSupply, safeSub(_value, currBalance));
}
balances[_target] = _value;
}
+
+ /// @dev Mints new tokens for sender
+ /// @param _value Amount of tokens to mint
+ function mint(uint256 _value)
+ external
+ {
+ require(
+ _value <= MAX_MINT_AMOUNT,
+ "VALUE_TOO_LARGE"
+ );
+
+ _mint(msg.sender, _value);
+ }
}
diff --git a/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyNoReturnERC20Token.sol b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyNoReturnERC20Token.sol
new file mode 100644
index 000000000..79156d3dd
--- /dev/null
+++ b/packages/contracts/src/2.0.0/test/DummyERC20Token/DummyNoReturnERC20Token.sol
@@ -0,0 +1,116 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+import "./DummyERC20Token.sol";
+
+
+// solhint-disable no-empty-blocks
+contract DummyNoReturnERC20Token is
+ DummyERC20Token
+{
+
+ constructor (
+ string _name,
+ string _symbol,
+ uint256 _decimals,
+ uint256 _totalSupply
+ )
+ public
+ DummyERC20Token(
+ _name,
+ _symbol,
+ _decimals,
+ _totalSupply
+ )
+ {}
+
+ /// @dev send `value` token to `to` from `msg.sender`
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ function transfer(address _to, uint256 _value)
+ external
+ returns (bool)
+ {
+ require(
+ balances[msg.sender] >= _value,
+ "ERC20_INSUFFICIENT_BALANCE"
+ );
+ require(
+ balances[_to] + _value >= balances[_to],
+ "UINT256_OVERFLOW"
+ );
+
+ balances[msg.sender] -= _value;
+ balances[_to] += _value;
+
+ emit Transfer(
+ msg.sender,
+ _to,
+ _value
+ );
+
+ // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return no data
+ assembly {
+ return(0, 0)
+ }
+ }
+
+ /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`
+ /// @param _from The address of the sender
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _value
+ )
+ external
+ returns (bool)
+ {
+ require(
+ balances[_from] >= _value,
+ "ERC20_INSUFFICIENT_BALANCE"
+ );
+ require(
+ allowed[_from][msg.sender] >= _value,
+ "ERC20_INSUFFICIENT_ALLOWANCE"
+ );
+ require(
+ balances[_to] + _value >= balances[_to],
+ "UINT256_OVERFLOW"
+ );
+
+ balances[_to] += _value;
+ balances[_from] -= _value;
+ allowed[_from][msg.sender] -= _value;
+
+ emit Transfer(
+ _from,
+ _to,
+ _value
+ );
+
+ // HACK: This contract will not compile if we remove `returns (bool)`, so we manually return no data
+ assembly {
+ return(0, 0)
+ }
+ }
+}
+
diff --git a/packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol b/packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol
index 5dce74a14..ac95e47bd 100644
--- a/packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol
+++ b/packages/contracts/src/2.0.0/test/DummyERC721Receiver/DummyERC721Receiver.sol
@@ -1,26 +1,19 @@
/*
-The MIT License (MIT)
-Copyright (c) 2016 Smart Contract Solutions, Inc.
+ Copyright 2018 ZeroEx Intl.
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
pragma solidity 0.4.24;
@@ -32,33 +25,44 @@ contract DummyERC721Receiver is
IERC721Receiver
{
+ // Function selector for ERC721Receiver.onERC721Received
+ // 0x150b7a02
+ bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
+
event TokenReceived(
+ address operator,
address from,
uint256 tokenId,
bytes data
);
- /**
- * @notice Handle the receipt of an NFT
- * @dev The ERC721 smart contract calls this function on the recipient
- * after a `safetransfer`. This function MAY throw to revert and reject the
- * transfer. This function MUST use 50,000 gas or less. Return of other
- * than the magic value MUST result in the transaction being reverted.
- * Note: the contract address is always the message sender.
- * @param _from The sending address
- * @param _tokenId The NFT identifier which is being transfered
- * @param _data Additional data with no specified format
- * @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
- */
+ /// @notice Handle the receipt of an NFT
+ /// @dev The ERC721 smart contract calls this function on the recipient
+ /// after a `transfer`. This function MAY throw to revert and reject the
+ /// transfer. Return of other than the magic value MUST result in the
+ /// transaction being reverted.
+ /// Note: the contract address is always the message sender.
+ /// @param _operator The address which called `safeTransferFrom` function
+ /// @param _from The address which previously owned the token
+ /// @param _tokenId The NFT identifier which is being transferred
+ /// @param _data Additional data with no specified format
+ /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
+ /// unless throwing
function onERC721Received(
+ address _operator,
address _from,
uint256 _tokenId,
bytes _data
)
- public
+ external
returns (bytes4)
{
- emit TokenReceived(_from, _tokenId, _data);
+ emit TokenReceived(
+ _operator,
+ _from,
+ _tokenId,
+ _data
+ );
return ERC721_RECEIVED;
}
}
diff --git a/packages/contracts/src/2.0.0/test/DummyERC721Receiver/InvalidERC721Receiver.sol b/packages/contracts/src/2.0.0/test/DummyERC721Receiver/InvalidERC721Receiver.sol
new file mode 100644
index 000000000..309633bf5
--- /dev/null
+++ b/packages/contracts/src/2.0.0/test/DummyERC721Receiver/InvalidERC721Receiver.sol
@@ -0,0 +1,66 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+import "../../tokens/ERC721Token/IERC721Receiver.sol";
+
+
+contract InvalidERC721Receiver is
+ IERC721Receiver
+{
+ // Actual function signature is `onERC721Received(address,address,uint256,bytes)`
+ bytes4 constant internal INVALID_ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,uint256,bytes)"));
+
+ event TokenReceived(
+ address operator,
+ address from,
+ uint256 tokenId,
+ bytes data
+ );
+
+ /// @notice Handle the receipt of an NFT
+ /// @dev The ERC721 smart contract calls this function on the recipient
+ /// after a `transfer`. This function MAY throw to revert and reject the
+ /// transfer. Return of other than the magic value MUST result in the
+ /// transaction being reverted.
+ /// Note: the contract address is always the message sender.
+ /// @param _operator The address which called `safeTransferFrom` function
+ /// @param _from The address which previously owned the token
+ /// @param _tokenId The NFT identifier which is being transferred
+ /// @param _data Additional data with no specified format
+ /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
+ /// unless throwing
+ function onERC721Received(
+ address _operator,
+ address _from,
+ uint256 _tokenId,
+ bytes _data
+ )
+ external
+ returns (bytes4)
+ {
+ emit TokenReceived(
+ _operator,
+ _from,
+ _tokenId,
+ _data
+ );
+ return INVALID_ERC721_RECEIVED;
+ }
+}
diff --git a/packages/contracts/src/2.0.0/test/DummyERC721Token/DummyERC721Token.sol b/packages/contracts/src/2.0.0/test/DummyERC721Token/DummyERC721Token.sol
index 627746a52..ac9068d1d 100644
--- a/packages/contracts/src/2.0.0/test/DummyERC721Token/DummyERC721Token.sol
+++ b/packages/contracts/src/2.0.0/test/DummyERC721Token/DummyERC721Token.sol
@@ -18,59 +18,46 @@
pragma solidity 0.4.24;
-import "../../tokens/ERC721Token/ERC721Token.sol";
+import "../../tokens/ERC721Token/MintableERC721Token.sol";
import "../../utils/Ownable/Ownable.sol";
// solhint-disable no-empty-blocks
contract DummyERC721Token is
Ownable,
- ERC721Token
+ MintableERC721Token
{
+ string public name;
+ string public symbol;
- /**
- * @dev Constructor passes its arguments to the base ERC721Token constructor
- * @param name of token
- * @param symbol of token
- */
constructor (
- string name,
- string symbol
+ string _name,
+ string _symbol
)
public
- ERC721Token(name, symbol)
- {}
+ {
+ name = _name;
+ symbol = _symbol;
+ }
- /**
- * @dev Function to mint a new token
- * @dev Reverts if the given token ID already exists
- * @param to address the beneficiary that will own the minted token
- * @param tokenId uint256 ID of the token to be minted by the msg.sender
- */
- function mint(address to, uint256 tokenId)
- public
- onlyOwner
+ /// @dev Function to mint a new token
+ /// Reverts if the given token ID already exists
+ /// @param _to Address of the beneficiary that will own the minted token
+ /// @param _tokenId ID of the token to be minted by the msg.sender
+ function mint(address _to, uint256 _tokenId)
+ external
{
- require(
- !exists(tokenId),
- "Token with tokenId already exists."
- );
- _mint(to, tokenId);
+ _mint(_to, _tokenId);
}
- /**
- * @dev Function to burn a token
- * @dev Reverts if the given token ID doesn't exist
- * @param tokenId uint256 ID of the token to be minted by the msg.sender
- */
- function burn(address owner, uint256 tokenId)
- public
+ /// @dev Function to burn a token
+ /// Reverts if the given token ID doesn't exist or not called by contract owner
+ /// @param _owner Owner of token with given token ID
+ /// @param _tokenId ID of the token to be burned by the msg.sender
+ function burn(address _owner, uint256 _tokenId)
+ external
onlyOwner
{
- require(
- exists(tokenId),
- "Token with tokenId does not exist."
- );
- _burn(owner, tokenId);
+ _burn(_owner, _tokenId);
}
}
diff --git a/packages/contracts/src/2.0.0/test/Mintable/Mintable.sol b/packages/contracts/src/2.0.0/test/Mintable/Mintable.sol
deleted file mode 100644
index 767cc8d25..000000000
--- a/packages/contracts/src/2.0.0/test/Mintable/Mintable.sol
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-
- Copyright 2018 ZeroEx Intl.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-*/
-
-pragma solidity 0.4.24;
-
-import "../../tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol";
-import "../../utils/SafeMath/SafeMath.sol";
-
-
-/*
- * Mintable
- * Base contract that creates a mintable UnlimitedAllowanceToken
- */
-contract Mintable is
- UnlimitedAllowanceToken,
- SafeMath
-{
- function mint(uint256 _value)
- public
- {
- require(
- _value <= 100000000000000000000,
- "Minting more than 100000000000000000000 is not allowed."
- );
- balances[msg.sender] = safeAdd(_value, balances[msg.sender]);
- totalSupply = safeAdd(totalSupply, _value);
- }
-}
diff --git a/packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol b/packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol
index d9950145d..5ef5ee7ce 100644
--- a/packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol
+++ b/packages/contracts/src/2.0.0/tokens/ERC20Token/ERC20Token.sol
@@ -21,15 +21,21 @@ pragma solidity 0.4.24;
import "./IERC20Token.sol";
-contract ERC20Token is IERC20Token {
+contract ERC20Token is
+ IERC20Token
+{
mapping (address => uint256) internal balances;
mapping (address => mapping (address => uint256)) internal allowed;
- uint256 public totalSupply;
+ uint256 internal _totalSupply;
+ /// @dev send `value` token to `to` from `msg.sender`
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ /// @return True if transfer was successful
function transfer(address _to, uint256 _value)
- public
+ external
returns (bool)
{
require(
@@ -38,16 +44,32 @@ contract ERC20Token is IERC20Token {
);
require(
balances[_to] + _value >= balances[_to],
- "OVERFLOW"
+ "UINT256_OVERFLOW"
);
+
balances[msg.sender] -= _value;
balances[_to] += _value;
- emit Transfer(msg.sender, _to, _value);
+
+ emit Transfer(
+ msg.sender,
+ _to,
+ _value
+ );
+
return true;
}
- function transferFrom(address _from, address _to, uint256 _value)
- public
+ /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`
+ /// @param _from The address of the sender
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ /// @return True if transfer was successful
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _value
+ )
+ external
returns (bool)
{
require(
@@ -60,34 +82,65 @@ contract ERC20Token is IERC20Token {
);
require(
balances[_to] + _value >= balances[_to],
- "OVERFLOW"
+ "UINT256_OVERFLOW"
);
+
balances[_to] += _value;
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
- emit Transfer(_from, _to, _value);
+
+ emit Transfer(
+ _from,
+ _to,
+ _value
+ );
+
return true;
}
+ /// @dev `msg.sender` approves `_spender` to spend `_value` tokens
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @param _value The amount of wei to be approved for transfer
+ /// @return Always true if the call has enough gas to complete execution
function approve(address _spender, uint256 _value)
- public
+ external
returns (bool)
{
allowed[msg.sender][_spender] = _value;
- emit Approval(msg.sender, _spender, _value);
+ emit Approval(
+ msg.sender,
+ _spender,
+ _value
+ );
return true;
}
+ /// @dev Query total supply of token
+ /// @return Total supply of token
+ function totalSupply()
+ external
+ view
+ returns (uint256)
+ {
+ return _totalSupply;
+ }
+
+ /// @dev Query the balance of owner
+ /// @param _owner The address from which the balance will be retrieved
+ /// @return Balance of owner
function balanceOf(address _owner)
- public
+ external
view
returns (uint256)
{
return balances[_owner];
}
+ /// @param _owner The address of the account owning tokens
+ /// @param _spender The address of the account able to transfer the tokens
+ /// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender)
- public
+ external
view
returns (uint256)
{
diff --git a/packages/contracts/src/2.0.0/tokens/ERC20Token/IERC20Token.sol b/packages/contracts/src/2.0.0/tokens/ERC20Token/IERC20Token.sol
index 5ee5e1011..258d47393 100644
--- a/packages/contracts/src/2.0.0/tokens/ERC20Token/IERC20Token.sol
+++ b/packages/contracts/src/2.0.0/tokens/ERC20Token/IERC20Token.sol
@@ -21,54 +21,67 @@ pragma solidity 0.4.24;
contract IERC20Token {
- /// @notice send `value` token to `to` from `msg.sender`
+ // solhint-disable no-simple-event-func-name
+ event Transfer(
+ address indexed _from,
+ address indexed _to,
+ uint256 _value
+ );
+
+ event Approval(
+ address indexed _owner,
+ address indexed _spender,
+ uint256 _value
+ );
+
+ /// @dev send `value` token to `to` from `msg.sender`
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
- /// @return Whether the transfer was successful or not
+ /// @return True if transfer was successful
function transfer(address _to, uint256 _value)
- public
+ external
returns (bool);
- /// @notice send `value` token to `to` from `from` on the condition it is approved by `from`
+ /// @dev send `value` token to `to` from `from` on the condition it is approved by `from`
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _value The amount of token to be transferred
- /// @return Whether the transfer was successful or not
- function transferFrom(address _from, address _to, uint256 _value)
- public
+ /// @return True if transfer was successful
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _value
+ )
+ external
returns (bool);
- /// @notice `msg.sender` approves `_spender` to spend `_value` tokens
+ /// @dev `msg.sender` approves `_spender` to spend `_value` tokens
/// @param _spender The address of the account able to transfer the tokens
/// @param _value The amount of wei to be approved for transfer
- /// @return Whether the approval was successful or not
+ /// @return Always true if the call has enough gas to complete execution
function approve(address _spender, uint256 _value)
- public
+ external
returns (bool);
+ /// @dev Query total supply of token
+ /// @return Total supply of token
+ function totalSupply()
+ external
+ view
+ returns (uint256);
+
/// @param _owner The address from which the balance will be retrieved
- /// @return The balance
+ /// @return Balance of owner
function balanceOf(address _owner)
- public view
+ external
+ view
returns (uint256);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens allowed to spent
function allowance(address _owner, address _spender)
- public view
+ external
+ view
returns (uint256);
-
- // solhint-disable-next-line no-simple-event-func-name
- event Transfer(
- address indexed _from,
- address indexed _to,
- uint256 _value
- );
-
- event Approval(
- address indexed _owner,
- address indexed _spender,
- uint256 _value
- );
}
diff --git a/packages/contracts/src/2.0.0/tokens/ERC20Token/MintableERC20Token.sol b/packages/contracts/src/2.0.0/tokens/ERC20Token/MintableERC20Token.sol
new file mode 100644
index 000000000..cd1c7b4bb
--- /dev/null
+++ b/packages/contracts/src/2.0.0/tokens/ERC20Token/MintableERC20Token.sol
@@ -0,0 +1,61 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+import "../../utils/SafeMath/SafeMath.sol";
+import "./UnlimitedAllowanceERC20Token.sol";
+
+
+contract MintableERC20Token is
+ SafeMath,
+ UnlimitedAllowanceERC20Token
+{
+
+ /// @dev Mints new tokens
+ /// @param _to Address of the beneficiary that will own the minted token
+ /// @param _value Amount of tokens to mint
+ function _mint(address _to, uint256 _value)
+ internal
+ {
+ balances[_to] = safeAdd(_value, balances[_to]);
+ _totalSupply = safeAdd(_totalSupply, _value);
+
+ emit Transfer(
+ address(0),
+ _to,
+ _value
+ );
+ }
+
+ /// @dev Mints new tokens
+ /// @param _owner Owner of tokens that will be burned
+ /// @param _value Amount of tokens to burn
+ function _burn(address _owner, uint256 _value)
+ internal
+ {
+ balances[_owner] = safeSub(balances[_owner], _value);
+ _totalSupply = safeSub(_totalSupply, _value);
+
+ emit Transfer(
+ _owner,
+ address(0),
+ _value
+ );
+ }
+}
diff --git a/packages/contracts/src/2.0.0/tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol b/packages/contracts/src/2.0.0/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol
index 9feb5c914..e6f7c063e 100644
--- a/packages/contracts/src/2.0.0/tokens/UnlimitedAllowanceToken/UnlimitedAllowanceToken.sol
+++ b/packages/contracts/src/2.0.0/tokens/ERC20Token/UnlimitedAllowanceERC20Token.sol
@@ -21,7 +21,9 @@ pragma solidity 0.4.24;
import "../ERC20Token/ERC20Token.sol";
-contract UnlimitedAllowanceToken is ERC20Token {
+contract UnlimitedAllowanceERC20Token is
+ ERC20Token
+{
uint256 constant internal MAX_UINT = 2**256 - 1;
@@ -30,8 +32,12 @@ contract UnlimitedAllowanceToken is ERC20Token {
/// @param _to Address to transfer to.
/// @param _value Amount to transfer.
/// @return Success of transfer.
- function transferFrom(address _from, address _to, uint256 _value)
- public
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _value
+ )
+ external
returns (bool)
{
uint256 allowance = allowed[_from][msg.sender];
@@ -45,14 +51,21 @@ contract UnlimitedAllowanceToken is ERC20Token {
);
require(
balances[_to] + _value >= balances[_to],
- "OVERFLOW"
+ "UINT256_OVERFLOW"
);
+
balances[_to] += _value;
balances[_from] -= _value;
if (allowance < MAX_UINT) {
allowed[_from][msg.sender] -= _value;
}
- emit Transfer(_from, _to, _value);
+
+ emit Transfer(
+ _from,
+ _to,
+ _value
+ );
+
return true;
}
}
diff --git a/packages/contracts/src/2.0.0/tokens/ERC721Token/ERC721Token.sol b/packages/contracts/src/2.0.0/tokens/ERC721Token/ERC721Token.sol
index 60603aa19..530f080c0 100644
--- a/packages/contracts/src/2.0.0/tokens/ERC721Token/ERC721Token.sol
+++ b/packages/contracts/src/2.0.0/tokens/ERC721Token/ERC721Token.sol
@@ -1,26 +1,19 @@
/*
-The MIT License (MIT)
-Copyright (c) 2016 Smart Contract Solutions, Inc.
+ Copyright 2018 ZeroEx Intl.
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
pragma solidity 0.4.24;
@@ -30,179 +23,250 @@ import "./IERC721Receiver.sol";
import "../../utils/SafeMath/SafeMath.sol";
-/**
- * @title ERC721 Non-Fungible Token Standard basic implementation
- * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
- * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721BasicToken.sol
- */
contract ERC721Token is
IERC721Token,
SafeMath
{
- // Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
- // which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
- bytes4 constant internal ERC721_RECEIVED = 0xf0b9e5ba;
+ // Function selector for ERC721Receiver.onERC721Received
+ // 0x150b7a02
+ bytes4 constant internal ERC721_RECEIVED = bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"));
- // Mapping from token ID to owner
- mapping (uint256 => address) internal tokenOwner;
+ // Mapping of tokenId => owner
+ mapping (uint256 => address) internal owners;
- // Mapping from token ID to approved address
- mapping (uint256 => address) internal tokenApprovals;
+ // Mapping of tokenId => approved address
+ mapping (uint256 => address) internal approvals;
- // Mapping from owner to number of owned token
- mapping (address => uint256) internal ownedTokensCount;
+ // Mapping of owner => number of tokens owned
+ mapping (address => uint256) internal balances;
- // Mapping from owner to operator approvals
+ // Mapping of owner => operator => approved
mapping (address => mapping (address => bool)) internal operatorApprovals;
- /**
- * @dev Guarantees msg.sender is owner of the given token
- * @param _tokenId uint256 ID of the token to validate its ownership belongs to msg.sender
- */
- modifier onlyOwnerOf(uint256 _tokenId) {
- require(ownerOf(_tokenId) == msg.sender);
- _;
- }
-
- /**
- * @dev Checks msg.sender can transfer a token, by being owner, approved, or operator
- * @param _tokenId uint256 ID of the token to validate
- */
- modifier canTransfer(uint256 _tokenId) {
- require(isApprovedOrOwner(msg.sender, _tokenId));
- _;
+ /// @notice Transfers the ownership of an NFT from one address to another address
+ /// @dev Throws unless `msg.sender` is the current owner, an authorized
+ /// operator, or the approved address for this NFT. Throws if `_from` is
+ /// not the current owner. Throws if `_to` is the zero address. Throws if
+ /// `_tokenId` is not a valid NFT. When transfer is complete, this function
+ /// checks if `_to` is a smart contract (code size > 0). If so, it calls
+ /// `onERC721Received` on `_to` and throws if the return value is not
+ /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ /// @param _data Additional data with no specified format, sent in call to `_to`
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId,
+ bytes _data
+ )
+ external
+ {
+ transferFrom(
+ _from,
+ _to,
+ _tokenId
+ );
+
+ uint256 receiverCodeSize;
+ assembly {
+ receiverCodeSize := extcodesize(_to)
+ }
+ if (receiverCodeSize > 0) {
+ bytes4 selector = IERC721Receiver(_to).onERC721Received(
+ msg.sender,
+ _from,
+ _tokenId,
+ _data
+ );
+ require(
+ selector == ERC721_RECEIVED,
+ "ERC721_INVALID_SELECTOR"
+ );
+ }
}
- constructor (
- string _name,
- string _symbol)
- public
+ /// @notice Transfers the ownership of an NFT from one address to another address
+ /// @dev This works identically to the other function with an extra data parameter,
+ /// except this function just sets data to "".
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
+ )
+ external
{
- name_ = _name;
- symbol_ = _symbol;
+ transferFrom(
+ _from,
+ _to,
+ _tokenId
+ );
+
+ uint256 receiverCodeSize;
+ assembly {
+ receiverCodeSize := extcodesize(_to)
+ }
+ if (receiverCodeSize > 0) {
+ bytes4 selector = IERC721Receiver(_to).onERC721Received(
+ msg.sender,
+ _from,
+ _tokenId,
+ ""
+ );
+ require(
+ selector == ERC721_RECEIVED,
+ "ERC721_INVALID_SELECTOR"
+ );
+ }
}
- /**
- * @dev Gets the token name
- * @return string representing the token name
- */
- function name()
- public
- view
- returns (string)
+ /// @notice Change or reaffirm the approved address for an NFT
+ /// @dev 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.
+ /// @param _approved The new approved NFT controller
+ /// @param _tokenId The NFT to approve
+ function approve(address _approved, uint256 _tokenId)
+ external
{
- return name_;
+ address owner = ownerOf(_tokenId);
+ require(
+ msg.sender == owner || isApprovedForAll(owner, msg.sender),
+ "ERC721_INVALID_SENDER"
+ );
+
+ approvals[_tokenId] = _approved;
+ emit Approval(
+ owner,
+ _approved,
+ _tokenId
+ );
}
- /**
- * @dev Gets the token symbol
- * @return string representing the token symbol
- */
- function symbol()
- public
- view
- returns (string)
+ /// @notice Enable or disable approval for a third party ("operator") to manage
+ /// all of `msg.sender`'s assets
+ /// @dev Emits the ApprovalForAll event. The contract MUST allow
+ /// multiple operators per owner.
+ /// @param _operator Address to add to the set of authorized operators
+ /// @param _approved True if the operator is approved, false to revoke approval
+ function setApprovalForAll(address _operator, bool _approved)
+ external
{
- return symbol_;
+ operatorApprovals[msg.sender][_operator] = _approved;
+ emit ApprovalForAll(
+ msg.sender,
+ _operator,
+ _approved
+ );
}
-
- /**
- * @dev Gets the balance of the specified address
- * @param _owner address to query the balance of
- * @return uint256 representing the amount owned by the passed address
- */
+
+ /// @notice Count all NFTs assigned to an owner
+ /// @dev NFTs assigned to the zero address are considered invalid, and this
+ /// function throws for queries about the zero address.
+ /// @param _owner An address for whom to query the balance
+ /// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner)
- public
+ external
view
returns (uint256)
{
- require(_owner != address(0));
- return ownedTokensCount[_owner];
+ require(
+ _owner != address(0),
+ "ERC721_ZERO_OWNER"
+ );
+ return balances[_owner];
}
- /**
- * @dev Gets the owner of the specified token ID
- * @param _tokenId uint256 ID of the token to query the owner of
- * @return owner address currently marked as the owner of the given token ID
- */
- function ownerOf(uint256 _tokenId)
- public
- view
- returns (address)
- {
- address owner = tokenOwner[_tokenId];
- require(owner != address(0));
- return owner;
- }
-
- /**
- * @dev Returns whether the specified token exists
- * @param _tokenId uint256 ID of the token to query the existance of
- * @return whether the token exists
- */
- function exists(uint256 _tokenId)
+ /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
+ /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+ /// THEY MAY BE PERMANENTLY LOST
+ /// @dev Throws unless `msg.sender` is the current owner, an authorized
+ /// operator, or the approved address for this NFT. Throws if `_from` is
+ /// not the current owner. Throws if `_to` is the zero address. Throws if
+ /// `_tokenId` is not a valid NFT.
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
+ )
public
- view
- returns (bool)
{
- address owner = tokenOwner[_tokenId];
- return owner != address(0);
- }
+ require(
+ _to != address(0),
+ "ERC721_ZERO_TO_ADDRESS"
+ );
- /**
- * @dev Approves another address to transfer the given token ID
- * @dev The zero address indicates there is no approved address.
- * @dev There can only be one approved address per token at a given time.
- * @dev Can only be called by the token owner or an approved operator.
- * @param _to address to be approved for the given token ID
- * @param _tokenId uint256 ID of the token to be approved
- */
- function approve(address _to, uint256 _tokenId)
- public
- {
address owner = ownerOf(_tokenId);
- require(_to != owner);
- require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
-
- if (getApproved(_tokenId) != address(0) || _to != address(0)) {
- tokenApprovals[_tokenId] = _to;
- emit Approval(owner, _to, _tokenId);
+ require(
+ _from == owner,
+ "ERC721_OWNER_MISMATCH"
+ );
+
+ address spender = msg.sender;
+ address approvedAddress = getApproved(_tokenId);
+ require(
+ spender == owner ||
+ isApprovedForAll(owner, spender) ||
+ approvedAddress == spender,
+ "ERC721_INVALID_SPENDER"
+ );
+
+ if (approvedAddress != address(0)) {
+ approvals[_tokenId] = address(0);
}
+
+ owners[_tokenId] = _to;
+ balances[_from] = safeSub(balances[_from], 1);
+ balances[_to] = safeAdd(balances[_to], 1);
+
+ emit Transfer(
+ _from,
+ _to,
+ _tokenId
+ );
}
- /**
- * @dev Gets the approved address for a token ID, or zero if no address set
- * @param _tokenId uint256 ID of the token to query the approval of
- * @return address currently approved for a the given token ID
- */
- function getApproved(uint256 _tokenId)
+ /// @notice Find the owner of an NFT
+ /// @dev NFTs assigned to zero address are considered invalid, and queries
+ /// about them do throw.
+ /// @param _tokenId The identifier for an NFT
+ /// @return The address of the owner of the NFT
+ function ownerOf(uint256 _tokenId)
public
view
returns (address)
{
- return tokenApprovals[_tokenId];
+ address owner = owners[_tokenId];
+ require(
+ owner != address(0),
+ "ERC721_ZERO_OWNER"
+ );
+ return owner;
}
- /**
- * @dev Sets or unsets the approval of a given operator
- * @dev An operator is allowed to transfer all tokens of the sender on their behalf
- * @param _to operator address to set the approval
- * @param _approved representing the status of the approval to be set
- */
- function setApprovalForAll(address _to, bool _approved)
+ /// @notice Get the approved address for a single NFT
+ /// @dev Throws if `_tokenId` is not a valid NFT.
+ /// @param _tokenId The NFT to find the approved address for
+ /// @return The approved address for this NFT, or the zero address if there is none
+ function getApproved(uint256 _tokenId)
public
+ view
+ returns (address)
{
- require(_to != msg.sender);
- operatorApprovals[msg.sender][_to] = _approved;
- emit ApprovalForAll(msg.sender, _to, _approved);
+ return approvals[_tokenId];
}
- /**
- * @dev Tells whether an operator is approved by a given owner
- * @param _owner owner address which you want to query the approval of
- * @param _operator operator address which you want to query the approval of
- * @return bool whether the given operator is approved by the given owner
- */
+ /// @notice Query if an address is an authorized operator for another address
+ /// @param _owner The address that owns the NFTs
+ /// @param _operator The address that acts on behalf of the owner
+ /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator)
public
view
@@ -210,198 +274,4 @@ contract ERC721Token is
{
return operatorApprovals[_owner][_operator];
}
-
- /**
- * @dev Transfers the ownership of a given token ID to another address
- * @dev Usage of this method is discouraged, use `safeTransferFrom` whenever possible
- * @dev Requires the msg sender to be the owner, approved, or operator
- * @param _from current owner of the token
- * @param _to address to receive the ownership of the given token ID
- * @param _tokenId uint256 ID of the token to be transferred
- */
- function transferFrom(address _from, address _to, uint256 _tokenId)
- public
- canTransfer(_tokenId)
- {
- require(_from != address(0));
- require(_to != address(0));
-
- clearApproval(_from, _tokenId);
- removeTokenFrom(_from, _tokenId);
- addTokenTo(_to, _tokenId);
-
- emit Transfer(_from, _to, _tokenId);
- }
-
- /**
- * @dev Safely transfers the ownership of a given token ID to another address
- * @dev If the target address is a contract, it must implement `onERC721Received`,
- * which is called upon a safe transfer, and return the magic value
- * `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; otherwise,
- * the transfer is reverted.
- * @dev Requires the msg sender to be the owner, approved, or operator
- * @param _from current owner of the token
- * @param _to address to receive the ownership of the given token ID
- * @param _tokenId uint256 ID of the token to be transferred
- */
- function safeTransferFrom(
- address _from,
- address _to,
- uint256 _tokenId)
- public
- canTransfer(_tokenId)
- {
- // solium-disable-next-line arg-overflow
- safeTransferFrom(_from, _to, _tokenId, "");
- }
-
- /**
- * @dev Safely transfers the ownership of a given token ID to another address
- * @dev If the target address is a contract, it must implement `onERC721Received`,
- * which is called upon a safe transfer, and return the magic value
- * `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`; otherwise,
- * the transfer is reverted.
- * @dev Requires the msg sender to be the owner, approved, or operator
- * @param _from current owner of the token
- * @param _to address to receive the ownership of the given token ID
- * @param _tokenId uint256 ID of the token to be transferred
- * @param _data bytes data to send along with a safe transfer check
- */
- function safeTransferFrom(
- address _from,
- address _to,
- uint256 _tokenId,
- bytes _data)
- public
- canTransfer(_tokenId)
- {
- transferFrom(_from, _to, _tokenId);
- // solium-disable-next-line arg-overflow
- require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data));
- }
-
- /**
- * @dev Returns whether the given spender can transfer a given token ID
- * @param _spender address of the spender to query
- * @param _tokenId uint256 ID of the token to be transferred
- * @return bool whether the msg.sender is approved for the given token ID,
- * is an operator of the owner, or is the owner of the token
- */
- function isApprovedOrOwner(address _spender, uint256 _tokenId)
- internal
- view
- returns (bool)
- {
- address owner = ownerOf(_tokenId);
- return _spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender);
- }
-
- /**
- * @dev Internal function to mint a new token
- * @dev Reverts if the given token ID already exists
- * @param _to The address that will own the minted token
- * @param _tokenId uint256 ID of the token to be minted by the msg.sender
- */
- function _mint(address _to, uint256 _tokenId)
- internal
- {
- require(_to != address(0));
- addTokenTo(_to, _tokenId);
- emit Transfer(address(0), _to, _tokenId);
- }
-
- /**
- * @dev Internal function to burn a specific token
- * @dev Reverts if the token does not exist
- * @param _tokenId uint256 ID of the token being burned by the msg.sender
- */
- function _burn(address _owner, uint256 _tokenId)
- internal
- {
- clearApproval(_owner, _tokenId);
- removeTokenFrom(_owner, _tokenId);
- emit Transfer(_owner, address(0), _tokenId);
- }
-
- /**
- * @dev Internal function to clear current approval of a given token ID
- * @dev Reverts if the given address is not indeed the owner of the token
- * @param _owner owner of the token
- * @param _tokenId uint256 ID of the token to be transferred
- */
- function clearApproval(address _owner, uint256 _tokenId)
- internal
- {
- require(ownerOf(_tokenId) == _owner);
- if (tokenApprovals[_tokenId] != address(0)) {
- tokenApprovals[_tokenId] = address(0);
- emit Approval(_owner, address(0), _tokenId);
- }
- }
-
- /**
- * @dev Internal function to add a token ID to the list of a given address
- * @param _to address representing the new owner of the given token ID
- * @param _tokenId uint256 ID of the token to be added to the tokens list of the given address
- */
- function addTokenTo(address _to, uint256 _tokenId)
- internal
- {
- require(tokenOwner[_tokenId] == address(0));
- tokenOwner[_tokenId] = _to;
- ownedTokensCount[_to] = safeAdd(ownedTokensCount[_to], 1);
- }
-
- /**
- * @dev Internal function to remove a token ID from the list of a given address
- * @param _from address representing the previous owner of the given token ID
- * @param _tokenId uint256 ID of the token to be removed from the tokens list of the given address
- */
- function removeTokenFrom(address _from, uint256 _tokenId)
- internal
- {
- require(ownerOf(_tokenId) == _from);
- ownedTokensCount[_from] = safeSub(ownedTokensCount[_from], 1);
- tokenOwner[_tokenId] = address(0);
- }
-
- /**
- * @dev Internal function to invoke `onERC721Received` on a target address
- * @dev The call is not executed if the target address is not a contract
- * @param _from address representing the previous owner of the given token ID
- * @param _to target address that will receive the tokens
- * @param _tokenId uint256 ID of the token to be transferred
- * @param _data bytes optional data to send along with the call
- * @return whether the call correctly returned the expected magic value
- */
- function checkAndCallSafeTransfer(
- address _from,
- address _to,
- uint256 _tokenId,
- bytes _data)
- internal
- returns (bool)
- {
- if (!isContract(_to)) {
- return true;
- }
- bytes4 retval = IERC721Receiver(_to).onERC721Received(_from, _tokenId, _data);
- return (retval == ERC721_RECEIVED);
- }
-
- function isContract(address addr)
- internal
- view
- returns (bool)
- {
- uint256 size;
- // XXX Currently there is no better way to check if there is a contract in an address
- // than to check the size of the code at that address.
- // See https://ethereum.stackexchange.com/a/14016/36603
- // for more details about how this works.
- // TODO Check this again before the Serenity release, because all addresses will be
- // contracts then.
- assembly { size := extcodesize(addr) } // solium-disable-line security/no-inline-assembly
- return size > 0;
- }
}
diff --git a/packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Receiver.sol b/packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Receiver.sol
index f2e8f3c88..8e0e32ab2 100644
--- a/packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Receiver.sol
+++ b/packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Receiver.sol
@@ -1,61 +1,44 @@
/*
-The MIT License (MIT)
-
-Copyright (c) 2016 Smart Contract Solutions, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
*/
pragma solidity 0.4.24;
-/**
- * @title ERC721 token receiver interface
- * @dev Interface for any contract that wants to support safeTransfers
- * rom ERC721 asset contracts.
- * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721Receiver.sol
- */
contract IERC721Receiver {
- /**
- * @dev Magic value to be returned upon successful reception of an NFT
- * Equals to `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`,
- * which can be also obtained as `ERC721Receiver(0).onERC721Received.selector`
- */
- bytes4 constant internal ERC721_RECEIVED = 0xf0b9e5ba;
-
- /**
- * @notice Handle the receipt of an NFT
- * @dev The ERC721 smart contract calls this function on the recipient
- * after a `safetransfer`. This function MAY throw to revert and reject the
- * transfer. This function MUST use 50,000 gas or less. Return of other
- * than the magic value MUST result in the transaction being reverted.
- * Note: the contract address is always the message sender.
- * @param _from The sending address
- * @param _tokenId The NFT identifier which is being transfered
- * @param _data Additional data with no specified format
- * @return `bytes4(keccak256("onERC721Received(address,uint256,bytes)"))`
- */
+
+ /// @notice Handle the receipt of an NFT
+ /// @dev The ERC721 smart contract calls this function on the recipient
+ /// after a `transfer`. This function MAY throw to revert and reject the
+ /// transfer. Return of other than the magic value MUST result in the
+ /// transaction being reverted.
+ /// Note: the contract address is always the message sender.
+ /// @param _operator The address which called `safeTransferFrom` function
+ /// @param _from The address which previously owned the token
+ /// @param _tokenId The NFT identifier which is being transferred
+ /// @param _data Additional data with no specified format
+ /// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
+ /// unless throwing
function onERC721Received(
+ address _operator,
address _from,
uint256 _tokenId,
- bytes _data)
- public
+ bytes _data
+ )
+ external
returns (bytes4);
}
diff --git a/packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Token.sol b/packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Token.sol
index 4d57ece38..ac992c80d 100644
--- a/packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Token.sol
+++ b/packages/contracts/src/2.0.0/tokens/ERC721Token/IERC721Token.sol
@@ -1,118 +1,158 @@
/*
-The MIT License (MIT)
-
-Copyright (c) 2016 Smart Contract Solutions, Inc.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
*/
pragma solidity 0.4.24;
-/**
- * @title ERC721 Non-Fungible Token Standard basic interface
- * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
- * Modified from https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC721/ERC721Basic.sol
- */
contract IERC721Token {
- string internal name_;
- string internal symbol_;
+ /// @dev This emits when ownership of any NFT changes by any mechanism.
+ /// This event emits when NFTs are created (`from` == 0) and destroyed
+ /// (`to` == 0). Exception: during contract creation, any number of NFTs
+ /// may be created and assigned without emitting Transfer. At the time of
+ /// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(
address indexed _from,
address indexed _to,
- uint256 _tokenId
+ uint256 indexed _tokenId
);
+ /// @dev This emits when the approved address for an NFT is changed or
+ /// reaffirmed. The zero address indicates there is no approved address.
+ /// When a Transfer event emits, this also indicates that the approved
+ /// address for that NFT (if any) is reset to none.
event Approval(
address indexed _owner,
address indexed _approved,
- uint256 _tokenId
+ uint256 indexed _tokenId
);
+ /// @dev This emits when an operator is enabled or disabled for an owner.
+ /// The operator can manage all NFTs of the owner.
event ApprovalForAll(
address indexed _owner,
address indexed _operator,
bool _approved
);
- function name()
- public
- view
- returns (string);
-
- function symbol()
- public
- view
- returns (string);
+ /// @notice Transfers the ownership of an NFT from one address to another address
+ /// @dev Throws unless `msg.sender` is the current owner, an authorized
+ /// perator, or the approved address for this NFT. Throws if `_from` is
+ /// not the current owner. Throws if `_to` is the zero address. Throws if
+ /// `_tokenId` is not a valid NFT. When transfer is complete, this function
+ /// checks if `_to` is a smart contract (code size > 0). If so, it calls
+ /// `onERC721Received` on `_to` and throws if the return value is not
+ /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ /// @param _data Additional data with no specified format, sent in call to `_to`
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId,
+ bytes _data
+ )
+ external;
+
+ /// @notice Transfers the ownership of an NFT from one address to another address
+ /// @dev This works identically to the other function with an extra data parameter,
+ /// except this function just sets data to "".
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ function safeTransferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
+ )
+ external;
+
+ /// @notice Change or reaffirm the approved address for an NFT
+ /// @dev 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.
+ /// @param _approved The new approved NFT controller
+ /// @param _tokenId The NFT to approve
+ function approve(address _approved, uint256 _tokenId)
+ external;
+
+ /// @notice Enable or disable approval for a third party ("operator") to manage
+ /// all of `msg.sender`'s assets
+ /// @dev Emits the ApprovalForAll event. The contract MUST allow
+ /// multiple operators per owner.
+ /// @param _operator Address to add to the set of authorized operators
+ /// @param _approved True if the operator is approved, false to revoke approval
+ function setApprovalForAll(address _operator, bool _approved)
+ external;
+ /// @notice Count all NFTs assigned to an owner
+ /// @dev NFTs assigned to the zero address are considered invalid, and this
+ /// function throws for queries about the zero address.
+ /// @param _owner An address for whom to query the balance
+ /// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner)
- public
+ external
view
- returns (uint256 _balance);
+ returns (uint256);
+
+ /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
+ /// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
+ /// THEY MAY BE PERMANENTLY LOST
+ /// @dev Throws unless `msg.sender` is the current owner, an authorized
+ /// operator, or the approved address for this NFT. Throws if `_from` is
+ /// not the current owner. Throws if `_to` is the zero address. Throws if
+ /// `_tokenId` is not a valid NFT.
+ /// @param _from The current owner of the NFT
+ /// @param _to The new owner
+ /// @param _tokenId The NFT to transfer
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _tokenId
+ )
+ public;
+ /// @notice Find the owner of an NFT
+ /// @dev NFTs assigned to zero address are considered invalid, and queries
+ /// about them do throw.
+ /// @param _tokenId The identifier for an NFT
+ /// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId)
public
view
- returns (address _owner);
+ returns (address);
- function exists(uint256 _tokenId)
+ /// @notice Get the approved address for a single NFT
+ /// @dev Throws if `_tokenId` is not a valid NFT.
+ /// @param _tokenId The NFT to find the approved address for
+ /// @return The approved address for this NFT, or the zero address if there is none
+ function getApproved(uint256 _tokenId)
public
view
- returns (bool _exists);
-
- function approve(address _to, uint256 _tokenId)
- public;
-
- function getApproved(uint256 _tokenId)
- public
- view
- returns (address _operator);
-
- function setApprovalForAll(address _operator, bool _approved)
- public;
-
+ returns (address);
+
+ /// @notice Query if an address is an authorized operator for another address
+ /// @param _owner The address that owns the NFTs
+ /// @param _operator The address that acts on behalf of the owner
+ /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator)
public
view
returns (bool);
-
- function transferFrom(
- address _from,
- address _to,
- uint256 _tokenId
- )
- public;
-
- function safeTransferFrom(
- address _from,
- address _to,
- uint256 _tokenId
- )
- public;
-
- function safeTransferFrom(
- address _from,
- address _to,
- uint256 _tokenId,
- bytes _data
- )
- public;
}
diff --git a/packages/contracts/src/2.0.0/tokens/ERC721Token/MintableERC721Token.sol b/packages/contracts/src/2.0.0/tokens/ERC721Token/MintableERC721Token.sol
new file mode 100644
index 000000000..85d192779
--- /dev/null
+++ b/packages/contracts/src/2.0.0/tokens/ERC721Token/MintableERC721Token.sol
@@ -0,0 +1,83 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity 0.4.24;
+
+import "./ERC721Token.sol";
+
+
+contract MintableERC721Token is
+ ERC721Token
+{
+
+ /// @dev Function to mint a new token
+ /// Reverts if the given token ID already exists
+ /// @param _to Address of the beneficiary that will own the minted token
+ /// @param _tokenId ID of the token to be minted by the msg.sender
+ function _mint(address _to, uint256 _tokenId)
+ internal
+ {
+ require(
+ _to != address(0),
+ "ERC721_ZERO_TO_ADDRESS"
+ );
+
+ address owner = owners[_tokenId];
+ require(
+ owner == address(0),
+ "ERC721_OWNER_ALREADY_EXISTS"
+ );
+
+ owners[_tokenId] = _to;
+ balances[_to] = safeAdd(balances[_to], 1);
+
+ emit Transfer(
+ address(0),
+ _to,
+ _tokenId
+ );
+ }
+
+ /// @dev Function to burn a token
+ /// Reverts if the given token ID doesn't exist
+ /// @param _owner Owner of token with given token ID
+ /// @param _tokenId ID of the token to be burned by the msg.sender
+ function _burn(address _owner, uint256 _tokenId)
+ internal
+ {
+ require(
+ _owner != address(0),
+ "ERC721_ZERO_OWNER_ADDRESS"
+ );
+
+ address owner = owners[_tokenId];
+ require(
+ owner == _owner,
+ "ERC721_OWNER_MISMATCH"
+ );
+
+ owners[_tokenId] = address(0);
+ balances[_owner] = safeSub(balances[_owner], 1);
+
+ emit Transfer(
+ _owner,
+ address(0),
+ _tokenId
+ );
+ }
+}
diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts
index 62215f08d..76a020222 100644
--- a/packages/contracts/test/asset_proxy/proxies.ts
+++ b/packages/contracts/test/asset_proxy/proxies.ts
@@ -261,7 +261,7 @@ describe('Asset Transfer Proxies', () => {
erc721Receiver.address,
amount,
);
- const logDecoder = new LogDecoder(web3Wrapper, erc721Receiver.address);
+ const logDecoder = new LogDecoder(web3Wrapper);
const tx = await logDecoder.getTxWithDecodedLogsAsync(
await web3Wrapper.sendTransactionAsync({
to: erc721Proxy.address,
@@ -271,7 +271,7 @@ describe('Asset Transfer Proxies', () => {
}),
);
// Verify that no log was emitted by erc721 receiver
- expect(tx.logs.length).to.be.equal(0);
+ expect(tx.logs.length).to.be.equal(1);
// Verify transfer was successful
const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(newOwnerMakerAsset).to.be.bignumber.equal(erc721Receiver.address);
diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts
index b324988cc..bc2bad749 100644
--- a/packages/contracts/test/exchange/core.ts
+++ b/packages/contracts/test/exchange/core.ts
@@ -10,6 +10,7 @@ import * as _ from 'lodash';
import { DummyERC20TokenContract } from '../../generated_contract_wrappers/dummy_erc20_token';
import { DummyERC721TokenContract } from '../../generated_contract_wrappers/dummy_erc721_token';
+import { DummyNoReturnERC20TokenContract } from '../../generated_contract_wrappers/dummy_no_return_erc20_token';
import { ERC20ProxyContract } from '../../generated_contract_wrappers/erc20_proxy';
import { ERC721ProxyContract } from '../../generated_contract_wrappers/erc721_proxy';
import { ExchangeCancelEventArgs, ExchangeContract } from '../../generated_contract_wrappers/exchange';
@@ -39,6 +40,7 @@ describe('Exchange core', () => {
let erc20TokenB: DummyERC20TokenContract;
let zrxToken: DummyERC20TokenContract;
let erc721Token: DummyERC721TokenContract;
+ let noReturnErc20Token: DummyNoReturnERC20TokenContract;
let exchange: ExchangeContract;
let erc20Proxy: ERC20ProxyContract;
let erc721Proxy: ERC721ProxyContract;
@@ -161,6 +163,137 @@ describe('Exchange core', () => {
});
});
+ describe('Testing exchange of ERC20 tokens with no return values', () => {
+ before(async () => {
+ noReturnErc20Token = await DummyNoReturnERC20TokenContract.deployFrom0xArtifactAsync(
+ artifacts.DummyNoReturnERC20Token,
+ provider,
+ txDefaults,
+ constants.DUMMY_TOKEN_NAME,
+ constants.DUMMY_TOKEN_SYMBOL,
+ constants.DUMMY_TOKEN_DECIMALS,
+ constants.DUMMY_TOKEN_TOTAL_SUPPLY,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await noReturnErc20Token.setBalance.sendTransactionAsync(makerAddress, constants.INITIAL_ERC20_BALANCE),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await noReturnErc20Token.approve.sendTransactionAsync(
+ erc20Proxy.address,
+ constants.INITIAL_ERC20_ALLOWANCE,
+ { from: makerAddress },
+ ),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ });
+ it('should transfer the correct amounts when makerAssetAmount === takerAssetAmount', async () => {
+ signedOrder = await orderFactory.newSignedOrderAsync({
+ makerAssetData: assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ });
+
+ const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
+ const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
+ const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+ const initialFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress);
+
+ await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
+
+ const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
+ const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
+ const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+ const finalFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress);
+
+ expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount));
+ expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount));
+ expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount));
+ expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount));
+ expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.minus(signedOrder.makerFee));
+ expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(signedOrder.takerFee));
+ expect(finalFeeRecipientZrxBalance).to.be.bignumber.equal(
+ initialFeeRecipientZrxBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)),
+ );
+ });
+ it('should transfer the correct amounts when makerAssetAmount > takerAssetAmount', async () => {
+ signedOrder = await orderFactory.newSignedOrderAsync({
+ makerAssetData: assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ });
+
+ const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
+ const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
+ const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+ const initialFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress);
+
+ await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
+
+ const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
+ const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
+ const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+ const finalFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress);
+
+ expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount));
+ expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount));
+ expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount));
+ expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount));
+ expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.minus(signedOrder.makerFee));
+ expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(signedOrder.takerFee));
+ expect(finalFeeRecipientZrxBalance).to.be.bignumber.equal(
+ initialFeeRecipientZrxBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)),
+ );
+ });
+ it('should transfer the correct amounts when makerAssetAmount < takerAssetAmount', async () => {
+ signedOrder = await orderFactory.newSignedOrderAsync({
+ makerAssetData: assetDataUtils.encodeERC20AssetData(noReturnErc20Token.address),
+ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18),
+ takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(200), 18),
+ });
+
+ const initialMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
+ const initialMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const initialMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const initialTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
+ const initialTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const initialTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+ const initialFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress);
+
+ await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
+
+ const finalMakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(makerAddress);
+ const finalMakerBalanceB = await erc20TokenB.balanceOf.callAsync(makerAddress);
+ const finalMakerZrxBalance = await zrxToken.balanceOf.callAsync(makerAddress);
+ const finalTakerBalanceA = await noReturnErc20Token.balanceOf.callAsync(takerAddress);
+ const finalTakerBalanceB = await erc20TokenB.balanceOf.callAsync(takerAddress);
+ const finalTakerZrxBalance = await zrxToken.balanceOf.callAsync(takerAddress);
+ const finalFeeRecipientZrxBalance = await zrxToken.balanceOf.callAsync(feeRecipientAddress);
+
+ expect(finalMakerBalanceA).to.be.bignumber.equal(initialMakerBalanceA.minus(signedOrder.makerAssetAmount));
+ expect(finalMakerBalanceB).to.be.bignumber.equal(initialMakerBalanceB.plus(signedOrder.takerAssetAmount));
+ expect(finalTakerBalanceA).to.be.bignumber.equal(initialTakerBalanceA.plus(signedOrder.makerAssetAmount));
+ expect(finalTakerBalanceB).to.be.bignumber.equal(initialTakerBalanceB.minus(signedOrder.takerAssetAmount));
+ expect(finalMakerZrxBalance).to.be.bignumber.equal(initialMakerZrxBalance.minus(signedOrder.makerFee));
+ expect(finalTakerZrxBalance).to.be.bignumber.equal(initialTakerZrxBalance.minus(signedOrder.takerFee));
+ expect(finalFeeRecipientZrxBalance).to.be.bignumber.equal(
+ initialFeeRecipientZrxBalance.plus(signedOrder.makerFee.plus(signedOrder.takerFee)),
+ );
+ });
+ });
+
describe('cancelOrder', () => {
beforeEach(async () => {
erc20Balances = await erc20Wrapper.getBalancesAsync();
diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts
index 81d142ca4..81871a680 100644
--- a/packages/contracts/test/exchange/dispatcher.ts
+++ b/packages/contracts/test/exchange/dispatcher.ts
@@ -145,7 +145,7 @@ describe('AssetProxyDispatcher', () => {
});
it('should log an event with correct arguments when an asset proxy is registered', async () => {
- const logDecoder = new LogDecoder(web3Wrapper, assetProxyDispatcher.address);
+ const logDecoder = new LogDecoder(web3Wrapper);
const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync(erc20Proxy.address, { from: owner }),
);
diff --git a/packages/contracts/test/exchange/fill_order.ts b/packages/contracts/test/exchange/fill_order.ts
index e79e2239e..b1e08324f 100644
--- a/packages/contracts/test/exchange/fill_order.ts
+++ b/packages/contracts/test/exchange/fill_order.ts
@@ -231,7 +231,7 @@ describe('FillOrder Tests', () => {
});
});
- describe('Testing Exchange of ERC721 Tokens', () => {
+ describe('Testing exchange of ERC721 Tokens', () => {
it('should successfully exchange a single token between the maker and taker (via fillOrder)', async () => {
const fillScenario = {
...defaultFillScenario,
diff --git a/packages/contracts/test/exchange/signature_validator.ts b/packages/contracts/test/exchange/signature_validator.ts
index 56a06c247..62aba45b8 100644
--- a/packages/contracts/test/exchange/signature_validator.ts
+++ b/packages/contracts/test/exchange/signature_validator.ts
@@ -65,7 +65,7 @@ describe('MixinSignatureValidator', () => {
txDefaults,
signerAddress,
);
- signatureValidatorLogDecoder = new LogDecoder(web3Wrapper, signatureValidator.address);
+ signatureValidatorLogDecoder = new LogDecoder(web3Wrapper);
await web3Wrapper.awaitTransactionSuccessAsync(
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(testValidator.address, true, {
from: signerAddress,
diff --git a/packages/contracts/test/multisig/asset_proxy_owner.ts b/packages/contracts/test/multisig/asset_proxy_owner.ts
index 6b98605d3..9515941ff 100644
--- a/packages/contracts/test/multisig/asset_proxy_owner.ts
+++ b/packages/contracts/test/multisig/asset_proxy_owner.ts
@@ -422,7 +422,7 @@ describe('AssetProxyOwner', () => {
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, owners[0]);
- const execLog = execRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
+ const execLog = execRes.logs[1] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
const tx = await testAssetProxyOwner.transactions.callAsync(txId);
@@ -449,7 +449,7 @@ describe('AssetProxyOwner', () => {
await multiSigWrapper.confirmTransactionAsync(txId, owners[1]);
const execRes = await multiSigWrapper.executeRemoveAuthorizedAddressAtIndexAsync(txId, owners[0]);
- const execLog = execRes.logs[0] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
+ const execLog = execRes.logs[1] as LogWithDecodedArgs<AssetProxyOwnerExecutionEventArgs>;
expect(execLog.args.transactionId).to.be.bignumber.equal(txId);
const tx = await testAssetProxyOwner.transactions.callAsync(txId);
diff --git a/packages/contracts/test/tokens/erc721_token.ts b/packages/contracts/test/tokens/erc721_token.ts
new file mode 100644
index 000000000..e61fd7d51
--- /dev/null
+++ b/packages/contracts/test/tokens/erc721_token.ts
@@ -0,0 +1,279 @@
+import { BlockchainLifecycle } from '@0xproject/dev-utils';
+import { RevertReason } from '@0xproject/types';
+import { BigNumber } from '@0xproject/utils';
+import * as chai from 'chai';
+import { LogWithDecodedArgs } from 'ethereum-types';
+
+import {
+ DummyERC721ReceiverContract,
+ DummyERC721ReceiverTokenReceivedEventArgs,
+} from '../../generated_contract_wrappers/dummy_erc721_receiver';
+import {
+ DummyERC721TokenContract,
+ DummyERC721TokenTransferEventArgs,
+} from '../../generated_contract_wrappers/dummy_erc721_token';
+import { InvalidERC721ReceiverContract } from '../../generated_contract_wrappers/invalid_erc721_receiver';
+import { artifacts } from '../utils/artifacts';
+import { expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync } from '../utils/assertions';
+import { chaiSetup } from '../utils/chai_setup';
+import { constants } from '../utils/constants';
+import { LogDecoder } from '../utils/log_decoder';
+import { provider, txDefaults, web3Wrapper } from '../utils/web3_wrapper';
+
+chaiSetup.configure();
+const expect = chai.expect;
+const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+// tslint:disable:no-unnecessary-type-assertion
+describe('ERC721Token', () => {
+ let owner: string;
+ let spender: string;
+ let token: DummyERC721TokenContract;
+ let erc721Receiver: DummyERC721ReceiverContract;
+ let logDecoder: LogDecoder;
+ const tokenId = new BigNumber(1);
+ before(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ after(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+ before(async () => {
+ const accounts = await web3Wrapper.getAvailableAddressesAsync();
+ owner = accounts[0];
+ spender = accounts[1];
+ token = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
+ artifacts.DummyERC721Token,
+ provider,
+ txDefaults,
+ constants.DUMMY_TOKEN_NAME,
+ constants.DUMMY_TOKEN_SYMBOL,
+ );
+ erc721Receiver = await DummyERC721ReceiverContract.deployFrom0xArtifactAsync(
+ artifacts.DummyERC721Receiver,
+ provider,
+ txDefaults,
+ );
+ logDecoder = new LogDecoder(web3Wrapper);
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await token.mint.sendTransactionAsync(owner, tokenId, { from: owner }),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+ });
+ beforeEach(async () => {
+ await blockchainLifecycle.startAsync();
+ });
+ afterEach(async () => {
+ await blockchainLifecycle.revertAsync();
+ });
+
+ describe('transferFrom', () => {
+ it('should revert if the tokenId is not owner', async () => {
+ const from = owner;
+ const to = erc721Receiver.address;
+ const unownedTokenId = new BigNumber(2);
+ await expectTransactionFailedAsync(
+ token.transferFrom.sendTransactionAsync(from, to, unownedTokenId),
+ RevertReason.Erc721ZeroOwner,
+ );
+ });
+ it('should revert if transferring to a null address', async () => {
+ const from = owner;
+ const to = constants.NULL_ADDRESS;
+ await expectTransactionFailedAsync(
+ token.transferFrom.sendTransactionAsync(from, to, tokenId),
+ RevertReason.Erc721ZeroToAddress,
+ );
+ });
+ it('should revert if the from address does not own the token', async () => {
+ const from = spender;
+ const to = erc721Receiver.address;
+ await expectTransactionFailedAsync(
+ token.transferFrom.sendTransactionAsync(from, to, tokenId),
+ RevertReason.Erc721OwnerMismatch,
+ );
+ });
+ it('should revert if spender does not own the token, is not approved, and is not approved for all', async () => {
+ const from = owner;
+ const to = erc721Receiver.address;
+ await expectTransactionFailedAsync(
+ token.transferFrom.sendTransactionAsync(from, to, tokenId, { from: spender }),
+ RevertReason.Erc721InvalidSpender,
+ );
+ });
+ it('should transfer the token if called by owner', async () => {
+ const from = owner;
+ const to = erc721Receiver.address;
+ const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
+ await token.transferFrom.sendTransactionAsync(from, to, tokenId),
+ );
+ const newOwner = await token.ownerOf.callAsync(tokenId);
+ expect(newOwner).to.be.equal(to);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
+ expect(log.args._from).to.be.equal(from);
+ expect(log.args._to).to.be.equal(to);
+ expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
+ });
+ it('should transfer the token if spender is approved for all', async () => {
+ const isApproved = true;
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await token.setApprovalForAll.sendTransactionAsync(spender, isApproved),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ const from = owner;
+ const to = erc721Receiver.address;
+ const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
+ await token.transferFrom.sendTransactionAsync(from, to, tokenId),
+ );
+ const newOwner = await token.ownerOf.callAsync(tokenId);
+ expect(newOwner).to.be.equal(to);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
+ expect(log.args._from).to.be.equal(from);
+ expect(log.args._to).to.be.equal(to);
+ expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
+ });
+ it('should transfer the token if spender is individually approved', async () => {
+ await web3Wrapper.awaitTransactionSuccessAsync(
+ await token.approve.sendTransactionAsync(spender, tokenId),
+ constants.AWAIT_TRANSACTION_MINED_MS,
+ );
+
+ const from = owner;
+ const to = erc721Receiver.address;
+ const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
+ await token.transferFrom.sendTransactionAsync(from, to, tokenId),
+ );
+ const newOwner = await token.ownerOf.callAsync(tokenId);
+ expect(newOwner).to.be.equal(to);
+
+ const approvedAddress = await token.getApproved.callAsync(tokenId);
+ expect(approvedAddress).to.be.equal(constants.NULL_ADDRESS);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
+ expect(log.args._from).to.be.equal(from);
+ expect(log.args._to).to.be.equal(to);
+ expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
+ });
+ });
+ describe('safeTransferFrom without data', () => {
+ it('should transfer token to a non-contract address if called by owner', async () => {
+ const from = owner;
+ const to = spender;
+ const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
+ await token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId),
+ );
+ const newOwner = await token.ownerOf.callAsync(tokenId);
+ expect(newOwner).to.be.equal(to);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
+ expect(log.args._from).to.be.equal(from);
+ expect(log.args._to).to.be.equal(to);
+ expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
+ });
+ it('should revert if transferring to a contract address without onERC721Received', async () => {
+ const contract = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
+ artifacts.DummyERC721Token,
+ provider,
+ txDefaults,
+ constants.DUMMY_TOKEN_NAME,
+ constants.DUMMY_TOKEN_SYMBOL,
+ );
+ const from = owner;
+ const to = contract.address;
+ await expectTransactionFailedWithoutReasonAsync(
+ token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId),
+ );
+ });
+ it('should revert if onERC721Received does not return the correct value', async () => {
+ const invalidErc721Receiver = await InvalidERC721ReceiverContract.deployFrom0xArtifactAsync(
+ artifacts.InvalidERC721Receiver,
+ provider,
+ txDefaults,
+ );
+ const from = owner;
+ const to = invalidErc721Receiver.address;
+ await expectTransactionFailedAsync(
+ token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId),
+ RevertReason.Erc721InvalidSelector,
+ );
+ });
+ it('should transfer to contract and call onERC721Received with correct return value', async () => {
+ const from = owner;
+ const to = erc721Receiver.address;
+ const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
+ await token.safeTransferFrom1.sendTransactionAsync(from, to, tokenId),
+ );
+ const newOwner = await token.ownerOf.callAsync(tokenId);
+ expect(newOwner).to.be.equal(to);
+ const transferLog = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
+ const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<DummyERC721ReceiverTokenReceivedEventArgs>;
+ expect(transferLog.args._from).to.be.equal(from);
+ expect(transferLog.args._to).to.be.equal(to);
+ expect(transferLog.args._tokenId).to.be.bignumber.equal(tokenId);
+ expect(receiverLog.args.operator).to.be.equal(owner);
+ expect(receiverLog.args.from).to.be.equal(from);
+ expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenId);
+ expect(receiverLog.args.data).to.be.equal(constants.NULL_BYTES);
+ });
+ });
+ describe('safeTransferFrom with data', () => {
+ const data = '0x0102030405060708090a0b0c0d0e0f';
+ it('should transfer token to a non-contract address if called by owner', async () => {
+ const from = owner;
+ const to = spender;
+ const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
+ await token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data),
+ );
+ const newOwner = await token.ownerOf.callAsync(tokenId);
+ expect(newOwner).to.be.equal(to);
+ const log = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
+ expect(log.args._from).to.be.equal(from);
+ expect(log.args._to).to.be.equal(to);
+ expect(log.args._tokenId).to.be.bignumber.equal(tokenId);
+ });
+ it('should revert if transferring to a contract address without onERC721Received', async () => {
+ const contract = await DummyERC721TokenContract.deployFrom0xArtifactAsync(
+ artifacts.DummyERC721Token,
+ provider,
+ txDefaults,
+ constants.DUMMY_TOKEN_NAME,
+ constants.DUMMY_TOKEN_SYMBOL,
+ );
+ const from = owner;
+ const to = contract.address;
+ await expectTransactionFailedWithoutReasonAsync(
+ token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data),
+ );
+ });
+ it('should revert if onERC721Received does not return the correct value', async () => {
+ const invalidErc721Receiver = await InvalidERC721ReceiverContract.deployFrom0xArtifactAsync(
+ artifacts.InvalidERC721Receiver,
+ provider,
+ txDefaults,
+ );
+ const from = owner;
+ const to = invalidErc721Receiver.address;
+ await expectTransactionFailedAsync(
+ token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data),
+ RevertReason.Erc721InvalidSelector,
+ );
+ });
+ it('should transfer to contract and call onERC721Received with correct return value', async () => {
+ const from = owner;
+ const to = erc721Receiver.address;
+ const txReceipt = await logDecoder.getTxWithDecodedLogsAsync(
+ await token.safeTransferFrom2.sendTransactionAsync(from, to, tokenId, data),
+ );
+ const newOwner = await token.ownerOf.callAsync(tokenId);
+ expect(newOwner).to.be.equal(to);
+ const transferLog = txReceipt.logs[0] as LogWithDecodedArgs<DummyERC721TokenTransferEventArgs>;
+ const receiverLog = txReceipt.logs[1] as LogWithDecodedArgs<DummyERC721ReceiverTokenReceivedEventArgs>;
+ expect(transferLog.args._from).to.be.equal(from);
+ expect(transferLog.args._to).to.be.equal(to);
+ expect(transferLog.args._tokenId).to.be.bignumber.equal(tokenId);
+ expect(receiverLog.args.operator).to.be.equal(owner);
+ expect(receiverLog.args.from).to.be.equal(from);
+ expect(receiverLog.args.tokenId).to.be.bignumber.equal(tokenId);
+ expect(receiverLog.args.data).to.be.equal(data);
+ });
+ });
+});
+// tslint:enable:no-unnecessary-type-assertion
diff --git a/packages/contracts/test/tokens/unlimited_allowance_token.ts b/packages/contracts/test/tokens/unlimited_allowance_token.ts
index 81d931fc5..f2725b408 100644
--- a/packages/contracts/test/tokens/unlimited_allowance_token.ts
+++ b/packages/contracts/test/tokens/unlimited_allowance_token.ts
@@ -17,7 +17,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
describe('UnlimitedAllowanceToken', () => {
let owner: string;
let spender: string;
- const MAX_MINT_VALUE = new BigNumber(100000000000000000000);
+ const MAX_MINT_VALUE = new BigNumber(10000000000000000000000);
let token: DummyERC20TokenContract;
before(async () => {
diff --git a/packages/contracts/test/utils/artifacts.ts b/packages/contracts/test/utils/artifacts.ts
index e608ee174..e86ad5406 100644
--- a/packages/contracts/test/utils/artifacts.ts
+++ b/packages/contracts/test/utils/artifacts.ts
@@ -4,12 +4,14 @@ import * as AssetProxyOwner from '../../artifacts/AssetProxyOwner.json';
import * as DummyERC20Token from '../../artifacts/DummyERC20Token.json';
import * as DummyERC721Receiver from '../../artifacts/DummyERC721Receiver.json';
import * as DummyERC721Token from '../../artifacts/DummyERC721Token.json';
+import * as DummyNoReturnERC20Token from '../../artifacts/DummyNoReturnERC20Token.json';
import * as ERC20Proxy from '../../artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../../artifacts/ERC721Proxy.json';
import * as Exchange from '../../artifacts/Exchange.json';
import * as ExchangeWrapper from '../../artifacts/ExchangeWrapper.json';
import * as Forwarder from '../../artifacts/Forwarder.json';
import * as IAssetProxy from '../../artifacts/IAssetProxy.json';
+import * as InvalidERC721Receiver from '../../artifacts/InvalidERC721Receiver.json';
import * as MixinAuthorizable from '../../artifacts/MixinAuthorizable.json';
import * as MultiSigWallet from '../../artifacts/MultiSigWallet.json';
import * as MultiSigWalletWithTimeLock from '../../artifacts/MultiSigWalletWithTimeLock.json';
@@ -32,6 +34,7 @@ export const artifacts = {
DummyERC20Token: (DummyERC20Token as any) as ContractArtifact,
DummyERC721Receiver: (DummyERC721Receiver as any) as ContractArtifact,
DummyERC721Token: (DummyERC721Token as any) as ContractArtifact,
+ DummyNoReturnERC20Token: (DummyNoReturnERC20Token as any) as ContractArtifact,
ERC20Proxy: (ERC20Proxy as any) as ContractArtifact,
ERC721Proxy: (ERC721Proxy as any) as ContractArtifact,
Exchange: (Exchange as any) as ContractArtifact,
@@ -39,6 +42,7 @@ export const artifacts = {
EtherToken: (EtherToken as any) as ContractArtifact,
Forwarder: (Forwarder as any) as ContractArtifact,
IAssetProxy: (IAssetProxy as any) as ContractArtifact,
+ InvalidERC721Receiver: (InvalidERC721Receiver as any) as ContractArtifact,
MixinAuthorizable: (MixinAuthorizable as any) as ContractArtifact,
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
MultiSigWalletWithTimeLock: (MultiSigWalletWithTimeLock as any) as ContractArtifact,
diff --git a/packages/contracts/test/utils/erc721_wrapper.ts b/packages/contracts/test/utils/erc721_wrapper.ts
index d023b4d02..743d10706 100644
--- a/packages/contracts/test/utils/erc721_wrapper.ts
+++ b/packages/contracts/test/utils/erc721_wrapper.ts
@@ -81,7 +81,8 @@ export class ERC721Wrapper {
}
public async doesTokenExistAsync(tokenAddress: string, tokenId: BigNumber): Promise<boolean> {
const tokenContract = this._getTokenContractFromAssetData(tokenAddress);
- const doesExist = await tokenContract.exists.callAsync(tokenId);
+ const owner = await tokenContract.ownerOf.callAsync(tokenId);
+ const doesExist = owner !== constants.NULL_ADDRESS;
return doesExist;
}
public async approveProxyAsync(tokenAddress: string, tokenId: BigNumber): Promise<void> {
diff --git a/packages/contracts/test/utils/exchange_wrapper.ts b/packages/contracts/test/utils/exchange_wrapper.ts
index d57592d6d..619d43994 100644
--- a/packages/contracts/test/utils/exchange_wrapper.ts
+++ b/packages/contracts/test/utils/exchange_wrapper.ts
@@ -17,7 +17,7 @@ export class ExchangeWrapper {
constructor(exchangeContract: ExchangeContract, provider: Provider) {
this._exchange = exchangeContract;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper, this._exchange.address);
+ this._logDecoder = new LogDecoder(this._web3Wrapper);
}
public async fillOrderAsync(
signedOrder: SignedOrder,
@@ -266,4 +266,7 @@ export class ExchangeWrapper {
);
return data;
}
+ public getExchangeAddress(): string {
+ return this._exchange.address;
+ }
}
diff --git a/packages/contracts/test/utils/fill_order_combinatorial_utils.ts b/packages/contracts/test/utils/fill_order_combinatorial_utils.ts
index f18ad0dd3..a9318571c 100644
--- a/packages/contracts/test/utils/fill_order_combinatorial_utils.ts
+++ b/packages/contracts/test/utils/fill_order_combinatorial_utils.ts
@@ -504,7 +504,11 @@ export class FillOrderCombinatorialUtils {
const actFilledTakerAmount = await this.exchangeWrapper.getTakerAssetFilledAmountAsync(orderHash);
expect(actFilledTakerAmount).to.be.bignumber.equal(expFilledTakerAmount, 'filledTakerAmount');
- expect(txReceipt.logs.length).to.be.equal(1, 'logs length');
+ const exchangeLogs = _.filter(
+ txReceipt.logs,
+ txLog => txLog.address === this.exchangeWrapper.getExchangeAddress(),
+ );
+ expect(exchangeLogs.length).to.be.equal(1, 'logs length');
// tslint:disable-next-line:no-unnecessary-type-assertion
const log = txReceipt.logs[0] as LogWithDecodedArgs<ExchangeFillEventArgs>;
expect(log.args.makerAddress).to.be.equal(makerAddress, 'log.args.makerAddress');
diff --git a/packages/contracts/test/utils/forwarder_wrapper.ts b/packages/contracts/test/utils/forwarder_wrapper.ts
index 5b9a63ddf..de247a878 100644
--- a/packages/contracts/test/utils/forwarder_wrapper.ts
+++ b/packages/contracts/test/utils/forwarder_wrapper.ts
@@ -58,7 +58,7 @@ export class ForwarderWrapper {
constructor(contractInstance: ForwarderContract, provider: Provider) {
this._forwarderContract = contractInstance;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper, this._forwarderContract.address);
+ this._logDecoder = new LogDecoder(this._web3Wrapper);
}
public async marketSellOrdersWithEthAsync(
orders: SignedOrder[],
diff --git a/packages/contracts/test/utils/log_decoder.ts b/packages/contracts/test/utils/log_decoder.ts
index 5a4801319..6064ef0f7 100644
--- a/packages/contracts/test/utils/log_decoder.ts
+++ b/packages/contracts/test/utils/log_decoder.ts
@@ -16,7 +16,6 @@ import { constants } from './constants';
export class LogDecoder {
private readonly _web3Wrapper: Web3Wrapper;
- private readonly _contractAddress: string;
private readonly _abiDecoder: AbiDecoder;
public static wrapLogBigNumbers(log: any): any {
const argNames = _.keys(log.args);
@@ -27,9 +26,8 @@ export class LogDecoder {
}
}
}
- constructor(web3Wrapper: Web3Wrapper, contractAddress: string) {
+ constructor(web3Wrapper: Web3Wrapper) {
this._web3Wrapper = web3Wrapper;
- this._contractAddress = contractAddress;
const abiArrays: AbiDefinition[][] = [];
_.forEach(artifacts, (artifact: ContractArtifact) => {
const compilerOutput = artifact.compilerOutput;
@@ -48,7 +46,6 @@ export class LogDecoder {
}
public async getTxWithDecodedLogsAsync(txHash: string): Promise<TransactionReceiptWithDecodedLogs> {
const tx = await this._web3Wrapper.awaitTransactionSuccessAsync(txHash, constants.AWAIT_TRANSACTION_MINED_MS);
- tx.logs = _.filter(tx.logs, log => log.address === this._contractAddress);
tx.logs = _.map(tx.logs, log => this.decodeLogOrThrow(log));
return tx;
}
diff --git a/packages/contracts/test/utils/multi_sig_wrapper.ts b/packages/contracts/test/utils/multi_sig_wrapper.ts
index 8c8055d4a..e0c27b839 100644
--- a/packages/contracts/test/utils/multi_sig_wrapper.ts
+++ b/packages/contracts/test/utils/multi_sig_wrapper.ts
@@ -16,7 +16,7 @@ export class MultiSigWrapper {
constructor(multiSigContract: MultiSigWalletContract, provider: Provider) {
this._multiSig = multiSigContract;
this._web3Wrapper = new Web3Wrapper(provider);
- this._logDecoder = new LogDecoder(this._web3Wrapper, this._multiSig.address);
+ this._logDecoder = new LogDecoder(this._web3Wrapper);
}
public async submitTransactionAsync(
destination: string,