Interacting with upgradeable ERC20 using geth

I am trying to interact with an erc20 token (contains proxies) using geth.

:computer: Environment

Geth/v1.9.24

:memo:Details

This is the process I follow when interacting with a regular erc20 token without proxies (ex. usdt):

  1. Create a .js file similar to below:

var usdtContractToken = eth.contract([{...usdtABI...}])
var usdtContractAddress = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
var usdt = usdtContractToken.at(usdtContractAddress);

  1. Preload geth with the above .js file using command:

geth attach --preload < pathToJsFile >

  1. Then interact with the erc20 token in geth console:

I am facing difficulties with using the above methodology when interacting with erc20 tokens that involve proxy contracts (ex. usdc, which uses OpenZeppelin's Unstructured Storage proxy pattern).

I am not sure what are the additional steps required to configure this setup.

Below is the .js file I am using to preload into geth:

:1234: Code to reproduce

// proxy
var usdcProxyContractToken = eth.contract([{...usdcProxyABI...}]);
var usdcProxyContractAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
var usdcProxy = usdcProxyContractToken.at(usdcProxyContractAddress);

// implementation
var usdcImpContractToken = eth.contract([{...usdcImpABI...}]);
var usdcImpContractAddress = "0xB7277a6e95992041568D9391D09d0122023778A2";
var usdcImp = usdcImpContractToken.at(usdcImpContractAddress);

Output from geth console:

usdcProxy.admin()
Error: execution reverted
at web3.js:6347:37(47)
at web3.js:5081:62(37)
at web3.js:4102:36(54)
at web3.js:4227:60(38)
at :1:1(2)

usdcImp.name()
Error: invalid address
at web3.js:3930:11(47)
at web3.js:3756:41(16)
at web3.js:5025:37(8)
at web3.js:5024:12(13)
at web3.js:5050:34(20)
at web3.js:5075:39(15)
at web3.js:4137:41(57)
at web3.js:4223:75(24)
at :1:1(2)

1 Like

Hi @beck1234,

Welcome to the community :wave:

To interact with an upgradeable ERC20, you should just be able to use the ABI of the ERC20 and the address of the proxy contract.

Hi @beck1234,

I wanted to check that you were able to interact with the token?

Hi @abcoathup! No luck yet with using the ABI of the ERC20 and the address of the proxy contract as you mentioned above.

I've tried on both geth v1.9.24 and v1.9.25

Preload above file to geth:

geth attach --preload pathToJsFile

Geth output:

usdc.name
function()

usdc.name()
Error: invalid address
at web3.js:3930:11(47)
at web3.js:3756:41(16)
at web3.js:5025:37(8)
at web3.js:5024:12(13)
at web3.js:5050:34(20)
at web3.js:5075:39(15)
at web3.js:4137:41(57)
at web3.js:4223:75(24)
at :1:1(2)

Proxy: https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#code
Implementation: https://etherscan.io/address/0xb7277a6e95992041568d9391d09d0122023778a2#code

Please let me know if there are any additional steps that I'm missing.

1 Like

HI @beck1234,

I haven’t interacted via geth before.

I created a simple script to interact via Alchemy, though you could modify to use a local node.

First I obtained an ERC20 artifact (though you could just use the ABI) from OpenZeppelin Contracts

$ npm i @openzeppelin/contracts
$ mkdir -p build/contracts/
$ cp node_modules/@openzeppelin/contracts/build/contracts/* build/contracts/

Then I installed web3 and the OpenZeppelin Contract Loader (for convenience)

$ npm i web3
$ npm i @openzeppelin/contract-loader

index.js

Interact using ERC20 and the address of the proxy

// scripts/index.js
const Web3 = require('web3');
const { setupLoader } = require('@openzeppelin/contract-loader');

const { alchemyApiKey, mnemonic } = require('../secrets.json');

const HDWalletProvider = require('@truffle/hdwallet-provider');

const provider = new HDWalletProvider(mnemonic, `https://eth-mainnet.alchemyapi.io/v2/${alchemyApiKey}`);

async function main() {
  // Set up web3 object
  const web3 = new Web3(provider);

  // Set up a web3 contract, representing a deployed ERC20, using the contract loader
  const address = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
  const loader = setupLoader({ provider: web3 }).web3;
  const token = loader.fromArtifact('ERC20', address);

  // Retrieve accounts
  const accounts = await web3.eth.getAccounts();

  console.log(`Account: ${accounts[0]}`);

  // Call the deployed token contract
  const name = await token.methods.name().call();
  const symbol = await token.methods.symbol().call();
  const decimals = await token.methods.decimals().call();
  const totalSupply = await token.methods.totalSupply().call();
  console.log(`${name} (${symbol}) - Decimals:${decimals} Total Supply:${totalSupply}`);

  // At termination, `provider.engine.stop()' should be called to finish the process elegantly.
  provider.engine.stop();
}

main();

Run

$ node scripts/index.js
Account: 0xEce6999C6c5BDA71d673090144b6d3bCD21d13d4
USD Coin (USDC) - Decimals:6 Total Supply:4750520962337187
1 Like

@abcoathup Thank you for all the help. I was able to leverage the above scripts to run against my local geth node. It is weird that the same method doesn’t work when interacting directly through geth. I’ll post any updates/progress on it here.

1 Like

Hi @beck1234,

Glad you can interact now. Updates are appreciated for anyone in future coming across this topic.