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

SNO-613: Arbitrary transact from Ethereum to Polkadot #925

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 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
16 changes: 11 additions & 5 deletions contracts/src/Assets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@ library Assets {
AssetsStorage.Layout storage $ = AssetsStorage.layout();

_transferToAgent(assetHubAgent, token, sender, amount);

extraFee = $.sendTokenFee;

if (destinationChain == assetHubParaID) {
payload = SubstrateTypes.SendToken(address(this), token, destinationAddress, amount);
payload = SubstrateTypes.SendToken(address(this), token, destinationAddress, amount, extraFee);
} else {
payload = SubstrateTypes.SendToken(address(this), token, destinationChain, destinationAddress, amount);
payload =
SubstrateTypes.SendToken(address(this), token, destinationChain, destinationAddress, amount, extraFee);
}
extraFee = $.sendTokenFee;

emit TokenSent(sender, token, destinationChain, abi.encodePacked(destinationAddress), amount);
}
Expand All @@ -75,8 +78,10 @@ library Assets {

_transferToAgent(assetHubAgent, token, sender, amount);

payload = SubstrateTypes.SendToken(address(this), token, destinationChain, destinationAddress, amount);
extraFee = $.sendTokenFee;

payload = SubstrateTypes.SendToken(address(this), token, destinationChain, destinationAddress, amount, extraFee);

Comment on lines +81 to +83
Copy link
Contributor

Choose a reason for hiding this comment

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

So if I understand this right, sendToken is a different kind of operation than an arbitrary contract call. So why is extraFee being sent here too, if it is not an arbitrary transact call being done here?

Copy link
Contributor Author

@yrong yrong Aug 25, 2023

Choose a reason for hiding this comment

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

Actually it's some improvement for https://linear.app/snowfork/issue/SNO-582 for transfer, but relevant to the Transact change here(i.e. extra fee will consistently be used to cover the cost of XCM dispatch) so I link it as subtask as https://linear.app/snowfork/issue/SNO-613

@vgeddes Could you help to confirm if that's also your intention?

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

Expand Down Expand Up @@ -105,9 +110,10 @@ library Assets {
revert InvalidToken();
}

payload = SubstrateTypes.RegisterToken(address(this), token, createTokenCallID);
extraFee = $.registerTokenFee;

payload = SubstrateTypes.RegisterToken(address(this), token, createTokenCallID, extraFee);

emit TokenRegistrationSent(token);
}
}
50 changes: 50 additions & 0 deletions contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {ScaleCodec} from "./utils/ScaleCodec.sol";

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

