-
Notifications
You must be signed in to change notification settings - Fork 159
Improve solidity code for PNA #1275
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
Changes from 1 commit
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 |
|---|---|---|
|
|
@@ -16,7 +16,7 @@ import {Address} from "./utils/Address.sol"; | |
| import {AgentExecutor} from "./AgentExecutor.sol"; | ||
| import {Agent} from "./Agent.sol"; | ||
| import {Call} from "./utils/Call.sol"; | ||
| import {ERC20} from "./ERC20.sol"; | ||
| import {Token} from "./Token.sol"; | ||
|
|
||
| /// @title Library for implementing Ethereum->Polkadot ERC20 transfers. | ||
| library Assets { | ||
|
|
@@ -108,6 +108,34 @@ library Assets { | |
| ) external returns (Ticket memory ticket) { | ||
| AssetsStorage.Layout storage $ = AssetsStorage.layout(); | ||
|
|
||
| TokenInfo storage info = $.tokenRegistry[token]; | ||
|
|
||
| if (!info.isRegistered) { | ||
| revert TokenNotRegistered(); | ||
| } | ||
|
|
||
| if (info.foreignID == bytes32(0)) { | ||
| return _sendNativeToken( | ||
| token, sender, destinationChain, destinationAddress, destinationChainFee, maxDestinationChainFee, amount | ||
| ); | ||
| } else { | ||
| return _sendForeignToken( | ||
| info.foreignID, token, sender, destinationChain, destinationAddress, destinationChainFee, amount | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| function _sendNativeToken( | ||
| address token, | ||
| address sender, | ||
| ParaID destinationChain, | ||
| MultiAddress calldata destinationAddress, | ||
| uint128 destinationChainFee, | ||
| uint128 maxDestinationChainFee, | ||
| uint128 amount | ||
| ) internal returns (Ticket memory ticket) { | ||
| AssetsStorage.Layout storage $ = AssetsStorage.layout(); | ||
|
|
||
| // Lock the funds into AssetHub's agent contract | ||
| _transferToAgent($.assetHubAgent, token, sender, amount); | ||
|
|
||
|
|
@@ -159,6 +187,42 @@ library Assets { | |
| emit IGateway.TokenSent(token, sender, destinationChain, destinationAddress, amount); | ||
| } | ||
|
|
||
| // @dev Transfer Polkadot-native tokens back to Polkadot | ||
| function _sendForeignToken( | ||
| bytes32 foreignID, | ||
| address token, | ||
| address sender, | ||
| ParaID destinationChain, | ||
| MultiAddress calldata destinationAddress, | ||
| uint128 destinationChainFee, | ||
| uint128 amount | ||
| ) internal returns (Ticket memory ticket) { | ||
| if (destinationChainFee == 0) { | ||
| revert InvalidDestinationFee(); | ||
| } | ||
|
|
||
| Token(token).burn(sender, amount); | ||
|
|
||
| ticket.dest = destinationChain; | ||
| ticket.costs = _sendForeignTokenCosts(destinationChainFee); | ||
|
|
||
| if (destinationAddress.isAddress32()) { | ||
| // The receiver has a 32-byte account ID | ||
| ticket.payload = SubstrateTypes.SendForeignTokenToAddress32( | ||
| foreignID, destinationChain, destinationAddress.asAddress32(), destinationChainFee, amount | ||
| ); | ||
| } else if (destinationAddress.isAddress20()) { | ||
| // The receiver has a 20-byte account ID | ||
| ticket.payload = SubstrateTypes.SendForeignTokenToAddress20( | ||
| foreignID, destinationChain, destinationAddress.asAddress20(), destinationChainFee, amount | ||
| ); | ||
| } else { | ||
| revert Unsupported(); | ||
| } | ||
|
Contributor
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. Need to double check the execution cost on AH(i.e.
Collaborator
Author
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. For now I'm assuming We should probably rename the field to something like |
||
|
|
||
| emit IGateway.TokenSent(token, sender, destinationChain, destinationAddress, amount); | ||
| } | ||
|
|
||
| function registerTokenCosts() external view returns (Costs memory costs) { | ||
| return _registerTokenCosts(); | ||
| } | ||
|
|
@@ -195,90 +259,36 @@ library Assets { | |
| emit IGateway.TokenRegistrationSent(token); | ||
| } | ||
|
|
||
| // @dev Transfer polkadot native tokens back | ||
| function sendForeignToken( | ||
| address agent, | ||
| address executor, | ||
| TokenInfo storage info, | ||
| address sender, | ||
| ParaID destinationChain, | ||
| MultiAddress calldata destinationAddress, | ||
| uint128 destinationChainFee, | ||
| uint128 amount | ||
| ) external returns (Ticket memory ticket) { | ||
| if (destinationChainFee == 0) { | ||
| revert InvalidDestinationFee(); | ||
| } | ||
| // Polkadot-native token: burn wrapped token | ||
| _burnToken(executor, agent, info.token, sender, amount); | ||
|
|
||
| ticket.dest = destinationChain; | ||
| ticket.costs = _sendForeignTokenCosts(destinationChainFee); | ||
|
|
||
| if (destinationAddress.isAddress32()) { | ||
| // The receiver has a 32-byte account ID | ||
| ticket.payload = SubstrateTypes.SendForeignTokenToAddress32( | ||
| info.tokenID, destinationChain, destinationAddress.asAddress32(), destinationChainFee, amount | ||
| ); | ||
| } else if (destinationAddress.isAddress20()) { | ||
| // The receiver has a 20-byte account ID | ||
| ticket.payload = SubstrateTypes.SendForeignTokenToAddress20( | ||
| info.tokenID, destinationChain, destinationAddress.asAddress20(), destinationChainFee, amount | ||
| ); | ||
| } else { | ||
| revert Unsupported(); | ||
| } | ||
|
|
||
| emit IGateway.TokenSent(info.token, sender, destinationChain, destinationAddress, amount); | ||
| } | ||
|
|
||
| function _burnToken(address agentExecutor, address agent, address token, address sender, uint256 amount) internal { | ||
| bytes memory call = abi.encodeCall(AgentExecutor.burnToken, (token, sender, amount)); | ||
| (bool success, bytes memory returndata) = (Agent(payable(agent)).invoke(agentExecutor, call)); | ||
| Call.verifyResult(success, returndata); | ||
| } | ||
|
|
||
| function _sendForeignTokenCosts(uint128 destinationChainFee) internal pure returns (Costs memory costs) { | ||
| costs.foreign = destinationChainFee; | ||
| costs.native = 0; | ||
| } | ||
|
|
||
| // @dev Register a new fungible Polkadot token for an agent | ||
| function registerForeignToken( | ||
| bytes32 agentID, | ||
| address agent, | ||
| bytes32 tokenID, | ||
| string memory name, | ||
| string memory symbol, | ||
| uint8 decimals | ||
| ) external { | ||
| function registerForeignToken(bytes32 foreignTokenID, string memory name, string memory symbol, uint8 decimals) | ||
| external | ||
| { | ||
| AssetsStorage.Layout storage $ = AssetsStorage.layout(); | ||
| if ($.tokenRegistryByID[tokenID].isRegistered == true) { | ||
| if ($.tokenAddressOf[foreignTokenID] != address(0)) { | ||
| revert TokenAlreadyRegistered(); | ||
| } | ||
| ERC20 foreignToken = new ERC20(agent, name, symbol, decimals); | ||
| address token = address(foreignToken); | ||
| TokenInfo memory info = | ||
| TokenInfo({isRegistered: true, isForeign: true, tokenID: tokenID, agentID: agentID, token: token}); | ||
| $.tokenRegistry[token] = info; | ||
| $.tokenRegistryByID[tokenID] = info; | ||
| emit IGateway.ForeignTokenRegistered(tokenID, agentID, token); | ||
| Token token = new Token(name, symbol, decimals); | ||
| TokenInfo memory info = TokenInfo({isRegistered: true, foreignID: foreignTokenID}); | ||
|
|
||
| $.tokenAddressOf[foreignTokenID] = address(token); | ||
| $.tokenRegistry[address(token)] = info; | ||
|
|
||
| emit IGateway.ForeignTokenRegistered(foreignTokenID, address(token)); | ||
| } | ||
|
|
||
| // @dev Mint foreign token from Polkadot | ||
| function mintForeignToken(address executor, address agent, bytes32 tokenID, address recipient, uint256 amount) | ||
| external | ||
| { | ||
| address token = _tokenAddressOf(tokenID); | ||
| bytes memory call = abi.encodeCall(AgentExecutor.mintToken, (token, recipient, amount)); | ||
| (bool success,) = Agent(payable(agent)).invoke(executor, call); | ||
| if (!success) { | ||
| revert TokenMintFailed(); | ||
| } | ||
| function mintForeignToken(bytes32 foreignTokenID, address recipient, uint256 amount) external { | ||
| address token = _ensureTokenAddressOf(foreignTokenID); | ||
| Token(token).mint(recipient, amount); | ||
| } | ||
|
|
||
| // @dev Transfer ERC20 to `recipient` | ||
| function transferToken(address executor, address agent, address token, address recipient, uint128 amount) | ||
| function transferNativeToken(address executor, address agent, address token, address recipient, uint128 amount) | ||
| external | ||
| { | ||
| bytes memory call = abi.encodeCall(AgentExecutor.transferToken, (token, recipient, amount)); | ||
|
|
@@ -290,15 +300,21 @@ library Assets { | |
|
|
||
| // @dev Get token address by tokenID | ||
| function tokenAddressOf(bytes32 tokenID) external view returns (address) { | ||
| return _tokenAddressOf(tokenID); | ||
| AssetsStorage.Layout storage $ = AssetsStorage.layout(); | ||
| return $.tokenAddressOf[tokenID]; | ||
| } | ||
|
|
||
| // @dev Get token address by tokenID | ||
| function _tokenAddressOf(bytes32 tokenID) internal view returns (address) { | ||
| function _ensureTokenAddressOf(bytes32 tokenID) internal view returns (address) { | ||
| AssetsStorage.Layout storage $ = AssetsStorage.layout(); | ||
| if ($.tokenRegistryByID[tokenID].isRegistered == false) { | ||
| if ($.tokenAddressOf[tokenID] == address(0)) { | ||
| revert TokenNotRegistered(); | ||
| } | ||
| return $.tokenRegistryByID[tokenID].token; | ||
| return $.tokenAddressOf[tokenID]; | ||
| } | ||
|
|
||
| function _isTokenRegistered(address token) internal view returns (bool) { | ||
| AssetsStorage.Layout storage $ = AssetsStorage.layout(); | ||
| return $.tokenRegistry[token].isRegistered; | ||
| } | ||
| } | ||
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.
Is the dest here supposed to be
assetHubParaID?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.
Yeah good catch. I've reimplemented
_sendForeignTokento mirror_sendNativeToken. So that PNA assets can be sent to assethub or a further hop from assethub.