ethereum / web3.py

A python interface for interacting with the Ethereum blockchain and ecosystem.
http://web3py.readthedocs.io
MIT License
4.97k stars 1.69k forks source link

Accessing elements through a Proxy Contract #1895

Closed vintrocode closed 3 years ago

vintrocode commented 3 years ago
alembic @ file:///home/conda/feedstock_root/build_artifacts/alembic_1611176215154/work
anyio @ file:///home/conda/feedstock_root/build_artifacts/anyio_1610315727443/work/dist
apipkg==1.5
appdirs @ file:///home/conda/feedstock_root/build_artifacts/appdirs_1603108395799/work
argon2-cffi @ file:///home/conda/feedstock_root/build_artifacts/argon2-cffi_1610522574055/work
asttokens==2.0.4
async-generator==1.10
attrs @ file:///home/conda/feedstock_root/build_artifacts/attrs_1605083924122/work
Babel @ file:///home/conda/feedstock_root/build_artifacts/babel_1605182336601/work
backcall @ file:///home/conda/feedstock_root/build_artifacts/backcall_1592338393461/work
backports.functools-lru-cache==1.6.1
base58==2.1.0
beautifulsoup4 @ file:///home/conda/feedstock_root/build_artifacts/beautifulsoup4_1601745390275/work
bitarray==1.2.2
black==19.10b0
bleach @ file:///home/conda/feedstock_root/build_artifacts/bleach_1611678357911/work
blinker==1.4
bokeh @ file:///home/conda/feedstock_root/build_artifacts/bokeh_1606141192957/work
Bottleneck @ file:///home/conda/feedstock_root/build_artifacts/bottleneck_1611195606760/work
brotlipy==0.7.0
cached-property==1.5.1
certifi==2020.12.5
certipy==0.1.3
cffi @ file:///home/conda/feedstock_root/build_artifacts/cffi_1606601123836/work
chardet @ file:///home/conda/feedstock_root/build_artifacts/chardet_1610093490430/work
click==7.1.2
cloudpickle @ file:///home/conda/feedstock_root/build_artifacts/cloudpickle_1598400192773/work
conda==4.9.2
conda-package-handling @ file:///home/conda/feedstock_root/build_artifacts/conda-package-handling_1602876795040/work
cryptography @ file:///home/conda/feedstock_root/build_artifacts/cryptography_1610338667514/work
cycler==0.10.0
Cython @ file:///home/conda/feedstock_root/build_artifacts/cython_1610095927075/work
cytoolz==0.11.0
dask @ file:///home/conda/feedstock_root/build_artifacts/dask-core_1611349541186/work
datatable==0.11.1
decorator==4.4.2
defusedxml==0.6.0
dill @ file:///home/conda/feedstock_root/build_artifacts/dill_1604312823872/work
distributed @ file:///home/conda/feedstock_root/build_artifacts/distributed_1611361822694/work
entrypoints @ file:///home/conda/feedstock_root/build_artifacts/entrypoints_1605121927639/work/dist/entrypoints-0.3-py2.py3-none-any.whl
eth-abi==2.1.1
eth-account==0.5.2
eth-brownie==1.13.2
eth-event==1.2.1
eth-hash==0.2.0
eth-keyfile==0.5.1
eth-keys==0.3.3
eth-rlp==0.2.1
eth-typing==2.2.2
eth-utils==1.9.5
execnet==1.8.0
fsspec @ file:///home/conda/feedstock_root/build_artifacts/fsspec_1608050797851/work
gmpy2==2.1.0b1
h5py @ file:///home/conda/feedstock_root/build_artifacts/h5py_1604753633781/work
HeapDict==1.0.1
hexbytes==0.2.1
hypothesis==5.41.3
idna @ file:///home/conda/feedstock_root/build_artifacts/idna_1593328102638/work
imagecodecs @ file:///home/conda/feedstock_root/build_artifacts/imagecodecs_1610511162507/work
imageio @ file:///home/conda/feedstock_root/build_artifacts/imageio_1594044661732/work
importlib-metadata @ file:///home/conda/feedstock_root/build_artifacts/importlib-metadata_1610355167281/work
inflection==0.5.0
iniconfig==1.1.1
ipfshttpclient==0.6.1
ipykernel @ file:///home/conda/feedstock_root/build_artifacts/ipykernel_1607737215242/work/dist/ipykernel-5.4.2-py3-none-any.whl
ipympl @ file:///home/conda/feedstock_root/build_artifacts/ipympl_1611166850005/work
ipython @ file:///home/conda/feedstock_root/build_artifacts/ipython_1610764430266/work
ipython-genutils==0.2.0
ipywidgets @ file:///home/conda/feedstock_root/build_artifacts/ipywidgets_1609995587151/work
jedi @ file:///home/conda/feedstock_root/build_artifacts/jedi_1605054524035/work
Jinja2==2.11.2
joblib @ file:///home/conda/feedstock_root/build_artifacts/joblib_1607956439537/work
json5 @ file:///home/conda/feedstock_root/build_artifacts/json5_1600692310011/work
jsonschema @ file:///home/conda/feedstock_root/build_artifacts/jsonschema_1602551949684/work
jupyter-client @ file:///home/conda/feedstock_root/build_artifacts/jupyter_client_1610375432619/work
jupyter-core @ file:///home/conda/feedstock_root/build_artifacts/jupyter_core_1611181604334/work
jupyter-server @ file:///home/conda/feedstock_root/build_artifacts/jupyter_server_1611092183461/work
jupyter-telemetry @ file:///home/conda/feedstock_root/build_artifacts/jupyter_telemetry_1605173804246/work
jupyterhub @ file:///home/conda/feedstock_root/build_artifacts/jupyterhub-feedstock_1607688149914/work
jupyterlab @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_1610663604779/work
jupyterlab-pygments @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_pygments_1601375948261/work
jupyterlab-server @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_server_1611690648627/work
jupyterlab-widgets @ file:///home/conda/feedstock_root/build_artifacts/jupyterlab_widgets_1609173350931/work
kiwisolver @ file:///home/conda/feedstock_root/build_artifacts/kiwisolver_1610099769230/work
llvmlite==0.35.0
locket==0.2.0
lru-dict==1.1.7
Mako @ file:///home/conda/feedstock_root/build_artifacts/mako_1610659158978/work
MarkupSafe @ file:///home/conda/feedstock_root/build_artifacts/markupsafe_1610127565888/work
matplotlib @ file:///home/conda/feedstock_root/build_artifacts/matplotlib-suite_1610582849386/work
mistune @ file:///home/conda/feedstock_root/build_artifacts/mistune_1610112875388/work
mock @ file:///home/conda/feedstock_root/build_artifacts/mock_1610094566888/work
more-itertools==8.7.0
mpmath==1.1.0
msgpack @ file:///home/conda/feedstock_root/build_artifacts/msgpack-python_1610121699837/work
multiaddr==0.0.9
mythx-models==1.9.1
nbclassic @ file:///home/conda/feedstock_root/build_artifacts/nbclassic_1610352513187/work
nbclient @ file:///home/conda/feedstock_root/build_artifacts/nbclient_1602859080374/work
nbconvert @ file:///home/conda/feedstock_root/build_artifacts/nbconvert_1605401836768/work
nbformat @ file:///home/conda/feedstock_root/build_artifacts/nbformat_1611005694671/work
nest-asyncio @ file:///home/conda/feedstock_root/build_artifacts/nest-asyncio_1605195931949/work
netaddr==0.8.0
networkx @ file:///home/conda/feedstock_root/build_artifacts/networkx_1598210780226/work
notebook @ file:///home/conda/feedstock_root/build_artifacts/notebook_1610575313697/work
numba @ file:///home/conda/feedstock_root/build_artifacts/numba_1607010260266/work
numexpr @ file:///home/conda/feedstock_root/build_artifacts/numexpr_1609341217654/work
numpy @ file:///home/conda/feedstock_root/build_artifacts/numpy_1610324535485/work
oauthlib==3.0.1
olefile @ file:///home/conda/feedstock_root/build_artifacts/olefile_1602866521163/work
packaging @ file:///home/conda/feedstock_root/build_artifacts/packaging_1607785313469/work
pamela==1.0.0
pandas==1.2.1
pandocfilters==1.4.2
parsimonious==0.8.1
parso @ file:///home/conda/feedstock_root/build_artifacts/parso_1595548966091/work
partd==1.1.0
pathspec==0.8.1
patsy==0.5.1
pexpect @ file:///home/conda/feedstock_root/build_artifacts/pexpect_1602535608087/work
pickleshare @ file:///home/conda/feedstock_root/build_artifacts/pickleshare_1602536217715/work
Pillow @ file:///home/conda/feedstock_root/build_artifacts/pillow_1610407357749/work
pluggy==0.13.1
pooch @ file:///home/conda/feedstock_root/build_artifacts/pooch_1606467285986/work
prometheus-client @ file:///home/conda/feedstock_root/build_artifacts/prometheus_client_1605543085815/work
prompt-toolkit==3.0.8
protobuf==3.14.0
psutil @ file:///home/conda/feedstock_root/build_artifacts/psutil_1610127095720/work
ptyprocess @ file:///home/conda/feedstock_root/build_artifacts/ptyprocess_1609419310487/work/dist/ptyprocess-0.7.0-py2.py3-none-any.whl
py==1.10.0
py-solc-ast==1.2.8
py-solc-x==1.1.0
pycosat @ file:///home/conda/feedstock_root/build_artifacts/pycosat_1610094800877/work
pycparser @ file:///home/conda/feedstock_root/build_artifacts/pycparser_1593275161868/work
pycryptodome==3.10.1
pycurl==7.43.0.6
Pygments==2.6.1
pygments-lexer-solidity==0.5.1
PyJWT==1.7.1
pyOpenSSL @ file:///home/conda/feedstock_root/build_artifacts/pyopenssl_1608055815057/work
pyparsing==2.4.7
pyrsistent @ file:///home/conda/feedstock_root/build_artifacts/pyrsistent_1610146798212/work
PySocks @ file:///home/conda/feedstock_root/build_artifacts/pysocks_1610291447907/work
pytest==6.0.1
pytest-forked==1.3.0
pytest-xdist==1.34.0
python-dateutil==2.8.1
python-editor==1.0.4
python-json-logger @ file:///home/conda/feedstock_root/build_artifacts/python-json-logger_1602545356084/work
pythx==1.6.1
pytz @ file:///home/conda/feedstock_root/build_artifacts/pytz_1608904108784/work
PyWavelets @ file:///home/conda/feedstock_root/build_artifacts/pywavelets_1607290812047/work
PyYAML==5.4.1
pyzmq @ file:///home/conda/feedstock_root/build_artifacts/pyzmq_1610965975478/work
regex==2020.11.13
requests @ file:///home/conda/feedstock_root/build_artifacts/requests_1608156231189/work
rlp==1.2.0
rpy2 @ file:///home/conda/feedstock_root/build_artifacts/rpy2_1610386004569/work
ruamel-yaml-conda @ file:///home/conda/feedstock_root/build_artifacts/ruamel_yaml_1610408218022/work
ruamel.yaml @ file:///home/conda/feedstock_root/build_artifacts/ruamel.yaml_1610291375472/work
ruamel.yaml.clib @ file:///home/conda/feedstock_root/build_artifacts/ruamel.yaml.clib_1610146840614/work
scikit-image==0.18.1
scikit-learn @ file:///home/conda/feedstock_root/build_artifacts/scikit-learn_1611079738450/work
scipy @ file:///home/conda/feedstock_root/build_artifacts/scipy_1609457852787/work
seaborn @ file:///home/conda/feedstock_root/build_artifacts/seaborn-base_1608544589436/work
semantic-version==2.8.5
Send2Trash==1.5.0
simplegeneric==0.8.1
six @ file:///home/conda/feedstock_root/build_artifacts/six_1590081179328/work
sniffio @ file:///home/conda/feedstock_root/build_artifacts/sniffio_1610318319305/work
sortedcontainers @ file:///home/conda/feedstock_root/build_artifacts/sortedcontainers_1605110889605/work
soupsieve @ file:///home/conda/feedstock_root/build_artifacts/soupsieve_1597680516047/work
SQLAlchemy @ file:///home/conda/feedstock_root/build_artifacts/sqlalchemy_1610127210540/work
statsmodels @ file:///home/conda/feedstock_root/build_artifacts/statsmodels_1610414531195/work
sympy @ file:///home/conda/feedstock_root/build_artifacts/sympy_1610127369423/work
tables @ file:///home/conda/feedstock_root/build_artifacts/pytables_1610156075390/work
tblib==1.6.0
terminado @ file:///home/conda/feedstock_root/build_artifacts/terminado_1609794172745/work
testpath==0.4.4
threadpoolctl @ file:///tmp/tmp79xdzxkt/threadpoolctl-2.1.0-py3-none-any.whl
tifffile @ file:///home/conda/feedstock_root/build_artifacts/tifffile_1610742624962/work
toml==0.10.2
toolz @ file:///home/conda/feedstock_root/build_artifacts/toolz_1600973991856/work
tornado @ file:///home/conda/feedstock_root/build_artifacts/tornado_1610094706440/work
tqdm==4.53.0
traitlets @ file:///home/conda/feedstock_root/build_artifacts/traitlets_1602771532708/work
typed-ast==1.4.2
typing-extensions @ file:///home/conda/feedstock_root/build_artifacts/typing_extensions_1602702424206/work
tzlocal @ file:///home/conda/feedstock_root/build_artifacts/tzlocal_1588939190034/work
urllib3 @ file:///home/conda/feedstock_root/build_artifacts/urllib3_1611695416663/work
varint==1.0.2
vincent==0.4.4
vvm==0.1.0
vyper==0.2.11
wcwidth @ file:///home/conda/feedstock_root/build_artifacts/wcwidth_1600965781394/work
web3==5.11.1
webencodings==0.5.1
websockets==8.1
widgetsnbextension @ file:///home/conda/feedstock_root/build_artifacts/widgetsnbextension_1605475534911/work
xlrd @ file:///home/conda/feedstock_root/build_artifacts/xlrd_1610224409810/work
zict==2.0.0
zipp @ file:///home/conda/feedstock_root/build_artifacts/zipp_1603668650351/work

