TwitterDiscordGitHub logo

2024-08-01 Bridged USDC withdrawal bug

What happened?

  • On April 24th, shortly before the Redstone mainnet launch, we deployed a USDC contract to Redstone, following Circle’s Bridged USDC Standard, with a small modification to make it compatible with the Optimism Standard Bridge
    • We followed Bridged USDC Standard to make it possible for Circle to take over the deployment in the future if they decide to issue USDC natively on Redstone, to avoid two different USDC deployments on Redstone (like it happened on OP Mainnet and Base).
    • We needed to add a few simple methods to the implementation to make it compatible with Optimism’s IOptimismMintableERC20 interface, which is required for the token to be compatible with the Optimism Standard Bridge.
  • On August 1st, we noticed that a transaction to withdraw USDC from Redstone to Ethereum reverted. Upon further investigation we realized that the token’s burn(uint256 _amount) method did not match the signature expected by the bridge (burn(address _from, uint256 _amount)).
  • While no funds were at risk and no funds were lost, this prevented USDC withdrawal from Redstone until a fix was deployed.
  • A fix was deployed at Aug 01 2024 4:32:57 PM UTC and USDC withdrawals are now possible.

How it happened

  • Both IOptimismMintableERC20 (Optimism Standard) and FiatTokenV1 (Circle Standard) implement a mint function with the same input types, but differing return types. (IOptimismMintableERC20 has no return value, while FiatTokenV1 returns bool).
    • This makes it impossible for a contract to extend both IOptimismMintableERC20 and FiatTokenV1, because Solidity won’t let you extend two parent interfaces with differing return types.
    • In practice, for a consumer of IOptimismMintableERC20 it’s irrelevant whether the actual implementation returns a bool or not.
    • We didn’t want to make changes to IOptimismMintableERC20 nor FiatTokenV1, so we opted for not explicitly extending the IOptimismMintableERC20 interface in our OptimismFiatTokenV2_2 implementation.
    • Unfortunately this caused us to miss another important mismatch between IOptimismMintableERC20 and FiatTokenV1 - the burn function of IOptimismMintableERC20 is expected to have two parameters, _from and _amount, while FiatTokenV1's burn function only takes _amount (and always burns from msg.sender).
  • The reason we noticed only now is that there is only a very small adoption of the USDC bridge on Redstone, and even fewer people who attempted to withdraw USDC from Redstone.

How it was fixed