Skip to content

Commit

Permalink
feat(NEAR): Add a test with successfully broadcasted NEP-141 transact…
Browse files Browse the repository at this point in the history
…ion (#3183)

* feat(NEAR): Add a test with successfully broadcasted NEP-141 transaction

* [r2r] Bump wallet-core-kotlin dep to 3.1.36

* [r2r] Bump wallet-core-kotlin dep to 3.1.35

* [r2r] Try to use 3.1.36 wallet-core-kotlin version

* feat(NEAR): Add TokenTransfer action to NEAR signing input
  • Loading branch information
satoshiotomakan authored Jun 2, 2023
1 parent 75445c4 commit 672e9ef
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 1 deletion.
34 changes: 33 additions & 1 deletion src/NEAR/Serialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@
#include "../BinaryCoding.h"
#include "../PrivateKey.h"

#include <nlohmann/json.hpp>

namespace TW::NEAR {

using json = nlohmann::json;

static constexpr auto tokenTransferMethodName = "ft_transfer";

static void writeU8(Data& data, uint8_t number) {
data.push_back(number);
}
Expand Down Expand Up @@ -105,8 +111,31 @@ static void writeDeleteAccount(Data& data, const Proto::DeleteAccount& deleteAcc
writeString(data, deleteAccount.beneficiary_id());
}

static void writeTokenTransfer(Data& data, const Proto::TokenTransfer& tokenTransfer) {
writeString(data, tokenTransferMethodName);

json functionCallArgs = {
{"amount", tokenTransfer.token_amount()},
{"receiver_id", tokenTransfer.receiver_id()},
};
auto functionCallArgsStr = functionCallArgs.dump();

writeU32(data, static_cast<uint32_t>(functionCallArgsStr.size()));
writeRawBuffer(data, functionCallArgsStr);

writeU64(data, tokenTransfer.gas());
writeU128(data, tokenTransfer.deposit());
}

static void writeAction(Data& data, const Proto::Action& action) {
writeU8(data, action.payload_case() - Proto::Action::kCreateAccount);
uint8_t actionByte = action.payload_case() - Proto::Action::kCreateAccount;
// `TokenTransfer` action is actually a `FunctionCall`,
// so we need to set the actionByte to the proper value.
if (action.payload_case() == Proto::Action::kTokenTransfer) {
actionByte = Proto::Action::kFunctionCall - Proto::Action::kCreateAccount;
}

writeU8(data, actionByte);
switch (action.payload_case()) {
case Proto::Action::kFunctionCall:
writeFunctionCall(data, action.function_call());
Expand All @@ -126,6 +155,9 @@ static void writeAction(Data& data, const Proto::Action& action) {
case Proto::Action::kDeleteAccount:
writeDeleteAccount(data, action.delete_account());
return;
case Proto::Action::kTokenTransfer:
writeTokenTransfer(data, action.token_transfer());
return;
default:
return;
}
Expand Down
17 changes: 17 additions & 0 deletions src/proto/NEAR.proto
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ message DeleteAccount {
string beneficiary_id = 1;
}

// Fungible token transfer
message TokenTransfer {
// Token amount. Base-10 decimal string.
string token_amount = 1;

// ID of the receiver.
string receiver_id = 2;

// Gas.
uint64 gas = 3;

// NEAR deposit amount; uint128 / big endian byte order.
bytes deposit = 4;
}

// Represents an action
message Action {
oneof payload {
Expand All @@ -104,6 +119,8 @@ message Action {
AddKey add_key = 6;
DeleteKey delete_key = 7;
DeleteAccount delete_account = 8;
// Gap in field numbering is intentional as it's not a standard NEAR action.
TokenTransfer token_transfer = 13;
}
}

Expand Down
34 changes: 34 additions & 0 deletions tests/chains/NEAR/TWAnySignerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,38 @@ TEST(TWAnySignerNEAR, SignUnstakeMainnetReplication) {
ASSERT_EQ(Base64::encode(data(output.signed_transaction())), "QAAAAGI4ZDVkZjI1MDQ3ODQxMzY1MDA4ZjMwZmI2YjMwZGQ4MjBlOWE4NGQ4NjlmMDU2MjNkMTE0ZTk2ODMxZjJmYmYAzgCT6NK76nb1mB7pToefgkGUHfUe5BKvvr3gW/nq+MgGuu1Mq0YAABEAAABhdmFkby5wb29sdjEubmVhcq0YnhRlt+TTtagkoy0qKn56zAfGhE+jkTJW6PR5k5r8AQAAAAILAAAAdW5zdGFrZV9hbGwCAAAAe30A0JjUr3EAAAAAAAAAAAAAAAAAAAAAAAAABaFP0EkfJU3VQZ4QAiTwq9ebWDJ7jx7TxbA+VGH4hwKX3gWnmDHVve+LK7/UbbffjF/y8vn0KrPxdh3ONAG0Ag==");
}

/// Implements NEP-141:
/// https://nomicon.io/Standards/Tokens/FungibleToken/Core
///
/// Successfully broadcasted tx:
/// https://explorer.near.org/transactions/ABQY6nfLdNrRVynHYNjYkfUM6Up5pDHHpuhRJe6FCMRu
TEST(TWAnySignerNEAR, SignTokenTransfer) {
auto privateKey = parse_hex("77006e227658c18da47546413926a26b839204b1b19e807c4a13d994d661c72e");

auto blockHash = Base58::decode("2dQBYs8XjprLLgtH7eVsJ3e58A5QuRcbuqFisSk9fFWQ");

// Deposit should be 1 yocto NEAR for security purposes.
auto deposit = parse_hex("01000000000000000000000000000000");

Proto::SigningInput input;
input.set_signer_id("105396228ac2e0ef144b93bcc5322fca1167d524422bb73d17440d35c714a58f");
input.set_nonce(93062928000003);
input.set_receiver_id("token.paras.near");
input.set_private_key(privateKey.data(), 32);
input.set_block_hash(blockHash.data(), blockHash.size());

auto& action = *input.add_actions();
auto& tokenTransfer = *action.mutable_token_transfer();
tokenTransfer.set_token_amount("100000000000000000");
tokenTransfer.set_receiver_id("c6d5e3e8f328436f595856a598239b691d3d136b24c05a4614f9e9716edc14fe");
tokenTransfer.set_gas(15000000000000);
tokenTransfer.set_deposit(deposit.data(), deposit.size());

Proto::SigningOutput output;
ANY_SIGN(input, TWCoinTypeNEAR);

ASSERT_EQ(Base58::encode(data(output.hash())), "ABQY6nfLdNrRVynHYNjYkfUM6Up5pDHHpuhRJe6FCMRu");
ASSERT_EQ(Base64::encode(data(output.signed_transaction())), "QAAAADEwNTM5NjIyOGFjMmUwZWYxNDRiOTNiY2M1MzIyZmNhMTE2N2Q1MjQ0MjJiYjczZDE3NDQwZDM1YzcxNGE1OGYAEFOWIorC4O8US5O8xTIvyhFn1SRCK7c9F0QNNccUpY8D5MPmo1QAABAAAAB0b2tlbi5wYXJhcy5uZWFyGC7O0jXN2b4SH1XfMtNISEnU8XATKOhZwxx0pLLZqTEBAAAAAgsAAABmdF90cmFuc2ZlcnAAAAB7ImFtb3VudCI6IjEwMDAwMDAwMDAwMDAwMDAwMCIsInJlY2VpdmVyX2lkIjoiYzZkNWUzZThmMzI4NDM2ZjU5NTg1NmE1OTgyMzliNjkxZDNkMTM2YjI0YzA1YTQ2MTRmOWU5NzE2ZWRjMTRmZSJ9APCrdaQNAAABAAAAAAAAAAAAAAAAAAAAANUjO7fmnTebSNW9EcHHwYwPNlQJcReGWJfJUuxWzPDAGEeo4JTcLB8pLCkqxKKsI0NE1Szv2+GAt5mCBum5mQY=");
}

} // namespace TW::NEAR

0 comments on commit 672e9ef

Please sign in to comment.