Build on Solana
Understanding to Solana Smart Contracts
In the following sections, we'll dive into the essentials of Solana Smart Contracts, covering everything from transactions and instructions to accounts and the intriguing concept of Program Derived Addresses (PDAs). Whether you're a seasoned developer or just starting your blockchain journey, this guide has something for you.
We'll explore how Solana's architecture allows for efficient processing, and we'll break down the life cycle of a smart contract on this platform. By the time you finish this tutorial, you'll have a solid understanding of how smart contracts work on Solana, and you'll be ready to take your next steps in this exciting field.
So, grab a cup of coffee, settle in, and let's get started. Solana's world of innovation awaits, and we're here to help you navigate it. Happy learning!
In Solana, a transaction is the highest level of abstraction that a client application interacts with. It is a signed instruction sequence that is executed atomically. Transactions are used to interact with programs (smart contracts) deployed on the Solana network.
A transaction contains one or more instructions, a recent blockhash, and signatures. The recent blockhash, also known as a transaction "recentBlockhash", is used for transaction processing and lifespan. It refers to the hash of a recent block in the ledger. Transactions are only valid for a limited period of time, roughly two minutes, and will be rejected by the cluster after the recentBlockhash expires.
The signatures are the cryptographic proof of the transaction's integrity and authority. Each transaction must be signed by the appropriate account holders as per the instructions contained within.
signatures:  message: header, addresses, recent_bockhash, instructions
instructions: program_id, accounts, instruction data,
Instructions in Solana are bundled within a transaction and are executed sequentially and atomically. Each instruction contains a program ID, accounts that it wishes to interact with, and a data field.
The program ID specifies the on-chain program (smart contract) that will process the instruction. The accounts array includes all accounts that the instruction will read from or write to. The accounts array must include at least one signer who authorizes the instruction.
The data field is an arbitrary byte array that is passed to the program for processing. The structure of this data is determined by the program itself. This is where the specific function call and parameters would be encoded in a program-specific way.
In Solana, the combination of transactions and instructions allows for high throughput and efficient processing. The network is capable of processing thousands of transactions per second, each containing multiple instructions, which can interact with different programs and accounts. This design is a key part of Solana's scalability and speed.
In Solana, an account is a persistent memory structure that a program can use for storing state. Every account in Solana is initially owned by the system program, but the system program can change the ownership if the correct private key for the account is provided. Each account includes several properties:
Public Key (Pubkey): This is the unique identifier of the account. It's generated from the private key of the account holder.
Signer Flag (is_signer): This flag indicates whether the account is a signer of the transaction. If true, the transaction must include the signature of this account.
Writable Flag (is_writable): This flag indicates whether the data in the account can be modified. If true, the account's data can be written to in the current transaction.
Lamports: This is the number of lamport's (the smallest unit of the native SOL token) held in the account. Lamports also serve as rent to keep the account on the network. A lamport is to SOL what a satoshi is to Bitcoin; 1 billion lamports make 1 SOL.
Data: This is a byte array that can hold arbitrary data. The structure of the data is defined by the owner program.
Owner: This is another public key that identifies the program that has authority over the account's data. Only the owner program can modify the account's data.
Executable Flag: If this flag is set to true, then the account's data field is treated as a program. In other words, the account is a smart contract. If false, the account is not a program.
Rent Epoch: This value represents the latest epoch that rent has been paid for the account. Rent is paid in SOL and ensures that the account remains active on the network.
In Solana, accounts not only hold the state of a program but can also be used to create complex relationships between different programs. This flexible account model is a key part of Solana's ability to handle high-performance, concurrent, and secure contract execution.
Program Derived Addresses (PDAs)
A Program Derived Address (PDA) is a special type of account in Solana that is derived from a base public key and a program ID, but does not have a known private key. Despite not having a known private key, a PDA can own data and tokens, and it can sign transactions, but only through the program that it's associated with.
PDAs are useful for a variety of reasons:
Security: Since PDAs do not have a known private key, they cannot be accessed directly by any user. Only the program that the PDA is derived from can make changes to the PDA, which provides a strong layer of security.
Predictability: The address of a PDA is deterministic, meaning it can be calculated ahead of time. This is useful in scenarios where you need to know the address of an account before it is created. For example, a program can ensure that a specific PDA will always be used to hold a certain type of data, and clients can know where to look for this data.
Authority Delegation: PDAs can be used to delegate authority. For example, a program can use a PDA as an intermediary to hold funds. The program can then allow specific users to initiate transactions that transfer funds from the PDA, effectively delegating authority to those users.
Creating a PDA involves the use of the Pubkey:create_program_address function, which takes two arguments: a seeds array and a program ID. The seeds array can contain any data that you want to use to derive the PDA. The program ID is the ID of the program that will have authority over the PDA.
One thing to note is that not all combinations of seeds and program IDs will create a valid PDA. If a combination does not create a valid PDA, the create_program_address function will return an error. This is by design to ensure that PDAs are spread out evenly across the address space and do not cluster around certain program IDs.
Program Derived Addresses (PDAs) in Solana have a variety of use cases due to their unique properties of security, predictability, and authority delegation. Here are a few examples:
Token Accounts: In the Solana Program Library (SPL), PDAs are used to create unique addresses for each user's token account. This allows the program to know where to find a user's token balance without the user having to provide their private key.
Cross-Program Invocations: PDAs can be used as intermediaries in cross-program invocations. For example, if Program A wants to call Program B and pass it some tokens, it can first transfer the tokens to a PDA that both programs know about. Program B can then take ownership of the PDA and access the tokens.
Authority Delegation: PDAs can be used to delegate authority in a controlled manner. For example, a lending program might use a PDA to hold collateral. The program can allow borrowers to withdraw their collateral from the PDA when their loan is repaid, but not before.
Predictable Contract Addresses: PDAs can be used to create predictable contract addresses. For example, a program could use a PDA to store global state that all instances of the program can access. Since the address of the PDA is deterministic, all instances of the program know where to find this state.
On-Chain Lotteries or Games: PDAs can be used to hold funds for an on-chain lottery or game. The program could allow users to buy tickets by transferring funds to the PDA. When the lottery is drawn or the game is over, the program can transfer the funds from the PDA to the winner.
Smart Contract Life Cycle on Solana
Writing the Smart Contract: The developer writes the smart contract using a language that can be compiled to Berkeley Packet Filter (BPF) bytecode. Rust and C are currently the most commonly used languages for this purpose on Solana.
Compiling to BPF: The smart contract code is compiled into BPF bytecode. BPF is a technology used in Linux for running a small piece of code safely and quickly. Solana uses BPF as its virtual machine because it's secure, fast, and allows for resource usage to be accurately calculated.
Deploying the BPF Program: The compiled BPF bytecode is deployed to the Solana blockchain. This is done by sending a transaction to the Solana network that includes the BPF bytecode.
Returning the Program ID: After the BPF program is deployed, Solana returns a Program ID. This ID is a public key that uniquely identifies the smart contract on the Solana blockchain.
Creating Transactions with Instructions: To interact with the smart contract, the developer creates transactions that include instructions for the smart contract. These instructions are specific to the smart contract and define the operations to be performed.
Executing Instructions in the BPF VM: When a transaction is submitted to the Solana blockchain, it's processed by validators. Part of this processing involves executing the instructions in the BPF VM. Solana's architecture allows for these instructions to be executed in parallel, which contributes to its high performance.
Interacting with the Program Derived Address (PDA): A PDA is an address derived from the program ID and is used for storing the state of the smart contract. The developer can interact with the PDA as part of the instructions in a transaction.
Updating the State of the Smart Contract: As the instructions in a transaction are executed, they may modify the state of the smart contract. This state is stored in the PDA, and changes to it are reflected in the Solana ledger.
In this diagram:
- A Transaction is initiated.
- This Transaction contains an Instruction.
- The Instruction interacts with a Program Derived Address (PDA).
- The PDA then interacts with the Berkeley Packet Filter (BPF).
The BPF processes the instruction and updates the Solana Blockchain.
The updated state of the Solana Blockchain is then reflected back to the Transaction.
Instruction Interacts with a Program Derived Address (PDA)
In Solana, a Program Derived Address (PDA) is a unique address that is derived from a base public key and a seed. The PDA is used to store the state of a smart contract. When a transaction is created, it contains one or more instructions. Each instruction specifies the program (smart contract) it is intended for, by referencing the program's ID (which is a public key).
The instruction also includes the accounts it wishes to interact with. These accounts can be normal user accounts or PDAs. The accounts are provided as inputs to the smart contract program. The program can read from and write to these accounts, but it cannot modify the list of accounts.
When an instruction is executed, the Solana runtime passes the accounts specified in the instruction to the program. If a PDA is specified, the runtime fetches the account associated with the PDA and passes it to the program. The program can then read the state of the smart contract from the PDA account or write new state to it.
PDA Interacts with the Berkeley Packet Filter (BPF)
The Berkeley Packet Filter (BPF) is a technology used in Linux for running a small piece of code safely and quickly. Solana uses BPF as its virtual machine for executing smart contract programs. When a program is deployed to Solana, it is compiled to BPF bytecode.
When an instruction is executed, the Solana runtime loads the BPF bytecode of the program specified in the instruction. It then sets up a BPF virtual machine and runs the bytecode in it. The accounts specified in the instruction, including any PDAs, are passed to the BPF VM as inputs to the program.
The BPF VM provides a sandboxed environment for running the program. It ensures that the program cannot perform any malicious actions, such as accessing memory it shouldn't or running forever. The BPF VM also accurately measures the resources used by the program, such as CPU time and memory, which is used for charging transaction fees.
In summary, the interaction between instructions, PDAs, and the BPF VM is a key part of how smart contracts work on Solana. The instructions specify what action to take and which accounts to interact with. The PDAs provide a way for programs to maintain state between transactions. And the BPF VM provides a safe and efficient environment for executing the programs.