contract Gateway is IGateway, IInitializable {
using Address for address;
Expand All @@ -29,6 +30,13 @@ contract Gateway is IGateway, IInitializable {
uint256 internal immutable DISPATCH_GAS;
address internal immutable AGENT_EXECUTOR;

// Default params for arbitrary transact
// should be big enough to cover most of the transact cost in destination chain
// could be somehow overestimated since the surplus will be refunded
uint256 internal constant DEFAULT_EXTRA_FEE = 100_000_000_000_000;
uint64 internal constant DEFAULT_REF_TIME = 1_000_000_000;
uint64 internal constant DEFAULT_PROOF_SIZE = 100_000;

// Verification state
address internal immutable BEEFY_CLIENT;

Expand Down Expand Up @@ -559,4 +567,46 @@ contract Gateway is IGateway, IInitializable {

Assets.initialize(registerTokenFee, sendTokenFee);
}

/**
* Transacts
*/

/// @inheritdoc IGateway
function transactAsGateway(ParaID destinationChain, bytes calldata payload) external payable {
bytes memory message_payload =
SubstrateTypes.Transact(address(this), payload, DEFAULT_EXTRA_FEE, DEFAULT_REF_TIME, DEFAULT_PROOF_SIZE);
_submitOutbound(destinationChain, message_payload, DEFAULT_EXTRA_FEE);
}

/// @inheritdoc IGateway
function transactAsGateway(
ParaID destinationChain,
bytes calldata payload,
uint256 extraFee,
uint64 refTime,
uint64 proofSize
) external payable {
bytes memory message_payload = SubstrateTypes.Transact(address(this), payload, extraFee, refTime, proofSize);
_submitOutbound(destinationChain, message_payload, extraFee);
}

/// @inheritdoc IGateway
function transact(ParaID destinationChain, bytes calldata payload) external payable {
bytes memory message_payload =
SubstrateTypes.Transact(msg.sender, payload, DEFAULT_EXTRA_FEE, DEFAULT_REF_TIME, DEFAULT_PROOF_SIZE);
_submitOutbound(destinationChain, message_payload, 0);
}

/// @inheritdoc IGateway
function transact(
ParaID destinationChain,
bytes calldata payload,
uint256 extraFee,
uint64 refTime,
uint64 proofSize
) external payable {
bytes memory message_payload = SubstrateTypes.Transact(msg.sender, payload, extraFee, refTime, proofSize);
_submitOutbound(destinationChain, message_payload, 0);
}
}
57 changes: 45 additions & 12 deletions contracts/src/SubstrateTypes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,15 @@ library SubstrateTypes {
* `NativeTokensMessage::Create`
*/
// solhint-disable-next-line func-name-mixedcase
function RegisterToken(address gateway, address token, bytes2 createCallIndex)
function RegisterToken(address gateway, address token, bytes2 createCallIndex, uint256 extraFee)
internal
view
returns (bytes memory)
{
return bytes.concat(
bytes1(0x00),
ScaleCodec.encodeU64(uint64(block.chainid)),
ScaleCodec.encodeU128(uint128(extraFee)),
bytes1(0x00),
SubstrateTypes.H160(gateway),
SubstrateTypes.H160(token),
Expand All @@ -76,14 +77,15 @@ library SubstrateTypes {
* `NativeTokensMessage::Mint`
*/
// solhint-disable-next-line func-name-mixedcase
function SendToken(address gateway, address token, bytes32 recipient, uint128 amount)
function SendToken(address gateway, address token, bytes32 recipient, uint128 amount, uint256 extraFee)
internal
view
returns (bytes memory)
{
return bytes.concat(
bytes1(0x00),
ScaleCodec.encodeU64(uint64(block.chainid)),
ScaleCodec.encodeU128(uint128(extraFee)),
bytes1(0x01),
SubstrateTypes.H160(gateway),
SubstrateTypes.H160(token),
Expand All @@ -93,14 +95,18 @@ library SubstrateTypes {
);
}

function SendToken(address gateway, address token, ParaID paraID, bytes32 recipient, uint128 amount)
internal
view
returns (bytes memory)
{
function SendToken(
address gateway,
address token,
ParaID paraID,
bytes32 recipient,
uint128 amount,
uint256 extraFee
) internal view returns (bytes memory) {
return bytes.concat(
bytes1(0x00),
ScaleCodec.encodeU64(uint64(block.chainid)),
ScaleCodec.encodeU128(uint128(extraFee)),
bytes1(0x01),
SubstrateTypes.H160(gateway),
SubstrateTypes.H160(token),
Expand All @@ -111,14 +117,18 @@ library SubstrateTypes {
);
}

function SendToken(address gateway, address token, ParaID paraID, address recipient, uint128 amount)
internal
view
returns (bytes memory)
{
function SendToken(
address gateway,
address token,
ParaID paraID,
address recipient,
uint128 amount,
uint256 extraFee
) internal view returns (bytes memory) {
return bytes.concat(
bytes1(0x00),
ScaleCodec.encodeU64(uint64(block.chainid)),
ScaleCodec.encodeU128(uint128(extraFee)),
bytes1(0x01),
SubstrateTypes.H160(gateway),
SubstrateTypes.H160(token),
Expand All @@ -128,4 +138,27 @@ library SubstrateTypes {
ScaleCodec.encodeU128(amount)
);
}

/**
* @dev SCALE-encodes `router_primitives::inbound::VersionedMessage` containing payload
* for arbitrary transact
*/
// solhint-disable-next-line func-name-mixedcase
function Transact(address sender, bytes calldata payload, uint256 extraFee, uint64 refTime, uint64 proofSize)
internal
view
returns (bytes memory)
{
return bytes.concat(
bytes1(0x00),
ScaleCodec.encodeU64(uint64(block.chainid)),
ScaleCodec.encodeU128(uint128(extraFee)),
bytes1(0x02),
SubstrateTypes.H160(sender),
ScaleCodec.checkedEncodeCompactU32(payload.length),
payload,
ScaleCodec.encodeU64(refTime),
ScaleCodec.encodeU64(proofSize)
);
}
}
24 changes: 24 additions & 0 deletions contracts/src/interfaces/IGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,28 @@ interface IGateway {
function sendToken(address token, ParaID destinationChain, address destinationAddress, uint128 amount)
external
payable;

/// @dev Send arbitrary transact with gateway as origin
function transactAsGateway(ParaID destinationChain, bytes calldata payload) external payable;

/// @dev Send arbitrary transact with customizable fee/weight and gateway as origin
function transactAsGateway(
ParaID destinationChain,
bytes calldata payload,
uint256 extraFee,
uint64 refTime,
uint64 proofSize
) external payable;

/// @dev Send arbitrary transact
function transact(ParaID destinationChain, bytes calldata payload) external payable;

/// @dev Send arbitrary transact with customizable fee/weight
function transact(
ParaID destinationChain,
bytes calldata payload,
uint256 extraFee,
uint64 refTime,
uint64 proofSize
) external payable;
}
6 changes: 3 additions & 3 deletions contracts/test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ contract GatewayTest is Test {

vm.expectEmit(true, false, false, false);
emit OutboundMessageAccepted(
assetHubParaID, 1, SubstrateTypes.RegisterToken(address(gateway), address(token), bytes2(0x3500))
assetHubParaID, 1, SubstrateTypes.RegisterToken(address(gateway), address(token), bytes2(0x3500), 0)
);

IGateway(address(gateway)).registerToken{value: 2 ether}(address(token));
Expand All @@ -488,7 +488,7 @@ contract GatewayTest is Test {
// Expect the gateway to emit `OutboundMessageAccepted`
vm.expectEmit(true, false, false, false);
emit OutboundMessageAccepted(
assetHubParaID, 1, SubstrateTypes.SendToken(address(gateway), address(token), destPara, destAddress, 1)
assetHubParaID, 1, SubstrateTypes.SendToken(address(gateway), address(token), destPara, destAddress, 1, 0)
);

IGateway(address(gateway)).sendToken{value: 2 ether}(address(token), destPara, destAddress, 1);
Expand All @@ -508,7 +508,7 @@ contract GatewayTest is Test {
// Expect the gateway to emit `OutboundMessageAccepted`
vm.expectEmit(true, false, false, false);
emit OutboundMessageAccepted(
assetHubParaID, 1, SubstrateTypes.SendToken(address(gateway), address(token), destAddress, 1)
assetHubParaID, 1, SubstrateTypes.SendToken(address(gateway), address(token), destAddress, 1, 0)
);

IGateway(address(gateway)).sendToken{value: 2 ether}(address(token), destPara, destAddress, 1);
Expand Down
20 changes: 20 additions & 0 deletions contracts/test/mocks/GatewayUpgradeMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,24 @@ contract GatewayUpgradeMock is IGateway, IInitializable {
(uint256 d0, uint256 d1) = abi.decode(data, (uint256, uint256));
emit Initialized(d0, d1);
}

function transact(ParaID destinationChain, bytes calldata payload) external payable {}

function transact(
ParaID destinationChain,
bytes calldata payload,
uint256 extraFee,
uint64 refTime,
uint64 proofSize
) external payable {}

function transactAsGateway(ParaID destinationChain, bytes calldata payload) external payable {}

function transactAsGateway(
ParaID destinationChain,
bytes calldata payload,
uint256 extraFee,
uint64 refTime,
uint64 proofSize
) external payable {}
}
1 change: 1 addition & 0 deletions parachain/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

35 changes: 22 additions & 13 deletions parachain/pallets/inbound-queue/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod test;

use codec::{Decode, DecodeAll, Encode};
use frame_support::{
log,
traits::{
fungible::{Inspect, Mutate},
GenesisBuild,
Expand All @@ -30,14 +31,15 @@ use scale_info::TypeInfo;
use sp_core::H160;
use sp_runtime::traits::AccountIdConversion;
use sp_std::convert::TryFrom;
use xcm::v3::{send_xcm, Junction::*, Junctions::*, MultiLocation, SendError};
use xcm::v3::{send_xcm, Junction::*, Junctions::*, MultiLocation, SendError, Xcm};

use envelope::Envelope;
use snowbridge_core::{
inbound::{Message, Verifier},
ParaId,
};
use snowbridge_router_primitives::inbound;
use sp_std::prelude::ToOwned;
pub use weights::WeightInfo;

type BalanceOf<T> =
Expand Down Expand Up @@ -207,24 +209,25 @@ pub mod pallet {
// succeed even if the message was not successfully decoded or dispatched.

// Attempt to decode message
let xcm = match inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref()) {
Ok(inbound::VersionedMessage::V1(message_v1)) => message_v1.into(),
Err(_) => {
Self::deposit_event(Event::MessageReceived {
dest: envelope.dest,
nonce: envelope.nonce,
result: MessageDispatchResult::InvalidPayload,
});
return Ok(())
},
};
let xcm: Xcm<()> =
match inbound::VersionedMessage::decode_all(&mut envelope.payload.as_ref()) {
Ok(inbound::VersionedMessage::V1(message_v1)) => message_v1.into(),
Err(_) => {
Self::deposit_event(Event::MessageReceived {
dest: envelope.dest,
nonce: envelope.nonce,
result: MessageDispatchResult::InvalidPayload,
});
return Ok(())
},
};

// Attempt to convert to XCM
let sibling_para =
MultiLocation { parents: 1, interior: X1(Parachain(envelope.dest.into())) };

// Attempt to send XCM to a sibling parachain
match send_xcm::<T::XcmSender>(sibling_para, xcm) {
match send_xcm::<T::XcmSender>(sibling_para, xcm.clone()) {
Ok(_) => Self::deposit_event(Event::MessageReceived {
dest: envelope.dest,
nonce: envelope.nonce,
Expand All @@ -236,6 +239,12 @@ pub mod pallet {
result: MessageDispatchResult::NotDispatched(err),
}),
}
log::info!(
target: &("xcm::".to_owned() + LOG_TARGET),
"💫 xcm {:?} sent to {:?}",
xcm,
sibling_para
);

Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions parachain/pallets/inbound-queue/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,9 @@ fn parse_dest(message: Message) -> ParaId {
// The originating channel address for the messages below
const GATEWAY_ADDRESS: [u8; 20] = hex!["EDa338E4dC46038493b885327842fD3E301CaB39"];

const OUTBOUND_QUEUE_EVENT_LOG: [u8; 253] = hex!(
const OUTBOUND_QUEUE_EVENT_LOG: [u8; 286] = hex!(
"
f8fb94eda338e4dc46038493b885327842fd3e301cab39f842a0d56f1b8dfd3ba41f19c499ceec5f9546f61befa5f10398a75d7dba53a219fecea000000000000000000000000000000000000000000000000000000000000003e8b8a0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000034000f000000000000000057a2d4ff0c3866d96556884bf09fecdd7ccd530c87d1f7fdfee7f651fabc8bfcb6e086c278b77a7d3500000000000000000000000000
f9011b94eda338e4dc46038493b885327842fd3e301cab39f842a0d56f1b8dfd3ba41f19c499ceec5f9546f61befa5f10398a75d7dba53a219fecea000000000000000000000000000000000000000000000000000000000000003e8b8c0000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000044000f000000000000000000000000000000000000000000000000eda338e4dc46038493b885327842fd3e301cab3987d1f7fdfee7f651fabc8bfcb6e086c278b77a7d350000000000000000000000000000000000000000000000000000000000
"
);

Expand Down
Loading
Loading