Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/multicall #1077

Closed
wants to merge 8 commits into from
Closed

feat/multicall #1077

wants to merge 8 commits into from

Conversation

banteg
Copy link
Collaborator

@banteg banteg commented May 5, 2021

What I did

Added multicall context manager I initially outlined here.

Related issue: #1011

How I did it

I used Multicall2 and wrapt library to replace results with proxies which are updated with actual values as soon as they are available.

Special thanks to @nymmrx for help with value wrappers.

How to verify it

Here is a complex example:

from brownie import *
import pandas as pd

def curve():
    curve_registry = Contract("0x7D86446dDb609eD0F5f8684AcF30380a356b2B4c")
    voter_proxy = Contract("0xF147b8125d2ef93FB6965Db97D6746952a133934")
    gauge_controller = Contract("0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB")
    
    pool_count = curve_registry.pool_count()  # call 1
    with multicall() as caller:  # call 2
        pools = [caller(curve_registry).pool_list(i) for i in range(pool_count)]
    
    with multicall() as caller:  # call 3
        gauges = [caller(curve_registry).get_gauges(pool) for pool in pools]
    
    pools = [
        {'pool': Contract(pool), 'gauge': Contract(gauge[0][0])}
        for pool, gauge in zip(pools, gauges) if gauge[0][0] != ZERO_ADDRESS
    ]
    
    with multicall() as caller:  # call 4
        for pool in pools:
            pool['last_vote'] = caller(gauge_controller).last_user_vote(voter_proxy, pool['gauge'])
            pool['last_weight'] = caller(gauge_controller).vote_user_slopes(voter_proxy, pool['gauge'])
            pool['balance'] = caller(pool['gauge']).balanceOf(voter_proxy)
            pool['working_balance'] = caller(pool['gauge']).working_balances(voter_proxy)
            pool['virtual_price'] = caller(pool['pool']).get_virtual_price()

    for pool in pools:
        pool['last_weight'] = pool['last_weight'].dict()['power']
        pool['boost'] = pool['working_balance'] / pool['balance'] * 2.5 if pool['balance'] > 0 else 0
        pool['balance'] /= 1e18  # NOTE: all curve LP tokens have 18 decimals
        pool['virtual_price'] /= 1e18

    print(pd.DataFrame(pools)[['last_vote', 'last_weight', 'balance', 'boost', 'virtual_price']])

Checklist

  • I have confirmed that my PR passes all linting checks
  • I have included test cases
  • I have updated the documentation
  • I have added an entry to the changelog

queue = []
yield partial(Caller, queue)

multicall2 = Contract("0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696")
Copy link
Contributor

@omarish omarish May 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: make this a default kwarg for multicall so it's not hard-coded in the framework?

@contextmanager
def multicall(block_identifier=None, multicall2_address="0x5BA1e12693Dc8F9c48aAD8770482f4739bEeD696"):
    ...
    multicall2 = Contract(multicall2_address)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking to make it even more flexible:

  • live network => use eth_call code override to make it available pre-deploy
  • dev network => seamlessly deploy a contract and cache it

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think eth_call code override should be optional as some providers don't support them (e.g. infura)

skellet0r added 5 commits June 4, 2021 15:24
Allows users to specify the location of the multicall2 contract they
intend to use. Useful on different networks (development and live).

Added Multicall2 abi to data/interfaces to allow for initializing
multicall contract via abi.

Reordered definitions and add type info.
add Multicall2.sol to brownie test project
add multicall2 test fixture
@iamdefinitelyahuman
Copy link
Member

Handled in #1125

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants