-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: custom token registrars #116
Changes from 16 commits
e9c1939
10e4503
2866ecf
1127093
943de1f
09b66bb
f4ef75b
4569336
14fecc4
8682513
1bd5f9b
3391cd8
f82b23c
f0a7e29
04972f9
df97235
8ea68ca
afdb98d
fc0d72b
4b0586d
8c28d6d
3ef1db2
1566fec
ed4be65
4ec3612
844bfb6
a8db922
6c4e38f
51a3ce4
68fcbdd
7b69d55
7815108
de5bcda
fe2af9f
6147bf3
09fa22d
551f67e
a6216f0
992ba96
05c1855
a593d69
2831316
394dca9
c555815
fc791d1
6cc49c0
b8ab648
a8aac7d
7d4d5bd
ca65004
f190a59
9ac0cf7
3b76ac1
44dc8fc
83353a6
e692dbc
f2d76c2
0f78048
f7f4b47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
interface ICanonicalTokenRegistrar { | ||
error ZeroAddress(); | ||
|
||
function chainName() external view returns (string memory); | ||
|
||
function chainNameHash() external view returns (bytes32); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
interface IStandardizedTokenRegistrar { | ||
error ZeroAddress(); | ||
|
||
function chainName() external view returns (string memory); | ||
|
||
function chainNameHash() external view returns (bytes32); | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,63 @@ | ||||||
// SPDX-License-Identifier: MIT | ||||||
|
||||||
pragma solidity ^0.8.0; | ||||||
|
||||||
import { IInterchainTokenService } from '../interfaces/IInterchainTokenService.sol'; | ||||||
import { ICanonicalTokenRegistrar } from '../interfaces/ICanonicalTokenRegistrar.sol'; | ||||||
import { ITokenManagerType } from '../interfaces/ITokenManagerType.sol'; | ||||||
import { IERC20Named } from '../interfaces/IERC20Named.sol'; | ||||||
|
||||||
import { Multicall } from '../utils/Multicall.sol'; | ||||||
|
||||||
contract CanonicalTokenRegistrar is ICanonicalTokenRegistrar, ITokenManagerType, Multicall { | ||||||
IInterchainTokenService public immutable service; | ||||||
string public chainName; | ||||||
bytes32 public immutable chainNameHash; | ||||||
|
||||||
bytes32 internal constant PREFIX_CANONICAL_TOKEN_SALT = keccak256('canonical-token-salt'); | ||||||
|
||||||
constructor(address interchainTokenServiceAddress, string memory chainName_) { | ||||||
if (interchainTokenServiceAddress == address(0)) revert ZeroAddress(); | ||||||
service = IInterchainTokenService(interchainTokenServiceAddress); | ||||||
chainName = chainName_; | ||||||
chainNameHash = keccak256(bytes(chainName_)); | ||||||
} | ||||||
|
||||||
function getCanonicalTokenSalt(address tokenAddress) public view returns (bytes32 salt) { | ||||||
salt = keccak256(abi.encode(PREFIX_CANONICAL_TOKEN_SALT, chainNameHash, tokenAddress)); | ||||||
} | ||||||
|
||||||
function getCanonicalTokenId(address tokenAddress) public view returns (bytes32 tokenId) { | ||||||
tokenId = service.getCustomTokenId(address(this), getCanonicalTokenSalt(tokenAddress)); | ||||||
} | ||||||
|
||||||
function registerCanonicalToken(address tokenAddress) external payable returns (bytes32 tokenId) { | ||||||
bytes memory params = abi.encode('', tokenAddress); | ||||||
bytes32 salt = getCanonicalTokenSalt(tokenAddress); | ||||||
tokenId = service.deployCustomTokenManager(salt, TokenManagerType.LOCK_UNLOCK, params); | ||||||
} | ||||||
|
||||||
function deployAndRegisterRemoteCanonicalToken(bytes32 salt, string calldata destinationChain, uint256 gasValue) external payable { | ||||||
// This ensures that the token manages has been deployed by this address, so it's safe to trust it. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
bytes32 tokenId = service.getCustomTokenId(address(this), salt); | ||||||
IERC20Named token = IERC20Named(service.getTokenAddress(tokenId)); | ||||||
// The 3 lines below will revert if the token manager does not exist. | ||||||
string memory tokenName = token.name(); | ||||||
string memory tokenSymbol = token.symbol(); | ||||||
uint8 tokenDecimals = token.decimals(); | ||||||
|
||||||
// slither-disable-next-line arbitrary-send-eth | ||||||
service.deployAndRegisterRemoteStandardizedToken{ value: gasValue }( | ||||||
salt, | ||||||
tokenName, | ||||||
tokenSymbol, | ||||||
tokenDecimals, | ||||||
'', | ||||||
'', | ||||||
0, | ||||||
'', | ||||||
destinationChain, | ||||||
gasValue | ||||||
); | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import { SafeTokenTransfer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol'; | ||
|
||
import { IInterchainTokenService } from '../interfaces/IInterchainTokenService.sol'; | ||
import { IStandardizedTokenRegistrar } from '../interfaces/IStandardizedTokenRegistrar.sol'; | ||
import { ITokenManagerType } from '../interfaces/ITokenManagerType.sol'; | ||
import { ITokenManager } from '../interfaces/ITokenManager.sol'; | ||
import { IStandardizedToken } from '../interfaces/IStandardizedToken.sol'; | ||
|
||
import { Multicall } from '../utils/Multicall.sol'; | ||
|
||
import { AddressBytesUtils } from '../libraries/AddressBytesUtils.sol'; | ||
|
||
contract StandardizedTokenRegistrar is IStandardizedTokenRegistrar, ITokenManagerType, Multicall { | ||
using AddressBytesUtils for bytes; | ||
using AddressBytesUtils for address; | ||
using SafeTokenTransfer for IStandardizedToken; | ||
|
||
IInterchainTokenService public immutable service; | ||
string public chainName; | ||
bytes32 public immutable chainNameHash; | ||
|
||
struct DeployParams { | ||
address deployer; | ||
bytes distributor; | ||
bytes operator; | ||
} | ||
|
||
mapping(bytes32 => DeployParams) public deploymentParameterMap; | ||
Comment on lines
+28
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unused? |
||
|
||
bytes32 internal constant PREFIX_STANDARDIZED_TOKEN_SALT = keccak256('standardized-token-salt'); | ||
|
||
constructor(address interchainTokenServiceAddress, string memory chainName_) { | ||
if (interchainTokenServiceAddress == address(0)) revert ZeroAddress(); | ||
service = IInterchainTokenService(interchainTokenServiceAddress); | ||
chainName = chainName_; | ||
chainNameHash = keccak256(bytes(chainName_)); | ||
} | ||
|
||
function getStandardizedTokenSalt(address deployer, bytes32 salt) public view returns (bytes32) { | ||
return keccak256(abi.encode(PREFIX_STANDARDIZED_TOKEN_SALT, chainNameHash, deployer, salt)); | ||
} | ||
|
||
function getStandardizedTokenId(address deployer, bytes32 salt) public view returns (bytes32 tokenId) { | ||
tokenId = service.getCustomTokenId(address(this), getStandardizedTokenSalt(deployer, salt)); | ||
} | ||
|
||
function getStandardizedTokenAddress(address deployer, bytes32 salt) public view returns (address tokenAddress) { | ||
tokenAddress = service.getStandardizedTokenAddress(getStandardizedTokenId(deployer, salt)); | ||
} | ||
|
||
function deployStandardizedToken( | ||
bytes32 salt, | ||
string calldata name, | ||
string calldata symbol, | ||
uint8 decimals, | ||
uint256 mintAmount, | ||
address distributor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let operator still be different than sender, since it's an argument for the remote deploy method as well? |
||
) external payable { | ||
address sender = msg.sender; | ||
salt = getStandardizedTokenSalt(sender, salt); | ||
bytes32 tokenId = service.getCustomTokenId(address(this), salt); | ||
|
||
service.deployAndRegisterStandardizedToken(salt, name, symbol, decimals, mintAmount, distributor); | ||
ITokenManager tokenManager = ITokenManager(service.getTokenManagerAddress(tokenId)); | ||
tokenManager.transferOperatorship(sender); | ||
|
||
IStandardizedToken token = IStandardizedToken(service.getStandardizedTokenAddress(tokenId)); | ||
token.safeTransfer(sender, mintAmount); | ||
} | ||
|
||
function deployRemoteStandarizedToken( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we combine the two deploy methods into one? The interchain address tracker has the current chain, so we can specialize the logic by comparing the chain name being passed. Reduces number of entrypoints. Perhaps we can simplify this in ITS itself. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, we could combine deploy custom token manager current/remote into one method |
||
bytes32 salt, | ||
bool sameDistributor, | ||
string memory destinationChain, | ||
uint256 gasValue | ||
) external payable { | ||
string memory tokenName; | ||
string memory tokenSymbol; | ||
uint8 tokenDecimals; | ||
bytes memory distributor = ''; | ||
bytes memory operator; | ||
|
||
{ | ||
address sender = msg.sender; | ||
salt = getStandardizedTokenSalt(sender, salt); | ||
bytes32 tokenId = service.getCustomTokenId(address(this), salt); | ||
|
||
IStandardizedToken token = IStandardizedToken(service.getStandardizedTokenAddress(tokenId)); | ||
ITokenManager tokenManager = ITokenManager(service.getTokenManagerAddress(tokenId)); | ||
|
||
tokenName = token.name(); | ||
tokenSymbol = token.symbol(); | ||
tokenDecimals = token.decimals(); | ||
if (sameDistributor) distributor = token.distributor().toBytes(); | ||
operator = tokenManager.operator().toBytes(); | ||
} | ||
|
||
_deployAndRegisterRemoteStandardizedToken( | ||
salt, | ||
tokenName, | ||
tokenSymbol, | ||
tokenDecimals, | ||
distributor, | ||
operator, | ||
destinationChain, | ||
gasValue | ||
); | ||
} | ||
|
||
function _deployAndRegisterRemoteStandardizedToken( | ||
bytes32 salt, | ||
string memory tokenName, | ||
string memory tokenSymbol, | ||
uint8 tokenDecimals, | ||
bytes memory distributor, | ||
bytes memory operator, | ||
string memory destinationChain, | ||
uint256 gasValue | ||
) internal { | ||
// slither-disable-next-line arbitrary-send-eth | ||
service.deployAndRegisterRemoteStandardizedToken{ value: gasValue }( | ||
salt, | ||
tokenName, | ||
tokenSymbol, | ||
tokenDecimals, | ||
distributor, | ||
'', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mintTo isn't being provided so I think the initial mint will be skipped? Can you add that, and add test coverage for it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
0, | ||
operator, | ||
destinationChain, | ||
gasValue | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall we remove the canonical register/deploy methods from ITS?