A cost-efficient standard for fungible assets.
Sigil is a novel fungible token standard and program on Solana that represents fungible tokens on-chain using minimal data, ensuring the lowest possible data storage costs. While off-chain data solutions, such as merkle proofs, could be even cheaper, Sigil's on-chain approach offers the benefits of small transactions without requiring cumbersome proofs. Additionally, token data is directly accessible by other Solana programs via account state. Sigil strikes the optimal balance between on-chain, accessible data and minimal costs, considering the current limitations of account data on Solana's runtime.
The Sigil specification is not intended to replace existing token programs on Solana, which are well-established and feature-rich. Instead, it aims to capture new use cases for which the current standards are prohibitively expensive. For example, in gaming, a game studio may want to create numerous assets for users while subsidizing their rent costs to reduce friction. However, the current costs for creating new token accounts for each asset could become excessively high, given a large user base and multiple assets per user.
To ensure optimal efficiency in terms of compute and memory usage, the Sigil program is implemented with all data structures using zero-copy (bytemuck) implementations.
The specification is currently represented entirely by two types of accounts: Mint
and Pocket
accounts. Mint
accounts uniquely define a type of fungible item and encode the authority and supply data in account state. Pocket
accounts are defined per user and contain pairs of mint ticker and amount β in other words tokens β to encode the user's ownership amounts of various assets.
Mint accounts are PDAs derived from the seeds "mint"
, the authority of the mint and a four character ticker (e.g., "USDC"). The authority acts as a namespace for tickers to prevent squatting on valuable tickers that would inevitably happen if tickers were globally namespaced.
The on-chain Mint
struct is shown below.
/// Mint data.
///
/// A `Mint` is a PDA with the seeds `["mint", <authority>, <ticker>]`.
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct Mint {
/// Internal data.
/// 0. tag
/// 1. bump
/// 2. decimals
/// 3. not in use
/// 4-7. ticker
data: [u8; 8],
/// Authority of the mint.
pub authority: Pubkey,
/// Current supply of the mint.
pub supply: u64,
/// Maximum supply.
pub max_supply: u64,
}
Pocket
are PDAs derived from the seeds "pocket"
, an authority and user pubkeys. They are defined per-user to allow efficient storing of mint and amount pairs (tokens), but are also namespaced by the authority of mints β there will be one Pocket
account for each mint authority (namespace). Each Pocket
account has a base header which stores the account tag as well as the authority and user pubkeys to allow for efficient indexing.
Important
The innovation of Sigil
consists on using a single Pocket
account to hold different types of tokens, therefore saving on storage space and costs: creating a new user pocket account requires paying the base rent cost of 68
bytes only once for each user in a given namespace, but adding a new token (mint and amount pair) only costs an additional 8
bytes (4
for the mint ticker and 4
to represent a u32
amount). This is approximately 36x
savings when compared to the cost of creating a new SPL Token account for each new user and mint.
The on-chain Pocket
struct is shown below.
/// Struct representing an account storing tokens.
///
/// A `Pocket` is a PDA with the seeds `["pocket", <authority>, <user>]`.
pub struct Pocket<'a> {
/// Base account data.
pub base: &'a Base,
/// Tokens stored in the account.
pub tokens: U32ArraySet<'a, Token>,
}
#[repr(C)]
#[derive(Clone, Copy, Pod, Zeroable)]
pub struct Base {
/// Internal data.
/// 0. tag
/// 1. not in use
data: [u8; 2],
/// Authority of the account.
pub authority: Pubkey,
/// Owner of the account.
pub user: Pubkey,
}
In the SPL Token program, the mint account is 82 bytes
in size, plus the standard account info overhead of 128 bytes
. It only has to be created once per asset, so typically represents a fixed up-front cost that is paid initially and it does not scale up by number of users. Sigil's mint account is not much smaller, but does save a few bytes coming in at 56 bytes
plus the standard 128
account info overhead.
Token accounts however have significant savings, as SPL Token accounts require a new token account per user and mint, which is 128
bytes plus 165
bytes for a total of 293
bytes. In Sigil, there is a fixed cost of 128
bytes plus 68
bytes for a new user token account and then each additional asset only requires 8
bytes without having to pay for extra account header each time as the pairs are simply stored in on the same account.
π¦ Base Comparisons
Account | Data Size (Bytes) | Rent Cost @ $200 SOL |
---|---|---|
SPL Mint | 82 | $0.29 |
Sigil Mint | 56 | $0.26 |
SPL Token Account (1 asset) | 165 | $0.41 |
Sigil Pocket (1 asset) | 76 | $0.28 |
π¦ User w/ 100 Assets
Account | Data Size (Bytes) | Rent Cost @ $200 SOL |
---|---|---|
SPL Token Account | 16,500 | $41 |
Sigil Pocket | 876 | $1.40 |
π¦ 1000 Users w/ 100 Assets each
Account | Data Size (Bytes) | Rent Cost @ $200 SOL |
---|---|---|
SPL Token Account | 16,500,000 | $41,000 |
Sigil Pocket | 876,000 | $1,400 |
Note
The cost to add a new asset (token) to an existing Sigil pocket account is $0.0111
@ $200
SOL and it takes 8
bytes of account space.
The specification currently does not support a delegate system as storing the extra data for that raises the costs significantly. However, delegates could likely be implemented in a cheaper and modular way by using an additional PDA to represent the delegation so that only use-cases that actually require delegates end up paying for them.
Similarly, there is no option to freeze a token but this could be implemented as a bit flag if needed.
To get started run the following command
pnpm install
to install the necessary dependencies to set up the project and run pnpm
scripts.
Now you can build the program:
pnpm programs:build
generate the clients and IDL:
pnpm generate
start a local validator
pnpm validator:start
and run tests:
pnpm clients:js:test
pnpm clients:rust:test
The following clients are available for the Sigil. You may use the following links to learn more about each client.
The following script is available to start a local validator for testing.
pnpm validator:start
By default, if a local validator is already running, the script will be skipped. You may use the validator:restart
script instead to force the validator to restart.
pnpm validator:restart
Finally, you may stop the local validator using the following command.
pnpm validator:stop
Copyright (c) 2024 nifty-oss maintainers
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.