Asset Proxy Dispatcher
This commit is contained in:
parent
b9e0cd4512
commit
78d81f193f
@ -26,8 +26,9 @@
|
||||
"test:circleci": "yarn test:coverage"
|
||||
},
|
||||
"config": {
|
||||
"abis": "../migrations/src/artifacts/@(DummyToken|TokenTransferProxy|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken).json",
|
||||
"contracts": "Exchange,DummyToken,ZRXToken,Token,WETH9,TokenTransferProxy,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,MaliciousToken,TokenRegistry"
|
||||
"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,AssetProxyDispatcher,ERC20TransferProxy_v1,ERC20TransferProxy,ERC721TransferProxy,DummyERC721Token",
|
||||
"dirs": "src/contracts,zeppelin:../../node_modules/zeppelin-solidity"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -71,6 +72,7 @@
|
||||
"ethereumjs-util": "^5.1.1",
|
||||
"ethers-contracts": "^2.2.1",
|
||||
"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 "./MixinSettlementProxy.sol";
|
||||
import "./MixinWrapperFunctions.sol";
|
||||
import "../AssetProxyDispatcher/IAssetProxyDispatcher.sol";
|
||||
import "../TokenTransferProxy/ITokenTransferProxy.sol";
|
||||
|
||||
contract Exchange is
|
||||
MixinExchangeCore,
|
||||
@ -34,11 +36,12 @@ contract Exchange is
|
||||
|
||||
function Exchange(
|
||||
IToken _zrxToken,
|
||||
ITokenTransferProxy _tokenTransferProxy)
|
||||
bytes _zrxProxyMetadata,
|
||||
IAssetProxyDispatcher _assetProxyDispatcher)
|
||||
public
|
||||
MixinExchangeCore()
|
||||
MixinSignatureValidator()
|
||||
MixinSettlementProxy(_tokenTransferProxy, _zrxToken)
|
||||
MixinSettlementProxy(_assetProxyDispatcher, _zrxToken, _zrxProxyMetadata)
|
||||
MixinWrapperFunctions()
|
||||
{}
|
||||
}
|
||||
|
@ -54,11 +54,6 @@ contract IExchange {
|
||||
bytes32 indexed orderHash
|
||||
);
|
||||
|
||||
event LogCancelBefore(
|
||||
address indexed maker,
|
||||
uint256 salt
|
||||
);
|
||||
|
||||
function ZRX_TOKEN_CONTRACT()
|
||||
public view
|
||||
returns (address);
|
||||
|
@ -33,7 +33,9 @@ contract LibOrder {
|
||||
"uint256 makerFee",
|
||||
"uint256 takerFee",
|
||||
"uint256 expirationTimeSeconds",
|
||||
"uint256 salt"
|
||||
"uint256 salt",
|
||||
"bytes makerAssetProxyData",
|
||||
"bytes takerAssetProxyData"
|
||||
);
|
||||
|
||||
struct Order {
|
||||
@ -48,6 +50,8 @@ contract LibOrder {
|
||||
uint256 takerFee;
|
||||
uint256 expirationTimeSeconds;
|
||||
uint256 salt;
|
||||
bytes makerAssetProxyData;
|
||||
bytes takerAssetProxyData;
|
||||
}
|
||||
|
||||
/// @dev Calculates Keccak-256 hash of the order.
|
||||
@ -73,7 +77,9 @@ contract LibOrder {
|
||||
order.makerFee,
|
||||
order.takerFee,
|
||||
order.expirationTimeSeconds,
|
||||
order.salt
|
||||
order.salt,
|
||||
order.makerAssetProxyData,
|
||||
order.takerAssetProxyData
|
||||
)
|
||||
);
|
||||
return orderHash;
|
||||
|
@ -36,5 +36,3 @@ contract LibPartialAmount is SafeMath {
|
||||
return partialAmount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,22 +20,22 @@ pragma solidity ^0.4.21;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./mixins/MSettlement.sol";
|
||||
import "../TokenTransferProxy/ITokenTransferProxy.sol";
|
||||
import "../../tokens/Token/IToken.sol";
|
||||
import "./LibPartialAmount.sol";
|
||||
import "../AssetProxyDispatcher/IAssetProxyDispatcher.sol";
|
||||
|
||||
/// @dev Provides MixinSettlement
|
||||
contract MixinSettlementProxy is
|
||||
MSettlement,
|
||||
LibPartialAmount
|
||||
{
|
||||
|
||||
ITokenTransferProxy TRANSFER_PROXY;
|
||||
IAssetProxyDispatcher TRANSFER_PROXY;
|
||||
bytes ZRX_PROXY_METADATA;
|
||||
IToken ZRX_TOKEN;
|
||||
|
||||
function transferProxy()
|
||||
external view
|
||||
returns (ITokenTransferProxy)
|
||||
public view
|
||||
returns (IAssetProxyDispatcher)
|
||||
{
|
||||
return TRANSFER_PROXY;
|
||||
}
|
||||
@ -47,15 +47,26 @@ contract MixinSettlementProxy is
|
||||
return ZRX_TOKEN;
|
||||
}
|
||||
|
||||
function zrxProxyMetadata()
|
||||
external view
|
||||
returns (bytes)
|
||||
{
|
||||
return ZRX_PROXY_METADATA;
|
||||
}
|
||||
|
||||
function MixinSettlementProxy(
|
||||
ITokenTransferProxy _proxyContract,
|
||||
IToken _zrxToken)
|
||||
IAssetProxyDispatcher assetProxyDispatcherContract,
|
||||
IToken zrxToken,
|
||||
bytes zrxProxyMetadata)
|
||||
public
|
||||
{
|
||||
ZRX_TOKEN = _zrxToken;
|
||||
TRANSFER_PROXY = _proxyContract;
|
||||
ZRX_TOKEN = zrxToken;
|
||||
TRANSFER_PROXY = assetProxyDispatcherContract;
|
||||
ZRX_PROXY_METADATA = zrxProxyMetadata;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function settleOrder(
|
||||
Order memory order,
|
||||
address takerAddress,
|
||||
@ -68,43 +79,35 @@ contract MixinSettlementProxy is
|
||||
)
|
||||
{
|
||||
makerTokenFilledAmount = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerTokenAmount);
|
||||
require(
|
||||
TRANSFER_PROXY.transferFrom(
|
||||
order.makerTokenAddress,
|
||||
order.makerAssetProxyData,
|
||||
order.makerAddress,
|
||||
takerAddress,
|
||||
makerTokenFilledAmount
|
||||
)
|
||||
);
|
||||
require(
|
||||
TRANSFER_PROXY.transferFrom(
|
||||
order.takerTokenAddress,
|
||||
order.takerAssetProxyData,
|
||||
takerAddress,
|
||||
order.makerAddress,
|
||||
takerTokenFilledAmount
|
||||
)
|
||||
);
|
||||
if (order.feeRecipientAddress != address(0)) {
|
||||
if (order.makerFee > 0) {
|
||||
makerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerFee);
|
||||
require(
|
||||
TRANSFER_PROXY.transferFrom(
|
||||
ZRX_TOKEN,
|
||||
ZRX_PROXY_METADATA,
|
||||
order.makerAddress,
|
||||
order.feeRecipientAddress,
|
||||
makerFeePaid
|
||||
)
|
||||
);
|
||||
}
|
||||
if (order.takerFee > 0) {
|
||||
takerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.takerFee);
|
||||
require(
|
||||
TRANSFER_PROXY.transferFrom(
|
||||
ZRX_TOKEN,
|
||||
ZRX_PROXY_METADATA,
|
||||
takerAddress,
|
||||
order.feeRecipientAddress,
|
||||
takerFeePaid
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -66,74 +66,161 @@ contract MixinWrapperFunctions is
|
||||
// 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].
|
||||
|
||||
// | Offset | Length | Contents |
|
||||
// |--------|---------|------------------------------|
|
||||
// | 0 | 4 | function selector |
|
||||
// | 4 | 11 * 32 | Order order |
|
||||
// | 356 | 32 | uint256 takerTokenFillAmount |
|
||||
// | 388 | 32 | offset to signature (416) |
|
||||
// | 420 | 32 | len(signature) |
|
||||
// | 452 | (1) | signature |
|
||||
// | (2) | (3) | padding (zero) |
|
||||
// | (4) | | end of input |
|
||||
// | Area | Offset | Length | Contents |
|
||||
// | -------- |--------|---------|-------------------------------------------- |
|
||||
// | Header | 0x00 | 4 | function selector |
|
||||
// | Params | | 3 * 32 | function parameters: |
|
||||
// | | 0x00 | | 1. offset to order (*) |
|
||||
// | | 0x20 | | 2. takerTokenFillAmount |
|
||||
// | | 0x40 | | 3. offset to signature (*) |
|
||||
// | Data | | 13 * 32 | order: |
|
||||
// | | 0x000 | | 1. makerAddress |
|
||||
// | | 0x020 | | 2. takerAddress |
|
||||
// | | 0x040 | | 3. makerTokenAddress |
|
||||
// | | 0x060 | | 4. takerTokenAddress |
|
||||
// | | 0x080 | | 5. feeRecipientAddress |
|
||||
// | | 0x0A0 | | 6. makerTokenAmount |
|
||||
// | | 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 |
|
||||
|
||||
// (1): len(signature)
|
||||
// (2): 452 + len(signature)
|
||||
// (3): (32 - len(signature)) mod 32
|
||||
// (4): 452 + len(signature) + (32 - len(signature)) mod 32
|
||||
// * 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
|
||||
|
||||
bytes4 fillOrderSelector = this.fillOrder.selector;
|
||||
|
||||
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
|
||||
let start := mload(0x40)
|
||||
let headerAreaStart := mload(0x40)
|
||||
mstore(headerAreaStart, fillOrderSelector)
|
||||
let headerAreaEnd := add(headerAreaStart, 0x4)
|
||||
|
||||
// Write function signature
|
||||
mstore(start, fillOrderSelector)
|
||||
/////// Setup Params Area ///////
|
||||
// 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
|
||||
mstore(add(start, 4), mload(order)) // makerAddress
|
||||
mstore(add(start, 36), mload(add(order, 32))) // takerAddress
|
||||
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
|
||||
/////// Setup Data Area ///////
|
||||
let dataAreaStart := paramsAreaEnd
|
||||
let dataAreaEnd := dataAreaStart
|
||||
|
||||
// Write takerTokenFillAmount
|
||||
mstore(add(start, 356), takerTokenFillAmount)
|
||||
// Offset from the source data we're reading from
|
||||
let sourceOffset := order
|
||||
// bytesLen and bytesLenPadded track the length of a dynamically-allocated bytes array.
|
||||
let bytesLen := 0
|
||||
let bytesLenPadded := 0
|
||||
|
||||
// Write signature offset
|
||||
mstore(add(start, 388), 416)
|
||||
/////// Write order Struct ///////
|
||||
// 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
|
||||
let sigLen := mload(signature)
|
||||
mstore(add(start, 420), sigLen)
|
||||
// Write values for each field in the order
|
||||
for{let i := 0} lt(i, 13) {i := add(i, 1)} {
|
||||
mstore(dataAreaEnd, mload(sourceOffset))
|
||||
dataAreaEnd := add(dataAreaEnd, 0x20)
|
||||
sourceOffset := add(sourceOffset, 0x20)
|
||||
}
|
||||
|
||||
// Calculate signature length with padding
|
||||
let paddingLen := mod(sub(0, sigLen), 32)
|
||||
let sigLenWithPadding := add(sigLen, paddingLen)
|
||||
// Write offset to <order.makerAssetProxyMetadata>
|
||||
mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
|
||||
|
||||
// Write signature
|
||||
let sigStart := add(signature, 32)
|
||||
for { let curr := 0 }
|
||||
lt(curr, sigLenWithPadding)
|
||||
{ curr := add(curr, 32) }
|
||||
{ mstore(add(start, add(452, curr)), mload(add(sigStart, curr))) } // Note: we assume that padding consists of only 0's
|
||||
// Calculate length of <order.makerAssetProxyMetadata>
|
||||
bytesLen := mload(sourceOffset)
|
||||
sourceOffset := add(sourceOffset, 0x20)
|
||||
bytesLenPadded := add(div(bytesLen, 0x20), gt(mod(bytesLen, 0x20), 0))
|
||||
|
||||
// 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
|
||||
let success := delegatecall(
|
||||
gas, // forward all gas, TODO: look into gas consumption of assert/throw
|
||||
address, // call address of this contract
|
||||
start, // pointer to start of input
|
||||
add(452, sigLenWithPadding), // input length is 420 + signature length + padding length
|
||||
start, // write output over input
|
||||
headerAreaStart, // pointer to start of input
|
||||
sub(dataAreaEnd, headerAreaStart), // length of input
|
||||
headerAreaStart, // write output over input
|
||||
128 // output size is 128 bytes
|
||||
)
|
||||
switch success
|
||||
@ -144,12 +231,11 @@ contract MixinWrapperFunctions is
|
||||
mstore(add(fillResults, 96), 0)
|
||||
}
|
||||
case 1 {
|
||||
mstore(fillResults, mload(start))
|
||||
mstore(add(fillResults, 32), mload(add(start, 32)))
|
||||
mstore(add(fillResults, 64), mload(add(start, 64)))
|
||||
mstore(add(fillResults, 96), mload(add(start, 96)))
|
||||
mstore(fillResults, mload(headerAreaStart))
|
||||
mstore(add(fillResults, 32), mload(add(headerAreaStart, 32)))
|
||||
mstore(add(fillResults, 64), mload(add(headerAreaStart, 64)))
|
||||
mstore(add(fillResults, 96), mload(add(headerAreaStart, 96)))
|
||||
}
|
||||
|
||||
}
|
||||
return fillResults;
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
contract Ownable {
|
||||
import "../Ownable/IOwnable.sol";
|
||||
|
||||
contract Ownable is IOwnable {
|
||||
address public owner;
|
||||
|
||||
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_APPROVE_GAS: 60000,
|
||||
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)),
|
||||
};
|
||||
|
@ -31,6 +31,8 @@ export const crypto = {
|
||||
argTypes.push('address');
|
||||
} else if (_.isString(arg)) {
|
||||
argTypes.push('string');
|
||||
} else if (arg instanceof Buffer) {
|
||||
argTypes.push('bytes');
|
||||
} else if (_.isBoolean(arg)) {
|
||||
argTypes.push('bool');
|
||||
} else {
|
||||
|
@ -35,6 +35,8 @@ export const orderUtils = {
|
||||
takerFee: signedOrder.takerFee,
|
||||
expirationTimeSeconds: signedOrder.expirationTimeSeconds,
|
||||
salt: signedOrder.salt,
|
||||
makerAssetProxyData: signedOrder.makerAssetProxyData,
|
||||
takerAssetProxyData: signedOrder.takerAssetProxyData,
|
||||
};
|
||||
return orderStruct;
|
||||
},
|
||||
@ -52,6 +54,8 @@ export const orderUtils = {
|
||||
'uint256 takerFee',
|
||||
'uint256 expirationTimeSeconds',
|
||||
'uint256 salt',
|
||||
'bytes makerAssetProxyData',
|
||||
'bytes takerAssetProxyData',
|
||||
]);
|
||||
const orderParamsHashBuff = crypto.solSHA3([
|
||||
order.exchangeAddress,
|
||||
@ -66,6 +70,8 @@ export const orderUtils = {
|
||||
order.takerFee,
|
||||
order.expirationTimeSeconds,
|
||||
order.salt,
|
||||
ethUtil.toBuffer(order.makerAssetProxyData),
|
||||
ethUtil.toBuffer(order.takerAssetProxyData),
|
||||
]);
|
||||
const orderSchemaHashHex = `0x${orderSchemaHashBuff.toString('hex')}`;
|
||||
const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`;
|
||||
|
@ -37,6 +37,13 @@ export interface CancelOrdersBefore {
|
||||
salt: BigNumber;
|
||||
}
|
||||
|
||||
export enum AssetProxyId {
|
||||
INVALID,
|
||||
ERC20_V1,
|
||||
ERC20,
|
||||
ERC721,
|
||||
}
|
||||
|
||||
export interface DefaultOrderParams {
|
||||
exchangeAddress: string;
|
||||
makerAddress: string;
|
||||
@ -45,8 +52,10 @@ export interface DefaultOrderParams {
|
||||
takerTokenAddress: string;
|
||||
makerTokenAmount: BigNumber;
|
||||
takerTokenAmount: BigNumber;
|
||||
makerFee: BigNumber;
|
||||
takerFee: BigNumber;
|
||||
makerFeeAmount: BigNumber;
|
||||
takerFeeAmount: BigNumber;
|
||||
makerAssetProxyData: string;
|
||||
takerAssetProxyData: string;
|
||||
}
|
||||
|
||||
export interface TransactionDataParams {
|
||||
@ -100,6 +109,11 @@ export enum ContractName {
|
||||
AccountLevels = 'AccountLevels',
|
||||
EtherDelta = 'EtherDelta',
|
||||
Arbitrage = 'Arbitrage',
|
||||
AssetProxyDispatcher = 'AssetProxyDispatcher',
|
||||
ERC20TransferProxy = 'ERC20TransferProxy',
|
||||
ERC20TransferProxy_V1 = 'ERC20TransferProxy_v1',
|
||||
ERC721TransferProxy = 'ERC721TransferProxy',
|
||||
DummyERC721Token = 'DummyERC721Token',
|
||||
}
|
||||
|
||||
export interface Artifact {
|
||||
@ -134,6 +148,8 @@ export interface OrderStruct {
|
||||
takerFee: BigNumber;
|
||||
expirationTimeSeconds: BigNumber;
|
||||
salt: BigNumber;
|
||||
makerAssetProxyData: string;
|
||||
takerAssetProxyData: string;
|
||||
}
|
||||
|
||||
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 { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils';
|
||||
import { BigNumber } from '@0xproject/utils';
|
||||
import { Web3Wrapper } from '@0xproject/web3-wrapper';
|
||||
import * as chai from 'chai';
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
import * as _ from 'lodash';
|
||||
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 {
|
||||
CancelContractEventArgs,
|
||||
ExchangeContract,
|
||||
@ -15,13 +20,25 @@ import {
|
||||
FillContractEventArgs,
|
||||
} from '../../src/contract_wrappers/generated/exchange';
|
||||
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 { crypto } from '../../src/utils/crypto';
|
||||
import { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||
import { OrderFactory } from '../../src/utils/order_factory';
|
||||
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 { deployer } from '../utils/deployer';
|
||||
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
||||
@ -35,14 +52,21 @@ describe('Exchange', () => {
|
||||
let tokenOwner: string;
|
||||
let takerAddress: string;
|
||||
let feeRecipientAddress: string;
|
||||
let assetProxyManagerAddress: string;
|
||||
const INITIAL_BALANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||
const INITIAL_ALLOWANCE = ZeroEx.toBaseUnitAmount(new BigNumber(10000), 18);
|
||||
|
||||
let rep: DummyTokenContract;
|
||||
let dgd: DummyTokenContract;
|
||||
let zrx: DummyTokenContract;
|
||||
let ck: DummyERC721TokenContract;
|
||||
let et: DummyERC721TokenContract;
|
||||
let exchange: ExchangeContract;
|
||||
let tokenTransferProxy: TokenTransferProxyContract;
|
||||
let assetProxyDispatcher: AssetProxyDispatcherContract;
|
||||
let erc20TransferProxyV1: ERC20TransferProxy_v1Contract;
|
||||
let erc20TransferProxy: ERC20TransferProxyContract;
|
||||
let erc721TransferProxy: ERC721TransferProxyContract;
|
||||
|
||||
let signedOrder: SignedOrder;
|
||||
let balances: BalancesByOwner;
|
||||
@ -50,32 +74,104 @@ describe('Exchange', () => {
|
||||
let dmyBalances: Balances;
|
||||
let orderFactory: OrderFactory;
|
||||
|
||||
let erc721TransferProxyInstance: Web3.ContractInstance;
|
||||
|
||||
let zeroEx: ZeroEx;
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
makerAddress = accounts[0];
|
||||
[tokenOwner, takerAddress, feeRecipientAddress] = accounts;
|
||||
const [repInstance, dgdInstance, zrxInstance] = await Promise.all([
|
||||
[tokenOwner, takerAddress, feeRecipientAddress, assetProxyManagerAddress] = accounts;
|
||||
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.DummyERC721Token, constants.DUMMY_ERC721TOKEN_ARGS),
|
||||
deployer.deployAsync(ContractName.DummyERC721Token, constants.DUMMY_ERC721TOKEN_ARGS),
|
||||
]);
|
||||
rep = new DummyTokenContract(repInstance.abi, repInstance.address, provider);
|
||||
dgd = new DummyTokenContract(dgdInstance.abi, dgdInstance.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);
|
||||
tokenTransferProxy = new TokenTransferProxyContract(
|
||||
tokenTransferProxyInstance.abi,
|
||||
tokenTransferProxyInstance.address,
|
||||
provider,
|
||||
);
|
||||
const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [
|
||||
zrx.address,
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
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);
|
||||
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, {
|
||||
exchangeContractAddress: exchange.address,
|
||||
networkId: constants.TESTRPC_NETWORK_ID,
|
||||
@ -92,6 +188,8 @@ describe('Exchange', () => {
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
makerFee: 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];
|
||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||
@ -103,6 +201,12 @@ describe('Exchange', () => {
|
||||
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(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||
@ -111,6 +215,12 @@ describe('Exchange', () => {
|
||||
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(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||
@ -119,8 +229,109 @@ describe('Exchange', () => {
|
||||
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(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 () => {
|
||||
@ -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
|
||||
|
@ -6,11 +6,16 @@ import * as chai from 'chai';
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
|
||||
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 { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||
import { OrderFactory } from '../../src/utils/order_factory';
|
||||
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 { deployer } from '../utils/deployer';
|
||||
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
||||
@ -23,6 +28,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
describe('Exchange', () => {
|
||||
let makerAddress: string;
|
||||
let feeRecipientAddress: string;
|
||||
let assetProxyManagerAddress: string;
|
||||
|
||||
let signedOrder: SignedOrder;
|
||||
let exchangeWrapper: ExchangeWrapper;
|
||||
@ -30,9 +36,14 @@ describe('Exchange', () => {
|
||||
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
[makerAddress, feeRecipientAddress] = accounts;
|
||||
[makerAddress, feeRecipientAddress, assetProxyManagerAddress] = accounts;
|
||||
const tokenRegistry = await deployer.deployAsync(ContractName.TokenRegistry);
|
||||
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([
|
||||
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, [
|
||||
zrx.address,
|
||||
tokenTransferProxy.address,
|
||||
AssetProxyId.ERC20,
|
||||
assetProxyDispatcher.address,
|
||||
]);
|
||||
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 });
|
||||
exchangeWrapper = new ExchangeWrapper(exchange, zeroEx);
|
||||
const defaultOrderParams = {
|
||||
@ -56,6 +93,8 @@ describe('Exchange', () => {
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
makerFee: 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];
|
||||
orderFactory = new OrderFactory(privateKey, defaultOrderParams);
|
||||
|
@ -6,15 +6,23 @@ import * as chai from 'chai';
|
||||
import * as _ from 'lodash';
|
||||
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 { 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 { TokenRegistryContract } from '../../src/contract_wrappers/generated/token_registry';
|
||||
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 { ExchangeWrapper } from '../../src/utils/exchange_wrapper';
|
||||
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 { deployer } from '../utils/deployer';
|
||||
import { provider, web3Wrapper } from '../utils/web3_wrapper';
|
||||
@ -28,6 +36,7 @@ describe('Exchange', () => {
|
||||
let tokenOwner: string;
|
||||
let takerAddress: string;
|
||||
let feeRecipientAddress: string;
|
||||
let assetProxyManagerAddress: string;
|
||||
|
||||
const INITIAL_BALANCE = 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 tokenRegistry: TokenRegistryContract;
|
||||
let tokenTransferProxy: TokenTransferProxyContract;
|
||||
let assetProxyDispatcher: AssetProxyDispatcherContract;
|
||||
let erc20TransferProxyV1: ERC20TransferProxy_v1Contract;
|
||||
let erc20TransferProxy: ERC20TransferProxyContract;
|
||||
|
||||
let balances: BalancesByOwner;
|
||||
|
||||
@ -48,7 +60,7 @@ describe('Exchange', () => {
|
||||
before(async () => {
|
||||
const accounts = await web3Wrapper.getAvailableAddressesAsync();
|
||||
tokenOwner = accounts[0];
|
||||
[makerAddress, takerAddress, feeRecipientAddress] = accounts;
|
||||
[makerAddress, takerAddress, feeRecipientAddress, assetProxyManagerAddress] = accounts;
|
||||
const [repInstance, dgdInstance, zrxInstance] = await Promise.all([
|
||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||
deployer.deployAsync(ContractName.DummyToken, constants.DUMMY_TOKEN_ARGS),
|
||||
@ -65,12 +77,58 @@ describe('Exchange', () => {
|
||||
tokenTransferProxyInstance.address,
|
||||
provider,
|
||||
);
|
||||
const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [
|
||||
zrx.address,
|
||||
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 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);
|
||||
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 });
|
||||
exWrapper = new ExchangeWrapper(exchange, zeroEx);
|
||||
|
||||
@ -84,6 +142,8 @@ describe('Exchange', () => {
|
||||
takerTokenAmount: ZeroEx.toBaseUnitAmount(new BigNumber(200), 18),
|
||||
makerFee: 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];
|
||||
@ -92,14 +152,20 @@ describe('Exchange', () => {
|
||||
await Promise.all([
|
||||
rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||
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(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||
dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||
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(takerAddress, INITIAL_BALANCE, { from: tokenOwner }),
|
||||
zrx.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, { from: makerAddress }),
|
||||
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(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 () => {
|
||||
const signedOrder = orderFactory.newSignedOrder();
|
||||
await rep.approve.sendTransactionAsync(tokenTransferProxy.address, new BigNumber(0), {
|
||||
await rep.approve.sendTransactionAsync(erc20TransferProxy.address, new BigNumber(0), {
|
||||
from: makerAddress,
|
||||
});
|
||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||
await rep.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||
await rep.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||
from: makerAddress,
|
||||
});
|
||||
|
||||
@ -260,11 +326,11 @@ describe('Exchange', () => {
|
||||
|
||||
it('should not change balances if taker allowances are too low to fill order', async () => {
|
||||
const signedOrder = orderFactory.newSignedOrder();
|
||||
await dgd.approve.sendTransactionAsync(tokenTransferProxy.address, new BigNumber(0), {
|
||||
await dgd.approve.sendTransactionAsync(erc20TransferProxy.address, new BigNumber(0), {
|
||||
from: takerAddress,
|
||||
});
|
||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||
await dgd.approve.sendTransactionAsync(tokenTransferProxy.address, INITIAL_ALLOWANCE, {
|
||||
await dgd.approve.sendTransactionAsync(erc20TransferProxy.address, INITIAL_ALLOWANCE, {
|
||||
from: takerAddress,
|
||||
});
|
||||
|
||||
@ -278,6 +344,7 @@ describe('Exchange', () => {
|
||||
makerTokenAddress: zrx.address,
|
||||
makerTokenAmount: makerZRXBalance,
|
||||
makerFee: new BigNumber(1),
|
||||
makerAssetProxyData: encodeERC20ProxyMetadata(zrx.address),
|
||||
});
|
||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||
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 () => {
|
||||
const makerZRXAllowance = await zrx.allowance.callAsync(makerAddress, tokenTransferProxy.address);
|
||||
const makerZRXAllowance = await zrx.allowance.callAsync(makerAddress, erc20TransferProxy.address);
|
||||
const signedOrder = orderFactory.newSignedOrder({
|
||||
makerTokenAddress: zrx.address,
|
||||
makerTokenAmount: new BigNumber(makerZRXAllowance),
|
||||
makerFee: new BigNumber(1),
|
||||
makerAssetProxyData: encodeERC20ProxyMetadata(zrx.address),
|
||||
});
|
||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
@ -302,6 +370,7 @@ describe('Exchange', () => {
|
||||
takerTokenAddress: zrx.address,
|
||||
takerTokenAmount: takerZRXBalance,
|
||||
takerFee: new BigNumber(1),
|
||||
takerAssetProxyData: encodeERC20ProxyMetadata(zrx.address),
|
||||
});
|
||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||
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 () => {
|
||||
const takerZRXAllowance = await zrx.allowance.callAsync(takerAddress, tokenTransferProxy.address);
|
||||
const takerZRXAllowance = await zrx.allowance.callAsync(takerAddress, erc20TransferProxy.address);
|
||||
const signedOrder = orderFactory.newSignedOrder({
|
||||
takerTokenAddress: zrx.address,
|
||||
takerTokenAmount: new BigNumber(takerZRXAllowance),
|
||||
takerFee: new BigNumber(1),
|
||||
takerAssetProxyData: encodeERC20ProxyMetadata(zrx.address),
|
||||
});
|
||||
await exWrapper.fillOrderNoThrowAsync(signedOrder, takerAddress);
|
||||
const newBalances = await dmyBalances.getAsync();
|
||||
|
33
yarn.lock
33
yarn.lock
@ -1731,10 +1731,6 @@ buffer-from@^0.1.1:
|
||||
version "0.1.2"
|
||||
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:
|
||||
version "1.1.1"
|
||||
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"
|
||||
|
||||
concat-stream@^1.4.10, concat-stream@^1.5.0:
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26"
|
||||
dependencies:
|
||||
buffer-from "^1.0.0"
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.2.2"
|
||||
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"
|
||||
|
||||
core-js@^2.4.0, core-js@^2.5.0:
|
||||
version "2.5.5"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.5.tgz#b14dde936c640c0579a6b50cabcc132dd6127e3b"
|
||||
version "2.5.3"
|
||||
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:
|
||||
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"
|
||||
|
||||
external-editor@^2.0.4:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5"
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48"
|
||||
dependencies:
|
||||
chardet "^0.4.0"
|
||||
iconv-lite "^0.4.17"
|
||||
@ -9402,14 +9397,14 @@ semver-sort@0.0.4:
|
||||
semver "^5.0.3"
|
||||
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:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
|
||||
|
||||
semver@5.4.1, semver@~5.4.1:
|
||||
"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.4.1"
|
||||
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:
|
||||
version "4.3.6"
|
||||
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"
|
||||
|
||||
symbol-observable@^1.0.3, symbol-observable@^1.0.4:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32"
|
||||
|
||||
symbol@~0.3.1:
|
||||
version "0.3.1"
|
||||
@ -12006,7 +12001,7 @@ yauzl@^2.4.2:
|
||||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.0.1"
|
||||
|
||||
zeppelin-solidity@1.8.0:
|
||||
zeppelin-solidity@1.8.0, zeppelin-solidity@^1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/zeppelin-solidity/-/zeppelin-solidity-1.8.0.tgz#049fcde7daea9fc85210f8c6db9f8cd1ab8a853a"
|
||||
dependencies:
|
||||
|
Loading…
x
Reference in New Issue
Block a user