I'm trying to access the Aave lending pool contract via web3py. I'm not sure if my issue is because I'm missing something with web3py or there's something with the Aave contracts, but I'll explain what I tried.

First, I connected to the Kovan testnet with no problem

from web3 import Web3
import json
import os

kovan_url = os.environ['KOVAN_URL']
web3 = Web3(Web3.HTTPProvider(kovan_url))

Aave has a lending pool addresses provider contract that you can use to get the actual lending pool's address, so I created that contract object first.

AAVE_LENDING_POOL_ADDRESSES_PROVIDER = web3.toChecksumAddress('0x88757f2f99175387aB4C6a4b3067c77A695b0349')

with open('LendingPoolAddressesProvider-kovan.json') as f:
    lp_addr_prov_abi = json.load(f)

lp_addr_prov_contract = web3.eth.contract(address=AAVE_LENDING_POOL_ADDRESSES_PROVIDER, abi=lp_addr_prov_abi)

I called the getLendingPool() function which returns (what I found out to be) a proxy address: 0xE0fBa4Fc209b4948668006B2bE61711b7f465bAe

AAVE_LENDING_POOL_PROXY = web3.toChecksumAddress(lp_addr_prov_contract.functions.getLendingPool().call())

If you find that address on Kovan Etherscan, you'll see it's called InitializableImmutableAdminUpgradeabilityProxy. A few discord discussions later, I found out that's a proxy contract and learned about EIP-1967, which says you can retrieve the address of the logic contract from this proxy or "beacon" contract using an implementation() method. I was able to verify this method exists with the .all_functions() command, so I tried calling it and this is what I got:

