Understanding Solana PDAs and CPIs

What are Program Derived Accounts?

Program Derived Accounts (PDAs) are arguably the most important concept to grasp as you learn to develop Solana programs. Essentially, PDAs are Solana accounts that are owned and controlled by programs, rather than users.

Typically, account addresses are the public key of their keypair; however, PDAs do not have a public key or private key. Instead, PDAs are owned by programs and thus are not controlled by private keys. Programs can programmatically sign for certain addresses without needing a private key.

On a more technical level, the address of a PDA is derived from the following:

  • The programId of its owner Solana program
  • Other arbitrary pieces of data, or seeds
  • A bump, which is an arbitrary u8 value that “bumps” the address off of the ed2559 curve

Because PDA addresses exist outside of the ed2559 curve, it is impossible for any user to calculate or co-opt the address of a PDA (and thus take over its data). This is because all user keypairs exist on the ed2559 curve.

When and Why Use PDAs?

PDAs are useful for when you want to store data for a specific user account. For instance, if someone connects their wallet to your application, and you want to store data just for that individual, then you would create a PDA for that user to store their data.

You can think of PDAs as hash maps which associate a user’s keypair with a data repository specific to their activity on an application.

Without using PDAs, there is no quick and seamless way of accessing the account data that’s associated with a given wallet address.

PDAs are also the foundation for cross program invocations, which allow us to break up complex business logic into composable programs.

What are Cross Program Invocations?

Cross program invocations (CPIs) are not unique to Solana. A CPI is simply the instance in which a smart contract makes a function call on another smart contract and then handles the data or response which results from the function call.

CPIs are an important topic in smart contract development because its necessary for creating modular programs; using CPIs, we can write wrote our business logic across multiple, manageable smart contracts rather than having a singular, monolithic piece of code.

For example, a smart contract that handles the minting and transfer of tokens could call another smart contract that manages a decentralized exchange, allowing users to trade those tokens. Cross program invocations enable these contracts to interact with each other seamlessly, without needing to duplicate code or manage complex state transitions between contracts.

We can credit the creation of CPIs to Armani Ferrante, founder of Coral, a company which is also responsible for the Anchor framework.

Why and When Use CPIs?

On other blockchains (i.e., Ethereum, Near, etc.), cross program invocations are called cross contract calls, and are primarily ever used when breaking up a particularly large and expensive function call (to reduce the cost of gas).

However, because Solana gas prices are so low, the primary use cases for implementing CPIs in your programs are different:

  • To refactor (or simplify) your code, breaking up complex functionality into smaller bits of code. This ultimately makes your code easier to test and debug.
  • To reduce execution time of function calls. In the case that a particular function (or set of functions) are computationally expensive, its calls will be sharded across multiple blocks. By breaking up complex functionality, these function calls will (more likely) be incorporated on the soonest possible block.