From 3b288f0593c606eaa1d0d2dc5e7d2a97ebaaf90f Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Wed, 11 Dec 2024 15:45:51 +0700 Subject: [PATCH 01/26] chore: bal addresses needs updated bal tools --- tools/python/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/python/requirements.txt b/tools/python/requirements.txt index eb23cae94..51ede4b8e 100644 --- a/tools/python/requirements.txt +++ b/tools/python/requirements.txt @@ -5,7 +5,7 @@ pandas tabulate requests web3 -git+https://github.com/BalancerMaxis/bal_addresses@main +git+https://github.com/BalancerMaxis/bal_addresses@dev/use-bal_tools-dev-branch dune-client pytest dataclasses-json From 9bb84530b2a1a9249da5c1583aa2b77365fc8e3e Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Wed, 11 Dec 2024 15:46:23 +0700 Subject: [PATCH 02/26] feat: query v3 subgraph for user balances --- tools/python/gen_morpho_airdrop.py | 43 ++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tools/python/gen_morpho_airdrop.py diff --git a/tools/python/gen_morpho_airdrop.py b/tools/python/gen_morpho_airdrop.py new file mode 100644 index 000000000..a58d212e0 --- /dev/null +++ b/tools/python/gen_morpho_airdrop.py @@ -0,0 +1,43 @@ +from bal_tools import Subgraph + + +SUBGRAPH = Subgraph() + + +def get_user_shares(pool, block): + query = """query PoolShares($where: PoolShare_filter, $block: Block_height) { + poolShares(where: $where, block: $block) { + user { + id + } + balance + } + }""" + params = { + "where": { + "balance_gt": 0.001, + "pool": pool, + }, + "block": {"number": block}, + } + raw = SUBGRAPH.fetch_graphql_data( + "subgraphs-v3", + query, + params, + url="https://api.studio.thegraph.com/query/75376/balancer-v3/version/latest", + ) + return dict([(x["user"]["id"], x["balance"]) for x in raw["poolShares"]]) + + +def build_airdrop(): + # https://docs.merkl.xyz/merkl-mechanisms/types-of-campaign/airdrop + pass + + +if __name__ == "__main__": + # https://etherscan.io/token/0x89bb794097234e5e930446c0cec0ea66b35d7570#balances + print( + get_user_shares( + pool="0x89bb794097234e5e930446c0cec0ea66b35d7570", block=21378029 + ) + ) From fb6e70a099804fe99e301f820eec78e4ae02a87d Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Tue, 17 Dec 2024 09:47:04 +0100 Subject: [PATCH 03/26] feat: consolidate multiple snapshots into an airdrop file --- tools/python/gen_morpho_airdrop.py | 70 +++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/tools/python/gen_morpho_airdrop.py b/tools/python/gen_morpho_airdrop.py index a58d212e0..53e3e8f83 100644 --- a/tools/python/gen_morpho_airdrop.py +++ b/tools/python/gen_morpho_airdrop.py @@ -1,7 +1,13 @@ +import json + +import numpy as np +import pandas as pd + from bal_tools import Subgraph SUBGRAPH = Subgraph() +MORPHO = "0x58D97B57BB95320F9a05dC918Aef65434969c2B2" def get_user_shares(pool, block): @@ -29,15 +35,65 @@ def get_user_shares(pool, block): return dict([(x["user"]["id"], x["balance"]) for x in raw["poolShares"]]) -def build_airdrop(): +def get_block_from_timestamp(ts): + query = """query GetBlockFromTimestamp($where: Block_filter) { + blocks(orderBy: "number", orderDirection: "desc", where: $where) { + number + timestamp + } + }""" + params = {"where": {"timestamp_lte": ts}} + raw = SUBGRAPH.fetch_graphql_data( + "blocks", + query, + params, + url="https://api.studio.thegraph.com/query/48427/ethereum-blocks/version/latest", + ) + return int(raw["blocks"][0]["number"]) + + +def build_snapshot_df( + pool, # pool address + end, # timestamp of the last snapshot + n=7, # amount of snapshots + step_size=60 * 60 * 24, # amount of seconds between snapshots +): + shares = {} + for _ in range(n): + block = get_block_from_timestamp(end) + shares[block] = get_user_shares(pool=pool, block=block) + end -= step_size + return pd.DataFrame(shares, dtype=float).fillna(0) + + +def consolidate_shares(df): + consolidated = pd.DataFrame() + for block in df.columns: + # calculate the percentage of the pool each user owns + consolidated[block] = df[block] / df[block].sum() + # weigh it by the total pool size of that block + consolidated[block] *= df.sum()[block] + # sum the weighted percentages per user + consolidated["total"] = consolidated.sum(axis=1) + # divide the weighted percentages by the sum of all weights + consolidated["total"] = consolidated["total"] / df.sum().sum() + return consolidated + + +def build_airdrop(reward_token, reward_total_wei, df): # https://docs.merkl.xyz/merkl-mechanisms/types-of-campaign/airdrop - pass + df["wei"] = df["total"] * reward_total_wei + df["wei"] = df["wei"].apply(np.floor).astype(int).astype(str) + return {"rewardToken": reward_token, "rewards": df[["wei"]].to_dict(orient="index")} if __name__ == "__main__": - # https://etherscan.io/token/0x89bb794097234e5e930446c0cec0ea66b35d7570#balances - print( - get_user_shares( - pool="0x89bb794097234e5e930446c0cec0ea66b35d7570", block=21378029 - ) + # get bpt balances for a pool at different timestamps + df = build_snapshot_df( + pool="0x89bb794097234e5e930446c0cec0ea66b35d7570", end=1734393600 ) + # consolidate user pool shares + df = consolidate_shares(df) + # build airdrop object and dump to json file + airdrop = build_airdrop(reward_token=MORPHO, reward_total_wei=1e18, df=df) + json.dump(airdrop, open("airdrop.json", "w"), indent=2) From 4c29d9c51de52f39717e8111fd2118688efcd586 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 6 Jan 2025 13:11:23 +0100 Subject: [PATCH 04/26] fix: prevent division by zero --- tools/python/gen_morpho_airdrop.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/python/gen_morpho_airdrop.py b/tools/python/gen_morpho_airdrop.py index 53e3e8f83..8f451f6d1 100644 --- a/tools/python/gen_morpho_airdrop.py +++ b/tools/python/gen_morpho_airdrop.py @@ -69,10 +69,13 @@ def build_snapshot_df( def consolidate_shares(df): consolidated = pd.DataFrame() for block in df.columns: - # calculate the percentage of the pool each user owns - consolidated[block] = df[block] / df[block].sum() - # weigh it by the total pool size of that block - consolidated[block] *= df.sum()[block] + if df[block].sum() == 0: + consolidated[block] = 0 + else: + # calculate the percentage of the pool each user owns + consolidated[block] = df[block] / df[block].sum() + # weigh it by the total pool size of that block + consolidated[block] *= df.sum()[block] # sum the weighted percentages per user consolidated["total"] = consolidated.sum(axis=1) # divide the weighted percentages by the sum of all weights From f06036619bb804d7c3cd2a9fc71512e8bc12ddae Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Thu, 9 Jan 2025 14:39:38 +0100 Subject: [PATCH 05/26] feat: get user shares from gauge similar to from pool --- tools/python/gen_morpho_airdrop.py | 33 +++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/tools/python/gen_morpho_airdrop.py b/tools/python/gen_morpho_airdrop.py index 8f451f6d1..0f6ff90ae 100644 --- a/tools/python/gen_morpho_airdrop.py +++ b/tools/python/gen_morpho_airdrop.py @@ -1,4 +1,5 @@ import json +from datetime import datetime import numpy as np import pandas as pd @@ -9,8 +10,12 @@ SUBGRAPH = Subgraph() MORPHO = "0x58D97B57BB95320F9a05dC918Aef65434969c2B2" +POOL = "0x10a04efba5b880e169920fd4348527c64fb29d4d" +GAUGE = "0x5bbaed1fadc08c5fb3e4ae3c8848777e2da77103" +LATEST_TS = int(datetime.now().timestamp()) -def get_user_shares(pool, block): + +def get_user_shares_pool(pool, block): query = """query PoolShares($where: PoolShare_filter, $block: Block_height) { poolShares(where: $where, block: $block) { user { @@ -35,6 +40,26 @@ def get_user_shares(pool, block): return dict([(x["user"]["id"], x["balance"]) for x in raw["poolShares"]]) +def get_user_shares_gauge(gauge, block): + query = """query GaugeShares($where: GaugeShare_filter, $block: Block_height) { + gaugeShares(where: $where, block: $block) { + user { + id + } + balance + } + }""" + params = { + "where": { + "balance_gt": 0.001, + "gauge": gauge, + }, + "block": {"number": block}, + } + raw = SUBGRAPH.fetch_graphql_data("gauges", query, params) + return dict([(x["user"]["id"], x["balance"]) for x in raw["gaugeShares"]]) + + def get_block_from_timestamp(ts): query = """query GetBlockFromTimestamp($where: Block_filter) { blocks(orderBy: "number", orderDirection: "desc", where: $where) { @@ -61,7 +86,7 @@ def build_snapshot_df( shares = {} for _ in range(n): block = get_block_from_timestamp(end) - shares[block] = get_user_shares(pool=pool, block=block) + shares[block] = get_user_shares_pool(pool=pool, block=block) end -= step_size return pd.DataFrame(shares, dtype=float).fillna(0) @@ -92,9 +117,7 @@ def build_airdrop(reward_token, reward_total_wei, df): if __name__ == "__main__": # get bpt balances for a pool at different timestamps - df = build_snapshot_df( - pool="0x89bb794097234e5e930446c0cec0ea66b35d7570", end=1734393600 - ) + df = build_snapshot_df(pool=POOL, end=LATEST_TS) # consolidate user pool shares df = consolidate_shares(df) # build airdrop object and dump to json file From 287176a9cdc79acba9adc03edf7f072b1061ef89 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Thu, 9 Jan 2025 15:12:23 +0100 Subject: [PATCH 06/26] feat: consider both pool and gauge shares --- tools/python/gen_morpho_airdrop.py | 32 +++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/tools/python/gen_morpho_airdrop.py b/tools/python/gen_morpho_airdrop.py index 0f6ff90ae..6d58d111b 100644 --- a/tools/python/gen_morpho_airdrop.py +++ b/tools/python/gen_morpho_airdrop.py @@ -1,5 +1,6 @@ import json from datetime import datetime +from decimal import Decimal import numpy as np import pandas as pd @@ -37,7 +38,7 @@ def get_user_shares_pool(pool, block): params, url="https://api.studio.thegraph.com/query/75376/balancer-v3/version/latest", ) - return dict([(x["user"]["id"], x["balance"]) for x in raw["poolShares"]]) + return dict([(x["user"]["id"], Decimal(x["balance"])) for x in raw["poolShares"]]) def get_user_shares_gauge(gauge, block): @@ -57,7 +58,7 @@ def get_user_shares_gauge(gauge, block): "block": {"number": block}, } raw = SUBGRAPH.fetch_graphql_data("gauges", query, params) - return dict([(x["user"]["id"], x["balance"]) for x in raw["gaugeShares"]]) + return dict([(x["user"]["id"], Decimal(x["balance"])) for x in raw["gaugeShares"]]) def get_block_from_timestamp(ts): @@ -83,12 +84,29 @@ def build_snapshot_df( n=7, # amount of snapshots step_size=60 * 60 * 24, # amount of seconds between snapshots ): - shares = {} + gauge = GAUGE # TODO: lookup gauge from pool + pool_shares = {} + gauge_shares = {} + total_shares = {} for _ in range(n): block = get_block_from_timestamp(end) - shares[block] = get_user_shares_pool(pool=pool, block=block) + pool_shares[block] = get_user_shares_pool(pool=pool, block=block) + gauge_shares[block] = get_user_shares_gauge(gauge=gauge, block=block) end -= step_size - return pd.DataFrame(shares, dtype=float).fillna(0) + for block in pool_shares: + total_shares[block] = {} + for user_id in pool_shares[block]: + if user_id == gauge: + # we do not want to count the gauge as a user + continue + total_shares[block][user_id] = pool_shares[block][user_id] + for user_id in gauge_shares[block]: + if user_id not in total_shares[block]: + total_shares[block][user_id] = gauge_shares[block][user_id] + else: + total_shares[block][user_id] += gauge_shares[block][user_id] + # TODO: checksum balances + return pd.DataFrame(total_shares, dtype=float).fillna(0) def consolidate_shares(df): @@ -118,8 +136,12 @@ def build_airdrop(reward_token, reward_total_wei, df): if __name__ == "__main__": # get bpt balances for a pool at different timestamps df = build_snapshot_df(pool=POOL, end=LATEST_TS) + df.to_csv("tools/python/df_snapshot.csv") + # consolidate user pool shares df = consolidate_shares(df) + df.to_csv("tools/python/df_consolidated.csv") + # build airdrop object and dump to json file airdrop = build_airdrop(reward_token=MORPHO, reward_total_wei=1e18, df=df) json.dump(airdrop, open("airdrop.json", "w"), indent=2) From af401a1308d0916eed5fb5e22028bc7118456d71 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Thu, 9 Jan 2025 16:35:20 +0100 Subject: [PATCH 07/26] feat: add checksums based on total supply onchain --- tools/python/gen_morpho_airdrop.py | 42 +++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/tools/python/gen_morpho_airdrop.py b/tools/python/gen_morpho_airdrop.py index 6d58d111b..8b91284ff 100644 --- a/tools/python/gen_morpho_airdrop.py +++ b/tools/python/gen_morpho_airdrop.py @@ -1,16 +1,25 @@ import json +import os +import pytest from datetime import datetime from decimal import Decimal import numpy as np import pandas as pd +from web3 import Web3, exceptions from bal_tools import Subgraph SUBGRAPH = Subgraph() MORPHO = "0x58D97B57BB95320F9a05dC918Aef65434969c2B2" +W3 = Web3( + Web3.HTTPProvider( + f"https://lb.drpc.org/ogrpc?network=ethereum&dkey={os.getenv('DRPC_KEY')}" + ) +) +# dev POOL = "0x10a04efba5b880e169920fd4348527c64fb29d4d" GAUGE = "0x5bbaed1fadc08c5fb3e4ae3c8848777e2da77103" LATEST_TS = int(datetime.now().timestamp()) @@ -84,15 +93,21 @@ def build_snapshot_df( n=7, # amount of snapshots step_size=60 * 60 * 24, # amount of seconds between snapshots ): - gauge = GAUGE # TODO: lookup gauge from pool + # TODO: lookup gauge from pool + gauge = GAUGE + + # get user shares for pool and gauge at different timestamps pool_shares = {} gauge_shares = {} - total_shares = {} for _ in range(n): block = get_block_from_timestamp(end) pool_shares[block] = get_user_shares_pool(pool=pool, block=block) gauge_shares[block] = get_user_shares_gauge(gauge=gauge, block=block) end -= step_size + + # calculate total shares per user per block + total_shares = {} + total_supply = {} for block in pool_shares: total_shares[block] = {} for user_id in pool_shares[block]: @@ -105,8 +120,27 @@ def build_snapshot_df( total_shares[block][user_id] = gauge_shares[block][user_id] else: total_shares[block][user_id] += gauge_shares[block][user_id] - # TODO: checksum balances - return pd.DataFrame(total_shares, dtype=float).fillna(0) + # collect onchain total supply per block + contract = W3.eth.contract( + address=Web3.to_checksum_address(pool), + abi=json.load(open("tools/python/abis/StablePoolV3.json")), + ) + try: + total_supply[block] = contract.functions.totalSupply().call( + block_identifier=block + ) + except exceptions.BadFunctionCallOutput: + total_supply[block] = 0 + + # build dataframe + df = pd.DataFrame(total_shares, dtype=float).fillna(0) + + # checksum total balances versus total supply + assert df.sum().sum() == pytest.approx(sum(total_supply.values()) / 1e18, rel=1e-6) + for block in df.columns: + assert df[block].sum() == pytest.approx(total_supply[block] / 1e18, rel=1e-6) + + return df def consolidate_shares(df): From 38ec5ae9a21c08514a8d6f937ac40441e160a591 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Thu, 9 Jan 2025 16:35:42 +0100 Subject: [PATCH 08/26] chore: add v3 stable pool abi --- tools/python/abis/StablePoolV3.json | 720 ++++++++++++++++++++++++++++ 1 file changed, 720 insertions(+) create mode 100644 tools/python/abis/StablePoolV3.json diff --git a/tools/python/abis/StablePoolV3.json b/tools/python/abis/StablePoolV3.json new file mode 100644 index 000000000..e89e0d7fe --- /dev/null +++ b/tools/python/abis/StablePoolV3.json @@ -0,0 +1,720 @@ +[ + { + "inputs": [ + { + "components": [ + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "symbol", "type": "string" }, + { + "internalType": "uint256", + "name": "amplificationParameter", + "type": "uint256" + }, + { "internalType": "string", "name": "version", "type": "string" } + ], + "internalType": "struct StablePool.NewPoolParams", + "name": "params", + "type": "tuple" + }, + { "internalType": "contract IVault", "name": "vault", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { "inputs": [], "name": "AmpUpdateAlreadyStarted", "type": "error" }, + { "inputs": [], "name": "AmpUpdateDurationTooShort", "type": "error" }, + { "inputs": [], "name": "AmpUpdateNotStarted", "type": "error" }, + { "inputs": [], "name": "AmpUpdateRateTooFast", "type": "error" }, + { "inputs": [], "name": "AmplificationFactorTooHigh", "type": "error" }, + { "inputs": [], "name": "AmplificationFactorTooLow", "type": "error" }, + { "inputs": [], "name": "ECDSAInvalidSignature", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "length", "type": "uint256" } + ], + "name": "ECDSAInvalidSignatureLength", + "type": "error" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "s", "type": "bytes32" }], + "name": "ECDSAInvalidSignatureS", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "deadline", "type": "uint256" } + ], + "name": "ERC2612ExpiredSignature", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "signer", "type": "address" }, + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "ERC2612InvalidSigner", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "currentNonce", "type": "uint256" } + ], + "name": "InvalidAccountNonce", + "type": "error" + }, + { "inputs": [], "name": "InvalidShortString", "type": "error" }, + { + "inputs": [ + { "internalType": "uint8", "name": "bits", "type": "uint8" }, + { "internalType": "uint256", "name": "value", "type": "uint256" } + ], + "name": "SafeCastOverflowedUintDowncast", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "SenderIsNotVault", + "type": "error" + }, + { "inputs": [], "name": "SenderNotAllowed", "type": "error" }, + { + "inputs": [], + "name": "StableComputeBalanceDidNotConverge", + "type": "error" + }, + { "inputs": [], "name": "StableInvariantDidNotConverge", "type": "error" }, + { + "inputs": [{ "internalType": "string", "name": "str", "type": "string" }], + "name": "StringTooLong", + "type": "error" + }, + { "inputs": [], "name": "ZeroDivision", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "startValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "endValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "startTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "endTime", + "type": "uint256" + } + ], + "name": "AmpUpdateStarted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "currentValue", + "type": "uint256" + } + ], + "name": "AmpUpdateStopped", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EIP712DomainChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [], + "name": "DOMAIN_SEPARATOR", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "PERMIT_TYPEHASH", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { "internalType": "uint256", "name": "tokenInIndex", "type": "uint256" }, + { "internalType": "uint256", "name": "invariantRatio", "type": "uint256" } + ], + "name": "computeBalance", + "outputs": [ + { "internalType": "uint256", "name": "newBalance", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { "internalType": "enum Rounding", "name": "rounding", "type": "uint8" } + ], + "name": "computeInvariant", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "eip712Domain", + "outputs": [ + { "internalType": "bytes1", "name": "fields", "type": "bytes1" }, + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "version", "type": "string" }, + { "internalType": "uint256", "name": "chainId", "type": "uint256" }, + { + "internalType": "address", + "name": "verifyingContract", + "type": "address" + }, + { "internalType": "bytes32", "name": "salt", "type": "bytes32" }, + { "internalType": "uint256[]", "name": "extensions", "type": "uint256[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "emitApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "emitTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "selector", "type": "bytes4" } + ], + "name": "getActionId", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAggregateFeePercentages", + "outputs": [ + { + "internalType": "uint256", + "name": "aggregateSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "aggregateYieldFeePercentage", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAmplificationParameter", + "outputs": [ + { "internalType": "uint256", "name": "value", "type": "uint256" }, + { "internalType": "bool", "name": "isUpdating", "type": "bool" }, + { "internalType": "uint256", "name": "precision", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getAmplificationState", + "outputs": [ + { + "components": [ + { "internalType": "uint64", "name": "startValue", "type": "uint64" }, + { "internalType": "uint64", "name": "endValue", "type": "uint64" }, + { "internalType": "uint32", "name": "startTime", "type": "uint32" }, + { "internalType": "uint32", "name": "endTime", "type": "uint32" } + ], + "internalType": "struct AmplificationState", + "name": "amplificationState", + "type": "tuple" + }, + { "internalType": "uint256", "name": "precision", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getCurrentLiveBalances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMaximumInvariantRatio", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getMaximumSwapFeePercentage", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumInvariantRatio", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getMinimumSwapFeePercentage", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getRate", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStablePoolDynamicData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256[]", + "name": "balancesLiveScaled18", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "tokenRates", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "staticSwapFeePercentage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + }, + { "internalType": "uint256", "name": "bptRate", "type": "uint256" }, + { + "internalType": "uint256", + "name": "amplificationParameter", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "startValue", + "type": "uint256" + }, + { "internalType": "uint256", "name": "endValue", "type": "uint256" }, + { "internalType": "uint32", "name": "startTime", "type": "uint32" }, + { "internalType": "uint32", "name": "endTime", "type": "uint32" }, + { "internalType": "bool", "name": "isAmpUpdating", "type": "bool" }, + { + "internalType": "bool", + "name": "isPoolInitialized", + "type": "bool" + }, + { "internalType": "bool", "name": "isPoolPaused", "type": "bool" }, + { + "internalType": "bool", + "name": "isPoolInRecoveryMode", + "type": "bool" + } + ], + "internalType": "struct StablePoolDynamicData", + "name": "data", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStablePoolImmutableData", + "outputs": [ + { + "components": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "decimalScalingFactors", + "type": "uint256[]" + }, + { + "internalType": "uint256", + "name": "amplificationParameterPrecision", + "type": "uint256" + } + ], + "internalType": "struct StablePoolImmutableData", + "name": "data", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStaticSwapFeePercentage", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTokenInfo", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + }, + { + "components": [ + { + "internalType": "enum TokenType", + "name": "tokenType", + "type": "uint8" + }, + { + "internalType": "contract IRateProvider", + "name": "rateProvider", + "type": "address" + }, + { "internalType": "bool", "name": "paysYieldFees", "type": "bool" } + ], + "internalType": "struct TokenInfo[]", + "name": "tokenInfo", + "type": "tuple[]" + }, + { + "internalType": "uint256[]", + "name": "balancesRaw", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "lastBalancesLiveScaled18", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTokens", + "outputs": [ + { + "internalType": "contract IERC20[]", + "name": "tokens", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getVault", + "outputs": [ + { "internalType": "contract IVault", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "incrementNonce", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "nonces", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { "internalType": "enum SwapKind", "name": "kind", "type": "uint8" }, + { + "internalType": "uint256", + "name": "amountGivenScaled18", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "balancesScaled18", + "type": "uint256[]" + }, + { "internalType": "uint256", "name": "indexIn", "type": "uint256" }, + { "internalType": "uint256", "name": "indexOut", "type": "uint256" }, + { "internalType": "address", "name": "router", "type": "address" }, + { "internalType": "bytes", "name": "userData", "type": "bytes" } + ], + "internalType": "struct PoolSwapParams", + "name": "request", + "type": "tuple" + } + ], + "name": "onSwap", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "spender", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "uint256", "name": "deadline", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "rawEndValue", "type": "uint256" }, + { "internalType": "uint256", "name": "endTime", "type": "uint256" } + ], + "name": "startAmplificationParameterUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stopAmplificationParameterUpdate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + } +] From 3da824e6cd81e368cf15bcde1773a925ef5f380a Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Fri, 10 Jan 2025 09:57:21 +0100 Subject: [PATCH 09/26] feat: for loop over multiple reward tokens and pools --- ...orpho_airdrop.py => gen_merkl_airdrops.py} | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) rename tools/python/{gen_morpho_airdrop.py => gen_merkl_airdrops.py} (81%) diff --git a/tools/python/gen_morpho_airdrop.py b/tools/python/gen_merkl_airdrops.py similarity index 81% rename from tools/python/gen_morpho_airdrop.py rename to tools/python/gen_merkl_airdrops.py index 8b91284ff..9a23ac013 100644 --- a/tools/python/gen_morpho_airdrop.py +++ b/tools/python/gen_merkl_airdrops.py @@ -11,8 +11,15 @@ from bal_tools import Subgraph +WATCHLIST = { + "0x58D97B57BB95320F9a05dC918Aef65434969c2B2": [ # morpho + "0x10A04efbA5B880e169920Fd4348527C64FB29d4D" # csUSDC-csUSDT + ], + "0x030bA81f1c18d280636F32af80b9AAd02Cf0854e": [ # aweth + "0xc4Ce391d82D164c166dF9c8336DDF84206b2F812" # waEthLidoWETH-waEthLidowstETH + ], +} SUBGRAPH = Subgraph() -MORPHO = "0x58D97B57BB95320F9a05dC918Aef65434969c2B2" W3 = Web3( Web3.HTTPProvider( f"https://lb.drpc.org/ogrpc?network=ethereum&dkey={os.getenv('DRPC_KEY')}" @@ -20,8 +27,10 @@ ) # dev -POOL = "0x10a04efba5b880e169920fd4348527c64fb29d4d" -GAUGE = "0x5bbaed1fadc08c5fb3e4ae3c8848777e2da77103" +GAUGES = { + "0x10A04efbA5B880e169920Fd4348527C64FB29d4D": "0x5bBaeD1fADC08C5fb3e4ae3C8848777E2dA77103", + "0xc4Ce391d82D164c166dF9c8336DDF84206b2F812": "0x4B891340b51889f438a03DC0e8aAAFB0Bc89e7A6", +} LATEST_TS = int(datetime.now().timestamp()) @@ -94,7 +103,7 @@ def build_snapshot_df( step_size=60 * 60 * 24, # amount of seconds between snapshots ): # TODO: lookup gauge from pool - gauge = GAUGE + gauge = GAUGES[pool] # get user shares for pool and gauge at different timestamps pool_shares = {} @@ -168,14 +177,18 @@ def build_airdrop(reward_token, reward_total_wei, df): if __name__ == "__main__": - # get bpt balances for a pool at different timestamps - df = build_snapshot_df(pool=POOL, end=LATEST_TS) - df.to_csv("tools/python/df_snapshot.csv") - - # consolidate user pool shares - df = consolidate_shares(df) - df.to_csv("tools/python/df_consolidated.csv") - - # build airdrop object and dump to json file - airdrop = build_airdrop(reward_token=MORPHO, reward_total_wei=1e18, df=df) - json.dump(airdrop, open("airdrop.json", "w"), indent=2) + for reward_token in WATCHLIST: + for pool in WATCHLIST[reward_token]: + # get bpt balances for a pool at different timestamps + df = build_snapshot_df(pool=pool, end=LATEST_TS) + df.to_csv("tools/python/df_snapshot.csv") + + # consolidate user pool shares + df = consolidate_shares(df) + df.to_csv("tools/python/df_consolidated.csv") + + # build airdrop object and dump to json file + airdrop = build_airdrop( + reward_token=reward_token, reward_total_wei=1e18, df=df + ) + json.dump(airdrop, open(f"airdrop-{pool}.json", "w"), indent=2) From 882e8c0ef3f4e65391fb253cae6ccc758d4ee004 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Fri, 10 Jan 2025 11:05:10 +0100 Subject: [PATCH 10/26] feat: move watchlist to json file --- tools/python/gen_merkl_airdrops.py | 34 +++++++++++++++--------------- tools/python/watchlist.json | 20 ++++++++++++++++++ 2 files changed, 37 insertions(+), 17 deletions(-) create mode 100644 tools/python/watchlist.json diff --git a/tools/python/gen_merkl_airdrops.py b/tools/python/gen_merkl_airdrops.py index 9a23ac013..720af725c 100644 --- a/tools/python/gen_merkl_airdrops.py +++ b/tools/python/gen_merkl_airdrops.py @@ -11,14 +11,7 @@ from bal_tools import Subgraph -WATCHLIST = { - "0x58D97B57BB95320F9a05dC918Aef65434969c2B2": [ # morpho - "0x10A04efbA5B880e169920Fd4348527C64FB29d4D" # csUSDC-csUSDT - ], - "0x030bA81f1c18d280636F32af80b9AAd02Cf0854e": [ # aweth - "0xc4Ce391d82D164c166dF9c8336DDF84206b2F812" # waEthLidoWETH-waEthLidowstETH - ], -} +WATCHLIST = json.load(open("tools/python/watchlist.json")) SUBGRAPH = Subgraph() W3 = Web3( Web3.HTTPProvider( @@ -46,7 +39,7 @@ def get_user_shares_pool(pool, block): params = { "where": { "balance_gt": 0.001, - "pool": pool, + "pool": pool.lower(), }, "block": {"number": block}, } @@ -71,7 +64,7 @@ def get_user_shares_gauge(gauge, block): params = { "where": { "balance_gt": 0.001, - "gauge": gauge, + "gauge": gauge.lower(), }, "block": {"number": block}, } @@ -120,7 +113,7 @@ def build_snapshot_df( for block in pool_shares: total_shares[block] = {} for user_id in pool_shares[block]: - if user_id == gauge: + if user_id == gauge.lower(): # we do not want to count the gauge as a user continue total_shares[block][user_id] = pool_shares[block][user_id] @@ -173,22 +166,29 @@ def build_airdrop(reward_token, reward_total_wei, df): # https://docs.merkl.xyz/merkl-mechanisms/types-of-campaign/airdrop df["wei"] = df["total"] * reward_total_wei df["wei"] = df["wei"].apply(np.floor).astype(int).astype(str) + df = df[df["wei"] != "0"] return {"rewardToken": reward_token, "rewards": df[["wei"]].to_dict(orient="index")} if __name__ == "__main__": - for reward_token in WATCHLIST: - for pool in WATCHLIST[reward_token]: + for protocol in WATCHLIST: + for pool in WATCHLIST[protocol]["pools"]: # get bpt balances for a pool at different timestamps - df = build_snapshot_df(pool=pool, end=LATEST_TS) - df.to_csv("tools/python/df_snapshot.csv") + df = build_snapshot_df( + pool=WATCHLIST[protocol]["pools"][pool]["address"], end=LATEST_TS + ) # consolidate user pool shares df = consolidate_shares(df) - df.to_csv("tools/python/df_consolidated.csv") + + # stdout + print(protocol, pool) + print(df) # build airdrop object and dump to json file airdrop = build_airdrop( - reward_token=reward_token, reward_total_wei=1e18, df=df + reward_token=WATCHLIST[protocol]["reward_token"], + reward_total_wei=int(WATCHLIST[protocol]["pools"][pool]["reward_wei"]), + df=df, ) json.dump(airdrop, open(f"airdrop-{pool}.json", "w"), indent=2) diff --git a/tools/python/watchlist.json b/tools/python/watchlist.json new file mode 100644 index 000000000..b2a5e792a --- /dev/null +++ b/tools/python/watchlist.json @@ -0,0 +1,20 @@ +{ + "morpho": { + "reward_token": "0x58D97B57BB95320F9a05dC918Aef65434969c2B2", + "pools": { + "csUSDC-csUSDT": { + "address": "0x10A04efbA5B880e169920Fd4348527C64FB29d4D", + "reward_wei": "1000000000000000000" + } + } + }, + "aave": { + "reward_token": "0x030bA81f1c18d280636F32af80b9AAd02Cf0854e", + "pools": { + "waEthLidoWETH-waEthLidowstETH": { + "address": "0xc4Ce391d82D164c166dF9c8336DDF84206b2F812", + "reward_wei": "5000000000000000000" + } + } + } +} From 8fef05fdb0f0e3315b0cd684c3fbc8e8ebc1963d Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Fri, 10 Jan 2025 13:35:13 +0100 Subject: [PATCH 11/26] fix: dev branch has been merged and deleted --- tools/python/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/python/requirements.txt b/tools/python/requirements.txt index 51ede4b8e..8399af01d 100644 --- a/tools/python/requirements.txt +++ b/tools/python/requirements.txt @@ -5,7 +5,7 @@ pandas tabulate requests web3 -git+https://github.com/BalancerMaxis/bal_addresses@dev/use-bal_tools-dev-branch +git+https://github.com/BalancerMaxis/bal_addresses.git@0.9.13 dune-client pytest dataclasses-json From dcc720e20e121ea61c5e6bc747b1c087de5a00f9 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Fri, 10 Jan 2025 13:36:58 +0100 Subject: [PATCH 12/26] feat: use latest version of bal_addresses by default --- tools/python/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/python/requirements.txt b/tools/python/requirements.txt index 8399af01d..2e8faa342 100644 --- a/tools/python/requirements.txt +++ b/tools/python/requirements.txt @@ -5,7 +5,7 @@ pandas tabulate requests web3 -git+https://github.com/BalancerMaxis/bal_addresses.git@0.9.13 +git+https://github.com/BalancerMaxis/bal_addresses dune-client pytest dataclasses-json From 95f5fdc7722bb790badaaf68c268f7bda9078a9f Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Tue, 14 Jan 2025 10:22:21 +0100 Subject: [PATCH 13/26] feat: retrieve pref gauge from subgraph --- tools/python/gen_merkl_airdrops.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tools/python/gen_merkl_airdrops.py b/tools/python/gen_merkl_airdrops.py index 720af725c..7dc128781 100644 --- a/tools/python/gen_merkl_airdrops.py +++ b/tools/python/gen_merkl_airdrops.py @@ -8,7 +8,7 @@ import pandas as pd from web3 import Web3, exceptions -from bal_tools import Subgraph +from bal_tools import Subgraph, BalPoolsGauges WATCHLIST = json.load(open("tools/python/watchlist.json")) @@ -20,10 +20,6 @@ ) # dev -GAUGES = { - "0x10A04efbA5B880e169920Fd4348527C64FB29d4D": "0x5bBaeD1fADC08C5fb3e4ae3C8848777E2dA77103", - "0xc4Ce391d82D164c166dF9c8336DDF84206b2F812": "0x4B891340b51889f438a03DC0e8aAAFB0Bc89e7A6", -} LATEST_TS = int(datetime.now().timestamp()) @@ -95,8 +91,7 @@ def build_snapshot_df( n=7, # amount of snapshots step_size=60 * 60 * 24, # amount of seconds between snapshots ): - # TODO: lookup gauge from pool - gauge = GAUGES[pool] + gauge = BalPoolsGauges().get_preferential_gauge(pool) # get user shares for pool and gauge at different timestamps pool_shares = {} From 88312a58f0fe6c0ee6f86e474479af4f4f65d520 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 3 Feb 2025 10:17:34 +0100 Subject: [PATCH 14/26] feat: add airdrop checksum --- tools/python/gen_merkl_airdrops.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/python/gen_merkl_airdrops.py b/tools/python/gen_merkl_airdrops.py index 7dc128781..b5e53ad61 100644 --- a/tools/python/gen_merkl_airdrops.py +++ b/tools/python/gen_merkl_airdrops.py @@ -44,6 +44,7 @@ def get_user_shares_pool(pool, block): query, params, url="https://api.studio.thegraph.com/query/75376/balancer-v3/version/latest", + retries=5, ) return dict([(x["user"]["id"], Decimal(x["balance"])) for x in raw["poolShares"]]) @@ -159,8 +160,8 @@ def consolidate_shares(df): def build_airdrop(reward_token, reward_total_wei, df): # https://docs.merkl.xyz/merkl-mechanisms/types-of-campaign/airdrop - df["wei"] = df["total"] * reward_total_wei - df["wei"] = df["wei"].apply(np.floor).astype(int).astype(str) + df["wei"] = df["total"].map(Decimal) * Decimal(reward_total_wei) + df["wei"] = df["wei"].apply(np.floor).astype(str) df = df[df["wei"] != "0"] return {"rewardToken": reward_token, "rewards": df[["wei"]].to_dict(orient="index")} @@ -186,4 +187,15 @@ def build_airdrop(reward_token, reward_total_wei, df): reward_total_wei=int(WATCHLIST[protocol]["pools"][pool]["reward_wei"]), df=df, ) + + # checksum + total = Decimal(0) + for user in airdrop["rewards"]: + total += Decimal(airdrop["rewards"][user]["wei"]) + assert total <= Decimal(WATCHLIST[protocol]["pools"][pool]["reward_wei"]) + print( + "dust:", + Decimal(WATCHLIST[protocol]["pools"][pool]["reward_wei"]) - total, + ) + json.dump(airdrop, open(f"airdrop-{pool}.json", "w"), indent=2) From 63d5f107a015ee26d63457189bc5cb7233ebd2e3 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 3 Feb 2025 14:46:11 +0100 Subject: [PATCH 15/26] feat: automatic epoch ts calc --- tools/python/gen_merkl_airdrops.py | 50 ++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/tools/python/gen_merkl_airdrops.py b/tools/python/gen_merkl_airdrops.py index b5e53ad61..2fe1fddda 100644 --- a/tools/python/gen_merkl_airdrops.py +++ b/tools/python/gen_merkl_airdrops.py @@ -18,9 +18,17 @@ f"https://lb.drpc.org/ogrpc?network=ethereum&dkey={os.getenv('DRPC_KEY')}" ) ) +EPOCH_DURATION = 60 * 60 * 24 * 14 +FIRST_EPOCH_END = 1737936000 -# dev -LATEST_TS = int(datetime.now().timestamp()) +EPOCHS = [] +ts = FIRST_EPOCH_END +while ts < int(datetime.now().timestamp()): + EPOCHS.append(ts) + ts += EPOCH_DURATION +epoch_name = f"epoch {len(EPOCHS)}" +print("epochs:", EPOCHS) +print(epoch_name) def get_user_shares_pool(pool, block): @@ -89,15 +97,14 @@ def get_block_from_timestamp(ts): def build_snapshot_df( pool, # pool address end, # timestamp of the last snapshot - n=7, # amount of snapshots - step_size=60 * 60 * 24, # amount of seconds between snapshots + step_size=60 * 60 * 8, # amount of seconds between snapshots ): gauge = BalPoolsGauges().get_preferential_gauge(pool) # get user shares for pool and gauge at different timestamps pool_shares = {} gauge_shares = {} - for _ in range(n): + while end > end - EPOCH_DURATION: block = get_block_from_timestamp(end) pool_shares[block] = get_user_shares_pool(pool=pool, block=block) gauge_shares[block] = get_user_shares_gauge(gauge=gauge, block=block) @@ -160,42 +167,53 @@ def consolidate_shares(df): def build_airdrop(reward_token, reward_total_wei, df): # https://docs.merkl.xyz/merkl-mechanisms/types-of-campaign/airdrop - df["wei"] = df["total"].map(Decimal) * Decimal(reward_total_wei) - df["wei"] = df["wei"].apply(np.floor).astype(str) - df = df[df["wei"] != "0"] - return {"rewardToken": reward_token, "rewards": df[["wei"]].to_dict(orient="index")} + df[epoch_name] = df["total"].map(Decimal) * Decimal(reward_total_wei) + df[epoch_name] = df[epoch_name].apply(np.floor).astype(str) + df = df[df[epoch_name] != "0"] + return { + "rewardToken": reward_token, + "rewards": df[[epoch_name]].to_dict(orient="index"), + } if __name__ == "__main__": for protocol in WATCHLIST: for pool in WATCHLIST[protocol]["pools"]: + print(protocol, pool) + # get bpt balances for a pool at different timestamps df = build_snapshot_df( - pool=WATCHLIST[protocol]["pools"][pool]["address"], end=LATEST_TS + pool=WATCHLIST[protocol]["pools"][pool]["address"], end=EPOCHS[-1] ) # consolidate user pool shares df = consolidate_shares(df) - - # stdout - print(protocol, pool) print(df) + # morpho takes a 50bips fee on json airdrops + if protocol == "morpho": + reward_total_wei = int( + Decimal(WATCHLIST[protocol]["pools"][pool]["reward_wei"]) + / Decimal(1 + 0.005) + ) + else: + reward_total_wei = int(WATCHLIST[protocol]["pools"][pool]["reward_wei"]) + # build airdrop object and dump to json file airdrop = build_airdrop( reward_token=WATCHLIST[protocol]["reward_token"], - reward_total_wei=int(WATCHLIST[protocol]["pools"][pool]["reward_wei"]), + reward_total_wei=reward_total_wei, df=df, ) # checksum total = Decimal(0) for user in airdrop["rewards"]: - total += Decimal(airdrop["rewards"][user]["wei"]) + total += Decimal(airdrop["rewards"][user][epoch_name]) assert total <= Decimal(WATCHLIST[protocol]["pools"][pool]["reward_wei"]) print( "dust:", - Decimal(WATCHLIST[protocol]["pools"][pool]["reward_wei"]) - total, + Decimal(reward_total_wei) - total, ) json.dump(airdrop, open(f"airdrop-{pool}.json", "w"), indent=2) From 7c296163e47ded90bc6b8511f7a2efd92f5dfd53 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 3 Feb 2025 15:03:46 +0100 Subject: [PATCH 16/26] fix: determine start before loop --- tools/python/gen_merkl_airdrops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/python/gen_merkl_airdrops.py b/tools/python/gen_merkl_airdrops.py index 2fe1fddda..604091472 100644 --- a/tools/python/gen_merkl_airdrops.py +++ b/tools/python/gen_merkl_airdrops.py @@ -104,7 +104,8 @@ def build_snapshot_df( # get user shares for pool and gauge at different timestamps pool_shares = {} gauge_shares = {} - while end > end - EPOCH_DURATION: + start = end - EPOCH_DURATION + while end > start: block = get_block_from_timestamp(end) pool_shares[block] = get_user_shares_pool(pool=pool, block=block) gauge_shares[block] = get_user_shares_gauge(gauge=gauge, block=block) From c1d4e321f0543f4912a1e38936fcd8aa98f441bf Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 3 Feb 2025 15:41:29 +0100 Subject: [PATCH 17/26] fix: morpho fee need multiplication --- tools/python/gen_merkl_airdrops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/python/gen_merkl_airdrops.py b/tools/python/gen_merkl_airdrops.py index 604091472..fd8ddea04 100644 --- a/tools/python/gen_merkl_airdrops.py +++ b/tools/python/gen_merkl_airdrops.py @@ -195,7 +195,7 @@ def build_airdrop(reward_token, reward_total_wei, df): if protocol == "morpho": reward_total_wei = int( Decimal(WATCHLIST[protocol]["pools"][pool]["reward_wei"]) - / Decimal(1 + 0.005) + * Decimal(1 - 0.005) ) else: reward_total_wei = int(WATCHLIST[protocol]["pools"][pool]["reward_wei"]) From 2341c27a17c952b98d109123bb224547183be54e Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 3 Feb 2025 15:53:59 +0100 Subject: [PATCH 18/26] chore: update morpho reward wei with real amount --- ...list.json => gen_merkl_airdrop_watchlist.json} | 2 +- tools/python/gen_merkl_airdrops.py | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) rename tools/python/{watchlist.json => gen_merkl_airdrop_watchlist.json} (90%) diff --git a/tools/python/watchlist.json b/tools/python/gen_merkl_airdrop_watchlist.json similarity index 90% rename from tools/python/watchlist.json rename to tools/python/gen_merkl_airdrop_watchlist.json index b2a5e792a..b0c51805e 100644 --- a/tools/python/watchlist.json +++ b/tools/python/gen_merkl_airdrop_watchlist.json @@ -4,7 +4,7 @@ "pools": { "csUSDC-csUSDT": { "address": "0x10A04efbA5B880e169920Fd4348527C64FB29d4D", - "reward_wei": "1000000000000000000" + "reward_wei": "1410348263937287967617" } } }, diff --git a/tools/python/gen_merkl_airdrops.py b/tools/python/gen_merkl_airdrops.py index fd8ddea04..ef2c267b3 100644 --- a/tools/python/gen_merkl_airdrops.py +++ b/tools/python/gen_merkl_airdrops.py @@ -11,7 +11,7 @@ from bal_tools import Subgraph, BalPoolsGauges -WATCHLIST = json.load(open("tools/python/watchlist.json")) +WATCHLIST = json.load(open("tools/python/gen_merkl_airdrop_watchlist.json")) SUBGRAPH = Subgraph() W3 = Web3( Web3.HTTPProvider( @@ -26,7 +26,7 @@ while ts < int(datetime.now().timestamp()): EPOCHS.append(ts) ts += EPOCH_DURATION -epoch_name = f"epoch {len(EPOCHS)}" +epoch_name = f"epoch_{len(EPOCHS)}" print("epochs:", EPOCHS) print(epoch_name) @@ -179,6 +179,9 @@ def build_airdrop(reward_token, reward_total_wei, df): if __name__ == "__main__": for protocol in WATCHLIST: + # TODO: aave not implemented yet + if protocol == "aave": + break for pool in WATCHLIST[protocol]["pools"]: print(protocol, pool) @@ -217,4 +220,10 @@ def build_airdrop(reward_token, reward_total_wei, df): Decimal(reward_total_wei) - total, ) - json.dump(airdrop, open(f"airdrop-{pool}.json", "w"), indent=2) + json.dump( + airdrop, + open( + f"MaxiOps/merkl/airdrops/{protocol}-{pool}-{epoch_name}.json", "w" + ), + indent=2, + ) From 8e75bfaba7a32192a0df4a840c595800c5936ab8 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 3 Feb 2025 15:54:14 +0100 Subject: [PATCH 19/26] chore: upload airdrop artifact --- .../morpho-csUSDC-csUSDT-epoch_0.json | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json diff --git a/MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json b/MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json new file mode 100644 index 000000000..77e6382f1 --- /dev/null +++ b/MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json @@ -0,0 +1,23 @@ +{ + "rewardToken": "0x58D97B57BB95320F9a05dC918Aef65434969c2B2", + "rewards": { + "0x027917095a4d4964eff7280676f405126bf9a6b5": { + "epoch_0": "5786896850296901" + }, + "0x4bc8121278056bfcac2bd14d455659224d6ddf48": { + "epoch_0": "19329447678395428" + }, + "0x977767f401fc909614905857c9174bd08af02363": { + "epoch_0": "1413141904207619" + }, + "0xd2525cd44dd450627cec73754f97378e417f6c2e": { + "epoch_0": "3868510996392043" + }, + "0xe01c417384a1e7f2e11393baa52f721d2e95d1aa": { + "epoch_0": "1403163766237861576414" + }, + "0x854b004700885a61107b458f11ecc169a019b764": { + "epoch_0": "102358382310414949" + } + } +} \ No newline at end of file From 94f693daa8f936ba7c57339edefeba4f6676085d Mon Sep 17 00:00:00 2001 From: gosuto-inzasheru Date: Mon, 3 Feb 2025 14:55:41 +0000 Subject: [PATCH 20/26] Automated processing of Payload PR (validations, transformations, and reports) + reformat JSON --- MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json b/MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json index 77e6382f1..596b28781 100644 --- a/MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json +++ b/MaxiOps/merkl/airdrops/morpho-csUSDC-csUSDT-epoch_0.json @@ -20,4 +20,4 @@ "epoch_0": "102358382310414949" } } -} \ No newline at end of file +} From d3112945f1610c10af069ceb1287f68e0fa1ff58 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 3 Feb 2025 16:00:44 +0100 Subject: [PATCH 21/26] chore: add payload for epoch 0 --- MaxiOps/merkl/payloads/merkl-epoch_0.json | 124 ++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 MaxiOps/merkl/payloads/merkl-epoch_0.json diff --git a/MaxiOps/merkl/payloads/merkl-epoch_0.json b/MaxiOps/merkl/payloads/merkl-epoch_0.json new file mode 100644 index 000000000..dc5fc3888 --- /dev/null +++ b/MaxiOps/merkl/payloads/merkl-epoch_0.json @@ -0,0 +1,124 @@ +{ + "version": "1.0", + "chainId": "1", + "createdAt": 1738594743, + "meta": { + "name": "Create Merkl Campaign", + "txBuilderVersion": "1.16.3", + "createdFromSafeAddress": "0x9ff471F9f98F42E5151C7855fD1b5aa906b1AF7e" + }, + "transactions": [ + { + "to": "0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077", + "value": "0", + "data": null, + "contractMethod": { + "inputs": [ + { "internalType": "bytes[]", "name": "data", "type": "bytes[]" } + ], + "name": "multicall", + "payable": true + }, + "contractInputsValues": { + "data": "[0x6b89026a000000000000000000000000330eefa8a787552dc5cad3c3ca644844b1e61ddb0000000000000000000000009ff471f9f98f42e5151c7855fd1b5aa906b1af7e00000000000000000000000058d97b57bb95320f9a05dc918aef65434969c2b200000000000000000000000000000000000000000000004c748391b4a2e8d38100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000eea2e120be5d26eee545e8535fccb5ea38f2340b502263927520adc30d7b9744d57e8ed75beee9c9ed92f47281a47b6efde7881ff35c39746de9b1b4b760457bd5cea201b922bdd85e98a7aa6e41bfa2b35490c4f35ac002acbc7611e499641fda1675d6f40dbacba5bd634b77d8c5350b935b121e8034ff94cd54cb560ea43574883beda04be8b8c91be901a111ee3bbeeca4b3e234e55b74fd15eb60be000bdc40443901247232d54e158ac337213593c694e01d512c50ac1825afd86c7a04f0ee6b3d000af89e2079c54e5392a4b2e9764a67280bf7723974d2aabcb6a234ed66388c926aeb083ef314a54d346a7fadf42fed4351d540d4a7e97bbd6105e69d8b447d2ade6278cf85592dbc41d1a08e1e968c287ffbc45dbc279a22fe6f5ef0f2f6320f481f1f7cce40bad9b86bea2f56ec5de68912646ee84d283f1f796826641b15795a0d6f5abd084a938f9bc02439d6c9e0e63e6a464c654719ea48e111e632b50acbdfd8e3b6d96b135812ff2643e67fe054b001c985e1687e7e5ce07f33bf03f812a39b88029cc602b6f3748abd014c5f06c6f76cd2b89d182188be0195d6d3e28c15340eea5a3e8cc70aff7dbfb39bc1ba5cc698e2577265875dbdb]" + } + }, + { + "to": "0x58D97B57BB95320F9a05dC918Aef65434969c2B2", + "value": "0", + "data": null, + "contractMethod": { + "inputs": [ + { + "name": "spender", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + } + ], + "name": "approve", + "payable": false + }, + "contractInputsValues": { + "spender": "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd", + "amount": "1410348263937287721964" + } + }, + { + "to": "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd", + "value": "0", + "data": null, + "contractMethod": { + "inputs": [], + "name": "acceptConditions", + "payable": false + }, + "contractInputsValues": null + }, + { + "to": "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd", + "value": "0", + "data": null, + "contractMethod": { + "inputs": [ + { + "components": [ + { + "internalType": "bytes32", + "name": "campaignId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "creator", + "type": "address" + }, + { + "internalType": "address", + "name": "rewardToken", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "campaignType", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "startTimestamp", + "type": "uint32" + }, + { + "internalType": "uint32", + "name": "duration", + "type": "uint32" + }, + { + "internalType": "bytes", + "name": "campaignData", + "type": "bytes" + } + ], + "internalType": "struct CampaignParameters", + "name": "newCampaign", + "type": "tuple" + } + ], + "name": "createCampaign", + "payable": false + }, + "contractInputsValues": { + "newCampaign": "[\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"0x0000000000000000000000000000000000000000\",\"0x58D97B57BB95320F9a05dC918Aef65434969c2B2\",\"1410348263937287721964\",4,1738594800,3600,\"0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d527538616f33535a41524a6d6e654e7a39467172334535536667777a41313759315534324579447962585047000000000000000000000000000000000000000000000000000000000000000000000000000000000000166373555344432d6373555344543a3a65706f63685f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"]" + } + } + ] +} From ba30b4cfd4cfe03157077fd537e6fbdecaba174b Mon Sep 17 00:00:00 2001 From: gosuto-inzasheru Date: Mon, 3 Feb 2025 15:02:16 +0000 Subject: [PATCH 22/26] Automated processing of Payload PR (validations, transformations, and reports) + reformat JSON --- .../merkl/payloads/merkl-epoch_0.report.txt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 MaxiOps/merkl/payloads/merkl-epoch_0.report.txt diff --git a/MaxiOps/merkl/payloads/merkl-epoch_0.report.txt b/MaxiOps/merkl/payloads/merkl-epoch_0.report.txt new file mode 100644 index 000000000..6f9a9aa76 --- /dev/null +++ b/MaxiOps/merkl/payloads/merkl-epoch_0.report.txt @@ -0,0 +1,38 @@ +FILENAME: `MaxiOps/merkl/payloads/merkl-epoch_0.json` +MULTISIG: `multisigs/maxi_omni (mainnet:0x9ff471F9f98F42E5151C7855fD1b5aa906b1AF7e)` +COMMIT: `d3112945f1610c10af069ceb1287f68e0fa1ff58` +CHAIN(S): `mainnet` +TENDERLY: `🟪 SKIPPED (Web3ValidationError("\nCould not identify the intended function with name `createCampaign`, positional arguments with type(s) `((str,address,address,int,int,int,int,str))` and keyword arguments with type(s) `{}`.\nFound 1 function(s) with the name `createCampaign`: ['createCampaign((bytes32,address,address,uint256,uint32,uint32,uint32,bytes))']\nFunction invocation failed due to no matching argument types."))` + +``` ++------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ +| fx_name | to | value | inputs | bip_number | tx_index | ++------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ +| multicall | 0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077 (Not Found) | 0 | { | N/A | N/A | +| | | | "data": [ | | | +| | | | "0x6b89026a000000000000000000000000330eefa8a787552dc5cad3c3ca644844b1e61ddb0000000000000000000000009ff471f9f98f42e5151c7855fd1b5aa906b1af7e00000000000000000000000058d97b57bb95320f9a05dc918aef65434969c2b200000000000000000000000000000000000000000000004c748391b4a2e8d38100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000eea2e120be5d26eee545e8535fccb5ea38f2340b502263927520adc30d7b9744d57e8ed75beee9c9ed92f47281a47b6efde7881ff35c39746de9b1b4b760457bd5cea201b922bdd85e98a7aa6e41bfa2b35490c4f35ac002acbc7611e499641fda1675d6f40dbacba5bd634b77d8c5350b935b121e8034ff94cd54cb560ea43574883beda04be8b8c91be901a111ee3bbeeca4b3e234e55b74fd15eb60be000bdc40443901247232d54e158ac337213593c694e01d512c50ac1825afd86c7a04f0ee6b3d000af89e2079c54e5392a4b2e9764a67280bf7723974d2aabcb6a234ed66388c926aeb083ef314a54d346a7fadf42fed4351d540d4a7e97bbd6105e69d8b447d2ade6278cf85592dbc41d1a08e1e968c287ffbc45dbc279a22fe6f5ef0f2f6320f481f1f7cce40bad9b86bea2f56ec5de68912646ee84d283f1f796826641b15795a0d6f5abd084a938f9bc02439d6c9e0e63e6a464c654719ea48e111e632b50acbdfd8e3b6d96b135812ff2643e67fe054b001c985e1687e7e5ce07f33bf03f812a39b88029cc602b6f3748abd014c5f06c6f76cd2b89d182188be0195d6d3e28c15340eea5a3e8cc70aff7dbfb39bc1ba5cc698e2577265875dbdb" | | | +| | | | ] | | | +| | | | } | | | +| approve | 0x58D97B57BB95320F9a05dC918Aef65434969c2B2 (Not Found) | 0 | { | N/A | N/A | +| | | | "spender": [ | | | +| | | | "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (N/A)" | | | +| | | | ], | | | +| | | | "amount": [ | | | +| | | | "raw:1410348263937287721964, 18 decimals:1410.348263937287721964, 6 decimals: 1410348263937287.721964" | | | +| | | | ] | | | +| | | | } | | | +| acceptConditions | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (Not Found) | 0 | "N/A" | N/A | N/A | +| createCampaign | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (Not Found) | 0 | { | N/A | N/A | +| | | | "newCampaign": [ | | | +| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000000\"", | | | +| | | | "\"0x0000000000000000000000000000000000000000\"", | | | +| | | | "\"0x58D97B57BB95320F9a05dC918Aef65434969c2B2\"", | | | +| | | | "\"1410348263937287721964\"", | | | +| | | | "4", | | | +| | | | "1738594800", | | | +| | | | "3600", | | | +| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d527538616f33535a41524a6d6e654e7a39467172334535536667777a41313759315534324579447962585047000000000000000000000000000000000000000000000000000000000000000000000000000000000000166373555344432d6373555344543a3a65706f63685f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"" | | | +| | | | ] | | | +| | | | } | | | ++------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ +``` From 42cc31ee012b8bcc9ef1fcd30a52ccd0941d32a0 Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Mon, 3 Feb 2025 16:18:27 +0100 Subject: [PATCH 23/26] docs: add instructions for running script --- .../merkl/payloads/merkl-epoch_0.report.txt | 38 ------------------- ...merkl-epoch_0.json => morpho-epoch_0.json} | 0 tools/python/gen_merkl_airdrops.md | 23 +++++++++++ ...json => gen_merkl_airdrops_watchlist.json} | 0 4 files changed, 23 insertions(+), 38 deletions(-) delete mode 100644 MaxiOps/merkl/payloads/merkl-epoch_0.report.txt rename MaxiOps/merkl/payloads/{merkl-epoch_0.json => morpho-epoch_0.json} (100%) create mode 100644 tools/python/gen_merkl_airdrops.md rename tools/python/{gen_merkl_airdrop_watchlist.json => gen_merkl_airdrops_watchlist.json} (100%) diff --git a/MaxiOps/merkl/payloads/merkl-epoch_0.report.txt b/MaxiOps/merkl/payloads/merkl-epoch_0.report.txt deleted file mode 100644 index 6f9a9aa76..000000000 --- a/MaxiOps/merkl/payloads/merkl-epoch_0.report.txt +++ /dev/null @@ -1,38 +0,0 @@ -FILENAME: `MaxiOps/merkl/payloads/merkl-epoch_0.json` -MULTISIG: `multisigs/maxi_omni (mainnet:0x9ff471F9f98F42E5151C7855fD1b5aa906b1AF7e)` -COMMIT: `d3112945f1610c10af069ceb1287f68e0fa1ff58` -CHAIN(S): `mainnet` -TENDERLY: `🟪 SKIPPED (Web3ValidationError("\nCould not identify the intended function with name `createCampaign`, positional arguments with type(s) `((str,address,address,int,int,int,int,str))` and keyword arguments with type(s) `{}`.\nFound 1 function(s) with the name `createCampaign`: ['createCampaign((bytes32,address,address,uint256,uint32,uint32,uint32,bytes))']\nFunction invocation failed due to no matching argument types."))` - -``` -+------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ -| fx_name | to | value | inputs | bip_number | tx_index | -+------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ -| multicall | 0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077 (Not Found) | 0 | { | N/A | N/A | -| | | | "data": [ | | | -| | | | "0x6b89026a000000000000000000000000330eefa8a787552dc5cad3c3ca644844b1e61ddb0000000000000000000000009ff471f9f98f42e5151c7855fd1b5aa906b1af7e00000000000000000000000058d97b57bb95320f9a05dc918aef65434969c2b200000000000000000000000000000000000000000000004c748391b4a2e8d38100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000eea2e120be5d26eee545e8535fccb5ea38f2340b502263927520adc30d7b9744d57e8ed75beee9c9ed92f47281a47b6efde7881ff35c39746de9b1b4b760457bd5cea201b922bdd85e98a7aa6e41bfa2b35490c4f35ac002acbc7611e499641fda1675d6f40dbacba5bd634b77d8c5350b935b121e8034ff94cd54cb560ea43574883beda04be8b8c91be901a111ee3bbeeca4b3e234e55b74fd15eb60be000bdc40443901247232d54e158ac337213593c694e01d512c50ac1825afd86c7a04f0ee6b3d000af89e2079c54e5392a4b2e9764a67280bf7723974d2aabcb6a234ed66388c926aeb083ef314a54d346a7fadf42fed4351d540d4a7e97bbd6105e69d8b447d2ade6278cf85592dbc41d1a08e1e968c287ffbc45dbc279a22fe6f5ef0f2f6320f481f1f7cce40bad9b86bea2f56ec5de68912646ee84d283f1f796826641b15795a0d6f5abd084a938f9bc02439d6c9e0e63e6a464c654719ea48e111e632b50acbdfd8e3b6d96b135812ff2643e67fe054b001c985e1687e7e5ce07f33bf03f812a39b88029cc602b6f3748abd014c5f06c6f76cd2b89d182188be0195d6d3e28c15340eea5a3e8cc70aff7dbfb39bc1ba5cc698e2577265875dbdb" | | | -| | | | ] | | | -| | | | } | | | -| approve | 0x58D97B57BB95320F9a05dC918Aef65434969c2B2 (Not Found) | 0 | { | N/A | N/A | -| | | | "spender": [ | | | -| | | | "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (N/A)" | | | -| | | | ], | | | -| | | | "amount": [ | | | -| | | | "raw:1410348263937287721964, 18 decimals:1410.348263937287721964, 6 decimals: 1410348263937287.721964" | | | -| | | | ] | | | -| | | | } | | | -| acceptConditions | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (Not Found) | 0 | "N/A" | N/A | N/A | -| createCampaign | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (Not Found) | 0 | { | N/A | N/A | -| | | | "newCampaign": [ | | | -| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000000\"", | | | -| | | | "\"0x0000000000000000000000000000000000000000\"", | | | -| | | | "\"0x58D97B57BB95320F9a05dC918Aef65434969c2B2\"", | | | -| | | | "\"1410348263937287721964\"", | | | -| | | | "4", | | | -| | | | "1738594800", | | | -| | | | "3600", | | | -| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d527538616f33535a41524a6d6e654e7a39467172334535536667777a41313759315534324579447962585047000000000000000000000000000000000000000000000000000000000000000000000000000000000000166373555344432d6373555344543a3a65706f63685f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"" | | | -| | | | ] | | | -| | | | } | | | -+------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ -``` diff --git a/MaxiOps/merkl/payloads/merkl-epoch_0.json b/MaxiOps/merkl/payloads/morpho-epoch_0.json similarity index 100% rename from MaxiOps/merkl/payloads/merkl-epoch_0.json rename to MaxiOps/merkl/payloads/morpho-epoch_0.json diff --git a/tools/python/gen_merkl_airdrops.md b/tools/python/gen_merkl_airdrops.md new file mode 100644 index 000000000..f710aadce --- /dev/null +++ b/tools/python/gen_merkl_airdrops.md @@ -0,0 +1,23 @@ +# `gen_merkl_airdrops.md` + +## Morpho + +### Step 1: Claim Rewards via Omni Msig + +Either use claim rewards button on the [Safe app](https://app.safe.global/apps/open?safe=eth:0x9ff471F9f98F42E5151C7855fD1b5aa906b1AF7e&appUrl=https%3A%2F%2Fsafe-app.morpho.org) to capture payload or grab `tx_data` from https://rewards.morpho.org/v1/users/0x9ff471F9f98F42E5151C7855fD1b5aa906b1AF7e/distributions and call `0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077.multicall([])` + +### Step 2: Configure Watchlist + +Adjust the `reward_wei` in `gen_merkl_airdrops_watchlist.json` to match the claimable amount from step 1. + +### Step 3: Determine User Shares + +Run `python tools/python/gen_merkl_airdrops.py` and upload the airdrop json artifact to https://merkl.angle.money/create/drop. Click `Preview Transaction` -> `Build a payload` -> `Download Safe Payload`. + +### Step 4: Consolidate both Payloads + +Add the `multicall` transaction from step 1 to the payload json artifact downloaded in step 3, as the first transaction. + +### Rename Final Payload and Upload + +Rename final payload to `morpho-epoch_<#>.json` and place in `MaxiOps/merkl/payloads/`. diff --git a/tools/python/gen_merkl_airdrop_watchlist.json b/tools/python/gen_merkl_airdrops_watchlist.json similarity index 100% rename from tools/python/gen_merkl_airdrop_watchlist.json rename to tools/python/gen_merkl_airdrops_watchlist.json From 40adbfa655649e49af3b87d413913282000fc8ce Mon Sep 17 00:00:00 2001 From: gosuto-inzasheru Date: Mon, 3 Feb 2025 15:20:07 +0000 Subject: [PATCH 24/26] Automated processing of Payload PR (validations, transformations, and reports) + reformat JSON --- .../merkl/payloads/morpho-epoch_0.report.txt | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 MaxiOps/merkl/payloads/morpho-epoch_0.report.txt diff --git a/MaxiOps/merkl/payloads/morpho-epoch_0.report.txt b/MaxiOps/merkl/payloads/morpho-epoch_0.report.txt new file mode 100644 index 000000000..df63e75b3 --- /dev/null +++ b/MaxiOps/merkl/payloads/morpho-epoch_0.report.txt @@ -0,0 +1,38 @@ +FILENAME: `MaxiOps/merkl/payloads/morpho-epoch_0.json` +MULTISIG: `multisigs/maxi_omni (mainnet:0x9ff471F9f98F42E5151C7855fD1b5aa906b1AF7e)` +COMMIT: `42cc31ee012b8bcc9ef1fcd30a52ccd0941d32a0` +CHAIN(S): `mainnet` +TENDERLY: `🟪 SKIPPED (Web3ValidationError("\nCould not identify the intended function with name `createCampaign`, positional arguments with type(s) `((str,address,address,int,int,int,int,str))` and keyword arguments with type(s) `{}`.\nFound 1 function(s) with the name `createCampaign`: ['createCampaign((bytes32,address,address,uint256,uint32,uint32,uint32,bytes))']\nFunction invocation failed due to no matching argument types."))` + +``` ++------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ +| fx_name | to | value | inputs | bip_number | tx_index | ++------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ +| multicall | 0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077 (Not Found) | 0 | { | N/A | N/A | +| | | | "data": [ | | | +| | | | "0x6b89026a000000000000000000000000330eefa8a787552dc5cad3c3ca644844b1e61ddb0000000000000000000000009ff471f9f98f42e5151c7855fd1b5aa906b1af7e00000000000000000000000058d97b57bb95320f9a05dc918aef65434969c2b200000000000000000000000000000000000000000000004c748391b4a2e8d38100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000eea2e120be5d26eee545e8535fccb5ea38f2340b502263927520adc30d7b9744d57e8ed75beee9c9ed92f47281a47b6efde7881ff35c39746de9b1b4b760457bd5cea201b922bdd85e98a7aa6e41bfa2b35490c4f35ac002acbc7611e499641fda1675d6f40dbacba5bd634b77d8c5350b935b121e8034ff94cd54cb560ea43574883beda04be8b8c91be901a111ee3bbeeca4b3e234e55b74fd15eb60be000bdc40443901247232d54e158ac337213593c694e01d512c50ac1825afd86c7a04f0ee6b3d000af89e2079c54e5392a4b2e9764a67280bf7723974d2aabcb6a234ed66388c926aeb083ef314a54d346a7fadf42fed4351d540d4a7e97bbd6105e69d8b447d2ade6278cf85592dbc41d1a08e1e968c287ffbc45dbc279a22fe6f5ef0f2f6320f481f1f7cce40bad9b86bea2f56ec5de68912646ee84d283f1f796826641b15795a0d6f5abd084a938f9bc02439d6c9e0e63e6a464c654719ea48e111e632b50acbdfd8e3b6d96b135812ff2643e67fe054b001c985e1687e7e5ce07f33bf03f812a39b88029cc602b6f3748abd014c5f06c6f76cd2b89d182188be0195d6d3e28c15340eea5a3e8cc70aff7dbfb39bc1ba5cc698e2577265875dbdb" | | | +| | | | ] | | | +| | | | } | | | +| approve | 0x58D97B57BB95320F9a05dC918Aef65434969c2B2 (Not Found) | 0 | { | N/A | N/A | +| | | | "spender": [ | | | +| | | | "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (N/A)" | | | +| | | | ], | | | +| | | | "amount": [ | | | +| | | | "raw:1410348263937287721964, 18 decimals:1410.348263937287721964, 6 decimals: 1410348263937287.721964" | | | +| | | | ] | | | +| | | | } | | | +| acceptConditions | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (Not Found) | 0 | "N/A" | N/A | N/A | +| createCampaign | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (Not Found) | 0 | { | N/A | N/A | +| | | | "newCampaign": [ | | | +| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000000\"", | | | +| | | | "\"0x0000000000000000000000000000000000000000\"", | | | +| | | | "\"0x58D97B57BB95320F9a05dC918Aef65434969c2B2\"", | | | +| | | | "\"1410348263937287721964\"", | | | +| | | | "4", | | | +| | | | "1738594800", | | | +| | | | "3600", | | | +| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d527538616f33535a41524a6d6e654e7a39467172334535536667777a41313759315534324579447962585047000000000000000000000000000000000000000000000000000000000000000000000000000000000000166373555344432d6373555344543a3a65706f63685f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"" | | | +| | | | ] | | | +| | | | } | | | ++------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ +``` From 9606694c706977ef03334a8b5135a32a1e5c0a1f Mon Sep 17 00:00:00 2001 From: Gosuto Inzasheru Date: Tue, 4 Feb 2025 12:19:43 +0100 Subject: [PATCH 25/26] perf: let bal tools determine blocks subgraph url --- tools/python/.env.template | 3 ++- tools/python/gen_merkl_airdrops.py | 9 ++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tools/python/.env.template b/tools/python/.env.template index 760ff3d84..68eec41d6 100644 --- a/tools/python/.env.template +++ b/tools/python/.env.template @@ -1,2 +1,3 @@ DUNE_API_KEY= -DRPC_KEY= \ No newline at end of file +DRPC_KEY= +GRAPH_API_KEY= diff --git a/tools/python/gen_merkl_airdrops.py b/tools/python/gen_merkl_airdrops.py index ef2c267b3..a1a3fc1d0 100644 --- a/tools/python/gen_merkl_airdrops.py +++ b/tools/python/gen_merkl_airdrops.py @@ -11,7 +11,7 @@ from bal_tools import Subgraph, BalPoolsGauges -WATCHLIST = json.load(open("tools/python/gen_merkl_airdrop_watchlist.json")) +WATCHLIST = json.load(open("tools/python/gen_merkl_airdrops_watchlist.json")) SUBGRAPH = Subgraph() W3 = Web3( Web3.HTTPProvider( @@ -85,12 +85,7 @@ def get_block_from_timestamp(ts): } }""" params = {"where": {"timestamp_lte": ts}} - raw = SUBGRAPH.fetch_graphql_data( - "blocks", - query, - params, - url="https://api.studio.thegraph.com/query/48427/ethereum-blocks/version/latest", - ) + raw = SUBGRAPH.fetch_graphql_data("blocks", query, params) return int(raw["blocks"][0]["number"]) From 8786900ac99d52f68c0b9e823cc479f152e8056d Mon Sep 17 00:00:00 2001 From: gosuto-inzasheru Date: Tue, 4 Feb 2025 11:43:59 +0000 Subject: [PATCH 26/26] Automated processing of Payload PR (validations, transformations, and reports) + reformat JSON --- .../merkl/payloads/morpho-epoch_0.report.txt | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/MaxiOps/merkl/payloads/morpho-epoch_0.report.txt b/MaxiOps/merkl/payloads/morpho-epoch_0.report.txt index df63e75b3..32665d421 100644 --- a/MaxiOps/merkl/payloads/morpho-epoch_0.report.txt +++ b/MaxiOps/merkl/payloads/morpho-epoch_0.report.txt @@ -1,38 +1,38 @@ FILENAME: `MaxiOps/merkl/payloads/morpho-epoch_0.json` MULTISIG: `multisigs/maxi_omni (mainnet:0x9ff471F9f98F42E5151C7855fD1b5aa906b1AF7e)` -COMMIT: `42cc31ee012b8bcc9ef1fcd30a52ccd0941d32a0` +COMMIT: `9606694c706977ef03334a8b5135a32a1e5c0a1f` CHAIN(S): `mainnet` TENDERLY: `🟪 SKIPPED (Web3ValidationError("\nCould not identify the intended function with name `createCampaign`, positional arguments with type(s) `((str,address,address,int,int,int,int,str))` and keyword arguments with type(s) `{}`.\nFound 1 function(s) with the name `createCampaign`: ['createCampaign((bytes32,address,address,uint256,uint32,uint32,uint32,bytes))']\nFunction invocation failed due to no matching argument types."))` ``` -+------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ -| fx_name | to | value | inputs | bip_number | tx_index | -+------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ -| multicall | 0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077 (Not Found) | 0 | { | N/A | N/A | -| | | | "data": [ | | | -| | | | "0x6b89026a000000000000000000000000330eefa8a787552dc5cad3c3ca644844b1e61ddb0000000000000000000000009ff471f9f98f42e5151c7855fd1b5aa906b1af7e00000000000000000000000058d97b57bb95320f9a05dc918aef65434969c2b200000000000000000000000000000000000000000000004c748391b4a2e8d38100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000eea2e120be5d26eee545e8535fccb5ea38f2340b502263927520adc30d7b9744d57e8ed75beee9c9ed92f47281a47b6efde7881ff35c39746de9b1b4b760457bd5cea201b922bdd85e98a7aa6e41bfa2b35490c4f35ac002acbc7611e499641fda1675d6f40dbacba5bd634b77d8c5350b935b121e8034ff94cd54cb560ea43574883beda04be8b8c91be901a111ee3bbeeca4b3e234e55b74fd15eb60be000bdc40443901247232d54e158ac337213593c694e01d512c50ac1825afd86c7a04f0ee6b3d000af89e2079c54e5392a4b2e9764a67280bf7723974d2aabcb6a234ed66388c926aeb083ef314a54d346a7fadf42fed4351d540d4a7e97bbd6105e69d8b447d2ade6278cf85592dbc41d1a08e1e968c287ffbc45dbc279a22fe6f5ef0f2f6320f481f1f7cce40bad9b86bea2f56ec5de68912646ee84d283f1f796826641b15795a0d6f5abd084a938f9bc02439d6c9e0e63e6a464c654719ea48e111e632b50acbdfd8e3b6d96b135812ff2643e67fe054b001c985e1687e7e5ce07f33bf03f812a39b88029cc602b6f3748abd014c5f06c6f76cd2b89d182188be0195d6d3e28c15340eea5a3e8cc70aff7dbfb39bc1ba5cc698e2577265875dbdb" | | | -| | | | ] | | | -| | | | } | | | -| approve | 0x58D97B57BB95320F9a05dC918Aef65434969c2B2 (Not Found) | 0 | { | N/A | N/A | -| | | | "spender": [ | | | -| | | | "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (N/A)" | | | -| | | | ], | | | -| | | | "amount": [ | | | -| | | | "raw:1410348263937287721964, 18 decimals:1410.348263937287721964, 6 decimals: 1410348263937287.721964" | | | -| | | | ] | | | -| | | | } | | | -| acceptConditions | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (Not Found) | 0 | "N/A" | N/A | N/A | -| createCampaign | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (Not Found) | 0 | { | N/A | N/A | -| | | | "newCampaign": [ | | | -| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000000\"", | | | -| | | | "\"0x0000000000000000000000000000000000000000\"", | | | -| | | | "\"0x58D97B57BB95320F9a05dC918Aef65434969c2B2\"", | | | -| | | | "\"1410348263937287721964\"", | | | -| | | | "4", | | | -| | | | "1738594800", | | | -| | | | "3600", | | | -| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d527538616f33535a41524a6d6e654e7a39467172334535536667777a41313759315534324579447962585047000000000000000000000000000000000000000000000000000000000000000000000000000000000000166373555344432d6373555344543a3a65706f63685f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"" | | | -| | | | ] | | | -| | | | } | | | -+------------------+--------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ ++------------------+-------------------------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ +| fx_name | to | value | inputs | bip_number | tx_index | ++------------------+-------------------------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ +| multicall | 0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077 (morpho/eth_bundler_v2) | 0 | { | N/A | N/A | +| | | | "data": [ | | | +| | | | "0x6b89026a000000000000000000000000330eefa8a787552dc5cad3c3ca644844b1e61ddb0000000000000000000000009ff471f9f98f42e5151c7855fd1b5aa906b1af7e00000000000000000000000058d97b57bb95320f9a05dc918aef65434969c2b200000000000000000000000000000000000000000000004c748391b4a2e8d38100000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000eea2e120be5d26eee545e8535fccb5ea38f2340b502263927520adc30d7b9744d57e8ed75beee9c9ed92f47281a47b6efde7881ff35c39746de9b1b4b760457bd5cea201b922bdd85e98a7aa6e41bfa2b35490c4f35ac002acbc7611e499641fda1675d6f40dbacba5bd634b77d8c5350b935b121e8034ff94cd54cb560ea43574883beda04be8b8c91be901a111ee3bbeeca4b3e234e55b74fd15eb60be000bdc40443901247232d54e158ac337213593c694e01d512c50ac1825afd86c7a04f0ee6b3d000af89e2079c54e5392a4b2e9764a67280bf7723974d2aabcb6a234ed66388c926aeb083ef314a54d346a7fadf42fed4351d540d4a7e97bbd6105e69d8b447d2ade6278cf85592dbc41d1a08e1e968c287ffbc45dbc279a22fe6f5ef0f2f6320f481f1f7cce40bad9b86bea2f56ec5de68912646ee84d283f1f796826641b15795a0d6f5abd084a938f9bc02439d6c9e0e63e6a464c654719ea48e111e632b50acbdfd8e3b6d96b135812ff2643e67fe054b001c985e1687e7e5ce07f33bf03f812a39b88029cc602b6f3748abd014c5f06c6f76cd2b89d182188be0195d6d3e28c15340eea5a3e8cc70aff7dbfb39bc1ba5cc698e2577265875dbdb" | | | +| | | | ] | | | +| | | | } | | | +| approve | 0x58D97B57BB95320F9a05dC918Aef65434969c2B2 (tokens/MORPHO) | 0 | { | N/A | N/A | +| | | | "spender": [ | | | +| | | | "0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (angle/distribution_creator)" | | | +| | | | ], | | | +| | | | "amount": [ | | | +| | | | "raw:1410348263937287721964, 18 decimals:1410.348263937287721964, 6 decimals: 1410348263937287.721964" | | | +| | | | ] | | | +| | | | } | | | +| acceptConditions | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (angle/distribution_creator) | 0 | "N/A" | N/A | N/A | +| createCampaign | 0x8BB4C975Ff3c250e0ceEA271728547f3802B36Fd (angle/distribution_creator) | 0 | { | N/A | N/A | +| | | | "newCampaign": [ | | | +| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000000\"", | | | +| | | | "\"0x0000000000000000000000000000000000000000\"", | | | +| | | | "\"0x58D97B57BB95320F9a05dC918Aef65434969c2B2\"", | | | +| | | | "\"1410348263937287721964\"", | | | +| | | | "4", | | | +| | | | "1738594800", | | | +| | | | "3600", | | | +| | | | "\"0x0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000005568747470733a2f2f616e676c652d626c6f672e696e667572612d697066732e696f2f697066732f516d527538616f33535a41524a6d6e654e7a39467172334535536667777a41313759315534324579447962585047000000000000000000000000000000000000000000000000000000000000000000000000000000000000166373555344432d6373555344543a3a65706f63685f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"" | | | +| | | | ] | | | +| | | | } | | | ++------------------+-------------------------------------------------------------------------+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------+----------+ ```