lp_proxy_contract.functions.implementation().call()

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-13-bd5de7964a13> in <module>
----> 1 lp_proxy_contract.functions.implementation().call()

/opt/conda/lib/python3.8/site-packages/web3/contract.py in call(self, transaction, block_identifier)
    952         block_id = parse_block_identifier(self.web3, block_identifier)
    953 
--> 954         return call_contract_function(
    955             self.web3,
    956             self.address,

/opt/conda/lib/python3.8/site-packages/web3/contract.py in call_contract_function(web3, address, normalizers, function_identifier, transaction, block_id, contract_abi, fn_abi, *args, **kwargs)
   1497         return_data = web3.eth.call(call_transaction)
   1498     else:
-> 1499         return_data = web3.eth.call(call_transaction, block_identifier=block_id)
   1500 
   1501     if fn_abi is None:

/opt/conda/lib/python3.8/site-packages/eth_utils/functional.py in inner(*args, **kwargs)
     43         @functools.wraps(fn)
     44         def inner(*args, **kwargs) -> T:  # type: ignore
---> 45             return callback(fn(*args, **kwargs))
     46 
     47         return inner

/opt/conda/lib/python3.8/site-packages/web3/eth.py in call(self, transaction, block_identifier)
    430         if block_identifier is None:
    431             block_identifier = self.defaultBlock
--> 432         return self.web3.manager.request_blocking(
    433             RPC.eth_call,
    434             [transaction, block_identifier],

/opt/conda/lib/python3.8/site-packages/web3/manager.py in request_blocking(self, method, params, error_formatters)
    151         if "error" in response:
    152             apply_error_formatters(error_formatters, response)
--> 153             raise ValueError(response["error"])
    154 
    155         return response['result']

ValueError: {'code': -32015, 'data': 'Reverted 0x', 'message': 'VM execution error.'}

My research on that led me to the source code of the solidity contract, which seems to have an admin lock on almost all of the functions:

function implementation() external ifAdmin returns (address) {
    return _implementation();
  }

I asked the Aave devs what to do but they seemed perplexed that I was even dealing with the proxy contract in the first place, as it seemed no one had familiarity with web3py and this hasn't occurred before in JS. They said my logic should have just worked, but the ultimate solution was to forget the proxy and hard code the address of the lending pool which works, but is ultimately a bandaid solution. Any help or direction is greatly appreciated, thanks in advance.

kclowes commented 3 years ago

Thanks for such a detailed issue @vintrocode! Looking over this briefly I'm not sure why you'd need to call the implementation function? It looks like you should be able to do all of the external facing tasks - deposit, withdraw, borrow, etc. from the lending pool address that you have: 0xE0fBa4Fc209b4948668006B2bE61711b7f465bAe. Am I misunderstanding?

The error you're getting back is a Solidity revert which I suspect is being triggered by the isAdmin check, like you said.

Huge commented 3 years ago

You should check the adresses in https://docs.aave.com/developers/getting-started/deployed-contracts , Kovan tab. Then you can either just copy the LendingPool adress now or at least compare it with your current target. Do something like: lp_addr = web3.eth.contract(address= 0xE0fBa4Fc209b4948668006B2bE61711b7f465bAe, abi=lp_abi), where you put the ABI of the current implementation, usually by having https://github.com/aave/protocol-v2 compiled aside your project.

vintrocode commented 3 years ago

The kovan tab has a note that says to make sure you go through the LendingPoolAddressesProvider to get the latest LendingPool. That address provider returns 0xE0fBa4Fc209b4948668006B2bE61711b7f465bAe, which is the address to the proxy contract. @kclowes the Aave devs said exactly what you did -- i should be able to do all those things apparently. But when I create the contract object with the appropriate ABI, these are the functions available to me:

lp_proxy_contract.all_functions()

[<Function admin()>,
 <Function implementation()>,
 <Function initialize(address,bytes)>,
 <Function upgradeTo(address)>,
 <Function upgradeToAndCall(address,bytes)>]

@Huge I do not currently have the aave V2 protocol contracts compiled aside my project. Do I need that even though I'm not deploying any contracts myself? Is that a best-practice or something?

vintrocode commented 3 years ago

I'll be damned. @Huge if I do as you're saying, and just use that proxy address like it's the LP address along with the LP ABI, it works. That's wild to me. I do my due diligence, check the addresses on etherscan, and it leads me astray when in reality I should have been ignorant and everything would have worked! Haha thanks both of you @kclowes and @Huge for your help.