Skip to content

Instantly share code, notes, and snippets.

@banteg
Created March 4, 2021 12:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save banteg/fe268f8a73657199eb79d725620506af to your computer and use it in GitHub Desktop.
Save banteg/fe268f8a73657199eb79d725620506af to your computer and use it in GitHub Desktop.
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