Skip to content

Commit

Permalink
Refactor ComputeBudget fees screen - calculate max fees from unit lim…
Browse files Browse the repository at this point in the history
…it and unit price (#15)

* Refactor ComputeBudget fees screen - calculate max fees from unit limit and unit price

* Fix lint error

* Fix max fees calculation - include signatures count

* Fix unit tests

* Improved logic for calculate_max_fee
  • Loading branch information
0xMMBD authored Aug 30, 2024
1 parent be255bd commit 81508bc
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 45 deletions.
54 changes: 29 additions & 25 deletions libsol/compute_budget_instruction.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,48 +46,52 @@ static int parse_loaded_accounts_data_size_limit(
return 0;
}

static int print_compute_budget_unit_price(ComputeBudgetChangeUnitPriceInfo* info,
const PrintConfig* print_config) {
UNUSED(print_config);

SummaryItem* item;

item = transaction_summary_general_item();
summary_item_set_u64(item, "Unit price", info->units);

return 0;
static uint32_t calculate_max_fee(const ComputeBudgetFeeInfo* info) {
uint32_t max_fee = FEE_LAMPORTS_PER_SIGNATURE * info->signatures_count;

if (info->change_unit_price != NULL) {
uint32_t max_compute = 0;
if (info->change_unit_limit != NULL) {
max_compute = info->change_unit_limit->units;
} else {
max_compute =
MIN(info->instructions_count * MAX_CU_PER_INSTRUCTION, MAX_CU_PER_TRANSACTION);
}
return max_fee + (info->change_unit_price->units * max_compute);
}
return max_fee;
}

static int print_compute_budget_unit_limit(ComputeBudgetChangeUnitLimitInfo* info,
const PrintConfig* print_config) {
static int print_compute_budget_max_fee(uint32_t max_fee, const PrintConfig* print_config) {
UNUSED(print_config);

SummaryItem* item;

item = transaction_summary_general_item();
summary_item_set_u64(item, "Unit limit", info->units);
summary_item_set_u64(item, "Max fees", max_fee);

return 0;
}

int print_compute_budget(ComputeBudgetInfo* info, const PrintConfig* print_config) {
switch (info->kind) {
case ComputeBudgetChangeUnitLimit:
return print_compute_budget_unit_limit(&info->change_unit_limit, print_config);
case ComputeBudgetChangeUnitPrice:
return print_compute_budget_unit_price(&info->change_unit_price, print_config);
case ComputeBudgetRequestHeapFrame:
case ComputeBudgetSetLoadedAccountsDataSizeLimit:
break;
}
return 1;
/**
* Display transaction max fees
* RequestHeapFrame and SetLoadedAccountsDataSizeLimit instruction kinds
* are omitted on purpose as they currently do not display any data on the screen
*/
void print_compute_budget(ComputeBudgetFeeInfo* info, const PrintConfig* print_config) {
uint32_t transaction_max_fee = calculate_max_fee(info);
print_compute_budget_max_fee(transaction_max_fee, print_config);
}

int parse_compute_budget_instructions(const Instruction* instruction, ComputeBudgetInfo* info) {
int parse_compute_budget_instructions(const Instruction* instruction,
const MessageHeader* header,
ComputeBudgetInfo* info) {
Parser parser = {instruction->data, instruction->data_length};

BAIL_IF(parse_compute_budget_instruction_kind(&parser, &info->kind));

info->signatures_count = header->pubkeys_header.num_required_signatures;

switch (info->kind) {
case ComputeBudgetRequestHeapFrame:
return parse_request_heap_frame_instruction(&parser, &info->request_heap_frame);
Expand Down
19 changes: 17 additions & 2 deletions libsol/compute_budget_instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
#include "sol/parser.h"
#include "sol/print_config.h"

// https://solana.com/docs/core/fees#compute-unit-limit
#define MAX_CU_PER_INSTRUCTION 200000
#define MAX_CU_PER_TRANSACTION 1400000
#define FEE_LAMPORTS_PER_SIGNATURE 5000

extern const Pubkey compute_budget_program_id;

enum ComputeBudgetInstructionKind {
Expand Down Expand Up @@ -32,6 +37,7 @@ typedef struct ComputeBudgetSetLoadedAccountsDataSizeLimitInfo {

typedef struct ComputeBudgetInfo {
enum ComputeBudgetInstructionKind kind;
size_t signatures_count;
union {
ComputeBudgetRequestHeapFrameInfo request_heap_frame;
ComputeBudgetChangeUnitLimitInfo change_unit_limit;
Expand All @@ -40,6 +46,15 @@ typedef struct ComputeBudgetInfo {
};
} ComputeBudgetInfo;

int parse_compute_budget_instructions(const Instruction* instruction, ComputeBudgetInfo* info);
typedef struct ComputeBudgetFeeInfo {
ComputeBudgetChangeUnitLimitInfo* change_unit_limit;
ComputeBudgetChangeUnitPriceInfo* change_unit_price;
size_t instructions_count;
size_t signatures_count;
} ComputeBudgetFeeInfo;

int parse_compute_budget_instructions(const Instruction* instruction,
const MessageHeader* header,
ComputeBudgetInfo* info);

int print_compute_budget(ComputeBudgetInfo* info, const PrintConfig* print_config);
void print_compute_budget(ComputeBudgetFeeInfo* info, const PrintConfig* print_config);
5 changes: 4 additions & 1 deletion libsol/compute_budget_instruction_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,12 @@ void test_parse_compute_budget_instructions_invalid_kind() {
Instruction instruction = {.data = message,
.data_length = sizeof(message),
.accounts_length = 0};

MessageHeader* header = {0};

ComputeBudgetInfo info;

int result = parse_compute_budget_instructions(&instruction, &info);
int result = parse_compute_budget_instructions(&instruction, header, &info);

assert(result == 1); // Invalid instruction kind for ComputeBudget program
}
Expand Down
4 changes: 3 additions & 1 deletion libsol/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ int process_message_body(const uint8_t* message_body,
break;
}
case ProgramIdComputeBudget: {
if (parse_compute_budget_instructions(&instruction, &info->compute_budget) == 0) {
if (parse_compute_budget_instructions(&instruction,
header,
&info->compute_budget) == 0) {
info->kind = program_id;
}
break;
Expand Down
2 changes: 1 addition & 1 deletion libsol/message_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ void test_process_message_body_transfer_with_compute_budget_limit_and_unit_price
0, 0, 0, 0, 0, 0, 0
};

process_message_body_and_sanity_check(message, sizeof(message), 6);
process_message_body_and_sanity_check(message, sizeof(message), 5);

}

Expand Down
58 changes: 43 additions & 15 deletions libsol/transaction_printers.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,48 @@ static int print_transaction_nonce_processed(const PrintConfig* print_config,
return 1;
}

InstructionInfo* const* preprocess_compute_budget_instructions(const PrintConfig* print_config,
InstructionInfo* const* infos,
size_t* infos_length) {
size_t infos_length_initial = *infos_length;
if (infos_length_initial > 1) {
// Iterate over infos and print compute budget instructions and offset pointers
// Handle ComputeBudget instructions first due to tech limitations of the
// print_transaction_nonce_processed. We can get one or 4 ComputeBudget instructions in a
// single transaction, so we are not able to handle it in a static switch case.
ComputeBudgetFeeInfo compute_budget_fee_info = {.change_unit_limit = NULL,
.change_unit_price = NULL,
.instructions_count = infos_length_initial,
.signatures_count = 0};
for (size_t info_idx = 0; info_idx < infos_length_initial; ++info_idx) {
InstructionInfo* instruction_info = infos[0];
if (instruction_info->kind == ProgramIdComputeBudget) {
compute_budget_fee_info.signatures_count =
instruction_info->compute_budget.signatures_count;
// Unit limit and unit price needs to be aggregated
// before displaying as this is needed for calculating max fee properly
if (instruction_info->compute_budget.kind == ComputeBudgetChangeUnitLimit) {
compute_budget_fee_info.change_unit_limit =
&instruction_info->compute_budget.change_unit_limit;
}
if (instruction_info->compute_budget.kind == ComputeBudgetChangeUnitPrice) {
compute_budget_fee_info.change_unit_price =
&instruction_info->compute_budget.change_unit_price;
}
infos++;
(*infos_length)--;
}
}
if (compute_budget_fee_info.change_unit_limit ||
compute_budget_fee_info.change_unit_price) {
// We do not want to display anything related to the compute budget
// if no instructions of this type were present in the transaction
print_compute_budget(&compute_budget_fee_info, print_config);
}
}
return infos;
}

int print_transaction(const PrintConfig* print_config,
InstructionInfo* const* infos,
size_t infos_length) {
Expand All @@ -681,21 +723,7 @@ int print_transaction(const PrintConfig* print_config,
infos_length--;
}

if (infos_length > 1) {
// Iterate over infos and print compute budget instructions and offset pointers
// Handle ComputeBudget instructions first due to tech limitations of the
// print_transaction_nonce_processed. We can get one or 4 ComputeBudget instructions in a
// single transaction, so we are not able to handle it in a static switch case.
size_t infos_length_initial = infos_length;
for (size_t info_idx = 0; info_idx < infos_length_initial; ++info_idx) {
InstructionInfo* instruction_info = infos[0];
if (instruction_info->kind == ProgramIdComputeBudget) {
print_compute_budget(&instruction_info->compute_budget, print_config);
infos++;
infos_length--;
}
}
}
infos = preprocess_compute_budget_instructions(print_config, infos, &infos_length);

return print_transaction_nonce_processed(print_config, infos, infos_length);
}

0 comments on commit 81508bc

Please sign in to comment.