Skip to content

Commit 16a5721

Browse files
committed
Merge pull request #621 from Cypherock/feat/solana/tokens-transferCheckInstruction
2 parents 58e981b + 4e2d789 commit 16a5721

File tree

6 files changed

+102
-49
lines changed

6 files changed

+102
-49
lines changed

apps/solana_app/solana_sign_txn.c

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,7 @@ STATIC bool solana_handle_initiate_query(const solana_query_t *query) {
266266
return false;
267267
}
268268

269-
snprintf(
270-
msg, sizeof(msg), UI_TEXT_SIGN_TXN_PROMPT, SOLANA_NAME, wallet_name
271-
);
269+
snprintf(msg, sizeof(msg), UI_TEXT_SIGN_TXN_PROMPT, SOLANA_NAME, wallet_name);
272270

273271
// Take user consent to sign the transaction for the wallet
274272
if (!core_confirmation(msg, solana_send_error)) {
@@ -357,7 +355,7 @@ STATIC bool solana_fetch_valid_transaction(solana_query_t *query) {
357355
return true;
358356
}
359357

360-
static bool solana_transfer_sol_transaction() {
358+
static bool verify_solana_transfer_sol_transaction() {
361359
char address[45] = {0};
362360
size_t address_size = sizeof(address);
363361

@@ -387,7 +385,7 @@ static bool solana_transfer_sol_transaction() {
387385
while (i--)
388386
be_lamports[i] = solana_txn_context->transaction_info
389387
.instruction[transfer_instruction_index]
390-
.program.transfer.amount >>
388+
.program.transfer.lamports >>
391389
8 * (7 - i);
392390

393391
byte_array_to_hex_string(
@@ -535,35 +533,59 @@ static bool is_token_whitelisted(const uint8_t *address,
535533
return status;
536534
}
537535

538-
static bool solana_transfer_token_transaction() {
539-
char address[45] = {0};
540-
size_t address_size = sizeof(address);
536+
static bool verify_solana_transfer_token_transaction() {
537+
const uint8_t transfer_instruction_index =
538+
solana_txn_context->transaction_info.transfer_instruction_index;
541539

542-
const solana_token_program_t *contract;
543-
if (!is_token_whitelisted(solana_txn_context->token_data.mint_address,
544-
&contract)) {
540+
const uint8_t *token_mint = solana_txn_context->transaction_info
541+
.instruction[transfer_instruction_index]
542+
.program.transfer_checked.token_mint;
543+
const solana_token_program_t *contract = NULL;
544+
if (!is_token_whitelisted(token_mint, &contract)) {
545545
// Contract Unverifed, Display warning
546-
delay_scr_init(ui_text_unverified_contract, DELAY_TIME);
546+
delay_scr_init(ui_text_unverified_token, DELAY_TIME);
547547

548548
char mint_address[45] = {0};
549549
size_t mint_address_size = sizeof(mint_address);
550550
// verify mint address
551551
if (!b58enc(mint_address,
552552
&mint_address_size,
553-
solana_txn_context->token_data.mint_address,
553+
token_mint,
554554
SOLANA_ACCOUNT_ADDRESS_LENGTH)) {
555555
solana_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2);
556556
return false;
557557
}
558558

559-
if (!core_scroll_page(ui_text_solana_verify_mint_authority,
560-
mint_address,
561-
solana_send_error)) {
559+
if (!core_scroll_page(
560+
ui_text_verify_token_address, mint_address, solana_send_error)) {
561+
return false;
562+
}
563+
564+
const uint8_t token_decimals = solana_txn_context->transaction_info
565+
.instruction[transfer_instruction_index]
566+
.program.transfer_checked.decimals;
567+
568+
solana_token_program_t empty_contract = {
569+
.symbol = "",
570+
.decimal = token_decimals,
571+
};
572+
573+
contract = &empty_contract;
574+
} else {
575+
char msg[100] = "";
576+
snprintf(msg,
577+
sizeof(msg),
578+
UI_TEXT_SEND_TOKEN_PROMPT,
579+
contract->symbol,
580+
SOLANA_NAME);
581+
if (!core_confirmation(msg, solana_send_error)) {
562582
return false;
563583
}
564584
}
565585

566586
// verify recipient address;
587+
char address[45] = {0};
588+
size_t address_size = sizeof(address);
567589
if (!b58enc(address,
568590
&address_size,
569591
solana_txn_context->token_data.recipient_address,
@@ -572,32 +594,30 @@ static bool solana_transfer_token_transaction() {
572594
return false;
573595
}
574596

575-
if (!core_scroll_page(ui_text_verify_address, address, solana_send_error)) {
576-
return false;
577-
}
578-
579-
// Upon recipient address confirmwation, calculate associated token address
580-
// and compare with utxn's value
597+
// Calculate associated token address and compare with utxn's value
581598
uint8_t associated_token_address[SOLANA_ACCOUNT_ADDRESS_LENGTH] = {0};
582599
if (!get_associated_token_address(
583-
solana_txn_context->token_data.mint_address,
600+
token_mint,
584601
solana_txn_context->token_data.recipient_address,
585602
associated_token_address)) {
586603
solana_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2);
587604
return false;
588605
}
589606

590-
const uint8_t transfer_instruction_index =
591-
solana_txn_context->transaction_info.transfer_instruction_index;
592607
if (memcmp(associated_token_address,
593608
solana_txn_context->transaction_info
594609
.instruction[transfer_instruction_index]
595-
.program.transfer.recipient_account,
610+
.program.transfer_checked.destination,
596611
SOLANA_ACCOUNT_ADDRESS_LENGTH) != 0) {
597612
solana_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, 2);
598613
return false;
599614
}
600615

616+
// Now take user verification
617+
if (!core_scroll_page(ui_text_verify_address, address, solana_send_error)) {
618+
return false;
619+
}
620+
601621
// verify recipient amount
602622
char amount_string[40] = {'\0'}, amount_decimal_string[30] = {'\0'};
603623
char display[100] = "";
@@ -607,7 +627,7 @@ static bool solana_transfer_token_transaction() {
607627
while (i--)
608628
be_units[i] = solana_txn_context->transaction_info
609629
.instruction[transfer_instruction_index]
610-
.program.transfer.amount >>
630+
.program.transfer_checked.amount >>
611631
8 * (7 - i);
612632

613633
byte_array_to_hex_string(be_units, 8, amount_string, sizeof(amount_string));
@@ -635,9 +655,9 @@ static bool solana_transfer_token_transaction() {
635655

636656
STATIC bool solana_get_user_verification() {
637657
if (solana_txn_context->is_token_transfer_transaction == true) {
638-
return solana_transfer_token_transaction();
658+
return verify_solana_transfer_token_transaction();
639659
} else
640-
return solana_transfer_sol_transaction();
660+
return verify_solana_transfer_sol_transaction();
641661
}
642662

643663
STATIC bool fetch_seed(solana_query_t *query, uint8_t *seed_out) {

apps/solana_app/solana_txn_helpers.c

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,9 @@ int solana_byte_array_to_unsigned_txn(uint8_t *byte_array,
178178
&error);
179179
if (error != SOL_OK)
180180
return error;
181-
// if (utxn->instruction[i].opaque_data_length == 0)
182-
// return SOL_D_MIN_LENGTH;
181+
if (i == utxn->transfer_instruction_index &&
182+
utxn->instruction[i].opaque_data_length == 0)
183+
return SOL_D_MIN_LENGTH;
183184

184185
utxn->instruction[i].opaque_data = byte_array + offset;
185186
offset += utxn->instruction[i].opaque_data_length;
@@ -221,9 +222,9 @@ int solana_byte_array_to_unsigned_txn(uint8_t *byte_array,
221222
.account_addresses_index +
222223
1) *
223224
SOLANA_ACCOUNT_ADDRESS_LENGTH);
224-
utxn->instruction[transfer_instruction_index].program.transfer.amount =
225-
U64_READ_LE_ARRAY(
226-
utxn->instruction[transfer_instruction_index].opaque_data + 4);
225+
utxn->instruction[transfer_instruction_index]
226+
.program.transfer.lamports = U64_READ_LE_ARRAY(
227+
utxn->instruction[transfer_instruction_index].opaque_data + 4);
227228
break;
228229

229230
default:
@@ -239,24 +240,43 @@ int solana_byte_array_to_unsigned_txn(uint8_t *byte_array,
239240
*(utxn->instruction[transfer_instruction_index].opaque_data);
240241

241242
switch (instruction_enum) {
242-
case STPI_TRANSFER: // transfer instruction
243+
case STPI_TRANSFER_CHECKED: // transfer checked instruction
243244
utxn->instruction[transfer_instruction_index]
244-
.program.transfer.funding_account =
245+
.program.transfer_checked.source =
245246
utxn->account_addresses +
246247
(*(utxn->instruction[transfer_instruction_index]
247248
.account_addresses_index +
248249
0) *
249250
SOLANA_ACCOUNT_ADDRESS_LENGTH);
250251
utxn->instruction[transfer_instruction_index]
251-
.program.transfer.recipient_account =
252+
.program.transfer_checked.token_mint =
252253
utxn->account_addresses +
253254
(*(utxn->instruction[transfer_instruction_index]
254255
.account_addresses_index +
255256
1) *
256257
SOLANA_ACCOUNT_ADDRESS_LENGTH);
257-
utxn->instruction[transfer_instruction_index].program.transfer.amount =
258-
U64_READ_LE_ARRAY(
259-
utxn->instruction[transfer_instruction_index].opaque_data + 1);
258+
utxn->instruction[transfer_instruction_index]
259+
.program.transfer_checked.destination =
260+
utxn->account_addresses +
261+
(*(utxn->instruction[transfer_instruction_index]
262+
.account_addresses_index +
263+
2) *
264+
SOLANA_ACCOUNT_ADDRESS_LENGTH);
265+
utxn->instruction[transfer_instruction_index]
266+
.program.transfer_checked.owner =
267+
utxn->account_addresses +
268+
(*(utxn->instruction[transfer_instruction_index]
269+
.account_addresses_index +
270+
3) *
271+
SOLANA_ACCOUNT_ADDRESS_LENGTH);
272+
utxn->instruction[transfer_instruction_index]
273+
.program.transfer_checked.amount = U64_READ_LE_ARRAY(
274+
utxn->instruction[transfer_instruction_index].opaque_data + 1);
275+
utxn->instruction[transfer_instruction_index]
276+
.program.transfer_checked.decimals =
277+
*(utxn->instruction[transfer_instruction_index].opaque_data +
278+
sizeof(uint64_t) +
279+
1); // decimal value comes after amount(which is a u64)
260280
break;
261281

262282
default:
@@ -316,7 +336,7 @@ int solana_validate_unsigned_txn(const solana_unsigned_txn *utxn) {
316336
*(utxn->instruction[transfer_instruction_index].opaque_data);
317337

318338
switch (instruction_enum) {
319-
case STPI_TRANSFER: // transfer instruction
339+
case STPI_TRANSFER_CHECKED: // transfer checked instruction
320340
break;
321341
default:
322342
return SOL_V_UNSUPPORTED_INSTRUCTION;

apps/solana_app/solana_txn_helpers.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,20 @@ enum SOLANA_ERROR_CODES {
106106
typedef struct solana_transfer_data {
107107
uint8_t *funding_account;
108108
uint8_t *recipient_account;
109-
uint64_t amount;
109+
uint64_t lamports;
110110
} solana_transfer_data;
111111

112+
// Reference :
113+
// https://docs.rs/spl-token/latest/spl_token/instruction/enum.TokenInstruction.html#variant.TransferChecked
114+
typedef struct solana_token_transfer_checked_data {
115+
uint8_t *source;
116+
uint8_t *token_mint;
117+
uint8_t *destination;
118+
uint8_t *owner; // signer/owner of the source account
119+
uint64_t amount;
120+
uint8_t decimals;
121+
} solana_token_transfer_checked_data;
122+
112123
// Reference :
113124
// https://docs.solana.com/developing/programming-model/transactions#instruction-format
114125
typedef struct solana_instruction {
@@ -119,6 +130,7 @@ typedef struct solana_instruction {
119130
uint8_t *opaque_data;
120131
union {
121132
solana_transfer_data transfer;
133+
solana_token_transfer_checked_data transfer_checked;
122134
} program;
123135
} solana_instruction;
124136

@@ -134,8 +146,8 @@ typedef struct solana_unsigned_txn {
134146

135147
uint8_t *blockhash;
136148

137-
uint16_t
138-
instructions_count; // deserialization only supports max 2 instructions: create account and transfer
149+
uint16_t instructions_count; // deserialization only supports max 2
150+
// instructions: create account and transfer
139151
solana_instruction instruction[2]; ///< Expects max 2 instructions
140152
uint8_t transfer_instruction_index;
141153

common/proto-options/solana/sign_txn.options

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
solana.SignTxnInitiateRequest.walletId type:FT_STATIC max_size:32 fixed_length:true
33
solana.SignTxnInitiateRequest.derivationPath type:FT_STATIC max_count:4 fixed_length:true
44
solana.SignTxnInitiateTokenData.recipient_address type:FT_STATIC max_size:32 fixed_length:true
5-
solana.SignTxnInitiateTokenData.mint_address type:FT_STATIC max_size:32 fixed_length:true
65
solana.SignTxnSignatureResponse.signature type:FT_STATIC max_size:64 fixed_length:true
76
solana.SignTxnSignatureRequest.blockhash type:FT_STATIC max_size:32 fixed_length:true

src/constant_texts.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,9 @@ const char *ui_text_verify_amount = "Verify amount";
366366
const char *ui_text_verify_contract = "Verify contract";
367367
const char *ui_text_unverified_contract =
368368
LV_SYMBOL_WARNING " Warning!\nUnverified contract";
369+
const char *ui_text_verify_token_address = "Verify token address";
370+
const char *ui_text_unverified_token =
371+
LV_SYMBOL_WARNING " Warning!\nUnverified token";
369372
const char *ui_text_confirm_wallet_name = "Confirm wallet name";
370373
const char *ui_text_enter_data = "Enter data";
371374
const char *ui_text_confirm_data = "Confirm data";

src/constant_texts.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
#define UI_TEXT_SEND_PROMPT "Send %s on %s"
3232
#define UI_TEXT_SIGN_TXN_PROMPT "Sign transaction on %s from %s"
3333
#define UI_TEXT_REVIEW_TXN_PROMPT "Review transaction to %s"
34-
#define UI_TEXT_SEND_TOKEN_PROMPT "Send %s on %s from %s"
34+
#define UI_TEXT_SEND_TOKEN_PROMPT "Send \n%s on \n%s"
3535
#define UI_TEXT_BTC_RECEIVER "Receiver #%d"
3636
#define UI_TEXT_BTC_FEE "Transaction fee"
3737
#define UI_TEXT_SIGN_PROMPT "Sign %s message on %s from %s"
@@ -258,6 +258,8 @@ extern const char *ui_text_verify_address;
258258
extern const char *ui_text_verify_amount;
259259
extern const char *ui_text_verify_contract;
260260
extern const char *ui_text_unverified_contract;
261+
extern const char *ui_text_verify_token_address;
262+
extern const char *ui_text_unverified_token;
261263
extern const char *ui_text_confirm_wallet_name;
262264
extern const char *ui_text_enter_data;
263265
extern const char *ui_text_confirm_data;
@@ -390,9 +392,6 @@ extern const char *ui_text_inheritance_decryption_flow_confirmation_generic;
390392
extern const char *ui_text_inheritance_decryption_flow_success;
391393
extern const char *ui_text_inheritance_decryption_flow_failure;
392394

393-
// Solana
394-
extern const char *ui_text_solana_verify_mint_authority;
395-
396395
#ifdef ALLOW_LOG_EXPORT
397396
extern const char *ui_text_send_logs_prompt;
398397
#endif

0 commit comments

Comments
 (0)