Skip to content

Commit

Permalink
BridgePool Factory (#280)
Browse files Browse the repository at this point in the history
Co-authored-by: ZJ <zijunlin32@gmail.com>
Co-authored-by: Gustavo Vazquez <gustavo@alice.net>
  • Loading branch information
3 people committed Nov 11, 2022
1 parent 1a4312e commit 24d95f4
Show file tree
Hide file tree
Showing 13 changed files with 1,078 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@ dist/
scripts/generated
node_modules/
legacy/V1
bridge/dist
bridge/bindings
.deps
4 changes: 2 additions & 2 deletions application/objs/dspi.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func RewardDepositEquation(deposit *uint256.Uint256, dataSize32, epochInitial, e
//
// The simple equation is
//
// deposit = (dataSize + BaseDatasizeConst) * (2 + numEpochs)
// deposit = (dataSize + BaseDatasizeConst) * (2 + numEpochs)
func BaseDepositEquation(dataSize32, numEpochs32 uint32) (*uint256.Uint256, error) {
if dataSize32 > constants.MaxDataStoreSize {
// dataSize is too large so we do not perform any checks
Expand All @@ -397,7 +397,7 @@ func BaseDepositEquation(dataSize32, numEpochs32 uint32) (*uint256.Uint256, erro
//
// The simple equation is
//
// numEpochs = (deposit / (dataSize + BaseDatasizeConst)) - 2
// numEpochs = (deposit / (dataSize + BaseDatasizeConst)) - 2
//
// We have additional checks to ensure there is no integer overflow.
func NumEpochsEquation(dataSize32 uint32, deposit *uint256.Uint256) (uint32, error) {
Expand Down
85 changes: 85 additions & 0 deletions bridge/contracts/BridgePoolFactory.sol
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_
);
}
}
10 changes: 10 additions & 0 deletions bridge/contracts/interfaces/IBridgePool.sol
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 bridge/contracts/libraries/errors/BridgePoolFactoryErrors.sol
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 bridge/contracts/libraries/factory/BridgePoolFactoryBase.sol
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;
}
}
48 changes: 48 additions & 0 deletions bridge/contracts/utils/BridgePoolAddressUtil.sol
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)
)
)
)
);
}
}
Loading

0 comments on commit 24d95f4

Please sign in to comment.