Skip to content

Commit 160a6e6

Browse files
weixuefeng0xh3rmansatoshiotomakan
authored
feat: add solana token 2022 support (trustwallet#3968)
* feat: add solana token 2022 support * feat: add test for token 2022 * fix: code format and unsort solana tx * feat: add solana sign test * fix: solana create and transfer token 2022 test * refactor: format code * fix: revert code for signer * fix: change bool is_token_2022 to enum TokenProgramId * refactor: format code * refactor: format code * refactor: format code Co-authored-by: 0xh3rman <119309671+0xh3rman@users.noreply.github.com> * refactor: format code --------- Co-authored-by: 0xh3rman <119309671+0xh3rman@users.noreply.github.com> Co-authored-by: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com>
1 parent d65c344 commit 160a6e6

5 files changed

Lines changed: 86 additions & 3 deletions

File tree

rust/chains/tw_solana/src/defined_addresses.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ macro_rules! define {
1616
define!(SYSTEM_PROGRAM_ID_ADDRESS = "11111111111111111111111111111111");
1717
define!(STAKE_PROGRAM_ID_ADDRESS = "Stake11111111111111111111111111111111111111");
1818
define!(TOKEN_PROGRAM_ID_ADDRESS = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
19+
define!(TOKEN_2022_PROGRAM_ID_ADDRESS = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
1920
define!(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
2021
define!(SYSVAR_RENT_ID_ADDRESS = "SysvarRent111111111111111111111111111111111");
2122
define!(SYSVAR_CLOCK_ID_ADDRESS = "SysvarC1ock11111111111111111111111111111111");

rust/chains/tw_solana/src/modules/instruction_builder/token_instruction.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,15 @@ impl TokenInstructionBuilder {
7474
other_main_pubkey: SolanaAddress,
7575
token_mint_pubkey: SolanaAddress,
7676
token_pubkey: SolanaAddress,
77+
token_program_id: SolanaAddress,
7778
) -> Instruction {
7879
let account_metas = vec![
7980
AccountMeta::new(funding_pubkey, true),
8081
AccountMeta::new(token_pubkey, false),
8182
AccountMeta::readonly(other_main_pubkey, false),
8283
AccountMeta::readonly(token_mint_pubkey, false),
8384
AccountMeta::readonly(*SYSTEM_PROGRAM_ID_ADDRESS, false),
84-
AccountMeta::readonly(*TOKEN_PROGRAM_ID_ADDRESS, false),
85+
AccountMeta::readonly(token_program_id, false),
8586
AccountMeta::readonly(*SYSVAR_RENT_ID_ADDRESS, false),
8687
];
8788
let data = Data::default();
@@ -96,6 +97,7 @@ impl TokenInstructionBuilder {
9697
signer: SolanaAddress,
9798
amount: u64,
9899
decimals: u8,
100+
token_program_id: SolanaAddress,
99101
) -> Instruction {
100102
let account_metas = vec![
101103
AccountMeta::new(sender_token_pubkey, false),
@@ -105,6 +107,6 @@ impl TokenInstructionBuilder {
105107
];
106108

107109
let data = TokenInstruction::TransferChecked { amount, decimals }.pack();
108-
Instruction::new(*TOKEN_PROGRAM_ID_ADDRESS, data, account_metas)
110+
Instruction::new(token_program_id, data, account_metas)
109111
}
110112
}

rust/chains/tw_solana/src/modules/message_builder.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use crate::address::SolanaAddress;
66
use crate::blockhash::Blockhash;
7+
use crate::defined_addresses::{TOKEN_2022_PROGRAM_ID_ADDRESS, TOKEN_PROGRAM_ID_ADDRESS};
78
use crate::instruction::Instruction;
89
use crate::modules::compiled_instructions::compile_instructions;
910
use crate::modules::compiled_keys::CompiledKeys;
@@ -333,6 +334,7 @@ impl<'a> MessageBuilder<'a> {
333334
other_main_address,
334335
token_mint_address,
335336
token_address,
337+
match_program_id(create_token_acc.token_program_id),
336338
);
337339
let mut builder = InstructionBuilder::default();
338340
builder
@@ -370,14 +372,14 @@ impl<'a> MessageBuilder<'a> {
370372
.context("Invalid token decimals. Expected lower than 256")?;
371373

372374
let references = Self::parse_references(&token_transfer.references)?;
373-
374375
let transfer_instruction = TokenInstructionBuilder::transfer_checked(
375376
sender_token_address,
376377
token_mint_address,
377378
recipient_token_address,
378379
signer,
379380
token_transfer.amount,
380381
decimals,
382+
match_program_id(token_transfer.token_program_id),
381383
)
382384
.with_references(references);
383385

@@ -432,6 +434,7 @@ impl<'a> MessageBuilder<'a> {
432434
recipient_main_address,
433435
token_mint_address,
434436
recipient_token_address,
437+
match_program_id(create_and_transfer.token_program_id),
435438
);
436439
let transfer_instruction = TokenInstructionBuilder::transfer_checked(
437440
sender_token_address,
@@ -440,6 +443,7 @@ impl<'a> MessageBuilder<'a> {
440443
signer,
441444
create_and_transfer.amount,
442445
decimals,
446+
match_program_id(create_and_transfer.token_program_id),
443447
)
444448
.with_references(references);
445449

@@ -769,3 +773,10 @@ where
769773
{
770774
u8::try_from(num).tw_err(|_| SigningErrorType::Error_tx_too_big)
771775
}
776+
777+
fn match_program_id(program_id: Proto::TokenProgramId) -> SolanaAddress {
778+
match program_id {
779+
Proto::TokenProgramId::TokenProgram => *TOKEN_PROGRAM_ID_ADDRESS,
780+
Proto::TokenProgramId::Token2022Program => *TOKEN_2022_PROGRAM_ID_ADDRESS,
781+
}
782+
}

rust/tw_any_coin/tests/chains/solana/solana_sign.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,7 @@ fn test_solana_sign_create_token_account() {
392392
main_address: "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V".into(),
393393
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
394394
token_address: "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP".into(),
395+
..Proto::CreateTokenAccount::default()
395396
};
396397
let input = Proto::SigningInput {
397398
private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"),
@@ -413,6 +414,7 @@ fn test_solana_sign_create_token_account_5ktpn1() {
413414
main_address: "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ".into(),
414415
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
415416
token_address: "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf".into(),
417+
..Proto::CreateTokenAccount::default()
416418
};
417419
let input = Proto::SigningInput {
418420
private_key: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"
@@ -438,6 +440,7 @@ fn test_solana_sign_create_token_account_for_other_3e6ufv() {
438440
main_address: "3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft".into(),
439441
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
440442
token_address: "67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3".into(),
443+
..Proto::CreateTokenAccount::default()
441444
};
442445
let input = Proto::SigningInput {
443446
private_key: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"
@@ -469,6 +472,7 @@ fn test_solana_sign_create_token_account_with_priority_fee_price() {
469472
// WBTC
470473
token_mint_address: "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh".into(),
471474
token_address: "94JfTH8qCeBQDBatvULREG43msjSQscT7oHnqx7jppX".into(),
475+
..Proto::CreateTokenAccount::default()
472476
};
473477

474478
let input = Proto::SigningInput {
@@ -611,6 +615,7 @@ fn test_solana_sign_create_and_transfer_token_with_memo_and_references() {
611615
"CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq".into(),
612616
"tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS".into(),
613617
],
618+
..Proto::CreateAndTransferToken::default()
614619
};
615620
let input = Proto::SigningInput {
616621
private_key: b58("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"),
@@ -918,3 +923,53 @@ fn test_solana_sign_raw_message_v0() {
918923
// Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet
919924
assert_eq!(output.encoded, "6NijVxwQoDjqt6A41HXCK9kXwNDp48uLgvRyE8uz6NY5dEzaEDLzjzuMnc5TGatHZZUXehKrzUGzbg9jPSdn6pVsMc9TXNH6JGe5RJLmHwWey3MC1p8Hs2zhjw5P439P57NToatraDX9ZwvBtK4EzZzRjWbyGdicheTPjeYKCzvPCLxDkTFtPCM9VZGGXSN2Bne92NLDvf6ntNm5pxsPkZGxPe4w9Eq26gkE83hZyrYXKaiDh8TbqbHatSkw");
920925
}
926+
927+
#[test]
928+
fn test_solana_sign_create_and_transfer_token_2022() {
929+
let create_transfer_token: Proto::CreateAndTransferToken = Proto::CreateAndTransferToken {
930+
recipient_main_address: "EbHdsfVpWzeQV4TceYQ2xENS8meBHyztyTKVSFtgHPUw".into(),
931+
token_mint_address: "BSQCmMAFB9itonyVSLsUxX92Ne1rgBZFqothBk3q91k6".into(),
932+
recipient_token_address: "FzsLNpzsLMBbm1LWpM6P3W4tKrCkd8KqnMmADNvArW5d".into(),
933+
sender_token_address: "EQxRyhzjyhRX4TJXt7FmQ3HfFdRcu49krjxHMszidQYS".into(),
934+
amount: 1000000000,
935+
decimals: 9,
936+
token_program_id: Proto::TokenProgramId::Token2022Program,
937+
..Proto::CreateAndTransferToken::default()
938+
};
939+
let input = Proto::SigningInput {
940+
private_key: b58("MCyXa2gTJELxTPemyVi5ydDcQ3vVgFyddQYXj6UM3tw"),
941+
recent_blockhash: "5oba9g5nWnvutTTb935aBMkHBYGXoak1ot4U2p34zEiJ".into(),
942+
transaction_type: TransactionType::create_and_transfer_token_transaction(
943+
create_transfer_token,
944+
),
945+
..Proto::SigningInput::default()
946+
};
947+
let mut signer = AnySignerHelper::<Proto::SigningOutput>::default();
948+
let output = signer.sign(CoinType::Solana, input);
949+
assert_eq!(output.error, SigningError::OK);
950+
assert_eq!(output.encoded, "2xzg9AVGv8wWEn9S4m8954WSzh2MUQPCTCyFmyrSs4DJCkSaZRMAbGL8NcyDeJFT3RwUabHsX1m5CFuqzJ5Jg9knNwG6uBjYjWjNjGLBEBURa3ARqziaMAL2mZY8uZwaZETE33WZeSxNrm7zv1jJYLfqbWxquEedGND9vB9AuEspHg7TCZxfJbzY4W8QtLqyQ598z9adxWgwNXanHzqu7B4bNsp1wfKPPyx8AGQaVSx6fepaevDEZX9h2Rg1daW9TjVpktp7EHrriYVs4m44WJ18fejWLyqituXqQPdhos5oZ3e5vNXE8KcgARKXtwsXCGwwKwc9ZEVNvUp6qyUZZV8os2FHorodrT9g3Xrso5dgdsRCb42AUrKHyDdXMpRA1PmeZX6UdzgL8knt2xfzCFxzGPuMKeTtvZKFcEPJvNg73CSMPVH1mm3jz75nATdChR7xu5R4m5Gy8vhr5ndEnb8fM5P1gv6hDbfmesAEf5wye4mKTVAC4B8Mhf8WC8YNaGUG7CcxeQZXrjEfUQenboArhqbxqHFYrURK3GJLAQojXmkwSMGwv4TYL");
951+
}
952+
953+
#[test]
954+
fn test_solana_sign_transfer_token_2022() {
955+
let transfer_token: Proto::TokenTransfer = Proto::TokenTransfer {
956+
amount: 1000000000,
957+
decimals: 9,
958+
token_program_id: Proto::TokenProgramId::Token2022Program,
959+
token_mint_address: "BSQCmMAFB9itonyVSLsUxX92Ne1rgBZFqothBk3q91k6".into(),
960+
sender_token_address: "EQxRyhzjyhRX4TJXt7FmQ3HfFdRcu49krjxHMszidQYS".into(),
961+
recipient_token_address: "FzsLNpzsLMBbm1LWpM6P3W4tKrCkd8KqnMmADNvArW5d".into(),
962+
..Proto::TokenTransfer::default()
963+
};
964+
let input = Proto::SigningInput {
965+
private_key: b58("MCyXa2gTJELxTPemyVi5ydDcQ3vVgFyddQYXj6UM3tw"),
966+
recent_blockhash: "9U2eTS9b2Essvo1s5hDmwgC1atkSCCUipj2FemLvdWbj".into(),
967+
transaction_type: TransactionType::token_transfer_transaction(transfer_token),
968+
..Proto::SigningInput::default()
969+
};
970+
let mut signer = AnySignerHelper::<Proto::SigningOutput>::default();
971+
let output = signer.sign(CoinType::Solana, input);
972+
assert_eq!(output.error, SigningError::OK);
973+
assert_eq!(output.encoded, "SAXNFUd7dNBu956Gi4XNuvMkKKjS9vp6puz45ErYMHFpMNwC3AQxDxGbweXt4GzY2FnUZ6ubm231NrdwWa8dg9bqgRMaHPLuPiy99YwtvcQ1E6mHxHqq8nL5VaN8wiVnrMU57zCLfHsSsVCHZc5peHHAPXMDE318uMCLLBwgDWuD1FfAvUAyXRSYniXzWG3jtBdDhuDohh13E2TMrtqTcKVv3crejFqFjtsNuW7KCqrZwxCv1ASNiiL2XScQBdHwStyjH2UTqLmT6wjGLiDYy7PZ88Tbz65r8NLr4Vb1aYSTChasfVjMLdybetfNaf4nJuBE4ZuXca7W66txKbHesxQbzrjUCXX12JFbKyaA8KJKBpbgkc9jWJjQkzyn");
974+
// https://explorer.solana.com/tx/Lg1xWzsC9GatQMu1ZXv23t7snC92RRvbKJe22bsS76GUb8C8a9q3HPkiUnFoK6AWKSoNSsmko1EBnvKkCnL8b7w?cluster=devnet
975+
}

src/proto/Solana.proto

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ message WithdrawAllStake {
6969
repeated StakeAccountValue stake_accounts = 1;
7070
}
7171

72+
enum TokenProgramId {
73+
TokenProgram = 0;
74+
Token2022Program = 1;
75+
}
76+
7277
// Create a token account under a main account for a token type
7378
message CreateTokenAccount {
7479
// main account -- can be same as signer, or other main account (if done on some other account's behalf)
@@ -79,6 +84,9 @@ message CreateTokenAccount {
7984

8085
// Token address
8186
string token_address = 3;
87+
88+
// optional token program id
89+
TokenProgramId token_program_id = 4;
8290
}
8391

8492
// Transfer tokens
@@ -103,6 +111,9 @@ message TokenTransfer {
103111

104112
// optional referenced public keys
105113
repeated string references = 7;
114+
115+
// optional token program id
116+
TokenProgramId token_program_id = 8;
106117
}
107118

108119
// CreateTokenAccount and TokenTransfer combined
@@ -130,6 +141,9 @@ message CreateAndTransferToken {
130141

131142
// optional referenced public keys
132143
repeated string references = 8;
144+
145+
// optional token program id
146+
TokenProgramId token_program_id = 9;
133147
}
134148

135149
message CreateNonceAccount {

0 commit comments

Comments
 (0)