-
-
Save banteg/fe268f8a73657199eb79d725620506af to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def curve_gauge_weights(): | |
ychad = accounts.at(web3.ens.resolve("ychad.eth"), force=True) | |
curve_registry = Contract("0x7D86446dDb609eD0F5f8684AcF30380a356b2B4c") | |
voter_proxy = Contract("0xF147b8125d2ef93FB6965Db97D6746952a133934") | |
strategy_proxy = Contract('0x9a165622a744C20E3B2CB443AeD98110a33a231b', owner=ychad) | |
gauge_controller = Contract("0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB") | |
WEIGHT_VOTE_DELAY = 10 * 86400 | |
now = chain[-1].timestamp | |
print('fetching pools') | |
pools = fetch_multicall(*[[curve_registry, 'pool_list', i] for i in range(curve_registry.pool_count())]) | |
print('fetching gauges') | |
gauges = fetch_multicall(*[[curve_registry, 'get_gauges', pool] for pool in pools]) | |
pools = {Contract(pool): {'gauge': Contract(gauge[0][0])} for pool, gauge in zip(pools, gauges) if gauge[0][0] != ZERO_ADDRESS} | |
print('fetching last votes and slopes') | |
last_votes = fetch_multicall(*[[gauge_controller, 'last_user_vote', voter_proxy, data['gauge']] for data in pools.values()]) | |
slopes = fetch_multicall(*[[gauge_controller, 'vote_user_slopes', voter_proxy, data['gauge']] for data in pools.values()]) | |
for pool, last_vote, slope in zip(pools, last_votes, slopes): | |
pools[pool]['last_vote'] = last_vote | |
pools[pool]['last_weight'] = slope.dict()['power'] | |
assert last_vote + WEIGHT_VOTE_DELAY < now, "voting too soon" | |
print('fetching gauge and working balances') | |
balances = fetch_multicall(*[[pools[pool]['gauge'], 'balanceOf', voter_proxy] for pool in pools]) | |
working_balances = fetch_multicall(*[[pools[pool]['gauge'], 'working_balances', voter_proxy] for pool in pools]) | |
for pool, balance, working in zip(pools, balances, working_balances): | |
pools[pool]['balance'] = balance / 1e18 # NOTE: all curve LP tokens have 18 decimals | |
pools[pool]['boost'] = working / balance * 2.5 if balance > 0 else 0 | |
print('fetching virtual prices') | |
virtual_prices = fetch_multicall(*[[pool, 'get_virtual_price'] for pool in pools]) | |
print('fetching underlying coins') | |
underlying_coins = fetch_multicall(*[[curve_registry, 'get_underlying_coins', pool] for pool in pools]) | |
for pool, virt_price, coins in zip(pools, virtual_prices, underlying_coins): | |
pools[pool]['virtual_price'] = virt_price / 1e18 | |
pools[pool]['coins'] = [coin for coin in coins if coin != ZERO_ADDRESS] | |
print('fetching oracles') | |
prices = fetch_multicall( | |
[Contract('eth-usd.data.eth'), 'latestAnswer'], | |
[Contract('btc-usd.data.eth'), 'latestAnswer'], | |
[Contract('eur-usd.data.eth'), 'latestAnswer'], | |
[Contract('link-usd.data.eth'), 'latestAnswer'], | |
) | |
eth_usd, btc_usd, eur_usd, link_usd = [price / 1e8 for price in prices] | |
# use chainlink price if a known coin is among underlying coins | |
prices = { | |
'0x6B175474E89094C44Da98b954EedeAC495271d0F': 1, # dai | |
'0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': eth_usd, # psedo eth | |
'0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599': btc_usd, # wbtc | |
'0xD71eCFF9342A5Ced620049e616c5035F1dB98620': eur_usd, # seur | |
'0x514910771AF9Ca656af840dff83E8264EcF986CA': link_usd, # link | |
} | |
for pool, data in pools.items(): | |
try: | |
known_coin = (set(data['coins']) & set(prices)).pop() | |
except KeyError: | |
raise KeyError(f'no known coins among: {data["coins"]}') | |
data['tvl'] = prices[known_coin] * data['balance'] * data['virtual_price'] | |
# add voters if not already added | |
if not strategy_proxy.voters(ychad): | |
strategy_proxy.approveVoter(web3.ens.resolve('ychad.eth')) | |
strategy_proxy.approveVoter(web3.ens.resolve('yfi.banteg.eth')) | |
strategy_proxy.approveVoter(web3.ens.resolve('andrecronje.eth')) | |
# exclude pools with max boost | |
tvl = sum(data['tvl'] for data in pools.values() if data['boost'] < 2.5) | |
print('total tvl', tvl) | |
for pool, data in pools.items(): | |
data['weight'] = int(data['tvl'] / tvl * 10000) if data['boost'] < 2.5 else 0 | |
# add the error introduced by floor division to the top vote | |
total_votes = sum(data['weight'] for data in pools.values()) | |
top_vote = max(pools.values(), key=lambda x: x['weight']) | |
top_vote['weight'] += 10000 - total_votes | |
# first we need to bring the total votes casted down | |
for pool, data in pools.items(): | |
if data['last_weight'] and data['last_weight'] > data['weight']: | |
strategy_proxy.vote(data['gauge'], data['weight']) | |
# then we can vote for increased weights | |
for pool, data in pools.items(): | |
if data['last_weight'] < data['weight']: | |
strategy_proxy.vote(data['gauge'], data['weight']) | |
assert gauge_controller.vote_user_power(voter_proxy) == 10000, 'not all votes used' | |
# display a summary table | |
tvl_desc = sorted(pools.values(), key=lambda x: x['tvl'], reverse=True) | |
table = [[data['gauge'], data['tvl'] / 1e6, data['boost'], data['last_weight'], data['weight']] for data in tvl_desc] | |
print(tabulate(table, headers=['gauge', 'tvl 1e6', 'boost', 'last_weight', 'weight'])) | |
safe_tx = multisend_from_receipts(ychad, history) | |
estimate_safe_tx(safe_tx) | |
broadcast_tx(safe_tx) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment