Skip to main content

3 posts tagged with "guides"

View All Tags

· 9 min read
Noah Prince

A major tenant of blockchain architecture is composability. By sharing state, dApps can create rich interactions that are not isolated to a single walled garden. A token can have uses throughout the ecosystem, not just in a single application.

Solana and Ethereum take vastly different approaches to this problem. In this post, we'll explore the composability patterns on both and the consequences of these design choices.

Background

My background is almost exclusively in Solana development. Ethereum devs, feel free to contribute and correct my understanding where needed.

Ethereum Composability

Ethereum Composability looks a lot like interface-based inheritance in classical Object Oriented Programming (OOP). Ethereum makes use of a set of Standards - Well-defined interfaces for common tasks, such as creating a token. The most well-known standard is the ERC-20 token standard:

interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}

Here's an overview of Ethereum's account structure:

Ethereum Accounts Overview

A smart contract (in this case, a single token), resides at a particular address. Both the data and execution logic for that token are stored at that address. For example, the data to find all holders is available at that particular address.

In ethereum, you can compose contracts by storing them in your contract's state:

contract FooContract {
IERC20 myTokenContract;
}

Similar to OOP, Ethereum is a system of smart contracts adopting interfaces so that they can interoperate together. "If it quacks like a duck, then treat it like a duck"

Solana Composability

Solana Composability looks a lot more like Functional Programming (FP). The execution logic for a smart contract (on Solana, called a program) exists separately from the state.

With Solana, data is still stored in accounts, but all of the data for a given program is distributed across multiple accounts. Every account in Solana is owned by a Program. If this were a SQL database, you could get all of the state for a given program by running:

SELECT * from solana.accounts WHERE owner = <program_id>

Where is a Solana program stored? The compiled code is also stored on accounts. The Solana runtime knows how to execute the binary for these special accounts. You can think of a program call as a function execution over some state.

Since we looked at ERC-20, let's look at spl-token. First, let's take a look at a Token Account:

pub struct Account {
/// The mint associated with this account
pub mint: Pubkey,
/// The owner of this account.
pub owner: Pubkey,
/// The amount of tokens this account holds.
pub amount: u64,
/// If `delegate` is `Some` then `delegated_amount` represents
/// the amount authorized by the delegate
pub delegate: COption<Pubkey>,
/// The account's state
pub state: AccountState,
/// If is_native.is_some, this is a native token, and the value logs the rent-exempt reserve. An
/// Account is required to be rent-exempt, so the value is used by the Processor to ensure that
/// wrapped SOL accounts do not drop below this threshold.
pub is_native: COption<u64>,
/// The amount delegated
pub delegated_amount: u64,
/// Optional authority to close the account.
pub close_authority: COption<Pubkey>,
}

This struct will be stored on a solana account for each unique holder of a token. The account is changed via function calls, for example:

Transfer {
/// The amount of tokens to transfer.
amount: u64,
}

There's some major differences to note here:

  • Solana has one token program for all of the spl-tokens in existance. Eth has a program for each token.
  • Solana state is stored separately from execution logic.

Composability works via composing functions. I can write a function that takes in an Account from the spl-token program, and then calls Transfer.

Solana Composability - Inheritance

Solana Program calls depend on the arity and ordering of account arguments. Making matters more complicated, every account used by an instruction must be passed to the instruction.

The upshot: If my implementation of a function requires more or different accounts than yours, they are incompatible. This makes inheritance difficult to impossible.

Lookup Tables

A new feature to Solana, lookup tables, may help alleviate this limitation.

Solana Composability - State as an Interface

With Solana, the State is the interface. Composition can be broken down into systems of state and actions on that state. What does this mean? If Program A wants to interact with Program B, it can either

  • Directly call Program B
  • Write State that is expected by Program B

The latter has massive implications for composability. Instead of needing to agree on an action based interface, Solana programs can agree on intermediary state. Tokens are the most common form of state.

  • Program A gives the user token A
  • Program B lets the user exchange token A for NFT C

In Solana, Program B is blissfully unaware of Program A. In Ethereum, program B would need a reference to the token program of A.

Composability Example - Solana vs Ethereum

Let's say you want to mint an NFT such that the price increases for every NFT purchased. The De-Facto way to mint a collection on Solana is the CandyMachine.

The problem: The CandyMachine takes a fixed price in either SOL or any SPL token.

On Ethereum, you may extend the interface of the CandyMachine contract and add your pricing logic. On Solana, you could do similar -- fork the candymachine and upload your own program. We're devs, and code duplication is bad! Instead, we can string two programs together!

