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

Add evm transaction support #170

Merged
merged 5 commits into from
Jun 17, 2022
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: 1 addition & 1 deletion contracts/periphery/contracts/s2s/examples/RemarkDemo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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...
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -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...
}
Expand All @@ -78,4 +84,8 @@ contract RemoteTransactDemo is SmartChainXApp, Ownable {
requireSenderOfSourceChain(0, senderOfSourceChain);
number = number + _value;
}

function setSenderOfSourceChain(address _senderOfSourceChain) public onlyOwner {
senderOfSourceChain = _senderOfSourceChain;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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...
}
}
26 changes: 0 additions & 26 deletions contracts/periphery/contracts/s2s/test/SubstrateTransact.sol

This file was deleted.

35 changes: 35 additions & 0 deletions contracts/periphery/contracts/s2s/test/Transact.sol
Original file line number Diff line number Diff line change
@@ -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
hackfisher marked this conversation as resolved.
Show resolved Hide resolved
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"
);
}
}
112 changes: 104 additions & 8 deletions contracts/periphery/contracts/s2s/types/PalletEthereum.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}
}
}