@0x/contracts-asset-proxy
: Add KyberBridge
.
This commit is contained in:
parent
1166b43946
commit
5da1fc8445
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "2.3.0-beta.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Implement `KyberBridge`.",
|
||||||
|
"pr": 2352
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "2.3.0-beta.3",
|
"version": "2.3.0-beta.3",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -61,8 +61,8 @@ contract Eth2DaiBridge is
|
|||||||
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
LibERC20Token.approve(fromTokenAddress, address(exchange), uint256(-1));
|
||||||
|
|
||||||
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
// Try to sell all of this contract's `fromTokenAddress` token balance.
|
||||||
uint256 boughtAmount = _getEth2DaiContract().sellAllAmount(
|
uint256 boughtAmount = exchange.sellAllAmount(
|
||||||
address(fromTokenAddress),
|
fromTokenAddress,
|
||||||
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
IERC20Token(fromTokenAddress).balanceOf(address(this)),
|
||||||
toTokenAddress,
|
toTokenAddress,
|
||||||
amount
|
amount
|
||||||
|
160
contracts/asset-proxy/contracts/src/bridges/KyberBridge.sol
Normal file
160
contracts/asset-proxy/contracts/src/bridges/KyberBridge.sol
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IEtherToken.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/LibERC20Token.sol";
|
||||||
|
import "@0x/contracts-exchange-libs/contracts/src/IWallet.sol";
|
||||||
|
import "../interfaces/IERC20Bridge.sol";
|
||||||
|
import "../interfaces/IKyberNetworkProxy.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable space-after-comma
|
||||||
|
contract KyberBridge is
|
||||||
|
IERC20Bridge,
|
||||||
|
IWallet
|
||||||
|
{
|
||||||
|
// @dev Structure used internally to get around stack limits.
|
||||||
|
struct TradeState {
|
||||||
|
IKyberNetworkProxy kyber;
|
||||||
|
IEtherToken weth;
|
||||||
|
address fromTokenAddress;
|
||||||
|
uint256 fromTokenBalance;
|
||||||
|
uint256 payableAmount;
|
||||||
|
uint256 minConversionRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Address of the WETH contract.
|
||||||
|
address constant public WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||||
|
/// @dev Address of the KyberNeworkProxy contract.
|
||||||
|
address constant public KYBER_NETWORK_PROXY_ADDRESS = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755;
|
||||||
|
/// @dev Kyber ETH pseudo-address.
|
||||||
|
address constant public KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
|
||||||
|
/// @dev Callback for `IKyberBridge`. Tries to buy `amount` of
|
||||||
|
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
|
||||||
|
/// to the `KyberNetworkProxy` contract, then transfers the bought
|
||||||
|
/// tokens to `to`.
|
||||||
|
/// @param toTokenAddress The token to give to `to`.
|
||||||
|
/// @param to The recipient of the bought tokens.
|
||||||
|
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
|
||||||
|
/// @param bridgeData The abi-encoeded "from" token address.
|
||||||
|
/// @return success The magic bytes if successful.
|
||||||
|
function bridgeTransferFrom(
|
||||||
|
address toTokenAddress,
|
||||||
|
address /* from */,
|
||||||
|
address to,
|
||||||
|
uint256 amount,
|
||||||
|
bytes calldata bridgeData
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
TradeState memory state;
|
||||||
|
state.kyber = _getKyberContract();
|
||||||
|
state.weth = _getWETHContract();
|
||||||
|
// Decode the bridge data to get the `fromTokenAddress`.
|
||||||
|
(state.fromTokenAddress) = abi.decode(bridgeData, (address));
|
||||||
|
state.fromTokenBalance = IERC20Token(state.fromTokenAddress).balanceOf(address(this));
|
||||||
|
if (state.fromTokenBalance == 0) {
|
||||||
|
// Do nothing if no input tokens.
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
}
|
||||||
|
state.minConversionRate = (10 ** 18) * amount / state.fromTokenBalance;
|
||||||
|
if (state.fromTokenAddress == toTokenAddress) {
|
||||||
|
// Just transfer the tokens if they're the same.
|
||||||
|
LibERC20Token.transfer(state.fromTokenAddress, to, state.fromTokenBalance);
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
} else if (state.fromTokenAddress != address(state.weth)) {
|
||||||
|
// If the input token is not WETH, grant an allowance to the exchange
|
||||||
|
// to spend them.
|
||||||
|
LibERC20Token.approve(state.fromTokenAddress, address(state.kyber), uint256(-1));
|
||||||
|
} else {
|
||||||
|
// If the input token is WETH, unwrap it and attach it to the call.
|
||||||
|
state.fromTokenAddress = KYBER_ETH_ADDRESS;
|
||||||
|
state.payableAmount = state.fromTokenBalance;
|
||||||
|
state.weth.withdraw(state.fromTokenBalance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to sell all of this contract's input token balance.
|
||||||
|
uint256 boughtAmount = state.kyber.trade.value(state.payableAmount)(
|
||||||
|
// Input token.
|
||||||
|
state.fromTokenAddress,
|
||||||
|
// Sell amount.
|
||||||
|
state.fromTokenBalance,
|
||||||
|
// Output token.
|
||||||
|
toTokenAddress == address(state.weth) ?
|
||||||
|
KYBER_ETH_ADDRESS :
|
||||||
|
toTokenAddress,
|
||||||
|
// Transfer to this contract if converting to ETH, otherwise
|
||||||
|
// transfer directly to the recipient.
|
||||||
|
toTokenAddress == address(state.weth) ?
|
||||||
|
address(uint160(address(this))) :
|
||||||
|
address(uint160(to)),
|
||||||
|
// Buy as much as possible.
|
||||||
|
uint256(-1),
|
||||||
|
// Minimum conversion rate.
|
||||||
|
state.minConversionRate,
|
||||||
|
// No affiliate address.
|
||||||
|
address(0)
|
||||||
|
);
|
||||||
|
// Wrap ETH output and transfer to recipient.
|
||||||
|
if (toTokenAddress == address(state.weth)) {
|
||||||
|
state.weth.deposit.value(boughtAmount)();
|
||||||
|
state.weth.transfer(to, boughtAmount);
|
||||||
|
}
|
||||||
|
return BRIDGE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev `SignatureType.Wallet` callback, so that this bridge can be the maker
|
||||||
|
/// and sign for itself in orders. Always succeeds.
|
||||||
|
/// @return magicValue Magic success bytes, always.
|
||||||
|
function isValidSignature(
|
||||||
|
bytes32,
|
||||||
|
bytes calldata
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (bytes4 magicValue)
|
||||||
|
{
|
||||||
|
return LEGACY_WALLET_MAGIC_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Overridable way to get the `KyberNetworkProxy` contract.
|
||||||
|
/// @return kyber The `IKyberNetworkProxy` contract.
|
||||||
|
function _getKyberContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IKyberNetworkProxy kyber)
|
||||||
|
{
|
||||||
|
return IKyberNetworkProxy(KYBER_NETWORK_PROXY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Overridable way to get the WETH contract.
|
||||||
|
/// @return weth The WETH contract.
|
||||||
|
function _getWETHContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IEtherToken weth)
|
||||||
|
{
|
||||||
|
return IEtherToken(WETH_ADDRESS);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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.5.9;
|
||||||
|
|
||||||
|
|
||||||
|
interface IKyberNetworkProxy {
|
||||||
|
|
||||||
|
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens.
|
||||||
|
/// @param sellTokenAddress Token to sell.
|
||||||
|
/// @param sellAmount Amount of tokens to sell.
|
||||||
|
/// @param buyTokenAddress Token to buy.
|
||||||
|
/// @param recipientAddress Address to send bought tokens to.
|
||||||
|
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
|
||||||
|
/// @param minConversionRate The minimal conversion rate. If actual rate
|
||||||
|
/// is lower, trade is canceled.
|
||||||
|
/// @param walletId The wallet ID to send part of the fees
|
||||||
|
/// @return boughtAmount Amount of tokens bought.
|
||||||
|
function trade(
|
||||||
|
address sellTokenAddress,
|
||||||
|
uint256 sellAmount,
|
||||||
|
address buyTokenAddress,
|
||||||
|
address payable recipientAddress,
|
||||||
|
uint256 maxBuyTokenAmount,
|
||||||
|
uint256 minConversionRate,
|
||||||
|
address walletId
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns(uint256 boughtAmount);
|
||||||
|
}
|
325
contracts/asset-proxy/contracts/test/TestKyberBridge.sol
Normal file
325
contracts/asset-proxy/contracts/test/TestKyberBridge.sol
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2019 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.5.9;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||||
|
import "../src/bridges/KyberBridge.sol";
|
||||||
|
import "../src/interfaces/IKyberNetworkProxy.sol";
|
||||||
|
|
||||||
|
|
||||||
|
// solhint-disable no-simple-event-func-name
|
||||||
|
interface ITestContract {
|
||||||
|
|
||||||
|
function wethWithdraw(
|
||||||
|
address payable ownerAddress,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external;
|
||||||
|
|
||||||
|
function wethDeposit(
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable;
|
||||||
|
|
||||||
|
function tokenTransfer(
|
||||||
|
address ownerAddress,
|
||||||
|
address recipientAddress,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool success);
|
||||||
|
|
||||||
|
function tokenApprove(
|
||||||
|
address ownerAddress,
|
||||||
|
address spenderAddress,
|
||||||
|
uint256 allowance
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool success);
|
||||||
|
|
||||||
|
function tokenBalanceOf(
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 balance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev A minimalist ERC20/WETH token.
|
||||||
|
contract TestToken {
|
||||||
|
|
||||||
|
ITestContract private _testContract;
|
||||||
|
|
||||||
|
constructor() public {
|
||||||
|
_testContract = ITestContract(msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function approve(address spender, uint256 allowance)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return _testContract.tokenApprove(
|
||||||
|
msg.sender,
|
||||||
|
spender,
|
||||||
|
allowance
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transfer(address recipient, uint256 amount)
|
||||||
|
external
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return _testContract.tokenTransfer(
|
||||||
|
msg.sender,
|
||||||
|
recipient,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function withdraw(uint256 amount)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
return _testContract.wethWithdraw(msg.sender, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deposit()
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
return _testContract.wethDeposit.value(msg.value)(msg.sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
function balanceOf(address owner)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256)
|
||||||
|
{
|
||||||
|
return _testContract.tokenBalanceOf(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Eth2DaiBridge overridden to mock tokens and
|
||||||
|
/// implement IEth2Dai.
|
||||||
|
contract TestKyberBridge is
|
||||||
|
KyberBridge,
|
||||||
|
ITestContract,
|
||||||
|
IKyberNetworkProxy
|
||||||
|
{
|
||||||
|
event KyberBridgeTrade(
|
||||||
|
uint256 msgValue,
|
||||||
|
address sellTokenAddress,
|
||||||
|
uint256 sellAmount,
|
||||||
|
address buyTokenAddress,
|
||||||
|
address payable recipientAddress,
|
||||||
|
uint256 maxBuyTokenAmount,
|
||||||
|
uint256 minConversionRate,
|
||||||
|
address walletId
|
||||||
|
);
|
||||||
|
|
||||||
|
event KyberBridgeWethWithdraw(
|
||||||
|
address ownerAddress,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
event KyberBridgeWethDeposit(
|
||||||
|
uint256 msgValue,
|
||||||
|
address ownerAddress,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
event KyberBridgeTokenApprove(
|
||||||
|
address tokenAddress,
|
||||||
|
address ownerAddress,
|
||||||
|
address spenderAddress,
|
||||||
|
uint256 allowance
|
||||||
|
);
|
||||||
|
|
||||||
|
event KyberBridgeTokenTransfer(
|
||||||
|
address tokenAddress,
|
||||||
|
address ownerAddress,
|
||||||
|
address recipientAddress,
|
||||||
|
uint256 amount
|
||||||
|
);
|
||||||
|
|
||||||
|
IEtherToken public weth;
|
||||||
|
mapping (address => mapping (address => uint256)) private _tokenBalances;
|
||||||
|
uint256 private _nextFillAmount;
|
||||||
|
|
||||||
|
constructor() public {
|
||||||
|
weth = IEtherToken(address(new TestToken()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Implementation of `IKyberNetworkProxy.trade()`
|
||||||
|
function trade(
|
||||||
|
address sellTokenAddress,
|
||||||
|
uint256 sellAmount,
|
||||||
|
address buyTokenAddress,
|
||||||
|
address payable recipientAddress,
|
||||||
|
uint256 maxBuyTokenAmount,
|
||||||
|
uint256 minConversionRate,
|
||||||
|
address walletId
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns(uint256 boughtAmount)
|
||||||
|
{
|
||||||
|
emit KyberBridgeTrade(
|
||||||
|
msg.value,
|
||||||
|
sellTokenAddress,
|
||||||
|
sellAmount,
|
||||||
|
buyTokenAddress,
|
||||||
|
recipientAddress,
|
||||||
|
maxBuyTokenAmount,
|
||||||
|
minConversionRate,
|
||||||
|
walletId
|
||||||
|
);
|
||||||
|
return _nextFillAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createToken()
|
||||||
|
external
|
||||||
|
returns (address tokenAddress)
|
||||||
|
{
|
||||||
|
return address(new TestToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
function setNextFillAmount(uint256 amount)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
if (msg.value != 0) {
|
||||||
|
require(amount == msg.value, "VALUE_AMOUNT_MISMATCH");
|
||||||
|
grantTokensTo(address(weth), address(this), msg.value);
|
||||||
|
}
|
||||||
|
_nextFillAmount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wethDeposit(
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
require(msg.sender == address(weth), "ONLY_WETH");
|
||||||
|
grantTokensTo(address(weth), ownerAddress, msg.value);
|
||||||
|
emit KyberBridgeWethDeposit(
|
||||||
|
msg.value,
|
||||||
|
ownerAddress,
|
||||||
|
msg.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function wethWithdraw(
|
||||||
|
address payable ownerAddress,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
{
|
||||||
|
require(msg.sender == address(weth), "ONLY_WETH");
|
||||||
|
_tokenBalances[address(weth)][ownerAddress] -= amount;
|
||||||
|
if (ownerAddress != address(this)) {
|
||||||
|
ownerAddress.transfer(amount);
|
||||||
|
}
|
||||||
|
emit KyberBridgeWethWithdraw(
|
||||||
|
ownerAddress,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenApprove(
|
||||||
|
address ownerAddress,
|
||||||
|
address spenderAddress,
|
||||||
|
uint256 allowance
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool success)
|
||||||
|
{
|
||||||
|
emit KyberBridgeTokenApprove(
|
||||||
|
msg.sender,
|
||||||
|
ownerAddress,
|
||||||
|
spenderAddress,
|
||||||
|
allowance
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenTransfer(
|
||||||
|
address ownerAddress,
|
||||||
|
address recipientAddress,
|
||||||
|
uint256 amount
|
||||||
|
)
|
||||||
|
external
|
||||||
|
returns (bool success)
|
||||||
|
{
|
||||||
|
_tokenBalances[msg.sender][ownerAddress] -= amount;
|
||||||
|
_tokenBalances[msg.sender][recipientAddress] += amount;
|
||||||
|
emit KyberBridgeTokenTransfer(
|
||||||
|
msg.sender,
|
||||||
|
ownerAddress,
|
||||||
|
recipientAddress,
|
||||||
|
amount
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenBalanceOf(
|
||||||
|
address ownerAddress
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 balance)
|
||||||
|
{
|
||||||
|
return _tokenBalances[msg.sender][ownerAddress];
|
||||||
|
}
|
||||||
|
|
||||||
|
function grantTokensTo(address tokenAddress, address ownerAddress, uint256 amount)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
{
|
||||||
|
_tokenBalances[tokenAddress][ownerAddress] += amount;
|
||||||
|
if (tokenAddress != address(weth)) {
|
||||||
|
// Send back ether if not WETH.
|
||||||
|
msg.sender.transfer(msg.value);
|
||||||
|
} else {
|
||||||
|
require(msg.value == amount, "VALUE_AMOUNT_MISMATCH");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @dev overridden to point to this contract.
|
||||||
|
function _getKyberContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IKyberNetworkProxy kyber)
|
||||||
|
{
|
||||||
|
return IKyberNetworkProxy(address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// @dev overridden to point to test WETH.
|
||||||
|
function _getWETHContract()
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (IEtherToken weth_)
|
||||||
|
{
|
||||||
|
return weth;
|
||||||
|
}
|
||||||
|
}
|
@ -38,8 +38,8 @@
|
|||||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,TestStaticCallTarget",
|
"publicInterfaceContracts": "ERC1155Proxy,ERC20Proxy,ERC721Proxy,MultiAssetProxy,StaticCallProxy,ERC20BridgeProxy,Eth2DaiBridge,IAssetData,IAssetProxy,UniswapBridge,KyberBridge,TestStaticCallTarget",
|
||||||
"abis": "./test/generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IUniswapExchange|IUniswapExchangeFactory|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
"abis": "./test/generated-artifacts/@(ERC1155Proxy|ERC20BridgeProxy|ERC20Proxy|ERC721Proxy|Eth2DaiBridge|IAssetData|IAssetProxy|IAssetProxyDispatcher|IAuthorizable|IERC20Bridge|IEth2Dai|IKyberNetworkProxy|IUniswapExchange|IUniswapExchangeFactory|KyberBridge|MixinAssetProxyDispatcher|MixinAuthorizable|MultiAssetProxy|Ownable|StaticCallProxy|TestERC20Bridge|TestEth2DaiBridge|TestKyberBridge|TestStaticCallTarget|TestUniswapBridge|UniswapBridge).json",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -12,6 +12,7 @@ import * as ERC721Proxy from '../generated-artifacts/ERC721Proxy.json';
|
|||||||
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
|
import * as Eth2DaiBridge from '../generated-artifacts/Eth2DaiBridge.json';
|
||||||
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
import * as IAssetData from '../generated-artifacts/IAssetData.json';
|
||||||
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
import * as IAssetProxy from '../generated-artifacts/IAssetProxy.json';
|
||||||
|
import * as KyberBridge from '../generated-artifacts/KyberBridge.json';
|
||||||
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
import * as MultiAssetProxy from '../generated-artifacts/MultiAssetProxy.json';
|
||||||
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
import * as StaticCallProxy from '../generated-artifacts/StaticCallProxy.json';
|
||||||
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
import * as TestStaticCallTarget from '../generated-artifacts/TestStaticCallTarget.json';
|
||||||
@ -27,5 +28,6 @@ export const artifacts = {
|
|||||||
IAssetData: IAssetData as ContractArtifact,
|
IAssetData: IAssetData as ContractArtifact,
|
||||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||||
|
KyberBridge: KyberBridge as ContractArtifact,
|
||||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@ -10,6 +10,7 @@ export * from '../generated-wrappers/erc721_proxy';
|
|||||||
export * from '../generated-wrappers/eth2_dai_bridge';
|
export * from '../generated-wrappers/eth2_dai_bridge';
|
||||||
export * from '../generated-wrappers/i_asset_data';
|
export * from '../generated-wrappers/i_asset_data';
|
||||||
export * from '../generated-wrappers/i_asset_proxy';
|
export * from '../generated-wrappers/i_asset_proxy';
|
||||||
|
export * from '../generated-wrappers/kyber_bridge';
|
||||||
export * from '../generated-wrappers/multi_asset_proxy';
|
export * from '../generated-wrappers/multi_asset_proxy';
|
||||||
export * from '../generated-wrappers/static_call_proxy';
|
export * from '../generated-wrappers/static_call_proxy';
|
||||||
export * from '../generated-wrappers/test_static_call_target';
|
export * from '../generated-wrappers/test_static_call_target';
|
||||||
|
@ -16,8 +16,10 @@ import * as IAssetProxyDispatcher from '../test/generated-artifacts/IAssetProxyD
|
|||||||
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
|
import * as IAuthorizable from '../test/generated-artifacts/IAuthorizable.json';
|
||||||
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
import * as IERC20Bridge from '../test/generated-artifacts/IERC20Bridge.json';
|
||||||
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
|
||||||
|
import * as IKyberNetworkProxy from '../test/generated-artifacts/IKyberNetworkProxy.json';
|
||||||
import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
|
import * as IUniswapExchange from '../test/generated-artifacts/IUniswapExchange.json';
|
||||||
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json';
|
import * as IUniswapExchangeFactory from '../test/generated-artifacts/IUniswapExchangeFactory.json';
|
||||||
|
import * as KyberBridge from '../test/generated-artifacts/KyberBridge.json';
|
||||||
import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
|
import * as MixinAssetProxyDispatcher from '../test/generated-artifacts/MixinAssetProxyDispatcher.json';
|
||||||
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
|
import * as MixinAuthorizable from '../test/generated-artifacts/MixinAuthorizable.json';
|
||||||
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
|
import * as MultiAssetProxy from '../test/generated-artifacts/MultiAssetProxy.json';
|
||||||
@ -25,6 +27,7 @@ import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
|||||||
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
|
import * as StaticCallProxy from '../test/generated-artifacts/StaticCallProxy.json';
|
||||||
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
|
import * as TestERC20Bridge from '../test/generated-artifacts/TestERC20Bridge.json';
|
||||||
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
import * as TestEth2DaiBridge from '../test/generated-artifacts/TestEth2DaiBridge.json';
|
||||||
|
import * as TestKyberBridge from '../test/generated-artifacts/TestKyberBridge.json';
|
||||||
import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
|
import * as TestStaticCallTarget from '../test/generated-artifacts/TestStaticCallTarget.json';
|
||||||
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
import * as TestUniswapBridge from '../test/generated-artifacts/TestUniswapBridge.json';
|
||||||
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
|
import * as UniswapBridge from '../test/generated-artifacts/UniswapBridge.json';
|
||||||
@ -39,6 +42,7 @@ export const artifacts = {
|
|||||||
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
MultiAssetProxy: MultiAssetProxy as ContractArtifact,
|
||||||
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
StaticCallProxy: StaticCallProxy as ContractArtifact,
|
||||||
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
Eth2DaiBridge: Eth2DaiBridge as ContractArtifact,
|
||||||
|
KyberBridge: KyberBridge as ContractArtifact,
|
||||||
UniswapBridge: UniswapBridge as ContractArtifact,
|
UniswapBridge: UniswapBridge as ContractArtifact,
|
||||||
IAssetData: IAssetData as ContractArtifact,
|
IAssetData: IAssetData as ContractArtifact,
|
||||||
IAssetProxy: IAssetProxy as ContractArtifact,
|
IAssetProxy: IAssetProxy as ContractArtifact,
|
||||||
@ -46,10 +50,12 @@ export const artifacts = {
|
|||||||
IAuthorizable: IAuthorizable as ContractArtifact,
|
IAuthorizable: IAuthorizable as ContractArtifact,
|
||||||
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
IERC20Bridge: IERC20Bridge as ContractArtifact,
|
||||||
IEth2Dai: IEth2Dai as ContractArtifact,
|
IEth2Dai: IEth2Dai as ContractArtifact,
|
||||||
|
IKyberNetworkProxy: IKyberNetworkProxy as ContractArtifact,
|
||||||
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
IUniswapExchange: IUniswapExchange as ContractArtifact,
|
||||||
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
IUniswapExchangeFactory: IUniswapExchangeFactory as ContractArtifact,
|
||||||
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
TestERC20Bridge: TestERC20Bridge as ContractArtifact,
|
||||||
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
TestEth2DaiBridge: TestEth2DaiBridge as ContractArtifact,
|
||||||
|
TestKyberBridge: TestKyberBridge as ContractArtifact,
|
||||||
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
TestStaticCallTarget: TestStaticCallTarget as ContractArtifact,
|
||||||
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
TestUniswapBridge: TestUniswapBridge as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
272
contracts/asset-proxy/test/kyber_bridge.ts
Normal file
272
contracts/asset-proxy/test/kyber_bridge.ts
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
getRandomInteger,
|
||||||
|
hexLeftPad,
|
||||||
|
hexRandom,
|
||||||
|
randomAddress,
|
||||||
|
verifyEventsFromLogs,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { AssetProxyId } from '@0x/types';
|
||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import { DecodedLogs } from 'ethereum-types';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { artifacts } from './artifacts';
|
||||||
|
|
||||||
|
import { TestKyberBridgeContract, TestKyberBridgeEvents } from './wrappers';
|
||||||
|
|
||||||
|
blockchainTests.resets('KyberBridge unit tests', env => {
|
||||||
|
const KYBER_ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
|
||||||
|
let testContract: TestKyberBridgeContract;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
testContract = await TestKyberBridgeContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestKyberBridge,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isValidSignature()', () => {
|
||||||
|
it('returns success bytes', async () => {
|
||||||
|
const LEGACY_WALLET_MAGIC_VALUE = '0xb0671381';
|
||||||
|
const result = await testContract.isValidSignature(hexRandom(), hexRandom(_.random(0, 32))).callAsync();
|
||||||
|
expect(result).to.eq(LEGACY_WALLET_MAGIC_VALUE);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bridgeTransferFrom()', () => {
|
||||||
|
let fromTokenAddress: string;
|
||||||
|
let toTokenAddress: string;
|
||||||
|
let wethAddress: string;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
wethAddress = await testContract.weth().callAsync();
|
||||||
|
fromTokenAddress = await testContract.createToken().callAsync();
|
||||||
|
await testContract.createToken().awaitTransactionSuccessAsync();
|
||||||
|
toTokenAddress = await testContract.createToken().callAsync();
|
||||||
|
await testContract.createToken().awaitTransactionSuccessAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
const STATIC_KYBER_TRADE_ARGS = {
|
||||||
|
maxBuyTokenAmount: constants.MAX_UINT256,
|
||||||
|
walletId: constants.NULL_ADDRESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
interface TransferFromOpts {
|
||||||
|
toTokenAddress: string;
|
||||||
|
fromTokenAddress: string;
|
||||||
|
toAddress: string;
|
||||||
|
// Amount to pass into `bridgeTransferFrom()`
|
||||||
|
amount: BigNumber;
|
||||||
|
// Amount to convert in `trade()`.
|
||||||
|
fillAmount: BigNumber;
|
||||||
|
// Token balance of the bridge.
|
||||||
|
fromTokenBalance: BigNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransferFromResult {
|
||||||
|
opts: TransferFromOpts;
|
||||||
|
result: string;
|
||||||
|
logs: DecodedLogs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTransferFromOpts(opts?: Partial<TransferFromOpts>): TransferFromOpts {
|
||||||
|
return {
|
||||||
|
fromTokenAddress,
|
||||||
|
toTokenAddress,
|
||||||
|
toAddress: randomAddress(),
|
||||||
|
amount: getRandomInteger(1, 10e18),
|
||||||
|
fillAmount: getRandomInteger(1, 10e18),
|
||||||
|
fromTokenBalance: getRandomInteger(1, 10e18),
|
||||||
|
...opts,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function withdrawToAsync(opts?: Partial<TransferFromOpts>): Promise<TransferFromResult> {
|
||||||
|
const _opts = createTransferFromOpts(opts);
|
||||||
|
// Fund the contract with input tokens.
|
||||||
|
await testContract
|
||||||
|
.grantTokensTo(_opts.fromTokenAddress, testContract.address, _opts.fromTokenBalance)
|
||||||
|
.awaitTransactionSuccessAsync({ value: _opts.fromTokenBalance });
|
||||||
|
// Fund the contract with output tokens.
|
||||||
|
await testContract.setNextFillAmount(_opts.fillAmount).awaitTransactionSuccessAsync({
|
||||||
|
value: _opts.toTokenAddress === wethAddress ? _opts.fillAmount : constants.ZERO_AMOUNT,
|
||||||
|
});
|
||||||
|
// Call bridgeTransferFrom().
|
||||||
|
const bridgeTransferFromFn = testContract.bridgeTransferFrom(
|
||||||
|
// Output token
|
||||||
|
_opts.toTokenAddress,
|
||||||
|
// Random maker address.
|
||||||
|
randomAddress(),
|
||||||
|
// Recipient address.
|
||||||
|
_opts.toAddress,
|
||||||
|
// Transfer amount.
|
||||||
|
_opts.amount,
|
||||||
|
// ABI-encode the input token address as the bridge data.
|
||||||
|
hexLeftPad(_opts.fromTokenAddress),
|
||||||
|
);
|
||||||
|
const result = await bridgeTransferFromFn.callAsync();
|
||||||
|
const { logs } = await bridgeTransferFromFn.awaitTransactionSuccessAsync();
|
||||||
|
return {
|
||||||
|
opts: _opts,
|
||||||
|
result,
|
||||||
|
logs: (logs as any) as DecodedLogs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMinimumConversionRate(opts: TransferFromOpts): BigNumber {
|
||||||
|
return opts.amount
|
||||||
|
.times(constants.ONE_ETHER)
|
||||||
|
.div(opts.fromTokenBalance)
|
||||||
|
.integerValue(BigNumber.ROUND_DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('returns magic bytes on success', async () => {
|
||||||
|
const BRIDGE_SUCCESS_RETURN_DATA = AssetProxyId.ERC20Bridge;
|
||||||
|
const { result } = await withdrawToAsync();
|
||||||
|
expect(result).to.eq(BRIDGE_SUCCESS_RETURN_DATA);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can trade token -> token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync();
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
sellTokenAddress: opts.fromTokenAddress,
|
||||||
|
buyTokenAddress: opts.toTokenAddress,
|
||||||
|
sellAmount: opts.fromTokenBalance,
|
||||||
|
recipientAddress: opts.toAddress,
|
||||||
|
minConversionRate: getMinimumConversionRate(opts),
|
||||||
|
msgValue: constants.ZERO_AMOUNT,
|
||||||
|
...STATIC_KYBER_TRADE_ARGS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can trade token -> ETH', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
toTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
sellTokenAddress: opts.fromTokenAddress,
|
||||||
|
buyTokenAddress: KYBER_ETH_ADDRESS,
|
||||||
|
sellAmount: opts.fromTokenBalance,
|
||||||
|
recipientAddress: testContract.address,
|
||||||
|
minConversionRate: getMinimumConversionRate(opts),
|
||||||
|
msgValue: constants.ZERO_AMOUNT,
|
||||||
|
...STATIC_KYBER_TRADE_ARGS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can trade ETH -> token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
sellTokenAddress: KYBER_ETH_ADDRESS,
|
||||||
|
buyTokenAddress: opts.toTokenAddress,
|
||||||
|
sellAmount: opts.fromTokenBalance,
|
||||||
|
recipientAddress: opts.toAddress,
|
||||||
|
minConversionRate: getMinimumConversionRate(opts),
|
||||||
|
msgValue: opts.fromTokenBalance,
|
||||||
|
...STATIC_KYBER_TRADE_ARGS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTrade,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does nothing if bridge has no token balance', async () => {
|
||||||
|
const { logs } = await withdrawToAsync({
|
||||||
|
fromTokenBalance: constants.ZERO_AMOUNT,
|
||||||
|
});
|
||||||
|
expect(logs).to.be.length(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only transfers the token if trading the same token', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
toTokenAddress: fromTokenAddress,
|
||||||
|
});
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
tokenAddress: fromTokenAddress,
|
||||||
|
ownerAddress: testContract.address,
|
||||||
|
recipientAddress: opts.toAddress,
|
||||||
|
amount: opts.fromTokenBalance,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTokenTransfer,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('grants Kyber an allowance when selling non-WETH', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync();
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
tokenAddress: opts.fromTokenAddress,
|
||||||
|
ownerAddress: testContract.address,
|
||||||
|
spenderAddress: testContract.address,
|
||||||
|
allowance: constants.MAX_UINT256,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestKyberBridgeEvents.KyberBridgeTokenApprove,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not grant Kyber an allowance when selling WETH', async () => {
|
||||||
|
const { logs } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
verifyEventsFromLogs(logs, [], TestKyberBridgeEvents.KyberBridgeTokenApprove);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('withdraws WETH and passes it to Kyber when selling WETH', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
fromTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethWithdraw);
|
||||||
|
expect(logs[0].args).to.deep.eq({
|
||||||
|
ownerAddress: testContract.address,
|
||||||
|
amount: opts.fromTokenBalance,
|
||||||
|
});
|
||||||
|
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
|
||||||
|
expect(logs[1].args.msgValue).to.bignumber.eq(opts.fromTokenBalance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('wraps WETH and transfers it to the recipient when buyng WETH', async () => {
|
||||||
|
const { opts, logs } = await withdrawToAsync({
|
||||||
|
toTokenAddress: wethAddress,
|
||||||
|
});
|
||||||
|
expect(logs[0].event).to.eq(TestKyberBridgeEvents.KyberBridgeTokenApprove);
|
||||||
|
expect(logs[0].args.tokenAddress).to.eq(opts.fromTokenAddress);
|
||||||
|
expect(logs[1].event).to.eq(TestKyberBridgeEvents.KyberBridgeTrade);
|
||||||
|
expect(logs[1].args.recipientAddress).to.eq(testContract.address);
|
||||||
|
expect(logs[2].event).to.eq(TestKyberBridgeEvents.KyberBridgeWethDeposit);
|
||||||
|
expect(logs[2].args).to.deep.eq({
|
||||||
|
msgValue: opts.fillAmount,
|
||||||
|
ownerAddress: testContract.address,
|
||||||
|
amount: opts.fillAmount,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -14,8 +14,10 @@ export * from '../test/generated-wrappers/i_asset_proxy_dispatcher';
|
|||||||
export * from '../test/generated-wrappers/i_authorizable';
|
export * from '../test/generated-wrappers/i_authorizable';
|
||||||
export * from '../test/generated-wrappers/i_erc20_bridge';
|
export * from '../test/generated-wrappers/i_erc20_bridge';
|
||||||
export * from '../test/generated-wrappers/i_eth2_dai';
|
export * from '../test/generated-wrappers/i_eth2_dai';
|
||||||
|
export * from '../test/generated-wrappers/i_kyber_network_proxy';
|
||||||
export * from '../test/generated-wrappers/i_uniswap_exchange';
|
export * from '../test/generated-wrappers/i_uniswap_exchange';
|
||||||
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
|
export * from '../test/generated-wrappers/i_uniswap_exchange_factory';
|
||||||
|
export * from '../test/generated-wrappers/kyber_bridge';
|
||||||
export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher';
|
export * from '../test/generated-wrappers/mixin_asset_proxy_dispatcher';
|
||||||
export * from '../test/generated-wrappers/mixin_authorizable';
|
export * from '../test/generated-wrappers/mixin_authorizable';
|
||||||
export * from '../test/generated-wrappers/multi_asset_proxy';
|
export * from '../test/generated-wrappers/multi_asset_proxy';
|
||||||
@ -23,6 +25,7 @@ export * from '../test/generated-wrappers/ownable';
|
|||||||
export * from '../test/generated-wrappers/static_call_proxy';
|
export * from '../test/generated-wrappers/static_call_proxy';
|
||||||
export * from '../test/generated-wrappers/test_erc20_bridge';
|
export * from '../test/generated-wrappers/test_erc20_bridge';
|
||||||
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
export * from '../test/generated-wrappers/test_eth2_dai_bridge';
|
||||||
|
export * from '../test/generated-wrappers/test_kyber_bridge';
|
||||||
export * from '../test/generated-wrappers/test_static_call_target';
|
export * from '../test/generated-wrappers/test_static_call_target';
|
||||||
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
export * from '../test/generated-wrappers/test_uniswap_bridge';
|
||||||
export * from '../test/generated-wrappers/uniswap_bridge';
|
export * from '../test/generated-wrappers/uniswap_bridge';
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"generated-artifacts/Eth2DaiBridge.json",
|
"generated-artifacts/Eth2DaiBridge.json",
|
||||||
"generated-artifacts/IAssetData.json",
|
"generated-artifacts/IAssetData.json",
|
||||||
"generated-artifacts/IAssetProxy.json",
|
"generated-artifacts/IAssetProxy.json",
|
||||||
|
"generated-artifacts/KyberBridge.json",
|
||||||
"generated-artifacts/MultiAssetProxy.json",
|
"generated-artifacts/MultiAssetProxy.json",
|
||||||
"generated-artifacts/StaticCallProxy.json",
|
"generated-artifacts/StaticCallProxy.json",
|
||||||
"generated-artifacts/TestStaticCallTarget.json",
|
"generated-artifacts/TestStaticCallTarget.json",
|
||||||
@ -25,8 +26,10 @@
|
|||||||
"test/generated-artifacts/IAuthorizable.json",
|
"test/generated-artifacts/IAuthorizable.json",
|
||||||
"test/generated-artifacts/IERC20Bridge.json",
|
"test/generated-artifacts/IERC20Bridge.json",
|
||||||
"test/generated-artifacts/IEth2Dai.json",
|
"test/generated-artifacts/IEth2Dai.json",
|
||||||
|
"test/generated-artifacts/IKyberNetworkProxy.json",
|
||||||
"test/generated-artifacts/IUniswapExchange.json",
|
"test/generated-artifacts/IUniswapExchange.json",
|
||||||
"test/generated-artifacts/IUniswapExchangeFactory.json",
|
"test/generated-artifacts/IUniswapExchangeFactory.json",
|
||||||
|
"test/generated-artifacts/KyberBridge.json",
|
||||||
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
|
"test/generated-artifacts/MixinAssetProxyDispatcher.json",
|
||||||
"test/generated-artifacts/MixinAuthorizable.json",
|
"test/generated-artifacts/MixinAuthorizable.json",
|
||||||
"test/generated-artifacts/MultiAssetProxy.json",
|
"test/generated-artifacts/MultiAssetProxy.json",
|
||||||
@ -34,6 +37,7 @@
|
|||||||
"test/generated-artifacts/StaticCallProxy.json",
|
"test/generated-artifacts/StaticCallProxy.json",
|
||||||
"test/generated-artifacts/TestERC20Bridge.json",
|
"test/generated-artifacts/TestERC20Bridge.json",
|
||||||
"test/generated-artifacts/TestEth2DaiBridge.json",
|
"test/generated-artifacts/TestEth2DaiBridge.json",
|
||||||
|
"test/generated-artifacts/TestKyberBridge.json",
|
||||||
"test/generated-artifacts/TestStaticCallTarget.json",
|
"test/generated-artifacts/TestStaticCallTarget.json",
|
||||||
"test/generated-artifacts/TestUniswapBridge.json",
|
"test/generated-artifacts/TestUniswapBridge.json",
|
||||||
"test/generated-artifacts/UniswapBridge.json"
|
"test/generated-artifacts/UniswapBridge.json"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user