Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

@0x/contracts-erc20-bridge-sampler: Generic liquidity provider sampling #2487

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ contract ChaiBridge is
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
/// @return success The magic bytes `0x37708e9b` if successful.
/// @return success The magic bytes `0xdc1600f3` if successful.
moodlezoup marked this conversation as resolved.
Show resolved Hide resolved
function bridgeTransferFrom(
address /* tokenAddress */,
address from,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ contract IERC20Bridge {
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
/// @return success The magic bytes `0x37708e9b` if successful.
/// @return success The magic bytes `0xdc1600f3` if successful.
function bridgeTransferFrom(
address tokenAddress,
address from,
Expand Down
9 changes: 9 additions & 0 deletions contracts/erc20-bridge-sampler/CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
[
{
"version": "1.5.0",
"changes": [
{
"note": "Add generic liquidity provider sampling",
"pr": 2487
}
]
},
{
"timestamp": 1582677073,
"version": "1.4.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import "./IEth2Dai.sol";
import "./IKyberNetwork.sol";
import "./IUniswapExchangeQuotes.sol";
import "./ICurve.sol";
import "./ILiquidityProvider.sol";


contract ERC20BridgeSampler is
Expand All @@ -47,6 +48,8 @@ contract ERC20BridgeSampler is
/// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
/// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
uint256 constant internal CURVE_CALL_GAS = 600e3; // 600k
/// @dev Default gas limit for liquidity provider calls.
uint256 constant internal DEFAULT_CALL_GAS = 200e3; // 200k

/// @dev Call multiple public functions on this contract in a single transaction.
/// @param callDatas ABI-encoded call data for each function call.
Expand Down Expand Up @@ -431,6 +434,84 @@ contract ERC20BridgeSampler is
}
}

/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
/// @param providerAddress Address of the liquidity provider contract.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromLiquidityProvider(
address providerAddress,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
providerAddress.staticcall.gas(DEFAULT_CALL_GAS)(
abi.encodeWithSelector(
ILiquidityProvider(0).getSellQuote.selector,
takerToken,
makerToken,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
moodlezoup marked this conversation as resolved.
Show resolved Hide resolved
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
} else {
// Exit early if the amount is too high for the liquidity provider to serve
break;
moodlezoup marked this conversation as resolved.
Show resolved Hide resolved
}
makerTokenAmounts[i] = buyAmount;
}
}

/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
/// @param providerAddress Address of the liquidity provider contract.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromLiquidityProvider(
address providerAddress,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
providerAddress.staticcall.gas(DEFAULT_CALL_GAS)(
abi.encodeWithSelector(
ILiquidityProvider(0).getBuyQuote.selector,
takerToken,
makerToken,
makerTokenAmounts[i]
));
uint256 sellAmount = 0;
if (didSucceed) {
sellAmount = abi.decode(resultData, (uint256));
} else {
// Exit early if the amount is too high for the liquidity provider to serve
break;
}
takerTokenAmounts[i] = sellAmount;
}
}

/// @dev Overridable way to get token decimals.
/// @param tokenAddress Address of the token.
/// @return decimals The decimal places for the token.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,38 @@ interface IERC20BridgeSampler {
external
view
returns (uint256[] memory makerTokenAmounts);

/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
/// @param providerAddress Address of the liquidity provider contract.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromLiquidityProvider(
address providerAddress,
address takerToken,
address makerToken,
uint256[] calldata takerTokenAmounts
)
external
view
returns (uint256[] memory makerTokenAmounts);

/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
/// @param providerAddress Address of the liquidity provider contract.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromLiquidityProvider(
address providerAddress,
address takerToken,
address makerToken,
uint256[] calldata makerTokenAmounts
)
external
view
returns (uint256[] memory takerTokenAmounts);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*

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 ILiquidityProvider {

/// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
/// @param tokenAddress The address of the ERC20 token to transfer.
/// @param from Address to transfer asset from.
/// @param to Address to transfer asset to.
/// @param amount Amount of asset to transfer.
/// @param bridgeData Arbitrary asset data needed by the bridge contract.
/// @return success The magic bytes `0xdc1600f3` if successful.
function bridgeTransferFrom(
moodlezoup marked this conversation as resolved.
Show resolved Hide resolved
address tokenAddress,
address from,
address to,
uint256 amount,
bytes calldata bridgeData
)
external
returns (bytes4 success);

/// @dev Quotes the amount of `makerToken` that would be obtained by
/// selling `sellAmount` of `takerToken`.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param sellAmount Amount of `takerToken` to sell.
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
function getSellQuote(
address takerToken,
address makerToken,
uint256 sellAmount
)
external
view
returns (uint256 makerTokenAmount);

/// @dev Quotes the amount of `takerToken` that would need to be sold in
/// order to obtain `buyAmount` of `makerToken`.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param buyAmount Amount of `makerToken` to buy.
/// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
function getBuyQuote(
address takerToken,
address makerToken,
uint256 buyAmount
)
external
view
returns (uint256 takerTokenAmount);
}
2 changes: 1 addition & 1 deletion contracts/erc20-bridge-sampler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,IERC20BridgeSampler",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
"abis": "./test/generated-artifacts/@(ERC20BridgeSampler|ICurve|IDevUtils|IERC20BridgeSampler|IEth2Dai|IKyberNetwork|ILiquidityProvider|IUniswapExchangeQuotes|TestERC20BridgeSampler).json"
},
"repository": {
"type": "git",
Expand Down
2 changes: 2 additions & 0 deletions contracts/erc20-bridge-sampler/test/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as IDevUtils from '../test/generated-artifacts/IDevUtils.json';
import * as IERC20BridgeSampler from '../test/generated-artifacts/IERC20BridgeSampler.json';
import * as IEth2Dai from '../test/generated-artifacts/IEth2Dai.json';
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
import * as ILiquidityProvider from '../test/generated-artifacts/ILiquidityProvider.json';
import * as IUniswapExchangeQuotes from '../test/generated-artifacts/IUniswapExchangeQuotes.json';
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
export const artifacts = {
Expand All @@ -20,6 +21,7 @@ export const artifacts = {
IERC20BridgeSampler: IERC20BridgeSampler as ContractArtifact,
IEth2Dai: IEth2Dai as ContractArtifact,
IKyberNetwork: IKyberNetwork as ContractArtifact,
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
};
1 change: 1 addition & 0 deletions contracts/erc20-bridge-sampler/test/wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export * from '../test/generated-wrappers/i_dev_utils';
export * from '../test/generated-wrappers/i_erc20_bridge_sampler';
export * from '../test/generated-wrappers/i_eth2_dai';
export * from '../test/generated-wrappers/i_kyber_network';
export * from '../test/generated-wrappers/i_liquidity_provider';
export * from '../test/generated-wrappers/i_uniswap_exchange_quotes';
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
1 change: 1 addition & 0 deletions contracts/erc20-bridge-sampler/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"test/generated-artifacts/IERC20BridgeSampler.json",
"test/generated-artifacts/IEth2Dai.json",
"test/generated-artifacts/IKyberNetwork.json",
"test/generated-artifacts/ILiquidityProvider.json",
"test/generated-artifacts/IUniswapExchangeQuotes.json",
"test/generated-artifacts/TestERC20BridgeSampler.json"
],
Expand Down