Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added solana token transferChecked instruction type support #621

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading