Skip to content

Commit

Permalink
Merge pull request #621 from Cypherock/feat/solana/tokens-transferChe…
Browse files Browse the repository at this point in the history
…ckInstruction
  • Loading branch information
TejasvOnly authored Jan 20, 2025
2 parents b316c13 + f016ff8 commit 63f5773
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 50 deletions.
78 changes: 49 additions & 29 deletions apps/solana_app/solana_sign_txn.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,7 @@ STATIC bool solana_handle_initiate_query(const solana_query_t *query) {
return false;
}

snprintf(
msg, sizeof(msg), UI_TEXT_SIGN_TXN_PROMPT, SOLANA_NAME, wallet_name
);
snprintf(msg, sizeof(msg), UI_TEXT_SIGN_TXN_PROMPT, SOLANA_NAME, wallet_name);

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

static bool solana_transfer_sol_transaction() {
static bool verify_solana_transfer_sol_transaction() {
char address[45] = {0};
size_t address_size = sizeof(address);

Expand Down Expand Up @@ -387,7 +385,7 @@ static bool solana_transfer_sol_transaction() {
while (i--)
be_lamports[i] = solana_txn_context->transaction_info
.instruction[transfer_instruction_index]
.program.transfer.amount >>
.program.transfer.lamports >>
8 * (7 - i);

byte_array_to_hex_string(
Expand Down Expand Up @@ -535,35 +533,59 @@ static bool is_token_whitelisted(const uint8_t *address,
return status;
}

static bool solana_transfer_token_transaction() {
char address[45] = {0};
size_t address_size = sizeof(address);
static bool verify_solana_transfer_token_transaction() {
const uint8_t transfer_instruction_index =
solana_txn_context->transaction_info.transfer_instruction_index;

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

char mint_address[45] = {0};
size_t mint_address_size = sizeof(mint_address);
// verify mint address
if (!b58enc(mint_address,
&mint_address_size,
solana_txn_context->token_data.mint_address,
token_mint,
SOLANA_ACCOUNT_ADDRESS_LENGTH)) {
solana_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2);
return false;
}

if (!core_scroll_page(ui_text_solana_verify_mint_authority,
mint_address,
solana_send_error)) {
if (!core_scroll_page(
ui_text_verify_token_address, mint_address, solana_send_error)) {
return false;
}

const uint8_t token_decimals = solana_txn_context->transaction_info
.instruction[transfer_instruction_index]
.program.transfer_checked.decimals;

solana_token_program_t empty_contract = {
.symbol = "",
.decimal = token_decimals,
};

contract = &empty_contract;
} else {
char msg[100] = "";
snprintf(msg,
sizeof(msg),
UI_TEXT_SEND_TOKEN_PROMPT,
contract->symbol,
SOLANA_NAME);
if (!core_confirmation(msg, solana_send_error)) {
return false;
}
}

// verify recipient address;
char address[45] = {0};
size_t address_size = sizeof(address);
if (!b58enc(address,
&address_size,
solana_txn_context->token_data.recipient_address,
Expand All @@ -572,32 +594,30 @@ static bool solana_transfer_token_transaction() {
return false;
}

if (!core_scroll_page(ui_text_verify_address, address, solana_send_error)) {
return false;
}

// Upon recipient address confirmwation, calculate associated token address
// and compare with utxn's value
// Calculate associated token address and compare with utxn's value
uint8_t associated_token_address[SOLANA_ACCOUNT_ADDRESS_LENGTH] = {0};
if (!get_associated_token_address(
solana_txn_context->token_data.mint_address,
token_mint,
solana_txn_context->token_data.recipient_address,
associated_token_address)) {
solana_send_error(ERROR_COMMON_ERROR_UNKNOWN_ERROR_TAG, 2);
return false;
}

const uint8_t transfer_instruction_index =
solana_txn_context->transaction_info.transfer_instruction_index;
if (memcmp(associated_token_address,
solana_txn_context->transaction_info
.instruction[transfer_instruction_index]
.program.transfer.recipient_account,
.program.transfer_checked.destination,
SOLANA_ACCOUNT_ADDRESS_LENGTH) != 0) {
solana_send_error(ERROR_COMMON_ERROR_CORRUPT_DATA_TAG, 2);
return false;
}

// Now take user verification
if (!core_scroll_page(ui_text_verify_address, address, solana_send_error)) {
return false;
}

// verify recipient amount
char amount_string[40] = {'\0'}, amount_decimal_string[30] = {'\0'};
char display[100] = "";
Expand All @@ -607,7 +627,7 @@ static bool solana_transfer_token_transaction() {
while (i--)
be_units[i] = solana_txn_context->transaction_info
.instruction[transfer_instruction_index]
.program.transfer.amount >>
.program.transfer_checked.amount >>
8 * (7 - i);

byte_array_to_hex_string(be_units, 8, amount_string, sizeof(amount_string));
Expand Down Expand Up @@ -635,9 +655,9 @@ static bool solana_transfer_token_transaction() {

STATIC bool solana_get_user_verification() {
if (solana_txn_context->is_token_transfer_transaction == true) {
return solana_transfer_token_transaction();
return verify_solana_transfer_token_transaction();
} else
return solana_transfer_sol_transaction();
return verify_solana_transfer_sol_transaction();
}

STATIC bool fetch_seed(solana_query_t *query, uint8_t *seed_out) {
Expand Down
44 changes: 32 additions & 12 deletions apps/solana_app/solana_txn_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,9 @@ int solana_byte_array_to_unsigned_txn(uint8_t *byte_array,
&error);
if (error != SOL_OK)
return error;
// if (utxn->instruction[i].opaque_data_length == 0)
// return SOL_D_MIN_LENGTH;
if (i == utxn->transfer_instruction_index &&
utxn->instruction[i].opaque_data_length == 0)
return SOL_D_MIN_LENGTH;

utxn->instruction[i].opaque_data = byte_array + offset;
offset += utxn->instruction[i].opaque_data_length;
Expand Down Expand Up @@ -221,9 +222,9 @@ int solana_byte_array_to_unsigned_txn(uint8_t *byte_array,
.account_addresses_index +
1) *
SOLANA_ACCOUNT_ADDRESS_LENGTH);
utxn->instruction[transfer_instruction_index].program.transfer.amount =
U64_READ_LE_ARRAY(
utxn->instruction[transfer_instruction_index].opaque_data + 4);
utxn->instruction[transfer_instruction_index]
.program.transfer.lamports = U64_READ_LE_ARRAY(
utxn->instruction[transfer_instruction_index].opaque_data + 4);
break;

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

switch (instruction_enum) {
case STPI_TRANSFER: // transfer instruction
case STPI_TRANSFER_CHECKED: // transfer checked instruction
utxn->instruction[transfer_instruction_index]
.program.transfer.funding_account =
.program.transfer_checked.source =
utxn->account_addresses +
(*(utxn->instruction[transfer_instruction_index]
.account_addresses_index +
0) *
SOLANA_ACCOUNT_ADDRESS_LENGTH);
utxn->instruction[transfer_instruction_index]
.program.transfer.recipient_account =
.program.transfer_checked.token_mint =
utxn->account_addresses +
(*(utxn->instruction[transfer_instruction_index]
.account_addresses_index +
1) *
SOLANA_ACCOUNT_ADDRESS_LENGTH);
utxn->instruction[transfer_instruction_index].program.transfer.amount =
U64_READ_LE_ARRAY(
utxn->instruction[transfer_instruction_index].opaque_data + 1);
utxn->instruction[transfer_instruction_index]
.program.transfer_checked.destination =
utxn->account_addresses +
(*(utxn->instruction[transfer_instruction_index]
.account_addresses_index +
2) *
SOLANA_ACCOUNT_ADDRESS_LENGTH);
utxn->instruction[transfer_instruction_index]
.program.transfer_checked.owner =
utxn->account_addresses +
(*(utxn->instruction[transfer_instruction_index]
.account_addresses_index +
3) *
SOLANA_ACCOUNT_ADDRESS_LENGTH);
utxn->instruction[transfer_instruction_index]
.program.transfer_checked.amount = U64_READ_LE_ARRAY(
utxn->instruction[transfer_instruction_index].opaque_data + 1);
utxn->instruction[transfer_instruction_index]
.program.transfer_checked.decimals =
*(utxn->instruction[transfer_instruction_index].opaque_data +
sizeof(uint64_t) +
1); // decimal value comes after amount(which is a u64)
break;

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

switch (instruction_enum) {
case STPI_TRANSFER: // transfer instruction
case STPI_TRANSFER_CHECKED: // transfer checked instruction
break;
default:
return SOL_V_UNSUPPORTED_INSTRUCTION;
Expand Down
18 changes: 15 additions & 3 deletions apps/solana_app/solana_txn_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,20 @@ enum SOLANA_ERROR_CODES {
typedef struct solana_transfer_data {
uint8_t *funding_account;
uint8_t *recipient_account;
uint64_t amount;
uint64_t lamports;
} solana_transfer_data;

// Reference :
// https://docs.rs/spl-token/latest/spl_token/instruction/enum.TokenInstruction.html#variant.TransferChecked
typedef struct solana_token_transfer_checked_data {
uint8_t *source;
uint8_t *token_mint;
uint8_t *destination;
uint8_t *owner; // signer/owner of the source account
uint64_t amount;
uint8_t decimals;
} solana_token_transfer_checked_data;

// Reference :
// https://docs.solana.com/developing/programming-model/transactions#instruction-format
typedef struct solana_instruction {
Expand All @@ -119,6 +130,7 @@ typedef struct solana_instruction {
uint8_t *opaque_data;
union {
solana_transfer_data transfer;
solana_token_transfer_checked_data transfer_checked;
} program;
} solana_instruction;

Expand All @@ -134,8 +146,8 @@ typedef struct solana_unsigned_txn {

uint8_t *blockhash;

uint16_t
instructions_count; // deserialization only supports max 2 instructions: create account and transfer
uint16_t instructions_count; // deserialization only supports max 2
// instructions: create account and transfer
solana_instruction instruction[2]; ///< Expects max 2 instructions
uint8_t transfer_instruction_index;

Expand Down
2 changes: 1 addition & 1 deletion common/cypherock-common
1 change: 0 additions & 1 deletion common/proto-options/solana/sign_txn.options
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
solana.SignTxnInitiateRequest.walletId type:FT_STATIC max_size:32 fixed_length:true
solana.SignTxnInitiateRequest.derivationPath type:FT_STATIC max_count:4 fixed_length:true
solana.SignTxnInitiateTokenData.recipient_address type:FT_STATIC max_size:32 fixed_length:true
solana.SignTxnInitiateTokenData.mint_address type:FT_STATIC max_size:32 fixed_length:true
solana.SignTxnSignatureResponse.signature type:FT_STATIC max_size:64 fixed_length:true
solana.SignTxnSignatureRequest.blockhash type:FT_STATIC max_size:32 fixed_length:true
3 changes: 3 additions & 0 deletions src/constant_texts.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@ const char *ui_text_verify_amount = "Verify amount";
const char *ui_text_verify_contract = "Verify contract";
const char *ui_text_unverified_contract =
LV_SYMBOL_WARNING " Warning!\nUnverified contract";
const char *ui_text_verify_token_address = "Verify token address";
const char *ui_text_unverified_token =
LV_SYMBOL_WARNING " Warning!\nUnverified token";
const char *ui_text_confirm_wallet_name = "Confirm wallet name";
const char *ui_text_enter_data = "Enter data";
const char *ui_text_confirm_data = "Confirm data";
Expand Down
7 changes: 3 additions & 4 deletions src/constant_texts.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#define UI_TEXT_SEND_PROMPT "Send %s on %s"
#define UI_TEXT_SIGN_TXN_PROMPT "Sign transaction on %s from %s"
#define UI_TEXT_REVIEW_TXN_PROMPT "Review transaction to %s"
#define UI_TEXT_SEND_TOKEN_PROMPT "Send %s on %s from %s"
#define UI_TEXT_SEND_TOKEN_PROMPT "Send \n%s on \n%s"
#define UI_TEXT_BTC_RECEIVER "Receiver #%d"
#define UI_TEXT_BTC_FEE "Transaction fee"
#define UI_TEXT_SIGN_PROMPT "Sign %s message on %s from %s"
Expand Down Expand Up @@ -258,6 +258,8 @@ extern const char *ui_text_verify_address;
extern const char *ui_text_verify_amount;
extern const char *ui_text_verify_contract;
extern const char *ui_text_unverified_contract;
extern const char *ui_text_verify_token_address;
extern const char *ui_text_unverified_token;
extern const char *ui_text_confirm_wallet_name;
extern const char *ui_text_enter_data;
extern const char *ui_text_confirm_data;
Expand Down Expand Up @@ -390,9 +392,6 @@ extern const char *ui_text_inheritance_decryption_flow_confirmation_generic;
extern const char *ui_text_inheritance_decryption_flow_success;
extern const char *ui_text_inheritance_decryption_flow_failure;

// Solana
extern const char *ui_text_solana_verify_mint_authority;

#ifdef ALLOW_LOG_EXPORT
extern const char *ui_text_send_logs_prompt;
#endif
Expand Down

0 comments on commit 63f5773

Please sign in to comment.