-
Notifications
You must be signed in to change notification settings - Fork 107
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
Improve Gateway API for sending tokens to Polkadot #1009
Changes from all commits
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 |
---|---|---|
|
@@ -9,7 +9,7 @@ import {SafeTokenTransferFrom} from "./utils/SafeTransfer.sol"; | |
|
||
import {AssetsStorage} from "./storage/AssetsStorage.sol"; | ||
import {SubstrateTypes} from "./SubstrateTypes.sol"; | ||
import {ParaID} from "./Types.sol"; | ||
import {ParaID, MultiAddress} from "./Types.sol"; | ||
import {Address} from "./utils/Address.sol"; | ||
|
||
/// @title Library for implementing Ethereum->Polkadot ERC20 transfers. | ||
|
@@ -21,6 +21,7 @@ library Assets { | |
error InvalidToken(); | ||
error InvalidAmount(); | ||
error InvalidDestination(); | ||
error Unsupported(); | ||
|
||
// This library requires state which must be initialized in the gateway's storage. | ||
function initialize(uint256 registerTokenFee, uint256 sendTokenFee) external { | ||
|
@@ -30,26 +31,17 @@ library Assets { | |
$.sendTokenFee = sendTokenFee; | ||
} | ||
|
||
function sendToken( | ||
ParaID assetHubParaID, | ||
address assetHubAgent, | ||
address token, | ||
address sender, | ||
ParaID destinationChain, | ||
bytes32 destinationAddress, | ||
uint128 amount | ||
) external returns (bytes memory payload, uint256 extraFee) { | ||
AssetsStorage.Layout storage $ = AssetsStorage.layout(); | ||
/// @dev transfer tokens from the sender to the specified | ||
function _transferToAgent(address assetHubAgent, address token, address sender, uint128 amount) internal { | ||
if (!token.isContract()) { | ||
revert InvalidToken(); | ||
} | ||
|
||
_transferToAgent(assetHubAgent, token, sender, amount); | ||
if (destinationChain == assetHubParaID) { | ||
payload = SubstrateTypes.SendToken(token, destinationAddress, amount); | ||
} else { | ||
payload = SubstrateTypes.SendToken(token, destinationChain, destinationAddress, amount); | ||
if (amount == 0) { | ||
revert InvalidAmount(); | ||
} | ||
extraFee = $.sendTokenFee; | ||
|
||
emit IGateway.TokenSent(sender, token, destinationChain, abi.encodePacked(destinationAddress), amount); | ||
IERC20(token).safeTransferFrom(sender, assetHubAgent, amount); | ||
} | ||
|
||
function sendToken( | ||
|
@@ -58,34 +50,35 @@ library Assets { | |
address token, | ||
address sender, | ||
ParaID destinationChain, | ||
address destinationAddress, | ||
MultiAddress calldata destinationAddress, | ||
uint128 amount | ||
) external returns (bytes memory payload, uint256 extraFee) { | ||
AssetsStorage.Layout storage $ = AssetsStorage.layout(); | ||
if (destinationChain == assetHubParaID) { | ||
// AssetHub parachain doesn't support Ethereum-style addresses | ||
revert InvalidDestination(); | ||
} | ||
|
||
_transferToAgent(assetHubAgent, token, sender, amount); | ||
|
||
payload = SubstrateTypes.SendToken(address(this), token, destinationChain, destinationAddress, amount); | ||
extraFee = $.sendTokenFee; | ||
|
||
emit IGateway.TokenSent(sender, token, destinationChain, abi.encodePacked(destinationAddress), amount); | ||
} | ||
|
||
/// @dev transfer tokens from the sender to the specified | ||
function _transferToAgent(address assetHubAgent, address token, address sender, uint128 amount) internal { | ||
if (!token.isContract()) { | ||
revert InvalidToken(); | ||
} | ||
|
||
if (amount == 0) { | ||
revert InvalidAmount(); | ||
if (destinationChain == assetHubParaID) { | ||
if (destinationAddress.isAddress32()) { | ||
payload = SubstrateTypes.SendTokenToAssetHubAddress32(token, destinationAddress.asAddress32(), amount); | ||
} else { | ||
revert Unsupported(); | ||
} | ||
} else { | ||
if (destinationAddress.isAddress32()) { | ||
payload = SubstrateTypes.SendTokenToAddress32( | ||
token, destinationChain, destinationAddress.asAddress32(), amount | ||
); | ||
} else if (destinationAddress.isAddress20()) { | ||
payload = SubstrateTypes.SendTokenToAddress20( | ||
token, destinationChain, destinationAddress.asAddress20(), amount | ||
); | ||
} else { | ||
revert Unsupported(); | ||
} | ||
Comment on lines
+61
to
+77
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. What if the 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. That's not supported right now, requires more plumbing on the BridgeHub side, to convert the message into the right XCM. Not many parachains use |
||
} | ||
extraFee = $.sendTokenFee; | ||
|
||
IERC20(token).safeTransferFrom(sender, assetHubAgent, amount); | ||
emit IGateway.TokenSent(sender, token, destinationChain, destinationAddress, amount); | ||
} | ||
|
||
/// @dev Enqueues a create native token message to substrate. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> | ||
pragma solidity 0.8.22; | ||
|
||
using {isIndex, asIndex, isAddress32, asAddress32, isAddress20, asAddress20} for MultiAddress global; | ||
|
||
/// @dev An address for an on-chain account | ||
struct MultiAddress { | ||
Kind kind; | ||
bytes data; | ||
} | ||
|
||
enum Kind { | ||
Index, | ||
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. What is this 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. If you install pallet_indices on a substrate chain, then its possible for the chain to map a So its basically a "short" id for accountid. In fact Substrate allows all sorts of mappings: |
||
Address32, | ||
Address20 | ||
} | ||
|
||
function isIndex(MultiAddress calldata multiAddress) pure returns (bool) { | ||
return multiAddress.kind == Kind.Index; | ||
} | ||
|
||
function asIndex(MultiAddress calldata multiAddress) pure returns (uint32) { | ||
return abi.decode(multiAddress.data, (uint32)); | ||
} | ||
|
||
function isAddress32(MultiAddress calldata multiAddress) pure returns (bool) { | ||
return multiAddress.kind == Kind.Address32; | ||
} | ||
|
||
function asAddress32(MultiAddress calldata multiAddress) pure returns (bytes32) { | ||
return bytes32(multiAddress.data); | ||
} | ||
|
||
function isAddress20(MultiAddress calldata multiAddress) pure returns (bool) { | ||
return multiAddress.kind == Kind.Address20; | ||
} | ||
|
||
function asAddress20(MultiAddress calldata multiAddress) pure returns (bytes20) { | ||
return bytes20(multiAddress.data); | ||
} | ||
|
||
function multiAddressFromUint32(uint32 id) pure returns (MultiAddress memory) { | ||
return MultiAddress({kind: Kind.Index, data: abi.encode(id)}); | ||
} | ||
|
||
function multiAddressFromBytes32(bytes32 id) pure returns (MultiAddress memory) { | ||
return MultiAddress({kind: Kind.Address32, data: bytes.concat(id)}); | ||
} | ||
|
||
function multiAddressFromBytes20(bytes20 id) pure returns (MultiAddress memory) { | ||
return MultiAddress({kind: Kind.Address20, data: bytes.concat(id)}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,10 @@ | |
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> | ||
pragma solidity 0.8.22; | ||
|
||
import { | ||
MultiAddress, multiAddressFromUint32, multiAddressFromBytes32, multiAddressFromBytes20 | ||
} from "./MultiAddress.sol"; | ||
|
||
type ParaID is uint32; | ||
|
||
using {ParaIDEq as ==, ParaIDNe as !=, into} for ParaID global; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> | ||
pragma solidity 0.8.22; | ||
|
||
import {OperatingMode, InboundMessage, ParaID, ChannelID} from "../Types.sol"; | ||
import {OperatingMode, InboundMessage, ParaID, ChannelID, MultiAddress} from "../Types.sol"; | ||
import {Verification} from "../Verification.sol"; | ||
|
||
interface IGateway { | ||
|
@@ -65,7 +65,11 @@ interface IGateway { | |
|
||
/// @dev Emitted once the funds are locked and an outbound message is successfully queued. | ||
event TokenSent( | ||
address indexed token, address indexed sender, ParaID destinationChain, bytes destinationAddress, uint128 amount | ||
address indexed token, | ||
address indexed sender, | ||
ParaID destinationChain, | ||
MultiAddress destinationAddress, | ||
uint128 amount | ||
); | ||
|
||
/// @dev Emitted when a command is sent to register a new wrapped token on AssetHub | ||
|
@@ -79,12 +83,7 @@ interface IGateway { | |
function registerToken(address token) external payable; | ||
|
||
/// @dev Send ERC20 tokens to parachain `destinationChain` and deposit into account `destinationAddress` | ||
function sendToken(address token, ParaID destinationChain, bytes32 destinationAddress, uint128 amount) | ||
external | ||
payable; | ||
|
||
/// @dev Send ERC20 tokens to parachain `destinationChain` and deposit into account `destinationAddress` | ||
function sendToken(address token, ParaID destinationChain, address destinationAddress, uint128 amount) | ||
function sendToken(address token, ParaID destinationChain, MultiAddress calldata destinationAddress, uint128 amount) | ||
external | ||
payable; | ||
} |
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.
ooh will this make it easier for native Moonbeam support?
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.
ah i also see we had it already, so just a refactor