Function modifiers (view, pure, payable) and fallback functions in Solana: why they don’t exist

Hero image showing view, pure, payable, fallback, and receive in Solona

Solana does not have fallback or receive functions

A Solana transaction must specify in advance the accounts it will modify or read as part of a transaction. If a “fallback” function accesses an indeterminant account, then the entire transaction will fail. This would put an onus on the user to anticipate the accounts the fallback function will access. It’s much simpler as a result to simply disallow these kinds of functions.

Solana does not have a notion of a “view” or “pure” function

A “view” function in Solidity creates a guarantee that the state will not change using two mechanisms:

  • All external calls in a view function are staticcalls (calls that revert if a state change happens)
  • If the compiler detects state-changing opcodes, it throws an error

A pure function takes this even further by the compiler checking if there are opcodes that view the state.

These function restrictions happen mostly at the compiler level, and Anchor does not implement any of these compiler checks. Anchor is not the only framework for building Solana programs. Seahorse is another one. Perhaps some other framework will come along with function annotations explicitly stating what functions can and cannot do, but for now we can rely on the following guarantee: if an account is not included in the Context struct definition, that function won’t access that account.

This does not mean that the account cannot be accessed at all. For example, we could craft a separate program to read in an account and somehow forward that data to the function in question.

Finally, there is no such thing as a staticcall in the Solana virtual machine or runtime.

View functions aren’t necessary in Solana anyway

Because a Solana program can read any account passed to it, it can read an account owned by another program.

The program owning the account does not need to implement a view function to grant access for another program to view that account. The web3 js client — or another program — can view the Solana account data directly.

This has a very important implication:

It is not possible to use reentrancy locks to directly defend against read only reentrancy in Solana. The program must expose flag for readers to know if the data is reliable.

Read only reentrancy happens when a victim contract accesses a view function that is showing manipulated data. This can be defended against in Solidity by adding a nonReentrant modifier to the view function. However, in Solana there is no way to prevent another program from viewing data in an account.

However, Solana programs can still implement the flags reentrancy guards use. Programs consuming the account of another program can check those flags to see if the account might currently be in a reentrant state and should not be trusted.

There are no custom modifiers in Rust

Custom modifiers like onlyOwner or nonReentrant are a Solidity construct, and not a feature available in Rust.

Custom units are not available in Rust or Anchor

Because Solidity is intertwined with Ethereum, it has handy keywords like ethers or wei to measure Ethereum. Not surprisingly, LAMPORTS_PER_SOL is not defined in Rust, but somewhat surprisingly, it is not defined in the Anchor Rust Framework either. It is available in the Solana web3 js library however.

Similarly, Solidity has days as a convenient alias for 84,600 seconds, but no such equivalent exists in Rust/Anchor.

There is no such thing as “payable” functions in Solana. Programs transfer SOL from the user, users do not transfer SOL to the program

This is the subject of the next tutorial.

Learn more with RareSkills

See our Solana development course for the next chapter.

Originally Published March, 1, 2024

Cross Program Invocation In Anchor

Cross Program Invocation In Anchor Cross Program Invocation (CPI) is Solana’s terminology for a program calling the public function of another program. We’ve already done CPI before when we sent a transfer SOL transaction to the system program. Here is the relevant snippet by way of reminder: pub fn send_sol(ctx: Context<SendSol>, amount: u64) -> Result<()> […]

Reading Another Anchor Program’s Account Data On Chain

Reading Another Anchor Program’s Account Data On Chain In Solidity, reading another contract’s storage requires calling a view function or the storage variable being public. In Solana, an off-chain client can read a storage account directly. This tutorial shows how an on-chain Solana program can read the data in an account it does not own. […]

#[derive(Accounts)] in Anchor: different kinds of accounts

[derive(Accounts)] in Anchor: different kinds of accounts #[derive(Accounts)] in Solana Anchor is an attribute-like macro for structs that holds references to all the accounts the function will access during its execution. In Solana, every account the transaction will access must be specified in advance One reason Solana is so fast is that it executes transactions […]

Modifying accounts using different signers

Modifying accounts using different signers In our Solana tutorials thus far, we’ve only had one account initialize and write to the account. In practice, this is very restrictive. For example, if user Alice is transferring points to Bob, Alice must be able to write to an account initialized by user Bob. In this tutorial we […]