@0x/contracts-asset-proxy: Emit ERC20BridgeTransfer events in bridges.

This commit is contained in:
Lawrence Forman 2020-03-04 20:57:44 -05:00
parent b604e2bd4e
commit 3f51b9322f
9 changed files with 130 additions and 29 deletions

View File

@ -3,7 +3,12 @@
"version": "3.3.0",
"changes": [
{
"note": "Use `approveIfBelowMax()` in DEX bridges for for approvals."
"note": "Use `approveIfBelowMax()` in DEX bridges for for approvals.",
"pr": 2512
},
{
"note": "Emit `ERC20BridgeTransfer` events in bridges.",
"pr": 2512
}
]
},

View File

@ -70,6 +70,16 @@ contract ChaiBridge is
// This will never fail if the `draw` call was successful
IERC20Token(_getDaiAddress()).transfer(to, amount);
emit ERC20BridgeTransfer(
address(this),
_getChaiAddress(),
_getDaiAddress(),
// draw() can change the rate so this is tricky to compute.
0,
amount,
from,
to
);
return BRIDGE_SUCCESS;
}
}

View File

@ -35,18 +35,26 @@ contract CurveBridge is
IWallet,
DeploymentConstants
{
struct CurveBridgeData {
address curveAddress;
int128 fromCoinIdx;
int128 toCoinIdx;
int128 version;
}
/// @dev Callback for `ICurve`. Tries to buy `amount` of
/// `toTokenAddress` tokens by selling the entirety of the opposing asset
/// (DAI, USDC) to the Curve contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to` (i.e DAI, USDC, USDT).
/// @param from The maker (this contract).
/// @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 from,
address to,
uint256 amount,
bytes calldata bridgeData
@ -55,32 +63,32 @@ contract CurveBridge is
returns (bytes4 success)
{
// Decode the bridge data to get the Curve metadata.
(address curveAddress, int128 fromCoinIdx, int128 toCoinIdx, int128 version) = abi.decode(bridgeData, (address, int128, int128, int128));
ICurve exchange = ICurve(curveAddress);
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
address fromTokenAddress = exchange.underlying_coins(fromCoinIdx);
address fromTokenAddress = ICurve(data.curveAddress).underlying_coins(data.fromCoinIdx);
require(toTokenAddress != fromTokenAddress, "CurveBridge/INVALID_PAIR");
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
LibERC20Token.approveIfBelowMax(fromTokenAddress, address(exchange));
LibERC20Token.approveIfBelowMax(fromTokenAddress, data.curveAddress);
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
// Try to sell all of this contract's `fromTokenAddress` token balance.
if (version == 0) {
exchange.exchange_underlying(
fromCoinIdx,
toCoinIdx,
if (data.version == 0) {
ICurve(data.curveAddress).exchange_underlying(
data.fromCoinIdx,
data.toCoinIdx,
// dx
IERC20Token(fromTokenAddress).balanceOf(address(this)),
fromTokenBalance,
// min dy
amount,
// expires
block.timestamp + 1
);
} else {
exchange.exchange_underlying(
fromCoinIdx,
toCoinIdx,
ICurve(data.curveAddress).exchange_underlying(
data.fromCoinIdx,
data.toCoinIdx,
// dx
IERC20Token(fromTokenAddress).balanceOf(address(this)),
fromTokenBalance,
// min dy
amount
);
@ -89,6 +97,16 @@ contract CurveBridge is
uint256 toTokenBalance = IERC20Token(toTokenAddress).balanceOf(address(this));
// Transfer the converted `toToken`s to `to`.
LibERC20Token.transfer(toTokenAddress, to, toTokenBalance);
emit ERC20BridgeTransfer(
address(this),
fromTokenAddress,
toTokenAddress,
fromTokenBalance,
toTokenBalance,
from,
to
);
return BRIDGE_SUCCESS;
}

View File

@ -44,13 +44,14 @@ contract DydxBridge is
/// 5. Withdrawals from dydx are made to the `to` address.
/// 6. Calling this function must always withdraw at least `amount`,
/// otherwise the `ERC20Bridge` will revert.
/// @param toTokenAddress The token to be withdrawn.
/// @param from The sender of the tokens and owner of the dydx account.
/// @param to The recipient of the tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to deposit or withdraw.
/// @param encodedBridgeData An abi-encoded `BridgeData` struct.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address,
address toTokenAddress,
address from,
address to,
uint256 amount,
@ -81,6 +82,18 @@ contract DydxBridge is
// Run operation. This will revert on failure.
IDydx(_getDydxAddress()).operate(accounts, actions);
emit ERC20BridgeTransfer(
address(this),
// The from token isn't necessarily clear since we can have zero
// or more deposits.
address(0),
toTokenAddress,
0,
amount,
from,
to
);
return BRIDGE_SUCCESS;
}

View File

@ -38,13 +38,14 @@ contract Eth2DaiBridge is
/// (DAI or WETH) to the Eth2Dai contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to` (either DAI or WETH).
/// @param from The maker (this contract).
/// @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 from,
address to,
uint256 amount,
bytes calldata bridgeData
@ -59,15 +60,27 @@ contract Eth2DaiBridge is
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
LibERC20Token.approveIfBelowMax(fromTokenAddress, address(exchange));
uint256 fromTokenBalance = IERC20Token(fromTokenAddress).balanceOf(address(this));
// Try to sell all of this contract's `fromTokenAddress` token balance.
uint256 boughtAmount = exchange.sellAllAmount(
fromTokenAddress,
IERC20Token(fromTokenAddress).balanceOf(address(this)),
fromTokenBalance,
toTokenAddress,
amount
);
// Transfer the converted `toToken`s to `to`.
LibERC20Token.transfer(toTokenAddress, to, boughtAmount);
emit ERC20BridgeTransfer(
address(this),
fromTokenAddress,
toTokenAddress,
fromTokenBalance,
boughtAmount,
from,
to
);
return BRIDGE_SUCCESS;
}

View File

@ -66,13 +66,14 @@ contract KyberBridge is
/// to the `KyberNetworkProxy` contract, then transfers the bought
/// tokens to `to`.
/// @param toTokenAddress The token to give to `to`.
/// @param from The maker (this contract).
/// @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 from,
address to,
uint256 amount,
bytes calldata bridgeData
@ -143,6 +144,16 @@ contract KyberBridge is
state.weth.deposit.value(boughtAmount)();
state.weth.transfer(to, boughtAmount);
}
emit ERC20BridgeTransfer(
address(this),
state.fromTokenAddress == KYBER_ETH_ADDRESS ? address(state.weth) : state.fromTokenAddress,
toTokenAddress,
state.fromTokenBalance,
boughtAmount,
from,
to
);
return BRIDGE_SUCCESS;
}

View File

@ -38,10 +38,11 @@ contract UniswapBridge is
{
// Struct to hold `bridgeTransferFrom()` local variables in memory and to avoid
// stack overflows.
struct WithdrawToState {
struct TransferState {
IUniswapExchange exchange;
uint256 fromTokenBalance;
IEtherToken weth;
uint256 boughtAmount;
}
// solhint-disable no-empty-blocks
@ -55,13 +56,14 @@ contract UniswapBridge is
/// `toTokenAddress` tokens by selling the entirety of the `fromTokenAddress`
/// token encoded in the bridge data.
/// @param toTokenAddress The token to buy and transfer to `to`.
/// @param from The maker (this contract).
/// @param to The recipient of the bought tokens.
/// @param amount Minimum amount of `toTokenAddress` tokens to buy.
/// @param bridgeData The abi-encoded "from" token address.
/// @return success The magic bytes if successful.
function bridgeTransferFrom(
address toTokenAddress,
address /* from */,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
@ -70,7 +72,7 @@ contract UniswapBridge is
returns (bytes4 success)
{
// State memory object to avoid stack overflows.
WithdrawToState memory state;
TransferState memory state;
// Decode the bridge data to get the `fromTokenAddress`.
(address fromTokenAddress) = abi.decode(bridgeData, (address));
@ -96,7 +98,7 @@ contract UniswapBridge is
state.weth.withdraw(state.fromTokenBalance);
// Buy as much of `toTokenAddress` token with ETH as possible and
// transfer it to `to`.
state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
state.boughtAmount = state.exchange.ethToTokenTransferInput.value(state.fromTokenBalance)(
// Minimum buy amount.
amount,
// Expires after this block.
@ -110,7 +112,7 @@ contract UniswapBridge is
// Grant the exchange an allowance.
_grantExchangeAllowance(state.exchange, fromTokenAddress);
// Buy as much ETH with `fromTokenAddress` token as possible.
uint256 ethBought = state.exchange.tokenToEthSwapInput(
state.boughtAmount = state.exchange.tokenToEthSwapInput(
// Sell all tokens we hold.
state.fromTokenBalance,
// Minimum buy amount.
@ -119,9 +121,9 @@ contract UniswapBridge is
block.timestamp
);
// Wrap the ETH.
state.weth.deposit.value(ethBought)();
state.weth.deposit.value(state.boughtAmount)();
// Transfer the WETH to `to`.
IEtherToken(toTokenAddress).transfer(to, ethBought);
IEtherToken(toTokenAddress).transfer(to, state.boughtAmount);
// Convert from one token to another.
} else {
@ -129,7 +131,7 @@ contract UniswapBridge is
_grantExchangeAllowance(state.exchange, fromTokenAddress);
// Buy as much `toTokenAddress` token with `fromTokenAddress` token
// and transfer it to `to`.
state.exchange.tokenToTokenTransferInput(
state.boughtAmount = state.exchange.tokenToTokenTransferInput(
// Sell all tokens we hold.
state.fromTokenBalance,
// Minimum buy amount.
@ -144,6 +146,16 @@ contract UniswapBridge is
toTokenAddress
);
}
emit ERC20BridgeTransfer(
address(this),
fromTokenAddress,
toTokenAddress,
state.fromTokenBalance,
state.boughtAmount,
from,
to
);
return BRIDGE_SUCCESS;
}

View File

@ -21,7 +21,25 @@ pragma solidity ^0.5.9;
contract IERC20Bridge {
// @dev Result of a successful bridge call.
/// @dev Emitted when a bridge transfer is completed.
/// @param bridge The bridge address.
/// @param fromToken The address of the "from" token.
/// @param toToken The address of the "to" token.
/// @param fromTokenAmount The "from" token amount consumed.
/// @param toTokenAmount The "to" token amount transferred.
/// @param from Supplier of "fromToken".
/// @param to Receiver of "toToken".
event ERC20BridgeTransfer(
address indexed bridge,
address fromToken,
address toToken,
uint256 fromTokenAmount,
uint256 toTokenAmount,
address from,
address to
);
/// @dev Result of a successful bridge call.
bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3;
/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.

View File

@ -3,7 +3,8 @@
"version": "3.2.0",
"changes": [
{
"note": "Add `LibERC20Token.approveIfBelowMax()`"
"note": "Add `LibERC20Token.approveIfBelowMax()`",
"pr": 2512
}
]
},