I'm using web3.py to interact with multiple unknown contracts, where I don't know beforehand if a contract is a proxy contract or the actual contract. I'm getting the ABIs for the contracts on-the-fly from BSCScan's API. Assume I am only interested in contracts whose ABIs are available on BSCscan.

When I retrieve the ABI of a contract from BSCScan, sometimes the ABI has functions like
`['admin', 'changeAdmin', 'implementation', 'upgradeTo', 'upgradeToAndCall']` which is how I understand it's a proxy contract address.

Now I'd like to get the address of the implementation contract from the proxy contract. The final goal is to get the ABI of the implementation contract via BSCScan, for which I need the address of the implementation contract.

How do I get the ABI (or the address) of the implementation contract?

(While I am using BSCScan and BSC, for the purposes of this answer I assume the answer for Ethereum and Etherscan is the same)

```py
from bscscan import BscScan
from web3 import Web3

contract_address = '0x88f1A5ae2A3BF98AEAF342D26B30a79438c9142e'  # Test case

def get_abi(contract_address):
    with BscScan(api_key, asynchronous=False) as client:
        abi_string = client.get_contract_abi(contract_address)
    return abi_string

web3 = Web3(Web3.HTTPProvider(bsc))
abi_string = get_abi(address)
contract = web3.eth.contract(address=address, abi=abi_string)
if 'implementation' in [foo.function_identifier for foo in contract.all_functions()]:
    # it's a proxy contract as its ABI contained implementation()
    implementation_address = contract.functions.implementation().call()  # <-- Error
    implementation_abi_string = get_abi(implementation_address)
    contract = web3.eth.contract(address=address, abi=implementation_abi_string)

desired_result = contract.functions.somefunction().call()

```

This results in

```
Traceback (most recent call last):
  ...
  File "helpers.py", line 86, in create_contract_object
    implementation_address = contract.functions.implementation().call()
  File "/venv-mac-py/lib/python3.9/site-packages/web3/contract.py", line 957, in call
    return call_contract_function(
  File "/venv-mac-py/lib/python3.9/site-packages/web3/contract.py", line 1501, in call_contract_function
    return_data = web3.eth.call(
  File "/venv-mac-py/lib/python3.9/site-packages/web3/module.py", line 57, in caller
    result = w3.manager.request_blocking(method_str, params, error_formatters)
  File "/venv-mac-py/lib/python3.9/site-packages/web3/manager.py", line 159, in request_blocking
    apply_error_formatters(error_formatters, response)
  File "/venv-mac-py/lib/python3.9/site-packages/web3/manager.py", line 63, in apply_error_formatters
    formatted_response = pipe(response, error_formatters)
  File "cytoolz/functoolz.pyx", line 667, in cytoolz.functoolz.pipe
  File "cytoolz/functoolz.pyx", line 642, in cytoolz.functoolz.c_pipe
  File "/venv-mac-py/lib/python3.9/site-packages/web3/_utils/method_formatters.py", line 544, in raise_solidity_error_on_revert
    raise ContractLogicError('execution reverted')
web3.exceptions.ContractLogicError: execution reverted

```