Skip to content

Commit

Permalink
[Aptos]: Add transfer_coins function call (#3344)
Browse files Browse the repository at this point in the history
* feat(aptos): Add `TokenTransferCoinsMessage` to call `aptos_account::transfer_coins` function

* Add test with testnet transaction

* feat(aptos): Add mainnet test

* feat(aptos): Fix comment
  • Loading branch information
satoshiotomakan authored Aug 2, 2023
1 parent 2e8ae43 commit b2749b2
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,47 @@ class TestAptosSigner {
"07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"
)
}

@Test
fun AptosTransferTokensCoins() {
// Successfully broadcasted https://explorer.aptoslabs.com/txn/0x197d40ea12e2bfc65a0a913b9f4ca3b0b0208fe0c1514d3d55cef3d5bcf25211?network=mainnet
val key =
"e7f56c77189e03699a75d8ec5c090e41f3d9d4783bc49c33df8a93d915e10de8".toHexBytesInByteString()

val function = Aptos.StructTag.newBuilder()
.setAccountAddress("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9")
.setModule("mee_coin")
.setName("MeeCoin")
.build()

val transfer = Aptos.TokenTransferCoinsMessage.newBuilder()
.setAmount(10000)
.setTo("0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c")
.setFunction(function)
.build()
val signingInput = Aptos.SigningInput.newBuilder()
.setChainId(1)
.setSender("0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25")
.setSequenceNumber(2)
.setGasUnitPrice(100)
.setMaxGasAmount(2000)
.setExpirationTimestampSecs(3664390082)
.setTokenTransferCoins(transfer)
.setPrivateKey(key)
.build()

val result = AnySigner.sign(signingInput, CoinType.APTOS, Aptos.SigningOutput.parser())
assertEquals(
Numeric.cleanHexPrefix(Numeric.toHexString(result.rawTxn.toByteArray())),
"1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001"
)
assertEquals(
Numeric.cleanHexPrefix(Numeric.toHexString(result.authenticator.signature.toByteArray())),
"30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d"
)
assertEquals(
Numeric.cleanHexPrefix(Numeric.toHexString(result.encoded.toByteArray())),
"1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d"
)
}
}
12 changes: 12 additions & 0 deletions src/Aptos/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ TransactionPayload tokenTransferPayload(const Proto::SigningInput& input) {
return payload;
}

TransactionPayload tokenTransferCoinsPayload(const Proto::SigningInput& input) {
auto&& [args, argsJson] = commonTransferPayload(input.token_transfer_coins());
auto& function = input.token_transfer_coins().function();
TypeTag tokenTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()),
function.module(), function.name(), {})})};
TransactionPayload payload = EntryFunction(gAptosAccountModule, "transfer_coins", {tokenTransferTag}, args, argsJson);
return payload;
}

TransactionPayload registerTokenPayload(const Proto::SigningInput& input) {

auto& function = input.register_token().function();
Expand Down Expand Up @@ -257,6 +266,9 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) {
case Proto::SigningInput::kLiquidStakingMessage: {
return liquidStakingFunctor(input.liquid_staking_message());
}
case Proto::SigningInput::kTokenTransferCoins: {
return tokenTransferCoinsPayload(input);
}
case Proto::SigningInput::TRANSACTION_PAYLOAD_NOT_SET:
throw std::runtime_error("Transaction payload should be set");
}
Expand Down
14 changes: 13 additions & 1 deletion src/proto/Aptos.proto
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ message StructTag {
string name = 3;
}

// Necessary fields to process a TokenTransferMessage
// Necessary fields to process a `0x1::coin::transfer` function.
message TokenTransferMessage {
// Destination Account address (string)
string to = 1;
Expand All @@ -37,6 +37,17 @@ message TokenTransferMessage {
StructTag function = 3;
}

// Necessary fields to process a `0x1::aptos_account::transfer_coins` function.
// Can be used to transfer tokens with registering the recipient account if needed.
message TokenTransferCoinsMessage {
// Destination Account address (string)
string to = 1;
// Amount to be transferred (uint64)
uint64 amount = 2;
// token function to call, e.g BTC: 0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC
StructTag function = 3;
}

// Necessary fields to process a ManagedTokensRegisterMessage
message ManagedTokensRegisterMessage {
// token function to register, e.g BTC: 0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC
Expand Down Expand Up @@ -153,6 +164,7 @@ message SigningInput {
NftMessage nft_message = 12;
ManagedTokensRegisterMessage register_token = 13;
LiquidStaking liquid_staking_message = 14;
TokenTransferCoinsMessage token_transfer_coins = 15;
}
}

Expand Down
46 changes: 46 additions & 0 deletions tests/chains/Aptos/SignerTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,4 +681,50 @@ TEST(AptosSigner, TokenTxSign) {
assertJSONEqual(expectedJson, parsedJson);
}

TEST(AptosSigner, TokenTransferCoins) {
// Successfully broadcasted https://explorer.aptoslabs.com/txn/0x197d40ea12e2bfc65a0a913b9f4ca3b0b0208fe0c1514d3d55cef3d5bcf25211?network=mainnet
Proto::SigningInput input;
input.set_sender("0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25");
input.set_sequence_number(2);
auto& tf = *input.mutable_token_transfer_coins();
tf.set_to("0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c");
tf.set_amount(10000);
tf.mutable_function()->set_account_address("0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9");
tf.mutable_function()->set_module("mee_coin");
tf.mutable_function()->set_name("MeeCoin");
input.set_max_gas_amount(2000);
input.set_gas_unit_price(100);
input.set_expiration_timestamp_secs(3664390082);
input.set_chain_id(1);
auto privateKey = PrivateKey(parse_hex("e7f56c77189e03699a75d8ec5c090e41f3d9d4783bc49c33df8a93d915e10de8"));
input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size());
auto result = Signer::sign(input);

ASSERT_EQ(hex(result.raw_txn()), "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001");
ASSERT_EQ(hex(result.authenticator().signature()), "30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d");
ASSERT_EQ(hex(result.encoded()), "1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf2502000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e730107e9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9086d65655f636f696e074d6565436f696e000220b7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c081027000000000000d0070000000000006400000000000000c2276ada0000000001002062e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca83694030ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d");
nlohmann::json expectedJson = R"(
{
"expiration_timestamp_secs": "3664390082",
"gas_unit_price": "100",
"max_gas_amount": "2000",
"payload": {
"arguments": ["0xb7c7d12080209e9dc14498c80200706e760363fb31782247e82cf57d1d6e5d6c","10000"],
"function": "0x1::aptos_account::transfer_coins",
"type": "entry_function_payload",
"type_arguments": ["0xe9c192ff55cffab3963c695cff6dbf9dad6aff2bb5ac19a6415cad26a81860d9::mee_coin::MeeCoin"]
},
"sender": "0x1869b853768f0ba935d67f837a66b172dd39a60ca2315f8d4e0e669bbd35cf25",
"sequence_number": "2",
"signature": {
"public_key": "0x62e7a6a486553b56a53e89dfae3f780693e537e5b0a7ed33290780e581ca8369",
"signature": "0x30ebd7e95cb464677f411868e2cbfcb22bc01cc63cded36c459dff45e6d2f1354ae4e090e7dfbb509851c0368b343e0e5ecaf6b08e7c1b94c186530b0f7dee0d",
"type": "ed25519_signature"
}
}
)"_json;
nlohmann::json parsedJson = nlohmann::json::parse(result.json());
assertJSONEqual(expectedJson, parsedJson);
}

} // namespace TW::Aptos::tests

0 comments on commit b2749b2

Please sign in to comment.