feat: Add Kyber Elastic mixin to Ethereum/Polygon/Arbitrum/Avalanche Bridge Adapters [LIT-753] (#661)
* add kyber elastic to ethereum and polygon * add to arbitrum and avalanche * added kyber quoter/router dummy addresses * move kyberelastic test to a different file, fix some bugs with addresses, blocknumber * lint
This commit is contained in:
parent
9f30823d70
commit
dc8ff32d51
@ -1 +1 @@
|
||||
Subproject commit f8e700ed5d605f53f2194dc8df05a0bc3a7c1e43
|
||||
Subproject commit a2edd39db95df7e9dd3f9ef9edc8c55fefddb6df
|
@ -23,6 +23,7 @@ import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinDodoV2.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinKyberElastic.sol";
|
||||
import "./mixins/MixinGMX.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinUniswapV3.sol";
|
||||
@ -38,6 +39,7 @@ contract ArbitrumBridgeAdapter is
|
||||
MixinCurveV2,
|
||||
MixinDodoV2,
|
||||
MixinKyberDmm,
|
||||
MixinKyberElastic,
|
||||
MixinGMX,
|
||||
MixinNerve,
|
||||
MixinUniswapV3,
|
||||
@ -80,6 +82,11 @@ contract ArbitrumBridgeAdapter is
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeKyberDmm(buyToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.KYBERELASTIC) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeKyberElastic(sellToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
|
@ -22,6 +22,7 @@ import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinGMX.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinKyberElastic.sol";
|
||||
import "./mixins/MixinAaveV2.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinPlatypus.sol";
|
||||
@ -36,6 +37,7 @@ contract AvalancheBridgeAdapter is
|
||||
MixinCurveV2,
|
||||
MixinGMX,
|
||||
MixinKyberDmm,
|
||||
MixinKyberElastic,
|
||||
MixinAaveV2,
|
||||
MixinNerve,
|
||||
MixinPlatypus,
|
||||
@ -78,6 +80,11 @@ contract AvalancheBridgeAdapter is
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeKyberDmm(buyToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.KYBERELASTIC) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeKyberElastic(sellToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
|
@ -54,4 +54,5 @@ library BridgeProtocols {
|
||||
uint128 internal constant SYNTHETIX = 30;
|
||||
uint128 internal constant WOOFI = 31;
|
||||
uint128 internal constant AAVEV3 = 32;
|
||||
uint128 internal constant KYBERELASTIC = 33;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import "./mixins/MixinCryptoCom.sol";
|
||||
import "./mixins/MixinDodo.sol";
|
||||
import "./mixins/MixinDodoV2.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinKyberElastic.sol";
|
||||
import "./mixins/MixinLido.sol";
|
||||
import "./mixins/MixinMakerPSM.sol";
|
||||
import "./mixins/MixinMStable.sol";
|
||||
@ -54,6 +55,7 @@ contract EthereumBridgeAdapter is
|
||||
MixinDodo,
|
||||
MixinDodoV2,
|
||||
MixinKyberDmm,
|
||||
MixinKyberElastic,
|
||||
MixinLido,
|
||||
MixinMakerPSM,
|
||||
MixinMStable,
|
||||
@ -165,6 +167,11 @@ contract EthereumBridgeAdapter is
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeKyberDmm(buyToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.KYBERELASTIC) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeKyberElastic(sellToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.LIDO) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
|
@ -25,6 +25,7 @@ import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinDodo.sol";
|
||||
import "./mixins/MixinDodoV2.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinKyberElastic.sol";
|
||||
import "./mixins/MixinMStable.sol";
|
||||
import "./mixins/MixinNerve.sol";
|
||||
import "./mixins/MixinSolidly.sol";
|
||||
@ -43,6 +44,7 @@ contract PolygonBridgeAdapter is
|
||||
MixinDodo,
|
||||
MixinDodoV2,
|
||||
MixinKyberDmm,
|
||||
MixinKyberElastic,
|
||||
MixinMStable,
|
||||
MixinNerve,
|
||||
MixinUniswapV2,
|
||||
@ -111,6 +113,11 @@ contract PolygonBridgeAdapter is
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeKyberDmm(buyToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.KYBERELASTIC) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
}
|
||||
boughtAmount = _tradeKyberElastic(sellToken, sellAmount, order.bridgeData);
|
||||
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||
if (dryRun) {
|
||||
return (0, true);
|
||||
|
@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/src/IERC20Token.sol";
|
||||
|
||||
interface IKyberElasticRouter {
|
||||
struct ExactInputParams {
|
||||
bytes path;
|
||||
address recipient;
|
||||
uint256 deadline;
|
||||
uint256 amountIn;
|
||||
uint256 minAmountOut;
|
||||
}
|
||||
|
||||
function swapExactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut);
|
||||
}
|
||||
|
||||
contract MixinKyberElastic {
|
||||
using LibERC20TokenV06 for IERC20Token;
|
||||
|
||||
function _tradeKyberElastic(
|
||||
IERC20Token sellToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
) internal returns (uint256 boughtAmount) {
|
||||
(IKyberElasticRouter router, bytes memory path) = abi.decode(bridgeData, (IKyberElasticRouter, bytes));
|
||||
|
||||
// Grant the Kyber router an allowance to sell the sell token.
|
||||
sellToken.approveIfBelow(address(router), sellAmount);
|
||||
|
||||
boughtAmount = router.swapExactInput(
|
||||
IKyberElasticRouter.ExactInputParams({
|
||||
path: path,
|
||||
recipient: address(this),
|
||||
deadline: block.timestamp,
|
||||
amountIn: sellAmount,
|
||||
minAmountOut: 1
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -1,30 +1,51 @@
|
||||
{
|
||||
"1": {
|
||||
"UniswapV2Router": "0xf164fc0ec4e93095b804a4795bbe1e041497b92a",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000"
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x952ffc4c47d66b454a8181f5c68b6248e18b66ec"
|
||||
},
|
||||
"56": {
|
||||
"UniswapV2Router": "0x10ed43c718714eb63d5aa57b78b54704e256024e",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000"
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0xfbfab68ba077d099cd4b66fa76920572cc0b557c"
|
||||
},
|
||||
"137": {
|
||||
"UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000"
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0xf9cc934753a127100585812181ac04d07158a4c2"
|
||||
},
|
||||
"43114": {
|
||||
"UniswapV2Router": "0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000"
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x6038373de7f64da99b2a31951628b7d778b2c3cf"
|
||||
},
|
||||
"250": {
|
||||
"UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000"
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x8dcf5fed6ae6bf0befb5e4f0c9414c2cb9a4ed01"
|
||||
},
|
||||
"10": {
|
||||
"UniswapV2Router": "0x0000000000000000000000000000000000000000",
|
||||
"UniswapV3Router": "0x61ffe014ba17989e743c5f6cb21bf9697530b21e"
|
||||
"UniswapV3Router": "0x61ffe014ba17989e743c5f6cb21bf9697530b21e",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x7e29ccaa4bf2894aca02c77e6b99cafc1d24b2f5"
|
||||
},
|
||||
"42161": {
|
||||
"UniswapV2Router": "0x1b02da8cb0d097eb8d57a175b88c7d8b47997506",
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000"
|
||||
"UniswapV3Router": "0x0000000000000000000000000000000000000000",
|
||||
"KyberElasticQuoter": "0x0d125c15d54ca1f8a813c74a81aee34ebb508c1f",
|
||||
"KyberElasticRouter": "0xc1e7dfe73e1598e3910ef4c7845b68a9ab6f4c83",
|
||||
"KyberElasticPool": "0x087abaab9cd85025a8b3916948c69fe173c837ea"
|
||||
}
|
||||
}
|
||||
|
170
contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol
Normal file
170
contracts/zero-ex/tests/forked/SwapERC20ForERC20Test.t.sol
Normal file
@ -0,0 +1,170 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
Copyright 2023 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.6;
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../utils/ForkUtils.sol";
|
||||
import "../utils/TestUtils.sol";
|
||||
|
||||
contract SwapERC20ForERC20Test is Test, ForkUtils, TestUtils {
|
||||
function setUp() public {
|
||||
string memory root = vm.projectRoot();
|
||||
string memory path = string(abi.encodePacked(root, "/", "tests/addresses/ContractAddresses.json"));
|
||||
json = vm.readFile(path);
|
||||
|
||||
for (uint256 i = 0; i < chains.length; i++) {
|
||||
forkIds[chains[i]] = vm.createFork(vm.rpcUrl(chains[i]));
|
||||
}
|
||||
for (uint256 i = 0; i < chains.length; i++) {
|
||||
chainsByChainId[chains[i]] = chainIds[i];
|
||||
indexChainsByChain[chains[i]] = indexChainIds[i];
|
||||
bytes memory details = json.parseRaw(indexChainIds[i]);
|
||||
addresses = abi.decode(details, (ContractAddresses));
|
||||
}
|
||||
}
|
||||
|
||||
function test_swapERC20ForERC20OnKyberElastic() public {
|
||||
for (uint256 i = 0; i < chains.length; i++) {
|
||||
// kyberelastic mixin not deployed to these chains yet (bsc, fantom, optimism)
|
||||
if (i == 1 || i == 4 || i == 5) {
|
||||
continue;
|
||||
}
|
||||
vm.selectFork(forkIds[chains[i]]);
|
||||
labelAddresses(
|
||||
chains[i],
|
||||
indexChainsByChain[chains[i]],
|
||||
getTokens(i),
|
||||
getContractAddresses(i),
|
||||
getLiquiditySourceAddresses(i)
|
||||
);
|
||||
swapOnKyberElastic(getTokens(i), getContractAddresses(i), getLiquiditySourceAddresses(i));
|
||||
}
|
||||
}
|
||||
|
||||
function swapOnKyberElastic(
|
||||
TokenAddresses memory tokens,
|
||||
ContractAddresses memory addresses,
|
||||
LiquiditySources memory sources
|
||||
) public onlyForked {
|
||||
if (sources.KyberElasticQuoter == address(0)) {
|
||||
emit log_string("KyberElasticQuoter not available on this chain");
|
||||
return;
|
||||
}
|
||||
if (sources.KyberElasticRouter == address(0)) {
|
||||
emit log_string("KyberElasticRouter not available on this chain");
|
||||
return;
|
||||
}
|
||||
if (sources.KyberElasticPool == address(0)) {
|
||||
emit log_string("KyberElasticPool not available on this chain");
|
||||
return;
|
||||
}
|
||||
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](2);
|
||||
|
||||
transformations[0].deploymentNonce = _findTransformerNonce(
|
||||
address(addresses.transformers.wethTransformer),
|
||||
address(addresses.exchangeProxyTransformerDeployer)
|
||||
);
|
||||
emit log_named_uint("WethTransformer nonce", transformations[0].deploymentNonce);
|
||||
createNewFQT(tokens.WrappedNativeToken, addresses.exchangeProxy, addresses.exchangeProxyTransformerDeployer);
|
||||
transformations[0].data = abi.encode(LibERC20Transformer.ETH_TOKEN_ADDRESS, 1e18);
|
||||
transformations[1].deploymentNonce = _findTransformerNonce(
|
||||
address(fillQuoteTransformer),
|
||||
address(addresses.exchangeProxyTransformerDeployer)
|
||||
);
|
||||
emit log_named_uint("FillQuoteTransformer nonce", transformations[1].deploymentNonce);
|
||||
|
||||
FillQuoteTransformer.TransformData memory fqtData;
|
||||
fqtData.side = FillQuoteTransformer.Side.Sell;
|
||||
fqtData.sellToken = IERC20Token(address(tokens.USDC));
|
||||
fqtData.buyToken = IERC20Token(address(tokens.USDT));
|
||||
fqtData.fillSequence = new FillQuoteTransformer.OrderType[](1);
|
||||
fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Bridge;
|
||||
fqtData.fillAmount = 1e6;
|
||||
|
||||
(uint256 amountOut, bytes memory path) = sampleKyberElastic(
|
||||
fqtData.fillAmount,
|
||||
address(fqtData.sellToken),
|
||||
address(fqtData.buyToken),
|
||||
sources.KyberElasticQuoter,
|
||||
address(sources.KyberElasticPool)
|
||||
);
|
||||
|
||||
log_named_uint("amountOut", amountOut);
|
||||
|
||||
fqtData.bridgeOrders = new IBridgeAdapter.BridgeOrder[](1);
|
||||
IBridgeAdapter.BridgeOrder memory order;
|
||||
order.source = bytes32(uint256(BridgeProtocols.KYBERELASTIC) << 128);
|
||||
order.takerTokenAmount = 1e6;
|
||||
order.makerTokenAmount = amountOut;
|
||||
order.bridgeData = abi.encode(address(sources.KyberElasticRouter), path);
|
||||
fqtData.bridgeOrders[0] = order;
|
||||
transformations[1].data = abi.encode(fqtData);
|
||||
|
||||
vm.deal(address(this), 1e18);
|
||||
uint256 balanceETHBefore = address(this).balance;
|
||||
uint256 balanceERC20Before = IERC20Token(tokens.USDT).balanceOf(address(this));
|
||||
|
||||
writeTokenBalance(address(this), address(tokens.USDC), 1e16);
|
||||
uint256 balanceUSDCbefore = IERC20Token(tokens.USDC).balanceOf(address(this));
|
||||
|
||||
IERC20Token(address(tokens.USDC)).approve(addresses.exchangeProxy, 1e16);
|
||||
|
||||
IZeroEx(payable(addresses.exchangeProxy)).transformERC20{value: 1e18}(
|
||||
// input token
|
||||
IERC20Token(address(tokens.USDC)),
|
||||
// output token
|
||||
IERC20Token(address(tokens.USDT)),
|
||||
// input token amount
|
||||
1e6,
|
||||
// min output token amount
|
||||
order.makerTokenAmount,
|
||||
// list of transform
|
||||
transformations
|
||||
);
|
||||
|
||||
log_named_uint("NativeAsset balance before", balanceETHBefore);
|
||||
log_named_uint("ERC-20 balance before", balanceERC20Before);
|
||||
log_named_uint("NativeAsset balance after", balanceETHBefore - address(this).balance);
|
||||
log_named_uint("ERC-20 balance after", IERC20Token(tokens.USDT).balanceOf(address(this)) - balanceERC20Before);
|
||||
log_named_uint("USDC balance before", balanceUSDCbefore);
|
||||
log_named_uint("USDC balance after", IERC20Token(tokens.USDT).balanceOf(address(tokens.USDC)));
|
||||
assert(IERC20Token(tokens.USDT).balanceOf(address(this)) > 0);
|
||||
}
|
||||
|
||||
function sampleKyberElastic(
|
||||
uint256 amount,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
address quoter,
|
||||
address pool
|
||||
) public returns (uint256 makerTokenAmount, bytes memory path) {
|
||||
log_string(" Sampling KyberElastic for tokens");
|
||||
log_named_address(" ", takerToken);
|
||||
log_string(" -> ");
|
||||
log_named_address(" ", makerToken);
|
||||
log_named_address(" quoter", quoter);
|
||||
log_named_address("pool:", pool);
|
||||
address[] memory tokenPath = new address[](2);
|
||||
tokenPath[0] = address(takerToken);
|
||||
tokenPath[1] = address(makerToken);
|
||||
IKyberElasticQuoter kyberQuoter = IKyberElasticQuoter(quoter);
|
||||
address[] memory poolPath = new address[](1);
|
||||
poolPath[0] = address(pool);
|
||||
path = _toKyberElasticPath(tokenPath, poolPath);
|
||||
(uint256 amountOut, , , ) = kyberQuoter.quoteExactInput(path, amount);
|
||||
return (amountOut, path);
|
||||
}
|
||||
}
|
@ -89,6 +89,9 @@ struct TokenAddresses {
|
||||
}
|
||||
|
||||
struct LiquiditySources {
|
||||
address KyberElasticPool;
|
||||
address KyberElasticQuoter;
|
||||
address KyberElasticRouter;
|
||||
address UniswapV2Router;
|
||||
address UniswapV3Router;
|
||||
}
|
||||
@ -97,6 +100,30 @@ interface IFQT {
|
||||
function bridgeAdapter() external returns (address);
|
||||
}
|
||||
|
||||
interface IKyberElasticQuoter {
|
||||
function quoteExactInput(
|
||||
bytes memory path,
|
||||
uint256 amountIn
|
||||
)
|
||||
external
|
||||
returns (
|
||||
uint256 amountOut,
|
||||
uint160[] memory afterSqrtPList,
|
||||
uint32[] memory initializedTicksCrossedList,
|
||||
uint256 gasEstimate
|
||||
);
|
||||
}
|
||||
|
||||
interface IKyberElasticPool {
|
||||
function token0() external view returns (address);
|
||||
|
||||
function token1() external view returns (address);
|
||||
|
||||
/// @notice The fee to be charged for a swap in basis points
|
||||
/// @return The swap fee in basis points
|
||||
function swapFeeUnits() external view returns (uint24);
|
||||
}
|
||||
|
||||
interface IUniswapV2Router01 {
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts);
|
||||
|
||||
@ -715,6 +742,34 @@ contract ForkUtils is Test {
|
||||
}
|
||||
}
|
||||
|
||||
function _toKyberElasticPath(
|
||||
address[] memory tokenPath,
|
||||
address[] memory poolPath
|
||||
) internal returns (bytes memory path) {
|
||||
require(tokenPath.length >= 2 && tokenPath.length == poolPath.length + 1, "invalid path lengths");
|
||||
// paths are tightly packed as:
|
||||
// [token0, token0token1PairFee, token1, token1Token2PairFee, token2, ...]
|
||||
path = new bytes(tokenPath.length * 20 + poolPath.length * 3);
|
||||
uint256 o;
|
||||
assembly {
|
||||
o := add(path, 32)
|
||||
}
|
||||
for (uint256 i = 0; i < tokenPath.length; ++i) {
|
||||
if (i > 0) {
|
||||
uint24 poolFee = IKyberElasticPool(poolPath[i - 1]).swapFeeUnits();
|
||||
assembly {
|
||||
mstore(o, shl(232, poolFee))
|
||||
o := add(o, 3)
|
||||
}
|
||||
}
|
||||
address token = tokenPath[i];
|
||||
assembly {
|
||||
mstore(o, shl(96, token))
|
||||
o := add(o, 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modifier onlyForked() {
|
||||
if (block.number >= 15000000) {
|
||||
_;
|
||||
@ -722,4 +777,8 @@ contract ForkUtils is Test {
|
||||
revert("Requires fork mode");
|
||||
}
|
||||
}
|
||||
|
||||
function writeTokenBalance(address who, address token, uint256 amt) internal {
|
||||
stdstore.target(token).sig(IERC20Token(token).balanceOf.selector).with_key(who).checked_write(amt);
|
||||
}
|
||||
}
|
||||
|
@ -163,6 +163,7 @@ export enum BridgeProtocol {
|
||||
Synthetix,
|
||||
WOOFi,
|
||||
AaveV3,
|
||||
KyberElastic,
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user