From ffc81d45842f21a7341e5be897514fe38a4f33dc Mon Sep 17 00:00:00 2001 From: Zoomer <87513793+ethzoomer@users.noreply.github.com> Date: Sat, 14 Dec 2024 09:15:02 -0600 Subject: [PATCH] LpSugar: _safe_symbol handles large string values (#95) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: symbol string size * feat: truncate symbols * simplify string conversion. * LpSugar: refactor `_safe_symbol()` * LpSugar: cleanups and tests. * env: base release * env: new optimism lp sugar release. --------- Co-authored-by: Stas SUȘCOV --- contracts/LpSugar.vy | 53 ++++++++++++++++-------------------------- contracts/test.vy | 24 +++++++++++++++++++ env.base | 4 ++-- env.optimism | 2 +- readme.md | 2 +- tests/test_lp_sugar.py | 32 +++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 37 deletions(-) create mode 100644 contracts/test.vy diff --git a/contracts/LpSugar.vy b/contracts/LpSugar.vy index 776c43a..f49bf73 100644 --- a/contracts/LpSugar.vy +++ b/contracts/LpSugar.vy @@ -68,7 +68,7 @@ struct Position: struct Token: token_address: address - symbol: String[100] + symbol: String[30] decimals: uint8 account_balance: uint256 listed: bool @@ -83,7 +83,7 @@ struct SwapLp: struct Lp: lp: address - symbol: String[100] + symbol: String[30] decimals: uint8 liquidity: uint256 @@ -131,11 +131,6 @@ struct AlmManagedPositionInfo: # Our contracts / Interfaces -interface IERC20: - def decimals() -> uint8: view - def symbol() -> String[100]: view - def balanceOf(_account: address) -> uint256: view - interface IPool: def token0() -> address: view def token1() -> address: view @@ -148,7 +143,6 @@ interface IPool: def index0() -> uint256: view def index1() -> uint256: view def totalSupply() -> uint256: view - def symbol() -> String[100]: view def decimals() -> uint8: view def stable() -> bool: view def balanceOf(_account: address) -> uint256: view @@ -348,7 +342,6 @@ def tokens(_limit: uint256, _offset: uint256, _account: address, \ @internal @view def _token(_address: address, _account: address) -> Token: - token: IERC20 = IERC20(_address) bal: uint256 = empty(uint256) if _account != empty(address): @@ -433,8 +426,6 @@ def _v2_lp(_data: address[4], _token0: address, _token1: address) -> Lp: is_stable: bool = staticcall pool.stable() pool_fee: uint256 = staticcall lp_shared.IPoolFactory(_data[0]).getFee(pool.address, is_stable) pool_fees: address = staticcall pool.poolFees() - token0: IERC20 = IERC20(_token0) - token1: IERC20 = IERC20(_token1) token0_fees: uint256 = self._safe_balance_of(_token0, pool_fees) token1_fees: uint256 = self._safe_balance_of(_token1, pool_fees) gauge_alive: bool = staticcall lp_shared.voter.isAlive(gauge.address) @@ -465,7 +456,7 @@ def _v2_lp(_data: address[4], _token0: address, _token1: address) -> Lp: return Lp({ lp: _data[1], - symbol: staticcall pool.symbol(), + symbol: self._safe_symbol(pool.address), decimals: decimals, liquidity: pool_liquidity, @@ -473,11 +464,11 @@ def _v2_lp(_data: address[4], _token0: address, _token1: address) -> Lp: tick: 0, sqrt_ratio: 0, - token0: token0.address, + token0: _token0, reserve0: reserve0, staked0: staked0, - token1: token1.address, + token1: _token1, reserve1: reserve1, staked1: staked1, @@ -500,9 +491,7 @@ def _v2_lp(_data: address[4], _token0: address, _token1: address) -> Lp: nfpm: empty(address), alm: empty(address), - root: lp_shared._root_lp_address( - _data[0], token0.address, token1.address, type - ) + root: lp_shared._root_lp_address(_data[0], _token0, _token1, type) }) @external @@ -958,8 +947,6 @@ def _cl_lp(_data: address[4], _token0: address, _token1: address) -> Lp: fee_voting_reward: address = empty(address) emissions: uint256 = 0 emissions_token: address = empty(address) - token0: IERC20 = IERC20(_token0) - token1: IERC20 = IERC20(_token1) staked0: uint256 = 0 staked1: uint256 = 0 tick_spacing: int24 = staticcall pool.tickSpacing() @@ -1006,11 +993,11 @@ def _cl_lp(_data: address[4], _token0: address, _token1: address) -> Lp: tick: slot.tick, sqrt_ratio: slot.sqrtPriceX96, - token0: token0.address, + token0: _token0, reserve0: self._safe_balance_of(_token0, pool.address), staked0: staked0, - token1: token1.address, + token1: _token1, reserve1: self._safe_balance_of(_token1, pool.address), staked1: staked1, @@ -1033,9 +1020,7 @@ def _cl_lp(_data: address[4], _token0: address, _token1: address) -> Lp: nfpm: _data[3], alm: alm_addresses[1], - root: lp_shared._root_lp_address( - _data[0], token0.address, token1.address, tick_spacing - ), + root: lp_shared._root_lp_address(_data[0], _token0, _token1, tick_spacing), }) @internal @@ -1088,28 +1073,30 @@ def _safe_decimals(_token: address) -> uint8: @internal @view -def _safe_symbol(_token: address) -> String[100]: +def _safe_symbol(_token: address) -> String[30]: """ - @notice Returns the `ERC20.symbol()` result safely + @notice Returns the `ERC20.symbol()` safely (max 30 chars) @param _token The token to call """ - # >=192 input size is required by Vyper's _abi_decode() - response: Bytes[192] = b"" - response = raw_call( + response: Bytes[100] = raw_call( _token, method_id("symbol()"), - max_outsize=192, + # Min bytes to use abi_decode() + max_outsize=100, gas=50000, is_delegate_call=False, is_static_call=True, revert_on_failure=False )[1] + resp_len: uint256 = len(response) + # Check response as revert_on_failure is set to False - if len(response) > 0: - return abi_decode(response, String[100]) + # And that the symbol size is not more than 10 chars (96 bytes) + if resp_len > 0 and resp_len <= 96: + return abi_decode(response, String[30]) - return "-NA-" + return "-???-" @internal @view diff --git a/contracts/test.vy b/contracts/test.vy new file mode 100644 index 0000000..2af39e3 --- /dev/null +++ b/contracts/test.vy @@ -0,0 +1,24 @@ +# To test on OP Mainnet, pass these token addresses: +# * 0x4200000000000000000000000000000000000006 (weth) +# * 0x045D841ba37E180bC9b9D4da718E14b9ED7925d6 (garbaged unicode) + +@external +@view +def safe_symbol(_token: address) -> String[10]: + response: Bytes[100] = raw_call( + _token, + method_id("symbol()"), + max_outsize=100, + gas=50000, + is_delegate_call=False, + is_static_call=True, + revert_on_failure=False, + )[1] + + response_len: uint256 = len(response) + + # Min bytes to use abi_decode() + if response_len > 0 and response_len <= 96: + return abi_decode(response, String[10]) + + return "-???-" diff --git a/env.base b/env.base index d20f8bc..f5dfc61 100644 --- a/env.base +++ b/env.base @@ -14,7 +14,7 @@ TEST_FACTORY_ADDRESS_8453=0x5e7BB104d84c7CB9B682AaC2F3d509f5F406809A TEST_ADDRESS_8453=0x892Ff98a46e5bd141E2D12618f4B2Fe6284debac TEST_ALM_ADDRESS_8453=0x892Ff98a46e5bd141E2D12618f4B2Fe6284debac -LP_SUGAR_ADDRESS_8453=0x25546d006fD79b6910Cb30e96a32B5ce296663bb -REWARDS_SUGAR_ADDRESS_8453=0xEbfD2d983340e0bA6109a387928ADAe9FEE47D4b +LP_SUGAR_ADDRESS_8453=0x82dA79111A0C79639B7a4c6dc149283D103f1860 +REWARDS_SUGAR_ADDRESS_8453=0xA44600F4DBA6683d8BD99270B1A6a143fB9F1C3B VE_SUGAR_ADDRESS_8453=0x4c5d3925fe65DFeB5A079485136e4De09cb664A5 RELAY_SUGAR_ADDRESS_8453=0x8932B5FE23C07Df06533F8f09E43e7cca6a24143 diff --git a/env.optimism b/env.optimism index 7649b1a..d38218e 100644 --- a/env.optimism +++ b/env.optimism @@ -13,7 +13,7 @@ GOVERNOR_10=0x1F82e10D58aEf03DeA2e478029fB0387A1cbE989 TEST_ADDRESS_10=0x892ff98a46e5bd141e2d12618f4b2fe6284debac TEST_ALM_ADDRESS_10=0x892ff98a46e5bd141e2d12618f4b2fe6284debac -LP_SUGAR_ADDRESS_10=0x5B3B370C85c8816cBB4BCE5f647dCa3e06c421d7 +LP_SUGAR_ADDRESS_10=0xf01d04b0963a175Ac43D31C515A4397356cF74D8 REWARDS_SUGAR_ADDRESS_10=0x62CCFB2496f49A80B0184AD720379B529E9152fB VE_SUGAR_ADDRESS_10=0x94f913362b232e31daB49a1aFB775cfd25DaA6a1 RELAY_SUGAR_ADDRESS_10=0xb8307e5842B9aeE75C704183F0355076aa74b4e2 diff --git a/readme.md b/readme.md index d529ecb..a9c7992 100644 --- a/readme.md +++ b/readme.md @@ -37,7 +37,7 @@ Below is the list of datasets we support. > [!NOTE] > `LpSugar.vy` is deployed on: -> Optimism - `0x35F233BE126d7D08aB2D65E647E8c379b1FACF39` +> Optimism - `0x54F8968CC76ECB17018E44049FdcC14ff833fa67` > Base - `0x63a73829C74e936C1D2EEbE64164694f16700138` It allows fetching on-chain pools data. diff --git a/tests/test_lp_sugar.py b/tests/test_lp_sugar.py index 71a2cec..ff2fd49 100644 --- a/tests/test_lp_sugar.py +++ b/tests/test_lp_sugar.py @@ -92,6 +92,38 @@ def test_tokens(sugar_contract, TokenStruct, LpStruct): assert token1.token_address == first_lp.token1 +@pytest.mark.skipif(int(CHAIN_ID) not in [10], reason="Only OP") +def test_tokens_long_symbol(sugar_contract, TokenStruct): + tokens = list(map( + lambda _p: TokenStruct(*_p), + sugar_contract.tokens(1, 995, ADDRESS_ZERO, []) + )) + + assert tokens is not None + assert len(tokens) > 1 + + token = tokens[0] + + assert token.symbol is not None + assert token.symbol == '-???-' + + +@pytest.mark.skipif(int(CHAIN_ID) not in [10], reason="Only OP") +def test_all_long_symbol(sugar_contract, LpStruct): + pools = list(map( + lambda _p: LpStruct(*_p), + sugar_contract.all(1, 995) + )) + + assert pools is not None + assert len(pools) == 1 + + pool = pools[0] + + assert pool.symbol is not None + assert pool.symbol == '-???-' + + def test_all(sugar_contract, LpStruct): first_lp = LpStruct(*sugar_contract.byIndex(0)) second_lp = LpStruct(*sugar_contract.byIndex(1))