To achieve this, we can tie a Strata Bonding Curve to a Metaplex Candymachine. A Strata Bonding Curve allows you to create a pricing system such that every new token minted is more expensive than the one before it.

The integration is straightforward:

  • Step 1. Create a bonding curve with desired pricing characteristics that lets users buy Token A
  • Step 2. Create a CandyMachine whose price is 1 Token A

At minting time, the minting UI:

  • Uses SOL to purchase a Mint Token, Token A
  • Uses Token A to mint an NFT from the CandyMachine.

The intermediary state between Strata and Metaplex is Token A. Importantly, neither the Strata nor Metaplex contracts know about each other.

Solana vs Ethereum Composability - Consequences

Neither composition strategy is inherently better. As you will find with FP vs OOP, there are plenty of flame wars arguing which is better. There are, however, notable tradeoffs.

Tradeoffs - Speed

Solana's programming model lends itself to massive amounts of parallelization. Because every piece of state is identified as read-only or writable, the Solana Sealevel runtime is able to run many transactions in parallel knowing that they will not interfere with each other. This is a core tenant of Solana, and why it has a much higher TPS than Ethereum.

Tradeoffs - UI Compatability

Ethereum's interface model makes it easy for one UI to integrate with multiple smart contracts implementing the same interface. This makes forking considerably easier on Ethereum than it is on Solana. Example: On Ethereum, if you fork Uniswap and match the interface, you will have out-of-the-box support in multiple user interfaces. On Solana, you would only have support if you did not add any accounts to the function signatures. Solana user interfaces tend to be heavily coupled to particular smart contracts.

Tradeoffs - Open Source

Neither Ethereum nor Solana has contracts that are open source by default. However, Solana has a good amount of closed source contracts that hinder composability. It is much harder to compose with something when you can't see the code. That being said, there is a strong culture of open source within Solana that is actively pushing for contracts to be open. Strata is, and will always be, open source.

Tradeoffs - Program Proliferation vs State Proliferation

Ethereum's model leads to a lot more programs and a lot more bespoke code running on chain. This makes it way easier to override behavior (for example, taking fees on token transfers). On Ethereum, forking is easy and encouraged.

Solana's model leads to a lot more state, and programs that act as state changing primitives. This makes it preferable to create chains of logic where generalized contracts interact with other generalized contracts. On Solana, forking adds complexity for any UI-only dApps. This causes a push for well-thought-out battle-tested contracts, like the Metaplex Token Metadata Standard.

Tradeoffs - Complexity

Solana tends to be more complex from a planning and architecture standpoint. You need to think hard about the state you're outputting, and how it can be used in other contracts. Stringing contracts together can be painful if this is done incorrectly.

With Ethereum, as long as you have a reasonable interface you can get away with ugly state.

Strata - Composability First

The future of Solana is chains of primitives working together. We can model tokens, and systems of tokens, using various primitives like Bonding Curves, Fanout Wallets, CandyMachines, Governance, and Multisigs.

For example, using these primitives Grape has been able to set up a multifaceted DAO with SubDAOs:

System of Composable Tokens - Grape

With systems like this, the question shifts from "How do we develop an individual smart contract?" to "How can we compose and orchestrate existing primitives?".

Interested in Learning More?

· 7 min read
Noah Prince

In this article, we'll cover how to create a Solana token on Strata. Launching your own Solana token can be daunting. There are a lot of considerations, and we’ll try to cover those as well.

First, what does it mean to “Create” a token? Creating a Solana token tends to come in 3 steps:

  1. Creating the token. How do you actually create a Solana token that will show up in wallets like Phantom, Slope, and Solflare?
  2. Distribution. How do you get this token out to the community? How do you sell an initial supply to your community?
  3. Liquidity. How does your community continually have the ability to buy and sell this token?

How to Create a Solana Token Easy Mode - Fully Managed Tokens

Let’s say you do not want to worry about steps 1 through 3. You just want a token that people can buy and sell right now. When users put SOL in, they get the token out. You will not manage the token supply or pricing, and will instead take a royalty on sales (similar to an NFT). If you would like to create a token where you manage your own supply, you can skip to Creating a Token

For this article, I’m going to create a Solana Token called NOAH. Since it’s a small token, there’s no guarantee that there will be a counterparty to any trades. Instead, by using a Strata Fully Managed token, the protocol will be the counterparty to every trade.

Head over to the Strata Launchpad. Select “Create a Token”

Create a Solana Token

Select Fully Managed:

Fully Managed Solana Token

Fill out the name, description, and photo for this token:

Fully Managed Token Information

Then, select a price sensitivity. Utility is generally a good place to start.

Price Sensitivity

