Skip to content
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

Asset from polkadot #1155

Merged
merged 64 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
7501630
Asset from polkadot
yrong Mar 13, 2024
e0c3581
Remove params unnecessary
yrong Mar 19, 2024
f070e16
Merge branch 'main' into ron/polkadot-assets-on-ethereum
yrong Mar 20, 2024
780c7bd
Experiment: return calldata from Agent instead of call Gateway directly
yrong Mar 20, 2024
27353f0
To store registries also on agent
yrong Mar 20, 2024
a543f0b
Merge branch 'main' into ron/polkadot-assets-on-ethereum
yrong Mar 21, 2024
6212597
Send polkadot tokens back
yrong Mar 22, 2024
d28d9df
Cleanup for not exceed the size limit
yrong Mar 25, 2024
c23f8bb
Add smoke test for register polkadot token
yrong Mar 26, 2024
8c8afac
Transfer relay token to Ethereum
yrong Mar 26, 2024
0b3344c
Reuse IGateway.sendToken for polkadot native asset
yrong Mar 26, 2024
2d17fca
More cleanup
yrong Mar 26, 2024
047becf
Fix encode substrate types
yrong Mar 26, 2024
3fc6cab
Add smoke test send relay token back
yrong Mar 26, 2024
e53afd0
Rename to sendForeignToken
yrong Mar 27, 2024
5e9820b
Improve ERC20.sol
yrong Mar 27, 2024
fddb9cf
Revert the change double register the token
yrong Mar 28, 2024
e5d9dd8
Migration for AssetsStorage
yrong Mar 29, 2024
9f7e99b
Rename to _sendForeignTokenCosts
yrong Mar 29, 2024
724c649
Use storage for less gas
yrong Mar 29, 2024
6eb8344
Rename to tokenAddressOf
yrong Mar 29, 2024
7c29684
Rename as _burnToken
yrong Mar 29, 2024
edccccd
_ensureAgent in Gateway
yrong Mar 29, 2024
c170eea
Remove unused
yrong Mar 29, 2024
953245b
Clean up smoke test
yrong Mar 29, 2024
adc73a3
Terminate register foreign token in Gateway
yrong Apr 4, 2024
28f513d
Mint foreign token without the callback
yrong Apr 4, 2024
9b7811d
Burn token without the callback
yrong Apr 4, 2024
b00538f
constructor ERC20 with the agent as owner
yrong Apr 4, 2024
3da6be4
Move handler to Assets.sol reduce contract size
yrong Apr 4, 2024
e051f4c
Refactoring to move transferToken to Assets
yrong Apr 4, 2024
79c1ba0
Remove TokenMinted&TokenBurned event
yrong Apr 4, 2024
fdf15aa
Move Command.RegisterToken to top level
yrong Apr 4, 2024
1e6dac0
Merge branch 'main' into ron/polkadot-assets-on-ethereum
yrong Apr 5, 2024
3439213
Fix smoke tests
yrong Apr 6, 2024
c2adf8f
Move the check before interactions
yrong Apr 8, 2024
b0b582d
Polish
yrong Apr 8, 2024
985115f
Introduce ERC20Lib.sol
yrong Apr 9, 2024
ec33548
Remove unused
yrong Apr 9, 2024
32f8d86
More test
yrong Apr 9, 2024
b2a666a
Remove AgentExecuteCommand
yrong Apr 9, 2024
17f328b
Merge branch 'bridge-next-gen' into ron/polkadot-assets-on-ethereum
yrong Apr 9, 2024
6148c03
More contract tests
yrong Apr 9, 2024
4075c11
Update contract address
yrong Apr 10, 2024
fcd7539
Add AgentExecuteCommand back for compatibility
yrong Apr 11, 2024
dc78276
Cleanup
yrong Apr 11, 2024
07545cf
Merge branch 'main' into ron/polkadot-assets-on-ethereum
yrong Apr 16, 2024
03cf01c
Add rfc
yrong Apr 16, 2024
e9013cd
Update polkadot-native-assets.md
yrong Apr 16, 2024
a028d5c
Merge branch 'bridge-next-gen' into ron/polkadot-assets-on-ethereum
yrong Apr 19, 2024
3b7e178
Update rfc
yrong Apr 22, 2024
63e8322
Update rfc with fee section
yrong Apr 22, 2024
c887e7e
Fix _calculateFee for polkadot native token
yrong May 3, 2024
f892da4
Merge branch 'main' into ron/polkadot-assets-on-ethereum
yrong Aug 22, 2024
84bb52c
Improve solidity code for PNA (#1275)
vgeddes Aug 28, 2024
64b9ef4
Remove storage migration
yrong Aug 29, 2024
de3863c
Merge branch 'main' into ron/polkadot-assets-on-ethereum
yrong Aug 29, 2024
25092dd
Merge branch 'main' into ron/polkadot-assets-on-ethereum
yrong Aug 29, 2024
001c990
Smoke test for register relay token
yrong Aug 30, 2024
324cf53
Fix fee estimation
yrong Aug 31, 2024
fe4cb73
Fix binding
yrong Aug 31, 2024
46bc45f
Remove outdated doc
yrong Aug 31, 2024
cf596f3
Fork upgrade test with sanity checks
yrong Aug 31, 2024
6efc7a1
Fix test
yrong Sep 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions contracts/src/AgentExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,15 @@ import {SubstrateTypes} from "./SubstrateTypes.sol";

import {IERC20} from "./interfaces/IERC20.sol";
import {SafeTokenTransfer, SafeNativeTransfer} from "./utils/SafeTransfer.sol";
import {ERC20} from "./ERC20.sol";
import {Gateway} from "./Gateway.sol";

/// @title Code which will run within an `Agent` using `delegatecall`.
/// @dev This is a singleton contract, meaning that all agents will execute the same code.
contract AgentExecutor {
using SafeTokenTransfer for IERC20;
using SafeNativeTransfer for address payable;

/// @dev Execute a message which originated from the Polkadot side of the bridge. In other terms,
/// the `data` parameter is constructed by the BridgeHub parachain.
///
function execute(bytes memory data) external {
(AgentExecuteCommand command, bytes memory params) = abi.decode(data, (AgentExecuteCommand, bytes));
if (command == AgentExecuteCommand.TransferToken) {
(address token, address recipient, uint128 amount) = abi.decode(params, (address, address, uint128));
_transferToken(token, recipient, amount);
}
}

/// @dev Transfer ether to `recipient`. Unlike `_transferToken` This logic is not nested within `execute`,
/// as the gateway needs to control an agent's ether balance directly.
///
Expand All @@ -33,7 +24,16 @@ contract AgentExecutor {
}

/// @dev Transfer ERC20 to `recipient`. Only callable via `execute`.
function _transferToken(address token, address recipient, uint128 amount) internal {
function transferToken(address token, address recipient, uint128 amount) external {
IERC20(token).safeTransfer(recipient, amount);
}

/// @dev Mint ERC20 token to `recipient`.
function mintToken(address token, address recipient, uint256 amount) external {
ERC20(token).mint(recipient, amount);
}

function burnToken(address token, address sender, uint256 amount) external {
ERC20(token).burn(sender, amount);
}
}
123 changes: 118 additions & 5 deletions contracts/src/Assets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ import {IGateway} from "./interfaces/IGateway.sol";
import {SafeTokenTransferFrom} from "./utils/SafeTransfer.sol";

import {AssetsStorage, TokenInfo} from "./storage/AssetsStorage.sol";
import {CoreStorage} from "./storage/CoreStorage.sol";

import {SubstrateTypes} from "./SubstrateTypes.sol";
import {ParaID, MultiAddress, Ticket, Costs} from "./Types.sol";
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";

/// @title Library for implementing Ethereum->Polkadot ERC20 transfers.
library Assets {
Expand All @@ -24,6 +30,10 @@ library Assets {
error TokenNotRegistered();
error Unsupported();
error InvalidDestinationFee();
error AgentDoesNotExist();
error TokenAlreadyRegistered();
error TokenMintFailed();
error TokenTransferFailed();

function isTokenRegistered(address token) external view returns (bool) {
return AssetsStorage.layout().tokenRegistry[token].isRegistered;
Expand Down Expand Up @@ -83,11 +93,6 @@ library Assets {
) external returns (Ticket memory ticket) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();

TokenInfo storage info = $.tokenRegistry[token];
if (!info.isRegistered) {
revert TokenNotRegistered();
}

// Lock the funds into AssetHub's agent contract
_transferToAgent($.assetHubAgent, token, sender, amount);

Expand Down Expand Up @@ -174,4 +179,112 @@ 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) {
// Polkadot-native token: burn wrapped token
_burnToken(executor, agent, info.token, sender, amount);

if (destinationChainFee == 0) {
revert InvalidDestinationFee();
}
claravanstaden marked this conversation as resolved.
Show resolved Hide resolved

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 {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
if ($.tokenRegistryByID[tokenID].isRegistered == true) {
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);
}

// @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();
}
}

// @dev Transfer ERC20 to `recipient`
function transferToken(address executor, address agent, address token, address recipient, uint128 amount)
external
{
bytes memory call = abi.encodeCall(AgentExecutor.transferToken, (token, recipient, amount));
(bool success,) = Agent(payable(agent)).invoke(executor, call);
if (!success) {
revert TokenTransferFailed();
}
}

// @dev Get token address by tokenID
function tokenAddressOf(bytes32 tokenID) external view returns (address) {
return _tokenAddressOf(tokenID);
}

// @dev Get token address by tokenID
function _tokenAddressOf(bytes32 tokenID) internal view returns (address) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
if ($.tokenRegistryByID[tokenID].isRegistered == false) {
revert TokenNotRegistered();
}
return $.tokenRegistryByID[tokenID].token;
}
}
Loading
Loading