CreateAccountAllowPrefund
Summary
A CreateAccountAllowPrefund instruction added to the system program reduces network overhead for applications that need to fund account rent (in whole or in part) in advance of account creation.
Motivation
The existing CreateAccount system program instruction creates accounts within a program by use of transfer, allocate, and assign.
CreateAccount fails if lamports > 0. This was intended as a protection measure, preventing developers from accidentally passing in some normal wallet address and permanently locking its lamports after allocate and assign.
However, it is common practice to provide rent lamports to accounts prior to the actual creation (allocation and assigning) of the account space, rather than forcing the fee payer of the transaction to provide all of the required lamports. In this and similar instances, developers currently must manually construct a patched CreateAccount call of their own with 2-3 CPI calls: sometimes Transfer, then Allocate, and then Assign. While these actions themselves are minimally expensive, the overhead incurred with every Cross-Program Invocation - depth check, signer check, account copy, etc. - can make up the bulk of the computation done in the transaction. Each CPI incurs a minimum of 1_000 compute units, plus additional amounts depending on the instruction and account length.
CreateAccountAllowPrefund performs allocate, assign, and transfer without asserting that the created account has zero lamports. Applications which do not need to transfer can specify 0 lamports, effectively providing them with an AllocateAndAssign.
p-ATA program benchmarks demonstrate that use of CreateAccountAllowPrefund can save approximately 2_500 compute units: https://github.com/solana-program/associated-token-account/pull/102
This is a stopgap measure, as it extends the undesired interface sprawl of helper instructions such as CreateAccount. However, any redesign of Cross-Program Invocations to safely reduce overhead would be an extensive protocol upgrade and slow to land. In the meantime, the network will benefit from CreateAccountAllowPrefund.
New Terminology
- Prefund: sending lamports to an account to pay for its rent in whole or in part in any prior instruction or prior transaction, before the instruction in which its space is
allocated and its owner isassigned.
Detailed Design
CreateAccountAllowPrefund is added as a system program instruction, identical to CreateAccount except for the following:
- The instruction has a new discriminant (13).
- The funding account is optional.
- Due to #2, the accounts are ordered differently.
Instruction Data
| Field | Type | Description |
|---|---|---|
| Discriminant | u32 | The instruction discriminant, value 13. |
lamports | u64 | Number of lamports to transfer to the new account. |
space | u64 | Number of bytes of memory to allocate. |
owner | Pubkey | Address of program that will own the new account. |
Accounts
The instruction requires the following accounts:
| Index | Role | Description |
|---|---|---|
| 0 | [WRITE, SIGNER] | New account: The account to be created. |
| 1 | [WRITE, SIGNER] (optional) | Funding account: The account that |
will pay for the lamport transfer. Required only when lamports > 0. |
Behavior
The CreateAccountAllowPrefund instruction performs the following actions:
- Allocate: As with
CreateAccount, this instruction callsallocate, which will fail if the new account is non-empty. - Assign: As with
CreateAccount, this instruction callsassign. - Transfer: If
lamportsis greater than 0, it transfers the specified number of lamports from thefunding_account(account index 1) to thenew_account(account index 0).lamportscan be used when the account is prefunded insufficiently; in other words, when the account has some lamports, but needs more to cover rent.
This instruction will fail if:
- The
funding_accountis not provided whenlamports > 0.
Additional underlying reasons may cause the instruction to fail; in other words, it will fail if allocate, assign, or transfer fail for any reason including:
- The
funding_accountdoes not have enough lamports for the transfer (whenlamports > 0). - The
new_accountalready contains data or is not owned by the System Program. - The
new_accountdoes not have sufficient lamports to be rent-exempt after the transfer. - Required accounts are not writable or not signers.
- The requested
spaceexceeds the max permitted data length.
Alternatives Considered
- A separate
AllocateAndAssigninstruction. However, usingCreateAccountAllowPrefundis appropriate for a caller needing toallocateandassign, astransferis called to top up the storage rent requirement only if current lamports are insufficient (equivalent to how an instruction namedAllocateAndAssignAndMaybeTransferwould function). A separateAllocateAndAssignwould save one check, but the compute savings would be minimal and not enough to justify such interface sprawl. - Redesigning CPIs. The current CPI model spins up a new context for every invocation - re-copying and re-verifying account and signer data. A CPI redesign could slash this overhead for innumerable programs, but such a redesign would involve extensive implementation and audit time. By contrast,
CreateAccountAllowPrefunddelivers quick compute savings for a common pattern with minimal surface area. Instruction-batching helpers such asCreateAccountAllowPrefundandCreateAccountcan be deprecated whenever CPI improvements land, enabling such helpers to become library-level functions rather than system instructions.
Drawbacks
Interface sprawl. As mentioned, this is a temporary compromise until CPI improvements can land.
Impact
The primary impact is an available reduction in CPI overhead for programs which currently must perform these operations manually across 2 to 3 CPIs.
As mentioned previously, the p-ATA program takes advantage of CreateAccountAllowPrefund to save approximately 2_500 compute units: https://github.com/solana-program/associated-token-account/pull/102
Security Considerations
Developers using CreateAccountAllowPrefund should ensure they are not passing an unintended wallet account with lamports. The usual checks of allocate and assign are still performed - an account with data or with an owner other than the system program will not be modified - but since the lamport check is removed, a wallet account with lamports can be bricked by this instruction.
Backwards Compatibility
This SIMD requires a feature gate, as it adds a new System Program instruction.