You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
175 lines
6.2 KiB
175 lines
6.2 KiB
4 years ago
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||
|
pragma solidity >=0.6.11;
|
||
|
|
||
3 years ago
|
// ============ Internal Imports ============
|
||
4 years ago
|
import {BridgeMessage} from "./BridgeMessage.sol";
|
||
|
import {BridgeToken} from "./BridgeToken.sol";
|
||
3 years ago
|
import {IBridgeToken} from "../../interfaces/bridge/IBridgeToken.sol";
|
||
|
import {XAppConnectionClient} from "../XAppConnectionClient.sol";
|
||
|
// ============ External Imports ============
|
||
4 years ago
|
import {
|
||
4 years ago
|
XAppConnectionManager,
|
||
4 years ago
|
TypeCasts
|
||
4 years ago
|
} from "@celo-org/optics-sol/contracts/XAppConnectionManager.sol";
|
||
3 years ago
|
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
|
||
4 years ago
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||
|
import {TypedMemView} from "@summa-tx/memview-sol/contracts/TypedMemView.sol";
|
||
|
|
||
3 years ago
|
/**
|
||
|
* @title TokenRegistry
|
||
|
* @notice manages a registry of token contracts on this chain
|
||
|
*
|
||
|
* We sort token types as "representation token" or "locally originating token".
|
||
|
* Locally originating - a token contract that was originally deployed on the local chain
|
||
|
* Representation (repr) - a token that was originally deployed on some other chain
|
||
|
*
|
||
|
* When the router handles an incoming message, it determines whether the
|
||
|
* transfer is for an asset of local origin. If not, it checks for an existing
|
||
|
* representation contract. If no such representation exists, it deploys a new
|
||
|
* representation contract. It then stores the relationship in the
|
||
|
* "reprToCanonical" and "canonicalToRepr" mappings to ensure we can always
|
||
|
* perform a lookup in either direction
|
||
|
* Note that locally originating tokens should NEVER be represented in these lookup tables.
|
||
|
*/
|
||
4 years ago
|
abstract contract TokenRegistry is XAppConnectionClient {
|
||
4 years ago
|
using TypedMemView for bytes;
|
||
|
using TypedMemView for bytes29;
|
||
|
using BridgeMessage for bytes29;
|
||
|
|
||
3 years ago
|
// We identify tokens by a TokenId:
|
||
|
// domain - 4 byte chain ID of the chain from which the token originates
|
||
|
// id - 32 byte identifier of the token address on the origin chain, in that chain's address format
|
||
4 years ago
|
struct TokenId {
|
||
|
uint32 domain;
|
||
|
bytes32 id;
|
||
|
}
|
||
|
|
||
3 years ago
|
// Contract bytecode that will be cloned to deploy
|
||
|
// new representation token contracts
|
||
4 years ago
|
address internal tokenTemplate;
|
||
4 years ago
|
|
||
3 years ago
|
// local representation token address => token ID
|
||
4 years ago
|
mapping(address => TokenId) internal reprToCanonical;
|
||
|
|
||
3 years ago
|
// hash of the tightly-packed TokenId => local representation token address
|
||
|
// If the token is of local origin, this MUST map to address(0).
|
||
4 years ago
|
mapping(bytes32 => address) internal canonicalToRepr;
|
||
|
|
||
3 years ago
|
// ======== Constructor =========
|
||
|
|
||
4 years ago
|
constructor(address _xAppConnectionManager)
|
||
|
XAppConnectionClient(_xAppConnectionManager)
|
||
|
{
|
||
4 years ago
|
tokenTemplate = address(new BridgeToken());
|
||
|
}
|
||
|
|
||
|
modifier typeAssert(bytes29 _view, BridgeMessage.Types _t) {
|
||
|
_view.assertType(uint40(_t));
|
||
|
_;
|
||
|
}
|
||
|
|
||
4 years ago
|
function setTemplate(address _newTemplate) external onlyOwner {
|
||
|
tokenTemplate = _newTemplate;
|
||
|
}
|
||
|
|
||
3 years ago
|
function _cloneTokenContract() internal returns (address result) {
|
||
|
bytes20 targetBytes = bytes20(tokenTemplate);
|
||
4 years ago
|
// solhint-disable-next-line no-inline-assembly
|
||
4 years ago
|
assembly {
|
||
4 years ago
|
let _clone := mload(0x40)
|
||
4 years ago
|
mstore(
|
||
4 years ago
|
_clone,
|
||
4 years ago
|
0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000
|
||
|
)
|
||
4 years ago
|
mstore(add(_clone, 0x14), targetBytes)
|
||
4 years ago
|
mstore(
|
||
4 years ago
|
add(_clone, 0x28),
|
||
4 years ago
|
0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000
|
||
|
)
|
||
4 years ago
|
result := create(0, _clone, 0x37)
|
||
4 years ago
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
function _deployToken(bytes29 _tokenId)
|
||
4 years ago
|
internal
|
||
|
typeAssert(_tokenId, BridgeMessage.Types.TokenId)
|
||
|
returns (address _token)
|
||
4 years ago
|
{
|
||
3 years ago
|
// Deploy the token contract by cloning tokenTemplate
|
||
|
_token = _cloneTokenContract();
|
||
4 years ago
|
// Initial details are set to a hash of the ID
|
||
3 years ago
|
bytes32 _idHash = _tokenId.keccak();
|
||
4 years ago
|
IBridgeToken(_token).setDetails(_idHash, _idHash, 18);
|
||
3 years ago
|
// store token in mappings
|
||
4 years ago
|
reprToCanonical[_token].domain = _tokenId.domain();
|
||
|
reprToCanonical[_token].id = _tokenId.id();
|
||
|
canonicalToRepr[_idHash] = _token;
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
function _ensureToken(bytes29 _tokenId)
|
||
4 years ago
|
internal
|
||
|
typeAssert(_tokenId, BridgeMessage.Types.TokenId)
|
||
|
returns (IERC20)
|
||
|
{
|
||
3 years ago
|
// Token is of local origin
|
||
4 years ago
|
if (_tokenId.domain() == _localDomain()) {
|
||
4 years ago
|
return IERC20(_tokenId.evmId());
|
||
|
}
|
||
3 years ago
|
// Token is a representation of a token of remote origin
|
||
4 years ago
|
address _local = canonicalToRepr[_tokenId.keccak()];
|
||
|
if (_local == address(0)) {
|
||
3 years ago
|
// Representation does not exist yet;
|
||
|
// deploy representation contract
|
||
4 years ago
|
_local = _deployToken(_tokenId);
|
||
4 years ago
|
}
|
||
|
return IERC20(_local);
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
function _tokenIdFor(address _token)
|
||
4 years ago
|
internal
|
||
|
view
|
||
|
returns (TokenId memory _id)
|
||
|
{
|
||
|
_id = reprToCanonical[_token];
|
||
|
if (_id.domain == 0) {
|
||
4 years ago
|
_id.domain = _localDomain();
|
||
4 years ago
|
_id.id = TypeCasts.addressToBytes32(_token);
|
||
|
}
|
||
|
}
|
||
|
|
||
3 years ago
|
function _isLocalOrigin(IERC20 _token) internal view returns (bool) {
|
||
|
return _isLocalOrigin(address(_token));
|
||
|
}
|
||
|
|
||
|
function _isLocalOrigin(address _addr) internal view returns (bool) {
|
||
|
// If the contract WAS deployed by the TokenRegistry,
|
||
|
// it will be stored in this mapping.
|
||
|
// If so, it IS NOT of local origin
|
||
4 years ago
|
if (reprToCanonical[_addr].domain != 0) {
|
||
|
return false;
|
||
|
}
|
||
3 years ago
|
// If the contract WAS NOT deployed by the TokenRegistry,
|
||
|
// and the contract exists, then it IS of local origin
|
||
|
// Return true if code exists at _addr
|
||
4 years ago
|
uint256 _codeSize;
|
||
4 years ago
|
// solhint-disable-next-line no-inline-assembly
|
||
4 years ago
|
assembly {
|
||
|
_codeSize := extcodesize(_addr)
|
||
|
}
|
||
|
return _codeSize != 0;
|
||
|
}
|
||
|
|
||
4 years ago
|
function _reprFor(bytes29 _tokenId)
|
||
4 years ago
|
internal
|
||
|
view
|
||
|
typeAssert(_tokenId, BridgeMessage.Types.TokenId)
|
||
|
returns (IERC20)
|
||
|
{
|
||
|
return IERC20(canonicalToRepr[_tokenId.keccak()]);
|
||
|
}
|
||
|
|
||
4 years ago
|
function _downcast(IERC20 _token) internal pure returns (IBridgeToken) {
|
||
4 years ago
|
return IBridgeToken(address(_token));
|
||
4 years ago
|
}
|
||
|
}
|