Asset Proxy Dispatcher
This commit is contained in:
parent
b9e0cd4512
commit
78d81f193f
@ -26,8 +26,9 @@
|
|||||||
"test:circleci": "yarn test:coverage"
|
"test:circleci": "yarn test:coverage"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"abis": "../migrations/src/artifacts/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken).json",
|
"abis": "../migrations/src/artifacts/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|AssetProxyDispatcher|ERC20TransferProxy_v1|ERC20TransferProxy|ERC721TransferProxy|DummyERC721Token).json",
|
||||||
"contracts": "Exchange,DummyToken,ZRXToken,Token,WETH9,TokenTransferProxy,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,MaliciousToken,TokenRegistry"
|
"contracts": "Exchange,DummyToken,ZRXToken,Token,WETH9,TokenTransferProxy,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,MaliciousToken,TokenRegistry,AssetProxyDispatcher,ERC20TransferProxy_v1,ERC20TransferProxy,ERC721TransferProxy,DummyERC721Token",
|
||||||
|
"dirs": "src/contracts,zeppelin:../../node_modules/zeppelin-solidity"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -71,6 +72,7 @@
|
|||||||
"ethereumjs-util": "^5.1.1",
|
"ethereumjs-util": "^5.1.1",
|
||||||
"ethers-contracts": "^2.2.1",
|
"ethers-contracts": "^2.2.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"web3": "^0.20.0"
|
"web3": "^0.20.0",
|
||||||
|
"zeppelin-solidity": "^1.8.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
import "./IAssetProxyDispatcher.sol";
|
||||||
|
import "./IAssetProxy.sol";
|
||||||
|
import "../../utils/Ownable/Ownable.sol";
|
||||||
|
import "../../utils/Authorizable/Authorizable.sol";
|
||||||
|
|
||||||
|
contract AssetProxyDispatcher is
|
||||||
|
Ownable,
|
||||||
|
Authorizable,
|
||||||
|
IAssetProxyDispatcher
|
||||||
|
{
|
||||||
|
// Mapping from Asset Proxy Id's to their respective Asset Proxy
|
||||||
|
mapping (uint8 => IAssetProxy) public assetProxies;
|
||||||
|
|
||||||
|
/// @dev Delegates transfer to the corresponding asset proxy.
|
||||||
|
/// @param assetMetadata Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer token from.
|
||||||
|
/// @param to Address to transfer token to.
|
||||||
|
/// @param amount Amount of token to transfer.
|
||||||
|
function transferFrom(
|
||||||
|
bytes assetMetadata,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount)
|
||||||
|
public
|
||||||
|
onlyAuthorized
|
||||||
|
{
|
||||||
|
// Lookup asset proxy
|
||||||
|
require(assetMetadata.length >= 1);
|
||||||
|
uint8 assetProxyId = uint8(assetMetadata[0]);
|
||||||
|
IAssetProxy assetProxy = assetProxies[assetProxyId];
|
||||||
|
|
||||||
|
// Dispatch transfer to asset proxy
|
||||||
|
// transferFrom will either succeed or throw.
|
||||||
|
assetProxy.transferFrom(assetMetadata, from, to, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Registers a new asset proxy.
|
||||||
|
/// @param assetProxyId Id of the asset proxy.
|
||||||
|
/// @param newAssetProxyAddress Address of the asset proxy contract to register.
|
||||||
|
/// @param currentAssetProxyAddress Address of existing asset proxy to overwrite.
|
||||||
|
function setAssetProxy(
|
||||||
|
uint8 assetProxyId,
|
||||||
|
address newAssetProxyAddress,
|
||||||
|
address currentAssetProxyAddress)
|
||||||
|
public
|
||||||
|
onlyOwner
|
||||||
|
{
|
||||||
|
// Ensure any existing asset proxy is not unintentionally overwritten
|
||||||
|
require(currentAssetProxyAddress == address(assetProxies[assetProxyId]));
|
||||||
|
|
||||||
|
// Store asset proxy and log registration
|
||||||
|
assetProxies[assetProxyId] = IAssetProxy(newAssetProxyAddress);
|
||||||
|
emit AssetProxyChanged(assetProxyId, newAssetProxyAddress, currentAssetProxyAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Gets an asset proxy.
|
||||||
|
/// @param assetProxyId Id of the asset proxy.
|
||||||
|
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
|
||||||
|
function getAssetProxy(uint8 assetProxyId)
|
||||||
|
public view
|
||||||
|
returns (IAssetProxy)
|
||||||
|
{
|
||||||
|
IAssetProxy assetProxy = assetProxies[assetProxyId];
|
||||||
|
return assetProxy;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
contract IAssetProxy {
|
||||||
|
|
||||||
|
/// @dev Transfers assets. Either succeeds or throws.
|
||||||
|
/// @param assetMetadata Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer token from.
|
||||||
|
/// @param to Address to transfer token to.
|
||||||
|
/// @param amount Amount of token to transfer.
|
||||||
|
function transferFrom(
|
||||||
|
bytes assetMetadata,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount)
|
||||||
|
public;
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
import "./IAssetProxy.sol";
|
||||||
|
import "../../utils/Authorizable/IAuthorizable.sol";
|
||||||
|
|
||||||
|
contract IAssetProxyDispatcher is
|
||||||
|
IAuthorizable,
|
||||||
|
IAssetProxy
|
||||||
|
{
|
||||||
|
// Logs registration of new asset proxy
|
||||||
|
event AssetProxyChanged(
|
||||||
|
uint8 id,
|
||||||
|
address newAssetClassAddress,
|
||||||
|
address oldAssetClassAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @dev Sets a new asset proxy.
|
||||||
|
/// @param assetProxyId Id of the asset proxy.
|
||||||
|
/// @param newAssetProxyAddress Address of the asset proxy contract to register.
|
||||||
|
/// @param currentAssetProxyAddress Address of existing asset proxy to overwrite.
|
||||||
|
function setAssetProxy(
|
||||||
|
uint8 assetProxyId,
|
||||||
|
address newAssetProxyAddress,
|
||||||
|
address currentAssetProxyAddress)
|
||||||
|
public;
|
||||||
|
|
||||||
|
/// @dev Gets an asset proxy.
|
||||||
|
/// @param assetProxyId Id of the asset proxy.
|
||||||
|
/// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
|
||||||
|
function getAssetProxy(uint8 assetProxyId)
|
||||||
|
public view
|
||||||
|
returns (IAssetProxy);
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
import "../IAssetProxy.sol";
|
||||||
|
import "../../../utils/LibBytes/LibBytes.sol";
|
||||||
|
import "../../../utils/Authorizable/Authorizable.sol";
|
||||||
|
import { Token_v1 as ERC20Token } from "../../../../previous/Token/Token_v1.sol";
|
||||||
|
|
||||||
|
contract ERC20TransferProxy is
|
||||||
|
LibBytes,
|
||||||
|
Authorizable,
|
||||||
|
IAssetProxy
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @dev Transfers ERC20 tokens.
|
||||||
|
/// @param assetMetadata Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer token from.
|
||||||
|
/// @param to Address to transfer token to.
|
||||||
|
/// @param amount Amount of token to transfer.
|
||||||
|
function transferFrom(
|
||||||
|
bytes assetMetadata,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount)
|
||||||
|
public
|
||||||
|
onlyAuthorized
|
||||||
|
{
|
||||||
|
address token = decodeMetadata(assetMetadata);
|
||||||
|
bool success = ERC20Token(token).transferFrom(from, to, amount);
|
||||||
|
require(success == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Encodes ERC20 byte array for the ERC20 asset proxy.
|
||||||
|
/// @param assetProxyId Id of the asset proxy.
|
||||||
|
/// @param tokenAddress Address of the asset.
|
||||||
|
/// @return assetMetadata Byte array encoded for the ERC20 asset proxy.
|
||||||
|
function encodeMetadata(
|
||||||
|
uint8 assetProxyId,
|
||||||
|
address tokenAddress)
|
||||||
|
public pure
|
||||||
|
returns (bytes assetMetadata)
|
||||||
|
{
|
||||||
|
// 0 is reserved as invalid proxy id
|
||||||
|
require(assetProxyId != 0);
|
||||||
|
|
||||||
|
// Encode fields into a byte array
|
||||||
|
assetMetadata = new bytes(21);
|
||||||
|
assetMetadata[0] = byte(assetProxyId);
|
||||||
|
writeAddress(tokenAddress, assetMetadata, 1);
|
||||||
|
return assetMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decodes ERC20-encoded byte array for the ERC20 asset proxy.
|
||||||
|
/// @param assetMetadata Byte array encoded for the ERC20 asset proxy.
|
||||||
|
/// @return tokenAddress Address of ERC20 token.
|
||||||
|
function decodeMetadata(bytes assetMetadata)
|
||||||
|
public pure
|
||||||
|
returns (address tokenAddress)
|
||||||
|
{
|
||||||
|
require(assetMetadata.length == 21);
|
||||||
|
return readAddress(assetMetadata, 1);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
import "../IAssetProxy.sol";
|
||||||
|
import "../../../utils/LibBytes/LibBytes.sol";
|
||||||
|
import "../../TokenTransferProxy/ITokenTransferProxy.sol";
|
||||||
|
import "../../../utils/Authorizable/Authorizable.sol";
|
||||||
|
|
||||||
|
contract ERC20TransferProxy_v1 is
|
||||||
|
LibBytes,
|
||||||
|
Authorizable,
|
||||||
|
IAssetProxy
|
||||||
|
{
|
||||||
|
ITokenTransferProxy TRANSFER_PROXY;
|
||||||
|
|
||||||
|
/// @dev Contract constructor.
|
||||||
|
/// @param tokenTransferProxyContract erc20 token transfer proxy contract.
|
||||||
|
function ERC20TransferProxy_v1(ITokenTransferProxy tokenTransferProxyContract)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
TRANSFER_PROXY = tokenTransferProxyContract;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Transfers ERC20 tokens.
|
||||||
|
/// @param assetMetadata Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer token from.
|
||||||
|
/// @param to Address to transfer token to.
|
||||||
|
/// @param amount Amount of token to transfer.
|
||||||
|
function transferFrom(
|
||||||
|
bytes assetMetadata,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount)
|
||||||
|
public
|
||||||
|
onlyAuthorized
|
||||||
|
{
|
||||||
|
address token = decodeMetadata(assetMetadata);
|
||||||
|
bool success = TRANSFER_PROXY.transferFrom(token, from, to, amount);
|
||||||
|
require(success == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Encodes ERC20 byte array for the ERC20 asset proxy.
|
||||||
|
/// @param assetProxyId Id of the asset proxy.
|
||||||
|
/// @param tokenAddress Address of the asset.
|
||||||
|
/// @return assetMetadata Byte array encoded for the ERC20 asset proxy.
|
||||||
|
function encodeMetadata(
|
||||||
|
uint8 assetProxyId,
|
||||||
|
address tokenAddress)
|
||||||
|
public pure
|
||||||
|
returns (bytes assetMetadata)
|
||||||
|
{
|
||||||
|
// 0 is reserved as invalid proxy id
|
||||||
|
require(assetProxyId != 0);
|
||||||
|
|
||||||
|
// Encode fields into a byte array
|
||||||
|
assetMetadata = new bytes(21);
|
||||||
|
assetMetadata[0] = byte(assetProxyId);
|
||||||
|
writeAddress(tokenAddress, assetMetadata, 1);
|
||||||
|
return assetMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decodes ERC20-encoded byte array for the ERC20 asset proxy.
|
||||||
|
/// @param assetMetadata Byte array encoded for the ERC20 asset proxy.
|
||||||
|
/// @return tokenAddress Address of ERC20 token.
|
||||||
|
function decodeMetadata(bytes assetMetadata)
|
||||||
|
public pure
|
||||||
|
returns (address tokenAddress)
|
||||||
|
{
|
||||||
|
require(assetMetadata.length == 21);
|
||||||
|
return readAddress(assetMetadata, 1);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
import "../IAssetProxy.sol";
|
||||||
|
import "../../../utils/LibBytes/LibBytes.sol";
|
||||||
|
import "../../../utils/Authorizable/Authorizable.sol";
|
||||||
|
import "/zeppelin/contracts/token/ERC721/ERC721Token.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract ERC721TransferProxy is
|
||||||
|
LibBytes,
|
||||||
|
Authorizable,
|
||||||
|
IAssetProxy
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @dev Transfers ERC20 tokens.
|
||||||
|
/// @param assetMetadata Byte array encoded for the respective asset proxy.
|
||||||
|
/// @param from Address to transfer token from.
|
||||||
|
/// @param to Address to transfer token to.
|
||||||
|
/// @param amount Amount of token to transfer.
|
||||||
|
function transferFrom(
|
||||||
|
bytes assetMetadata,
|
||||||
|
address from,
|
||||||
|
address to,
|
||||||
|
uint256 amount)
|
||||||
|
public
|
||||||
|
onlyAuthorized
|
||||||
|
{
|
||||||
|
// Decode metadata
|
||||||
|
address token;
|
||||||
|
uint256 tokenId;
|
||||||
|
(token, tokenId) = decodeMetadata(assetMetadata);
|
||||||
|
|
||||||
|
// There exists only 1 of each token.
|
||||||
|
require(amount == 1);
|
||||||
|
|
||||||
|
// Call ERC721 contract. Either succeeds or throws.
|
||||||
|
ERC721Token(token).transferFrom(from, to, tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Encodes ERC721 byte array for the ERC20 asset proxy.
|
||||||
|
/// @param assetProxyId Id of the asset proxy.
|
||||||
|
/// @param tokenAddress Address of the asset.
|
||||||
|
/// @param tokenId Id of ERC721 token.
|
||||||
|
/// @return assetMetadata Byte array encoded for the ERC721 asset proxy.
|
||||||
|
function encodeMetadata(
|
||||||
|
uint8 assetProxyId,
|
||||||
|
address tokenAddress,
|
||||||
|
uint256 tokenId)
|
||||||
|
public pure
|
||||||
|
returns (bytes assetMetadata)
|
||||||
|
{
|
||||||
|
// 0 is reserved as invalid proxy id
|
||||||
|
require(assetProxyId != 0);
|
||||||
|
|
||||||
|
// Encode fields into a byte array
|
||||||
|
assetMetadata = new bytes(53);
|
||||||
|
assetMetadata[0] = byte(assetProxyId);
|
||||||
|
writeAddress(tokenAddress, assetMetadata, 1);
|
||||||
|
writeUint256(tokenId, assetMetadata, 21);
|
||||||
|
return assetMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Decodes ERC721-encoded byte array for the ERC721 asset proxy.
|
||||||
|
/// @param assetMetadata Byte array encoded for the ERC721 asset proxy.
|
||||||
|
/// @return tokenAddress Address of ERC721 token.
|
||||||
|
/// @return tokenId Id of ERC721 token.
|
||||||
|
function decodeMetadata(bytes assetMetadata)
|
||||||
|
public pure
|
||||||
|
returns (address tokenAddress, uint256 tokenId)
|
||||||
|
{
|
||||||
|
require(assetMetadata.length == 53);
|
||||||
|
tokenAddress = readAddress(assetMetadata, 1);
|
||||||
|
tokenId = readUint256(assetMetadata, 21);
|
||||||
|
return (tokenAddress, tokenId);
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,8 @@ import "./MixinExchangeCore.sol";
|
|||||||
import "./MixinSignatureValidator.sol";
|
import "./MixinSignatureValidator.sol";
|
||||||
import "./MixinSettlementProxy.sol";
|
import "./MixinSettlementProxy.sol";
|
||||||
import "./MixinWrapperFunctions.sol";
|
import "./MixinWrapperFunctions.sol";
|
||||||
|
import "../AssetProxyDispatcher/IAssetProxyDispatcher.sol";
|
||||||
|
import "../TokenTransferProxy/ITokenTransferProxy.sol";
|
||||||
|
|
||||||
contract Exchange is
|
contract Exchange is
|
||||||
MixinExchangeCore,
|
MixinExchangeCore,
|
||||||
@ -34,11 +36,12 @@ contract Exchange is
|
|||||||
|
|
||||||
function Exchange(
|
function Exchange(
|
||||||
IToken _zrxToken,
|
IToken _zrxToken,
|
||||||
ITokenTransferProxy _tokenTransferProxy)
|
bytes _zrxProxyMetadata,
|
||||||
|
IAssetProxyDispatcher _assetProxyDispatcher)
|
||||||
public
|
public
|
||||||
MixinExchangeCore()
|
MixinExchangeCore()
|
||||||
MixinSignatureValidator()
|
MixinSignatureValidator()
|
||||||
MixinSettlementProxy(_tokenTransferProxy, _zrxToken)
|
MixinSettlementProxy(_assetProxyDispatcher, _zrxToken, _zrxProxyMetadata)
|
||||||
MixinWrapperFunctions()
|
MixinWrapperFunctions()
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
|
@ -54,11 +54,6 @@ contract IExchange {
|
|||||||
bytes32 indexed orderHash
|
bytes32 indexed orderHash
|
||||||
);
|
);
|
||||||
|
|
||||||
event LogCancelBefore(
|
|
||||||
address indexed maker,
|
|
||||||
uint256 salt
|
|
||||||
);
|
|
||||||
|
|
||||||
function ZRX_TOKEN_CONTRACT()
|
function ZRX_TOKEN_CONTRACT()
|
||||||
public view
|
public view
|
||||||
returns (address);
|
returns (address);
|
||||||
|
@ -33,7 +33,9 @@ contract LibOrder {
|
|||||||
"uint256 makerFee",
|
"uint256 makerFee",
|
||||||
"uint256 takerFee",
|
"uint256 takerFee",
|
||||||
"uint256 expirationTimeSeconds",
|
"uint256 expirationTimeSeconds",
|
||||||
"uint256 salt"
|
"uint256 salt",
|
||||||
|
"bytes makerAssetProxyData",
|
||||||
|
"bytes takerAssetProxyData"
|
||||||
);
|
);
|
||||||
|
|
||||||
struct Order {
|
struct Order {
|
||||||
@ -48,6 +50,8 @@ contract LibOrder {
|
|||||||
uint256 takerFee;
|
uint256 takerFee;
|
||||||
uint256 expirationTimeSeconds;
|
uint256 expirationTimeSeconds;
|
||||||
uint256 salt;
|
uint256 salt;
|
||||||
|
bytes makerAssetProxyData;
|
||||||
|
bytes takerAssetProxyData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Calculates Keccak-256 hash of the order.
|
/// @dev Calculates Keccak-256 hash of the order.
|
||||||
@ -73,7 +77,9 @@ contract LibOrder {
|
|||||||
order.makerFee,
|
order.makerFee,
|
||||||
order.takerFee,
|
order.takerFee,
|
||||||
order.expirationTimeSeconds,
|
order.expirationTimeSeconds,
|
||||||
order.salt
|
order.salt,
|
||||||
|
order.makerAssetProxyData,
|
||||||
|
order.takerAssetProxyData
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return orderHash;
|
return orderHash;
|
||||||
|
@ -36,5 +36,3 @@ contract LibPartialAmount is SafeMath {
|
|||||||
return partialAmount;
|
return partialAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,22 +20,22 @@ pragma solidity ^0.4.21;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./mixins/MSettlement.sol";
|
import "./mixins/MSettlement.sol";
|
||||||
import "../TokenTransferProxy/ITokenTransferProxy.sol";
|
|
||||||
import "../../tokens/Token/IToken.sol";
|
import "../../tokens/Token/IToken.sol";
|
||||||
import "./LibPartialAmount.sol";
|
import "./LibPartialAmount.sol";
|
||||||
|
import "../AssetProxyDispatcher/IAssetProxyDispatcher.sol";
|
||||||
|
|
||||||
/// @dev Provides MixinSettlement
|
/// @dev Provides MixinSettlement
|
||||||
contract MixinSettlementProxy is
|
contract MixinSettlementProxy is
|
||||||
MSettlement,
|
MSettlement,
|
||||||
LibPartialAmount
|
LibPartialAmount
|
||||||
{
|
{
|
||||||
|
IAssetProxyDispatcher TRANSFER_PROXY;
|
||||||
ITokenTransferProxy TRANSFER_PROXY;
|
bytes ZRX_PROXY_METADATA;
|
||||||
IToken ZRX_TOKEN;
|
IToken ZRX_TOKEN;
|
||||||
|
|
||||||
function transferProxy()
|
function transferProxy()
|
||||||
external view
|
public view
|
||||||
returns (ITokenTransferProxy)
|
returns (IAssetProxyDispatcher)
|
||||||
{
|
{
|
||||||
return TRANSFER_PROXY;
|
return TRANSFER_PROXY;
|
||||||
}
|
}
|
||||||
@ -47,15 +47,26 @@ contract MixinSettlementProxy is
|
|||||||
return ZRX_TOKEN;
|
return ZRX_TOKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function zrxProxyMetadata()
|
||||||
|
external view
|
||||||
|
returns (bytes)
|
||||||
|
{
|
||||||
|
return ZRX_PROXY_METADATA;
|
||||||
|
}
|
||||||
|
|
||||||
function MixinSettlementProxy(
|
function MixinSettlementProxy(
|
||||||
ITokenTransferProxy _proxyContract,
|
IAssetProxyDispatcher assetProxyDispatcherContract,
|
||||||
IToken _zrxToken)
|
IToken zrxToken,
|
||||||
|
bytes zrxProxyMetadata)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
ZRX_TOKEN = _zrxToken;
|
ZRX_TOKEN = zrxToken;
|
||||||
TRANSFER_PROXY = _proxyContract;
|
TRANSFER_PROXY = assetProxyDispatcherContract;
|
||||||
|
ZRX_PROXY_METADATA = zrxProxyMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function settleOrder(
|
function settleOrder(
|
||||||
Order memory order,
|
Order memory order,
|
||||||
address takerAddress,
|
address takerAddress,
|
||||||
@ -68,43 +79,35 @@ contract MixinSettlementProxy is
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
makerTokenFilledAmount = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerTokenAmount);
|
makerTokenFilledAmount = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerTokenAmount);
|
||||||
require(
|
TRANSFER_PROXY.transferFrom(
|
||||||
TRANSFER_PROXY.transferFrom(
|
order.makerAssetProxyData,
|
||||||
order.makerTokenAddress,
|
order.makerAddress,
|
||||||
order.makerAddress,
|
takerAddress,
|
||||||
takerAddress,
|
makerTokenFilledAmount
|
||||||
makerTokenFilledAmount
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
require(
|
TRANSFER_PROXY.transferFrom(
|
||||||
TRANSFER_PROXY.transferFrom(
|
order.takerAssetProxyData,
|
||||||
order.takerTokenAddress,
|
takerAddress,
|
||||||
takerAddress,
|
order.makerAddress,
|
||||||
order.makerAddress,
|
takerTokenFilledAmount
|
||||||
takerTokenFilledAmount
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
if (order.feeRecipientAddress != address(0)) {
|
if (order.feeRecipientAddress != address(0)) {
|
||||||
if (order.makerFee > 0) {
|
if (order.makerFee > 0) {
|
||||||
makerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerFee);
|
makerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerFee);
|
||||||
require(
|
TRANSFER_PROXY.transferFrom(
|
||||||
TRANSFER_PROXY.transferFrom(
|
ZRX_PROXY_METADATA,
|
||||||
ZRX_TOKEN,
|
order.makerAddress,
|
||||||
order.makerAddress,
|
order.feeRecipientAddress,
|
||||||
order.feeRecipientAddress,
|
makerFeePaid
|
||||||
makerFeePaid
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (order.takerFee > 0) {
|
if (order.takerFee > 0) {
|
||||||
takerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.takerFee);
|
takerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.takerFee);
|
||||||
require(
|
TRANSFER_PROXY.transferFrom(
|
||||||
TRANSFER_PROXY.transferFrom(
|
ZRX_PROXY_METADATA,
|
||||||
ZRX_TOKEN,
|
takerAddress,
|
||||||
takerAddress,
|
order.feeRecipientAddress,
|
||||||
order.feeRecipientAddress,
|
takerFeePaid
|
||||||
takerFeePaid
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,76 +65,163 @@ contract MixinWrapperFunctions is
|
|||||||
// We need to call MExchangeCore.fillOrder using a delegatecall in
|
// We need to call MExchangeCore.fillOrder using a delegatecall in
|
||||||
// assembly so that we can intercept a call that throws. For this, we
|
// assembly so that we can intercept a call that throws. For this, we
|
||||||
// need the input encoded in memory in the Ethereum ABIv2 format [1].
|
// need the input encoded in memory in the Ethereum ABIv2 format [1].
|
||||||
|
|
||||||
// | Offset | Length | Contents |
|
// | Area | Offset | Length | Contents |
|
||||||
// |--------|---------|------------------------------|
|
// | -------- |--------|---------|-------------------------------------------- |
|
||||||
// | 0 | 4 | function selector |
|
// | Header | 0x00 | 4 | function selector |
|
||||||
// | 4 | 11 * 32 | Order order |
|
// | Params | | 3 * 32 | function parameters: |
|
||||||
// | 356 | 32 | uint256 takerTokenFillAmount |
|
// | | 0x00 | | 1. offset to order (*) |
|
||||||
// | 388 | 32 | offset to signature (416) |
|
// | | 0x20 | | 2. takerTokenFillAmount |
|
||||||
// | 420 | 32 | len(signature) |
|
// | | 0x40 | | 3. offset to signature (*) |
|
||||||
// | 452 | (1) | signature |
|
// | Data | | 13 * 32 | order: |
|
||||||
// | (2) | (3) | padding (zero) |
|
// | | 0x000 | | 1. makerAddress |
|
||||||
// | (4) | | end of input |
|
// | | 0x020 | | 2. takerAddress |
|
||||||
|
// | | 0x040 | | 3. makerTokenAddress |
|
||||||
// (1): len(signature)
|
// | | 0x060 | | 4. takerTokenAddress |
|
||||||
// (2): 452 + len(signature)
|
// | | 0x080 | | 5. feeRecipientAddress |
|
||||||
// (3): (32 - len(signature)) mod 32
|
// | | 0x0A0 | | 6. makerTokenAmount |
|
||||||
// (4): 452 + len(signature) + (32 - len(signature)) mod 32
|
// | | 0x0C0 | | 7. takerTokenAmount |
|
||||||
|
// | | 0x0E0 | | 8. makerFeeAmount |
|
||||||
|
// | | 0x100 | | 9. takerFeeAmount |
|
||||||
|
// | | 0x120 | | 10. expirationTimeSeconds |
|
||||||
|
// | | 0x140 | | 11. salt |
|
||||||
|
// | | 0x160 | | 12. Offset to makerAssetProxyMetadata (*) |
|
||||||
|
// | | 0x180 | | 13. Offset to takerAssetProxyMetadata (* |
|
||||||
|
// | | 0x1A0 | 32 | makerAssetProxyMetadata Length |
|
||||||
|
// | | 0x1C0 | ** | makerAssetProxyMetadata Contents |
|
||||||
|
// | | 0x1E0 | 32 | takerAssetProxyMetadata Length |
|
||||||
|
// | | 0x200 | ** | takerAssetProxyMetadata Contents |
|
||||||
|
// | | 0x220 | 32 | signature Length |
|
||||||
|
// | | 0x240 | ** | signature Contents |
|
||||||
|
|
||||||
|
// * Offsets are calculated from the beginning of the current area: Header, Params, Data:
|
||||||
|
// An offset stored in the Params area is calculated from the beginning of the Params section.
|
||||||
|
// An offset stored in the Data area is calculated from the beginning of the Data section.
|
||||||
|
|
||||||
|
// ** The length of dynamic array contents are stored in the field immediately preceeding the contents.
|
||||||
|
|
||||||
// [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html
|
// [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html
|
||||||
|
|
||||||
bytes4 fillOrderSelector = this.fillOrder.selector;
|
bytes4 fillOrderSelector = this.fillOrder.selector;
|
||||||
|
|
||||||
assembly {
|
assembly {
|
||||||
|
|
||||||
|
// Areas below may use the following variables:
|
||||||
|
// 1. <area>Start -- Start of this area in memory
|
||||||
|
// 2. <area>End -- End of this area in memory. This value may
|
||||||
|
// be precomputed (before writing contents),
|
||||||
|
// or it may be computed as contents are written.
|
||||||
|
// 3. <area>Offset -- Current offset into area. If an area's End
|
||||||
|
// is precomputed, this variable tracks the
|
||||||
|
// offsets of contents as they are written.
|
||||||
|
|
||||||
|
/////// Setup Header Area ///////
|
||||||
// Load free memory pointer
|
// Load free memory pointer
|
||||||
let start := mload(0x40)
|
let headerAreaStart := mload(0x40)
|
||||||
|
mstore(headerAreaStart, fillOrderSelector)
|
||||||
|
let headerAreaEnd := add(headerAreaStart, 0x4)
|
||||||
|
|
||||||
// Write function signature
|
/////// Setup Params Area ///////
|
||||||
mstore(start, fillOrderSelector)
|
// This area is preallocated and written to later.
|
||||||
|
// This is because we need to fill in offsets that have not yet been calculated.
|
||||||
|
let paramsAreaStart := headerAreaEnd
|
||||||
|
let paramsAreaEnd := add(paramsAreaStart, 0x60)
|
||||||
|
let paramsAreaOffset := paramsAreaStart
|
||||||
|
|
||||||
// Write order struct
|
/////// Setup Data Area ///////
|
||||||
mstore(add(start, 4), mload(order)) // makerAddress
|
let dataAreaStart := paramsAreaEnd
|
||||||
mstore(add(start, 36), mload(add(order, 32))) // takerAddress
|
let dataAreaEnd := dataAreaStart
|
||||||
mstore(add(start, 68), mload(add(order, 64))) // makerTokenAddress
|
|
||||||
mstore(add(start, 100), mload(add(order, 96))) // takerTokenAddress
|
|
||||||
mstore(add(start, 132), mload(add(order, 128))) // feeRecipientAddress
|
|
||||||
mstore(add(start, 164), mload(add(order, 160))) // makerTokenAmount
|
|
||||||
mstore(add(start, 196), mload(add(order, 192))) // takerTokenAmount
|
|
||||||
mstore(add(start, 228), mload(add(order, 224))) // makerFeeAmount
|
|
||||||
mstore(add(start, 260), mload(add(order, 256))) // takerFeeAmount
|
|
||||||
mstore(add(start, 292), mload(add(order, 288))) // expirationTimeSeconds
|
|
||||||
mstore(add(start, 324), mload(add(order, 320))) // salt
|
|
||||||
|
|
||||||
// Write takerTokenFillAmount
|
// Offset from the source data we're reading from
|
||||||
mstore(add(start, 356), takerTokenFillAmount)
|
let sourceOffset := order
|
||||||
|
// bytesLen and bytesLenPadded track the length of a dynamically-allocated bytes array.
|
||||||
|
let bytesLen := 0
|
||||||
|
let bytesLenPadded := 0
|
||||||
|
|
||||||
// Write signature offset
|
/////// Write order Struct ///////
|
||||||
mstore(add(start, 388), 416)
|
// Write memory location of Order, relative to the start of the
|
||||||
|
// parameter list, then increment the paramsAreaOffset respectively.
|
||||||
|
mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
|
||||||
|
paramsAreaOffset := add(paramsAreaOffset, 0x20)
|
||||||
|
|
||||||
// Write signature length
|
// Write values for each field in the order
|
||||||
let sigLen := mload(signature)
|
for{let i := 0} lt(i, 13) {i := add(i, 1)} {
|
||||||
mstore(add(start, 420), sigLen)
|
mstore(dataAreaEnd, mload(sourceOffset))
|
||||||
|
dataAreaEnd := add(dataAreaEnd, 0x20)
|
||||||
|
sourceOffset := add(sourceOffset, 0x20)
|
||||||
|
}
|
||||||
|
|
||||||
// Calculate signature length with padding
|
// Write offset to <order.makerAssetProxyMetadata>
|
||||||
let paddingLen := mod(sub(0, sigLen), 32)
|
mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
|
||||||
let sigLenWithPadding := add(sigLen, paddingLen)
|
|
||||||
|
|
||||||
// Write signature
|
// Calculate length of <order.makerAssetProxyMetadata>
|
||||||
let sigStart := add(signature, 32)
|
bytesLen := mload(sourceOffset)
|
||||||
for { let curr := 0 }
|
sourceOffset := add(sourceOffset, 0x20)
|
||||||
lt(curr, sigLenWithPadding)
|
bytesLenPadded := add(div(bytesLen, 0x20), gt(mod(bytesLen, 0x20), 0))
|
||||||
{ curr := add(curr, 32) }
|
|
||||||
{ mstore(add(start, add(452, curr)), mload(add(sigStart, curr))) } // Note: we assume that padding consists of only 0's
|
// Write length of <order.makerAssetProxyMetadata>
|
||||||
|
mstore(dataAreaEnd, bytesLen)
|
||||||
|
dataAreaEnd := add(dataAreaEnd, 0x20)
|
||||||
|
|
||||||
|
// Write contents of <order.makerAssetProxyMetadata>
|
||||||
|
for {let i := 0} lt(i, bytesLenPadded) {i := add(i, 1)} {
|
||||||
|
mstore(dataAreaEnd, mload(sourceOffset))
|
||||||
|
dataAreaEnd := add(dataAreaEnd, 0x20)
|
||||||
|
sourceOffset := add(sourceOffset, 0x20)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write offset to <order.takerAssetProxyMetadata>
|
||||||
|
mstore(add(dataAreaStart, mul(12, 0x20)), sub(dataAreaEnd, dataAreaStart))
|
||||||
|
|
||||||
|
// Calculate length of <order.takerAssetProxyMetadata>
|
||||||
|
bytesLen := mload(sourceOffset)
|
||||||
|
sourceOffset := add(sourceOffset, 0x20)
|
||||||
|
bytesLenPadded := add(div(bytesLen, 0x20), gt(mod(bytesLen, 0x20), 0))
|
||||||
|
|
||||||
|
// Write length of <order.takerAssetProxyMetadata>
|
||||||
|
mstore(dataAreaEnd, bytesLen)
|
||||||
|
dataAreaEnd := add(dataAreaEnd, 0x20)
|
||||||
|
|
||||||
|
// Write contents of <order.takerAssetProxyMetadata>
|
||||||
|
for {let i := 0} lt(i, bytesLenPadded) {i := add(i, 1)} {
|
||||||
|
mstore(dataAreaEnd, mload(sourceOffset))
|
||||||
|
dataAreaEnd := add(dataAreaEnd, 0x20)
|
||||||
|
sourceOffset := add(sourceOffset, 0x20)
|
||||||
|
}
|
||||||
|
|
||||||
|
/////// Write takerTokenFillAmount ///////
|
||||||
|
mstore(paramsAreaOffset, takerTokenFillAmount)
|
||||||
|
paramsAreaOffset := add(paramsAreaOffset, 0x20)
|
||||||
|
|
||||||
|
/////// Write signature ///////
|
||||||
|
// Write offset to paramsArea
|
||||||
|
mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
|
||||||
|
|
||||||
|
// Calculate length of signature
|
||||||
|
sourceOffset := signature
|
||||||
|
bytesLen := mload(sourceOffset)
|
||||||
|
sourceOffset := add(sourceOffset, 0x20)
|
||||||
|
bytesLenPadded := add(div(bytesLen, 0x20), gt(mod(bytesLen, 0x20), 0))
|
||||||
|
|
||||||
|
// Write length of signature
|
||||||
|
mstore(dataAreaEnd, bytesLen)
|
||||||
|
dataAreaEnd := add(dataAreaEnd, 0x20)
|
||||||
|
|
||||||
|
// Write contents of signature
|
||||||
|
for {let i := 0} lt(i, bytesLenPadded) {i := add(i, 1)} {
|
||||||
|
mstore(dataAreaEnd, mload(sourceOffset))
|
||||||
|
dataAreaEnd := add(dataAreaEnd, 0x20)
|
||||||
|
sourceOffset := add(sourceOffset, 0x20)
|
||||||
|
}
|
||||||
|
|
||||||
// Execute delegatecall
|
// Execute delegatecall
|
||||||
let success := delegatecall(
|
let success := delegatecall(
|
||||||
gas, // forward all gas, TODO: look into gas consumption of assert/throw
|
gas, // forward all gas, TODO: look into gas consumption of assert/throw
|
||||||
address, // call address of this contract
|
address, // call address of this contract
|
||||||
start, // pointer to start of input
|
headerAreaStart, // pointer to start of input
|
||||||
add(452, sigLenWithPadding), // input length is 420 + signature length + padding length
|
sub(dataAreaEnd, headerAreaStart), // length of input
|
||||||
start, // write output over input
|
headerAreaStart, // write output over input
|
||||||
128 // output size is 128 bytes
|
128 // output size is 128 bytes
|
||||||
)
|
)
|
||||||
switch success
|
switch success
|
||||||
case 0 {
|
case 0 {
|
||||||
@ -144,12 +231,11 @@ contract MixinWrapperFunctions is
|
|||||||
mstore(add(fillResults, 96), 0)
|
mstore(add(fillResults, 96), 0)
|
||||||
}
|
}
|
||||||
case 1 {
|
case 1 {
|
||||||
mstore(fillResults, mload(start))
|
mstore(fillResults, mload(headerAreaStart))
|
||||||
mstore(add(fillResults, 32), mload(add(start, 32)))
|
mstore(add(fillResults, 32), mload(add(headerAreaStart, 32)))
|
||||||
mstore(add(fillResults, 64), mload(add(start, 64)))
|
mstore(add(fillResults, 64), mload(add(headerAreaStart, 64)))
|
||||||
mstore(add(fillResults, 96), mload(add(start, 96)))
|
mstore(add(fillResults, 96), mload(add(headerAreaStart, 96)))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return fillResults;
|
return fillResults;
|
||||||
}
|
}
|
||||||
@ -228,10 +314,10 @@ contract MixinWrapperFunctions is
|
|||||||
|
|
||||||
// Token being sold by taker must be the same for each order
|
// Token being sold by taker must be the same for each order
|
||||||
require(orders[i].takerTokenAddress == orders[0].takerTokenAddress);
|
require(orders[i].takerTokenAddress == orders[0].takerTokenAddress);
|
||||||
|
|
||||||
// Calculate the remaining amount of takerToken to sell
|
// Calculate the remaining amount of takerToken to sell
|
||||||
uint256 remainingTakerTokenFillAmount = safeSub(takerTokenFillAmount, totalFillResults.takerTokenFilledAmount);
|
uint256 remainingTakerTokenFillAmount = safeSub(takerTokenFillAmount, totalFillResults.takerTokenFilledAmount);
|
||||||
|
|
||||||
// Attempt to sell the remaining amount of takerToken
|
// Attempt to sell the remaining amount of takerToken
|
||||||
FillResults memory singleFillResults = fillOrder(
|
FillResults memory singleFillResults = fillOrder(
|
||||||
orders[i],
|
orders[i],
|
||||||
@ -270,7 +356,7 @@ contract MixinWrapperFunctions is
|
|||||||
|
|
||||||
// Calculate the remaining amount of takerToken to sell
|
// Calculate the remaining amount of takerToken to sell
|
||||||
uint256 remainingTakerTokenFillAmount = safeSub(takerTokenFillAmount, totalFillResults.takerTokenFilledAmount);
|
uint256 remainingTakerTokenFillAmount = safeSub(takerTokenFillAmount, totalFillResults.takerTokenFilledAmount);
|
||||||
|
|
||||||
// Attempt to sell the remaining amount of takerToken
|
// Attempt to sell the remaining amount of takerToken
|
||||||
FillResults memory singleFillResults = fillOrderNoThrow(
|
FillResults memory singleFillResults = fillOrderNoThrow(
|
||||||
orders[i],
|
orders[i],
|
||||||
@ -308,7 +394,7 @@ contract MixinWrapperFunctions is
|
|||||||
|
|
||||||
// Calculate the remaining amount of makerToken to buy
|
// Calculate the remaining amount of makerToken to buy
|
||||||
uint256 remainingMakerTokenFillAmount = safeSub(makerTokenFillAmount, totalFillResults.makerTokenFilledAmount);
|
uint256 remainingMakerTokenFillAmount = safeSub(makerTokenFillAmount, totalFillResults.makerTokenFilledAmount);
|
||||||
|
|
||||||
// Convert the remaining amount of makerToken to buy into remaining amount
|
// Convert the remaining amount of makerToken to buy into remaining amount
|
||||||
// of takerToken to sell, assuming entire amount can be sold in the current order
|
// of takerToken to sell, assuming entire amount can be sold in the current order
|
||||||
uint256 remainingTakerTokenFillAmount = getPartialAmount(
|
uint256 remainingTakerTokenFillAmount = getPartialAmount(
|
||||||
@ -405,5 +491,5 @@ contract MixinWrapperFunctions is
|
|||||||
totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);
|
totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);
|
||||||
totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);
|
totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
import "/zeppelin/contracts/token/ERC721/ERC721Token.sol";
|
||||||
|
import "../../utils/Ownable/Ownable.sol";
|
||||||
|
|
||||||
|
contract DummyERC721Token is
|
||||||
|
Ownable,
|
||||||
|
ERC721Token
|
||||||
|
{
|
||||||
|
function DummyERC721Token(
|
||||||
|
string name,
|
||||||
|
string symbol)
|
||||||
|
public
|
||||||
|
ERC721Token(name, symbol)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Internal 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
|
||||||
|
{
|
||||||
|
super._mint(to, tokenId);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
import "./IAuthorizable.sol";
|
||||||
|
import "../Ownable/Ownable.sol";
|
||||||
|
|
||||||
|
contract Authorizable is
|
||||||
|
Ownable,
|
||||||
|
IAuthorizable
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @dev Only authorized addresses can invoke functions with this modifier.
|
||||||
|
modifier onlyAuthorized {
|
||||||
|
require(authorized[msg.sender]);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier targetAuthorized(address target) {
|
||||||
|
require(authorized[target]);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier targetNotAuthorized(address target) {
|
||||||
|
require(!authorized[target]);
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping (address => bool) public authorized;
|
||||||
|
address[] public authorities;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// @dev Authorizes an address.
|
||||||
|
/// @param target Address to authorize.
|
||||||
|
function addAuthorizedAddress(address target)
|
||||||
|
public
|
||||||
|
onlyOwner
|
||||||
|
targetNotAuthorized(target)
|
||||||
|
{
|
||||||
|
authorized[target] = true;
|
||||||
|
authorities.push(target);
|
||||||
|
emit LogAuthorizedAddressAdded(target, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Removes authorizion of an address.
|
||||||
|
/// @param target Address to remove authorization from.
|
||||||
|
function removeAuthorizedAddress(address target)
|
||||||
|
public
|
||||||
|
onlyOwner
|
||||||
|
targetAuthorized(target)
|
||||||
|
{
|
||||||
|
delete authorized[target];
|
||||||
|
for (uint i = 0; i < authorities.length; i++) {
|
||||||
|
if (authorities[i] == target) {
|
||||||
|
authorities[i] = authorities[authorities.length - 1];
|
||||||
|
authorities.length -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit LogAuthorizedAddressRemoved(target, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Removes authorizion of an address.
|
||||||
|
/// @param target Address to remove authorization from.
|
||||||
|
/// @param index Index of target in authorities array.
|
||||||
|
function removeAuthorizedAddressAtIndex(address target, uint256 index)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
require(index < authorities.length);
|
||||||
|
require(authorities[index] == target);
|
||||||
|
delete authorized[target];
|
||||||
|
authorities[index] = authorities[authorities.length - 1];
|
||||||
|
authorities.length -= 1;
|
||||||
|
emit LogAuthorizedAddressRemoved(target, msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public constant functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// @dev Gets all authorized addresses.
|
||||||
|
/// @return Array of authorized addresses.
|
||||||
|
function getAuthorizedAddresses()
|
||||||
|
public
|
||||||
|
constant
|
||||||
|
returns (address[])
|
||||||
|
{
|
||||||
|
return authorities;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
/// @title TokenTransferProxy - Transfers tokens on behalf of contracts that have been approved via decentralized governance.
|
||||||
|
contract IAuthorizable {
|
||||||
|
|
||||||
|
/// @dev Gets all authorized addresses.
|
||||||
|
/// @return Array of authorized addresses.
|
||||||
|
function getAuthorizedAddresses()
|
||||||
|
public view
|
||||||
|
returns (address[]);
|
||||||
|
|
||||||
|
/// @dev Authorizes an address.
|
||||||
|
/// @param target Address to authorize.
|
||||||
|
function addAuthorizedAddress(address target)
|
||||||
|
public;
|
||||||
|
|
||||||
|
/// @dev Removes authorizion of an address.
|
||||||
|
/// @param target Address to remove authorization from.
|
||||||
|
function removeAuthorizedAddress(address target)
|
||||||
|
public;
|
||||||
|
|
||||||
|
/// @dev Removes authorizion of an address.
|
||||||
|
/// @param target Address to remove authorization from.
|
||||||
|
/// @param index Index of target in authorities array.
|
||||||
|
function removeAuthorizedAddressAtIndex(address target, uint256 index)
|
||||||
|
public;
|
||||||
|
|
||||||
|
event LogAuthorizedAddressAdded(
|
||||||
|
address indexed target,
|
||||||
|
address indexed caller);
|
||||||
|
|
||||||
|
event LogAuthorizedAddressRemoved(
|
||||||
|
address indexed target,
|
||||||
|
address indexed caller);
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
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.21;
|
||||||
|
|
||||||
|
contract LibBytes {
|
||||||
|
|
||||||
|
/// @dev Reads an address from a position in a byte array.
|
||||||
|
/// @param b Byte array containing an address.
|
||||||
|
/// @param index Index in byte array of address.
|
||||||
|
/// @return address from byte array.
|
||||||
|
function readAddress(
|
||||||
|
bytes b,
|
||||||
|
uint256 index)
|
||||||
|
public pure
|
||||||
|
returns (address result)
|
||||||
|
{
|
||||||
|
require(b.length >= index + 20); // 20 is length of address
|
||||||
|
|
||||||
|
// Add offset to index:
|
||||||
|
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
|
||||||
|
// 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
|
||||||
|
index += 20;
|
||||||
|
|
||||||
|
// Read address from array memory
|
||||||
|
assembly {
|
||||||
|
// 1. Add index to to address of bytes array
|
||||||
|
// 2. Load 32-byte word from memory
|
||||||
|
// 3. Apply 20-byte mask to obtain address
|
||||||
|
result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Writes an address into a specific position in a byte array.
|
||||||
|
/// @param input Address to put into byte array.
|
||||||
|
/// @param b Byte array to insert address into.
|
||||||
|
/// @param index Index in byte array of address.
|
||||||
|
function writeAddress(
|
||||||
|
address input,
|
||||||
|
bytes b,
|
||||||
|
uint256 index)
|
||||||
|
public pure
|
||||||
|
{
|
||||||
|
require(b.length >= index + 20); // 20 is length of address
|
||||||
|
|
||||||
|
// Add offset to index:
|
||||||
|
// 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
|
||||||
|
// 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
|
||||||
|
index += 20;
|
||||||
|
|
||||||
|
// Store address into array memory
|
||||||
|
assembly {
|
||||||
|
// The address occupies 20 bytes and mstore stores 32 bytes.
|
||||||
|
// First fetch the 32-byte word where we'll be storing the address, then
|
||||||
|
// apply a mask so we have only the bytes in the word that the address will not occupy.
|
||||||
|
// Then combine these bytes with the address and store the 32 bytes back to memory with mstore.
|
||||||
|
|
||||||
|
// 1. Add index to address of bytes array
|
||||||
|
// 2. Load 32-byte word from memory
|
||||||
|
// 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address
|
||||||
|
let neighbors := and(mload(add(b, index)), 0xffffffffffffffffffffffff0000000000000000000000000000000000000000)
|
||||||
|
|
||||||
|
// Store the neighbors and address into memory
|
||||||
|
mstore(add(b, index), xor(input, neighbors))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Reads a uint256 value from a position in a byte array.
|
||||||
|
/// @param b Byte array containing a uint256 value.
|
||||||
|
/// @param index Index in byte array of uint256 value.
|
||||||
|
/// @return uint256 value from byte array.
|
||||||
|
function readUint256(
|
||||||
|
bytes b,
|
||||||
|
uint256 index)
|
||||||
|
public pure
|
||||||
|
returns (uint256 result)
|
||||||
|
{
|
||||||
|
require(b.length >= index + 32);
|
||||||
|
|
||||||
|
// Arrays are prefixed by a 256 bit length parameter
|
||||||
|
index += 32;
|
||||||
|
|
||||||
|
// Read the uint256 from array memory
|
||||||
|
assembly {
|
||||||
|
result := mload(add(b, index))
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Writes a uint256 into a specific position in a byte array.
|
||||||
|
/// @param input uint256 to put into byte array.
|
||||||
|
/// @param b Byte array to insert <input> into.
|
||||||
|
/// @param index Index in byte array of <input>.
|
||||||
|
function writeUint256(
|
||||||
|
uint256 input,
|
||||||
|
bytes b,
|
||||||
|
uint256 index)
|
||||||
|
public pure
|
||||||
|
{
|
||||||
|
require(b.length >= index + 32);
|
||||||
|
|
||||||
|
// Arrays are prefixed by a 256 bit length parameter
|
||||||
|
index += 32;
|
||||||
|
|
||||||
|
// Read the uint256 from array memory
|
||||||
|
assembly {
|
||||||
|
mstore(add(b, index), input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
pragma solidity ^0.4.21;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ownable
|
||||||
|
*
|
||||||
|
* Base contract with an owner.
|
||||||
|
* Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
|
||||||
|
*/
|
||||||
|
|
||||||
|
contract IOwnable {
|
||||||
|
function transferOwnership(address newOwner)
|
||||||
|
public;
|
||||||
|
}
|
@ -7,7 +7,9 @@ pragma solidity ^0.4.21;
|
|||||||
* Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
|
* Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
contract Ownable {
|
import "../Ownable/IOwnable.sol";
|
||||||
|
|
||||||
|
contract Ownable is IOwnable {
|
||||||
address public owner;
|
address public owner;
|
||||||
|
|
||||||
function Ownable()
|
function Ownable()
|
||||||
|
67
packages/contracts/src/utils/asset_proxy_utils.ts
Normal file
67
packages/contracts/src/utils/asset_proxy_utils.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import * as Web3 from 'web3';
|
||||||
|
|
||||||
|
import { AssetProxyId } from './types';
|
||||||
|
const ethersUtils = require('ethers-utils');
|
||||||
|
|
||||||
|
export function zeroPad(value: string, width: number): string {
|
||||||
|
return '0'.repeat(width - value.length) + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeAssetProxyId(assetProxyId: AssetProxyId, encoded_metadata: { value: string }) {
|
||||||
|
encoded_metadata.value += zeroPad(new BigNumber(assetProxyId).toString(16), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeAddress(address: string, encoded_metadata: { value: string }) {
|
||||||
|
encoded_metadata.value += zeroPad(address.replace('0x', ''), 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeUint256(value: BigNumber, encoded_metadata: { value: string }) {
|
||||||
|
encoded_metadata.value += zeroPad(value.toString(16), 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeERC20ProxyMetadata_V1(tokenAddress: string) {
|
||||||
|
// Encode metadata
|
||||||
|
const encoded_metadata = { value: '0x' };
|
||||||
|
encodeAssetProxyId(AssetProxyId.ERC20_V1, encoded_metadata);
|
||||||
|
encodeAddress(tokenAddress, encoded_metadata);
|
||||||
|
|
||||||
|
// Verify encoding length - '0x' plus 21 bytes of encoded data
|
||||||
|
if (encoded_metadata.value.length != 44) {
|
||||||
|
throw Error('Bad encoding length. Expected 44, got ' + encoded_metadata.value.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return encoded metadata
|
||||||
|
return encoded_metadata.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeERC20ProxyMetadata(tokenAddress: string) {
|
||||||
|
// Encode metadata
|
||||||
|
const encoded_metadata = { value: '0x' };
|
||||||
|
encodeAssetProxyId(AssetProxyId.ERC20, encoded_metadata);
|
||||||
|
encodeAddress(tokenAddress, encoded_metadata);
|
||||||
|
|
||||||
|
// Verify encoding length - '0x' plus 21 bytes of encoded data
|
||||||
|
if (encoded_metadata.value.length != 44) {
|
||||||
|
throw Error('Bad encoding length. Expected 44, got ' + encoded_metadata.value.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return encoded metadata
|
||||||
|
return encoded_metadata.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function encodeERC721ProxyMetadata(tokenAddress: string, tokenId: BigNumber) {
|
||||||
|
// Encode metadata
|
||||||
|
const encoded_metadata = { value: '0x' };
|
||||||
|
encodeAssetProxyId(AssetProxyId.ERC721, encoded_metadata);
|
||||||
|
encodeAddress(tokenAddress, encoded_metadata);
|
||||||
|
encodeUint256(tokenId, encoded_metadata);
|
||||||
|
|
||||||
|
// Verify encoding length - '0x' plus 53 bytes of encoded data
|
||||||
|
if (encoded_metadata.value.length != 108) {
|
||||||
|
throw Error('Bad encoding length. Expected 108, got ' + encoded_metadata.value.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return encoded metadata
|
||||||
|
return encoded_metadata.value;
|
||||||
|
}
|
@ -26,5 +26,6 @@ export const constants = {
|
|||||||
MAX_TOKEN_TRANSFERFROM_GAS: 80000,
|
MAX_TOKEN_TRANSFERFROM_GAS: 80000,
|
||||||
MAX_TOKEN_APPROVE_GAS: 60000,
|
MAX_TOKEN_APPROVE_GAS: 60000,
|
||||||
DUMMY_TOKEN_ARGS: [DUMMY_TOKEN_NAME, DUMMY_TOKEN_SYMBOL, DUMMY_TOKEN_DECIMALS, DUMMY_TOKEN_TOTAL_SUPPLY],
|
DUMMY_TOKEN_ARGS: [DUMMY_TOKEN_NAME, DUMMY_TOKEN_SYMBOL, DUMMY_TOKEN_DECIMALS, DUMMY_TOKEN_TOTAL_SUPPLY],
|
||||||
|
DUMMY_ERC721TOKEN_ARGS: [DUMMY_TOKEN_NAME, DUMMY_TOKEN_SYMBOL],
|
||||||
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
|
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
|
||||||
};
|
};
|
||||||
|
@ -31,6 +31,8 @@ export const crypto = {
|
|||||||
argTypes.push('address');
|
argTypes.push('address');
|
||||||
} else if (_.isString(arg)) {
|
} else if (_.isString(arg)) {
|
||||||
argTypes.push('string');
|
argTypes.push('string');
|
||||||
|
} else if (arg instanceof Buffer) {
|
||||||
|
argTypes.push('bytes');
|
||||||
} else if (_.isBoolean(arg)) {
|
} else if (_.isBoolean(arg)) {
|
||||||
argTypes.push('bool');
|
argTypes.push('bool');
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,6 +35,8 @@ export const orderUtils = {
|
|||||||
takerFee: signedOrder.takerFee,
|
takerFee: signedOrder.takerFee,
|
||||||
expirationTimeSeconds: signedOrder.expirationTimeSeconds,
|
expirationTimeSeconds: signedOrder.expirationTimeSeconds,
|
||||||
salt: signedOrder.salt,
|
salt: signedOrder.salt,
|
||||||
|
makerAssetProxyData: signedOrder.makerAssetProxyData,
|
||||||
|
takerAssetProxyData: signedOrder.takerAssetProxyData,
|
||||||
};
|
};
|
||||||
return orderStruct;
|
return orderStruct;
|
||||||
},
|
},
|
||||||
@ -52,6 +54,8 @@ export const orderUtils = {
|
|||||||
'uint256 takerFee',
|
'uint256 takerFee',
|
||||||
'uint256 expirationTimeSeconds',
|
'uint256 expirationTimeSeconds',
|
||||||
'uint256 salt',
|
'uint256 salt',
|
||||||
|
'bytes makerAssetProxyData',
|
||||||
|
'bytes takerAssetProxyData',
|
||||||
]);
|
]);
|
||||||
const orderParamsHashBuff = crypto.solSHA3([
|
const orderParamsHashBuff = crypto.solSHA3([
|
||||||
order.exchangeAddress,
|
order.exchangeAddress,
|
||||||
@ -66,6 +70,8 @@ export const orderUtils = {
|
|||||||
order.takerFee,
|
order.takerFee,
|
||||||
order.expirationTimeSeconds,
|
order.expirationTimeSeconds,
|
||||||
order.salt,
|
order.salt,
|
||||||
|
ethUtil.toBuffer(order.makerAssetProxyData),
|
||||||
|
ethUtil.toBuffer(order.takerAssetProxyData),
|
||||||
]);
|
]);
|
||||||
const orderSchemaHashHex = `0x${orderSchemaHashBuff.toString('hex')}`;
|
const orderSchemaHashHex = `0x${orderSchemaHashBuff.toString('hex')}`;
|
||||||
const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`;
|
const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`;
|
||||||
|
@ -37,6 +37,13 @@ export interface CancelOrdersBefore {
|
|||||||
salt: BigNumber;
|
salt: BigNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AssetProxyId {
|
||||||
|
INVALID,
|
||||||
|
ERC20_V1,
|
||||||
|
ERC20,
|
||||||
|
ERC721,
|
||||||
|
}
|
||||||
|
|
||||||
export interface DefaultOrderParams {
|
export interface DefaultOrderParams {
|
||||||
exchangeAddress: string;
|
exchangeAddress: string;
|
||||||
makerAddress: string;
|
makerAddress: string;
|
||||||
@ -45,8 +52,10 @@ export interface DefaultOrderParams {
|
|||||||
takerTokenAddress: string;
|
takerTokenAddress: string;
|
||||||
makerTokenAmount: BigNumber;
|
makerTokenAmount: BigNumber;
|
||||||
takerTokenAmount: BigNumber;
|
takerTokenAmount: BigNumber;
|
||||||
makerFee: BigNumber;
|
makerFeeAmount: BigNumber;
|
||||||
takerFee: BigNumber;
|
takerFeeAmount: BigNumber;
|
||||||
|
makerAssetProxyData: string;
|
||||||
|
takerAssetProxyData: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionDataParams {
|
export interface TransactionDataParams {
|
||||||
@ -100,6 +109,11 @@ export enum ContractName {
|
|||||||
AccountLevels = 'AccountLevels',
|
AccountLevels = 'AccountLevels',
|
||||||
EtherDelta = 'EtherDelta',
|
EtherDelta = 'EtherDelta',
|
||||||
Arbitrage = 'Arbitrage',
|
Arbitrage = 'Arbitrage',
|
||||||
|
AssetProxyDispatcher = 'AssetProxyDispatcher',
|
||||||
|
ERC20TransferProxy = 'ERC20TransferProxy',
|
||||||
|
ERC20TransferProxy_V1 = 'ERC20TransferProxy_v1',
|
||||||
|
ERC721TransferProxy = 'ERC721TransferProxy',
|
||||||
|
DummyERC721Token = 'DummyERC721Token',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Artifact {
|
export interface Artifact {
|
||||||
@ -134,6 +148,8 @@ export interface OrderStruct {
|
|||||||
takerFee: BigNumber;
|
takerFee: BigNumber;
|
||||||
expirationTimeSeconds: BigNumber;
|
expirationTimeSeconds: BigNumber;
|
||||||
salt: BigNumber;
|
salt: BigNumber;
|
||||||
|
makerAssetProxyData: string;
|
||||||
|
takerAssetProxyData: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UnsignedOrder extends OrderStruct {
|
export interface UnsignedOrder extends OrderStruct {
|
||||||
|
339
packages/contracts/test/asset_proxy_dispatcher/dispatcher.ts
Normal file
339
packages/contracts/test/asset_proxy_dispatcher/dispatcher.ts
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js';
|
||||||
|
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import * as Web3 from 'web3';
|
||||||
|
|
||||||
|
import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher';
|
||||||
|
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
|
||||||
|
import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token';
|
||||||
|
import { ERC20TransferProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_transfer_proxy';
|
||||||
|
import { ERC721TransferProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_transfer_proxy';
|
||||||
|
import { ERC20TransferProxy_v1Contract } from '../../src/contract_wrappers/generated/erc20transferproxy_v1';
|
||||||
|
import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy';
|
||||||
|
import {
|
||||||
|
encodeERC20ProxyMetadata,
|
||||||
|
encodeERC20ProxyMetadata_V1,
|
||||||
|
encodeERC721ProxyMetadata,
|
||||||
|
} from '../../src/utils/asset_proxy_utils';
|
||||||
|
import { Balances } from '../../src/utils/balances';
|
||||||
|
import { constants } from '../../src/utils/constants';
|
||||||
|
import { AssetProxyId, ContractName } from '../../src/utils/types';
|
||||||
|
import { chaiSetup } from '../utils/chai_setup';
|
||||||
|
import { deployer } from '../utils/deployer';
|
||||||
|
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
|
describe('AssetProxyDispatcher', () => {
|
||||||
|
let owner: string;
|
||||||
|
let notOwner: string;
|
||||||
|
let assetProxyManagerAddress: string;
|
||||||
|
let tokenOwner: string;
|
||||||
|
let makerAddress: string;
|
||||||
|
let takerAddress: string;
|
||||||
|
let zrx: DummyTokenContract;
|
||||||
|
let dmyBalances: Balances;
|
||||||
|
let tokenTransferProxy: TokenTransferProxyContract;
|
||||||
|
let assetProxyDispatcher: AssetProxyDispatcherContract;
|
||||||
|
let erc20TransferProxyV1: ERC20TransferProxy_v1Contract;
|
||||||
|
let erc20TransferProxy: ERC20TransferProxyContract;
|
||||||
|
let erc721TransferProxy: ERC721TransferProxyContract;
|
||||||
|
const nilAddress = '0x0000000000000000000000000000000000000000';
|
||||||
|
const INITIAL_BALANCE = new BigNumber(10000);
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
owner = tokenOwner = accounts[0];
|
||||||
|
notOwner = accounts[1];
|
||||||
|
assetProxyManagerAddress = accounts[2];
|
||||||
|
makerAddress = accounts[3];
|
||||||
|
takerAddress = accounts[4];
|
||||||
|
const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy);
|
||||||
|
tokenTransferProxy = new TokenTransferProxyContract(
|
||||||
|
tokenTransferProxyInstance.abi,
|
||||||
|
tokenTransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const erc20TransferProxyV1Instance = await deployer.deployAsync(ContractName.ERC20TransferProxy_V1, [
|
||||||
|
tokenTransferProxy.address,
|
||||||
|
]);
|
||||||
|
erc20TransferProxyV1 = new ERC20TransferProxy_v1Contract(
|
||||||
|
erc20TransferProxyV1Instance.abi,
|
||||||
|
erc20TransferProxyV1Instance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const erc20TransferProxyInstance = await deployer.deployAsync(ContractName.ERC20TransferProxy);
|
||||||
|
erc20TransferProxy = new ERC20TransferProxyContract(
|
||||||
|
erc20TransferProxyInstance.abi,
|
||||||
|
erc20TransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const erc721TransferProxyInstance = await deployer.deployAsync(ContractName.ERC721TransferProxy);
|
||||||
|
erc721TransferProxy = new ERC721TransferProxyContract(
|
||||||
|
erc721TransferProxyInstance.abi,
|
||||||
|
erc721TransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.AssetProxyDispatcher);
|
||||||
|
assetProxyDispatcher = new AssetProxyDispatcherContract(
|
||||||
|
assetProxyDispatcherInstance.abi,
|
||||||
|
assetProxyDispatcherInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const zrxInstance = await deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS);
|
||||||
|
zrx = new DummyTokenContract(zrxInstance.abi, zrxInstance.address, provider);
|
||||||
|
await zrx.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner });
|
||||||
|
await zrx.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner });
|
||||||
|
dmyBalances = new Balances([zrx], [makerAddress, takerAddress]);
|
||||||
|
await zrx.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_BALANCE, {
|
||||||
|
from: takerAddress,
|
||||||
|
});
|
||||||
|
await zrx.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_BALANCE, {
|
||||||
|
from: makerAddress,
|
||||||
|
});
|
||||||
|
|
||||||
|
await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(assetProxyManagerAddress, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc20TransferProxyV1.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc20TransferProxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc721TransferProxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(erc20TransferProxyV1.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
beforeEach(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('setAssetProxy', () => {
|
||||||
|
it('should record proxy upon registration', async () => {
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(proxyAddress).to.be.equal(erc20TransferProxy.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to record multiple proxies', async () => {
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
let proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(proxyAddress).to.be.equal(erc20TransferProxy.address);
|
||||||
|
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC721,
|
||||||
|
erc721TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC721);
|
||||||
|
expect(proxyAddress).to.be.equal(erc721TransferProxy.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should replace proxy address upon re-registration', async () => {
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
let proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(proxyAddress).to.be.equal(erc20TransferProxy.address);
|
||||||
|
|
||||||
|
// Deploy a new version of the ERC20 Transfer Proxy contract
|
||||||
|
const newErc20TransferProxyInstance = await deployer.deployAsync(ContractName.ERC20TransferProxy);
|
||||||
|
const newErc20TransferProxy = new ERC20TransferProxyContract(
|
||||||
|
newErc20TransferProxyInstance.abi,
|
||||||
|
newErc20TransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const newAddress = newErc20TransferProxy.address;
|
||||||
|
const currentAddress = erc20TransferProxy.address;
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
newAddress,
|
||||||
|
currentAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(proxyAddress).to.be.equal(newAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if registering with incorrect "old_address" field', async () => {
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(proxyAddress).to.be.equal(erc20TransferProxy.address);
|
||||||
|
|
||||||
|
// The following transaction will throw because the currentAddress is no longer nilAddress
|
||||||
|
return expect(
|
||||||
|
assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to reset proxy address to NULL', async () => {
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(proxyAddress).to.be.equal(erc20TransferProxy.address);
|
||||||
|
|
||||||
|
// The following transaction will reset the proxy address
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
nilAddress,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
const newProxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(newProxyAddress).to.be.equal(nilAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if requesting address is not authorized', async () => {
|
||||||
|
return expect(
|
||||||
|
assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: notOwner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getAssetProxy', () => {
|
||||||
|
it('should return correct address of registered proxy', async () => {
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(proxyAddress).to.be.equal(erc20TransferProxy.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return NULL address if requesting non-existent proxy', async () => {
|
||||||
|
const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20);
|
||||||
|
expect(proxyAddress).to.be.equal(nilAddress);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('transferFrom', () => {
|
||||||
|
it('should dispatch transfer to registered proxy', async () => {
|
||||||
|
// Register ERC20 proxy
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC20ProxyMetadata(zrx.address);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(10);
|
||||||
|
await assetProxyDispatcher.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: assetProxyManagerAddress },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify transfer was successful
|
||||||
|
const newBalances = await dmyBalances.getAsync();
|
||||||
|
expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[makerAddress][zrx.address].minus(amount),
|
||||||
|
);
|
||||||
|
expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[takerAddress][zrx.address].add(amount),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if delegating to unregistered proxy', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC20ProxyMetadata(zrx.address);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(10);
|
||||||
|
return expect(
|
||||||
|
assetProxyDispatcher.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: notOwner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if requesting address is not authorized', async () => {
|
||||||
|
// Register ERC20 proxy
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: owner },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC20ProxyMetadata(zrx.address);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(10);
|
||||||
|
return expect(
|
||||||
|
assetProxyDispatcher.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: notOwner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
407
packages/contracts/test/asset_proxy_dispatcher/proxies.ts
Normal file
407
packages/contracts/test/asset_proxy_dispatcher/proxies.ts
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js';
|
||||||
|
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
|
||||||
|
import { BigNumber } from '@0xproject/utils';
|
||||||
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
|
import * as chai from 'chai';
|
||||||
|
import * as Web3 from 'web3';
|
||||||
|
|
||||||
|
import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher';
|
||||||
|
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
|
||||||
|
import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token';
|
||||||
|
import { ERC20TransferProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_transfer_proxy';
|
||||||
|
import { ERC721TransferProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_transfer_proxy';
|
||||||
|
import { ERC20TransferProxy_v1Contract } from '../../src/contract_wrappers/generated/erc20transferproxy_v1';
|
||||||
|
import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy';
|
||||||
|
import {
|
||||||
|
encodeERC20ProxyMetadata,
|
||||||
|
encodeERC20ProxyMetadata_V1,
|
||||||
|
encodeERC721ProxyMetadata,
|
||||||
|
} from '../../src/utils/asset_proxy_utils';
|
||||||
|
import { Balances } from '../../src/utils/balances';
|
||||||
|
import { constants } from '../../src/utils/constants';
|
||||||
|
import { AssetProxyId, ContractName } from '../../src/utils/types';
|
||||||
|
import { chaiSetup } from '../utils/chai_setup';
|
||||||
|
import { deployer } from '../utils/deployer';
|
||||||
|
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
||||||
|
|
||||||
|
chaiSetup.configure();
|
||||||
|
const expect = chai.expect;
|
||||||
|
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||||
|
|
||||||
|
describe('Asset Transfer Proxies', () => {
|
||||||
|
let owner: string;
|
||||||
|
let notOwner: string;
|
||||||
|
let assetProxyManagerAddress: string;
|
||||||
|
let tokenOwner: string;
|
||||||
|
let makerAddress: string;
|
||||||
|
let takerAddress: string;
|
||||||
|
let zrx: DummyTokenContract;
|
||||||
|
let ck: DummyERC721TokenContract;
|
||||||
|
let dmyBalances: Balances;
|
||||||
|
let tokenTransferProxy: TokenTransferProxyContract;
|
||||||
|
let assetProxyDispatcher: AssetProxyDispatcherContract;
|
||||||
|
let erc20TransferProxyV1: ERC20TransferProxy_v1Contract;
|
||||||
|
let erc20TransferProxy: ERC20TransferProxyContract;
|
||||||
|
let erc721TransferProxy: ERC721TransferProxyContract;
|
||||||
|
const nilAddress = '0x0000000000000000000000000000000000000000';
|
||||||
|
const makerTokenId = new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010');
|
||||||
|
const INITIAL_BALANCE = new BigNumber(10000);
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
|
owner = tokenOwner = accounts[0];
|
||||||
|
notOwner = accounts[1];
|
||||||
|
assetProxyManagerAddress = accounts[2];
|
||||||
|
makerAddress = accounts[3];
|
||||||
|
takerAddress = accounts[4];
|
||||||
|
const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy);
|
||||||
|
tokenTransferProxy = new TokenTransferProxyContract(
|
||||||
|
tokenTransferProxyInstance.abi,
|
||||||
|
tokenTransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const erc20TransferProxyV1Instance = await deployer.deployAsync(ContractName.ERC20TransferProxy_V1, [
|
||||||
|
tokenTransferProxy.address,
|
||||||
|
]);
|
||||||
|
erc20TransferProxyV1 = new ERC20TransferProxy_v1Contract(
|
||||||
|
erc20TransferProxyV1Instance.abi,
|
||||||
|
erc20TransferProxyV1Instance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const erc20TransferProxyInstance = await deployer.deployAsync(ContractName.ERC20TransferProxy);
|
||||||
|
erc20TransferProxy = new ERC20TransferProxyContract(
|
||||||
|
erc20TransferProxyInstance.abi,
|
||||||
|
erc20TransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const erc721TransferProxyInstance = await deployer.deployAsync(ContractName.ERC721TransferProxy);
|
||||||
|
erc721TransferProxy = new ERC721TransferProxyContract(
|
||||||
|
erc721TransferProxyInstance.abi,
|
||||||
|
erc721TransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.AssetProxyDispatcher);
|
||||||
|
assetProxyDispatcher = new AssetProxyDispatcherContract(
|
||||||
|
assetProxyDispatcherInstance.abi,
|
||||||
|
assetProxyDispatcherInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const zrxInstance = await deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS);
|
||||||
|
zrx = new DummyTokenContract(zrxInstance.abi, zrxInstance.address, provider);
|
||||||
|
await zrx.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner });
|
||||||
|
await zrx.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner });
|
||||||
|
dmyBalances = new Balances([zrx], [makerAddress, takerAddress]);
|
||||||
|
await zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_BALANCE, {
|
||||||
|
from: takerAddress,
|
||||||
|
});
|
||||||
|
await zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_BALANCE, {
|
||||||
|
from: makerAddress,
|
||||||
|
});
|
||||||
|
await zrx.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_BALANCE, {
|
||||||
|
from: takerAddress,
|
||||||
|
});
|
||||||
|
await zrx.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_BALANCE, {
|
||||||
|
from: makerAddress,
|
||||||
|
});
|
||||||
|
|
||||||
|
const ckInstance = await deployer.deployAsync(ContractName.DummyERC721Token, constants.DUMMY_ERC721TOKEN_ARGS);
|
||||||
|
ck = new DummyERC721TokenContract(ckInstance.abi, ckInstance.address, provider);
|
||||||
|
await ck.setApprovalForAll.sendTransactionAsync(erc721TransferProxy.address, true, { from: makerAddress });
|
||||||
|
await ck.setApprovalForAll.sendTransactionAsync(erc721TransferProxy.address, true, { from: takerAddress });
|
||||||
|
await ck.mint.sendTransactionAsync(makerAddress, makerTokenId, { from: tokenOwner });
|
||||||
|
await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(assetProxyManagerAddress, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc20TransferProxyV1.addAuthorizedAddress.sendTransactionAsync(assetProxyManagerAddress, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc20TransferProxy.addAuthorizedAddress.sendTransactionAsync(assetProxyManagerAddress, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc721TransferProxy.addAuthorizedAddress.sendTransactionAsync(assetProxyManagerAddress, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(erc20TransferProxyV1.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
beforeEach(async () => {
|
||||||
|
await blockchainLifecycle.startAsync();
|
||||||
|
});
|
||||||
|
afterEach(async () => {
|
||||||
|
await blockchainLifecycle.revertAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Transfer Proxy - ERC20_V1', () => {
|
||||||
|
it('should successfully encode/decode metadata', async () => {
|
||||||
|
const metadata = await erc20TransferProxyV1.encodeMetadata.callAsync(AssetProxyId.ERC20_V1, zrx.address);
|
||||||
|
const address = await erc20TransferProxyV1.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(zrx.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully decode metadata encoded by typescript helpers', async () => {
|
||||||
|
const metadata = encodeERC20ProxyMetadata_V1(zrx.address);
|
||||||
|
const address = await erc20TransferProxyV1.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(zrx.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully encode/decode metadata padded with zeros', async () => {
|
||||||
|
const testAddress = '0x0000000000000000056000000000000000000010';
|
||||||
|
const metadata = await erc20TransferProxyV1.encodeMetadata.callAsync(AssetProxyId.ERC20_V1, testAddress);
|
||||||
|
const address = await erc20TransferProxyV1.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(testAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully decode metadata encoded padded with zeros by typescript helpers', async () => {
|
||||||
|
const testAddress = '0x0000000000000000056000000000000000000010';
|
||||||
|
const metadata = encodeERC20ProxyMetadata_V1(testAddress);
|
||||||
|
const address = await erc20TransferProxyV1.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(testAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully transfer tokens', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC20ProxyMetadata_V1(zrx.address);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(10);
|
||||||
|
await erc20TransferProxyV1.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: assetProxyManagerAddress },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify transfer was successful
|
||||||
|
const newBalances = await dmyBalances.getAsync();
|
||||||
|
expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[makerAddress][zrx.address].minus(amount),
|
||||||
|
);
|
||||||
|
expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[takerAddress][zrx.address].add(amount),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if requesting address is not authorized', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC20ProxyMetadata_V1(zrx.address);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(10);
|
||||||
|
expect(
|
||||||
|
erc20TransferProxyV1.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: notOwner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Transfer Proxy - ERC20', () => {
|
||||||
|
it('should successfully encode/decode metadata', async () => {
|
||||||
|
const metadata = await erc20TransferProxy.encodeMetadata.callAsync(AssetProxyId.ERC20, zrx.address);
|
||||||
|
const address = await erc20TransferProxy.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(zrx.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully decode metadata encoded by typescript helpers', async () => {
|
||||||
|
const metadata = encodeERC20ProxyMetadata(zrx.address);
|
||||||
|
const address = await erc20TransferProxy.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(zrx.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully encode/decode metadata padded with zeros', async () => {
|
||||||
|
const testAddress = '0x0000000000000000056000000000000000000010';
|
||||||
|
const metadata = await erc20TransferProxy.encodeMetadata.callAsync(AssetProxyId.ERC20, testAddress);
|
||||||
|
const address = await erc20TransferProxy.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(testAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully decode metadata encoded padded with zeros by typescript helpers', async () => {
|
||||||
|
const testAddress = '0x0000000000000000056000000000000000000010';
|
||||||
|
const metadata = encodeERC20ProxyMetadata(testAddress);
|
||||||
|
const address = await erc20TransferProxy.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(testAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully transfer tokens', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC20ProxyMetadata(zrx.address);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(10);
|
||||||
|
await erc20TransferProxy.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: assetProxyManagerAddress },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify transfer was successful
|
||||||
|
const newBalances = await dmyBalances.getAsync();
|
||||||
|
expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[makerAddress][zrx.address].minus(amount),
|
||||||
|
);
|
||||||
|
expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[takerAddress][zrx.address].add(amount),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if requesting address is not authorized', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC20ProxyMetadata(zrx.address);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(10);
|
||||||
|
expect(
|
||||||
|
erc20TransferProxy.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: notOwner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Transfer Proxy - ERC721', () => {
|
||||||
|
it('should successfully encode/decode metadata', async () => {
|
||||||
|
const metadata = await erc721TransferProxy.encodeMetadata.callAsync(
|
||||||
|
AssetProxyId.ERC721,
|
||||||
|
ck.address,
|
||||||
|
makerTokenId,
|
||||||
|
);
|
||||||
|
const [address, tokenId] = await erc721TransferProxy.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(ck.address);
|
||||||
|
expect(tokenId).to.be.bignumber.equal(makerTokenId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully decode metadata encoded by typescript helpers', async () => {
|
||||||
|
const metadata = encodeERC721ProxyMetadata(ck.address, makerTokenId);
|
||||||
|
const [address, tokenId] = await erc721TransferProxy.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(ck.address);
|
||||||
|
expect(tokenId).to.be.bignumber.equal(makerTokenId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully encode/decode metadata padded with zeros', async () => {
|
||||||
|
const testAddress = '0x0000000000000000056000000000000000000010';
|
||||||
|
const metadata = await erc721TransferProxy.encodeMetadata.callAsync(
|
||||||
|
AssetProxyId.ERC721,
|
||||||
|
testAddress,
|
||||||
|
makerTokenId,
|
||||||
|
);
|
||||||
|
const [address, tokenId] = await erc721TransferProxy.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(testAddress);
|
||||||
|
expect(tokenId).to.be.bignumber.equal(makerTokenId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully decode metadata encoded padded with zeros by typescript helpers', async () => {
|
||||||
|
const testAddress = '0x0000000000000000056000000000000000000010';
|
||||||
|
const metadata = encodeERC721ProxyMetadata(testAddress, makerTokenId);
|
||||||
|
const [address, tokenId] = await erc721TransferProxy.decodeMetadata.callAsync(metadata);
|
||||||
|
expect(address).to.be.equal(testAddress);
|
||||||
|
expect(tokenId).to.be.bignumber.equal(makerTokenId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully transfer tokens', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC721ProxyMetadata(ck.address, makerTokenId);
|
||||||
|
|
||||||
|
// Verify pre-condition
|
||||||
|
const ownerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(ownerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(1);
|
||||||
|
await erc721TransferProxy.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: assetProxyManagerAddress },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify transfer was successful
|
||||||
|
const newOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(newOwnerMakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if transferring 0 amount of a token', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC721ProxyMetadata(ck.address, makerTokenId);
|
||||||
|
|
||||||
|
// Verify pre-condition
|
||||||
|
const ownerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(ownerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(0);
|
||||||
|
expect(
|
||||||
|
erc20TransferProxy.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: notOwner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if transferring >1 amount of a token', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC721ProxyMetadata(ck.address, makerTokenId);
|
||||||
|
|
||||||
|
// Verify pre-condition
|
||||||
|
const ownerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(ownerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(500);
|
||||||
|
expect(
|
||||||
|
erc20TransferProxy.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: notOwner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw if requesting address is not authorized', async () => {
|
||||||
|
// Construct metadata for ERC20 proxy
|
||||||
|
const encodedProxyMetadata = encodeERC721ProxyMetadata(zrx.address, makerTokenId);
|
||||||
|
|
||||||
|
// Perform a transfer from makerAddress to takerAddress
|
||||||
|
const balances = await dmyBalances.getAsync();
|
||||||
|
const amount = new BigNumber(1);
|
||||||
|
expect(
|
||||||
|
erc20TransferProxy.transferFrom.sendTransactionAsync(
|
||||||
|
encodedProxyMetadata,
|
||||||
|
makerAddress,
|
||||||
|
takerAddress,
|
||||||
|
amount,
|
||||||
|
{ from: notOwner },
|
||||||
|
),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,13 +1,18 @@
|
|||||||
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js';
|
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs, ZeroEx } from '0x.js';
|
||||||
|
|
||||||
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
|
import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
|
||||||
import { BigNumber } from '@0xproject/utils';
|
import { BigNumber } from '@0xproject/utils';
|
||||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||||
import * as chai from 'chai';
|
import * as chai from 'chai';
|
||||||
import ethUtil = require('ethereumjs-util');
|
import ethUtil = require('ethereumjs-util');
|
||||||
|
import * as _ from 'lodash';
|
||||||
import * as Web3 from 'web3';
|
import * as Web3 from 'web3';
|
||||||
|
|
||||||
|
import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher';
|
||||||
|
import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token';
|
||||||
import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token';
|
import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token';
|
||||||
|
import { ERC20TransferProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_transfer_proxy';
|
||||||
|
import { ERC721TransferProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_transfer_proxy';
|
||||||
|
import { ERC20TransferProxy_v1Contract } from '../../src/contract_wrappers/generated/erc20transferproxy_v1';
|
||||||
import {
|
import {
|
||||||
CancelContractEventArgs,
|
CancelContractEventArgs,
|
||||||
ExchangeContract,
|
ExchangeContract,
|
||||||
@ -15,13 +20,25 @@ import {
|
|||||||
FillContractEventArgs,
|
FillContractEventArgs,
|
||||||
} from '../../src/contract_wrappers/generated/exchange';
|
} from '../../src/contract_wrappers/generated/exchange';
|
||||||
import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy';
|
import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy';
|
||||||
|
import {
|
||||||
|
encodeERC20ProxyMetadata,
|
||||||
|
encodeERC20ProxyMetadata_V1,
|
||||||
|
encodeERC721ProxyMetadata,
|
||||||
|
} from '../../src/utils/asset_proxy_utils';
|
||||||
import { Balances } from '../../src/utils/balances';
|
import { Balances } from '../../src/utils/balances';
|
||||||
import { constants } from '../../src/utils/constants';
|
import { constants } from '../../src/utils/constants';
|
||||||
import { crypto } from '../../src/utils/crypto';
|
import { crypto } from '../../src/utils/crypto';
|
||||||
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||||
import { OrderFactory } from '../../src/utils/order_factory';
|
import { OrderFactory } from '../../src/utils/order_factory';
|
||||||
import { orderUtils } from '../../src/utils/order_utils';
|
import { orderUtils } from '../../src/utils/order_utils';
|
||||||
import { BalancesByOwner, ContractName, ExchangeContractErrs, SignatureType, SignedOrder } from '../../src/utils/types';
|
import {
|
||||||
|
AssetProxyId,
|
||||||
|
BalancesByOwner,
|
||||||
|
ContractName,
|
||||||
|
ExchangeContractErrs,
|
||||||
|
SignatureType,
|
||||||
|
SignedOrder,
|
||||||
|
} from '../../src/utils/types';
|
||||||
import { chaiSetup } from '../utils/chai_setup';
|
import { chaiSetup } from '../utils/chai_setup';
|
||||||
import { deployer } from '../utils/deployer';
|
import { deployer } from '../utils/deployer';
|
||||||
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
||||||
@ -35,14 +52,21 @@ describe('Exchange', () => {
|
|||||||
let tokenOwner: string;
|
let tokenOwner: string;
|
||||||
let takerAddress: string;
|
let takerAddress: string;
|
||||||
let feeRecipientAddress: string;
|
let feeRecipientAddress: string;
|
||||||
|
let assetProxyManagerAddress: string;
|
||||||
const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||||
const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||||
|
|
||||||
let rep: DummyTokenContract;
|
let rep: DummyTokenContract;
|
||||||
let dgd: DummyTokenContract;
|
let dgd: DummyTokenContract;
|
||||||
let zrx: DummyTokenContract;
|
let zrx: DummyTokenContract;
|
||||||
|
let ck: DummyERC721TokenContract;
|
||||||
|
let et: DummyERC721TokenContract;
|
||||||
let exchange: ExchangeContract;
|
let exchange: ExchangeContract;
|
||||||
let tokenTransferProxy: TokenTransferProxyContract;
|
let tokenTransferProxy: TokenTransferProxyContract;
|
||||||
|
let assetProxyDispatcher: AssetProxyDispatcherContract;
|
||||||
|
let erc20TransferProxyV1: ERC20TransferProxy_v1Contract;
|
||||||
|
let erc20TransferProxy: ERC20TransferProxyContract;
|
||||||
|
let erc721TransferProxy: ERC721TransferProxyContract;
|
||||||
|
|
||||||
let signedOrder: SignedOrder;
|
let signedOrder: SignedOrder;
|
||||||
let balances: BalancesByOwner;
|
let balances: BalancesByOwner;
|
||||||
@ -50,32 +74,104 @@ describe('Exchange', () => {
|
|||||||
let dmyBalances: Balances;
|
let dmyBalances: Balances;
|
||||||
let orderFactory: OrderFactory;
|
let orderFactory: OrderFactory;
|
||||||
|
|
||||||
|
let erc721TransferProxyInstance: Web3.ContractInstance;
|
||||||
|
|
||||||
let zeroEx: ZeroEx;
|
let zeroEx: ZeroEx;
|
||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
makerAddress = accounts[0];
|
makerAddress = accounts[0];
|
||||||
[tokenOwner, takerAddress, feeRecipientAddress] = accounts;
|
[tokenOwner, takerAddress, feeRecipientAddress, assetProxyManagerAddress] = accounts;
|
||||||
const [repInstance, dgdInstance, zrxInstance] = await Promise.all([
|
const [repInstance, dgdInstance, zrxInstance, ckInstance, etInstance] = await Promise.all([
|
||||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||||
|
deployer.deployAsync(ContractName.DummyERC721Token, constants.DUMMY_ERC721TOKEN_ARGS),
|
||||||
|
deployer.deployAsync(ContractName.DummyERC721Token, constants.DUMMY_ERC721TOKEN_ARGS),
|
||||||
]);
|
]);
|
||||||
rep = new DummyTokenContract(repInstance.abi, repInstance.address, provider);
|
rep = new DummyTokenContract(repInstance.abi, repInstance.address, provider);
|
||||||
dgd = new DummyTokenContract(dgdInstance.abi, dgdInstance.address, provider);
|
dgd = new DummyTokenContract(dgdInstance.abi, dgdInstance.address, provider);
|
||||||
zrx = new DummyTokenContract(zrxInstance.abi, zrxInstance.address, provider);
|
zrx = new DummyTokenContract(zrxInstance.abi, zrxInstance.address, provider);
|
||||||
|
ck = new DummyERC721TokenContract(ckInstance.abi, ckInstance.address, provider);
|
||||||
|
et = new DummyERC721TokenContract(etInstance.abi, etInstance.address, provider);
|
||||||
const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy);
|
const tokenTransferProxyInstance = await deployer.deployAsync(ContractName.TokenTransferProxy);
|
||||||
tokenTransferProxy = new TokenTransferProxyContract(
|
tokenTransferProxy = new TokenTransferProxyContract(
|
||||||
tokenTransferProxyInstance.abi,
|
tokenTransferProxyInstance.abi,
|
||||||
tokenTransferProxyInstance.address,
|
tokenTransferProxyInstance.address,
|
||||||
provider,
|
provider,
|
||||||
);
|
);
|
||||||
const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [
|
|
||||||
zrx.address,
|
const erc20TransferProxyV1Instance = await deployer.deployAsync(ContractName.ERC20TransferProxy_V1, [
|
||||||
tokenTransferProxy.address,
|
tokenTransferProxy.address,
|
||||||
]);
|
]);
|
||||||
|
erc20TransferProxyV1 = new ERC20TransferProxy_v1Contract(
|
||||||
|
erc20TransferProxyV1Instance.abi,
|
||||||
|
erc20TransferProxyV1Instance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const erc20TransferProxyInstance = await deployer.deployAsync(ContractName.ERC20TransferProxy);
|
||||||
|
erc20TransferProxy = new ERC20TransferProxyContract(
|
||||||
|
erc20TransferProxyInstance.abi,
|
||||||
|
erc20TransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
erc721TransferProxyInstance = await deployer.deployAsync(ContractName.ERC721TransferProxy);
|
||||||
|
erc721TransferProxy = new ERC721TransferProxyContract(
|
||||||
|
erc721TransferProxyInstance.abi,
|
||||||
|
erc721TransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.AssetProxyDispatcher);
|
||||||
|
assetProxyDispatcher = new AssetProxyDispatcherContract(
|
||||||
|
assetProxyDispatcherInstance.abi,
|
||||||
|
assetProxyDispatcherInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [
|
||||||
|
zrx.address,
|
||||||
|
encodeERC20ProxyMetadata(zrx.address),
|
||||||
|
assetProxyDispatcher.address,
|
||||||
|
]);
|
||||||
exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider);
|
exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider);
|
||||||
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: accounts[0] });
|
await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(assetProxyManagerAddress, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: accounts[0] });
|
||||||
|
await erc20TransferProxyV1.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc20TransferProxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc721TransferProxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(erc20TransferProxyV1.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
const nilAddress = '0x0000000000000000000000000000000000000000';
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20_V1,
|
||||||
|
erc20TransferProxyV1.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: accounts[0] },
|
||||||
|
);
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: accounts[0] },
|
||||||
|
);
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC721,
|
||||||
|
erc721TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: accounts[0] },
|
||||||
|
);
|
||||||
zeroEx = new ZeroEx(provider, {
|
zeroEx = new ZeroEx(provider, {
|
||||||
exchangeContractAddress: exchange.address,
|
exchangeContractAddress: exchange.address,
|
||||||
networkId: constants.TESTRPC_NETWORK_ID,
|
networkId: constants.TESTRPC_NETWORK_ID,
|
||||||
@ -92,6 +188,8 @@ describe('Exchange', () => {
|
|||||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||||
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||||
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||||
|
makerAssetProxyData: encodeERC20ProxyMetadata(rep.address),
|
||||||
|
takerAssetProxyData: encodeERC20ProxyMetadata(dgd.address),
|
||||||
};
|
};
|
||||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[0];
|
const privateKey = constants.TESTRPC_PRIVATE_KEYS[0];
|
||||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||||
@ -103,6 +201,12 @@ describe('Exchange', () => {
|
|||||||
rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
from: takerAddress,
|
from: takerAddress,
|
||||||
}),
|
}),
|
||||||
|
rep.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
|
from: makerAddress,
|
||||||
|
}),
|
||||||
|
rep.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
|
from: takerAddress,
|
||||||
|
}),
|
||||||
rep.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
rep.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
rep.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
rep.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
@ -111,6 +215,12 @@ describe('Exchange', () => {
|
|||||||
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
from: takerAddress,
|
from: takerAddress,
|
||||||
}),
|
}),
|
||||||
|
dgd.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
|
from: makerAddress,
|
||||||
|
}),
|
||||||
|
dgd.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
|
from: takerAddress,
|
||||||
|
}),
|
||||||
dgd.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
dgd.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
dgd.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
dgd.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
@ -119,8 +229,109 @@ describe('Exchange', () => {
|
|||||||
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
from: takerAddress,
|
from: takerAddress,
|
||||||
}),
|
}),
|
||||||
|
zrx.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
|
from: makerAddress,
|
||||||
|
}),
|
||||||
|
zrx.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
|
from: takerAddress,
|
||||||
|
}),
|
||||||
zrx.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
zrx.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
zrx.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
zrx.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
|
|
||||||
|
// Distribute ck ERC721 tokens to maker & taker
|
||||||
|
// maker owns [0x1010.., ... , 0x4040..] and taker owns [0x5050.., ..., 0x9090..]
|
||||||
|
ck.setApprovalForAll.sendTransactionAsync(erc721TransferProxy.address, true, { from: makerAddress }),
|
||||||
|
ck.setApprovalForAll.sendTransactionAsync(erc721TransferProxy.address, true, { from: takerAddress }),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
makerAddress,
|
||||||
|
new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
makerAddress,
|
||||||
|
new BigNumber('0x2020202020202020202020202020202020202020202020202020202020202020'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
makerAddress,
|
||||||
|
new BigNumber('0x3030303030303030303030303030303030303030303030303030303030303030'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
makerAddress,
|
||||||
|
new BigNumber('0x4040404040404040404040404040404040404040404040404040404040404040'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x5050505050505050505050505050505050505050505050505050505050505050'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x6060606060606060606060606060606060606060606060606060606060606060'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x7070707070707070707070707070707070707070707070707070707070707070'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x8080808080808080808080808080808080808080808080808080808080808080'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
ck.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
|
||||||
|
// Distribute et ERC721 tokens to maker & taker
|
||||||
|
// maker owns [0x1010.., ... , 0x4040..] and taker owns [0x5050.., ..., 0x9090..]
|
||||||
|
et.setApprovalForAll.sendTransactionAsync(erc721TransferProxy.address, true, { from: makerAddress }),
|
||||||
|
et.setApprovalForAll.sendTransactionAsync(erc721TransferProxy.address, true, { from: takerAddress }),
|
||||||
|
et.mint.sendTransactionAsync(
|
||||||
|
makerAddress,
|
||||||
|
new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
et.mint.sendTransactionAsync(
|
||||||
|
makerAddress,
|
||||||
|
new BigNumber('0x2020202020202020202020202020202020202020202020202020202020202020'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
et.mint.sendTransactionAsync(
|
||||||
|
makerAddress,
|
||||||
|
new BigNumber('0x3030303030303030303030303030303030303030303030303030303030303030'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
et.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x5050505050505050505050505050505050505050505050505050505050505050'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
et.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x6060606060606060606060606060606060606060606060606060606060606060'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
et.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x7070707070707070707070707070707070707070707070707070707070707070'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
et.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x8080808080808080808080808080808080808080808080808080808080808080'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
|
et.mint.sendTransactionAsync(
|
||||||
|
takerAddress,
|
||||||
|
new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090'),
|
||||||
|
{ from: tokenOwner },
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -749,4 +960,284 @@ describe('Exchange', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Testing Exchange of ERC721 Tokens', () => {
|
||||||
|
it('should successfully exchange a single token between the maker and taker (via fillOrder)', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const makerTokenId = new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010');
|
||||||
|
const takerTokenId = new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
makerTokenAddress: ck.address,
|
||||||
|
takerTokenAddress: ck.address,
|
||||||
|
makerTokenAmount: new BigNumber(1),
|
||||||
|
takerTokenAmount: new BigNumber(1),
|
||||||
|
makerAssetProxyData: encodeERC721ProxyMetadata(ck.address, makerTokenId),
|
||||||
|
takerAssetProxyData: encodeERC721ProxyMetadata(ck.address, takerTokenId),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(initialOwnerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
const initialOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(initialOwnerTakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
const res = await exWrapper.fillOrderAsync(signedOrder, takerAddress, { takerTokenFillAmount });
|
||||||
|
|
||||||
|
// Verify post-conditions
|
||||||
|
const newOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(newOwnerMakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
const newOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(newOwnerTakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully exchange a single token between the maker and taker (via filleOrderNoThrow)', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const makerTokenId = new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010');
|
||||||
|
const takerTokenId = new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
makerTokenAddress: ck.address,
|
||||||
|
takerTokenAddress: ck.address,
|
||||||
|
makerTokenAmount: new BigNumber(1),
|
||||||
|
takerTokenAmount: new BigNumber(1),
|
||||||
|
makerAssetProxyData: encodeERC721ProxyMetadata(ck.address, makerTokenId),
|
||||||
|
takerAssetProxyData: encodeERC721ProxyMetadata(ck.address, takerTokenId),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(initialOwnerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
const initialOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(initialOwnerTakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress, { takerTokenFillAmount });
|
||||||
|
|
||||||
|
// Verify post-conditions
|
||||||
|
const newOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(newOwnerMakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
const newOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(newOwnerTakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when maker does not own the token with id makerTokenId', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const makerTokenId = new BigNumber('0x5050505050505050505050505050505050505050505050505050505050505050');
|
||||||
|
const takerTokenId = new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
makerTokenAddress: ck.address,
|
||||||
|
takerTokenAddress: ck.address,
|
||||||
|
makerTokenAmount: new BigNumber(1),
|
||||||
|
takerTokenAmount: new BigNumber(1),
|
||||||
|
makerAssetProxyData: encodeERC721ProxyMetadata(ck.address, makerTokenId),
|
||||||
|
takerAssetProxyData: encodeERC721ProxyMetadata(ck.address, takerTokenId),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(initialOwnerMakerToken).to.be.bignumber.not.equal(makerAddress);
|
||||||
|
const initialOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(initialOwnerTakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
return expect(
|
||||||
|
exWrapper.fillOrderAsync(signedOrder, takerAddress, { takerTokenFillAmount }),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when taker does not own the token with id takerTokenId', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const makerTokenId = new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010');
|
||||||
|
const takerTokenId = new BigNumber('0x2020202020202020202020202020202020202020202020202020202020202020');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
makerTokenAddress: ck.address,
|
||||||
|
takerTokenAddress: ck.address,
|
||||||
|
makerTokenAmount: new BigNumber(1),
|
||||||
|
takerTokenAmount: new BigNumber(1),
|
||||||
|
makerAssetProxyData: encodeERC721ProxyMetadata(ck.address, makerTokenId),
|
||||||
|
takerAssetProxyData: encodeERC721ProxyMetadata(ck.address, takerTokenId),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(initialOwnerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
const initialOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(initialOwnerTakerToken).to.be.bignumber.not.equal(takerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
return expect(
|
||||||
|
exWrapper.fillOrderAsync(signedOrder, takerAddress, { takerTokenFillAmount }),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when makerTokenAmount is greater than 1', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const makerTokenId = new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010');
|
||||||
|
const takerTokenId = new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
makerTokenAddress: ck.address,
|
||||||
|
takerTokenAddress: ck.address,
|
||||||
|
makerTokenAmount: new BigNumber(2),
|
||||||
|
takerTokenAmount: new BigNumber(1),
|
||||||
|
makerAssetProxyData: encodeERC721ProxyMetadata(ck.address, makerTokenId),
|
||||||
|
takerAssetProxyData: encodeERC721ProxyMetadata(ck.address, takerTokenId),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(initialOwnerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
const initialOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(initialOwnerTakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
return expect(
|
||||||
|
exWrapper.fillOrderAsync(signedOrder, takerAddress, { takerTokenFillAmount }),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when takerTokenAmount is greater than 1', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const makerTokenId = new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010');
|
||||||
|
const takerTokenId = new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
makerTokenAddress: ck.address,
|
||||||
|
takerTokenAddress: ck.address,
|
||||||
|
makerTokenAmount: new BigNumber(1),
|
||||||
|
takerTokenAmount: new BigNumber(500),
|
||||||
|
makerAssetProxyData: encodeERC721ProxyMetadata(ck.address, makerTokenId),
|
||||||
|
takerAssetProxyData: encodeERC721ProxyMetadata(ck.address, takerTokenId),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(initialOwnerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
const initialOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(initialOwnerTakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
return expect(
|
||||||
|
exWrapper.fillOrderAsync(signedOrder, takerAddress, { takerTokenFillAmount }),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw on partial fill', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const makerTokenId = new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010');
|
||||||
|
const takerTokenId = new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
makerTokenAddress: ck.address,
|
||||||
|
takerTokenAddress: ck.address,
|
||||||
|
makerTokenAmount: new BigNumber(1),
|
||||||
|
takerTokenAmount: new BigNumber(0),
|
||||||
|
makerAssetProxyData: encodeERC721ProxyMetadata(ck.address, makerTokenId),
|
||||||
|
takerAssetProxyData: encodeERC721ProxyMetadata(ck.address, takerTokenId),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(initialOwnerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
const initialOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(initialOwnerTakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
return expect(
|
||||||
|
exWrapper.fillOrderAsync(signedOrder, takerAddress, { takerTokenFillAmount }),
|
||||||
|
).to.be.rejectedWith(constants.REVERT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully fill order when makerToken is ERC721 and takerToken is ERC20', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const makerTokenId = new BigNumber('0x1010101010101010101010101010101010101010101010101010101010101010');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
makerTokenAddress: ck.address,
|
||||||
|
takerTokenAddress: dgd.address,
|
||||||
|
makerTokenAmount: new BigNumber(1),
|
||||||
|
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
|
makerAssetProxyData: encodeERC721ProxyMetadata(ck.address, makerTokenId),
|
||||||
|
takerAssetProxyData: encodeERC20ProxyMetadata(dgd.address),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(initialOwnerMakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
balances = await dmyBalances.getAsync();
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
await exWrapper.fillOrderAsync(signedOrder, takerAddress, { takerTokenFillAmount });
|
||||||
|
|
||||||
|
// Verify ERC721 token was transferred from Maker to Taker
|
||||||
|
const newOwnerMakerToken = await ck.ownerOf.callAsync(makerTokenId);
|
||||||
|
expect(newOwnerMakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
|
||||||
|
// Verify ERC20 tokens were transferred from Taker to Maker & fees were paid correctly
|
||||||
|
const newBalances = await dmyBalances.getAsync();
|
||||||
|
expect(newBalances[makerAddress][signedOrder.takerTokenAddress]).to.be.bignumber.equal(
|
||||||
|
balances[makerAddress][signedOrder.takerTokenAddress].add(takerTokenFillAmount),
|
||||||
|
);
|
||||||
|
expect(newBalances[takerAddress][signedOrder.takerTokenAddress]).to.be.bignumber.equal(
|
||||||
|
balances[takerAddress][signedOrder.takerTokenAddress].minus(takerTokenFillAmount),
|
||||||
|
);
|
||||||
|
expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[makerAddress][zrx.address].minus(signedOrder.makerFee),
|
||||||
|
);
|
||||||
|
expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[takerAddress][zrx.address].minus(signedOrder.takerFee),
|
||||||
|
);
|
||||||
|
expect(newBalances[feeRecipientAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[feeRecipientAddress][zrx.address].add(signedOrder.makerFee.add(signedOrder.takerFee)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should successfully fill order when makerToken is ERC20 and takerToken is ERC721', async () => {
|
||||||
|
// Construct Exchange parameters
|
||||||
|
const takerTokenId = new BigNumber('0x9090909090909090909090909090909090909090909090909090909090909090');
|
||||||
|
signedOrder = orderFactory.newSignedOrder({
|
||||||
|
takerTokenAddress: ck.address,
|
||||||
|
makerTokenAddress: dgd.address,
|
||||||
|
takerTokenAmount: new BigNumber(1),
|
||||||
|
makerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(100), 18),
|
||||||
|
takerAssetProxyData: encodeERC721ProxyMetadata(ck.address, takerTokenId),
|
||||||
|
makerAssetProxyData: encodeERC20ProxyMetadata(dgd.address),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify pre-conditions
|
||||||
|
const initialOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(initialOwnerTakerToken).to.be.bignumber.equal(takerAddress);
|
||||||
|
|
||||||
|
// Call Exchange
|
||||||
|
balances = await dmyBalances.getAsync();
|
||||||
|
const takerTokenFillAmount = signedOrder.takerTokenAmount;
|
||||||
|
await exWrapper.fillOrderAsync(signedOrder, takerAddress, { takerTokenFillAmount });
|
||||||
|
|
||||||
|
// Verify ERC721 token was transferred from Taker to Maker
|
||||||
|
const newOwnerTakerToken = await ck.ownerOf.callAsync(takerTokenId);
|
||||||
|
expect(newOwnerTakerToken).to.be.bignumber.equal(makerAddress);
|
||||||
|
|
||||||
|
// Verify ERC20 tokens were transferred from Maker to Taker & fees were paid correctly
|
||||||
|
const newBalances = await dmyBalances.getAsync();
|
||||||
|
expect(newBalances[takerAddress][signedOrder.makerTokenAddress]).to.be.bignumber.equal(
|
||||||
|
balances[takerAddress][signedOrder.makerTokenAddress].add(signedOrder.makerTokenAmount),
|
||||||
|
);
|
||||||
|
expect(newBalances[makerAddress][signedOrder.makerTokenAddress]).to.be.bignumber.equal(
|
||||||
|
balances[makerAddress][signedOrder.makerTokenAddress].minus(signedOrder.makerTokenAmount),
|
||||||
|
);
|
||||||
|
expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[makerAddress][zrx.address].minus(signedOrder.makerFee),
|
||||||
|
);
|
||||||
|
expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[takerAddress][zrx.address].minus(signedOrder.takerFee),
|
||||||
|
);
|
||||||
|
expect(newBalances[feeRecipientAddress][zrx.address]).to.be.bignumber.equal(
|
||||||
|
balances[feeRecipientAddress][zrx.address].add(signedOrder.makerFee.add(signedOrder.takerFee)),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
}); // tslint:disable-line:max-file-line-count
|
}); // tslint:disable-line:max-file-line-count
|
||||||
|
@ -6,11 +6,16 @@ import * as chai from 'chai';
|
|||||||
import ethUtil = require('ethereumjs-util');
|
import ethUtil = require('ethereumjs-util');
|
||||||
|
|
||||||
import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange';
|
import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange';
|
||||||
|
import {
|
||||||
|
encodeERC20ProxyMetadata,
|
||||||
|
encodeERC20ProxyMetadata_V1,
|
||||||
|
encodeERC721ProxyMetadata,
|
||||||
|
} from '../../src/utils/asset_proxy_utils';
|
||||||
import { constants } from '../../src/utils/constants';
|
import { constants } from '../../src/utils/constants';
|
||||||
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||||
import { OrderFactory } from '../../src/utils/order_factory';
|
import { OrderFactory } from '../../src/utils/order_factory';
|
||||||
import { orderUtils } from '../../src/utils/order_utils';
|
import { orderUtils } from '../../src/utils/order_utils';
|
||||||
import { ContractName, SignedOrder } from '../../src/utils/types';
|
import { AssetProxyId, ContractName, SignedOrder } from '../../src/utils/types';
|
||||||
import { chaiSetup } from '../utils/chai_setup';
|
import { chaiSetup } from '../utils/chai_setup';
|
||||||
import { deployer } from '../utils/deployer';
|
import { deployer } from '../utils/deployer';
|
||||||
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
||||||
@ -23,6 +28,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
|||||||
describe('Exchange', () => {
|
describe('Exchange', () => {
|
||||||
let makerAddress: string;
|
let makerAddress: string;
|
||||||
let feeRecipientAddress: string;
|
let feeRecipientAddress: string;
|
||||||
|
let assetProxyManagerAddress: string;
|
||||||
|
|
||||||
let signedOrder: SignedOrder;
|
let signedOrder: SignedOrder;
|
||||||
let exchangeWrapper: ExchangeWrapper;
|
let exchangeWrapper: ExchangeWrapper;
|
||||||
@ -30,9 +36,14 @@ describe('Exchange', () => {
|
|||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
[makerAddress, feeRecipientAddress] = accounts;
|
[makerAddress, feeRecipientAddress, assetProxyManagerAddress] = accounts;
|
||||||
const tokenRegistry = await deployer.deployAsync(ContractName.TokenRegistry);
|
const tokenRegistry = await deployer.deployAsync(ContractName.TokenRegistry);
|
||||||
const tokenTransferProxy = await deployer.deployAsync(ContractName.TokenTransferProxy);
|
const tokenTransferProxy = await deployer.deployAsync(ContractName.TokenTransferProxy);
|
||||||
|
const assetProxyDispatcher = await deployer.deployAsync(ContractName.AssetProxyDispatcher);
|
||||||
|
const erc20TransferProxyV1 = await deployer.deployAsync(ContractName.ERC20TransferProxy_V1, [
|
||||||
|
tokenTransferProxy.address,
|
||||||
|
]);
|
||||||
|
const erc20TransferProxy = await deployer.deployAsync(ContractName.ERC20TransferProxy);
|
||||||
const [rep, dgd, zrx] = await Promise.all([
|
const [rep, dgd, zrx] = await Promise.all([
|
||||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||||
@ -40,10 +51,36 @@ describe('Exchange', () => {
|
|||||||
]);
|
]);
|
||||||
const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [
|
const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [
|
||||||
zrx.address,
|
zrx.address,
|
||||||
tokenTransferProxy.address,
|
AssetProxyId.ERC20,
|
||||||
|
assetProxyDispatcher.address,
|
||||||
]);
|
]);
|
||||||
const exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider);
|
const exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider);
|
||||||
await tokenTransferProxy.addAuthorizedAddress(exchange.address, { from: accounts[0] });
|
await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(assetProxyManagerAddress, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: accounts[0] });
|
||||||
|
await erc20TransferProxyV1.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc20TransferProxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(erc20TransferProxyV1.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
const nilAddress = '0x0000000000000000000000000000000000000000';
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20_V1,
|
||||||
|
erc20TransferProxyV1.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: accounts[0] },
|
||||||
|
);
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: accounts[0] },
|
||||||
|
);
|
||||||
const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
|
const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
|
||||||
exchangeWrapper = new ExchangeWrapper(exchange, zeroEx);
|
exchangeWrapper = new ExchangeWrapper(exchange, zeroEx);
|
||||||
const defaultOrderParams = {
|
const defaultOrderParams = {
|
||||||
@ -56,6 +93,8 @@ describe('Exchange', () => {
|
|||||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||||
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||||
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||||
|
makerAssetProxyData: encodeERC20ProxyMetadata(rep.address),
|
||||||
|
takerAssetProxyData: encodeERC20ProxyMetadata(dgd.address),
|
||||||
};
|
};
|
||||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[0];
|
const privateKey = constants.TESTRPC_PRIVATE_KEYS[0];
|
||||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||||
|
@ -6,15 +6,23 @@ import * as chai from 'chai';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as Web3 from 'web3';
|
import * as Web3 from 'web3';
|
||||||
|
|
||||||
|
import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher';
|
||||||
import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token';
|
import { DummyTokenContract } from '../../src/contract_wrappers/generated/dummy_token';
|
||||||
|
import { ERC20TransferProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_transfer_proxy';
|
||||||
|
import { ERC20TransferProxy_v1Contract } from '../../src/contract_wrappers/generated/erc20transferproxy_v1';
|
||||||
import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange';
|
import { ExchangeContract } from '../../src/contract_wrappers/generated/exchange';
|
||||||
import { TokenRegistryContract } from '../../src/contract_wrappers/generated/token_registry';
|
import { TokenRegistryContract } from '../../src/contract_wrappers/generated/token_registry';
|
||||||
import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy';
|
import { TokenTransferProxyContract } from '../../src/contract_wrappers/generated/token_transfer_proxy';
|
||||||
|
import {
|
||||||
|
encodeERC20ProxyMetadata,
|
||||||
|
encodeERC20ProxyMetadata_V1,
|
||||||
|
encodeERC721ProxyMetadata,
|
||||||
|
} from '../../src/utils/asset_proxy_utils';
|
||||||
import { Balances } from '../../src/utils/balances';
|
import { Balances } from '../../src/utils/balances';
|
||||||
import { constants } from '../../src/utils/constants';
|
import { constants } from '../../src/utils/constants';
|
||||||
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||||
import { OrderFactory } from '../../src/utils/order_factory';
|
import { OrderFactory } from '../../src/utils/order_factory';
|
||||||
import { BalancesByOwner, ContractName, SignedOrder } from '../../src/utils/types';
|
import { AssetProxyId, BalancesByOwner, ContractName, SignedOrder } from '../../src/utils/types';
|
||||||
import { chaiSetup } from '../utils/chai_setup';
|
import { chaiSetup } from '../utils/chai_setup';
|
||||||
import { deployer } from '../utils/deployer';
|
import { deployer } from '../utils/deployer';
|
||||||
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
||||||
@ -28,6 +36,7 @@ describe('Exchange', () => {
|
|||||||
let tokenOwner: string;
|
let tokenOwner: string;
|
||||||
let takerAddress: string;
|
let takerAddress: string;
|
||||||
let feeRecipientAddress: string;
|
let feeRecipientAddress: string;
|
||||||
|
let assetProxyManagerAddress: string;
|
||||||
|
|
||||||
const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||||
const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||||
@ -38,6 +47,9 @@ describe('Exchange', () => {
|
|||||||
let exchange: ExchangeContract;
|
let exchange: ExchangeContract;
|
||||||
let tokenRegistry: TokenRegistryContract;
|
let tokenRegistry: TokenRegistryContract;
|
||||||
let tokenTransferProxy: TokenTransferProxyContract;
|
let tokenTransferProxy: TokenTransferProxyContract;
|
||||||
|
let assetProxyDispatcher: AssetProxyDispatcherContract;
|
||||||
|
let erc20TransferProxyV1: ERC20TransferProxy_v1Contract;
|
||||||
|
let erc20TransferProxy: ERC20TransferProxyContract;
|
||||||
|
|
||||||
let balances: BalancesByOwner;
|
let balances: BalancesByOwner;
|
||||||
|
|
||||||
@ -48,7 +60,7 @@ describe('Exchange', () => {
|
|||||||
before(async () => {
|
before(async () => {
|
||||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||||
tokenOwner = accounts[0];
|
tokenOwner = accounts[0];
|
||||||
[makerAddress, takerAddress, feeRecipientAddress] = accounts;
|
[makerAddress, takerAddress, feeRecipientAddress, assetProxyManagerAddress] = accounts;
|
||||||
const [repInstance, dgdInstance, zrxInstance] = await Promise.all([
|
const [repInstance, dgdInstance, zrxInstance] = await Promise.all([
|
||||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||||
@ -65,12 +77,58 @@ describe('Exchange', () => {
|
|||||||
tokenTransferProxyInstance.address,
|
tokenTransferProxyInstance.address,
|
||||||
provider,
|
provider,
|
||||||
);
|
);
|
||||||
const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [
|
const erc20TransferProxyV1Instance = await deployer.deployAsync(ContractName.ERC20TransferProxy_V1, [
|
||||||
zrx.address,
|
|
||||||
tokenTransferProxy.address,
|
tokenTransferProxy.address,
|
||||||
]);
|
]);
|
||||||
|
erc20TransferProxyV1 = new ERC20TransferProxy_v1Contract(
|
||||||
|
erc20TransferProxyV1Instance.abi,
|
||||||
|
erc20TransferProxyV1Instance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
const erc20TransferProxyInstance = await deployer.deployAsync(ContractName.ERC20TransferProxy);
|
||||||
|
erc20TransferProxy = new ERC20TransferProxyContract(
|
||||||
|
erc20TransferProxyInstance.abi,
|
||||||
|
erc20TransferProxyInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.AssetProxyDispatcher);
|
||||||
|
assetProxyDispatcher = new AssetProxyDispatcherContract(
|
||||||
|
assetProxyDispatcherInstance.abi,
|
||||||
|
assetProxyDispatcherInstance.address,
|
||||||
|
provider,
|
||||||
|
);
|
||||||
|
const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [
|
||||||
|
zrx.address,
|
||||||
|
encodeERC20ProxyMetadata(zrx.address),
|
||||||
|
assetProxyDispatcher.address,
|
||||||
|
]);
|
||||||
exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider);
|
exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider);
|
||||||
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: accounts[0] });
|
await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(assetProxyManagerAddress, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: accounts[0] });
|
||||||
|
await erc20TransferProxyV1.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await erc20TransferProxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
await tokenTransferProxy.addAuthorizedAddress.sendTransactionAsync(erc20TransferProxyV1.address, {
|
||||||
|
from: accounts[0],
|
||||||
|
});
|
||||||
|
const nilAddress = '0x0000000000000000000000000000000000000000';
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20_V1,
|
||||||
|
erc20TransferProxyV1.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: accounts[0] },
|
||||||
|
);
|
||||||
|
await assetProxyDispatcher.setAssetProxy.sendTransactionAsync(
|
||||||
|
AssetProxyId.ERC20,
|
||||||
|
erc20TransferProxy.address,
|
||||||
|
nilAddress,
|
||||||
|
{ from: accounts[0] },
|
||||||
|
);
|
||||||
const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
|
const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID });
|
||||||
exWrapper = new ExchangeWrapper(exchange, zeroEx);
|
exWrapper = new ExchangeWrapper(exchange, zeroEx);
|
||||||
|
|
||||||
@ -84,6 +142,8 @@ describe('Exchange', () => {
|
|||||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||||
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
makerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||||
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
takerFee: ZeroEx.toBaseUnitAmount(new BigNumber(1), 18),
|
||||||
|
makerAssetProxyData: encodeERC20ProxyMetadata(rep.address),
|
||||||
|
takerAssetProxyData: encodeERC20ProxyMetadata(dgd.address),
|
||||||
};
|
};
|
||||||
|
|
||||||
const privateKey = constants.TESTRPC_PRIVATE_KEYS[0];
|
const privateKey = constants.TESTRPC_PRIVATE_KEYS[0];
|
||||||
@ -92,14 +152,20 @@ describe('Exchange', () => {
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||||
rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
||||||
|
rep.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||||
|
rep.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
||||||
rep.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
rep.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
rep.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
rep.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||||
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
||||||
|
dgd.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||||
|
dgd.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
||||||
dgd.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
dgd.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
dgd.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
dgd.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||||
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
||||||
|
zrx.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||||
|
zrx.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, { from: takerAddress }),
|
||||||
zrx.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
zrx.setBalance.sendTransactionAsync(makerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
zrx.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
zrx.setBalance.sendTransactionAsync(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||||
]);
|
]);
|
||||||
@ -246,11 +312,11 @@ describe('Exchange', () => {
|
|||||||
|
|
||||||
it('should not change balances if maker allowances are too low to fill order', async () => {
|
it('should not change balances if maker allowances are too low to fill order', async () => {
|
||||||
const signedOrder = orderFactory.newSignedOrder();
|
const signedOrder = orderFactory.newSignedOrder();
|
||||||
await rep.approve.sendTransactionAsync(tokenTransferProxy.address, new BigNumber(0), {
|
await rep.approve.sendTransactionAsync(erc20TransferProxy.address, new BigNumber(0), {
|
||||||
from: makerAddress,
|
from: makerAddress,
|
||||||
});
|
});
|
||||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||||
await rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
await rep.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
from: makerAddress,
|
from: makerAddress,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -260,11 +326,11 @@ describe('Exchange', () => {
|
|||||||
|
|
||||||
it('should not change balances if taker allowances are too low to fill order', async () => {
|
it('should not change balances if taker allowances are too low to fill order', async () => {
|
||||||
const signedOrder = orderFactory.newSignedOrder();
|
const signedOrder = orderFactory.newSignedOrder();
|
||||||
await dgd.approve.sendTransactionAsync(tokenTransferProxy.address, new BigNumber(0), {
|
await dgd.approve.sendTransactionAsync(erc20TransferProxy.address, new BigNumber(0), {
|
||||||
from: takerAddress,
|
from: takerAddress,
|
||||||
});
|
});
|
||||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||||
await dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
await dgd.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||||
from: takerAddress,
|
from: takerAddress,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -278,6 +344,7 @@ describe('Exchange', () => {
|
|||||||
makerTokenAddress: zrx.address,
|
makerTokenAddress: zrx.address,
|
||||||
makerTokenAmount: makerZRXBalance,
|
makerTokenAmount: makerZRXBalance,
|
||||||
makerFee: new BigNumber(1),
|
makerFee: new BigNumber(1),
|
||||||
|
makerAssetProxyData: encodeERC20ProxyMetadata(zrx.address),
|
||||||
});
|
});
|
||||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||||
const newBalances = await dmyBalances.getAsync();
|
const newBalances = await dmyBalances.getAsync();
|
||||||
@ -285,11 +352,12 @@ describe('Exchange', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not change balances if makerTokenAddress is ZRX, makerTokenAmount + makerFee > maker allowance', async () => {
|
it('should not change balances if makerTokenAddress is ZRX, makerTokenAmount + makerFee > maker allowance', async () => {
|
||||||
const makerZRXAllowance = await zrx.allowance.callAsync(makerAddress, tokenTransferProxy.address);
|
const makerZRXAllowance = await zrx.allowance.callAsync(makerAddress, erc20TransferProxy.address);
|
||||||
const signedOrder = orderFactory.newSignedOrder({
|
const signedOrder = orderFactory.newSignedOrder({
|
||||||
makerTokenAddress: zrx.address,
|
makerTokenAddress: zrx.address,
|
||||||
makerTokenAmount: new BigNumber(makerZRXAllowance),
|
makerTokenAmount: new BigNumber(makerZRXAllowance),
|
||||||
makerFee: new BigNumber(1),
|
makerFee: new BigNumber(1),
|
||||||
|
makerAssetProxyData: encodeERC20ProxyMetadata(zrx.address),
|
||||||
});
|
});
|
||||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||||
const newBalances = await dmyBalances.getAsync();
|
const newBalances = await dmyBalances.getAsync();
|
||||||
@ -302,6 +370,7 @@ describe('Exchange', () => {
|
|||||||
takerTokenAddress: zrx.address,
|
takerTokenAddress: zrx.address,
|
||||||
takerTokenAmount: takerZRXBalance,
|
takerTokenAmount: takerZRXBalance,
|
||||||
takerFee: new BigNumber(1),
|
takerFee: new BigNumber(1),
|
||||||
|
takerAssetProxyData: encodeERC20ProxyMetadata(zrx.address),
|
||||||
});
|
});
|
||||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||||
const newBalances = await dmyBalances.getAsync();
|
const newBalances = await dmyBalances.getAsync();
|
||||||
@ -309,11 +378,12 @@ describe('Exchange', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not change balances if takerTokenAddress is ZRX, takerTokenAmount + takerFee > taker allowance', async () => {
|
it('should not change balances if takerTokenAddress is ZRX, takerTokenAmount + takerFee > taker allowance', async () => {
|
||||||
const takerZRXAllowance = await zrx.allowance.callAsync(takerAddress, tokenTransferProxy.address);
|
const takerZRXAllowance = await zrx.allowance.callAsync(takerAddress, erc20TransferProxy.address);
|
||||||
const signedOrder = orderFactory.newSignedOrder({
|
const signedOrder = orderFactory.newSignedOrder({
|
||||||
takerTokenAddress: zrx.address,
|
takerTokenAddress: zrx.address,
|
||||||
takerTokenAmount: new BigNumber(takerZRXAllowance),
|
takerTokenAmount: new BigNumber(takerZRXAllowance),
|
||||||
takerFee: new BigNumber(1),
|
takerFee: new BigNumber(1),
|
||||||
|
takerAssetProxyData: encodeERC20ProxyMetadata(zrx.address),
|
||||||
});
|
});
|
||||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||||
const newBalances = await dmyBalances.getAsync();
|
const newBalances = await dmyBalances.getAsync();
|
||||||
|
33
yarn.lock
33
yarn.lock
@ -1731,10 +1731,6 @@ buffer-from@^0.1.1:
|
|||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-0.1.2.tgz#15f4b9bcef012044df31142c14333caf6e0260d0"
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-0.1.2.tgz#15f4b9bcef012044df31142c14333caf6e0260d0"
|
||||||
|
|
||||||
buffer-from@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
|
|
||||||
|
|
||||||
buffer-indexof@^1.0.0:
|
buffer-indexof@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
|
resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
|
||||||
@ -2313,10 +2309,9 @@ concat-map@0.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||||
|
|
||||||
concat-stream@^1.4.10, concat-stream@^1.5.0:
|
concat-stream@^1.4.10, concat-stream@^1.5.0:
|
||||||
version "1.6.2"
|
version "1.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26"
|
||||||
dependencies:
|
dependencies:
|
||||||
buffer-from "^1.0.0"
|
|
||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^2.2.2"
|
readable-stream "^2.2.2"
|
||||||
typedarray "^0.0.6"
|
typedarray "^0.0.6"
|
||||||
@ -2570,8 +2565,8 @@ core-js@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||||
|
|
||||||
core-js@^2.4.0, core-js@^2.5.0:
|
core-js@^2.4.0, core-js@^2.5.0:
|
||||||
version "2.5.5"
|
version "2.5.3"
|
||||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b"
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
|
||||||
|
|
||||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
@ -3955,8 +3950,8 @@ extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
|
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
|
||||||
|
|
||||||
external-editor@^2.0.4:
|
external-editor@^2.0.4:
|
||||||
version "2.2.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
|
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48"
|
||||||
dependencies:
|
dependencies:
|
||||||
chardet "^0.4.0"
|
chardet "^0.4.0"
|
||||||
iconv-lite "^0.4.17"
|
iconv-lite "^0.4.17"
|
||||||
@ -9402,14 +9397,14 @@ semver-sort@0.0.4:
|
|||||||
semver "^5.0.3"
|
semver "^5.0.3"
|
||||||
semver-regex "^1.0.0"
|
semver-regex "^1.0.0"
|
||||||
|
|
||||||
"semver@2 || 3 || 4 || 5", semver@5.5.0, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0:
|
"semver@2 || 3 || 4 || 5", semver@5.4.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@~5.4.1:
|
||||||
version "5.5.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
|
||||||
|
|
||||||
semver@5.4.1, semver@~5.4.1:
|
|
||||||
version "5.4.1"
|
version "5.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
|
||||||
|
|
||||||
|
semver@5.5.0, semver@^5.5.0:
|
||||||
|
version "5.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||||
|
|
||||||
semver@^4.1.0:
|
semver@^4.1.0:
|
||||||
version "4.3.6"
|
version "4.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
|
resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
|
||||||
@ -10250,8 +10245,8 @@ swarm-js@0.1.37:
|
|||||||
xhr-request-promise "^0.1.2"
|
xhr-request-promise "^0.1.2"
|
||||||
|
|
||||||
symbol-observable@^1.0.3, symbol-observable@^1.0.4:
|
symbol-observable@^1.0.3, symbol-observable@^1.0.4:
|
||||||
version "1.2.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32"
|
||||||
|
|
||||||
symbol@~0.3.1:
|
symbol@~0.3.1:
|
||||||
version "0.3.1"
|
version "0.3.1"
|
||||||
@ -12006,7 +12001,7 @@ yauzl@^2.4.2:
|
|||||||
buffer-crc32 "~0.2.3"
|
buffer-crc32 "~0.2.3"
|
||||||
fd-slicer "~1.0.1"
|
fd-slicer "~1.0.1"
|
||||||
|
|
||||||
zeppelin-solidity@1.8.0:
|
zeppelin-solidity@1.8.0, zeppelin-solidity@^1.8.0:
|
||||||
version "1.8.0"
|
version "1.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/zeppelin-solidity/-/zeppelin-solidity-1.8.0.tgz#049fcde7daea9fc85210f8c6db9f8cd1ab8a853a"
|
resolved "https://registry.yarnpkg.com/zeppelin-solidity/-/zeppelin-solidity-1.8.0.tgz#049fcde7daea9fc85210f8c6db9f8cd1ab8a853a"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user