Next, I'm indicating that this is a Social Token. I want this token to link to my wallet, so that it can be bought and sold in apps like Wum.bo. We’ll use SOL to price this token. When users put in SOL, they’ll get NOAH out. If this token isn't associated with a person, project, dao, it's fine to leave this toggle off.

Since I have set the royalties to 5% NOAH on Buy, I will get 5% of every NOAH token sale. If there are 100 NOAH tokens minted via the protocol, I will have received 5. I can also take SOL on buy/sell.

Fully managed royalties/info

Done! You now have a tradable token! You can see this token on devnet here.

Fully managed swap form

You can skip the rest of this guide, as it only applies to tokens not having their liquidity managed by Strata.

How to Create a Solana Token - Self Managed Tokens

A self managed token is one where you decide the supply, and have full control over distribution. You will want to think about who should have this token, how it gets distributed, and the percentages/amounts for each party. You can see an example of this kind of workup here

Step 1: How to Create a Solana Token - Creating the Token

This is the easiest part. Head over to the Strata Launchpad and select Create a Token

Create a Solana Token

Select self managed:

Self managed

Fill out your token’s information like the name, symbol, image, and supply:

Self managed info

Click Create Token. Strata will create the token and you should be able to see it in your wallet.

Step 2: How to Create a Solana Token - Distribution

You have a token in your wallet, but how do you distribute it? You can send the token to people using your wallet, but what if you want to sell the token? This is called “Liquidity Bootstrapping.” In the previous step, you will have been greeted with a screen like this. If not, you can get to this screen by clicking “Sell a Token” on the launchpad.

Sell a Solana Token

Here, you have a choice. Do you want to sell the token with price discovery or for a fixed price?

Price Discovery

If you expect the token to be in high demand, this is the best option. This option lets the market and your community decide on a fair price for the token. This uses the Strata Liquidity Bootstrapping Curve (LBC), which is similar to an LBP on Ethereum. The protocol will sell the token using a process similar to a dutch auction. The price will fall over time, and increase with every purchase. This creates a price discovery, which you can read more on here.

When selling a token with price discovery, the price will float throughout the launch period. You can control the following settings

Options
  • Starting Price - The starting price when the LBC launches. The price will drop quickly in the beginning and more slowly as time goes on. We suggest you set this price at the highest price you expect the token to sell for. This is not the maximum price. If the token is in high demand, it could sell for an average price higher than the starting price.
  • Minimum Price - The absolute minimum price you would like to accept for these tokens. The price will never fall below this threshold. A minimum price that is too high may mean that the token does not sell out. This price should be within 5x of the starting price. If the starting price is 5 SOL, the minimum price should not be less than 1 SOL. The larger the difference, the faster the price will need to drop.
  • Number of Tokens - How many tokens do you want to sell in this LBC?
  • Interval - How long should this LBC go on for (in seconds)? This period is the time during which the price will fall. We recommend you set this period long enough so that everyone gets a chance to participate. Windows less than 15 minutes (900 seconds) are not recommended.
  • Launch Date - When should the LBC start?

Fixed Price

This option sells your token for a fixed price to anyone who visits the sales page. While this leads to predictable liquidity, it is also vulnerable to bots looking to buy your token and flip it on a secondary market. As a general rule of thumb, if your token is in high demand and you expect the fixed price to be less than the aftermarket price, you should use Price Discovery.

Bootstrapped Liquidity

After you finish launching your token, you’ll be able to distribute the funds from the sale back to your wallet. When using the wallet that launched this sale, you should see a form like this:

Disburse funds

Step 3: How to Create a Solana Token - Liquidity

Now you have both your token and SOL in your wallet. Your community has tokens which they have purchased. How do you enable trading on this token? The best way to enable trading is via an Automated Market Maker (AMM). The most common form of this is a Swap (think Uniswap). On Solana, you have a lot of options when it comes to swaps. We will do a more in-depth post on this step later, but for now you can look at several different options with some of the features:

Let’s say you bootstrapped $200,000 in liquidity with a finishing price of $0.50 per token. After the liquidity bootstrapping is finished, you could go create a pool on any of these services with:

  • $100,000 USDC
  • 200,000 Your token

This will start the price of the pool out at $0.50 with $100k in liquidity. You can then invite your community to provide additional liquidity on this pool.

· 10 min read
Noah Prince

Splash

Automated Governance on your Anchor Programs

Have you been deploying your programs with solana program deploy with deploy keys on your local file system? While it works for prototyping, it's not great for a production program on mainnet that's holding real money.

Local file system deploy keys are a security risk. If your computer is compromised, a hacker could take the deploy keys and do whatever they want with your program; including siphon all funds stored in or owned by the program.

