diff --git a/contracts/periphery/contracts/s2s/examples/RemarkDemo.sol b/contracts/periphery/contracts/s2s/examples/RemarkDemo.sol index 283860b72..1894d2988 100644 --- a/contracts/periphery/contracts/s2s/examples/RemarkDemo.sol +++ b/contracts/periphery/contracts/s2s/examples/RemarkDemo.sol @@ -74,7 +74,7 @@ contract RemarkDemo is SmartChainXApp, Ownable { ) external override { require( msg.sender == callbackSender, - "Only pallet address is allowed call 'onMessageDelivered'" + "Only pallet address is allowed to call 'onMessageDelivered'" ); // TODO: Your code goes here... } diff --git a/contracts/periphery/contracts/s2s/examples/RemoteTransactDemo.sol b/contracts/periphery/contracts/s2s/examples/TransactDemo.sol similarity index 63% rename from contracts/periphery/contracts/s2s/examples/RemoteTransactDemo.sol rename to contracts/periphery/contracts/s2s/examples/TransactDemo.sol index e842a096d..73e79d3d1 100644 --- a/contracts/periphery/contracts/s2s/examples/RemoteTransactDemo.sol +++ b/contracts/periphery/contracts/s2s/examples/TransactDemo.sol @@ -6,8 +6,12 @@ import "../SmartChainXApp.sol"; import "@darwinia/contracts-utils/contracts/Ownable.sol"; import "../types/PalletEthereum.sol"; -// deploy on the target chain first, then deploy on the source chain -contract RemoteTransactDemo is SmartChainXApp, Ownable { +/// 0. Correct the globle settings, line 22. Corrent the storage keys, line 60, 62 +/// 1. Deploy on the target chain, get the address of the deployed contract: `tgtAddress`. +/// 2. Replace the `to` of evm transaction with `tgtAddress`, line 43. +/// 3. Deploy on the source chain, get the address of the deployed contract: `srcAddress`. +/// 4. Update the senderOfSourceChain to `srcAddress` by calling `setSenderOfSourceChain` function. +contract TransactDemo is SmartChainXApp, Ownable { uint256 public number; // source chain ethereum sender address, @@ -28,18 +32,20 @@ contract RemoteTransactDemo is SmartChainXApp, Ownable { function callAddOnTheTargetChain() public payable { // 1. prepare the call that will be executed on the target chain - PalletEthereum.SubstrateTransactCall memory call = PalletEthereum - .SubstrateTransactCall( - // the call index of substrate_transact - 0x2902, - // the address of the contract on the target chain + PalletEthereum.TransactCall memory call = PalletEthereum.TransactCall( + // the call index of substrate_transact + 0x2902, + // the evm transaction to transact + PalletEthereum.buildTransactionV2( + 0, // evm tx nonce, nonce on the target chain + pending nonce on the source chain + 1 + 1000000000, // gasPrice, get from the target chain + 600000, // gasLimit, get from the target chain 0x50275d3F95E0F2FCb2cAb2Ec7A231aE188d7319d, // <------------------ change to the contract address on the target chain - // the add function bytes that will be called on the target chain, add(2) - hex"1003e2d20000000000000000000000000000000000000000000000000000000000000002" - ); - bytes memory callEncoded = PalletEthereum.encodeSubstrateTransactCall( - call + 0, // value, 0 means no value transfer + hex"1003e2d20000000000000000000000000000000000000000000000000000000000000002" // the add function bytes that will be called on the target chain, add(2) + ) ); + bytes memory callEncoded = PalletEthereum.encodeTransactCall(call); // 2. send the message MessagePayload memory payload = MessagePayload( @@ -65,7 +71,7 @@ contract RemoteTransactDemo is SmartChainXApp, Ownable { ) external override { require( msg.sender == callbackSender, - "Only pallet address is allowed call 'onMessageDelivered'" + "Only pallet address is allowed to call 'onMessageDelivered'" ); // TODO: Your code goes here... } @@ -78,4 +84,8 @@ contract RemoteTransactDemo is SmartChainXApp, Ownable { requireSenderOfSourceChain(0, senderOfSourceChain); number = number + _value; } + + function setSenderOfSourceChain(address _senderOfSourceChain) public onlyOwner { + senderOfSourceChain = _senderOfSourceChain; + } } diff --git a/contracts/periphery/contracts/s2s/examples/UnlockFromRemoteDemo.sol b/contracts/periphery/contracts/s2s/examples/UnlockFromRemoteDemo.sol index fa87aa5e7..c297bec9f 100644 --- a/contracts/periphery/contracts/s2s/examples/UnlockFromRemoteDemo.sol +++ b/contracts/periphery/contracts/s2s/examples/UnlockFromRemoteDemo.sol @@ -47,7 +47,7 @@ contract UnlockFromRemoteDemo is SmartChainXApp { } function onMessageDelivered(bytes4 lane, uint64 nonce, bool result) external override { - require(msg.sender == callbackSender, "Only pallet address is allowed call 'onMessageDelivered'"); + require(msg.sender == callbackSender, "Only pallet address is allowed to call 'onMessageDelivered'"); // TODO: Your code goes here... } } diff --git a/contracts/periphery/contracts/s2s/test/SubstrateTransact.sol b/contracts/periphery/contracts/s2s/test/SubstrateTransact.sol deleted file mode 100644 index b3d4d600a..000000000 --- a/contracts/periphery/contracts/s2s/test/SubstrateTransact.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity >=0.6.0; - -import "../SmartChainXLib.sol"; -import "../types/PalletEthereum.sol"; - -// used for testing -contract SubstrateTransact { - function substrateTransact() public payable { - // 1. prepare the call that will be executed on the target chain - PalletEthereum.SubstrateTransactCall memory call = PalletEthereum.SubstrateTransactCall( - 0x2902, - 0xC2Bf5F29a4384b1aB0C063e1c666f02121B6084a, - hex"1003e2d20000000000000000000000000000000000000000000000000000000000000002" - ); - bytes memory callEncoded = PalletEthereum.encodeSubstrateTransactCall(call); - - // 2. send the message - SmartChainXLib.dispatch( - 0x0000000000000000000000000000000000000019, - callEncoded, - "Dispatch substrate_transact failed" - ); - } -} diff --git a/contracts/periphery/contracts/s2s/test/Transact.sol b/contracts/periphery/contracts/s2s/test/Transact.sol new file mode 100644 index 000000000..9e884bf5a --- /dev/null +++ b/contracts/periphery/contracts/s2s/test/Transact.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.6.0; + +import "../SmartChainXLib.sol"; +import "../types/PalletEthereum.sol"; + +// used for testing +contract Transact { + function transact() public payable { + // 1. prepare the call that will be executed on the target chain + PalletEthereum.TransactCall memory call = PalletEthereum + .TransactCall( + // the call index of substrate_transact + 0x2902, + // the evm transaction to transact + PalletEthereum.buildTransactionV2( + 0, // evm tx nonce, nonce on the target chain + pending nonce on the source chain + 1 + 1000000000, // gasPrice, get from the target chain + 600000, // gasLimit, get from the target chain + 0x50275d3F95E0F2FCb2cAb2Ec7A231aE188d7319d, // <------------------ change to the contract address on the target chain + 0, // value, 0 means no value transfer + hex"1003e2d20000000000000000000000000000000000000000000000000000000000000002" // the add function bytes that will be called on the target chain, add(2) + ) + ); + bytes memory callEncoded = PalletEthereum.encodeTransactCall(call); + + // 2. send the message + SmartChainXLib.dispatch( + 0x0000000000000000000000000000000000000019, + callEncoded, + "Dispatch substrate_transact failed" + ); + } +} diff --git a/contracts/periphery/contracts/s2s/types/PalletEthereum.sol b/contracts/periphery/contracts/s2s/types/PalletEthereum.sol index f42e0d6e2..0b1776178 100644 --- a/contracts/periphery/contracts/s2s/types/PalletEthereum.sol +++ b/contracts/periphery/contracts/s2s/types/PalletEthereum.sol @@ -7,17 +7,113 @@ import "@darwinia/contracts-utils/contracts/ScaleCodec.sol"; import "./CommonTypes.sol"; library PalletEthereum { - struct SubstrateTransactCall { + struct TransactCall { bytes2 callIndex; - address target; + EnumItemTransactionV2WithLegacyTransaction transaction; + } + + function encodeTransactCall(TransactCall memory call) + internal + pure + returns (bytes memory) + { + return + abi.encodePacked( + call.callIndex, + encodeEnumItemTransactionV2WithLegacyTransaction( + call.transaction + ) + ); + } + + struct EnumItemTransactionActionWithAddress { + uint8 index; + address h160; + } + + function encodeEnumItemTransactionActionWithAddress( + EnumItemTransactionActionWithAddress memory item + ) internal pure returns (bytes memory) { + return abi.encodePacked(item.index, item.h160); + } + + struct LegacyTransaction { + uint256 nonce; + uint256 gasPrice; + uint256 gasLimit; + EnumItemTransactionActionWithAddress action; + uint256 value; bytes input; + uint64 v; + bytes32 r; + bytes32 s; } - function encodeSubstrateTransactCall(SubstrateTransactCall memory call) internal pure returns (bytes memory) { - return abi.encodePacked( - call.callIndex, - call.target, - ScaleCodec.encodeBytes(call.input) + function encodeLegacyTransaction(LegacyTransaction memory transaction) + internal + pure + returns (bytes memory) + { + return + abi.encodePacked( + ScaleCodec.encode256(transaction.nonce), + ScaleCodec.encode256(transaction.gasPrice), + ScaleCodec.encode256(transaction.gasLimit), + encodeEnumItemTransactionActionWithAddress(transaction.action), + ScaleCodec.encode256(transaction.value), + ScaleCodec.encodeBytes(transaction.input), + ScaleCodec.encode64(transaction.v), + transaction.r, + transaction.s + ); + } + + struct EnumItemTransactionV2WithLegacyTransaction { + uint8 index; + LegacyTransaction legacyTransaction; + } + + function encodeEnumItemTransactionV2WithLegacyTransaction( + EnumItemTransactionV2WithLegacyTransaction memory item + ) internal pure returns (bytes memory) { + return + abi.encodePacked( + item.index, + encodeLegacyTransaction(item.legacyTransaction) + ); + } + + function buildTransactionV2( + uint256 nonce, + uint256 gasPrice, + uint256 gasLimit, + address to, + uint256 value, + bytes memory input + ) + internal + pure + returns (EnumItemTransactionV2WithLegacyTransaction memory) + { + LegacyTransaction memory transaction = LegacyTransaction( + nonce, + gasPrice, + gasLimit, + PalletEthereum.EnumItemTransactionActionWithAddress( + 0, // enum index + to + ), + value, + input, + 0, // v + 0, // r + 0 // s ); + + return + EnumItemTransactionV2WithLegacyTransaction( + 0, // enum index + transaction // legacyTransaction + ); } -} \ No newline at end of file +}