From b29c23b1fedd45a483d087b912ad12e3ba354244 Mon Sep 17 00:00:00 2001 From: Oleg <40476427+amfet42@users.noreply.github.com> Date: Fri, 21 Nov 2025 00:57:46 +0100 Subject: [PATCH 1/4] add router for stablechain --- .../helpers/router/router_v_stablechain.vy | 371 ++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 contracts/helpers/router/router_v_stablechain.vy diff --git a/contracts/helpers/router/router_v_stablechain.vy b/contracts/helpers/router/router_v_stablechain.vy new file mode 100644 index 0000000..c5cdac7 --- /dev/null +++ b/contracts/helpers/router/router_v_stablechain.vy @@ -0,0 +1,371 @@ +# pragma version 0.3.10 + +""" +@title CurveRouter +@custom:version 1.1.0 +@author Curve.Fi +@license Copyright (c) Curve.Fi, 2020-2024 - all rights reserved +@notice Performs up to 5 swaps in a single transaction + Can do estimations with get_dy and get_dx +""" + +version: public(constant(String[8])) = "1.1.0" # ng pools + + +from vyper.interfaces import ERC20 + +interface StableNgPool: + def get_dy(i: int128, j: int128, in_amount: uint256) -> uint256: view + def get_dx(i: int128, j: int128, out_amount: uint256) -> uint256: view + def exchange(i: int128, j: int128, dx: uint256, min_dy: uint256): nonpayable + def calc_token_amount(_amounts: DynArray[uint256, 8], _is_deposit: bool) -> uint256: view + def add_liquidity(_amounts: DynArray[uint256, 8], _min_mint_amount: uint256) -> uint256: nonpayable + def calc_withdraw_one_coin(token_amount: uint256, i: int128) -> uint256: view + def remove_liquidity_one_coin(token_amount: uint256, i: int128, min_amount: uint256): nonpayable + +interface StableNgMetaPool: + def get_dy_underlying(i: int128, j: int128, amount: uint256) -> uint256: view + def get_dx_underlying(i: int128, j: int128, amount: uint256) -> uint256: view + def exchange_underlying(i: int128, j: int128, dx: uint256, min_dy: uint256): nonpayable + +interface CryptoNgPool: + def get_dy(i: uint256, j: uint256, in_amount: uint256) -> uint256: view + def get_dx(i: uint256, j: uint256, out_amount: uint256) -> uint256: view + def exchange(i: uint256, j: uint256, dx: uint256, min_dy: uint256): nonpayable + def calc_withdraw_one_coin(token_amount: uint256, i: uint256) -> uint256: view + def remove_liquidity_one_coin(token_amount: uint256, i: uint256, min_amount: uint256): nonpayable + +interface TwoCryptoNgPool: + def calc_token_amount(amounts: uint256[2], is_deposit: bool) -> uint256: view + def add_liquidity(amounts: uint256[2], min_mint_amount: uint256) -> uint256: nonpayable + +interface TriCryptoNgPool: + def calc_token_amount(amounts: uint256[3], is_deposit: bool) -> uint256: view + def add_liquidity(amounts: uint256[3], min_mint_amount: uint256) -> uint256: nonpayable + +interface WETH: + def deposit(): payable + def withdraw(_amount: uint256): nonpayable + + +event Exchange: + sender: indexed(address) + receiver: indexed(address) + route: address[11] + swap_params: uint256[4][5] + in_amount: uint256 + out_amount: uint256 + +NATIVE_TOKEN_ADDRESS: immutable(address) +GTOKEN_ADDRESS: immutable(address) + +is_approved: HashMap[address, HashMap[address, bool]] + + +@external +@payable +def __default__(): + pass + + +@external +def __init__( _native_token: address, _gtoken: address): + NATIVE_TOKEN_ADDRESS = _native_token + GTOKEN_ADDRESS = _gtoken + + +@external +@payable +@nonreentrant('lock') +def exchange( + _route: address[11], + _swap_params: uint256[4][5], + _amount: uint256, + _min_dy: uint256, + _receiver: address=msg.sender +) -> uint256: + """ + @notice Performs up to 5 swaps in a single transaction. + @dev Routing and swap params must be determined off-chain. This + functionality is designed for gas efficiency over ease-of-use. + @param _route Array of [initial token, pool, token, pool, token, ...] + The array is iterated until a pool address of 0x00, then the last + given token is transferred to `_receiver` + @param _swap_params Multidimensional array of [i, j, swap type, pool_type] where + i is the index of input token + j is the index of output token + + The swap_type should be: + 1. for `exchange`, + 2. for `exchange_underlying` (stable-ng metapools), + 3. -- legacy -- + 4. for coin -> LP token "exchange" (actually `add_liquidity`), + 5. -- legacy -- + 6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`) + 7. -- legacy -- + 8. for USDT <-> gUSDT + + pool_type: 10 - stable-ng, 20 - twocrypto-ng, 30 - tricrypto-ng, 4 - llamma + + @param _amount The amount of input token (`_route[0]`) to be sent. + @param _min_dy The minimum amount received after the final swap. + @param _receiver Address to transfer the final output token to. + @return Received amount of the final output token. + """ + input_token: address = _route[0] + output_token: address = empty(address) + amount: uint256 = _amount + + # validate / transfer initial token + if input_token == NATIVE_TOKEN_ADDRESS: + assert msg.value == amount + else: + assert msg.value == 0 + assert ERC20(input_token).transferFrom(msg.sender, self, amount, default_return_value=True) + + for i in range(5): + # 5 rounds of iteration to perform up to 5 swaps + swap: address = _route[i * 2 + 1] + output_token = _route[(i + 1) * 2] + params: uint256[4] = _swap_params[i] # i, j, swap_type, pool_type + + # store the initial balance of the output_token + output_token_initial_balance: uint256 = self.balance + if output_token != NATIVE_TOKEN_ADDRESS: + output_token_initial_balance = ERC20(output_token).balanceOf(self) + + if not self.is_approved[input_token][swap]: + assert ERC20(input_token).approve(swap, max_value(uint256), default_return_value=True, skip_contract_check=True) + self.is_approved[input_token][swap] = True + + # perform the swap according to the swap type + if params[2] == 1: + if params[3] == 10: # stable-ng + StableNgPool(swap).exchange(convert(params[0], int128), convert(params[1], int128), amount, 0) + else: # twocrypto-ng, tricrypto-ng, llamma + CryptoNgPool(swap).exchange(params[0], params[1], amount, 0) + elif params[2] == 2: # stable-ng metapools + StableNgMetaPool(swap).exchange_underlying(convert(params[0], int128), convert(params[1], int128), amount, 0) + elif params[2] == 4: + if params[3] == 10: # stable-ng + amounts: DynArray[uint256, 8] = [0, 0, 0, 0, 0, 0, 0, 0] + amounts[params[0]] = amount + StableNgPool(swap).add_liquidity(amounts, 0) + elif params[3] == 20: # twocrypto-ng + amounts: uint256[2] = [0, 0] + amounts[params[0]] = amount + TwoCryptoNgPool(swap).add_liquidity(amounts, 0) + elif params[3] == 30: # tricrypto-ng + amounts: uint256[3] = [0, 0, 0] + amounts[params[0]] = amount + TriCryptoNgPool(swap).add_liquidity(amounts, 0) + elif params[2] == 6: + if params[3] == 10: # stable-ng + StableNgPool(swap).remove_liquidity_one_coin(amount, convert(params[1], int128), 0) + else: # twocrypto-ng, tricrypto-ng + CryptoNgPool(swap).remove_liquidity_one_coin(amount, params[1], 0) + elif params[2] == 8: + if input_token == NATIVE_TOKEN_ADDRESS and output_token == GTOKEN_ADDRESS: + # hadnled by converter + WETH(swap).deposit(value=amount) + elif input_token == GTOKEN_ADDRESS and output_token == NATIVE_TOKEN_ADDRESS: + # hadnled by converter + WETH(swap).withdraw(amount) + else: + raise "Swap type 8 is only for USDT <-> gUSDT" + else: + raise "Bad swap type" + + # update the amount received + if output_token == NATIVE_TOKEN_ADDRESS: + amount = self.balance + else: + amount = ERC20(output_token).balanceOf(self) + + # sanity check, if the routing data is incorrect we will have a 0 balance change and that is bad + assert amount - output_token_initial_balance != 0, "Received nothing" + + # check if this was the last swap + if i == 4 or _route[i * 2 + 3] == empty(address): + break + # if there is another swap, the output token becomes the input for the next round + input_token = output_token + + amount -= 1 # Change non-zero -> non-zero costs less gas than zero -> non-zero + assert amount >= _min_dy, "Slippage" + + # transfer the final token to the receiver + if output_token == NATIVE_TOKEN_ADDRESS: + raw_call(_receiver, b"", value=amount) + else: + assert ERC20(output_token).transfer(_receiver, amount, default_return_value=True) + + log Exchange(msg.sender, _receiver, _route, _swap_params, _amount, amount) + + return amount + + +@view +@external +def get_dy( + _route: address[11], + _swap_params: uint256[4][5], + _amount: uint256, +) -> uint256: + """ + @notice Get amount of the final output token received in an exchange + @dev Routing and swap params must be determined off-chain. This + functionality is designed for gas efficiency over ease-of-use. + @param _route Array of [initial token, pool, token, pool, token, ...] + The array is iterated until a pool address of 0x00, then the last + given token is transferred to `_receiver` + @param _swap_params Multidimensional array of [i, j, swap type, pool_type] where + i is the index of input token + j is the index of output token + + The swap_type should be: + 1. for `exchange`, + 2. for `exchange_underlying` (stable-ng metapools), + 3. -- legacy -- + 4. for coin -> LP token "exchange" (actually `add_liquidity`), + 5. -- legacy -- + 6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`) + 7. -- legacy -- + 8. for USDT <-> gUSDT + + pool_type: 10 - stable-ng, 20 - twocrypto-ng, 30 - tricrypto-ng, 4 - llamma + + @param _amount The amount of input token (`_route[0]`) to be sent. + @return Expected amount of the final output token. + """ + amount: uint256 = _amount + + for i in range(5): + # 5 rounds of iteration to perform up to 5 swaps + swap: address = _route[i * 2 + 1] + params: uint256[4] = _swap_params[i] # i, j, swap_type, pool_type + + # Calc output amount according to the swap type + if params[2] == 1: + if params[3] == 10: # stable_ng + amount = StableNgPool(swap).get_dy(convert(params[0], int128), convert(params[1], int128), amount) + else: # twocrypto-ng, tricrypto-ng, llamma + amount = CryptoNgPool(swap).get_dy(params[0], params[1], amount) + elif params[2] == 2: # stable-ng metapools + amount = StableNgMetaPool(swap).get_dy_underlying(convert(params[0], int128), convert(params[1], int128), amount) + elif params[2] == 4: + if params[3] == 10: # stable-ng + amounts: DynArray[uint256, 8] = [0, 0, 0, 0, 0, 0, 0, 0] + amounts[params[0]] = amount + amount = StableNgPool(swap).calc_token_amount(amounts, True) + elif params[3] == 20: # twocrypto-ng + amounts: uint256[2] = [0, 0] + amounts[params[0]] = amount + amount = TwoCryptoNgPool(swap).calc_token_amount(amounts, True) + elif params[3] == 30: # tricrypto-ng + amounts: uint256[3] = [0, 0, 0] + amounts[params[0]] = amount + amount = TriCryptoNgPool(swap).calc_token_amount(amounts, True) + elif params[2] == 6: + if params[3] == 10: # stable-ng + amount = StableNgPool(swap).calc_withdraw_one_coin(amount, convert(params[1], int128)) + else: # twocrypto-ng, tricrypto-ng + amount = CryptoNgPool(swap).calc_withdraw_one_coin(amount, params[1]) + elif params[2] == 8: + # USDT <-> gUSDT rate is 1:1 + pass + else: + raise "Bad swap type" + + # check if this was the last swap + if i == 4 or _route[i * 2 + 3] == empty(address): + break + + return amount - 1 + + +@view +@external +def get_dx( + _route: address[11], + _swap_params: uint256[4][5], + _out_amount: uint256, + _base_pools: address[5]=empty(address[5]), +) -> uint256: + """ + @notice Calculate the input amount required to receive the desired output amount + @dev Routing and swap params must be determined off-chain. This + functionality is designed for gas efficiency over ease-of-use. + @param _route Array of [initial token, pool, token, pool, token, ...] + The array is iterated until a pool address of 0x00, then the last + given token is transferred to `_receiver` + @param _swap_params Multidimensional array of [i, j, swap type, pool_type] where + i is the index of input token + j is the index of output token + + The swap_type should be: + 1. for `exchange`, + 2. for `exchange_underlying` (stable-ng metapools), + 3. -- legacy -- + 4. for coin -> LP token "exchange" (actually `add_liquidity`), + 5. -- legacy -- + 6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`) + 7. -- legacy -- + 8. for USDT <-> gUSDT + + pool_type: 10 - stable-ng, 20 - twocrypto-ng, 30 - tricrypto-ng, 4 - llamma + + @param _out_amount The desired amount of output coin to receive. + @param _base_pools Array of base pools (for meta pools). + @return Required amount of input token to send. + """ + amount: uint256 = _out_amount + + for _i in range(5): + # 5 rounds of iteration to perform up to 5 swaps + i: uint256 = 4 - _i + swap: address = _route[i * 2 + 1] + if swap == empty(address): + continue + base_pool: address = _base_pools[i] + params: uint256[4] = _swap_params[i] # i, j, swap_type, pool_type + + # Calc a required input amount according to the swap type + if params[2] == 1: + if params[3] == 10: # stable-ng + amount = StableNgPool(swap).get_dx(convert(params[0], int128), convert(params[1], int128), amount) + else: # twocrypto-ng, tricrypto-ng, llamma + amount = CryptoNgPool(swap).get_dx(params[0], params[1], amount) + elif params[2] == 2: # stable-ng metapool + _n: int128 = convert(params[0], int128) + _k: int128 = convert(params[1], int128) + if _n > 0 and _k > 0: + amount = StableNgPool(base_pool).get_dx(_n - 1, _k - 1, amount) + else: + amount = StableNgMetaPool(swap).get_dx_underlying(_n, _k, amount) + elif params[2] == 4: + # This is not correct. Should be something like calc_add_one_coin. But tests say that it's precise enough. + if params[3] == 10: # stable_ng + amount = StableNgPool(swap).calc_withdraw_one_coin(amount, convert(params[0], int128)) + else: # twocrypto-ng, tricrypto-ng + amount = CryptoNgPool(swap).calc_withdraw_one_coin(amount, params[0]) + elif params[2] == 6: + if params[3] == 10: # stable-ng + amounts: DynArray[uint256, 8] = [0, 0, 0, 0, 0, 0, 0, 0] + amounts[params[1]] = amount + amount = StableNgPool(swap).calc_token_amount(amounts, False) + elif params[3] == 20: # twocrypto-ng + amounts: uint256[2] = [0, 0] + amounts[params[1]] = amount + amount = TwoCryptoNgPool(swap).calc_token_amount(amounts, False) + elif params[3] == 30: # tricrypto-ng + amounts: uint256[3] = [0, 0, 0] + amounts[params[1]] = amount + amount = TriCryptoNgPool(swap).calc_token_amount(amounts, False) + elif params[2] == 8: + # USDT <-> gUSDT rate is 1:1 + pass + else: + raise "Bad swap type" + + return amount From 2b3cab7b126a881153381581f3023a68722ccdbb Mon Sep 17 00:00:00 2001 From: macket Date: Tue, 25 Nov 2025 13:32:58 +0400 Subject: [PATCH 2/4] feat: custom native token address for Router --- contracts/helpers/router/router_v_110.vy | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/helpers/router/router_v_110.vy b/contracts/helpers/router/router_v_110.vy index 6f0fb6b..d452bfa 100644 --- a/contracts/helpers/router/router_v_110.vy +++ b/contracts/helpers/router/router_v_110.vy @@ -57,7 +57,9 @@ event Exchange: out_amount: uint256 -ETH_ADDRESS: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE +DEFAULT_ETH_ADDRESS: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE + +ETH_ADDRESS: immutable(address) WETH_ADDRESS: immutable(address) is_approved: HashMap[address, HashMap[address, bool]] @@ -70,8 +72,9 @@ def __default__(): @external -def __init__( _weth: address): +def __init__(_weth: address, _eth: address = DEFAULT_ETH_ADDRESS): WETH_ADDRESS = _weth + ETH_ADDRESS = _eth @external From cbe986e48a2fd3c86d89f6f2754e55c4466737ef Mon Sep 17 00:00:00 2001 From: Oleg <40476427+amfet42@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:39:23 +0100 Subject: [PATCH 3/4] fix router contract version --- contracts/helpers/router/router_v_110.vy | 7 +-- ...outer_v_stablechain.vy => router_v_111.vy} | 43 ++++++++++--------- 2 files changed, 24 insertions(+), 26 deletions(-) rename contracts/helpers/router/{router_v_stablechain.vy => router_v_111.vy} (93%) diff --git a/contracts/helpers/router/router_v_110.vy b/contracts/helpers/router/router_v_110.vy index d452bfa..6f0fb6b 100644 --- a/contracts/helpers/router/router_v_110.vy +++ b/contracts/helpers/router/router_v_110.vy @@ -57,9 +57,7 @@ event Exchange: out_amount: uint256 -DEFAULT_ETH_ADDRESS: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE - -ETH_ADDRESS: immutable(address) +ETH_ADDRESS: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE WETH_ADDRESS: immutable(address) is_approved: HashMap[address, HashMap[address, bool]] @@ -72,9 +70,8 @@ def __default__(): @external -def __init__(_weth: address, _eth: address = DEFAULT_ETH_ADDRESS): +def __init__( _weth: address): WETH_ADDRESS = _weth - ETH_ADDRESS = _eth @external diff --git a/contracts/helpers/router/router_v_stablechain.vy b/contracts/helpers/router/router_v_111.vy similarity index 93% rename from contracts/helpers/router/router_v_stablechain.vy rename to contracts/helpers/router/router_v_111.vy index c5cdac7..975a93e 100644 --- a/contracts/helpers/router/router_v_stablechain.vy +++ b/contracts/helpers/router/router_v_111.vy @@ -2,14 +2,14 @@ """ @title CurveRouter -@custom:version 1.1.0 +@custom:version 1.1.1 @author Curve.Fi @license Copyright (c) Curve.Fi, 2020-2024 - all rights reserved @notice Performs up to 5 swaps in a single transaction Can do estimations with get_dy and get_dx """ -version: public(constant(String[8])) = "1.1.0" # ng pools +version: public(constant(String[8])) = "1.1.1" from vyper.interfaces import ERC20 @@ -56,8 +56,11 @@ event Exchange: in_amount: uint256 out_amount: uint256 -NATIVE_TOKEN_ADDRESS: immutable(address) -GTOKEN_ADDRESS: immutable(address) + +DEFAULT_ETH_ADDRESS: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE + +ETH_ADDRESS: immutable(address) +WETH_ADDRESS: immutable(address) is_approved: HashMap[address, HashMap[address, bool]] @@ -69,9 +72,9 @@ def __default__(): @external -def __init__( _native_token: address, _gtoken: address): - NATIVE_TOKEN_ADDRESS = _native_token - GTOKEN_ADDRESS = _gtoken +def __init__(_weth: address, _eth: address = DEFAULT_ETH_ADDRESS): + WETH_ADDRESS = _weth + ETH_ADDRESS = _eth @external @@ -103,7 +106,7 @@ def exchange( 5. -- legacy -- 6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`) 7. -- legacy -- - 8. for USDT <-> gUSDT + 8. for ETH <-> WETH pool_type: 10 - stable-ng, 20 - twocrypto-ng, 30 - tricrypto-ng, 4 - llamma @@ -117,7 +120,7 @@ def exchange( amount: uint256 = _amount # validate / transfer initial token - if input_token == NATIVE_TOKEN_ADDRESS: + if input_token == ETH_ADDRESS: assert msg.value == amount else: assert msg.value == 0 @@ -131,7 +134,7 @@ def exchange( # store the initial balance of the output_token output_token_initial_balance: uint256 = self.balance - if output_token != NATIVE_TOKEN_ADDRESS: + if output_token != ETH_ADDRESS: output_token_initial_balance = ERC20(output_token).balanceOf(self) if not self.is_approved[input_token][swap]: @@ -165,19 +168,17 @@ def exchange( else: # twocrypto-ng, tricrypto-ng CryptoNgPool(swap).remove_liquidity_one_coin(amount, params[1], 0) elif params[2] == 8: - if input_token == NATIVE_TOKEN_ADDRESS and output_token == GTOKEN_ADDRESS: - # hadnled by converter + if input_token == ETH_ADDRESS and output_token == WETH_ADDRESS: WETH(swap).deposit(value=amount) - elif input_token == GTOKEN_ADDRESS and output_token == NATIVE_TOKEN_ADDRESS: - # hadnled by converter + elif input_token == WETH_ADDRESS and output_token == ETH_ADDRESS: WETH(swap).withdraw(amount) else: - raise "Swap type 8 is only for USDT <-> gUSDT" + raise "Swap type 8 is only for ETH <-> WETH" else: raise "Bad swap type" # update the amount received - if output_token == NATIVE_TOKEN_ADDRESS: + if output_token == ETH_ADDRESS: amount = self.balance else: amount = ERC20(output_token).balanceOf(self) @@ -195,7 +196,7 @@ def exchange( assert amount >= _min_dy, "Slippage" # transfer the final token to the receiver - if output_token == NATIVE_TOKEN_ADDRESS: + if output_token == ETH_ADDRESS: raw_call(_receiver, b"", value=amount) else: assert ERC20(output_token).transfer(_receiver, amount, default_return_value=True) @@ -231,7 +232,7 @@ def get_dy( 5. -- legacy -- 6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`) 7. -- legacy -- - 8. for USDT <-> gUSDT + 8. for ETH <-> WETH pool_type: 10 - stable-ng, 20 - twocrypto-ng, 30 - tricrypto-ng, 4 - llamma @@ -272,7 +273,7 @@ def get_dy( else: # twocrypto-ng, tricrypto-ng amount = CryptoNgPool(swap).calc_withdraw_one_coin(amount, params[1]) elif params[2] == 8: - # USDT <-> gUSDT rate is 1:1 + # ETH <--> WETH rate is 1:1 pass else: raise "Bad swap type" @@ -311,7 +312,7 @@ def get_dx( 5. -- legacy -- 6. for LP token -> coin "exchange" (actually `remove_liquidity_one_coin`) 7. -- legacy -- - 8. for USDT <-> gUSDT + 8. for ETH <-> WETH pool_type: 10 - stable-ng, 20 - twocrypto-ng, 30 - tricrypto-ng, 4 - llamma @@ -363,7 +364,7 @@ def get_dx( amounts[params[1]] = amount amount = TriCryptoNgPool(swap).calc_token_amount(amounts, False) elif params[2] == 8: - # USDT <-> gUSDT rate is 1:1 + # ETH <--> WETH rate is 1:1 pass else: raise "Bad swap type" From 58ea3754be1d2822c430cee9853af9bb73951f38 Mon Sep 17 00:00:00 2001 From: Oleg <40476427+amfet42@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:36:30 +0100 Subject: [PATCH 4/4] add changes to deployment --- abi/helpers/router/router_v_111.json | 211 +++++++++++++++++++++++ contracts/helpers/router/router_v_111.vy | 5 +- scripts/deploy/helpers/router.py | 1 + settings/models.py | 10 +- 4 files changed, 222 insertions(+), 5 deletions(-) create mode 100644 abi/helpers/router/router_v_111.json diff --git a/abi/helpers/router/router_v_111.json b/abi/helpers/router/router_v_111.json new file mode 100644 index 0000000..dc4b71f --- /dev/null +++ b/abi/helpers/router/router_v_111.json @@ -0,0 +1,211 @@ +[ + { + "name": "Exchange", + "inputs": [ + { + "name": "sender", + "type": "address", + "indexed": true + }, + { + "name": "receiver", + "type": "address", + "indexed": true + }, + { + "name": "route", + "type": "address[11]", + "indexed": false + }, + { + "name": "swap_params", + "type": "uint256[4][5]", + "indexed": false + }, + { + "name": "in_amount", + "type": "uint256", + "indexed": false + }, + { + "name": "out_amount", + "type": "uint256", + "indexed": false + } + ], + "anonymous": false, + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "stateMutability": "nonpayable", + "type": "constructor", + "inputs": [ + { + "name": "_weth", + "type": "address" + }, + { + "name": "_eth", + "type": "address" + } + ], + "outputs": [] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "exchange", + "inputs": [ + { + "name": "_route", + "type": "address[11]" + }, + { + "name": "_swap_params", + "type": "uint256[4][5]" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_min_dy", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "payable", + "type": "function", + "name": "exchange", + "inputs": [ + { + "name": "_route", + "type": "address[11]" + }, + { + "name": "_swap_params", + "type": "uint256[4][5]" + }, + { + "name": "_amount", + "type": "uint256" + }, + { + "name": "_min_dy", + "type": "uint256" + }, + { + "name": "_receiver", + "type": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_dy", + "inputs": [ + { + "name": "_route", + "type": "address[11]" + }, + { + "name": "_swap_params", + "type": "uint256[4][5]" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_dx", + "inputs": [ + { + "name": "_route", + "type": "address[11]" + }, + { + "name": "_swap_params", + "type": "uint256[4][5]" + }, + { + "name": "_out_amount", + "type": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "get_dx", + "inputs": [ + { + "name": "_route", + "type": "address[11]" + }, + { + "name": "_swap_params", + "type": "uint256[4][5]" + }, + { + "name": "_out_amount", + "type": "uint256" + }, + { + "name": "_base_pools", + "type": "address[5]" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256" + } + ] + }, + { + "stateMutability": "view", + "type": "function", + "name": "version", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "string" + } + ] + } +] diff --git a/contracts/helpers/router/router_v_111.vy b/contracts/helpers/router/router_v_111.vy index 975a93e..02062c1 100644 --- a/contracts/helpers/router/router_v_111.vy +++ b/contracts/helpers/router/router_v_111.vy @@ -56,9 +56,6 @@ event Exchange: in_amount: uint256 out_amount: uint256 - -DEFAULT_ETH_ADDRESS: constant(address) = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE - ETH_ADDRESS: immutable(address) WETH_ADDRESS: immutable(address) @@ -72,7 +69,7 @@ def __default__(): @external -def __init__(_weth: address, _eth: address = DEFAULT_ETH_ADDRESS): +def __init__(_weth: address, _eth: address): WETH_ADDRESS = _weth ETH_ADDRESS = _eth diff --git a/scripts/deploy/helpers/router.py b/scripts/deploy/helpers/router.py index 9eb47ac..9fb5bde 100644 --- a/scripts/deploy/helpers/router.py +++ b/scripts/deploy/helpers/router.py @@ -10,4 +10,5 @@ def deploy_router(chain_settings: ChainConfig): chain_settings, Path(BASE_DIR, "contracts", "helpers", "router"), chain_settings.wrapped_native_token, + chain_settings.native_token, ) diff --git a/settings/models.py b/settings/models.py index d118b03..3112a6a 100644 --- a/settings/models.py +++ b/settings/models.py @@ -1,7 +1,7 @@ from enum import StrEnum from pathlib import Path -from pydantic import BaseModel +from pydantic import BaseModel, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict BASE_DIR = Path(__file__).resolve().parent.parent @@ -52,7 +52,9 @@ class ChainConfig(BaseSettings): layer: int rollup_type: RollupType evm_version: str = "shanghai" + native_token: str | None = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" wrapped_native_token: str + wrapper: str | None = None dao: CurveDAOSettings | None = None explorer_base_url: str logo_url: str @@ -62,3 +64,9 @@ class ChainConfig(BaseSettings): public_rpc_url: str multicall2: str | None = None multicall3: str = "0xcA11bde05977b3631167028862bE2a173976CA11" + + @field_validator("wrapper", mode="after") + def default_wrapper(cls, v, info): + if v is None: + return info.data["wrapped_native_token"] + return v