Skip to content

Commit

Permalink
Asset transfer (#904)
Browse files Browse the repository at this point in the history
* update gateway to registry

* fixed test

* fmt

* make agents relative to the relaychain

* use address(this)

* fmt

* log out reciept and drop sent amount

* update cumulus

* use real agent ids

* create agents on initialize

* Revert "create agents on initialize"

This reverts commit e53557d.

* update agents and fix abi encode

* encode with enum

* fmt

* update cumulus

* fix encoding

* fix tests

* update cumulus

* rustfmt

* remove duplicate enum

* generate bindings

* remove tuple

* use framebenchmarking v2

* updated cumulus
  • Loading branch information
alistair-singh authored Jul 28, 2023
1 parent c9c98ef commit b0f922c
Show file tree
Hide file tree
Showing 23 changed files with 535 additions and 257 deletions.
8 changes: 3 additions & 5 deletions core/packages/contracts/src/AgentExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]>
pragma solidity 0.8.20;

import {ParaID} from "./Types.sol";
import {AgentExecuteCommand, ParaID} from "./Types.sol";
import {SubstrateTypes} from "./SubstrateTypes.sol";

import {IERC20} from "./interfaces/IERC20.sol";
Expand All @@ -12,11 +12,9 @@ contract AgentExecutor {
using SafeTokenTransfer for IERC20;
using SafeNativeTransfer for address payable;

bytes32 internal constant COMMAND_TRANSFER_TOKEN = keccak256("transferToken");

function execute(address, bytes memory data) external {
(bytes32 command, bytes memory params) = abi.decode(data, (bytes32, bytes));
if (command == COMMAND_TRANSFER_TOKEN) {
(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);
}
Expand Down
10 changes: 5 additions & 5 deletions core/packages/contracts/src/Assets.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ library Assets {

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

Expand All @@ -74,7 +74,7 @@ library Assets {

_transferToAgent(assetHubAgent, token, sender, amount);

payload = SubstrateTypes.SendToken(assetHubAgent, token, destinationChain, destinationAddress, amount);
payload = SubstrateTypes.SendToken(address(this), token, destinationChain, destinationAddress, amount);
extraFee = $.sendTokenFee;
emit TokenSent(sender, token, destinationChain, abi.encodePacked(destinationAddress), amount);
}
Expand All @@ -93,7 +93,7 @@ library Assets {

/// @dev Enqueues a create native token message to substrate.
/// @param token The ERC20 token address.
function registerToken(address gateway, address token, bytes2 createTokenCallID)
function registerToken(address token, bytes2 createTokenCallID)
external
returns (bytes memory payload, uint256 extraFee)
{
Expand All @@ -103,7 +103,7 @@ library Assets {
revert InvalidToken();
}

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

emit TokenRegistrationSent(token);
Expand Down
5 changes: 1 addition & 4 deletions core/packages/contracts/src/Gateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -384,10 +384,7 @@ contract Gateway is IGateway {

// Register a token on AssetHub
function registerToken(address token) external payable {
CoreStorage.Layout storage $ = CoreStorage.layout();
address assetHubAgent = $.agents[ASSET_HUB_AGENT_ID];

(bytes memory payload, uint256 extraFee) = Assets.registerToken(assetHubAgent, token, CREATE_TOKEN_CALL_ID);
(bytes memory payload, uint256 extraFee) = Assets.registerToken(token, CREATE_TOKEN_CALL_ID);

_submitOutbound(ASSET_HUB_PARA_ID, payload, extraFee);
}
Expand Down
2 changes: 2 additions & 0 deletions core/packages/contracts/src/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,5 @@ enum Command {
SetOperatingMode,
TransferNativeFromAgent
}

enum AgentExecuteCommand {TransferToken}
4 changes: 2 additions & 2 deletions core/packages/contracts/test/Gateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {SubstrateTypes} from "./../src/SubstrateTypes.sol";

import {NativeTransferFailed} from "../src/utils/SafeTransfer.sol";

import {InboundMessage, OperatingMode, ParaID, Config, Command} from "../src/Types.sol";
import {AgentExecuteCommand, InboundMessage, OperatingMode, ParaID, Config, Command} from "../src/Types.sol";

import {WETH9} from "canonical-weth/WETH9.sol";

Expand Down Expand Up @@ -259,7 +259,7 @@ contract GatewayTest is Test {

Gateway.AgentExecuteParams memory params = Gateway.AgentExecuteParams({
agentID: assetHubAgentID,
payload: abi.encode(keccak256("transferToken"), abi.encode(address(token), address(this), 1))
payload: abi.encode(AgentExecuteCommand.TransferToken, abi.encode(address(token), address(this), 1))
});

GatewayMock(address(gateway)).agentExecutePublic(abi.encode(params));
Expand Down
2 changes: 1 addition & 1 deletion core/packages/test/scripts/configure-bridgehub.sh
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fund_accounts()
transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $statemine_sovereign_account
transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $beacon_relayer_pub_key
transfer_balance $relaychain_ws_url "//Charlie" 1013 1000000000000000 $execution_relayer_pub_key
transfer_balance $relaychain_ws_url "//Charlie" 1000 1000000000000000 $registry_contract_sovereign_account
transfer_balance $relaychain_ws_url "//Charlie" 1000 1000000000000000 $gateway_contract_sovereign_account
}

configure_bridgehub()
Expand Down
8 changes: 4 additions & 4 deletions core/packages/test/scripts/set-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ statemine_sovereign_account="${STATEMINE_SOVEREIGN_ACCOUNT:-0x70617261e803000000
beacon_relayer_pub_key="${BEACON_RELAYER_PUB_KEY:-0xc46e141b5083721ad5f5056ba1cded69dce4a65f027ed3362357605b1687986a}"
# Execution relay account (//ExecutionRelay 5CFNWKMFPsw5Cs2Teo6Pvg7rWyjKiFfqPZs8U4MZXzMYFwXL in testnet)
execution_relayer_pub_key="${EXECUTION_RELAYER_PUB_KEY:-0x08228efd065c58a043da95c8bf177659fc587643e71e7ed1534666177730196f}"
# Registry contract account (5EBBfBLm4uV4JMXXcKvZrPVmP9VyER9YSCgGdMUw5wBXnqag in testnet)
registry_contract_sovereign_account="${REGISTRY_CONTRACT_SOVEREIGN_ACCOUNT:-0x5d6987649e0dac78ddf852eb0f1b1d1bf2be9623d81cb16c17cfa145948bb6dc}"
# Gateway contract account (H8VBFC4LG91ByxMG6GwsCcAacjitnzGmGbqnvSEQFBywJEL in testnet)
gateway_contract_sovereign_account="${GATEWAY_CONTRACT_SOVEREIGN_ACCOUNT:-0xc9794dd8013efb2ad83f668845c62b373c16ad33971745731408058e4d0c6ff5}"

# Config for deploying contracts

Expand All @@ -80,11 +80,11 @@ export RANDAO_COMMIT_EXP="${ETH_RANDAO_EXP:-3}"

export BRIDGE_HUB_PARAID=$bridgehub_para_id
# TODO: update placeholder value
export BRIDGE_HUB_AGENT_ID="00000000000000000000000000000000000000000000000000000000000003f5"
export BRIDGE_HUB_AGENT_ID="0x05f0ced792884ed09997292bd95f8d0d1094bb3bded91ec3f2f08531624037d6"

export ASSET_HUB_PARAID=$statemine_para_id
# TODO: update placeholder value
export ASSET_HUB_AGENT_ID="00000000000000000000000000000000000000000000000000000000000003e8"
export ASSET_HUB_AGENT_ID="0x72456f48efed08af20e5b317abf8648ac66e86bb90a411d9b0b713f7364b75b4"

export DEFAULT_FEE=1
export DEFAULT_REWARD=1
Expand Down
2 changes: 2 additions & 0 deletions parachain/Cargo.lock

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

5 changes: 4 additions & 1 deletion parachain/pallets/control/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "ma

xcm = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false }
xcm-builder = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false }
xcm-executor = { git = "https://github.com/paritytech/polkadot.git", branch = "master", default-features = false }

ethabi = { git = "https://github.com/Snowfork/ethabi-decode.git", package = "ethabi-decode", branch = "master", default-features = false }

Expand All @@ -49,10 +50,12 @@ std = [
"sp-runtime/std",
"xcm/std",
"xcm-builder/std",
"xcm-executor/std",
"ethabi/std"
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"xcm-builder/runtime-benchmarks"
"xcm-builder/runtime-benchmarks",
"xcm-executor/runtime-benchmarks"
]
try-runtime = ["frame-support/try-runtime"]
21 changes: 14 additions & 7 deletions parachain/pallets/control/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ pub use weights::*;

use snowbridge_core::{Command, OutboundMessage, OutboundQueue as OutboundQueueTrait, ParaId};
use sp_core::{H160, H256};
use sp_io::hashing::blake2_256;
use sp_runtime::traits::Hash;
use sp_std::prelude::*;
use xcm::prelude::*;
use xcm_builder::DescribeLocation;
use xcm_executor::traits::ConvertLocation;

pub use pallet::*;

Expand All @@ -47,7 +46,9 @@ pub mod pallet {
type WeightInfo: WeightInfo;
type MaxUpgradeDataSize: Get<u32>;
type CreateAgentOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = MultiLocation>;
type DescribeAgentLocation: DescribeLocation;
type AgentHashedDescription: ConvertLocation<H256>;
type UniversalLocation: Get<InteriorMultiLocation>;
type RelayLocation: Get<MultiLocation>;
}

#[pallet::event]
Expand Down Expand Up @@ -107,13 +108,19 @@ pub mod pallet {
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::create_agent())]
pub fn create_agent(origin: OriginFor<T>) -> DispatchResult {
let agent_location: MultiLocation = T::CreateAgentOrigin::ensure_origin(origin)?;
let mut agent_location: MultiLocation = T::CreateAgentOrigin::ensure_origin(origin)?;

// Normalize all locations relative to the relay unless its the relay itself.
let relay_location = T::RelayLocation::get();
if agent_location != relay_location {
agent_location
.reanchor(&relay_location, T::UniversalLocation::get())
.or(Err(Error::<T>::LocationConversionFailed))?;
}

let agent_description = T::DescribeAgentLocation::describe_location(&agent_location)
let agent_id = T::AgentHashedDescription::convert_location(&agent_location)
.ok_or(Error::<T>::LocationConversionFailed)?;

let agent_id: H256 = blake2_256(&agent_description).into();

if Agents::<T>::contains_key(agent_id) {
return Ok(());
}
Expand Down
22 changes: 9 additions & 13 deletions parachain/pallets/control/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use frame_support::{
};

#[cfg(feature = "runtime-benchmarks")]
use frame_benchmarking::whitelisted_caller;
use frame_benchmarking::v2::whitelisted_caller;

use snowbridge_core::{OutboundMessage, OutboundMessageHash, ParaId, SubmitError};
use sp_core::H256;
Expand All @@ -18,10 +18,7 @@ use sp_runtime::{
AccountId32,
};
use xcm::prelude::*;
use xcm_builder::{
DescribeAccountId32Terminal, DescribeAccountKey20Terminal, DescribeAllTerminal, DescribeFamily,
DescribePalletTerminal,
};
use xcm_builder::{DescribeAllTerminal, DescribeFamily, HashedDescription};

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
Expand Down Expand Up @@ -71,6 +68,10 @@ parameter_types! {
pub const MaxUpgradeDataSize: u32 = 1024;
pub const SS58Prefix: u8 = 42;
pub const AnyNetwork: Option<NetworkId> = None;
pub const RelayNetwork: Option<NetworkId> = Some(NetworkId::Kusama);
pub const RelayLocation: MultiLocation = MultiLocation::parent();
pub UniversalLocation: InteriorMultiLocation =
X2(GlobalConsensus(RelayNetwork::get().unwrap()), Parachain(1013));
}

static ORIGIN_TABLE: &[([u8; 32], MultiLocation)] = &[
Expand Down Expand Up @@ -184,21 +185,16 @@ impl snowbridge_control::OutboundQueueTrait for MockOutboundQueue {
}
}

pub type DescribeAgentLocation = (
DescribePalletTerminal,
DescribeAccountId32Terminal,
DescribeAccountKey20Terminal,
DescribeFamily<DescribeAllTerminal>,
);

impl snowbridge_control::Config for Test {
type RuntimeEvent = RuntimeEvent;
type OwnParaId = OwnParaId;
type OutboundQueue = MockOutboundQueue;
type MessageHasher = BlakeTwo256;
type MaxUpgradeDataSize = MaxUpgradeDataSize;
type CreateAgentOrigin = EnsureOriginFromTable;
type DescribeAgentLocation = DescribeAgentLocation;
type UniversalLocation = UniversalLocation;
type RelayLocation = RelayLocation;
type AgentHashedDescription = HashedDescription<H256, DescribeFamily<DescribeAllTerminal>>;
type WeightInfo = ();
}

Expand Down
25 changes: 13 additions & 12 deletions parachain/pallets/control/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ fn create_agent_with_local_account32_yields_success() {
new_test_ext().execute_with(|| {
let origin = RuntimeOrigin::signed(AccountId32::new([2; 32]));
let expected_agent_id =
H256(hex!("9e85ef53611dcb973a337977a79217890f6c0d605de20ae4a828b1b9a95162c4"));
H256(hex!("57fc5659083f0cc883125ccb2c380a1397a3b08434586b8647cc44bcb3647d29"));
let expected_multi_location = MultiLocation {
parents: 0,
interior: X1(Junction::AccountId32 { network: None, id: [0; 32] }),
interior: X2(Parachain(1013), Junction::AccountId32 { network: None, id: [0; 32] }),
};

assert!(!Agents::<Test>::contains_key(expected_agent_id));
Expand All @@ -89,10 +89,10 @@ fn create_agent_with_local_account20_yields_success() {
new_test_ext().execute_with(|| {
let origin = RuntimeOrigin::signed(AccountId32::new([3; 32]));
let expected_agent_id =
H256(hex!("927a4def0d0bdd151dfa247a07e4036e12335ee71977426847be6e6e36e3c460"));
H256(hex!("fc29ec0899cf25874937d04b9b011760fa5dc5cf59af1448abefd389bba7bea2"));
let expected_multi_location = MultiLocation {
parents: 0,
interior: X1(AccountKey20 { network: None, key: [0; 20] }),
interior: X2(Parachain(1013), AccountKey20 { network: None, key: [0; 20] }),
};

assert!(!Agents::<Test>::contains_key(expected_agent_id));
Expand All @@ -111,8 +111,9 @@ fn create_agent_with_local_pallet_yields_success() {
new_test_ext().execute_with(|| {
let origin = RuntimeOrigin::signed(AccountId32::new([4; 32]));
let expected_agent_id =
H256(hex!("964c3b3f978db1febb282d675dcf2196eae3c28fd7c0885b738cee828262fcc2"));
let expected_multi_location = MultiLocation { parents: 0, interior: X1(PalletInstance(1)) };
H256(hex!("ed40c69763094b73c0e3585eeb576fbcee6999123ff1f1beac1f05f5f4c9d945"));
let expected_multi_location =
MultiLocation { parents: 0, interior: X2(Parachain(1013), PalletInstance(1)) };

assert!(!Agents::<Test>::contains_key(expected_agent_id));
assert_eq!(EthereumControl::create_agent(origin), Ok(()));
Expand All @@ -130,8 +131,8 @@ fn create_agent_with_sibling_chain_origin_yields_success() {
new_test_ext().execute_with(|| {
let origin = RuntimeOrigin::signed(AccountId32::new([5; 32]));
let expected_agent_id =
H256(hex!("81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79"));
let expected_multi_location = MultiLocation { parents: 1, interior: X1(Parachain(1000)) };
H256(hex!("72456f48efed08af20e5b317abf8648ac66e86bb90a411d9b0b713f7364b75b4"));
let expected_multi_location = MultiLocation { parents: 0, interior: X1(Parachain(1000)) };

assert!(!Agents::<Test>::contains_key(expected_agent_id));
assert_eq!(EthereumControl::create_agent(origin), Ok(()));
Expand All @@ -149,9 +150,9 @@ fn create_agent_with_sibling_chain_account32_origin_yields_success() {
new_test_ext().execute_with(|| {
let origin = RuntimeOrigin::signed(AccountId32::new([7; 32]));
let expected_agent_id =
H256(hex!("75ad585e231db5daf900819e8fb62af432610619d0d7a1156e5d78531b2c6493"));
H256(hex!("fb804b0b77f9c9d69a16d7a45de81225ab8da112e0eb8d2e0229c78086b8927a"));
let expected_multi_location = MultiLocation {
parents: 1,
parents: 0,
interior: X2(Parachain(1000), Junction::AccountId32 { network: None, id: [0; 32] }),
};

Expand All @@ -171,9 +172,9 @@ fn create_agent_with_sibling_chain_account20_origin_yields_success() {
new_test_ext().execute_with(|| {
let origin = RuntimeOrigin::signed(AccountId32::new([8; 32]));
let expected_agent_id =
H256(hex!("3a737a558137d674c5e9c49bd0e6389bf69e1825c8fd531af5534081016501ef"));
H256(hex!("74867486f141b159ba1e295bf616d740429269879d4291a12a65eaedbb4b502a"));
let expected_multi_location = MultiLocation {
parents: 1,
parents: 0,
interior: X2(Parachain(1000), AccountKey20 { network: None, key: [0; 20] }),
};

Expand Down
4 changes: 2 additions & 2 deletions parachain/pallets/inbound-queue/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ mod benchmarks {
use super::*;
use crate::benchmarking::fixtures::make_create_message;
use hex_literal::hex;
use sp_runtime::print;

const GATEWAY_ADDRESS: [u8; 20] = hex!["eda338e4dc46038493b885327842fd3e301cab39"];

Expand All @@ -34,7 +33,8 @@ mod benchmarks {
let sovereign_account = dest_para.into_account_truncating();

let minimum_balance = T::Token::minimum_balance();
let minimum_balance_u32: u32 = minimum_balance.try_into()
let minimum_balance_u32: u32 = minimum_balance
.try_into()
.unwrap_or_else(|_| panic!("unable to cast minimum balance to u32"));

// Make sure the sovereign balance is enough. This is a funny number, because
Expand Down
14 changes: 11 additions & 3 deletions parachain/primitives/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,21 @@ pub enum AgentExecuteCommand {
}

impl AgentExecuteCommand {
fn index(&self) -> u8 {
match self {
AgentExecuteCommand::TransferToken { .. } => 0,
}
}
pub fn abi_encode(&self) -> Vec<u8> {
match self {
AgentExecuteCommand::TransferToken { token, recipient, amount } => {
ethabi::encode(&vec![
Token::Address(*token),
Token::Address(*token),
Token::Uint(U256::from(*amount)),
Token::Uint(self.index().into()),
Token::Bytes(ethabi::encode(&vec![
Token::Address(*token),
Token::Address(*recipient),
Token::Uint(U256::from(*amount)),
])),
])
},
}
Expand Down
Loading

0 comments on commit b0f922c

Please sign in to comment.