Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): USDCAdaptor deployment script + documentation #15478

Merged
merged 2 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions packages/protocol/docs/native_token_support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Taiko native vs. wrapped token bridging

![Wrapped_vs_Native](./native_bridge/native_support.png "Wrapped vs. Native bridging")

Taiko's briding concept is a lock-and-mint type. It simply means (the red path above) on the canonical chain we take custody of the assets and on the destination chain we mint the wrapped counterpart. When someone wants to bridge back (from destination to canonical) it will first burn the tokens, then release the funds on the canonical chain.

But there might be some incentives (e.g.: adoption, liquidity fragmentation, etc.) when deploying a native token on the destination chain is beneficial. For this reason Taiko introduced the possibilty of deploying the canonical assets (together with all their sub/parent/proxy contracts) and plug it into our ERC20Vault via adaptors (green path).

Important to note that while wrapped asset briding is 'automatical', the native one requires the willingness and efforts from Taiko side (and maybe also original token issuer green light to recognise officially as "native"), to support that type of asset-transfer.

## Howto

There are some steps to do in order to facilitate native token bridging. In the next steps, here is a TLDR breakdown how we do it with USDC.

1. Deploy the same (bytecode equivalent) ERC-20 token on L2. An example of the contracts + deployments can be found in our [USDC repo](https://github.com/taikoxyz/USDC).
2. Deploy adaptor (e.g.: [USDC adaptor](../contracts/tokenvault/adaptors/USDCAdaptor.sol)). As this will serve as the plug-in to our `ERC20Vault` for custom (native) tokens. This adaptor serves multiple purposes. It is also a wrapper around the native token in a way - that it matches our conform `ERC20Vault` interfaces so that we can be sure any kind of native ERC-20 can be supported on L2. Also can handle custom logic required by the native asset (roles handling, specific logic, etc.).
3. Transfer the ownership (if not already owned by) to `ERC20Vault` owner since those 2 have to be owned by the same address. (!IMPORTANT! Not the token owned by the same owner, but the token adpter! USDC owner will still be Circle on L2.)
4. Since our bridge is permissionless, there might have been some USDC bridge operations in the past. It would mean, there is already an existing `BridgedUSDC` on our L2. To overcome liquidity fragmentation, we (Taiko) need to call `ERC20Vault` `changeBridgedToken()` function with the appropriate parameters. This way the "old" `BridgedUSDC` can be migrated to this new native token and the bridging operation will mint into the new token frm that point on.

The above steps (2. - 4.) is incorporated into the script [DeployUSDCAdaptor.s.sol](../script/DeployUSDCAdaptor.s.sol).
96 changes: 96 additions & 0 deletions packages/protocol/script/DeployUSDCAdaptor.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: MIT
// _____ _ _ _ _
// |_ _|_ _(_) |_____ | | __ _| |__ ___
// | |/ _` | | / / _ \ | |__/ _` | '_ (_-<
// |_|\__,_|_|_\_\___/ |____\__,_|_.__/__/
//
// Email: [email protected]
// Website: https://taiko.xyz
// GitHub: https://github.com/taikoxyz
// Discord: https://discord.gg/taikoxyz
// Twitter: https://twitter.com/taikoxyz
// Blog: https://mirror.xyz/labs.taiko.eth
// Youtube: https://www.youtube.com/@taikoxyz

pragma solidity 0.8.20;

import "../contracts/tokenvault/adaptors/USDCAdaptor.sol";
import "../contracts/tokenvault/ERC20Vault.sol";
import "../test/DeployCapability.sol";

/// @title DeployUSDCAdaptor
/// @notice This script deploys the adaptor contract for USDC.
contract DeployUSDCAdaptor is DeployCapability {
address public usdcProxyL2 = vm.envAddress("NATIVE_USDC_PROXY_ON_L2");
address public usdcProxyL1 = vm.envAddress("NATIVE_USDC_PROXY_ON_L1");
address public l2SharedAddressManager = vm.envAddress("L2_SHARED_ADDRESS_MANAGER");
address public erc20Vault = vm.envAddress("ERC20_VAULT_ADDRESS");
address public erc20VaultOwner = vm.envAddress("ERC20_VAULT_OWNER");

uint256 public deployerPrivKey = vm.envUint("ADAPTOR_DEPLOYER_PRIVATE_KEY");
uint256 public masterMinterPrivKey = vm.envUint("MASTER_MINTER_PRIVATE_KEY");

uint256 public erc20VaultOwnerPrivKey = vm.envUint("ERC20_VAULT_OWNER_PRIVATE_KEY");

// Fixed, not changed. (From circle contracts)
// https://holesky.etherscan.io/tx/0xbfb68e7d92506498553e09ee22aba0adc23401108d508fc92f56da5e42ffc828
bytes4 public configureMinterSelector = 0x4e44d956;

error CANNOT_CONFIGURE_MINTER();
error CANNOT_CHANGE_BRIDGED_TOKEN();

function setUp() external { }

function run() external {
require(deployerPrivKey != 0, "invalid deplyoer priv key");
require(masterMinterPrivKey != 0, "invalid master minter priv key");
vm.startBroadcast(deployerPrivKey);
// Verify this contract after deployment (!)
address adaptorProxy = deployProxy({
name: "usdc_adaptor",
impl: address(new USDCAdaptor()),
data: abi.encodeCall(USDCAdaptor.init, (l2SharedAddressManager, IUSDC(usdcProxyL2)))
});

USDCAdaptor(adaptorProxy).transferOwnership(erc20VaultOwner);

vm.stopBroadcast();

// Grant the adaptor the minter role by master minter
vm.startBroadcast(masterMinterPrivKey);
(bool success, bytes memory retVal) = address(usdcProxyL2).call(
abi.encodeWithSelector(configureMinterSelector, adaptorProxy, type(uint256).max)
);

if (!success) {
console2.log("Error is:");
console2.logBytes(retVal);
revert CANNOT_CONFIGURE_MINTER();
}

vm.stopBroadcast();

vm.startBroadcast(erc20VaultOwnerPrivKey);
ERC20Vault.CanonicalERC20 memory canonicalToken = ERC20Vault.CanonicalERC20({
chainId: 17_000, // On mainnet, Ethereum chainID
addr: usdcProxyL1, // On mainnet, USDC contract address
decimals: 6,
symbol: "USDC",
name: "USD Coin"
});

(success, retVal) = erc20Vault.call(
abi.encodeWithSelector(
ERC20Vault.changeBridgedToken.selector, canonicalToken, adaptorProxy
)
);

if (!success) {
console2.log("Error is:");
console2.logBytes(retVal);
revert CANNOT_CHANGE_BRIDGED_TOKEN();
}

vm.stopBroadcast();
}
}
Loading