Skip to content

Commit

Permalink
feat(protocol): USDCAdaptor deployment script + documentation (#15478)
Browse files Browse the repository at this point in the history
Co-authored-by: David <[email protected]>
  • Loading branch information
adaki2004 and davidtaikocha authored Jan 10, 2024
1 parent ea2d929 commit f4b0955
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
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();
}
}

2 comments on commit f4b0955

@vercel
Copy link

@vercel vercel bot commented on f4b0955 Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

bridge-ui-v2-internal – ./packages/bridge-ui-v2

bridge-ui-v2-internal-taikoxyz.vercel.app
bridge-ui-v2-internal.vercel.app
bridge-ui-v2-internal-git-alpha-6-taikoxyz.vercel.app

@vercel
Copy link

@vercel vercel bot commented on f4b0955 Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

bridge-ui-v2-a6 – ./packages/bridge-ui-v2

bridge-ui-v2-a6-git-alpha-6-taikoxyz.vercel.app
bridge-ui-v2-a6-taikoxyz.vercel.app
bridge-ui-v2-a6.vercel.app
bridge.katla.taiko.xyz

Please sign in to comment.