Storing Non-Native Tokens in a Smart Contract

How do we store USDC in a vault contract?

​​Such a naive question sent me down a rabbit hole of how storage works on the Ethereum blockchain for ERC20 tokens. When your vault accepts a token, you actually need to reference the token’s own smart contract to figure out your vault’s contract balance. It’s like a pointer in the first token’s contract that says ‘hey, your vault contract owns this amount of tokens,’ and you need to make this cross contract read every time you want to get your vault’s balance.


When you are using a token in your contract, you need to use the balanceOf() method in your vault. Now, because the vault doesn’t natively have this method, you need to use the same code as the contract token that you are trying to obtain the value of. This is part of the reason that standards were introduced- now you have a uniform way to import the tokens interface to interact with its state.

So you need to import `import '@openzeppelin/token/ERC20/IERC20.sol';` or `import '@openzeppelin/token/ERC20/ERC20.sol';` to get access to balanceOf() function that you will be directly executing in a cross-contract call from the second contract. I prefer the ERC20 contract, since you can see the code in its entirety and you can replace OpenZeppelin’s contract with Solmate’s ERC20 implementation.

The code order is as follows:

  1. User invokes withdraw() on the UI of the vaults dashboard
  2. The UI queries the Vault contract and invokes the withdraw() method, passing in their amount of "advanced" tokens they plan to burn to receive USDC
  3. The USDC vault contract makes a cross-contract call to fetch the stored balance of USDC, from the USDC token contract
  4. This invokes the balanceOf() in the USDC smart contract

Tired of doing the same activities over and over again?

Get Started