Skip to content

Commit

Permalink
feat: add solana token 2022 support (#3968)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* refactor: format code

---------

Co-authored-by: 0xh3rman <[email protected]>
Co-authored-by: satoshiotomakan <[email protected]>
  • Loading branch information
3 people authored Aug 16, 2024
1 parent d65c344 commit 160a6e6
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 3 deletions.
1 change: 1 addition & 0 deletions rust/chains/tw_solana/src/defined_addresses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ macro_rules! define {
define!(SYSTEM_PROGRAM_ID_ADDRESS = "11111111111111111111111111111111");
define!(STAKE_PROGRAM_ID_ADDRESS = "Stake11111111111111111111111111111111111111");
define!(TOKEN_PROGRAM_ID_ADDRESS = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
define!(TOKEN_2022_PROGRAM_ID_ADDRESS = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
define!(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
define!(SYSVAR_RENT_ID_ADDRESS = "SysvarRent111111111111111111111111111111111");
define!(SYSVAR_CLOCK_ID_ADDRESS = "SysvarC1ock11111111111111111111111111111111");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,15 @@ impl TokenInstructionBuilder {
other_main_pubkey: SolanaAddress,
token_mint_pubkey: SolanaAddress,
token_pubkey: SolanaAddress,
token_program_id: SolanaAddress,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(funding_pubkey, true),
AccountMeta::new(token_pubkey, false),
AccountMeta::readonly(other_main_pubkey, false),
AccountMeta::readonly(token_mint_pubkey, false),
AccountMeta::readonly(*SYSTEM_PROGRAM_ID_ADDRESS, false),
AccountMeta::readonly(*TOKEN_PROGRAM_ID_ADDRESS, false),
AccountMeta::readonly(token_program_id, false),
AccountMeta::readonly(*SYSVAR_RENT_ID_ADDRESS, false),
];
let data = Data::default();
Expand All @@ -96,6 +97,7 @@ impl TokenInstructionBuilder {
signer: SolanaAddress,
amount: u64,
decimals: u8,
token_program_id: SolanaAddress,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(sender_token_pubkey, false),
Expand All @@ -105,6 +107,6 @@ impl TokenInstructionBuilder {
];

let data = TokenInstruction::TransferChecked { amount, decimals }.pack();
Instruction::new(*TOKEN_PROGRAM_ID_ADDRESS, data, account_metas)
Instruction::new(token_program_id, data, account_metas)
}
}
13 changes: 12 additions & 1 deletion rust/chains/tw_solana/src/modules/message_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use crate::address::SolanaAddress;
use crate::blockhash::Blockhash;
use crate::defined_addresses::{TOKEN_2022_PROGRAM_ID_ADDRESS, TOKEN_PROGRAM_ID_ADDRESS};
use crate::instruction::Instruction;
use crate::modules::compiled_instructions::compile_instructions;
use crate::modules::compiled_keys::CompiledKeys;
Expand Down Expand Up @@ -333,6 +334,7 @@ impl<'a> MessageBuilder<'a> {
other_main_address,
token_mint_address,
token_address,
match_program_id(create_token_acc.token_program_id),
);
let mut builder = InstructionBuilder::default();
builder
Expand Down Expand Up @@ -370,14 +372,14 @@ impl<'a> MessageBuilder<'a> {
.context("Invalid token decimals. Expected lower than 256")?;

let references = Self::parse_references(&token_transfer.references)?;

let transfer_instruction = TokenInstructionBuilder::transfer_checked(
sender_token_address,
token_mint_address,
recipient_token_address,
signer,
token_transfer.amount,
decimals,
match_program_id(token_transfer.token_program_id),
)
.with_references(references);

Expand Down Expand Up @@ -432,6 +434,7 @@ impl<'a> MessageBuilder<'a> {
recipient_main_address,
token_mint_address,
recipient_token_address,
match_program_id(create_and_transfer.token_program_id),
);
let transfer_instruction = TokenInstructionBuilder::transfer_checked(
sender_token_address,
Expand All @@ -440,6 +443,7 @@ impl<'a> MessageBuilder<'a> {
signer,
create_and_transfer.amount,
decimals,
match_program_id(create_and_transfer.token_program_id),
)
.with_references(references);

Expand Down Expand Up @@ -769,3 +773,10 @@ where
{
u8::try_from(num).tw_err(|_| SigningErrorType::Error_tx_too_big)
}

fn match_program_id(program_id: Proto::TokenProgramId) -> SolanaAddress {
match program_id {
Proto::TokenProgramId::TokenProgram => *TOKEN_PROGRAM_ID_ADDRESS,
Proto::TokenProgramId::Token2022Program => *TOKEN_2022_PROGRAM_ID_ADDRESS,
}
}
55 changes: 55 additions & 0 deletions rust/tw_any_coin/tests/chains/solana/solana_sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ fn test_solana_sign_create_token_account() {
main_address: "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V".into(),
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
token_address: "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP".into(),
..Proto::CreateTokenAccount::default()
};
let input = Proto::SigningInput {
private_key: b58("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"),
Expand All @@ -413,6 +414,7 @@ fn test_solana_sign_create_token_account_5ktpn1() {
main_address: "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ".into(),
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
token_address: "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf".into(),
..Proto::CreateTokenAccount::default()
};
let input = Proto::SigningInput {
private_key: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"
Expand All @@ -438,6 +440,7 @@ fn test_solana_sign_create_token_account_for_other_3e6ufv() {
main_address: "3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft".into(),
token_mint_address: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt".into(),
token_address: "67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3".into(),
..Proto::CreateTokenAccount::default()
};
let input = Proto::SigningInput {
private_key: "4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"
Expand Down Expand Up @@ -469,6 +472,7 @@ fn test_solana_sign_create_token_account_with_priority_fee_price() {
// WBTC
token_mint_address: "3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh".into(),
token_address: "94JfTH8qCeBQDBatvULREG43msjSQscT7oHnqx7jppX".into(),
..Proto::CreateTokenAccount::default()
};

let input = Proto::SigningInput {
Expand Down Expand Up @@ -611,6 +615,7 @@ fn test_solana_sign_create_and_transfer_token_with_memo_and_references() {
"CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq".into(),
"tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS".into(),
],
..Proto::CreateAndTransferToken::default()
};
let input = Proto::SigningInput {
private_key: b58("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"),
Expand Down Expand Up @@ -918,3 +923,53 @@ fn test_solana_sign_raw_message_v0() {
// Successfully broadcasted: https://explorer.solana.com/tx/4ffBzXxLPYEEdCYpQGETkCTCCsH6iTdmKzwUZXZZgFemdhRpxQwboguFFoKCeGF3SsZPzuwwE7LbRwLgJbsyRqyP?cluster=testnet
assert_eq!(output.encoded, "6NijVxwQoDjqt6A41HXCK9kXwNDp48uLgvRyE8uz6NY5dEzaEDLzjzuMnc5TGatHZZUXehKrzUGzbg9jPSdn6pVsMc9TXNH6JGe5RJLmHwWey3MC1p8Hs2zhjw5P439P57NToatraDX9ZwvBtK4EzZzRjWbyGdicheTPjeYKCzvPCLxDkTFtPCM9VZGGXSN2Bne92NLDvf6ntNm5pxsPkZGxPe4w9Eq26gkE83hZyrYXKaiDh8TbqbHatSkw");
}

#[test]
fn test_solana_sign_create_and_transfer_token_2022() {
let create_transfer_token: Proto::CreateAndTransferToken = Proto::CreateAndTransferToken {
recipient_main_address: "EbHdsfVpWzeQV4TceYQ2xENS8meBHyztyTKVSFtgHPUw".into(),
token_mint_address: "BSQCmMAFB9itonyVSLsUxX92Ne1rgBZFqothBk3q91k6".into(),
recipient_token_address: "FzsLNpzsLMBbm1LWpM6P3W4tKrCkd8KqnMmADNvArW5d".into(),
sender_token_address: "EQxRyhzjyhRX4TJXt7FmQ3HfFdRcu49krjxHMszidQYS".into(),
amount: 1000000000,
decimals: 9,
token_program_id: Proto::TokenProgramId::Token2022Program,
..Proto::CreateAndTransferToken::default()
};
let input = Proto::SigningInput {
private_key: b58("MCyXa2gTJELxTPemyVi5ydDcQ3vVgFyddQYXj6UM3tw"),
recent_blockhash: "5oba9g5nWnvutTTb935aBMkHBYGXoak1ot4U2p34zEiJ".into(),
transaction_type: TransactionType::create_and_transfer_token_transaction(
create_transfer_token,
),
..Proto::SigningInput::default()
};
let mut signer = AnySignerHelper::<Proto::SigningOutput>::default();
let output = signer.sign(CoinType::Solana, input);
assert_eq!(output.error, SigningError::OK);
assert_eq!(output.encoded, "2xzg9AVGv8wWEn9S4m8954WSzh2MUQPCTCyFmyrSs4DJCkSaZRMAbGL8NcyDeJFT3RwUabHsX1m5CFuqzJ5Jg9knNwG6uBjYjWjNjGLBEBURa3ARqziaMAL2mZY8uZwaZETE33WZeSxNrm7zv1jJYLfqbWxquEedGND9vB9AuEspHg7TCZxfJbzY4W8QtLqyQ598z9adxWgwNXanHzqu7B4bNsp1wfKPPyx8AGQaVSx6fepaevDEZX9h2Rg1daW9TjVpktp7EHrriYVs4m44WJ18fejWLyqituXqQPdhos5oZ3e5vNXE8KcgARKXtwsXCGwwKwc9ZEVNvUp6qyUZZV8os2FHorodrT9g3Xrso5dgdsRCb42AUrKHyDdXMpRA1PmeZX6UdzgL8knt2xfzCFxzGPuMKeTtvZKFcEPJvNg73CSMPVH1mm3jz75nATdChR7xu5R4m5Gy8vhr5ndEnb8fM5P1gv6hDbfmesAEf5wye4mKTVAC4B8Mhf8WC8YNaGUG7CcxeQZXrjEfUQenboArhqbxqHFYrURK3GJLAQojXmkwSMGwv4TYL");
}

#[test]
fn test_solana_sign_transfer_token_2022() {
let transfer_token: Proto::TokenTransfer = Proto::TokenTransfer {
amount: 1000000000,
decimals: 9,
token_program_id: Proto::TokenProgramId::Token2022Program,
token_mint_address: "BSQCmMAFB9itonyVSLsUxX92Ne1rgBZFqothBk3q91k6".into(),
sender_token_address: "EQxRyhzjyhRX4TJXt7FmQ3HfFdRcu49krjxHMszidQYS".into(),
recipient_token_address: "FzsLNpzsLMBbm1LWpM6P3W4tKrCkd8KqnMmADNvArW5d".into(),
..Proto::TokenTransfer::default()
};
let input = Proto::SigningInput {
private_key: b58("MCyXa2gTJELxTPemyVi5ydDcQ3vVgFyddQYXj6UM3tw"),
recent_blockhash: "9U2eTS9b2Essvo1s5hDmwgC1atkSCCUipj2FemLvdWbj".into(),
transaction_type: TransactionType::token_transfer_transaction(transfer_token),
..Proto::SigningInput::default()
};
let mut signer = AnySignerHelper::<Proto::SigningOutput>::default();
let output = signer.sign(CoinType::Solana, input);
assert_eq!(output.error, SigningError::OK);
assert_eq!(output.encoded, "SAXNFUd7dNBu956Gi4XNuvMkKKjS9vp6puz45ErYMHFpMNwC3AQxDxGbweXt4GzY2FnUZ6ubm231NrdwWa8dg9bqgRMaHPLuPiy99YwtvcQ1E6mHxHqq8nL5VaN8wiVnrMU57zCLfHsSsVCHZc5peHHAPXMDE318uMCLLBwgDWuD1FfAvUAyXRSYniXzWG3jtBdDhuDohh13E2TMrtqTcKVv3crejFqFjtsNuW7KCqrZwxCv1ASNiiL2XScQBdHwStyjH2UTqLmT6wjGLiDYy7PZ88Tbz65r8NLr4Vb1aYSTChasfVjMLdybetfNaf4nJuBE4ZuXca7W66txKbHesxQbzrjUCXX12JFbKyaA8KJKBpbgkc9jWJjQkzyn");
// https://explorer.solana.com/tx/Lg1xWzsC9GatQMu1ZXv23t7snC92RRvbKJe22bsS76GUb8C8a9q3HPkiUnFoK6AWKSoNSsmko1EBnvKkCnL8b7w?cluster=devnet
}
14 changes: 14 additions & 0 deletions src/proto/Solana.proto
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ message WithdrawAllStake {
repeated StakeAccountValue stake_accounts = 1;
}

enum TokenProgramId {
TokenProgram = 0;
Token2022Program = 1;
}

// Create a token account under a main account for a token type
message CreateTokenAccount {
// main account -- can be same as signer, or other main account (if done on some other account's behalf)
Expand All @@ -79,6 +84,9 @@ message CreateTokenAccount {

// Token address
string token_address = 3;

// optional token program id
TokenProgramId token_program_id = 4;
}

// Transfer tokens
Expand All @@ -103,6 +111,9 @@ message TokenTransfer {

// optional referenced public keys
repeated string references = 7;

// optional token program id
TokenProgramId token_program_id = 8;
}

// CreateTokenAccount and TokenTransfer combined
Expand Down Expand Up @@ -130,6 +141,9 @@ message CreateAndTransferToken {

// optional referenced public keys
repeated string references = 8;

// optional token program id
TokenProgramId token_program_id = 9;
}

message CreateNonceAccount {
Expand Down

0 comments on commit 160a6e6

Please sign in to comment.