Multisigs are great for simplicity, but with governance you can transfer voting shares around. You can also change the voting settings as the project evolves. It's also got a nifty UI!

In addition to the security risks of deploying locally, there's also several risks associated with manually deploying to mainnet, including:

  • Accidentally deploying dev code to mainnet because you ran the wrong command
  • Updating the program but not the IDL
  • Forgetting to publish a verified build
  • Inconsistency with your git repository and what's deployed, making debugging difficult.

Let's instead set up automation so we get something like this:

Governance on Mainnet

By the end of this guide, we'll set up automation such that when you issue a release on github, you'll get a Governance proposal to deploy the contract. All you need to do is vote yes on the proposal, then execute the transaction. For devnet, you'll get a new proposal every time the rust code changes on master.

note

Do this in devnet before you try it on mainnet

In this guide, we're going to:

  • Issue governance voting shares
  • Setup SPL Governance around an Anchor Program
  • Deploying a program using governance without CI/CD
  • Automate program deployments using CI/CD
  • Setup Anchor Verifiable Builds

Issue Govenance Voting Shares

Governance works by creating and executing proposals to upgrade the program. These proposals are voted on by governance token holders. In a simple case, the governance token holders may just be the founders of the company. If all hold an even number of tokens, this acts like a multisig.

Let's issue a governance token. We're going to use Metaplex spl-token-metadata to asssociate a name and symbol with our governance token, so it's easy to use in wallets. You can edit the name and symbol in createMetadata to name your own token.

