Skip to content

Conversation

febo
Copy link
Contributor

@febo febo commented Aug 31, 2025

Problem

Currently p-token uses the "generic" entrypoint. There is an opportunity to give priority to most used instruction to further save CUs. Looking at token instructions from a period of one month, the most used instructions are:

Instruction Usage (%)
transfer_checked 36.33%
transfer 13.22%
close_account 12.23%
initialize_account3 9.98%
initialize_immutable_owner 9.78%
sync_native 4.53%
initialize_account 2.58%

Other instructions account for less than 1% each.

Solution

This PR adds a custom entrypoint to p-token that includes a "fast-path" to transfer instructions. It also modifies the current processor to give priority to sync_native and initialize_immutable_owner instructions. Both modifications lead to significant improvements in CU consumption when considering the usage distribution.

| Instruction                | spl token | p-token |  p-token  |   delta   |
|                            |           |  (main) | (this PR) | (p-token) |
| -------------------------- | --------- | ------  | --------- | --------- |
| initialize_mint            |    2967   |    98   |    105    |     7     |
| initialize_account         |    4527   |   139   |    155    |    16     |
| initialize_multisig        |    2973   |   188   |    193    |     5     |
| transfer                   |    4645   |   116   |     79    |   -37     |
| approve                    |    2904   |   115   |    124    |     9     |
| revoke                     |    2677   |    95   |     99    |     4     |
| set_authority              |    3167   |   132   |    136    |     4     |
| mint_to                    |    4538   |   117   |    123    |     6     |
| burn                       |    4753   |   123   |    133    |    10     |
| close_account              |    2916   |   123   |    125    |     2     |
| freeze_account             |    4265   |   141   |    149    |     8     |
| thaw_account               |    4267   |   138   |    146    |     8     |
| transfer_checked           |    6200   |   152   |    111    |   -41     |
| approve_checked            |    4458   |   157   |    171    |    14     |
| mint_to_checked            |    4545   |   166   |    172    |     6     |
| burn_checked               |    4754   |   126   |    136    |    10     |
| initialize_account2        |    4388   |   128   |    172    |    44     |
| initialize_account3        |    4240   |   243   |    248    |     5     |
| initialize_multisig2       |    2826   |   312   |    319    |     7     |
| initialize_mint2           |    2827   |   222   |    226    |     4     |
| amount_to_ui_amount        |    2499   |   457   |    461    |     4     |
| ui_amount_to_amount        |    3161   |   690   |    694    |     4     |
| initialize_immutable_owner |    1404   |    60   |     38    |   -22     |
| sync_native                |    3045   |    88   |     62    |   -26     |

➡️ Credits to @cavemanloverboy for the "fast-path" approach.

@febo febo force-pushed the febo/custom-entrypoint branch from 3d74a84 to fafdd3e Compare September 2, 2025 16:06
@febo febo force-pushed the febo/simplify-math branch from 2edf20b to 9e62a06 Compare September 2, 2025 16:07
@febo febo force-pushed the febo/custom-entrypoint branch from fafdd3e to 8a3de38 Compare September 2, 2025 16:08
@febo febo marked this pull request as ready for review September 2, 2025 16:09
@febo febo requested a review from joncinque September 2, 2025 16:09
Copy link
Contributor

@joncinque joncinque left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No show-stoppers from my side! I didn't calculate all of the consts myself to check, but the overall logic looks good to me.

Comment on lines +110 to +115
&& (*input.add(ACCOUNT1_DATA_LEN).cast::<u64>() == 165)
&& (*input.add(ACCOUNT2_HEADER_OFFSET) == 255)
&& (*input.add(ACCOUNT2_DATA_LEN).cast::<u64>() == 82)
&& (*input.add(IX12_ACCOUNT3_HEADER_OFFSET) == 255)
&& (*input.add(IX12_ACCOUNT3_DATA_LEN).cast::<u64>() == 165)
&& (*input.add(IX12_ACCOUNT4_HEADER_OFFSET) == 255)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's use the consts for token account size / mint size / non-dup marker here

Comment on lines +75 to +76

/// Offset for the second account.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Offset for the second account.
/// Offset for the third account.

Comment on lines +62 to +66
/// Offset for the fourth account data length.
///
/// This is expected to be an account with variable data
/// length.
const IX12_ACCOUNT4_DATA_LEN: usize = 0x7b20;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only ever used once, and still called with align -- why not just put the aligned value as the const?

// Check that we have enough instruction data.
//
// Expected: instruction discriminator (u8) + amount (u64) + decimals (u8)
if input.add(offset).cast::<usize>().read() >= 10 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe use u64 here since size_of::<u64>() is added right after?

Suggested change
if input.add(offset).cast::<usize>().read() >= 10 {
if input.add(offset).cast::<u64>().read() >= 10 {

let discriminator = input.add(offset + size_of::<u64>()).cast::<u8>().read();

// Check for transfer discriminator.
if likely(discriminator == 12) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you use const here too?

Comment on lines +83 to +85
/// Offset for the third account data length. This is
/// expected to be a mint account (82 bytes).
const IX3_ACCOUNT3_DATA_LEN: usize = 0x5268;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with this, just align it from the start?

Comment on lines +68 to +69
/// Expected offset for the instruction data in the case all
/// previous accounts have zero data.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this only assume that the authority has zero data?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants