From f883873ff06f6c2082740ff4fb719145320f9266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stas=20SU=C8=98COV?= Date: Fri, 20 Dec 2024 19:21:17 +0000 Subject: [PATCH 1/4] chore: switched to `eth-ape` --- .github/workflows/ci.yml | 2 +- .gitignore | 2 +- Dockerfile | 22 ++-- ape-config.yaml | 35 ++++++ brownie-config.yaml | 13 --- env.base => deployments/base.env | 0 env.fraxtal => deployments/fraxtal.env | 0 env.lisk => deployments/lisk.env | 0 env.metall2 => deployments/metall2.env | 0 env.mode => deployments/mode.env | 0 env.optimism => deployments/optimism.env | 0 readme.md | 8 +- requirements.txt | 8 +- scripts/deploy.py | 84 +++++++------- tests/test_factory_registry.py | 8 +- tests/test_lp_sugar.py | 141 +++++++---------------- tests/test_relay_sugar.py | 31 ++--- tests/test_rewards_sugar.py | 100 ++++------------ tests/test_ve_sugar.py | 55 +++------ 19 files changed, 190 insertions(+), 319 deletions(-) create mode 100644 ape-config.yaml delete mode 100644 brownie-config.yaml rename env.base => deployments/base.env (100%) rename env.fraxtal => deployments/fraxtal.env (100%) rename env.lisk => deployments/lisk.env (100%) rename env.metall2 => deployments/metall2.env (100%) rename env.mode => deployments/mode.env (100%) rename env.optimism => deployments/optimism.env (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3beb8fe..1d17fa3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,4 +27,4 @@ jobs: run: docker build ./ -t velodrome/sugar - name: Runs code QA and tests - run: docker run --rm --env-file=env.${{ matrix.chain }} -v $(pwd):/app -w /app -t velodrome/sugar sh -c 'flake8 && brownie test --network=${{ matrix.chain }}-main' + run: docker run --rm --env-file=deployments/${{ matrix.chain }}.env -w /app -t velodrome/sugar sh -c 'python -m black . && ape test --network ${{ matrix.chain }}:mainnet:node --disable-isolation' diff --git a/.gitignore b/.gitignore index 898188a..c1f731d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ __pycache__ .env .history .hypothesis/ -build/ +.build/ reports/ diff --git a/Dockerfile b/Dockerfile index 4aa0f44..ae078ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,23 +1,15 @@ -FROM python:3.10-alpine +FROM ghcr.io/apeworx/ape:latest-slim -RUN apk add --no-cache \ - git npm build-base linux-headers python3-dev tk libc6-compat gcompat cargo - -RUN npm install -g ganache +USER root COPY . /app WORKDIR /app -RUN pip install "cython<3.0.0" && pip install --no-build-isolation pyyaml==5.4.1 RUN pip install -r requirements.txt -RUN brownie networks modify optimism-test host=https://sepolia.optimism.io -RUN brownie networks modify optimism-main host=https://optimism-mainnet.wallet.coinbase.com - -RUN brownie networks modify base-main host=https://mainnet.base.org +# TODO: move this into an ENV or sort of... +RUN python -c 'import vvm; vvm.install_vyper("0.4.0", True)' +RUN ape pm install +RUN ape pm compile -RUN brownie networks add Bob bob-main host=https://rpc.gobob.xyz chainid=60808 -RUN brownie networks add Mode mode-main host=https://mainnet.mode.network chainid=34443 -RUN brownie networks add Lisk lisk-main host=https://lisk.drpc.org chainid=1135 -RUN brownie networks add Fraxtal fraxtal-main host=https://rpc.frax.com chainid=252 -RUN brownie networks add MetalL2 metall2-main host=https://rpc.metall2.com chainid=1750 +ENTRYPOINT [] diff --git a/ape-config.yaml b/ape-config.yaml new file mode 100644 index 0000000..235fdc2 --- /dev/null +++ b/ape-config.yaml @@ -0,0 +1,35 @@ +name: Sugar + +vyper: + evm_version: cancun + +dependencies: + - name: snekmate + github: pcaversaccio/snekmate + version: 0.1.0 + +networks: + custom: + - name: mainnet + ecosystem: mode + chain_id: 34443 + - name: mainnet + ecosystem: lisk + chain_id: 1135 + - name: mainnet + ecosystem: metall2 + chain_id: 1750 + +node: + mode: + mainnet: + uri: https://mode.drpc.org + lisk: + mainnet: + uri: https://lisk.drpc.org + metall2: + mainnet: + uri: https://metall2.drpc.org + optimism: + mainnet: + uri: https://optimism-mainnet.wallet.coinbase.com diff --git a/brownie-config.yaml b/brownie-config.yaml deleted file mode 100644 index 1a6bebb..0000000 --- a/brownie-config.yaml +++ /dev/null @@ -1,13 +0,0 @@ -networks: - default: development - development: - timeout: 120 - cmd_settings: - accounts: 1 - mnemonic: brownie - fork: https://optimism-mainnet.wallet.coinbase.com - evm_version: shanghai -compiler: - evm_version: cancun - vyper: - version: 0.4.0 diff --git a/env.base b/deployments/base.env similarity index 100% rename from env.base rename to deployments/base.env diff --git a/env.fraxtal b/deployments/fraxtal.env similarity index 100% rename from env.fraxtal rename to deployments/fraxtal.env diff --git a/env.lisk b/deployments/lisk.env similarity index 100% rename from env.lisk rename to deployments/lisk.env diff --git a/env.metall2 b/deployments/metall2.env similarity index 100% rename from env.metall2 rename to deployments/metall2.env diff --git a/env.mode b/deployments/mode.env similarity index 100% rename from env.mode rename to deployments/mode.env diff --git a/env.optimism b/deployments/optimism.env similarity index 100% rename from env.optimism rename to deployments/optimism.env diff --git a/readme.md b/readme.md index 47cabec..bab140a 100644 --- a/readme.md +++ b/readme.md @@ -236,21 +236,21 @@ docker build ./ -t velodrome/sugar Next start the container with existing environment variables: ```sh -docker run --env-file=env.{{chain}} --rm -v $(pwd):/app -w /app -it velodrome/sugar sh +docker run --env-file=deployments/{{chain}}.env --rm -v $(pwd):/app -w /app -it velodrome/sugar sh ``` The environment has Brownie and Vyper already installed. To run the tests inside the container, use: ```sh -brownie test --network={{chain}}-test +ape test --network={{chain}}:mainnet:node ``` ## Releases This repository is used to manage the releases for multiple chains. -The latest deployed contracts can be found in the `env.{{chain}}` files in the -root of the repository. +The latest deployed contracts can be found in the `deployments/{{chain}}.env` +files in the root of the repository. ## Why the contracts are not verified? diff --git a/requirements.txt b/requirements.txt index 057e2cd..ef9fc32 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ -eth-brownie @ git+https://github.com/velodrome-finance/brownie@vyper-v0.4 -vyper==0.4.0 -flake8 +eth-ape +ape-vyper +ape-etherscan + +black diff --git a/scripts/deploy.py b/scripts/deploy.py index 001c3a8..a164416 100644 --- a/scripts/deploy.py +++ b/scripts/deploy.py @@ -1,62 +1,66 @@ # SPDX-License-Identifier: BUSL-1.1 import os -from brownie import ( - accounts, VeSugar, LpSugar, RelaySugar, FactoryRegistry, RewardsSugar -) +from ape import project, accounts def main(): - contract_name = str(os.getenv('CONTRACT')).lower() - chain_id = os.getenv('CHAIN_ID') + contract_name = str(os.getenv("CONTRACT")).lower() + chain_id = os.getenv("CHAIN_ID") + publish = os.getenv("PUBLISH", False) account = None - if os.getenv('PROD'): - account = accounts.load('sugar') + if os.getenv("PROD"): + account = accounts.load("sugar") elif len(accounts) > 0: account = accounts[0] - if 'lp' in contract_name: - LpSugar.deploy( - os.getenv(f'VOTER_{chain_id}'), - os.getenv(f'REGISTRY_{chain_id}'), - os.getenv(f'CONVERTOR_{chain_id}'), - os.getenv(f'SLIPSTREAM_HELPER_{chain_id}'), - os.getenv(f'ALM_FACTORY_{chain_id}'), - {'from': account} + if "lp" in contract_name: + project.LpSugar.deploy( + os.getenv(f"VOTER_{chain_id}"), + os.getenv(f"REGISTRY_{chain_id}"), + os.getenv(f"CONVERTOR_{chain_id}"), + os.getenv(f"SLIPSTREAM_HELPER_{chain_id}"), + os.getenv(f"ALM_FACTORY_{chain_id}"), + sender=account, + publish=publish, ) - elif 'rewards' in contract_name: - RewardsSugar.deploy( - os.getenv(f'VOTER_{chain_id}'), - os.getenv(f'REGISTRY_{chain_id}'), - os.getenv(f'CONVERTOR_{chain_id}'), - {'from': account} + elif "rewards" in contract_name: + project.RewardsSugar.deploy( + os.getenv(f"VOTER_{chain_id}"), + os.getenv(f"REGISTRY_{chain_id}"), + os.getenv(f"CONVERTOR_{chain_id}"), + sender=account, + publish=publish, ) - elif 've' in contract_name: - VeSugar.deploy( - os.getenv(f'VOTER_{chain_id}'), - os.getenv(f'DIST_{chain_id}'), - os.getenv(f'GOVERNOR_{chain_id}'), - {'from': account} + elif "ve" in contract_name: + project.VeSugar.deploy( + os.getenv(f"VOTER_{chain_id}"), + os.getenv(f"DIST_{chain_id}"), + os.getenv(f"GOVERNOR_{chain_id}"), + sender=account, + publish=publish, ) - elif 'relay' in contract_name: - RelaySugar.deploy( - str(os.getenv(f'RELAY_REGISTRY_ADDRESSES_{chain_id}')).split(','), - os.getenv(f'VOTER_{chain_id}'), - {'from': account} + elif "relay" in contract_name: + project.RelaySugar.deploy( + str(os.getenv(f"RELAY_REGISTRY_ADDRESSES_{chain_id}")).split(","), + os.getenv(f"VOTER_{chain_id}"), + sender=account, + publish=publish, ) - elif 'registry' in contract_name: - FactoryRegistry.deploy( - str(os.getenv(f'FACTORIES_{chain_id}')).split(','), - str(os.getenv(f'REWARDS_FACTORIES_{chain_id}')).split(','), - str(os.getenv(f'GAUGE_FACTORIES_{chain_id}')).split(','), - str(os.getenv(f'INIT_HASHES_{chain_id}')).split(','), - {'from': account} + elif "registry" in contract_name: + project.FactoryRegistry.deploy( + str(os.getenv(f"FACTORIES_{chain_id}")).split(","), + str(os.getenv(f"REWARDS_FACTORIES_{chain_id}")).split(","), + str(os.getenv(f"GAUGE_FACTORIES_{chain_id}")).split(","), + str(os.getenv(f"INIT_HASHES_{chain_id}")).split(","), + sender=account, + publish=publish, ) else: - print('Set the `CONTRACT` environment variable to deploy a contract.') + print("Set the `CONTRACT` environment variable to deploy a contract.") diff --git a/tests/test_factory_registry.py b/tests/test_factory_registry.py index 26c6106..c5bf140 100644 --- a/tests/test_factory_registry.py +++ b/tests/test_factory_registry.py @@ -3,14 +3,14 @@ from web3.constants import ADDRESS_ZERO -CHAIN_ID = os.getenv('CHAIN_ID', 34443) +CHAIN_ID = os.getenv("CHAIN_ID", 34443) @pytest.fixture @pytest.mark.skipif(int(CHAIN_ID) in [10, 8453], reason="Only leaf chains") -def factory_registry(FactoryRegistry): +def factory_registry(project): # Deploy the contract using the first test account as the owner - yield FactoryRegistry.at(os.getenv(f'REGISTRY_{CHAIN_ID}')) + yield project.FactoryRegistry.at(os.getenv(f"REGISTRY_{CHAIN_ID}")) @pytest.mark.skipif(int(CHAIN_ID) in [10, 8453], reason="Only leaf chains") @@ -34,4 +34,4 @@ def test_initHashToPoolFactory(factory_registry): factories = factory_registry.poolFactories() result = factory_registry.initHashToPoolFactory(factories[0]) - assert str(result) in os.getenv(f'INIT_HASHES_{CHAIN_ID}') + assert result.hex() in os.getenv(f"INIT_HASHES_{CHAIN_ID}") diff --git a/tests/test_lp_sugar.py b/tests/test_lp_sugar.py index 4a35ff4..67578d7 100644 --- a/tests/test_lp_sugar.py +++ b/tests/test_lp_sugar.py @@ -1,54 +1,21 @@ # SPDX-License-Identifier: BUSL-1.1 import os import pytest -from collections import namedtuple from web3.constants import ADDRESS_ZERO -CHAIN_ID = os.getenv('CHAIN_ID', 10) +CHAIN_ID = os.getenv("CHAIN_ID", 10) @pytest.fixture -def sugar_contract(LpSugar, accounts): +def sugar_contract(project): # Since we depend on the rest of the protocol, # we just point to an existing deployment - yield LpSugar.at(os.getenv(f'LP_SUGAR_ADDRESS_{CHAIN_ID}')) + yield project.LpSugar.at(os.getenv(f"LP_SUGAR_ADDRESS_{CHAIN_ID}")) -@pytest.fixture -def TokenStruct(sugar_contract): - method_output = sugar_contract.tokens.abi['outputs'][0] - members = list(map(lambda _e: _e['name'], method_output['components'])) - - yield namedtuple('TokenStruct', members) - - -@pytest.fixture -def LpStruct(sugar_contract): - method_output = sugar_contract.byIndex.abi['outputs'][0] - members = list(map(lambda _e: _e['name'], method_output['components'])) - - yield namedtuple('LpStruct', members) - - -@pytest.fixture -def SwapLpStruct(sugar_contract): - method_output = sugar_contract.forSwaps.abi['outputs'][0] - members = list(map(lambda _e: _e['name'], method_output['components'])) - - yield namedtuple('SwapLpStruct', members) - - -@pytest.fixture -def PositionStruct(sugar_contract): - method_output = sugar_contract.positionsByFactory.abi['outputs'][0] - members = list(map(lambda _e: _e['name'], method_output['components'])) - - yield namedtuple('PositionStruct', members) - - -def test_byIndex(sugar_contract, LpStruct): - lp = LpStruct(*sugar_contract.byIndex(0)) +def test_byIndex(sugar_contract): + lp = sugar_contract.byIndex(0) assert lp is not None assert len(lp) == 28 @@ -56,13 +23,10 @@ def test_byIndex(sugar_contract, LpStruct): assert lp.gauge != ADDRESS_ZERO -def test_forSwaps(sugar_contract, SwapLpStruct, LpStruct): - first_lp = LpStruct(*sugar_contract.byIndex(0)) - second_lp = LpStruct(*sugar_contract.byIndex(1)) - swap_lps = list(map( - lambda _p: SwapLpStruct(*_p), - sugar_contract.forSwaps(10, 0) - )) +def test_forSwaps(sugar_contract): + first_lp = sugar_contract.byIndex(0) + second_lp = sugar_contract.byIndex(1) + swap_lps = sugar_contract.forSwaps(10, 0) assert swap_lps is not None assert len(swap_lps) > 1 @@ -73,17 +37,14 @@ def test_forSwaps(sugar_contract, SwapLpStruct, LpStruct): assert second_lp.lp in lps -def test_tokens(sugar_contract, TokenStruct, LpStruct): - first_lp = LpStruct(*sugar_contract.byIndex(0)) - tokens = list(map( - lambda _p: TokenStruct(*_p), - sugar_contract.tokens(10, 0, ADDRESS_ZERO, []) - )) +def test_tokens(sugar_contract): + first_lp = sugar_contract.byIndex(0) + tokens = sugar_contract.tokens(10, 0, ADDRESS_ZERO, []) assert tokens is not None assert len(tokens) > 1 - token0, token1 = tokens[0: 2] + token0, token1 = tokens[0:2] assert token0.token_address == first_lp.token0 assert token0.symbol is not None @@ -93,11 +54,8 @@ def test_tokens(sugar_contract, TokenStruct, LpStruct): @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, []) - )) +def test_tokens_long_symbol(sugar_contract): + tokens = sugar_contract.tokens(1, 995, ADDRESS_ZERO, []) assert tokens is not None assert len(tokens) > 1 @@ -105,15 +63,12 @@ def test_tokens_long_symbol(sugar_contract, TokenStruct): token = tokens[0] assert token.symbol is not None - assert token.symbol == '-???-' + assert token.symbol == "-???-" @pytest.mark.skipif(int(CHAIN_ID) not in [8453], reason="Only BASE") -def test_tokens_max_long_symbol(sugar_contract, TokenStruct): - tokens = list(map( - lambda _p: TokenStruct(*_p), - sugar_contract.tokens(1, 2508, ADDRESS_ZERO, []) - )) +def test_tokens_max_long_symbol(sugar_contract): + tokens = sugar_contract.tokens(1, 2508, ADDRESS_ZERO, []) assert tokens is not None assert len(tokens) > 1 @@ -121,15 +76,12 @@ def test_tokens_max_long_symbol(sugar_contract, TokenStruct): token = tokens[0] assert token.symbol is not None - assert token.symbol != '-???-' + 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) - )) +def test_all_long_symbol(sugar_contract): + pools = sugar_contract.all(1, 995) assert pools is not None assert len(pools) == 1 @@ -137,16 +89,13 @@ def test_all_long_symbol(sugar_contract, LpStruct): pool = pools[0] assert pool.symbol is not None - assert pool.symbol == '-???-' + assert pool.symbol == "-???-" -def test_all(sugar_contract, LpStruct): - first_lp = LpStruct(*sugar_contract.byIndex(0)) - second_lp = LpStruct(*sugar_contract.byIndex(1)) - lps = list(map( - lambda _p: LpStruct(*_p), - sugar_contract.all(10, 0) - )) +def test_all(sugar_contract): + first_lp = sugar_contract.byIndex(0) + second_lp = sugar_contract.byIndex(1) + lps = sugar_contract.all(10, 0) assert lps is not None assert len(lps) > 1 @@ -161,7 +110,7 @@ def test_all(sugar_contract, LpStruct): @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_all_pagination(sugar_contract, LpStruct): +def test_all_pagination(sugar_contract): max_lps = sugar_contract.MAX_LPS() for i in range(0, max_lps, max_lps): @@ -171,12 +120,9 @@ def test_all_pagination(sugar_contract, LpStruct): assert len(lps) > max_lps - 1 -def test_all_limit_offset(sugar_contract, LpStruct): - second_lp = LpStruct(*sugar_contract.byIndex(1)) - lps = list(map( - lambda _p: LpStruct(*_p), - sugar_contract.all(1, 1) - )) +def test_all_limit_offset(sugar_contract): + second_lp = sugar_contract.byIndex(1) + lps = sugar_contract.all(1, 1) assert lps is not None assert len(lps) == 1 @@ -187,15 +133,12 @@ def test_all_limit_offset(sugar_contract, LpStruct): assert lp1.lp == second_lp.lp -def test_positions(sugar_contract, PositionStruct): +def test_positions(sugar_contract): limit = 100 offset = 0 - account = os.getenv(f'TEST_ADDRESS_{CHAIN_ID}') + account = os.getenv(f"TEST_ADDRESS_{CHAIN_ID}") - positions = list(map( - lambda _p: PositionStruct(*_p), - sugar_contract.positions(limit, offset, account) - )) + positions = sugar_contract.positions(limit, offset, account) assert positions is not None assert len(positions) > 0 @@ -207,15 +150,12 @@ def test_positions(sugar_contract, PositionStruct): @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_positionsUnstakedConcentrated(sugar_contract, PositionStruct): +def test_positionsUnstakedConcentrated(sugar_contract): limit = 100 offset = 0 - account = os.getenv(f'TEST_ADDRESS_{CHAIN_ID}') + account = os.getenv(f"TEST_ADDRESS_{CHAIN_ID}") - positions = list(map( - lambda _p: PositionStruct(*_p), - sugar_contract.positionsUnstakedConcentrated(limit, offset, account) - )) + positions = sugar_contract.positionsUnstakedConcentrated(limit, offset, account) assert positions is not None assert len(positions) > 0 @@ -227,13 +167,10 @@ def test_positionsUnstakedConcentrated(sugar_contract, PositionStruct): @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_positions_ALM(sugar_contract, PositionStruct): - account = os.getenv(f'TEST_ALM_ADDRESS_{CHAIN_ID}') +def test_positions_ALM(sugar_contract): + account = os.getenv(f"TEST_ALM_ADDRESS_{CHAIN_ID}") - positions = list(map( - lambda _p: PositionStruct(*_p), - sugar_contract.positions(1000, 0, account) - )) + positions = sugar_contract.positions(1000, 0, account) assert positions is not None assert len(positions) > 0 diff --git a/tests/test_relay_sugar.py b/tests/test_relay_sugar.py index a2f9642..f7a5842 100644 --- a/tests/test_relay_sugar.py +++ b/tests/test_relay_sugar.py @@ -2,43 +2,32 @@ import os import pytest -from collections import namedtuple from web3.constants import ADDRESS_ZERO -CHAIN_ID = os.getenv('CHAIN_ID', 10) +CHAIN_ID = os.getenv("CHAIN_ID", 10) @pytest.fixture @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def sugar_contract(RelaySugar, accounts): +def sugar_contract(project, accounts): # Since we depend on the rest of the protocol, # we just point to an existing deployment - yield RelaySugar.at(os.getenv(f'RELAY_SUGAR_ADDRESS_{CHAIN_ID}')) - - -@pytest.fixture -@pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def RelayStruct(sugar_contract): - method_output = sugar_contract.all.abi['outputs'][0] - members = list(map(lambda _e: _e['name'], method_output['components'])) - - yield namedtuple('RelayStruct', members) + yield project.RelaySugar.at(os.getenv(f"RELAY_SUGAR_ADDRESS_{CHAIN_ID}")) @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") def test_initial_state(sugar_contract): - assert sugar_contract.voter() == os.getenv(f'VOTER_{CHAIN_ID}') - assert sugar_contract.registries(0) == \ - os.getenv(f'RELAY_REGISTRY_ADDRESSES_{CHAIN_ID}').split(',')[0] + assert sugar_contract.voter() == os.getenv(f"VOTER_{CHAIN_ID}") + assert ( + sugar_contract.registries(0) + == os.getenv(f"RELAY_REGISTRY_ADDRESSES_{CHAIN_ID}").split(",")[0] + ) assert sugar_contract.ve() is not None assert sugar_contract.token() is not None @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_all(sugar_contract, RelayStruct): - relays = list(map( - lambda _r: RelayStruct(*_r), - sugar_contract.all(ADDRESS_ZERO) - )) +def test_all(sugar_contract): + relays = sugar_contract.all(ADDRESS_ZERO) assert len(relays) > 5 diff --git a/tests/test_rewards_sugar.py b/tests/test_rewards_sugar.py index 669fa37..07e3f0a 100644 --- a/tests/test_rewards_sugar.py +++ b/tests/test_rewards_sugar.py @@ -1,103 +1,49 @@ # SPDX-License-Identifier: BUSL-1.1 import os import pytest -from collections import namedtuple -CHAIN_ID = os.getenv('CHAIN_ID', 10) +CHAIN_ID = os.getenv("CHAIN_ID", 10) @pytest.fixture -def sugar_contract(RewardsSugar, accounts): +def sugar_contract(project, accounts): # Since we depend on the rest of the protocol, # we just point to an existing deployment - yield RewardsSugar.at(os.getenv(f'REWARDS_SUGAR_ADDRESS_{CHAIN_ID}')) + yield project.RewardsSugar.at(os.getenv(f"REWARDS_SUGAR_ADDRESS_{CHAIN_ID}")) @pytest.fixture -def lp_sugar_contract(LpSugar, accounts): +def lp_sugar_contract(project, accounts): # Since we depend on the rest of the protocol, # we just point to an existing deployment - yield LpSugar.at(os.getenv(f'LP_SUGAR_ADDRESS_{CHAIN_ID}')) - - -@pytest.fixture -def LpStruct(lp_sugar_contract): - method_output = lp_sugar_contract.byIndex.abi['outputs'][0] - members = list(map(lambda _e: _e['name'], method_output['components'])) - - yield namedtuple('LpStruct', members) - - -@pytest.fixture -def LpEpochStruct(sugar_contract): - method_output = sugar_contract.epochsByAddress.abi['outputs'][0] - members = list(map(lambda _e: _e['name'], method_output['components'])) - - yield namedtuple('LpEpochStruct', members) - - -@pytest.fixture -def LpEpochBribeStruct(sugar_contract): - lp_epoch_comp = sugar_contract.epochsByAddress.abi['outputs'][0] - pe_bribe_comp = lp_epoch_comp['components'][4] - members = list(map(lambda _e: _e['name'], pe_bribe_comp['components'])) - - yield namedtuple('LpEpochBribeStruct', members) + yield project.LpSugar.at(os.getenv(f"LP_SUGAR_ADDRESS_{CHAIN_ID}")) @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_epochsByAddress_limit_offset( - sugar_contract, - lp_sugar_contract, - LpStruct, - LpEpochStruct, - LpEpochBribeStruct -): - first_lp = LpStruct(*lp_sugar_contract.byIndex(1)) - lp_epochs = list(map( - lambda _p: LpEpochStruct(*_p), - sugar_contract.epochsByAddress(20, 3, first_lp.lp) - )) +def test_epochsByAddress_limit_offset(sugar_contract, lp_sugar_contract): + first_lp = lp_sugar_contract.byIndex(1) + lp_epochs = sugar_contract.epochsByAddress(20, 3, first_lp.lp) assert lp_epochs is not None assert len(lp_epochs) > 10 epoch = lp_epochs[1] - epoch_bribes = list(map( - lambda _b: LpEpochBribeStruct(*_b), - epoch.bribes - )) - epoch_fees = list(map( - lambda _f: LpEpochBribeStruct(*_f), - epoch.fees - )) assert epoch.lp == first_lp.lp assert epoch.votes > 0 assert epoch.emissions > 0 - if len(epoch_bribes) > 0: - assert epoch_bribes[0].amount > 0 - - if len(epoch_fees) > 0: - assert epoch_fees[0].amount > 0 - - -def test_epochsLatest_limit_offset( - sugar_contract, - lp_sugar_contract, - LpStruct, - LpEpochStruct -): - second_lp = LpStruct(*lp_sugar_contract.byIndex(1)) - lp_epoch = list(map( - lambda _p: LpEpochStruct(*_p), - sugar_contract.epochsByAddress(1, 0, second_lp.lp) - )) - latest_epoch = list(map( - lambda _p: LpEpochStruct(*_p), - sugar_contract.epochsLatest(1, 1) - )) + if len(epoch.bribes) > 0: + assert epoch.bribes[0].amount > 0 + + if len(epoch.fees) > 0: + assert epoch.fees[0].amount > 0 + + +def test_epochsLatest_limit_offset(sugar_contract, lp_sugar_contract): + second_lp = lp_sugar_contract.byIndex(1) + lp_epoch = sugar_contract.epochsByAddress(1, 0, second_lp.lp) + latest_epoch = sugar_contract.epochsLatest(1, 1) assert lp_epoch is not None @@ -105,16 +51,16 @@ def test_epochsLatest_limit_offset( if len(latest_epoch) < 1: return - pepoch = LpEpochStruct(*lp_epoch[0]) - lepoch = LpEpochStruct(*latest_epoch[0]) + pepoch = lp_epoch[0] + lepoch = latest_epoch[0] assert lepoch.lp == pepoch.lp assert lepoch.ts == pepoch.ts @pytest.mark.skipif(int(CHAIN_ID) not in [10], reason="Only Optimism") -def test_forRoot(sugar_contract, lp_sugar_contract, LpStruct): - first_lp = LpStruct(*lp_sugar_contract.byIndex(1)) +def test_forRoot(sugar_contract, lp_sugar_contract): + first_lp = lp_sugar_contract.byIndex(1) # Use `lp` instead of `root` for testing addresses = sugar_contract.forRoot(first_lp.lp) diff --git a/tests/test_ve_sugar.py b/tests/test_ve_sugar.py index 1fcb668..e8867d4 100644 --- a/tests/test_ve_sugar.py +++ b/tests/test_ve_sugar.py @@ -2,39 +2,27 @@ import os import pytest -from collections import namedtuple - -CHAIN_ID = os.getenv('CHAIN_ID', 10) +CHAIN_ID = os.getenv("CHAIN_ID", 10) @pytest.fixture @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def sugar_contract(VeSugar, accounts): +def sugar_contract(project): # Since we depend on the rest of the protocol, # we just point to an existing deployment - yield VeSugar.at(os.getenv(f'VE_SUGAR_ADDRESS_{CHAIN_ID}')) - - -@pytest.fixture -@pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def VeNFTStruct(sugar_contract): - method_output = sugar_contract.byId.abi['outputs'][0] - members = list(map(lambda _e: _e['name'], method_output['components'])) - - yield namedtuple('VeNFTStruct', members) + yield project.VeSugar.at(os.getenv(f"VE_SUGAR_ADDRESS_{CHAIN_ID}")) @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") def test_initial_state(sugar_contract): - assert sugar_contract.voter() == os.getenv(f'VOTER_{CHAIN_ID}') - assert sugar_contract.dist() == \ - os.getenv(f'DIST_{CHAIN_ID}') + assert sugar_contract.voter() == os.getenv(f"VOTER_{CHAIN_ID}") + assert sugar_contract.dist() == os.getenv(f"DIST_{CHAIN_ID}") assert sugar_contract.ve() is not None @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_byId(sugar_contract, VeNFTStruct): - venft = VeNFTStruct(*sugar_contract.byId(1)) +def test_byId(sugar_contract): + venft = sugar_contract.byId(1) assert venft is not None assert len(venft) == 14 @@ -43,12 +31,9 @@ def test_byId(sugar_contract, VeNFTStruct): @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_byAccount(sugar_contract, VeNFTStruct): - venft = VeNFTStruct(*sugar_contract.byId(1)) - acc_venft = list(map( - lambda _v: VeNFTStruct(*_v), - sugar_contract.byAccount(venft.account) - )) +def test_byAccount(sugar_contract): + venft = sugar_contract.byId(1) + acc_venft = sugar_contract.byAccount(venft.account) assert venft is not None assert len(venft) == 14 @@ -56,13 +41,10 @@ def test_byAccount(sugar_contract, VeNFTStruct): @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_all(sugar_contract, VeNFTStruct): - first_venft = VeNFTStruct(*sugar_contract.byId(1)) - second_venft = VeNFTStruct(*sugar_contract.byId(2)) - venfts = list(map( - lambda _v: VeNFTStruct(*_v), - sugar_contract.all(30, 0) - )) +def test_all(sugar_contract): + first_venft = sugar_contract.byId(1) + second_venft = sugar_contract.byId(2) + venfts = sugar_contract.all(30, 0) assert venfts is not None assert len(venfts) > 2 @@ -77,12 +59,9 @@ def test_all(sugar_contract, VeNFTStruct): @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") -def test_all_limit_offset(sugar_contract, VeNFTStruct): - second_venft = VeNFTStruct(*sugar_contract.byId(1)) - venfts = list(map( - lambda _v: VeNFTStruct(*_v), - sugar_contract.all(1, 1) - )) +def test_all_limit_offset(sugar_contract): + second_venft = sugar_contract.byId(1) + venfts = sugar_contract.all(1, 1) assert venfts is not None assert len(venfts) == 1 From 5dcf3776eb681dd4dab768afedfccb3f8605f7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stas=20SU=C8=98COV?= Date: Fri, 20 Dec 2024 22:05:50 +0000 Subject: [PATCH 2/4] cleanups. --- contracts/test.vy | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 contracts/test.vy diff --git a/contracts/test.vy b/contracts/test.vy deleted file mode 100644 index 2af39e3..0000000 --- a/contracts/test.vy +++ /dev/null @@ -1,24 +0,0 @@ -# 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 "-???-" From abd33c0ccd013e948e433bb566899784eb1afe10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stas=20SU=C8=98COV?= Date: Fri, 20 Dec 2024 22:51:08 +0000 Subject: [PATCH 3/4] refactor: lp_shared to use snekmate. --- ape-config.yaml | 7 ++++--- contracts/modules/lp_shared.vy | 17 +++-------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/ape-config.yaml b/ape-config.yaml index 235fdc2..2861bf8 100644 --- a/ape-config.yaml +++ b/ape-config.yaml @@ -4,9 +4,10 @@ vyper: evm_version: cancun dependencies: - - name: snekmate - github: pcaversaccio/snekmate - version: 0.1.0 + - pypi: snekmate + config_override: + base_path: src + contracts_folder: snekmate networks: custom: diff --git a/contracts/modules/lp_shared.vy b/contracts/modules/lp_shared.vy index 8e8a2ae..816286d 100644 --- a/contracts/modules/lp_shared.vy +++ b/contracts/modules/lp_shared.vy @@ -4,6 +4,8 @@ # @title Velodrome Finance LP Module # @author Velodrome Finance +from snekmate.utils import create2_address + MAX_FACTORIES: constant(uint256) = 10 MAX_POOLS: constant(uint256) = 2000 MAX_ITERATIONS: constant(uint256) = 8000 @@ -202,17 +204,4 @@ def _root_lp_address( ) ) - - data: bytes32 = keccak256( - concat( - 0xFF, - convert(_factory, bytes20), - salt, - init_hash - ) - ) - - return convert( - convert(data, uint256) & convert(max_value(uint160), uint256), - address - ) + return create2_address._compute_address(salt, init_hash, _factory) From 0c319552c9d70f01c5c39c4592769772e4982260 Mon Sep 17 00:00:00 2001 From: Stas Date: Tue, 7 Jan 2025 20:41:09 +0100 Subject: [PATCH 4/4] ALM v2 (#104) * LpSugar: add ALM estimation helper. * LpSugar: alm v2. * env: alm v2 releases. * LpSugar: account for ALM temper strategy. * tests: assert the root pool address. * env: new LpSugar release. --- contracts/LpSugar.vy | 85 +++++++++++++++++++++++++++++++--------- deployments/base.env | 4 +- deployments/mode.env | 2 +- deployments/optimism.env | 4 +- tests/test_lp_sugar.py | 5 +++ 5 files changed, 76 insertions(+), 24 deletions(-) diff --git a/contracts/LpSugar.vy b/contracts/LpSugar.vy index 9df5eca..aca9375 100644 --- a/contracts/LpSugar.vy +++ b/contracts/LpSugar.vy @@ -6,6 +6,7 @@ # @notice Makes it nicer to work with the liquidity pools. from modules import lp_shared +from snekmate.utils import math initializes: lp_shared @@ -16,6 +17,9 @@ MAX_LPS: public(constant(uint256)) = 500 MAX_POSITIONS: public(constant(uint256)) = 200 MAX_TOKEN_SYMBOL_LEN: public(constant(uint256)) = 32 +ALM_SCALE: constant(uint256) = as_wei_value(1000, "ether") +MAX_UINT: constant(uint256) = max_value(uint256) + # Slot0 from CLPool.sol struct Slot: sqrtPriceX96: uint160 @@ -121,7 +125,7 @@ struct Lp: root: address # See: -# https://github.com/mellow-finance/mellow-alm-toolkit/blob/main/src/interfaces/ICore.sol#L12-L60 +# https://github.com/mellow-finance/mellow-alm-toolkit/blob/main/src/interfaces/ICore.sol#L71-L120 struct AlmManagedPositionInfo: slippageD9: uint32 property: uint24 @@ -195,15 +199,15 @@ interface ISlipstreamHelper: def poolFees(_pool: address, _liquidity: uint128, _current_tick: int24, _lower_tick: int24, _upper_tick: int24) -> Amounts: view interface IAlmFactory: - def poolToAddresses(pool: address) -> address[2]: view - def getImmutableParams() -> address[5]: view + def poolToWrapper(pool: address) -> address: view + def core() -> address: view interface IAlmCore: def managedPositionAt(_id: uint256) -> AlmManagedPositionInfo: view interface IAlmLpWrapper: def positionId() -> uint256: view - def totalSupply() -> uint256: view + def previewMint(scale: uint256) -> uint256[2]: view # Vars cl_helper: public(ISlipstreamHelper) @@ -557,7 +561,7 @@ def _positions( alm_core: IAlmCore = empty(IAlmCore) if self.alm_factory != empty(IAlmFactory): - alm_core = IAlmCore((staticcall self.alm_factory.getImmutableParams())[0]) + alm_core = IAlmCore(staticcall self.alm_factory.core()) for index: uint256 in range(0, lp_shared.MAX_FACTORIES): if index >= factories_count: @@ -669,11 +673,11 @@ def _positions( if self.alm_factory == empty(IAlmFactory): continue - alm_addresses: address[2] = staticcall self.alm_factory.poolToAddresses(pool_addr) - alm_staking: IGauge = IGauge(alm_addresses[0]) - alm_vault: IAlmLpWrapper = IAlmLpWrapper(alm_addresses[1]) + alm_staking: IGauge = IGauge( + staticcall self.alm_factory.poolToWrapper(pool_addr) + ) - if alm_vault.address == empty(address): + if alm_staking.address == empty(address): continue alm_user_liq: uint256 = staticcall alm_staking.balanceOf(_account) @@ -682,7 +686,7 @@ def _positions( continue alm_pos: AlmManagedPositionInfo = staticcall alm_core.managedPositionAt( - staticcall alm_vault.positionId() + staticcall IAlmLpWrapper(alm_staking.address).positionId() ) if gauge.address != empty(address) and len(alm_pos.ammPositionIds) > 0: @@ -700,22 +704,38 @@ def _positions( nfpm.address ) - alm_liq: uint256 = staticcall alm_vault.totalSupply() + # For the Temper strategy we might have a second position to add up + if len(alm_pos.ammPositionIds) > 1: + pos2: Position = self._cl_position( + alm_pos.ammPositionIds[1], + # Account is the ALM Core contract here... + alm_core.address, + pool_addr, + gauge.address if staked else empty(address), + factory.address, + nfpm.address + ) + pos.amount0 += pos2.amount0 + pos.amount1 += pos2.amount1 + pos.staked0 += pos2.staked0 + pos.staked1 += pos2.staked1 + + alm_liq: uint256 = staticcall alm_staking.totalSupply() # adjust user share of the vault... pos.amount0 = (alm_user_liq * pos.amount0) // alm_liq pos.amount1 = (alm_user_liq * pos.amount1) // alm_liq pos.staked0 = (alm_user_liq * pos.staked0) // alm_liq pos.staked1 = (alm_user_liq * pos.staked1) // alm_liq - pos.emissions_earned = staticcall alm_staking.earned(_account) # ignore dust as the rebalancing might report "fees" pos.unstaked_earned0 = 0 pos.unstaked_earned1 = 0 - pos.liquidity = (alm_user_liq * pos.liquidity) // alm_liq - pos.staked = (alm_user_liq * pos.staked) // alm_liq - - pos.alm = alm_vault.address + pos.emissions_earned = staticcall alm_staking.earned(_account) + # ALM liquidity is fully staked + pos.liquidity = 0 + pos.staked = alm_user_liq + pos.alm = alm_staking.address if len(positions) < MAX_POSITIONS: positions.append(pos) @@ -980,9 +1000,9 @@ def _cl_lp(_data: address[4], _token0: address, _token1: address) -> Lp: if gauge_alive and staticcall gauge.periodFinish() > block.timestamp: emissions = staticcall gauge.rewardRate() - alm_addresses: address[2] = [empty(address), empty(address)] + alm_wrapper: address = empty(address) if self.alm_factory != empty(IAlmFactory): - alm_addresses = staticcall self.alm_factory.poolToAddresses(pool.address) + alm_wrapper = staticcall self.alm_factory.poolToWrapper(pool.address) return Lp({ lp: pool.address, @@ -1019,7 +1039,7 @@ def _cl_lp(_data: address[4], _token0: address, _token1: address) -> Lp: token1_fees: token1_fees, nfpm: _data[3], - alm: alm_addresses[1], + alm: alm_wrapper, root: lp_shared._root_lp_address(_data[0], _token0, _token1, tick_spacing), }) @@ -1121,3 +1141,30 @@ def _has_userPositions(_nfpm: address) -> bool: )[1] return len(response) > 0 + + +@external +@view +def almEstimateAmounts( + _wrapper: address, + _amount0: uint256, + _amount1: uint256 +) -> uint256[3]: + """ + @notice Estimates the ALM amounts and LP tokens for a deposit + @param _wrapper The LP Wrapper contract + @param _amount0 First token amount + @param _amount1 Second token amount + @return Returns an array of tokens and LP amounts + """ + targets: uint256[2] = staticcall IAlmLpWrapper(_wrapper).previewMint(ALM_SCALE) + + lp_amount: uint256 = min( + MAX_UINT if (targets[0] == 0) else math._mul_div(_amount0, ALM_SCALE, targets[0], False), + MAX_UINT if (targets[1] == 0) else math._mul_div(_amount1, ALM_SCALE, targets[1], False) + ) + + max0: uint256 = 0 if (targets[0] == 0) else math._mul_div(targets[0], lp_amount, ALM_SCALE, True) + max1: uint256 = 0 if (targets[1] == 0) else math._mul_div(targets[1], lp_amount, ALM_SCALE, True) + + return [max0, max1, lp_amount] diff --git a/deployments/base.env b/deployments/base.env index 5b1c8d7..e35134d 100644 --- a/deployments/base.env +++ b/deployments/base.env @@ -5,7 +5,7 @@ VOTER_8453=0x16613524e02ad97eDfeF371bC883F2F5d6C480A5 REGISTRY_8453=0x5C3F18F06CC09CA1910767A34a20F771039E37C0 CONVERTOR_8453=0x1111111111111111111111111111111111111111 SLIPSTREAM_HELPER_8453=0x9c62ab10577fB3C20A22E231b7703Ed6D456CC7a -ALM_FACTORY_8453=0x5B1b1aaC71bDca9Ed1dCb2AA357f678584db4029 +ALM_FACTORY_8453=0xb4eA34bDD77D75b97dF6b07DA0b2A3021B6D2227 DIST_8453=0x227f65131A261548b057215bB1D5Ab2997964C7d RELAY_REGISTRY_ADDRESSES_8453=0x05e41604B9463e2224227053980dfF3f57fb6dB5,0xD308aBCe663302d3b86b36d332CEFd8A4F62C5Ed GOVERNOR_8453=0x94C012A23A8A65A6f40608dA30534a46a433F410 @@ -14,7 +14,7 @@ TEST_FACTORY_ADDRESS_8453=0x5e7BB104d84c7CB9B682AaC2F3d509f5F406809A TEST_ADDRESS_8453=0x892Ff98a46e5bd141E2D12618f4B2Fe6284debac TEST_ALM_ADDRESS_8453=0x892Ff98a46e5bd141E2D12618f4B2Fe6284debac -LP_SUGAR_ADDRESS_8453=0xF117AFbE285f867c67960F4bfEDe3121Cf6d8F76 +LP_SUGAR_ADDRESS_8453=0x8D1eaAFe47D6b2d560d69Ff44A7e0D48980ab69b REWARDS_SUGAR_ADDRESS_8453=0xA44600F4DBA6683d8BD99270B1A6a143fB9F1C3B VE_SUGAR_ADDRESS_8453=0x4c5d3925fe65DFeB5A079485136e4De09cb664A5 RELAY_SUGAR_ADDRESS_8453=0x8932B5FE23C07Df06533F8f09E43e7cca6a24143 diff --git a/deployments/mode.env b/deployments/mode.env index 9ba31c0..69ac642 100644 --- a/deployments/mode.env +++ b/deployments/mode.env @@ -12,7 +12,7 @@ CONVERTOR_34443=0x1111111111111111111111111111111111111111 SLIPSTREAM_HELPER_34443=0xD24a61656AB0d70994Ef5F42fE11AA95c0a1d329 ALM_FACTORY_34443=0x0000000000000000000000000000000000000000 -LP_SUGAR_ADDRESS_34443=0x8A5e97184E8850064805fAc2427ce7728689De5B +LP_SUGAR_ADDRESS_34443=0x7f23Fa4D139CCD5b7a4e4EFfFe1d2e61b33BCD21 REWARDS_SUGAR_ADDRESS_34443=0xD5d3ABAcB8CF075636792658EE0be8B03AF517B8 TEST_ADDRESS_34443=0x892ff98a46e5bd141e2d12618f4b2fe6284debac diff --git a/deployments/optimism.env b/deployments/optimism.env index ce11781..4a42cfa 100644 --- a/deployments/optimism.env +++ b/deployments/optimism.env @@ -5,7 +5,7 @@ VOTER_10=0x41C914ee0c7E1A5edCD0295623e6dC557B5aBf3C REGISTRY_10=0xF4c67CdEAaB8360370F41514d06e32CcD8aA1d7B CONVERTOR_10=0x585Af0b397AC42dbeF7f18395426BF878634f18D SLIPSTREAM_HELPER_10=0xD45624bf2CB9f65ecbdF3067d21992b099b56202 -ALM_FACTORY_10=0xeD8b81E3fF6c54951621715F5992CA52007D88bA +ALM_FACTORY_10=0xb4eA34bDD77D75b97dF6b07DA0b2A3021B6D2227 DIST_10=0x9D4736EC60715e71aFe72973f7885DCBC21EA99b RELAY_REGISTRY_ADDRESSES_10=0xe9F00f2e61CB0c6fb00A2e457546aCbF0fC303C2,0x6b1253B116B5919932399295C75116d33F8EfF96 GOVERNOR_10=0x1F82e10D58aEf03DeA2e478029fB0387A1cbE989 @@ -13,7 +13,7 @@ GOVERNOR_10=0x1F82e10D58aEf03DeA2e478029fB0387A1cbE989 TEST_ADDRESS_10=0x892ff98a46e5bd141e2d12618f4b2fe6284debac TEST_ALM_ADDRESS_10=0x892ff98a46e5bd141e2d12618f4b2fe6284debac -LP_SUGAR_ADDRESS_10=0x191FF5F5EAC07b082ff877236E8e7537c2d9D2A7 +LP_SUGAR_ADDRESS_10=0x7F6D3A4c8a1111DDbFe282794f4D608aB7Cb23A2 REWARDS_SUGAR_ADDRESS_10=0x62CCFB2496f49A80B0184AD720379B529E9152fB VE_SUGAR_ADDRESS_10=0x94f913362b232e31daB49a1aFB775cfd25DaA6a1 RELAY_SUGAR_ADDRESS_10=0xb8307e5842B9aeE75C704183F0355076aa74b4e2 diff --git a/tests/test_lp_sugar.py b/tests/test_lp_sugar.py index 67578d7..60d454c 100644 --- a/tests/test_lp_sugar.py +++ b/tests/test_lp_sugar.py @@ -108,6 +108,11 @@ def test_all(sugar_contract): assert lp2.lp == second_lp.lp assert lp2.gauge == second_lp.gauge + # check we calculate the root pool address + if int(CHAIN_ID) == 34443: + assert lp1.root == "0x2bb4CFF1FE3F56599b4D409B2498B96D3E3f6665" + assert lp2.root == "0x48a3Ed8552483ed31Fd87ECa1a7b2F94aa1Cc394" + @pytest.mark.skipif(int(CHAIN_ID) not in [10, 8453], reason="Only root chains") def test_all_pagination(sugar_contract):