import { ASSOCIATED_TOKEN_PROGRAM_ID, Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
import { Connection, Keypair, sendAndConfirmTransaction, SystemProgram, Transaction } from '@solana/web3.js';
import { createMetadata, Data } from "@strata-foundation/spl-utils";
Loading...

Take note of the mint address above. Mine was "65uCXAukbwXFMUdT75SjmBfr6HhFL4h17QtzsM5MdmLD"

Change your wallet network to devnet. If you check your wallet, you should see our test governance token. After you're done with this guide, you can send it to anyone you'd like.

Wallet

Setup SPL Governance

First, to setup governance we must have deployed the anchor program once. If you haven't yet, run

anchor build
solana program deploy ./target/deploy/<YOUR_PROGRAM>.so -u devnet

You'll also want to init the idl:

anchor idl init <YOUR_PROGRAM_ADDRESS> --filepath target/idl/<YOUR_PROGRAM>.json --provider.cluster devnet

Since you likely deployed with a local deploy key, you will want to temporarily transfer the authority to your current web wallet.

caution

This command cannot be undone without the new upgrade authority signing off. Make absolute sure your wallet address is correct here.

solana program set-upgrade-authority -u devnet <PROGRAM_ADDRESS> --new-upgrade-authority <YOUR_WALLET_ADDRESS>

Now we will need to create a realm. Navigate to: https://realms.today/realms?cluster=devnet

Once you connect your wallet, you should be able to click Create Realm:

Add Realm

You'll want to choose the second option, as bespoke realm:

Bespoke Realm

Fill out this form with your mint from earlier as the community mint id:

Realm Form

You should be taken to a new page. This is your realm page. Bookmark this.

Deposit your governance tokens using the deposit button

Now, we're going to add our program. Click the plus button next to assets to add a program asset:

Add Asset

Fill out your program information. You can also fill out voting threshold information:

Create New Program Governance

Deploy Your First Update

Deploying with governance works in two steps. First, we'll write a buffer to Solana. Then, we'll create a Proposal to deploy that buffer. This allows us to separate building from signing to deploy.

solana program write-buffer /path/to/your/deploy/contract.so -u devnet

This write buffer will be owned by you, so let's transfer it to your governance. To get the governance id, click your program under "Assets" then click Upgrade:

Upgrade

You should see Upgrade Authority. Copy that to clipboard

Gov ID

solana program set-buffer-authority <YOUR_BUFFER_FROM_ABOVE> --new-buffer-authority <GOVERNANCE_ID_FROM_CLIPBOARD> -u devnet

Now that we've deployed our buffer, let's propose an upgrade to our contract. Input your buffer from above into the upgrade form

Upgrade Form

Once you've created your proposal, you will be able to vote on it like so:

Vote Yes

Once the vote passes, click on the instructions drop down and scroll down to execute:

Execute

Done!! Now you've succesfully deployed your first program using SPL Governance!

CI/CD - Automation

If you're like me, you're probably thinking: "this could use some automation."

Yup. Let's set this up.

First, let's transfer the IDL to governance:

caution

This command cannot be undone without the new authority signing off. Make absolute sure the governance address is correct here

Gov ID

Make sure you're using this address, and double check it in the explorer to make sure it's owned by the governance program.

anchor idl set-authority --provider.cluster devnet --program-id <PROGRAM_ADDRESS> --new-authority <GOVERNANCE_ID>

Secrets

We'll need two github secrets

  • DEPLOY_KEYPAIR - A keypair that we will use to deploy buffers. This won't have permission to change the program, but we will need to load it with SOL to write the buffers.
  • ANCHOR_TOKEN - The authentication token for anchor.projectserum.com

To add a secret, navigate to Settings > Secrets in github:

Creating a Secret

First, head over to https://anchor.projectserum.com/ and sign up for an account. This is where we'll post our verified builds.

Click your Profile in the top right > Account Settings > New Token. Create a token for CI/CD. It will say:

anchor login <ANCHOR_TOKEN>

Copy that down. Fill that in for ANCHOR_TOKEN

Next, let's generate a keypair for deploys:

solana-keygen new -o deploy.json

Now cat that file and copy its contents and fill that in for DEPLOY_KEYPAIR

cat deploy.json

Deploy Wallet

We need to set this deploy wallet up with permissions to create proposals and write buffers. First, it will need some SOL:

Get the address:

solana address -k deploy.json

Now airdrop some sol to it (run this a few times). On mainnet, you will need to fund this wallet:

solana airdrop -u devnet 2 <DEPLOY_ADDRESS>

You may want to store the deploy key somewhere safe. Now delete that file so it cannot be compromised:

rm deploy.json

Now, go back to the govenance UI and withdraw your tokens. You will need to send 1% of them to the deploy wallet so that it can create proposals.

Now, we need to deposit those voting tokens. You can add the deploy to your wallet in phantom via "Add / Connect Wallet" > "Import private key". Re-visit your realm page using this wallet and deposit the tokens.

Github Actions

You'll want to setup github actions for your repo. Strata has two main workflows relating to this:

  • Devnet Proposal - When a commit is pushed to master, if the rust contracts have changed, create a proposal to devnet governance to release the new version
  • Mainnet Proposal - When a github release happens, if the rust contracts have changed, create a proposal to mainnet governance to release the new version

To get these in your repo, you'll want to clone https://github.com/StrataFoundation/strata

You will need to copy and edit the variables in:

  • .github/workflows/devnet-proposal.yaml - When a commit is pushed to master, if the rust contracts have changed, create a proposal to devnet governance to release the new version. Make sure to set governance to the dev governance address we got from the Upgrade Form.
  • .github/workflows/mainnet-proposal.yaml - When a github release happens, if the rust contracts have changed, create a proposal to mainnet governance to release the new version. Make sure to set governance to a production governance, you will need to follow the above steps on mainnet after you verify it works on devnet.

In particular, make sure there's an entry for each program, and that you set program, program-id, network, keypair, governance, name, description. If signatory is set, that account must "sign off" on the proposal to get it out of draft stage.

These workflows rely on some actions that you will probably not need to edit, but will need to copy:

  • .github/actions/anchor-publish - Action that publishes your anchor contract to Anchor Verified Builds.
    • If you do not want to do this, simply comment out this action in .github/workflows/mainnet-proposal.yaml
  • .github/actions/upload-bpf - Action that builds your smart contract and uploads it to solana via write-buffer
  • .github/actions/create-proposal - Uses a script we wrote to create a governance proposal to set your program to the one deployed via upload-bpf
  • .github/actions/deploy-with-gov-proposal - Execute upload-bpf then create-proposal, only if a proposal hasn't been created for this action.
  • .github/actions/setup - General setup
  • .github/actions/setup-anchor - Setting up anchor cli
  • .github/actions/setup-solana - Setting up solana cli

Test It

Create a fork of your repository. These hooks only run on the master branch, so you'll want to use a forked version of your repo until you get them stable.

Add the secrets to this fork, then push these actions to master. Go to the "Actions" tab on github and watch them go:

Actions

If it succeeds, you should be able to go to your realm and see the proposal!

Vote in Progress

Congrats! You're now running program governance on auto-pilot!

To test mainnet proposals, click the releases button on the side of your github repo. Draft a new release and publish it. Then go to the actions tab and make sure it runs successfuly.

Stuck?

There's a lot of steps in this process, it's easy for a misstep. If you're having trouble debugging, you can come join us in the Strata discord and we'll do our best to help you out:

Want to see more like this?

We'll be posting tips and tricks as we solve problems ourselves. If you're interested in launching a token or token(s), let us know! We can help with that too!