1 Introduction
The options for dynamically deploying contracts in Solidity contracts are:
- 1) In Wormhole 1.0
deployWrappedAsset
: https://github.com/certusone/wormhole/blob/v1.3.0/ethereum/contracts/Wormhole.sol#L212 - 2) In Optics Bridge
_deployToken
: https://github.com/celo-org/optics-monorepo/blob/main/solidity/optics-xapps/contracts/bridge/TokenRegistry.sol#L174
2. Wormhole 1.0deployWrappedAsset
The corresponding WrappedAsset contract needs to be deployed first. Refer to https://github.com/OpenZeppelin/openzeppelin-sdk/blob/master/packages/lib/contracts/upgradeability/ProxyFactory.sol .
The pre-order blogs are:
The detailed code is:
function deployWrappedAsset(bytes32 seed, uint8 token_chain, bytes32 token_address, uint8 decimals) private returns (address asset){
// Taken from https://github.com/OpenZeppelin/openzeppelin-sdk/blob/master/packages/lib/contracts/upgradeability/ProxyFactory.sol
// Licensed under MIT
bytes20 targetBytes = bytes20(wrappedAssetMaster);
assembly {
let clone := mload(0x40)
mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x14), targetBytes)
mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
asset := create2(0, clone, 0x37, seed)
}
// Call initializer
WrappedAsset(asset).initialize(token_chain, token_address, decimals);
// Store address
wrappedAssets[seed] = asset;
isWrappedAsset[asset] = true;
}
In Wormhole 2.0, the way to deploy Wrapped contracts is no longer by contract deployment, but by external call deployment:
function createWrapped(bytes memory encodedVm) external returns (address token) {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole().parseAndVerifyVM(encodedVm);
require(valid, reason);
require(verifyBridgeVM(vm), "invalid emitter");
BridgeStructs.AssetMeta memory meta = parseAssetMeta(vm.payload);
return _createWrapped(meta, vm.sequence);
}
// Creates a wrapped asset using AssetMeta
function _createWrapped(BridgeStructs.AssetMeta memory meta, uint64 sequence) internal returns (address token) {
require(meta.tokenChain != chainId(), "can only wrap tokens from foreign chains");
require(wrappedAsset(meta.tokenChain, meta.tokenAddress) == address(0), "wrapped asset already exists");
// initialize the TokenImplementation
bytes memory initialisationArgs = abi.encodeWithSelector(
TokenImplementation.initialize.selector,
bytes32ToString(meta.name),
bytes32ToString(meta.symbol),
meta.decimals,
sequence,
address(this),
meta.tokenChain,
meta.tokenAddress
);
// initialize the BeaconProxy
bytes memory constructorArgs = abi.encode(address(this), initialisationArgs);
// deployment code
bytes memory bytecode = abi.encodePacked(type(BridgeToken).creationCode, constructorArgs);
bytes32 salt = keccak256(abi.encodePacked(meta.tokenChain, meta.tokenAddress));
assembly {
token := create2(0, add(bytecode, 0x20), mload(bytecode), salt)//部署合约
if iszero(extcodesize(token)) {
revert(0, 0)
}
}
setWrappedAsset(meta.tokenChain, meta.tokenAddress, token);
}
function setWrappedAsset(uint16 tokenChainId, bytes32 tokenAddress, address wrapper) internal {
_state.wrappedAssets[tokenChainId][tokenAddress] = wrapper;
_state.isWrappedAsset[wrapper] = true;
}
3. In Optics Bridge_deployToken
The corresponding BridgeToken contract needs to be deployed first. For the actual deployment dependencies, please refer to: tokenBeacon dependencies
The pre-order blogs are:
The detailed code is:
/**
* @notice Deploy and initialize a new token contract
* @dev Each token contract is a proxy which
* points to the token upgrade beacon
* @return _token the address of the token contract
*/
function _deployToken(bytes29 _tokenId) internal returns (address _token) {
// deploy and initialize the token contract
_token = address(new UpgradeBeaconProxy(tokenBeacon, ""));
// initialize the token separately from the
IBridgeToken(_token).initialize();
// set the default token name & symbol
string memory _name;
string memory _symbol;
(_name, _symbol) = _defaultDetails(_tokenId);
IBridgeToken(_token).setDetails(_name, _symbol, 18);
// store token in mappings
representationToCanonical[_token].domain = _tokenId.domain();
representationToCanonical[_token].id = _tokenId.id();
canonicalToRepresentation[_tokenId.keccak()] = _token;
// emit event upon deploying new token
emit TokenDeployed(_tokenId.domain(), _tokenId.id(), _token);
}
The example of contract deployment contract transaction is: [Compare https://etherscan.io/tx/0x43a0836d748ccdc431a2ef63f0fbb51eeb201114db605eb19d8c693809395900 only the gas of the proveAndProcess is 188K, while the gas of the token contract +proveAndProcess is 633K. So the gas for deploying the token contract is about 445K. 】
The address of the deployed token is:
Read from Optics BridgeRouter contract:
https://etherscan.io/address/0x4fc16De11deAc71E8b2Db539d82d93BE4b486892#readProxyContract
From https://explorer.celo.org/address/0x74c0C58B99b68cF16A717279AC2d056A34ba2bFe/contracts , the deployed token corresponds to the Source token on the Celo chain.