chore: Remove old execution and encoding files (#221)
They are unused now. See https://github.com/propeller-heads/tycho-execution for the new workflow
This commit is contained in:
43
.github/actions/setup_env/action.yaml
vendored
43
.github/actions/setup_env/action.yaml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Setup/Cache Env
|
||||
description: 'Sets up and caches a python env. Will only install dependencies if no cache was hit.'
|
||||
|
||||
inputs:
|
||||
role-to-assume:
|
||||
required: true
|
||||
description: 'The role to assume'
|
||||
aws-region:
|
||||
required: true
|
||||
description: 'The aws region to use'
|
||||
domain-owner:
|
||||
required: true
|
||||
description: 'The domain owner'
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Cache Env
|
||||
uses: actions/cache@v3
|
||||
id: env-cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
key: ${{ env.pythonLocation }}-${{ hashFiles('./propeller-swap-encoders/requirements.txt') }}
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4.0.1
|
||||
with:
|
||||
role-to-assume: ${{ inputs.role-to-assume }}
|
||||
audience: sts.amazonaws.com
|
||||
aws-region: ${{ inputs.aws-region }}
|
||||
|
||||
- name: Install Dependencies
|
||||
if: ${{ steps.env-cache.outputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
aws codeartifact login --tool pip --domain propeller --domain-owner ${{inputs.domain-owner}} --repository protosim
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r propeller-swap-encoders/requirements.txt --no-cache
|
||||
shell: bash
|
||||
@@ -1,27 +0,0 @@
|
||||
name: Publish Propeller Swap Encoders Python Packages to AWS CodeArtifact
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- prereleased
|
||||
- released
|
||||
workflow_dispatch: { }
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
publish_propeller_swap_encoders:
|
||||
uses: propeller-heads/ci-cd-templates/.github/workflows/release-python-package.yaml@main
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
with:
|
||||
package_root: "propeller-swap-encoders"
|
||||
secrets:
|
||||
app_id: ${{ secrets.APP_ID }}
|
||||
app_private_key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||
role_to_assume: ${{ secrets.ROLE_TO_ASSUME }}
|
||||
aws_region: ${{ secrets.AWS_REGION }}
|
||||
domain_owner: ${{ secrets.DOMAIN_OWNER }}
|
||||
63
.github/workflows/python-tests.yaml
vendored
63
.github/workflows/python-tests.yaml
vendored
@@ -1,63 +0,0 @@
|
||||
name: Test code using pytest
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
runs_on:
|
||||
required: false
|
||||
type: string
|
||||
default: ubuntu-latest
|
||||
timeout_minutes:
|
||||
required: false
|
||||
type: number
|
||||
default: 15
|
||||
secrets:
|
||||
role-to-assume:
|
||||
required: true
|
||||
aws-region:
|
||||
required: true
|
||||
domain-owner:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
test-python:
|
||||
runs-on: "${{ inputs.runs_on }}"
|
||||
timeout-minutes: "${{ inputs.timeout_minutes }}"
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Cache Env
|
||||
uses: actions/cache@v3
|
||||
id: env-cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
key: ${{ env.pythonLocation }}-${{ hashFiles('./propeller-swap-encoders/requirements.txt') }}
|
||||
|
||||
- name: Configure AWS Credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4.0.1
|
||||
with:
|
||||
role-to-assume: ${{ secrets.role-to-assume }}
|
||||
audience: sts.amazonaws.com
|
||||
aws-region: ${{ secrets.aws-region }}
|
||||
|
||||
- name: Install Dependencies
|
||||
if: ${{ steps.env-cache.outputs.cache-hit != 'true' }}
|
||||
run: |
|
||||
aws codeartifact login --tool pip --domain propeller --domain-owner ${{secrets.domain-owner}} --repository protosim
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r propeller-swap-encoders/requirements.txt --no-cache
|
||||
shell: bash
|
||||
|
||||
- name: Test with pytest
|
||||
id: tests
|
||||
run: |
|
||||
export PYTHONPATH=$PYTHONPATH:$GITHUB_WORKSPACE/propeller-swap-encoders
|
||||
pytest --disable-warnings ./propeller-swap-encoders
|
||||
40
.github/workflows/swap-encoders.yaml
vendored
40
.github/workflows/swap-encoders.yaml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Swap encoders CI
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PYTEST_ADDOPTS: "--color=yes"
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
uses: propeller-heads/tycho-protocol-sdk/.github/workflows/python-tests.yaml@main
|
||||
secrets:
|
||||
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
domain-owner: ${{ secrets.DOMAIN_OWNER }}
|
||||
|
||||
formatting:
|
||||
name: Formatting
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Check out Repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Env
|
||||
uses: ./.github/actions/setup_env
|
||||
with:
|
||||
role-to-assume: ${{ secrets.ROLE_TO_ASSUME }}
|
||||
aws-region: ${{ secrets.AWS_REGION }}
|
||||
domain-owner: ${{ secrets.DOMAIN_OWNER }}
|
||||
|
||||
- name: Black Formatting
|
||||
run: |
|
||||
black ./propeller-swap-encoders --check --skip-magic-trailing-comma
|
||||
@@ -1,132 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENCED
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../interfaces/ISwapExecutor.sol";
|
||||
|
||||
contract BalancerSwapExecutor is ISwapExecutor {
|
||||
address private constant vaultAddress =
|
||||
0xBA12222222228d8Ba445958a75a0704d566BF2C8;
|
||||
bytes32 private constant swapSelector =
|
||||
0x52bbbe2900000000000000000000000000000000000000000000000000000000;
|
||||
bytes32 private constant maxUint256 =
|
||||
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
|
||||
|
||||
/**
|
||||
* @dev Executes a Balancer swap.
|
||||
* @param givenAmount how much of to swap, depending on exactOut either in-
|
||||
* or outAmount.
|
||||
* @param data the parameters of the swap. This data is roughly the packed
|
||||
* encoding of
|
||||
* the struct below:
|
||||
* ```
|
||||
* struct Params {
|
||||
* // the token that the caller is selling
|
||||
* IERC20 tokenIn;
|
||||
*
|
||||
* // the token that the caller is receiving in exchange
|
||||
* IERC20 tokenOut;
|
||||
*
|
||||
* // the target pool id
|
||||
* bytes32 poolId;
|
||||
*
|
||||
* // the receiver of `tokenOut`
|
||||
* address receiver;
|
||||
*
|
||||
* // whether we want exactOut semantics
|
||||
* bool exactOut;
|
||||
*
|
||||
* // whether we need to approve the pool to spend `tokenIn`
|
||||
* bool tokenApprovalNeeded;
|
||||
*
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
function swap(uint256 givenAmount, bytes calldata data)
|
||||
external
|
||||
payable
|
||||
returns (uint256 calculatedAmount)
|
||||
{
|
||||
IERC20 tokenIn;
|
||||
IERC20 tokenOut;
|
||||
bytes32 poolId;
|
||||
address receiver;
|
||||
bool exactOut;
|
||||
bool tokenApprovalNeeded;
|
||||
assembly {
|
||||
tokenIn := shr(96, calldataload(data.offset))
|
||||
tokenOut := shr(96, calldataload(add(data.offset, 20)))
|
||||
poolId := calldataload(add(data.offset, 40))
|
||||
let dataLoad := calldataload(add(data.offset, 72))
|
||||
receiver := shr(96, dataLoad)
|
||||
exactOut := and(shr(88, dataLoad), 0xff)
|
||||
tokenApprovalNeeded := and(shr(80, dataLoad), 0xff)
|
||||
|
||||
// Check if token approval is needed and perform the approval
|
||||
if tokenApprovalNeeded {
|
||||
// Prepare approve call
|
||||
let approveCalldata := mload(0x40)
|
||||
mstore(
|
||||
approveCalldata,
|
||||
0x095ea7b300000000000000000000000000000000000000000000000000000000
|
||||
) // approve selector
|
||||
mstore(add(approveCalldata, 4), vaultAddress) // spender
|
||||
mstore(add(approveCalldata, 36), maxUint256) // value
|
||||
// (maxUint256)
|
||||
|
||||
let success :=
|
||||
call(gas(), tokenIn, 0, approveCalldata, 68, 0, 0)
|
||||
if iszero(success) {
|
||||
returndatacopy(0, 0, returndatasize())
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
}
|
||||
|
||||
let ptr := mload(0x40)
|
||||
mstore(ptr, swapSelector)
|
||||
//limit: as it is always recalculated during the swap, we use the
|
||||
// extremums 0 tokenOut or max(uint256) tokenIn.
|
||||
let limit := 0
|
||||
if exactOut { limit := maxUint256 }
|
||||
// as the singleSwap struct contains a bytes, it's considered as
|
||||
// dynamic.
|
||||
// dynamic values are encoded at the end of the calldata and have a
|
||||
// corresponding offset at the beginning
|
||||
// we first need to encode the offset of the singleSwap struct
|
||||
mstore(add(ptr, 4), 0xe0)
|
||||
// fundManagement.sender: is always address(this)
|
||||
mstore(add(ptr, 36), address())
|
||||
// fundManagement.fromInternalBalance
|
||||
mstore(add(ptr, 68), 0)
|
||||
// fundManagement.receiver
|
||||
mstore(add(ptr, 100), receiver)
|
||||
// fundManagement.toInternalBalance
|
||||
mstore(add(ptr, 132), 0)
|
||||
// limit
|
||||
mstore(add(ptr, 164), limit)
|
||||
// deadline
|
||||
mstore(add(ptr, 196), timestamp())
|
||||
// singleSwap.poolId
|
||||
mstore(add(ptr, 228), poolId)
|
||||
// singleSwap.exactOut
|
||||
mstore(add(ptr, 260), exactOut)
|
||||
// singleSwap.assetIn
|
||||
mstore(add(ptr, 292), tokenIn)
|
||||
// singleSwap.assetOut
|
||||
mstore(add(ptr, 324), tokenOut)
|
||||
// singleSwap.amount
|
||||
mstore(add(ptr, 356), givenAmount)
|
||||
// singleSwap.userData offset
|
||||
mstore(add(ptr, 388), 0xc0)
|
||||
// singleSwap.userData lenght
|
||||
mstore(add(ptr, 420), 0)
|
||||
|
||||
let success := call(gas(), vaultAddress, 0, ptr, 452, ptr, 32)
|
||||
switch success
|
||||
case 0 {
|
||||
returndatacopy(0, 0, returndatasize())
|
||||
revert(0, returndatasize())
|
||||
}
|
||||
default { calculatedAmount := mload(ptr) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENCED
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
import "../interfaces/ISwapExecutor.sol";
|
||||
import "./interfaces/ICurvePool.sol";
|
||||
import "./interfaces/ICurvePoolNoReturn.sol";
|
||||
import "./interfaces/ICurveCryptoPool.sol";
|
||||
import "./interfaces/ICurvePoolNoReturn.sol";
|
||||
import "./interfaces/ICurvePoolWithReturn.sol";
|
||||
import {
|
||||
IERC20,
|
||||
SafeERC20
|
||||
} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import "src/libraries/EfficientERC20.sol";
|
||||
|
||||
interface IWETH is IERC20 {
|
||||
function deposit() external payable;
|
||||
|
||||
function withdraw(uint256) external;
|
||||
}
|
||||
|
||||
contract CurveSwapExecutor is ISwapExecutor, ISwapExecutorErrors {
|
||||
using EfficientERC20 for IERC20;
|
||||
|
||||
IWETH private constant weth =
|
||||
IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
|
||||
address private constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
|
||||
function _decodeParams(bytes calldata data)
|
||||
internal
|
||||
pure
|
||||
returns (
|
||||
IERC20 tokenOut,
|
||||
address target,
|
||||
address receiver,
|
||||
uint8 poolType,
|
||||
int128 i,
|
||||
int128 j,
|
||||
bool tokenApprovalNeeded
|
||||
)
|
||||
{
|
||||
tokenOut = IERC20(address(bytes20(data[0:20])));
|
||||
target = address(bytes20(data[20:40]));
|
||||
receiver = address(bytes20(data[40:60]));
|
||||
poolType = uint8(data[60]);
|
||||
i = int128(uint128(uint8(data[61])));
|
||||
j = int128(uint128(uint8(data[62])));
|
||||
tokenApprovalNeeded = data[63] != 0;
|
||||
}
|
||||
|
||||
function swap(uint256 amountIn, bytes calldata data)
|
||||
external
|
||||
payable
|
||||
returns (uint256 res)
|
||||
{
|
||||
(
|
||||
IERC20 tokenOut,
|
||||
address target,
|
||||
address receiver,
|
||||
uint8 poolType,
|
||||
int128 i,
|
||||
int128 j,
|
||||
bool tokenApprovalNeeded
|
||||
) = _decodeParams(data);
|
||||
|
||||
// Approve the token for the pool's address if `tokenApprovalNeeded` is
|
||||
// true
|
||||
if (tokenApprovalNeeded) {
|
||||
address tokenIn;
|
||||
// pool type 6 has a different function signature to get the coins
|
||||
if (poolType == 6) {
|
||||
tokenIn = ICurvePoolNoReturn(target).underlying_coins(int128(i));
|
||||
} else {
|
||||
tokenIn = ICurvePool(target).coins(uint256(uint128(i)));
|
||||
}
|
||||
IERC20(tokenIn).forceApprove(target, type(uint256).max);
|
||||
}
|
||||
if (poolType == 0) {
|
||||
// simple exchange with int128
|
||||
// e.g. AAVE, EURS
|
||||
res = ICurvePoolWithReturn(target).exchange(i, j, amountIn, 0);
|
||||
if (receiver != address(this)) {
|
||||
tokenOut.safeTransfer(receiver, res);
|
||||
}
|
||||
} else if (poolType == 1) {
|
||||
// simple exchange with int128 but no amountOut,
|
||||
// e.g. BUSD, HBTC, PAX, renBTC, sBTC, SUSD, USDT, Y, 3pool
|
||||
uint256 tokenOutBalanceBeforeSwap =
|
||||
tokenOut.balanceOf(address(this));
|
||||
ICurvePoolNoReturn(target).exchange(i, j, amountIn, 0);
|
||||
uint256 tokenOutBalanceAfterSwap = tokenOut.balanceOf(address(this));
|
||||
res = tokenOutBalanceAfterSwap - tokenOutBalanceBeforeSwap;
|
||||
if (receiver != address(this)) {
|
||||
tokenOut.safeTransfer(receiver, res);
|
||||
}
|
||||
} else if (poolType == 3) {
|
||||
// tricrypto case
|
||||
uint256 tokenOutBalanceBeforeSwap =
|
||||
tokenOut.balanceOf(address(this));
|
||||
ICurveCryptoPool(target).exchange(
|
||||
uint256(uint128(i)),
|
||||
uint256(uint128(j)),
|
||||
amountIn,
|
||||
0,
|
||||
false //TODO: Check if we can call the entrypoint without
|
||||
// 'use_eth' as it's false by default.
|
||||
);
|
||||
uint256 tokenOutBalanceAfterSwap = tokenOut.balanceOf(address(this));
|
||||
res = tokenOutBalanceAfterSwap - tokenOutBalanceBeforeSwap;
|
||||
if (receiver != address(this)) {
|
||||
tokenOut.safeTransfer(receiver, res);
|
||||
}
|
||||
} else if (poolType == 4) {
|
||||
// (payable) ether based stableswaps - so far no liquidity
|
||||
// e.g. sETH, stETH, rETH, etc
|
||||
ICurveCryptoPool pool = ICurveCryptoPool(target);
|
||||
if (pool.coins(uint256(uint128(i))) == ETH) {
|
||||
weth.withdraw(amountIn);
|
||||
res = pool.exchange{value: amountIn}(i, j, amountIn, 0);
|
||||
} else {
|
||||
res = pool.exchange(i, j, amountIn, 0);
|
||||
}
|
||||
|
||||
if (pool.coins(uint256(uint128(j))) == ETH) {
|
||||
weth.deposit{value: res}();
|
||||
}
|
||||
if (receiver != address(this)) {
|
||||
tokenOut.safeTransfer(receiver, res);
|
||||
}
|
||||
} else if (poolType == 5) {
|
||||
// metapool or lending pool interface using int128
|
||||
// e.g. AAVE
|
||||
res = ICurvePoolWithReturn(target).exchange_underlying(
|
||||
i, j, amountIn, 0
|
||||
);
|
||||
if (receiver != address(this)) {
|
||||
tokenOut.safeTransfer(receiver, res);
|
||||
}
|
||||
} else if (poolType == 6) {
|
||||
// metapool or lending pool interface using int128 no amountOut
|
||||
// returned
|
||||
// e.g. Y, Compound
|
||||
uint256 tokenOutBalanceBeforeSwap =
|
||||
tokenOut.balanceOf(address(this));
|
||||
ICurvePoolNoReturn(target).exchange_underlying(i, j, amountIn, 0);
|
||||
uint256 tokenOutBalanceAfterSwap = tokenOut.balanceOf(address(this));
|
||||
res = tokenOutBalanceAfterSwap - tokenOutBalanceBeforeSwap;
|
||||
if (receiver != address(this)) {
|
||||
tokenOut.safeTransfer(receiver, res);
|
||||
}
|
||||
} else if (poolType == 7) {
|
||||
// cryptov2 pool with two tokens
|
||||
// e.g. LDO/ETH
|
||||
res = ICurvePoolWithReturn(target).exchange(
|
||||
uint256(uint128(i)),
|
||||
uint256(uint128(j)),
|
||||
amountIn,
|
||||
0,
|
||||
false,
|
||||
receiver
|
||||
);
|
||||
} else if (poolType == 8) {
|
||||
// cryptov2 two tokens not factory pools ETH/CRV and ETH/CVX
|
||||
res = ICurvePoolWithReturn(target).exchange(
|
||||
uint256(uint128(i)), uint256(uint128(j)), amountIn, 0, false
|
||||
);
|
||||
if (receiver != address(this)) {
|
||||
tokenOut.safeTransfer(receiver, res);
|
||||
}
|
||||
} else {
|
||||
revert UnknownPoolType(poolType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.4.0;
|
||||
|
||||
interface ICurveCryptoPool {
|
||||
function get_dy(uint256 i, uint256 j, uint256 dx)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
// tricrypto
|
||||
function exchange(
|
||||
uint256 i,
|
||||
uint256 j,
|
||||
uint256 dx,
|
||||
uint256 min_dy,
|
||||
bool use_eth
|
||||
) external payable;
|
||||
|
||||
// eth accepting pools
|
||||
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy)
|
||||
external
|
||||
payable
|
||||
returns (uint256);
|
||||
|
||||
function coins(uint256 i) external view returns (address);
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
pragma solidity >=0.4.0;
|
||||
|
||||
interface ICurvePool {
|
||||
function initialize(
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
address _coin,
|
||||
uint256 _decimals,
|
||||
uint256 _A,
|
||||
uint256 _fee,
|
||||
address _admin
|
||||
) external;
|
||||
function decimals() external view returns (uint256);
|
||||
function transfer(address _to, uint256 _value) external returns (bool);
|
||||
function transferFrom(address _from, address _to, uint256 _value)
|
||||
external
|
||||
returns (bool);
|
||||
function approve(address _spender, uint256 _value)
|
||||
external
|
||||
returns (bool);
|
||||
function get_previous_balances()
|
||||
external
|
||||
view
|
||||
returns (uint256[2] memory);
|
||||
function get_balances() external view returns (uint256[2] memory);
|
||||
function get_twap_balances(
|
||||
uint256[2] memory _first_balances,
|
||||
uint256[2] memory _last_balances,
|
||||
uint256 _time_elapsed
|
||||
) external view returns (uint256[2] memory);
|
||||
function get_price_cumulative_last()
|
||||
external
|
||||
view
|
||||
returns (uint256[2] memory);
|
||||
function admin_fee() external view returns (uint256);
|
||||
function A() external view returns (uint256);
|
||||
function A_precise() external view returns (uint256);
|
||||
function get_virtual_price() external view returns (uint256);
|
||||
function calc_token_amount(uint256[2] memory _amounts, bool _is_deposit)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
function calc_token_amount(
|
||||
uint256[2] memory _amounts,
|
||||
bool _is_deposit,
|
||||
bool _previous
|
||||
) external view returns (uint256);
|
||||
function add_liquidity(uint256[2] memory _amounts, uint256 _min_mint_amount)
|
||||
external
|
||||
returns (uint256);
|
||||
function add_liquidity(
|
||||
uint256[2] memory _amounts,
|
||||
uint256 _min_mint_amount,
|
||||
address _receiver
|
||||
) external returns (uint256);
|
||||
function get_dy(int128 i, int128 j, uint256 dx)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
function get_dy(int128 i, int128 j, uint256 dx, uint256[2] memory _balances)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
function get_dy_underlying(int128 i, int128 j, uint256 dx)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
function get_dy_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 dx,
|
||||
uint256[2] memory _balances
|
||||
) external view returns (uint256);
|
||||
function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy)
|
||||
external;
|
||||
function exchange(
|
||||
uint256 i,
|
||||
uint256 j,
|
||||
uint256 dx,
|
||||
uint256 min_dy,
|
||||
address _receiver
|
||||
) external;
|
||||
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy)
|
||||
external;
|
||||
function exchange(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 dx,
|
||||
uint256 min_dy,
|
||||
address _receiver
|
||||
) external;
|
||||
function exchange_underlying(
|
||||
uint256 i,
|
||||
uint256 j,
|
||||
uint256 dx,
|
||||
uint256 min_dy
|
||||
) external;
|
||||
function exchange_underlying(
|
||||
uint256 i,
|
||||
uint256 j,
|
||||
uint256 dx,
|
||||
uint256 min_dy,
|
||||
address _receiver
|
||||
) external;
|
||||
function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy)
|
||||
external;
|
||||
function exchange_underlying(
|
||||
int128 i,
|
||||
int128 j,
|
||||
uint256 dx,
|
||||
uint256 min_dy,
|
||||
address _receiver
|
||||
) external;
|
||||
function remove_liquidity(
|
||||
uint256 _burn_amount,
|
||||
uint256[2] memory _min_amounts
|
||||
) external returns (uint256[2] memory);
|
||||
function remove_liquidity(
|
||||
uint256 _burn_amount,
|
||||
uint256[2] memory _min_amounts,
|
||||
address _receiver
|
||||
) external returns (uint256[2] memory);
|
||||
function remove_liquidity_imbalance(
|
||||
uint256[2] memory _amounts,
|
||||
uint256 _max_burn_amount
|
||||
) external returns (uint256);
|
||||
function remove_liquidity_imbalance(
|
||||
uint256[2] memory _amounts,
|
||||
uint256 _max_burn_amount,
|
||||
address _receiver
|
||||
) external returns (uint256);
|
||||
function calc_withdraw_one_coin(uint256 _burn_amount, int128 i)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
function calc_withdraw_one_coin(
|
||||
uint256 _burn_amount,
|
||||
int128 i,
|
||||
bool _previous
|
||||
) external view returns (uint256);
|
||||
function remove_liquidity_one_coin(
|
||||
uint256 _burn_amount,
|
||||
int128 i,
|
||||
uint256 _min_received
|
||||
) external returns (uint256);
|
||||
function remove_liquidity_one_coin(
|
||||
uint256 _burn_amount,
|
||||
int128 i,
|
||||
uint256 _min_received,
|
||||
address _receiver
|
||||
) external returns (uint256);
|
||||
function ramp_A(uint256 _future_A, uint256 _future_time) external;
|
||||
function stop_ramp_A() external;
|
||||
function admin_balances(uint256 i) external view returns (uint256);
|
||||
function withdraw_admin_fees() external;
|
||||
function admin() external view returns (address);
|
||||
function coins(uint256 arg0) external view returns (address);
|
||||
function balances(uint256 arg0) external view returns (uint256);
|
||||
function fee() external view returns (uint256);
|
||||
function block_timestamp_last() external view returns (uint256);
|
||||
function initial_A() external view returns (uint256);
|
||||
function future_A() external view returns (uint256);
|
||||
function initial_A_time() external view returns (uint256);
|
||||
function future_A_time() external view returns (uint256);
|
||||
function name() external view returns (string memory);
|
||||
function symbol() external view returns (string memory);
|
||||
function balanceOf(address arg0) external view returns (uint256);
|
||||
function allowance(address arg0, address arg1)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
function totalSupply() external view returns (uint256);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.4.0;
|
||||
|
||||
interface ICurvePoolNoReturn {
|
||||
function get_dy(int128 i, int128 j, uint256 dx)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
function get_dy_underlying(int128 i, int128 j, uint256 dx)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy)
|
||||
external;
|
||||
|
||||
function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy)
|
||||
external;
|
||||
function coins(int128 arg0) external view returns (address);
|
||||
function underlying_coins(int128 arg0) external view returns (address);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity >=0.4.0;
|
||||
|
||||
interface ICurvePoolWithReturn {
|
||||
function get_dy(int128 i, int128 j, uint256 dx)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
function get_dy_underlying(int128 i, int128 j, uint256 dx)
|
||||
external
|
||||
view
|
||||
returns (uint256);
|
||||
|
||||
function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy)
|
||||
external
|
||||
returns (uint256);
|
||||
|
||||
function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy)
|
||||
external
|
||||
returns (uint256);
|
||||
|
||||
function exchange(
|
||||
uint256 i,
|
||||
uint256 j,
|
||||
uint256 dx,
|
||||
uint256 min_dy,
|
||||
bool use_eth,
|
||||
address receiver
|
||||
) external returns (uint256);
|
||||
|
||||
function exchange(
|
||||
uint256 i,
|
||||
uint256 j,
|
||||
uint256 dx,
|
||||
uint256 min_dy,
|
||||
bool use_eth
|
||||
) external returns (uint256);
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.13;
|
||||
|
||||
import "./SwapExecutor.t.sol";
|
||||
import "../src/balancer-v2/BalancerSwapExecutor.sol";
|
||||
|
||||
contract TestBalancerSwapExecutor is SwapExecutorTest {
|
||||
BalancerSwapExecutor balancer;
|
||||
IERC20 USDC = IERC20(USDC_ADDR);
|
||||
IERC20 USDT = IERC20(USDT_ADDR);
|
||||
|
||||
constructor() {}
|
||||
|
||||
function setUp() public {
|
||||
//Fork
|
||||
uint256 forkBlock = 16000000;
|
||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||
|
||||
//Setup
|
||||
balancer = new BalancerSwapExecutor();
|
||||
}
|
||||
|
||||
function testBalancerSwap() public {
|
||||
//Set up
|
||||
uint256 sellAmount = 1000_000000;
|
||||
uint256 expectedAmount = 998_919380; //Swap 1k USDT for 998 USDC
|
||||
bool exactOut = false;
|
||||
// This is required because balancer does a transferFrom sender.
|
||||
// That also means we need to do this approval with our swapRouter.
|
||||
bool tokenApprovalNeeded = true;
|
||||
bytes memory protocolData = abi.encodePacked(
|
||||
USDT_ADDR,
|
||||
USDC_ADDR,
|
||||
DAI_USDC_USDT_balancer,
|
||||
bob,
|
||||
exactOut,
|
||||
tokenApprovalNeeded
|
||||
);
|
||||
|
||||
// Logic
|
||||
vm.prank(address(balancer));
|
||||
deal(USDT_ADDR, address(balancer), sellAmount);
|
||||
vm.prank(executor);
|
||||
uint256 responseAmount = balancer.swap(sellAmount, protocolData);
|
||||
|
||||
//Assertions
|
||||
assertEq(responseAmount, expectedAmount);
|
||||
assertEq(USDC.balanceOf(bob), expectedAmount);
|
||||
assertEq(USDT.balanceOf(address(balancer)), 0);
|
||||
}
|
||||
|
||||
function testBalancerExactOutSwap() public {
|
||||
//Set up
|
||||
uint256 buyAmount = 1000_979168;
|
||||
uint256 expectedSellAmount = 1000 * 10 ** 6;
|
||||
bool exactOut = true;
|
||||
bool tokenApprovalNeeded = true;
|
||||
bytes memory protocolData = abi.encodePacked(
|
||||
USDC_ADDR,
|
||||
USDT_ADDR,
|
||||
DAI_USDC_USDT_balancer,
|
||||
bob,
|
||||
exactOut,
|
||||
tokenApprovalNeeded
|
||||
);
|
||||
|
||||
//Logic
|
||||
// This is required because balancer does a transferFrom sender.
|
||||
// That also means we need to do this approval with our swapRouter.
|
||||
vm.prank(address(balancer));
|
||||
|
||||
deal(USDC_ADDR, address(balancer), expectedSellAmount);
|
||||
vm.prank(executor);
|
||||
uint256 responseAmount = balancer.swap(buyAmount, protocolData);
|
||||
|
||||
// //Assertions
|
||||
assertEq(responseAmount, expectedSellAmount);
|
||||
assertEq(USDT.balanceOf(bob), buyAmount);
|
||||
assertEq(USDC.balanceOf(address(balancer)), 0);
|
||||
}
|
||||
}
|
||||
@@ -1,303 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.13;
|
||||
|
||||
import "./SwapExecutor.t.sol";
|
||||
import "../src/curve/CurveSwapExecutor.sol";
|
||||
|
||||
contract CurveSwapExecutorExposed is CurveSwapExecutor {
|
||||
function decodeParams(bytes calldata data)
|
||||
external
|
||||
pure
|
||||
returns (
|
||||
IERC20 tokenOut,
|
||||
address target,
|
||||
address receiver,
|
||||
uint8 poolType,
|
||||
int128 i,
|
||||
int128 j,
|
||||
bool tokenApprovalNeeded
|
||||
)
|
||||
{
|
||||
return _decodeParams(data);
|
||||
}
|
||||
}
|
||||
|
||||
contract CurveSwapExecutorPayable is CurveSwapExecutor {
|
||||
receive() external payable {}
|
||||
}
|
||||
|
||||
interface ILendingPool {
|
||||
function deposit(
|
||||
address asset,
|
||||
uint256 amount,
|
||||
address onBehalfOf,
|
||||
uint16 referralCode
|
||||
) external;
|
||||
|
||||
function withdraw(address asset, uint256 amount, address to)
|
||||
external
|
||||
returns (uint256);
|
||||
}
|
||||
|
||||
contract TestCurveSwapExecutor is SwapExecutorTest {
|
||||
CurveSwapExecutor swapMethod;
|
||||
address swapMethodAddress;
|
||||
// type 0 pool
|
||||
address aDAI_ADDR = 0x028171bCA77440897B824Ca71D1c56caC55b68A3;
|
||||
address aUSDC_ADDR = 0xBcca60bB61934080951369a648Fb03DF4F96263C;
|
||||
IERC20 aDAI = IERC20(aDAI_ADDR);
|
||||
IERC20 aUSDC = IERC20(aUSDC_ADDR);
|
||||
address AAVE_POOL = 0xDeBF20617708857ebe4F679508E7b7863a8A8EeE;
|
||||
|
||||
// type 1 - 3pool
|
||||
IERC20 DAI = IERC20(DAI_ADDR);
|
||||
IERC20 USDC = IERC20(USDC_ADDR);
|
||||
address THREE_POOL = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7;
|
||||
|
||||
// type 3 - tricrypto case
|
||||
IERC20 WETH = IERC20(WETH_ADDR);
|
||||
IERC20 WBTC = IERC20(WBTC_ADDR);
|
||||
address TRICRYPTO_POOL = 0xD51a44d3FaE010294C616388b506AcdA1bfAAE46;
|
||||
|
||||
// type 4 - stETH
|
||||
address stETH_ADDR = 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84;
|
||||
IERC20 stETH = IERC20(stETH_ADDR);
|
||||
address stETH_POOL = 0xDC24316b9AE028F1497c275EB9192a3Ea0f67022;
|
||||
|
||||
// type 5 - LUSD
|
||||
address LUSD_ADDR = 0x5f98805A4E8be255a32880FDeC7F6728C6568bA0;
|
||||
IERC20 LUSD = IERC20(LUSD_ADDR);
|
||||
IERC20 USDT = IERC20(USDT_ADDR);
|
||||
address LUSD_POOL = 0xEd279fDD11cA84bEef15AF5D39BB4d4bEE23F0cA;
|
||||
|
||||
// type 6 - compound
|
||||
address CPOOL = 0xA2B47E3D5c44877cca798226B7B8118F9BFb7A56;
|
||||
|
||||
// type 7
|
||||
address LDO_POOL = 0x9409280DC1e6D33AB7A8C6EC03e5763FB61772B5;
|
||||
IERC20 LDO = IERC20(LDO_ADDR);
|
||||
|
||||
// type 8
|
||||
address CRV_POOL = 0x8301AE4fc9c624d1D396cbDAa1ed877821D7C511;
|
||||
IERC20 CRV = IERC20(CRV_ADDR);
|
||||
|
||||
function setUp() public {
|
||||
//Fork
|
||||
uint256 forkBlock = 16000000;
|
||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||
|
||||
//Setup
|
||||
swapMethod = new CurveSwapExecutor();
|
||||
swapMethodAddress = address(swapMethod);
|
||||
vm.makePersistent(swapMethodAddress);
|
||||
}
|
||||
|
||||
// foundry deal doesn't work with the atokens:
|
||||
// https://github.com/foundry-rs/forge-std/issues/140
|
||||
function dealAaveDai() internal {
|
||||
deal(DAI_ADDR, swapMethodAddress, 100_000 * 10 ** 18);
|
||||
ILendingPool aave =
|
||||
ILendingPool(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9);
|
||||
|
||||
vm.startPrank(swapMethodAddress);
|
||||
DAI.approve(address(aave), type(uint256).max);
|
||||
aave.deposit(DAI_ADDR, 100_000 * 10 ** 18, swapMethodAddress, 0);
|
||||
vm.stopPrank();
|
||||
}
|
||||
|
||||
function testSwapType0() public {
|
||||
dealAaveDai();
|
||||
IERC20[] memory tokens = twoTokens(aDAI_ADDR, aUSDC_ADDR);
|
||||
uint256 expAmountOut = 999647;
|
||||
address receiver = bob;
|
||||
bytes memory data =
|
||||
getDataCurve(tokens[1], AAVE_POOL, receiver, 1, 0, 1, true);
|
||||
uint256 amountOut = swapMethod.swap(10 ** 18, data);
|
||||
|
||||
uint256 finalBalance = aUSDC.balanceOf(receiver);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(amountOut, expAmountOut);
|
||||
}
|
||||
|
||||
// 3pool
|
||||
function testSwapType1() public {
|
||||
deal(DAI_ADDR, swapMethodAddress, 10_000 * 10 ** 18);
|
||||
IERC20[] memory tokens = twoTokens(DAI_ADDR, USDC_ADDR);
|
||||
uint256 expAmountOut = 999963;
|
||||
address receiver = bob;
|
||||
|
||||
bytes memory data =
|
||||
getDataCurve(tokens[1], THREE_POOL, receiver, 1, 0, 1, true);
|
||||
|
||||
uint256 amountOut = swapMethod.swap(10 ** 18, data);
|
||||
|
||||
uint256 finalBalance = USDC.balanceOf(receiver);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(amountOut, expAmountOut);
|
||||
}
|
||||
|
||||
// tricrypto
|
||||
function testSwapType3() public {
|
||||
deal(USDT_ADDR, swapMethodAddress, 10_000 * 10 ** 6);
|
||||
IERC20[] memory tokens = twoTokens(USDT_ADDR, WBTC_ADDR);
|
||||
uint256 expAmountOut = 60232482;
|
||||
address receiver = bob;
|
||||
|
||||
bytes memory data =
|
||||
getDataCurve(tokens[1], TRICRYPTO_POOL, receiver, 3, 0, 1, true);
|
||||
|
||||
uint256 amountOut = swapMethod.swap(10_000 * 10 ** 6, data);
|
||||
|
||||
uint256 finalBalance = WBTC.balanceOf(receiver);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(amountOut, expAmountOut);
|
||||
}
|
||||
|
||||
// stETH/ETH pool
|
||||
function testSwapType4() public {
|
||||
CurveSwapExecutorPayable swapMethodPayable =
|
||||
new CurveSwapExecutorPayable();
|
||||
address swapMethodPayableAddress = address(swapMethodPayable);
|
||||
deal(WETH_ADDR, swapMethodPayableAddress, 100 * 10 ** 18);
|
||||
IERC20[] memory tokens = twoTokens(WETH_ADDR, stETH_ADDR);
|
||||
uint256 expAmountOut = 1011264689661846353;
|
||||
bytes memory data = getDataCurve(
|
||||
tokens[1], stETH_POOL, swapMethodPayableAddress, 4, 0, 1, false
|
||||
);
|
||||
|
||||
vm.prank(swapMethodPayableAddress);
|
||||
uint256 amountOut = swapMethodPayable.swap(10 ** 18, data);
|
||||
|
||||
uint256 finalBalance = stETH.balanceOf(swapMethodPayableAddress);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
// There is something weird with
|
||||
// stETH that it gives me 1 Wei more here sometimes
|
||||
assertGe(amountOut, expAmountOut);
|
||||
|
||||
// part 2 swap back stETH
|
||||
tokens = twoTokens(stETH_ADDR, WETH_ADDR);
|
||||
expAmountOut = 988069860569702379;
|
||||
address receiver = bob;
|
||||
|
||||
data = getDataCurve(tokens[1], stETH_POOL, receiver, 4, 1, 0, true);
|
||||
uint256 initialBalance = WETH.balanceOf(receiver);
|
||||
|
||||
amountOut = swapMethodPayable.swap(10 ** 18, data);
|
||||
|
||||
finalBalance = WETH.balanceOf(receiver) - initialBalance;
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(amountOut, expAmountOut);
|
||||
}
|
||||
|
||||
// // metapool - LUSD
|
||||
function testSwapType5() public {
|
||||
deal(LUSD_ADDR, swapMethodAddress, 10_000 * 10 ** 18);
|
||||
IERC20[] memory tokens = twoTokens(LUSD_ADDR, USDT_ADDR);
|
||||
uint256 expAmountOut = 1035119;
|
||||
address receiver = bob;
|
||||
|
||||
bytes memory data =
|
||||
getDataCurve(tokens[1], LUSD_POOL, receiver, 5, 0, 3, true);
|
||||
|
||||
uint256 amountOut = swapMethod.swap(10 ** 18, data);
|
||||
|
||||
uint256 finalBalance = USDT.balanceOf(receiver);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(amountOut, expAmountOut);
|
||||
}
|
||||
|
||||
// Compound
|
||||
function testSwapType6() public {
|
||||
deal(DAI_ADDR, swapMethodAddress, 10_000 * 10 ** 18);
|
||||
IERC20[] memory tokens = twoTokens(DAI_ADDR, USDC_ADDR);
|
||||
uint256 expAmountOut = 999430;
|
||||
address receiver = bob;
|
||||
|
||||
bytes memory data =
|
||||
getDataCurve(tokens[1], CPOOL, receiver, 6, 0, 1, true);
|
||||
|
||||
uint256 amountOut = swapMethod.swap(10 ** 18, data);
|
||||
|
||||
uint256 finalBalance = USDC.balanceOf(receiver);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(amountOut, expAmountOut);
|
||||
}
|
||||
|
||||
// Curve v2
|
||||
function testSwapType7() public {
|
||||
vm.rollFork(17_000_000); //change block because this pool wasn't
|
||||
// deployed at block 16M
|
||||
uint256 amountIn = 10 ** 18;
|
||||
uint256 expAmountOut = 743676671921315909289;
|
||||
address receiver = bob;
|
||||
deal(WETH_ADDR, swapMethodAddress, amountIn);
|
||||
bytes memory data = abi.encodePacked(
|
||||
getDataCurve(LDO, LDO_POOL, receiver, 7, 0, 1, true), receiver
|
||||
);
|
||||
|
||||
uint256 amountOut = swapMethod.swap(amountIn, data);
|
||||
|
||||
uint256 finalBalance = LDO.balanceOf(bob);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(amountOut, expAmountOut);
|
||||
}
|
||||
// Curve v2 2 token not factory pool
|
||||
|
||||
function testSwapType8() public {
|
||||
vm.rollFork(17_000_000); //change block because this pool wasn't
|
||||
// deployed at block 16M
|
||||
uint256 amountIn = 10 ** 18;
|
||||
uint256 expAmountOut = 1831110768300490995125;
|
||||
address receiver = bob;
|
||||
deal(WETH_ADDR, swapMethodAddress, amountIn);
|
||||
bytes memory data = abi.encodePacked(
|
||||
getDataCurve(CRV, CRV_POOL, receiver, 8, 0, 1, true), receiver
|
||||
);
|
||||
|
||||
uint256 amountOut = swapMethod.swap(amountIn, data);
|
||||
|
||||
uint256 finalBalance = CRV.balanceOf(bob);
|
||||
assertGe(finalBalance, expAmountOut);
|
||||
assertEq(amountOut, expAmountOut);
|
||||
}
|
||||
|
||||
function testDecodeParams() public {
|
||||
CurveSwapExecutorExposed swapMethodExposed =
|
||||
new CurveSwapExecutorExposed();
|
||||
|
||||
//Logic
|
||||
bytes memory data = getDataCurve(LDO, LDO_POOL, bob, 7, 0, 1, true);
|
||||
(
|
||||
IERC20 tokenOut,
|
||||
address target,
|
||||
address receiver,
|
||||
uint8 poolType,
|
||||
int128 i,
|
||||
int128 j,
|
||||
bool tokenApprovalNeeded
|
||||
) = swapMethodExposed.decodeParams(data);
|
||||
|
||||
//Assertions
|
||||
assertEq(address(tokenOut), LDO_ADDR);
|
||||
assertEq(address(target), LDO_POOL);
|
||||
assertEq(address(receiver), bob);
|
||||
assertEq(poolType, 7);
|
||||
assertEq(i, 0);
|
||||
assertEq(j, 1);
|
||||
assertEq(tokenApprovalNeeded, true);
|
||||
}
|
||||
|
||||
function getDataCurve(
|
||||
IERC20 tokenOut,
|
||||
address pool,
|
||||
address receiver,
|
||||
uint8 poolType,
|
||||
uint8 i,
|
||||
uint8 j,
|
||||
bool tokenApprovalNeeded
|
||||
) internal pure returns (bytes memory data) {
|
||||
data = abi.encodePacked(
|
||||
tokenOut, pool, receiver, poolType, i, j, tokenApprovalNeeded
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.13;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
import "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
|
||||
import "./Constants.sol";
|
||||
|
||||
contract SwapExecutorTest is Test, Constants {
|
||||
function twoTokens(address token0, address token1)
|
||||
internal
|
||||
pure
|
||||
returns (IERC20[] memory tokens)
|
||||
{
|
||||
tokens = new IERC20[](2);
|
||||
tokens[0] = IERC20(token0);
|
||||
tokens[1] = IERC20(token1);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
name: propeller-swap-encoders
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- python=3.9
|
||||
@@ -1,23 +0,0 @@
|
||||
from typing import Any
|
||||
|
||||
from core.encoding.interface import SwapStructEncoder, EncodingContext
|
||||
from core.type_aliases import Address
|
||||
from eth_abi.packed import encode_abi_packed
|
||||
from hexbytes import HexBytes
|
||||
|
||||
|
||||
class BalancerSwapStructEncoder(SwapStructEncoder):
|
||||
def encode_swap_struct(
|
||||
self, swap: dict[str, Any], receiver: Address, encoding_context: EncodingContext
|
||||
) -> bytes:
|
||||
return encode_abi_packed(
|
||||
["address", "address", "bytes32", "address", "bool", "bool"],
|
||||
[
|
||||
swap["sell_token"].address,
|
||||
swap["buy_token"].address,
|
||||
HexBytes(swap["pool_id"]),
|
||||
receiver,
|
||||
encoding_context.exact_out,
|
||||
swap["token_approval_needed"],
|
||||
],
|
||||
)
|
||||
@@ -1,98 +0,0 @@
|
||||
import enum
|
||||
from typing import Any
|
||||
|
||||
from core.encoding.interface import EncodingContext, SwapStructEncoder
|
||||
from core.type_aliases import Address
|
||||
from eth_abi.packed import encode_abi_packed
|
||||
from eth_utils import to_checksum_address
|
||||
|
||||
curve_config = {
|
||||
# curve pool type 4
|
||||
"eth_stable_pools": [
|
||||
"0xA96A65c051bF88B4095Ee1f2451C2A9d43F53Ae2",
|
||||
"0xF9440930043eb3997fc70e1339dBb11F341de7A8",
|
||||
"0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577",
|
||||
"0xBfAb6FA95E0091ed66058ad493189D2cB29385E6",
|
||||
"0x94B17476A93b3262d87B9a326965D1E91f9c13E7",
|
||||
],
|
||||
# curve pool type 7
|
||||
"v2_eth_pools": [
|
||||
"0x9409280DC1e6D33AB7A8C6EC03e5763FB61772B5",
|
||||
"0x5FAE7E604FC3e24fd43A72867ceBaC94c65b404A",
|
||||
"0x0f3159811670c117c372428D4E69AC32325e4D0F",
|
||||
"0x838af967537350D2C44ABB8c010E49E32673ab94",
|
||||
"0xC26b89A667578ec7b3f11b2F98d6Fd15C07C54ba",
|
||||
"0x6bfE880Ed1d639bF80167b93cc9c56a39C1Ba2dC",
|
||||
"0x0E9B5B092caD6F1c5E6bc7f89Ffe1abb5c95F1C2",
|
||||
"0x21410232B484136404911780bC32756D5d1a9Fa9",
|
||||
"0xfB8814D005C5f32874391e888da6eB2fE7a27902",
|
||||
"0xe0e970a99bc4F53804D8145beBBc7eBc9422Ba7F",
|
||||
"0x6e314039f4C56000F4ebb3a7854A84cC6225Fb92",
|
||||
"0xf861483fa7E511fbc37487D91B6FAa803aF5d37c",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class CurvePoolType(enum.IntEnum):
|
||||
"""
|
||||
Represents different swap logics of curve pools. For more details, please see
|
||||
CurveSwapMethodV1 in defibot-contracts repository.
|
||||
"""
|
||||
|
||||
simple = 0
|
||||
simple_no_amount = 1
|
||||
tricrypto = 3
|
||||
eth_stableswap = 4
|
||||
underlying = 5
|
||||
underlying_no_amount = 6
|
||||
crypto_v2 = 7
|
||||
crypto_v2_2_tokens_not_factory = 8
|
||||
|
||||
|
||||
curve_v2_pool_type_mapping: dict[str, CurvePoolType] = {
|
||||
"tricrypto2_non_factory": CurvePoolType.tricrypto,
|
||||
"two_token_factory": CurvePoolType.crypto_v2,
|
||||
"two_token_non_factory": CurvePoolType.crypto_v2_2_tokens_not_factory,
|
||||
}
|
||||
|
||||
|
||||
class CurveSwapStructEncoder(SwapStructEncoder):
|
||||
eth_stable_pools: list[str] = curve_config["eth_stable_pools"]
|
||||
v2_eth_pools = curve_config["v2_eth_pools"]
|
||||
|
||||
def encode_swap_struct(
|
||||
self, swap: dict[str, Any], receiver: Address, encoding_context: EncodingContext
|
||||
) -> bytes:
|
||||
|
||||
pool_type = swap["pool_type"]
|
||||
if pool_type == "CurveSimulatedPoolState":
|
||||
curve_pool_type = (
|
||||
CurvePoolType.tricrypto
|
||||
if swap["protocol_specific_attrs"]["is_curve_tricrypto"]
|
||||
else CurvePoolType.simple_no_amount
|
||||
)
|
||||
elif to_checksum_address(swap["pool_id"]) in self.v2_eth_pools:
|
||||
curve_pool_type = CurvePoolType.crypto_v2
|
||||
elif to_checksum_address(swap["pool_id"]) in self.eth_stable_pools:
|
||||
curve_pool_type = CurvePoolType.eth_stableswap
|
||||
else:
|
||||
curve_pool_type = (
|
||||
curve_v2_pool_type_mapping[
|
||||
swap["protocol_specific_attrs"]["curve_v2_pool_type"]
|
||||
]
|
||||
if pool_type == "CurveV2PoolState"
|
||||
else CurvePoolType.simple_no_amount
|
||||
)
|
||||
|
||||
return encode_abi_packed(
|
||||
["address", "address", "address", "uint8", "uint8", "uint8", "bool"],
|
||||
[
|
||||
swap["buy_token"].address,
|
||||
swap["pool_id"],
|
||||
receiver,
|
||||
curve_pool_type,
|
||||
swap["pool_tokens"].index(swap["sell_token"]),
|
||||
swap["pool_tokens"].index(swap["buy_token"]),
|
||||
swap["token_approval_needed"],
|
||||
],
|
||||
)
|
||||
@@ -1,51 +0,0 @@
|
||||
from core.models.evm.ethereum_token import EthereumToken
|
||||
from propeller_swap_encoders.balancer import BalancerSwapStructEncoder
|
||||
from core.encoding.interface import EncodingContext
|
||||
|
||||
|
||||
def test_encode_balancer():
|
||||
WETH = EthereumToken(
|
||||
symbol="WETH",
|
||||
address="0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
|
||||
decimals=18,
|
||||
gas=0,
|
||||
)
|
||||
DAI = EthereumToken(
|
||||
symbol="DAI", address="0x6b175474e89094c44da98b954eedeac495271d0f", decimals=18
|
||||
)
|
||||
bob = "0x000000000000000000000000000000000000007B"
|
||||
swap = {
|
||||
"pool_id": "0x06df3b2bbb68adc8b0e302443692037ed9f91b42000000000000000000000063",
|
||||
"sell_token": DAI,
|
||||
"buy_token": WETH,
|
||||
"split": 0,
|
||||
"sell_amount": 0,
|
||||
"buy_amount": 100,
|
||||
"token_approval_needed": False,
|
||||
"pool_tokens": (),
|
||||
"pool_type": "BalancerStablePoolState",
|
||||
"curve_v2_pool_type": None,
|
||||
"is_curve_tricrypto": None,
|
||||
"quote": None,
|
||||
"pool_fee": None,
|
||||
}
|
||||
balancer_encoder = BalancerSwapStructEncoder()
|
||||
encoded = balancer_encoder.encode_swap_struct(
|
||||
swap, receiver=bob, encoding_context=EncodingContext(exact_out=False)
|
||||
)
|
||||
assert (
|
||||
encoded.hex()
|
||||
==
|
||||
# sell token
|
||||
"6b175474e89094c44da98b954eedeac495271d0f"
|
||||
# buy token
|
||||
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
|
||||
# pool address
|
||||
"06df3b2bbb68adc8b0e302443692037ed9f91b42000000000000000000000063"
|
||||
# receiver
|
||||
"000000000000000000000000000000000000007b"
|
||||
# exact_out
|
||||
"00"
|
||||
# token_approval_needed
|
||||
"00"
|
||||
)
|
||||
@@ -1,148 +0,0 @@
|
||||
from core.encoding.interface import EncodingContext
|
||||
from core.models.evm.ethereum_token import EthereumToken
|
||||
|
||||
from propeller_swap_encoders.curve import CurveSwapStructEncoder
|
||||
|
||||
WETH = EthereumToken(
|
||||
symbol="WETH",
|
||||
address="0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
|
||||
decimals=18,
|
||||
gas=0,
|
||||
)
|
||||
USDT = EthereumToken(
|
||||
symbol="USDT", address="0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals=6
|
||||
)
|
||||
WBTC = EthereumToken(
|
||||
symbol="WBTC", address="0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", decimals=8
|
||||
)
|
||||
|
||||
|
||||
def test_encode_curve_v2():
|
||||
bob = "0x000000000000000000000000000000000000007B"
|
||||
|
||||
swap = {
|
||||
"pool_id": "0xD51a44d3FaE010294C616388b506AcdA1bfAAE46",
|
||||
"sell_token": USDT,
|
||||
"buy_token": WETH,
|
||||
"split": 0,
|
||||
"sell_amount": 0,
|
||||
"buy_amount": 100,
|
||||
"token_approval_needed": False,
|
||||
"pool_tokens": (USDT, WBTC, WETH),
|
||||
"pool_type": "CurveV2PoolState",
|
||||
"protocol_specific_attrs": {
|
||||
"curve_v2_pool_type": "tricrypto2_non_factory",
|
||||
"is_curve_tricrypto": None,
|
||||
"quote": None,
|
||||
"pool_fee": None,
|
||||
},
|
||||
}
|
||||
|
||||
curve_encoder = CurveSwapStructEncoder()
|
||||
encoded = curve_encoder.encode_swap_struct(
|
||||
swap, receiver=bob, encoding_context=EncodingContext()
|
||||
)
|
||||
assert (
|
||||
encoded.hex()
|
||||
==
|
||||
# buy token
|
||||
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
|
||||
# pool address
|
||||
"d51a44d3fae010294c616388b506acda1bfaae46"
|
||||
# receiver
|
||||
"000000000000000000000000000000000000007b"
|
||||
# pool type (tricrypto = 3)
|
||||
"03"
|
||||
# i (sell token index)
|
||||
"00"
|
||||
# j (buy token index)
|
||||
"02"
|
||||
# token_approval_needed
|
||||
"00"
|
||||
)
|
||||
|
||||
|
||||
def test_encode_curve_v1():
|
||||
bob = "0x000000000000000000000000000000000000007B"
|
||||
swap = {
|
||||
"pool_id": "bebc44782c7db0a1a60cb6fe97d0b483032ff1c7",
|
||||
"sell_token": USDT,
|
||||
"buy_token": WETH,
|
||||
"split": 0,
|
||||
"sell_amount": 0,
|
||||
"buy_amount": 100,
|
||||
"token_approval_needed": False,
|
||||
"pool_tokens": (USDT, WBTC, WETH),
|
||||
"pool_type": "CurveV1PoolState",
|
||||
"protocol_specific_attrs": {
|
||||
"curve_v2_pool_type": None,
|
||||
"is_curve_tricrypto": None,
|
||||
"quote": None,
|
||||
"pool_fee": 1000000,
|
||||
},
|
||||
}
|
||||
curve_encoder = CurveSwapStructEncoder()
|
||||
encoded = curve_encoder.encode_swap_struct(
|
||||
swap, receiver=bob, encoding_context=EncodingContext()
|
||||
)
|
||||
assert (
|
||||
encoded.hex()
|
||||
==
|
||||
# buy token
|
||||
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
|
||||
# pool address
|
||||
"bebc44782c7db0a1a60cb6fe97d0b483032ff1c7"
|
||||
# receiver
|
||||
"000000000000000000000000000000000000007b"
|
||||
# pool type (simple_no_amount = 1)
|
||||
"01"
|
||||
# i (sell token index)
|
||||
"00"
|
||||
# j (buy token index)
|
||||
"02"
|
||||
# token_approval_needed
|
||||
"00"
|
||||
)
|
||||
|
||||
|
||||
def test_encode_curve_evm_crypto_pool():
|
||||
bob = "0x000000000000000000000000000000000000007B"
|
||||
swap = {
|
||||
"pool_id": "bebc44782c7db0a1a60cb6fe97d0b483032ff1c7",
|
||||
"sell_token": USDT,
|
||||
"buy_token": WETH,
|
||||
"split": 0,
|
||||
"sell_amount": 0,
|
||||
"buy_amount": 100,
|
||||
"token_approval_needed": False,
|
||||
"pool_tokens": (USDT, WBTC, WETH),
|
||||
"pool_type": "CurveSimulatedPoolState",
|
||||
"protocol_specific_attrs": {
|
||||
"curve_v2_pool_type": None,
|
||||
"is_curve_tricrypto": True,
|
||||
"quote": None,
|
||||
"pool_fee": None,
|
||||
},
|
||||
}
|
||||
curve_encoder = CurveSwapStructEncoder()
|
||||
encoded = curve_encoder.encode_swap_struct(
|
||||
swap, receiver=bob, encoding_context=EncodingContext()
|
||||
)
|
||||
assert (
|
||||
encoded.hex()
|
||||
==
|
||||
# buy token
|
||||
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
|
||||
# pool address
|
||||
"bebc44782c7db0a1a60cb6fe97d0b483032ff1c7"
|
||||
# receiver
|
||||
"000000000000000000000000000000000000007b"
|
||||
# pool type (tricrypto = 3)
|
||||
"03"
|
||||
# i (sell token index)
|
||||
"00"
|
||||
# j (buy token index)
|
||||
"02"
|
||||
# token_approval_needed
|
||||
"00"
|
||||
)
|
||||
@@ -1,35 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=60", "setuptools-scm>=8.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "propeller-swap-encoders"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = [
|
||||
{ name = "Propeller Heads" }
|
||||
]
|
||||
keywords = ["propeller-swap-encoders"]
|
||||
classifiers = [
|
||||
"Intended Audience :: Developers",
|
||||
"Natural Language :: English",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.7"
|
||||
dependencies = [
|
||||
"propeller-solver-core==0.1.0",
|
||||
]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
include = ["propeller_swap_encoders"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
testing = [
|
||||
"pytest",
|
||||
"pytest-runner"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://github.com/propeller-heads/tycho-protocol-sdk"
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
propeller-solver-core==0.1.0
|
||||
pyyaml==5.4.1
|
||||
pytest==6.2.4
|
||||
black==24.4.2
|
||||
Reference in New Issue
Block a user