From 54919601af47db9aa789c0fb18018f782b90780e Mon Sep 17 00:00:00 2001 From: 0xBasic <0xBasic@yearn.finance> Date: Mon, 11 Sep 2023 19:50:16 -0400 Subject: [PATCH] feat: add base network (#609) * add: constants info * add: prices/constants * add: mc3 * add: release registry * add: yearn.py products * add: uni v3 * add: uni v3 2 * add: binary search barrier * add: vaults start date * add: base network * add: base network make * add: base docker-compose * fix: set network envs * add hacky brownie init fix * rebase * fix: batch size * update contant * fix: vaults.py * update: treasury.py * rebase * refactor: yearn.py * bump dank * bump ypm * add: delegated partners contract * add: lens v2 * update: s3 * fix: registry * temp: no curve pool base * delete: no cache * rebase: async update ypm * update: uni v3 quoter for base * fix: typo * add: batch_size and block info to transactions.py * fix: transactions batch end block * update: s3 registry * update s3 registry adapter * increase: batchsize * fix: wallets.py start time * fix: wallets * fix-treasury rebase messed up * fix: tuple error wallets * bump ypm * add: base wrapped gas coin * chore: add debug logger * fix: rebase err * fix: EventLookupError --------- Co-authored-by: BobTheBuidler --- Makefile | 8 ++++++-- brownie_init.sh | 5 +++-- requirements.txt | 3 ++- scripts/exporters/transactions.py | 2 ++ scripts/exporters/vaults.py | 4 ++++ scripts/exporters/wallets.py | 7 ++++++- scripts/s3.py | 2 ++ services/dashboard/docker-compose.yml | 1 + set_network_envs.sh | 7 +++++++ yearn/constants.py | 10 ++++++++++ yearn/middleware/middleware.py | 1 + yearn/multicall2.py | 3 ++- yearn/partners/delegated.py | 1 + yearn/prices/constants.py | 17 +++++++++++++++-- yearn/prices/curve.py | 3 +++ yearn/prices/uniswap/v2.py | 7 +++++++ yearn/prices/uniswap/v3.py | 13 ++++++++++--- yearn/prices/yearn.py | 3 +++ yearn/treasury/treasury.py | 2 ++ yearn/utils.py | 1 + yearn/v2/registry.py | 6 +++++- yearn/yearn.py | 14 ++------------ 22 files changed, 95 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index a2ee55fb2..de4820324 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ endif ####################################### # specify all supported networks here # ####################################### -supported_networks := ethereum fantom arbitrum optimism +supported_networks := ethereum fantom arbitrum optimism base ############################################### # specify all supported exporter scripts here # @@ -65,7 +65,6 @@ down: .PHONY: build build: docker build -t ghcr.io/yearn/yearn-exporter . - logs: $(eval filter = $(if $(filter),yearn-exporter-$(filter),$(if $(network),$(network),yearn-exporter-worker))) $(eval since = $(if $(since),$(since),300s)) @@ -98,6 +97,7 @@ up: make down filter=yearn-exporter-worker-fantom-exporters-treasury-transactions make down filter=yearn-exporter-worker-arbitrum-exporters-treasury-transactions make down filter=yearn-exporter-worker-optimism-exporters-treasury-transactions + make down filter=yearn-exporter-worker-base-exporters-treasury-transactions # LOGGING $(eval with_logs = $(if $(with_logs),$(with_logs),true)) @@ -197,6 +197,10 @@ optimism: gnosis: make up logs network=gnosis +# Base Chain +base: + make up logs network=base + ############################ # Exporter-specifc recipes # ############################ diff --git a/brownie_init.sh b/brownie_init.sh index 175b906e0..981581137 100755 --- a/brownie_init.sh +++ b/brownie_init.sh @@ -3,8 +3,9 @@ set -e BROWNIE_NETWORK=${BROWNIE_NETWORK:-mainnet} # default to Ethereum mainnet EXPLORER=${EXPLORER:-$DEFAULT_EXPLORER} +brownie networks add Base base-main host=https://base.meowrpc.com chainid=8453 explorer=https://api.basescan.org/api || true -# modify the network +# modify the network & add Base network if [[ ! -z "$WEB3_PROVIDER" ]]; then brownie networks modify $BROWNIE_NETWORK host=$WEB3_PROVIDER explorer=$EXPLORER -fi +fi \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 032481b8b..b4eae018f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,8 @@ eth_retry>=0.1.18 eth-hash[pysha3] ez-a-sync==0.6.6 python-telegram-bot==13.15 -git+https://www.github.com/BobTheBuidler/ypricemagic@88ae753ba020f74b4febb1216c83e63a0ed8294e +git+https://www.github.com/banteg/multicall.py@5cdb0f08ddadf1c381dc0966b8bd71e3ac7c6272 +git+https://www.github.com/BobTheBuidler/ypricemagic@2f1895d0efde0802c1ada8a5361701f759273ddd git+https://www.github.com/BobTheBuidler/eth-portfolio@6c71e6200663063c92b7d7f27bbafba7a0b02997 git+https://www.github.com/BobTheBuidler/toolcache@e37b53cec64556d2b35df66767fca4c8f366a2fa memray diff --git a/scripts/exporters/transactions.py b/scripts/exporters/transactions.py index c8e692851..7483eb0db 100644 --- a/scripts/exporters/transactions.py +++ b/scripts/exporters/transactions.py @@ -39,6 +39,7 @@ Network.Gnosis: 2_000_000, Network.Arbitrum: 1_500_000, Network.Optimism: 4_000_000, + Network.Base: 100_000, }[chain.id] FIRST_END_BLOCK = { @@ -47,6 +48,7 @@ Network.Gnosis: 21_440_000, # # NOTE block some arbitrary time after first vault deployment Network.Arbitrum: 4_837_859, Network.Optimism: 18_111_485, + Network.Base: 3_571_971, }[chain.id] def main(): diff --git a/scripts/exporters/vaults.py b/scripts/exporters/vaults.py index f870b7275..a575a4333 100644 --- a/scripts/exporters/vaults.py +++ b/scripts/exporters/vaults.py @@ -38,6 +38,10 @@ # end: 2020-02-12 first iearn deployment start = datetime(2020, 2, 12, 0, 1, tzinfo=timezone.utc) data_query = 'iearn{network="' + Network.label() + '"}' +elif Network(chain.id) == Network.Base: + # end: release registry Aug-29-2023 01:37:43 PM +UTC block 3263458 + start = datetime(2023, 8, 29, tzinfo=timezone.utc) + data_query = 'yearn_vault{network="' + Network.label() + '"}' else: raise NotImplementedError() diff --git a/scripts/exporters/wallets.py b/scripts/exporters/wallets.py index b0998e71d..4fb07b120 100644 --- a/scripts/exporters/wallets.py +++ b/scripts/exporters/wallets.py @@ -24,7 +24,12 @@ # start: 2020-02-12 first iearn deployment # start opti: 2022-01-01 an arbitrary start timestamp because the default start is < block 1 on opti and messes things up -start = datetime(2022, 1, 1, tzinfo=timezone.utc) if chain.id in [Network.Arbitrum, Network.Optimism] else datetime(2020, 2, 12, tzinfo=timezone.utc) +if chain.id in [Network.Arbitrum, Network.Optimism]: + start = datetime(2022, 1, 1, tzinfo=timezone.utc) +elif Network.Base: + start = datetime(2023, 9, 1, tzinfo=timezone.utc) +else: + start = datetime(2020, 2, 12, tzinfo=timezone.utc) @ttl_cache(ttl=60) def get_last_recorded_block(): diff --git a/scripts/s3.py b/scripts/s3.py index 62e87e1b2..d9df9c45d 100644 --- a/scripts/s3.py +++ b/scripts/s3.py @@ -162,6 +162,8 @@ async def get_registry_adapter(): registry_adapter_address = "0x57AA88A0810dfe3f9b71a9b179Dd8bF5F956C46A" elif chain.id == Network.Optimism: registry_adapter_address = "0xBcfCA75fF12E2C1bB404c2C216DBF901BE047690" + elif chain.id == Network.Base: + registry_adapter_address = "0xACd0CEa837A6E6f5824F4Cac6467a67dfF4B0868" return await Contract.coroutine(registry_adapter_address) diff --git a/services/dashboard/docker-compose.yml b/services/dashboard/docker-compose.yml index 8a935eda0..b6575edad 100644 --- a/services/dashboard/docker-compose.yml +++ b/services/dashboard/docker-compose.yml @@ -97,6 +97,7 @@ x-envs: &envs - FTMSCAN_TOKEN - ARBISCAN_TOKEN - OPTISCAN_TOKEN + - BASESCAN_TOKEN # POSTGRES ENVS - PGHOST=postgres diff --git a/set_network_envs.sh b/set_network_envs.sh index 053ca178e..8e1c621ed 100755 --- a/set_network_envs.sh +++ b/set_network_envs.sh @@ -44,6 +44,13 @@ elif [[ "$network" =~ ^op$|^OPTI$|^opti$|^optimism$ ]]; then export EXPLORER=$OPTI_EXPLORER export DEFAULT_EXPLORER=https://api-optimistic.etherscan.io/api +elif [[ "$network" =~ ^base$|^BASE$ ]]; then + export NETWORK=base + export BROWNIE_NETWORK=base-main + export WEB3_PROVIDER=$BASE_WEB3_PROVIDER + export EXPLORER=$BASE_EXPLORER + export DEFAULT_EXPLORER=https://api.basescan.org/api + else echo "Error! Unsupported network $network specified!" exit 1 diff --git a/yearn/constants.py b/yearn/constants.py index 66b9e75c5..605ea4c4f 100644 --- a/yearn/constants.py +++ b/yearn/constants.py @@ -19,6 +19,7 @@ Network.Gnosis: "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d", Network.Optimism: "0x4200000000000000000000000000000000000006", Network.Polygon: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + Network.Base: "0x4200000000000000000000000000000000000006" }.get(chain.id, None) YEARN_ADDRESSES_PROVIDER = "0x9be19Ee7Bc4099D62737a7255f5c227fBcd6dB93" @@ -45,6 +46,9 @@ Network.Optimism: { "0xea3a15df68fCdBE44Fdb0DB675B2b3A14a148b26", }, + Network.Base: { + "0x01fE3347316b2223961B20689C65eaeA71348e93", + }, }.get(chain.id,set()) STRATEGIST_MULTISIG = {convert.to_address(address) for address in STRATEGIST_MULTISIG} @@ -55,6 +59,7 @@ Network.Gnosis: "0x22eAe41c7Da367b9a15e942EB6227DF849Bb498C", Network.Arbitrum: "0xb6bc033d34733329971b938fef32fad7e98e56ad", Network.Optimism: "0xF5d9D6133b698cE29567a90Ab35CfB874204B3A7", + Network.Base: "0xbfAABa9F56A39B814281D68d2Ad949e88D06b02E", }.get(chain.id, None) if YCHAD_MULTISIG: @@ -65,6 +70,7 @@ Network.Fantom: "0x89716Ad7EDC3be3B35695789C475F3e7A3Deb12a", Network.Arbitrum: "0x1deb47dcc9a35ad454bf7f0fcdb03c09792c08c1", Network.Optimism: "0x84654e35E504452769757AAe5a8C7C6599cBf954", + Network.Base: "0x02ff746D8cb62709aEEc611CeC9B17d7dD1D3480", }.get(chain.id, None) if TREASURY_MULTISIG: @@ -97,6 +103,10 @@ YCHAD_MULTISIG, TREASURY_MULTISIG, }, + Network.Base: { + YCHAD_MULTISIG, + TREASURY_MULTISIG, + } }.get(chain.id,set()) TREASURY_WALLETS = {convert.to_address(address) for address in TREASURY_WALLETS} diff --git a/yearn/middleware/middleware.py b/yearn/middleware/middleware.py index 2b315c6ac..85e5c47af 100644 --- a/yearn/middleware/middleware.py +++ b/yearn/middleware/middleware.py @@ -27,6 +27,7 @@ Network.Fantom: 10_000, # 0.1 days Network.Arbitrum: 20_000, # 0.34 days Network.Optimism: 800_000, # 10.02 days + Network.Base: 800_000, # test value } def _get_batch_size() -> int: diff --git a/yearn/multicall2.py b/yearn/multicall2.py index dae743b91..649d6ea68 100644 --- a/yearn/multicall2.py +++ b/yearn/multicall2.py @@ -24,7 +24,8 @@ Network.Gnosis: '0xFAa296891cA6CECAF2D86eF5F7590316d0A17dA0', # maker has not yet deployed multicall2. This is from another deployment Network.Fantom: '0xD98e3dBE5950Ca8Ce5a4b59630a5652110403E5c', Network.Arbitrum: '0x5B5CFE992AdAC0C9D48E05854B2d91C73a003858', - Network.Optimism: '0xcA11bde05977b3631167028862bE2a173976CA11' # Multicall 3 + Network.Optimism: '0xcA11bde05977b3631167028862bE2a173976CA11', # Multicall 3 + Network.Base: '0xcA11bde05977b3631167028862bE2a173976CA11' # MC3 } multicall2 = contract(MULTICALL2[chain.id]) diff --git a/yearn/partners/delegated.py b/yearn/partners/delegated.py index 8a90e1c9d..35c5f98d8 100644 --- a/yearn/partners/delegated.py +++ b/yearn/partners/delegated.py @@ -15,6 +15,7 @@ Network.Fantom: "0x086865B2983320b36C42E48086DaDc786c9Ac73B", Network.Arbitrum: "0x0e5b46E4b2a05fd53F5a4cD974eb98a9a613bcb7", Network.Optimism: "0x7E08735690028cdF3D81e7165493F1C34065AbA2", + Network.Base: "0xD0F08E42A40569fF83D28AA783a5b6537462667c", }[chain.id]) class AsOfDict(dict): diff --git a/yearn/prices/constants.py b/yearn/prices/constants.py index d682c093d..d3721be6b 100644 --- a/yearn/prices/constants.py +++ b/yearn/prices/constants.py @@ -26,6 +26,13 @@ 'weth': '0x4200000000000000000000000000000000000006', 'usdc': '0x7F5c764cBc14f9669B88837ca1490cCa17c31607', 'dai': '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', + }, + Network.Base: { + 'weth': '0x4200000000000000000000000000000000000006', + 'dai': '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', + 'usdc': '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', #temp + 'usdt': '0x4A3A6Dd60A34bB2Aba60D73B4C88315E9CeB6A3D', #temp + 'wbtc': '0x77852193BD608A518dd7b7C2f891A1d02ceeB4d4', #temp } } @@ -87,15 +94,21 @@ '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1': 'dai', '0x2E3D870790dC77A83DD1d18184Acc7439A53f475': 'frax', '0x8c6f28f2F1A3C87F0f938b96d27520d9751ec8d9': 'susd', + }, + Network.Base: { + '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA': 'usdbc', + '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb': 'dai', + '0x4A3A6Dd60A34bB2Aba60D73B4C88315E9CeB6A3D': 'mim', } } ib_snapshot_block_by_network = { Network.Mainnet: 14051986, Network.Fantom: 28680044, - Network.Gnosis: 1, # TODO revisit as IB is not deployed in gnosis - Network.Arbitrum: 1, + Network.Gnosis: 1, # TODO revisit as IB is not deployed on gnosis + Network.Arbitrum: 1, # TODO revisit as IB is not deployed on arbitrum Network.Optimism: 12658427, + Network.Base: 1, # TODO revisit as IB is not deployed on base } # We convert to checksum address here to prevent minor annoyances. It's worth it. diff --git a/yearn/prices/curve.py b/yearn/prices/curve.py index cb597f24f..aa089bbd2 100644 --- a/yearn/prices/curve.py +++ b/yearn/prices/curve.py @@ -130,6 +130,9 @@ def __init__(self) -> None: @sentry_catch_all def watch_events(self) -> None: + if chain.id == Network.Base: + # No curve pool + return sleep_time = 600 registries = [] registry_logs = [] diff --git a/yearn/prices/uniswap/v2.py b/yearn/prices/uniswap/v2.py index 8bc0ec2de..c9f014f38 100644 --- a/yearn/prices/uniswap/v2.py +++ b/yearn/prices/uniswap/v2.py @@ -75,6 +75,13 @@ 'router': '0xE6Df0BB08e5A97b40B21950a0A51b94c4DbA0Ff6', } + ], + Network.Base: [ + { + 'name': 'sushiswap', + 'factory': '0x71524B4f93c58fcbF659783284E38825f0622859', + 'router': '0x6BDED42c6DA8FBf0d2bA55B2fa120C5e0c8D7891', + } ] } diff --git a/yearn/prices/uniswap/v3.py b/yearn/prices/uniswap/v3.py index b60cd5445..08aae9493 100644 --- a/yearn/prices/uniswap/v3.py +++ b/yearn/prices/uniswap/v3.py @@ -29,26 +29,33 @@ def __init__(self, v) -> None: # https://github.com/Uniswap/uniswap-v3-periphery/blob/main/deploys.md UNISWAP_V3_FACTORY = '0x1F98431c8aD98523631AE4a59f267346ea31F984' +UNISWAP_V3_QUOTER = '0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6' FEE_DENOMINATOR = 1_000_000 USDC_SCALE = 1e6 -# same addresses on all networks addresses = { Network.Mainnet: { 'factory': UNISWAP_V3_FACTORY, + 'quoter': UNISWAP_V3_QUOTER, 'fee_tiers': [3000, 500, 10_000, 100], }, Network.Arbitrum: { 'factory': UNISWAP_V3_FACTORY, + 'quoter': UNISWAP_V3_QUOTER, 'fee_tiers': [3000, 500, 10_000], }, Network.Optimism: { 'factory': UNISWAP_V3_FACTORY, + 'quoter': UNISWAP_V3_QUOTER, 'fee_tiers': [3000, 500, 10_000, 100], }, + Network.Base: { + 'factory': '0x33128a8fC17869897dcE68Ed026d694621f6FDfD', + 'quoter': '0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a', # quoter v2 + 'fee_tiers': [3000, 500, 10_000, 100], + } } - class UniswapV3(metaclass=Singleton): def __init__(self) -> None: if chain.id not in addresses: @@ -58,7 +65,7 @@ def __init__(self) -> None: self.factory: Contract = contract(conf['factory']) self.quoter: Contract = Contract.from_abi( name='Quoter', - address='0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6', + address=addresses[chain.id]['quoter'], abi=json.load(open('interfaces/uniswap/UniswapV3Quoter.json')) ) # use direct abi from etherscan because the quoter is not verified on all chains (opti) self.fee_tiers = [FeeTier(fee) for fee in conf['fee_tiers']] diff --git a/yearn/prices/yearn.py b/yearn/prices/yearn.py index 45443d911..519b70d8d 100644 --- a/yearn/prices/yearn.py +++ b/yearn/prices/yearn.py @@ -31,6 +31,9 @@ Network.Optimism: { 'v2': '0xBcfCA75fF12E2C1bB404c2C216DBF901BE047690', }, + Network.Base: { + 'v2': '0xACd0CEa837A6E6f5824F4Cac6467a67dfF4B0868', + } } diff --git a/yearn/treasury/treasury.py b/yearn/treasury/treasury.py index a168949bf..5b017b9ad 100644 --- a/yearn/treasury/treasury.py +++ b/yearn/treasury/treasury.py @@ -84,6 +84,7 @@ def __init__(self, asynchronous: bool = False, load_prices: bool = False) -> Non Network.Gnosis: 20_000_000, Network.Arbitrum: 4_837_859, Network.Optimism: 18_100_336, + Network.Base: 3_264_243, }[chain.id] ''' TODO: confirm these starts match start_block @@ -122,6 +123,7 @@ def __init__(self, asynchronous: bool = False, load_prices: bool = False) -> Non Network.Gnosis: 20_455_212, Network.Arbitrum: 2_434_174, Network.Optimism: 18_084_577, + Network.Base: 3_263_643, }[chain.id] super().__init__(constants.STRATEGIST_MULTISIG, label='sms', start_block=start_block, asynchronous=asynchronous, load_prices=load_prices) diff --git a/yearn/utils.py b/yearn/utils.py index 394797aa2..8e6412cca 100644 --- a/yearn/utils.py +++ b/yearn/utils.py @@ -30,6 +30,7 @@ Network.Fantom: 4_564_024, # fantom returns "missing trie node" before that Network.Arbitrum: 0, Network.Optimism: 0, + Network.Base: 0, } _erc20 = lru_cache(maxsize=None)(interface.ERC20) diff --git a/yearn/v2/registry.py b/yearn/v2/registry.py index 928d51d09..6731430b1 100644 --- a/yearn/v2/registry.py +++ b/yearn/v2/registry.py @@ -66,6 +66,8 @@ def load_registry(self): contract('0x81291ceb9bB265185A9D07b91B5b50Df94f005BF'), contract('0x8ED9F6343f057870F1DeF47AaE7CD88dfAA049A8'), # StakingRewardsRegistry ] + elif chain.id == Network.Base: + registries = [contract('0xF3885eDe00171997BFadAa98E01E167B53a78Ec5')] else: raise UnsupportedNetwork('yearn v2 is not available on this network') @@ -73,7 +75,9 @@ def load_registry(self): if hasattr(r, 'releaseRegistry') and "ReleaseRegistryUpdated" in r.topics: logs = get_logs_asap(str(r), [r.topics["ReleaseRegistryUpdated"]]) # Add all past and present Release Registries - registries.extend({contract(list(event.values())[0]) for event in decode_logs(logs)}) + for rr in {list(event.values())[0] for event in decode_logs(logs)}: + registries.append(contract(rr)) + logger.debug("release registry %s found for registry %s", rr, r) return registries def load_from_ens(self): diff --git a/yearn/yearn.py b/yearn/yearn.py index 1c46a7faf..7f0ec550c 100644 --- a/yearn/yearn.py +++ b/yearn/yearn.py @@ -38,21 +38,11 @@ def __init__(self, load_strategies=True, load_harvests=False, load_transfers=Fal "ib": yearn.ironbank.Registry(exclude_ib_tvl=exclude_ib_tvl), "special": yearn.special.Registry(), } - elif chain.id == Network.Gnosis: + elif chain.id in [Network.Gnosis, Network.Base]: self.registries = { "v2": yearn.v2.registry.Registry(watch_events_forever=watch_events_forever), } - elif chain.id == Network.Fantom: - self.registries = { - "v2": yearn.v2.registry.Registry(watch_events_forever=watch_events_forever), - "ib": yearn.ironbank.Registry(exclude_ib_tvl=exclude_ib_tvl), - } - elif chain.id == Network.Arbitrum: - self.registries = { - "v2": yearn.v2.registry.Registry(watch_events_forever=watch_events_forever), - "ib": yearn.ironbank.Registry(exclude_ib_tvl=exclude_ib_tvl), - } - elif chain.id == Network.Optimism: + elif chain.id in [Network.Fantom, Network.Arbitrum, Network.Optimism]: self.registries = { "v2": yearn.v2.registry.Registry(watch_events_forever=watch_events_forever), "ib": yearn.ironbank.Registry(exclude_ib_tvl=exclude_ib_tvl),