-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: ZJ <zijunlin32@gmail.com> Co-authored-by: Gustavo Vazquez <gustavo@alice.net>
- Loading branch information
1 parent
1a4312e
commit 24d95f4
Showing
13 changed files
with
1,078 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,3 +28,6 @@ dist/ | |
scripts/generated | ||
node_modules/ | ||
legacy/V1 | ||
bridge/dist | ||
bridge/bindings | ||
.deps |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// SPDX-License-Identifier: MIT-open-group | ||
pragma solidity ^0.8.16; | ||
|
||
import "contracts/libraries/errors/BridgePoolFactoryErrors.sol"; | ||
import "contracts/libraries/factory/BridgePoolFactoryBase.sol"; | ||
|
||
/// @custom:salt BridgePoolFactory | ||
/// @custom:deploy-type deployUpgradeable | ||
contract BridgePoolFactory is BridgePoolFactoryBase { | ||
constructor() BridgePoolFactoryBase() {} | ||
|
||
/** | ||
* @notice Deploys a new bridge to pass tokens to our chain from the specified ERC contract. | ||
* The pools are created as thin proxies (EIP1167) routing to versioned implementations identified by corresponding salt. | ||
* @param tokenType_ type of token (1=ERC20, 2=ERC721) | ||
* @param ercContract_ address of ERC20 source token contract | ||
* @param implementationVersion_ version of BridgePool implementation to use | ||
*/ | ||
function deployNewNativePool( | ||
uint8 tokenType_, | ||
address ercContract_, | ||
uint16 implementationVersion_ | ||
) public onlyFactoryOrPublicEnabled { | ||
_deployNewNativePool(tokenType_, ercContract_, implementationVersion_); | ||
} | ||
|
||
/** | ||
* @notice deploys logic for bridge pools and stores it in a logicAddresses mapping | ||
* @param tokenType_ type of token (1=ERC20, 2=ERC721) | ||
* @param chainId_ address of ERC20 source token contract | ||
* @param value_ amount of eth to send to the contract on creation | ||
* @param deployCode_ logic contract deployment bytecode | ||
*/ | ||
function deployPoolLogic( | ||
uint8 tokenType_, | ||
uint256 chainId_, | ||
uint256 value_, | ||
bytes calldata deployCode_ | ||
) public onlyFactory returns (address) { | ||
return _deployPoolLogic(tokenType_, chainId_, value_, deployCode_); | ||
} | ||
|
||
/** | ||
* @dev enables or disables public pool deployment | ||
**/ | ||
function togglePublicPoolDeployment() public onlyFactory { | ||
_togglePublicPoolDeployment(); | ||
} | ||
|
||
/** | ||
* @notice calculates bridge pool address with associated bytes32 salt | ||
* @param bridgePoolSalt_ bytes32 salt associated with the pool, calculated with getBridgePoolSalt | ||
* @return poolAddress calculated calculated bridgePool Address | ||
*/ | ||
function lookupBridgePoolAddress(bytes32 bridgePoolSalt_) | ||
public | ||
view | ||
returns (address poolAddress) | ||
{ | ||
poolAddress = BridgePoolAddressUtil.getBridgePoolAddress(bridgePoolSalt_, address(this)); | ||
} | ||
|
||
/** | ||
* @notice calculates salt for a BridgePool contract based on ERC contract's address, tokenType, chainID and version_ | ||
* @param tokenContractAddr_ address of ERC Token contract | ||
* @param tokenType_ type of token (1=ERC20, 2=ERC721) | ||
* @param version_ version of the implementation | ||
* @param chainID_ chain ID | ||
* @return calculated calculated salt | ||
*/ | ||
function getBridgePoolSalt( | ||
address tokenContractAddr_, | ||
uint8 tokenType_, | ||
uint256 chainID_, | ||
uint16 version_ | ||
) public pure returns (bytes32) { | ||
return | ||
BridgePoolAddressUtil.getBridgePoolSalt( | ||
tokenContractAddr_, | ||
tokenType_, | ||
chainID_, | ||
version_ | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// SPDX-License-Identifier: MIT-open-group | ||
pragma solidity ^0.8.11; | ||
|
||
interface IBridgePool { | ||
function initialize(address ercContract_) external; | ||
|
||
function deposit(address msgSender, bytes calldata depositParameters) external; | ||
|
||
function withdraw(bytes memory _txInPreImage, bytes[4] memory _proofs) external; | ||
} |
12 changes: 12 additions & 0 deletions
12
bridge/contracts/libraries/errors/BridgePoolFactoryErrors.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: MIT-open-group | ||
pragma solidity ^0.8.16; | ||
|
||
library BridgePoolFactoryErrors { | ||
error FailedToDeployLogic(); | ||
error PoolVersionNotSupported(uint16 version); | ||
error StaticPoolDeploymentFailed(bytes32 salt_); | ||
error UnexistentBridgePoolImplementationVersion(uint16 version); | ||
error UnableToDeployBridgePool(bytes32 salt_); | ||
error InsufficientFunds(); | ||
error PublicPoolDeploymentTemporallyDisabled(); | ||
} |
204 changes: 204 additions & 0 deletions
204
bridge/contracts/libraries/factory/BridgePoolFactoryBase.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
// SPDX-License-Identifier: MIT-open-group | ||
pragma solidity ^0.8.16; | ||
|
||
import "contracts/utils/ImmutableAuth.sol"; | ||
import "contracts/libraries/errors/BridgePoolFactoryErrors.sol"; | ||
import "contracts/interfaces/IBridgePool.sol"; | ||
import "contracts/utils/BridgePoolAddressUtil.sol"; | ||
import "@openzeppelin/contracts/utils/Strings.sol"; | ||
|
||
abstract contract BridgePoolFactoryBase is ImmutableFactory { | ||
enum TokenType { | ||
ERC20, | ||
ERC721, | ||
ERC1155 | ||
} | ||
enum PoolType { | ||
NATIVE, | ||
EXTERNAL | ||
} | ||
//chainid of layer 1 chain, 1 for ether mainnet | ||
uint256 internal immutable _chainID; | ||
bool public publicPoolDeploymentEnabled; | ||
address internal _implementation; | ||
mapping(string => address) internal _logicAddresses; | ||
//mapping of native and external pools to mapping of pool types to most recent version of logic | ||
mapping(PoolType => mapping(TokenType => uint16)) internal _logicVersionsDeployed; | ||
//existing pools | ||
mapping(address => bool) public poolExists; | ||
event BridgePoolCreated(address poolAddress, address token); | ||
|
||
modifier onlyFactoryOrPublicEnabled() { | ||
if (msg.sender != _factoryAddress() && !publicPoolDeploymentEnabled) { | ||
revert BridgePoolFactoryErrors.PublicPoolDeploymentTemporallyDisabled(); | ||
} | ||
_; | ||
} | ||
|
||
constructor() ImmutableFactory(msg.sender) { | ||
_chainID = block.chainid; | ||
} | ||
|
||
// NativeERC20V! | ||
/** | ||
* @notice returns bytecode for a Minimal Proxy (EIP-1167) that routes to BridgePool implementation | ||
*/ | ||
// solhint-disable-next-line | ||
fallback() external { | ||
address implementation_ = _implementation; | ||
assembly { | ||
let ptr := mload(0x40) | ||
mstore(ptr, shl(176, 0x363d3d373d3d3d363d73)) //10 | ||
mstore(add(ptr, 10), shl(96, implementation_)) //20 | ||
mstore(add(ptr, 30), shl(136, 0x5af43d82803e903d91602b57fd5bf3)) //15 | ||
return(ptr, 45) | ||
} | ||
} | ||
|
||
/** | ||
* @notice returns the most recent version of the pool logic | ||
* @param chainId_ native chainID of the token ie 1 for ethereum erc20 | ||
* @param tokenType_ type of token 0 for ERC20 1 for ERC721 and 2 for ERC1155 | ||
*/ | ||
function getLatestPoolLogicVersion(uint256 chainId_, uint8 tokenType_) | ||
public | ||
view | ||
returns (uint16) | ||
{ | ||
if (chainId_ != _chainID) { | ||
return _logicVersionsDeployed[PoolType.EXTERNAL][TokenType(tokenType_)]; | ||
} else { | ||
return _logicVersionsDeployed[PoolType.NATIVE][TokenType(tokenType_)]; | ||
} | ||
} | ||
|
||
function _deployPoolLogic( | ||
uint8 tokenType_, | ||
uint256 chainId_, | ||
uint256 value_, | ||
bytes calldata deployCode_ | ||
) internal returns (address) { | ||
address addr; | ||
uint32 codeSize; | ||
bool native = true; | ||
uint16 version; | ||
bytes memory alicenetFactoryAddress = abi.encodePacked( | ||
bytes32(uint256(uint160(_factoryAddress()))) | ||
); | ||
assembly { | ||
let ptr := mload(0x40) | ||
calldatacopy(ptr, deployCode_.offset, deployCode_.length) | ||
// add bytes32 alicenet factory address as parameter to constructor | ||
mstore(add(ptr, deployCode_.length), alicenetFactoryAddress) | ||
addr := create(value_, ptr, add(deployCode_.length, 32)) | ||
codeSize := extcodesize(addr) | ||
} | ||
if (codeSize == 0) { | ||
revert BridgePoolFactoryErrors.FailedToDeployLogic(); | ||
} | ||
if (chainId_ != _chainID) { | ||
native = false; | ||
version = _logicVersionsDeployed[PoolType.EXTERNAL][TokenType(tokenType_)] + 1; | ||
_logicVersionsDeployed[PoolType.EXTERNAL][TokenType(tokenType_)] = version; | ||
} else { | ||
version = _logicVersionsDeployed[PoolType.NATIVE][TokenType(tokenType_)] + 1; | ||
_logicVersionsDeployed[PoolType.NATIVE][TokenType(tokenType_)] = version; | ||
} | ||
_logicAddresses[_getImplementationAddressKey(tokenType_, version, native)] = addr; | ||
return addr; | ||
} | ||
|
||
/** | ||
* @dev enables or disables public pool deployment | ||
**/ | ||
function _togglePublicPoolDeployment() internal { | ||
publicPoolDeploymentEnabled = !publicPoolDeploymentEnabled; | ||
} | ||
|
||
/** | ||
* @notice Deploys a new bridge to pass tokens to layer 2 chain from the specified ERC contract. | ||
* The pools are created as thin proxies (EIP1167) routing to versioned implementations identified by correspondent salt. | ||
* @param tokenType_ type of token (0=ERC20, 1=ERC721, 2=ERC1155) | ||
* @param ercContract_ address of ERC20 source token contract | ||
* @param poolVersion_ version of BridgePool implementation to use | ||
*/ | ||
function _deployNewNativePool( | ||
uint8 tokenType_, | ||
address ercContract_, | ||
uint16 poolVersion_ | ||
) internal { | ||
bool native = true; | ||
//calculate the unique salt for the bridge pool | ||
bytes32 bridgePoolSalt = BridgePoolAddressUtil.getBridgePoolSalt( | ||
ercContract_, | ||
tokenType_, | ||
_chainID, | ||
poolVersion_ | ||
); | ||
//calculate the address of the pool's logic contract | ||
address implementation = _logicAddresses[ | ||
_getImplementationAddressKey(tokenType_, poolVersion_, native) | ||
]; | ||
_implementation = implementation; | ||
//check if the logic exists for the specified pool | ||
uint256 implementationSize; | ||
assembly { | ||
implementationSize := extcodesize(implementation) | ||
} | ||
if (implementationSize == 0) { | ||
revert BridgePoolFactoryErrors.PoolVersionNotSupported(poolVersion_); | ||
} | ||
address contractAddr = _deployStaticPool(bridgePoolSalt); | ||
IBridgePool(contractAddr).initialize(ercContract_); | ||
emit BridgePoolCreated(contractAddr, ercContract_); | ||
} | ||
|
||
/** | ||
* @notice creates a BridgePool contract with specific salt and bytecode returned by this contract fallback | ||
* @param salt_ salt of the implementation contract | ||
* @return contractAddr the address of the BridgePool | ||
*/ | ||
function _deployStaticPool(bytes32 salt_) internal returns (address contractAddr) { | ||
uint256 contractSize; | ||
assembly { | ||
let ptr := mload(0x40) | ||
mstore(ptr, shl(136, 0x5880818283335afa3d82833e3d82f3)) | ||
contractAddr := create2(0, ptr, 15, salt_) | ||
contractSize := extcodesize(contractAddr) | ||
} | ||
if (contractSize == 0) { | ||
revert BridgePoolFactoryErrors.StaticPoolDeploymentFailed(salt_); | ||
} | ||
poolExists[contractAddr] = true; | ||
return contractAddr; | ||
} | ||
|
||
/** | ||
* @notice calculates salt for a BridgePool implementation contract based on tokenType and version | ||
* @param tokenType_ type of token (0=ERC20, 1=ERC721, 2=ERC1155) | ||
* @param version_ version of the implementation | ||
* @param native_ boolean flag to specifier native or external token pools | ||
* @return calculated key | ||
*/ | ||
function _getImplementationAddressKey( | ||
uint8 tokenType_, | ||
uint16 version_, | ||
bool native_ | ||
) internal pure returns (string memory) { | ||
string memory key; | ||
if (native_) { | ||
key = "Native"; | ||
} else { | ||
key = "External"; | ||
} | ||
if (tokenType_ == uint8(TokenType.ERC20)) { | ||
key = string.concat(key, "ERC20"); | ||
} else if (tokenType_ == uint8(TokenType.ERC721)) { | ||
key = string.concat(key, "ERC721"); | ||
} else if (tokenType_ == uint8(TokenType.ERC1155)) { | ||
key = string.concat(key, "ERC1155"); | ||
} | ||
key = string.concat(key, "V", Strings.toString(version_)); | ||
return key; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// SPDX-License-Identifier: MIT-open-group | ||
pragma solidity ^0.8.16; | ||
|
||
library BridgePoolAddressUtil { | ||
/** | ||
* @notice calculates salt for a BridgePool contract based on ERC contract's address, tokenType, chainID and version_ | ||
* @param tokenContractAddr_ address of ERC contract of BridgePool | ||
* @param tokenType_ type of token (0=ERC20, 1=ERC721, 2=ERC1155) | ||
* @param version_ version of the implementation | ||
* @param chainID_ chain ID | ||
* @return calculated calculated salt | ||
*/ | ||
function getBridgePoolSalt( | ||
address tokenContractAddr_, | ||
uint8 tokenType_, | ||
uint256 chainID_, | ||
uint16 version_ | ||
) internal pure returns (bytes32) { | ||
return | ||
keccak256( | ||
bytes.concat( | ||
keccak256(abi.encodePacked(tokenContractAddr_)), | ||
keccak256(abi.encodePacked(tokenType_)), | ||
keccak256(abi.encodePacked(chainID_)), | ||
keccak256(abi.encodePacked(version_)) | ||
) | ||
); | ||
} | ||
|
||
function getBridgePoolAddress(bytes32 bridgePoolSalt_, address bridgeFactory_) | ||
internal | ||
pure | ||
returns (address) | ||
{ | ||
// works: 5880818283335afa3d82833e3d82f3 | ||
bytes32 initCodeHash = 0xf231e946a2f88d89eafa7b43271c54f58277304b93ac77d138d9b0bb3a989b6d; | ||
return | ||
address( | ||
uint160( | ||
uint256( | ||
keccak256( | ||
abi.encodePacked(hex"ff", bridgeFactory_, bridgePoolSalt_, initCodeHash) | ||
) | ||
) | ||
) | ||
); | ||
} | ||
} |
Oops, something went wrong.