On-chain Data Storage
A smart contract that allows for easy storage and retrieval of data on-chain
Currently in Solana there is no standard for data storage on-chain. By data, I mean anything that can be stored as bytes like a text file, a HTML document, a PNG image, NFT JSON Metadata etc. This RFC proposes a solution for storing data on-chain and having it be retrieved easily.
To solve this issue, I have developed the Solana Data Program. This is a Solana smart contract that allows users to initialize a Data Account to store any data as bytes and have its metadata be stored in a separate PDA
Here’s a flow diagram to describe how the accounts are linked:
In the Metadata PDA Account, we store the metadata regarding the data in the Data Account. A few important fields stored in it are:
authority: The authority needs to be a signer in any instruction that involves the Data Account - updating the data and/or data type, finalizing the data, transferring the authority, and/or closing the account
serialization_status: Currently the supported data types are:
i. CUSTOM: To store custom data or a currently unsupported data type
ii. IMG: To store image data as raw bytes
iii. HTML: To store a HTML file as raw bytes
iv. JSON: To store minified JSON as raw bytes
The motivation behind having set
data_types is that it helps the client application determine how to display the data. It also opens doors for data verification (denoted by
serialization_status). Currently when JSON data being updated, the user could optionally verify to ensure it is valid JSON.
dynamic: A dynamic Data Account can realloc (up or down) while a static account will always stay a fixed size. This could be useful when users want to pay a fixed amount for the storage space (static) in the Data Account rather than a only-pay-how-much-is-needed approach (dynamic)
A Data Account is any Solana account that is owned by the Data Program and has an associated Metadata PDA Account. The data is stored directly as raw bytes so one could easily retrieve it using a single
getAccountInfo RPC call as such:
const data = (await connection.getAccountInfo(dataKey, commitment)).data;
The data account need not be created by the Data Program. You can also pass in a previously created Data Account to the Initialize instruction and it will assign it to the Data Program
InitializeDataAccount: This instruction creates and initializes the Metadata Account and optionally creates a Data Account.
UpdateDataAccount: This instruction updates the
data_typefield in the Metadata Account and the data in the Data Account.
UpdateDataAccountAuthority: This instruction updates the
authorityof the Data Account by updating the value in the Metadata Account. It requires both the old and new authority to be signers to prevent accidental transfers.
FinalizeDataAccount: This instruction finalizes the data in the Data Account by setting the
data_statusin the Metadata Account to be
FINALIZED. Finalized data can no longer be updated.
CloseDataAccount: This instruction closes the Data Account and the Metadata Account and transfers the lamports to the
SolD is a website that acts as an editor for the Data Program:
- It allows users to view the metadata and data associated with a Data Account via the
- It allows users to connect with their wallet and upload files directly to the Data Program by going to the
- If the user is logged in as the
authority, it allows the user to edit the data, finalize it and/or close the metadata and data accounts.
- Users can also view all data accounts they “own” via the
/authority/<authority>route and perform group actions on them
solana-data-program is a Typescript SDK that exposes APIs to interact with the Data Program and helper methods to parse the data, metadata etc. of a Data Account
One potential use case of the Data Program are fully on-chain dynamic NFTs. By this I mean an NFT with:
- JSON metadata stored on-chain
- Image data stored on-chain
- JSON metadata that can be updated via an on-chain crank
- Image data that can be updated via an on-chain crank
Here’s a link to such an NFT: Quine NFT
On clicking View original you will see the original HTML file that was pulled from on-chain. You can also inspect the Metadata and Image data on SolD separately:
Metadata JSON: Hb9vkWax5AeLWvCtYSjSvWrN6gTw324gKMa28kcBsgT3
P.S. The NFT image is a quine. The code on the surface of the rotating sphere is the code used to generate the sphere with code on its surface
An important consideration went into making sure that the Data Program is easy to use and composable. To demonstrate that, I have also made example smart contracts (two of which involve minting NFTs including the Quine NFT ) that CPI into the Data Program: solana-data-program/examples at main · nvsriram/solana-data-program · GitHub
URI Standard for Data Retrieval
Currently, to have the data be pulled from on-chain I have an API route
/data/<dataKey>?cluster=<cluster>&ext=<ext> that just returns the data as is (or in the extension format specified by
ext). It would be more handy to have a URI standard which might look something of the sort:
to get the data stored in
datakey in the format specified by
to get the metadata associated with the
This RFC discusses the features of the Data Program and how it can be used to store data on-chain. It presents the SolD website editor and Typescript SDK that make it easy to interact with the Data Program. It showcases potential use cases in fully on-chain dynamic NFTs and proposes a URI standard for data retrieval.
Data Program Smart Contract: GitHub - nvsriram/solana-data-program: Solana smart contract that handles on-chain data storage
SolD Website Editor: https://sold-website.vercel.app/
Typescript SDK: solana-data-program - npm
Quine NFT: Solscan
Quine NFT Image: https://sold-website.vercel.app/HoyEJgwKhQG1TPRB2ziBU9uGziwy4f25kDcnKDNgsCkg?cluster=Devnet
Quine NFT Metadata: https://sold-website.vercel.app/Hb9vkWax5AeLWvCtYSjSvWrN6gTw324gKMa28kcBsgT3?cluster=Devnet
Examples: solana-data-program/examples at main · nvsriram/solana-data-program · GitHub