Unable to interact with Upgradeable OpenZeppelin Contract

I have created a simple ERC-20 token as shown below:

// contracts/Ember.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts-upgradeable/token/ERC20/presets/ERC20PresetMinterPauserUpgradeable.sol";

contract Ember is ERC20PresetMinterPauserUpgradeable {
	uint8 public constant SUCCESS_CODE = 0;
	uint8 public constant ZERO_ADDRESS_RESTRICTION_CODE = 1;
	string public constant SUCCESS_MESSAGE = "SUCCESS";
	string public constant ZERO_ADDRESS_RESTRICTION_MESSAGE = "ILLEGAL_TRANSFER_TO_ZERO_ADDRESS";
	address public admin;

	modifier notRestricted (address from, address to, uint256 value) {
		uint8 restrictionCode = detectTransferRestriction(from, to, value);
		require(restrictionCode == SUCCESS_CODE, messageForTransferRestriction(restrictionCode));
		_;
	}

	function TESTInit(address _admin, string memory name, string memory symbol, uint256 initialSupply) public virtual initializer {
		admin = _admin;
		__ERC20PresetMinterPauser_init(name, symbol);
        _mint(_msgSender(), initialSupply);
    }

	function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20PresetMinterPauserUpgradeable) {
        super._beforeTokenTransfer(from, to, amount);
    }

	// returns a restriction code, where 0 is reserved for success
	function detectTransferRestriction (address from, address to, uint256 value) public pure returns (uint8 restrictionCode) {
		restrictionCode = SUCCESS_CODE; // success
		if (to == address(0)) {
			restrictionCode = ZERO_ADDRESS_RESTRICTION_CODE; // illegal transfer to zero address
		}
	}
	
	// returns a message string -- a human-readable message for the passed restriction code
	function messageForTransferRestriction (uint8 restrictionCode) public pure returns (string memory message) {
		message = SUCCESS_MESSAGE;
		if (restrictionCode == ZERO_ADDRESS_RESTRICTION_CODE) {
			message = ZERO_ADDRESS_RESTRICTION_MESSAGE; 
		}

	}

	function transfer (address to, uint256 value) override public notRestricted(msg.sender, to, value) returns (bool) {
		return super.transfer(to, value);
	}

	function transferFrom (address from, address to, uint256 value) override public notRestricted(from, to, value) returns (bool) {
		require(msg.sender == admin, "AdminBox: not admin");
		return super.transferFrom(from, to, value);
	}
}

I then successfully deploy it to Rinkeby using the following command:
$ npx hardhat run --network localhost deploy_ember.js

Compilation finished successfully
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 is deploying a Security Token Contract…
ERC1404 Compliant Security Token deployed at: 0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0

The deployment script used is as follows:

// scripts/deploy_ember.js
const { ethers, upgrades } = require("hardhat");

async function main() {
  let contractDeployAddr = `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`;
  let securityTokenName = `Ember`;
  let securityTokenSymbol = `MBR`;
  let securityTotalSupply = `2021`;
  let securityTokenContract = await ethers.getContractFactory("Ember");
  console.log(`${contractDeployAddr} is deploying a Security Token Contract...`);
  
  let securityContractInstance = await upgrades.deployProxy(securityTokenContract, [contractDeployAddr, securityTokenName, securityTokenSymbol, securityTotalSupply], { initializer: 'TESTInit' });
  await securityContractInstance.deployed();
  console.log("ERC1404 Compliant Security Token deployed at:", securityContractInstance.address);
}

main();

This contract pattern is now available on Rinkeby at:

I am unable to understand how to interact with the actual contract implementation (v1) through this proxy contract.

I tried to get the ABI by compiling /contracts/Ember.sol using Remix and copy pasting the ABI into a file called contract_abi.py

Next I am trying to interact with the deployed contract on Rinkeby using web3.py using the following a script called /eth_middleware.py as follows:

# eth_middleware.py

from web3 import HTTPProvider, Web3
import contract_abi, web3.exceptions

w3 = Web3(Web3.HTTPProvider('https://rinkeby.infura.io/v3/6c7e9aed2af146138cc7ef1986d9b558'))
Contract = w3.eth.contract(abi = contract_abi.abi)
contract1 = Contract(address = '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0')
print(contract1.all_functions())
print(contract1.functions.symbol.call())

<web3.contract.ContractFunctions object at 0x7f3ce9d484c0>
[<Function DEFAULT_ADMIN_ROLE()>, <Function getRoleAdmin(bytes32)>, <Function getRoleMember(bytes32,uint256)>, <Function getRoleMemberCount(bytes32)>, <Function grantRole(bytes32,address)>, <Function hasRole(bytes32,address)>, <Function renounceRole(bytes32,address)>, <Function revokeRole(bytes32,address)>, <Function supportsInterface(bytes4)>]

web3.exceptions.ABIFunctionNotFound: ("The function ‘symbol’ was not found in this contract’s abi. ", ‘Are you sure you provided the correct contract abi?’)

I don’t understand how to get the ABI of the implementation contract (which has loads and loads of nested OpenZeppelin imports).

I don’t understand how to call the different functions of my implementation contract (and all the token functions it inherits)…

Hi, welcome! :wave:

I am not sure what do you mean, I think you should just call proxy contract, the proxy contract will delegate your call the implementation contract.

And it seems like you deployed contracts with hardhat, but tested with python, I am not familiar with Brownie(maybe you use this to test), but for the hardhat, you can have a look at this tutorial:

When you compile your contracts using Hardhat it generates artifacts under artifacts/contracts and you can get the ABI from there.