Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 0 additions & 2 deletions contracts/.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ GatewayTest:testDisableOutboundMessagingForChannel() (gas: 242421)
GatewayTest:testGetters() (gas: 40174)
GatewayTest:testHandlersNotExternallyCallable() (gas: 53345)
GatewayTest:testInitializeNotExternallyCallable() (gas: 16720)
GatewayTest:testRegisterToken() (gas: 132103)
GatewayTest:testRegisterTokenReimbursesExcessFees() (gas: 144342)
GatewayTest:testRelayerNotRewarded() (gas: 315261)
GatewayTest:testRelayerRewardedFromAgent() (gas: 327113)
GatewayTest:testSendTokenAddress20() (gas: 236521)
Expand Down
3 changes: 1 addition & 2 deletions contracts/scripts/DeployLocal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ contract DeployLocal is Script {
Initializer.Config memory config = Initializer.Config({
mode: OperatingMode.Normal,
deliveryCost: uint128(vm.envUint("DELIVERY_COST")),
registerTokenFee: uint128(vm.envUint("REGISTER_TOKEN_FEE")),
assetHubCreateAssetFee: uint128(vm.envUint("CREATE_ASSET_FEE")),
assetHubReserveTransferFee: uint128(vm.envUint("RESERVE_TRANSFER_FEE")),
exchangeRate: ud60x18(vm.envUint("EXCHANGE_RATE")),
Expand All @@ -94,7 +93,7 @@ contract DeployLocal is Script {
// For testing call contract
new HelloWorld();

// Deploy test token for registration testing
// Deploy test token for registration testing
new Token("Test Token", "TEST", 18);

// Fund the gateway proxy contract. Used to reward relayers
Expand Down
2 changes: 1 addition & 1 deletion contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ contract Gateway is IGatewayBase, IGatewayV1, IGatewayV2, IInitializable, IUpgra
override(IGatewayV1, IGatewayV2)
returns (bool)
{
return CallsV1.isTokenRegistered(token);
return CallsV2.isTokenRegistered(token);
}

function depositEther() external payable {
Expand Down
18 changes: 3 additions & 15 deletions contracts/src/Initializer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ library Initializer {
uint128 assetHubCreateAssetFee;
/// @dev The extra fee charged for sending tokens (DOT)
uint128 assetHubReserveTransferFee;
/// @dev extra fee to discourage spamming
uint256 registerTokenFee;
/// @dev Fee multiplier
UD60x18 multiplier;
uint8 foreignTokenDecimals;
Expand All @@ -55,18 +53,12 @@ library Initializer {

// Initialize channel for primary governance track
core.channels[Constants.PRIMARY_GOVERNANCE_CHANNEL_ID] = Channel({
mode: OperatingMode.Normal,
agent: bridgeHubAgent,
inboundNonce: 0,
outboundNonce: 0
mode: OperatingMode.Normal, agent: bridgeHubAgent, inboundNonce: 0, outboundNonce: 0
});

// Initialize channel for secondary governance track
core.channels[Constants.SECONDARY_GOVERNANCE_CHANNEL_ID] = Channel({
mode: OperatingMode.Normal,
agent: bridgeHubAgent,
inboundNonce: 0,
outboundNonce: 0
mode: OperatingMode.Normal, agent: bridgeHubAgent, inboundNonce: 0, outboundNonce: 0
});

// Initialize agent for for AssetHub
Expand All @@ -75,10 +67,7 @@ library Initializer {

// Initialize channel for AssetHub
core.channels[Constants.ASSET_HUB_PARA_ID.into()] = Channel({
mode: OperatingMode.Normal,
agent: assetHubAgent,
inboundNonce: 0,
outboundNonce: 0
mode: OperatingMode.Normal, agent: assetHubAgent, inboundNonce: 0, outboundNonce: 0
});

// Initialize pricing storage
Expand All @@ -92,7 +81,6 @@ library Initializer {

assets.assetHubParaID = Constants.ASSET_HUB_PARA_ID;
assets.assetHubAgent = assetHubAgent;
assets.registerTokenFee = config.registerTokenFee;
assets.assetHubCreateAssetFee = config.assetHubCreateAssetFee;
assets.assetHubReserveTransferFee = config.assetHubReserveTransferFee;
assets.foreignTokenDecimals = config.foreignTokenDecimals;
Expand Down
15 changes: 0 additions & 15 deletions contracts/src/SubstrateTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,6 @@ library SubstrateTypes {
return hex"00";
}

/**
* @dev SCALE-encodes `router_primitives::inbound::VersionedMessage` containing payload
* `NativeTokensMessage::Create`
*/
// solhint-disable-next-line func-name-mixedcase
function RegisterToken(address token, uint128 fee) internal view returns (bytes memory) {
return bytes.concat(
bytes1(0x00),
ScaleCodec.encodeU64(uint64(block.chainid)),
bytes1(0x00),
SubstrateTypes.H160(token),
ScaleCodec.encodeU128(fee)
);
}

/**
* @dev SCALE-encodes `router_primitives::inbound::VersionedMessage` containing payload
* `NativeTokensMessage::Mint`
Expand Down
3 changes: 0 additions & 3 deletions contracts/src/interfaces/IGatewayBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,4 @@ interface IGatewayBase {

// Emitted when foreign token from polkadot registered
event ForeignTokenRegistered(bytes32 indexed tokenID, address token);

/// @dev Emitted when a command is sent to register a new wrapped token on AssetHub
event TokenRegistrationSent(address token);
}
4 changes: 2 additions & 2 deletions contracts/src/storage/AssetsStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ library AssetsStorage {
uint128 assetHubCreateAssetFee;
// XCM fee charged by AssetHub for receiving a token from the Gateway (DOT)
uint128 assetHubReserveTransferFee;
// Extra fee for registering a token, to discourage spamming (Ether)
uint256 registerTokenFee;
// Previously used in V1 for registering a native token, this is now obsolete as token registration has been moved to V2 without on-chain fees.
uint256 __obsolete_1;
// Foreign token registry by token ID
mapping(bytes32 foreignID => address) tokenAddressOf;
uint8 foreignTokenDecimals;
Expand Down
55 changes: 5 additions & 50 deletions contracts/src/v1/Calls.sol
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file still has the implementation for isTokenRegistered. Since token registration functionality has been moved to V2, we should move it there surely?

Copy link
Contributor Author

@yrong yrong Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
Expand Up @@ -66,32 +66,6 @@ library CallsV1 {
* External API
*/

/// @dev Registers a token (only native tokens at this time)
/// @param token The ERC20 token address.
function registerToken(address token) external {
AssetsStorage.Layout storage $ = AssetsStorage.layout();

// NOTE: Explicitly allow a token to be re-registered. This offers resiliency
// in case a previous registration attempt of the same token failed on the remote side.
// It means that registration can be retried.
Functions.registerNativeToken(token);

Ticket memory ticket = Ticket({
dest: $.assetHubParaID,
costs: _registerTokenCosts(),
payload: SubstrateTypes.RegisterToken(token, $.assetHubCreateAssetFee),
value: 0
});

emit IGatewayBase.TokenRegistrationSent(token);

_submitOutbound(ticket);
}

function quoteRegisterTokenFee() external view returns (uint256) {
return _calculateFee(_registerTokenCosts());
}

function sendToken(
address token,
address sender,
Expand Down Expand Up @@ -138,11 +112,11 @@ library CallsV1 {
}
}

function quoteSendTokenFee(address token, ParaID destinationChain, uint128 destinationChainFee)
external
view
returns (uint256)
{
function quoteSendTokenFee(
address token,
ParaID destinationChain,
uint128 destinationChainFee
) external view returns (uint256) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
TokenInfo storage info = $.tokenRegistry[token];
if (!info.isRegistered) {
Expand Down Expand Up @@ -246,10 +220,6 @@ library CallsV1 {
);
}

function isTokenRegistered(address token) external view returns (bool) {
return AssetsStorage.layout().tokenRegistry[token].isRegistered;
}

function _sendTokenCosts(ParaID destinationChain, uint128 destinationChainFee)
internal
view
Expand Down Expand Up @@ -381,19 +351,4 @@ library CallsV1 {

emit IGatewayV1.TokenSent(token, sender, destinationChain, destinationAddress, amount);
}

function _registerTokenCosts() internal view returns (Costs memory costs) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();

// Cost of registering this asset on AssetHub
costs.foreign = $.assetHubCreateAssetFee;

// Extra fee to prevent spamming
costs.native = $.registerTokenFee;
}

function _isTokenRegistered(address token) internal view returns (bool) {
AssetsStorage.Layout storage $ = AssetsStorage.layout();
return $.tokenRegistry[token].isRegistered;
}
}
1 change: 0 additions & 1 deletion contracts/src/v1/Handlers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ library HandlersV1 {
SetTokenTransferFeesParams memory params = abi.decode(data, (SetTokenTransferFeesParams));
$.assetHubCreateAssetFee = params.assetHubCreateAssetFee;
$.assetHubReserveTransferFee = params.assetHubReserveTransferFee;
$.registerTokenFee = params.registerTokenFee;
emit IGatewayV1.TokenTransferFeesChanged();
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/src/v1/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ struct SetTokenTransferFeesParams {
uint128 assetHubCreateAssetFee;
/// @dev The remote fee (DOT) for send tokens to AssetHub
uint128 assetHubReserveTransferFee;
/// @dev extra fee to register an asset and discourage spamming (Ether)
/// @dev The extra fee to register an asset (Ether), though this field is now ignored and won't be applied, as the token registration in V1 is removed
uint256 registerTokenFee;
}

Expand Down
4 changes: 4 additions & 0 deletions contracts/src/v2/Calls.sol
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,8 @@ library CallsV2 {
function outboundNonce() external view returns (uint64) {
return CoreStorage.layout().outboundNonce;
}

function isTokenRegistered(address token) external view returns (bool) {
return AssetsStorage.layout().tokenRegistry[token].isRegistered;
}
}
2 changes: 0 additions & 2 deletions contracts/test/GatewayV1.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ contract GatewayV1Test is Test {

// remote fees in DOT
uint128 public outboundFee = 1e10;
uint128 public registerTokenFee = 0;
uint128 public sendTokenFee = 1e10;
uint128 public createTokenFee = 1e10;
uint128 public maxDestinationFee = 1e11;
Expand All @@ -130,7 +129,6 @@ contract GatewayV1Test is Test {
Initializer.Config memory config = Initializer.Config({
mode: OperatingMode.Normal,
deliveryCost: outboundFee,
registerTokenFee: registerTokenFee,
assetHubCreateAssetFee: createTokenFee,
assetHubReserveTransferFee: sendTokenFee,
exchangeRate: exchangeRate,
Expand Down
1 change: 0 additions & 1 deletion contracts/test/GatewayV2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ contract GatewayV2Test is Test {
Initializer.Config memory config = Initializer.Config({
mode: OperatingMode.Normal,
deliveryCost: 1e10,
registerTokenFee: 0,
assetHubCreateAssetFee: 1e10,
assetHubReserveTransferFee: 1e10,
exchangeRate: ud60x18(0.0025e18),
Expand Down
20 changes: 0 additions & 20 deletions contracts/test/SubstrateTypes.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ contract SubstrateTypesWrapper {
return SubstrateTypes.None();
}

function RegisterToken(address token, uint128 fee) external view returns (bytes memory) {
return SubstrateTypes.RegisterToken(token, fee);
}

function SendTokenToAssetHubAddress32(
address token,
bytes32 recipient,
Expand Down Expand Up @@ -127,22 +123,6 @@ contract SubstrateTypesTest is Test {
assertEq(uint8(got[0]), 0);
}

function testRegisterToken() public {
address token = address(0xdEADBEeF00000000000000000000000000000000);
uint128 fee = 123_456;
bytes memory got = wrapper.RegisterToken(token, fee);

bytes memory expect = bytes.concat(
bytes1(0x00),
abi.encodePacked(ScaleCodec.encodeU64(uint64(block.chainid))),
bytes1(0x00),
abi.encodePacked(token),
abi.encodePacked(ScaleCodec.encodeU128(fee))
);

assertEq(keccak256(got), keccak256(expect));
}

function testSendTokenToAssetHubAddress32() public {
address token = address(0x1111111111222222222233333333333344444444);
bytes32 recipient = keccak256("recipient32");
Expand Down
14 changes: 0 additions & 14 deletions docs/developers/snowbridge-v1/token-transfers.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,6 @@ A token transfer can be initiated with a single transaction to our [Gateway](../

Sending tokens is usually a single step for the user. However, a preliminary registration step is required for tokens which have not previously been bridged before.

### Token Registration

First, the ERC20 token needs to be registered on AssetHub in the `ForeignAssets` pallet.

This can initiated by sending the following transaction to the Gateway.

```solidity
/// @dev Send a message to the AssetHub parachain to register a new fungible asset
/// in the `ForeignAssets` pallet.
function registerToken(address token) external payable;
```

This function will charge a fee in Ether that can be retrieved ahead of time by calling `quoteRegisterTokenFee`.

### Token Sending

To send a previously registered token to a destination parachain, send this transaction to the Gateway:
Expand Down
Loading
Loading