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)…