Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ types = "packages/client/src/types"
seeds = false
skip-lint = false
[programs.localnet]
sunrise_stake = "sunzv8N3A8dRHwUBvxgRDEbWKk8t7yiHR4FLRgFsTX6"
gsol_mint_manager = "sgmdmefVZeTCzNFaxaUyZxG5HvBjU8VYVc1DR151iXb"
marinade_liquidity_pool_beam = "sbm1g72BpYd3pPdHtwna8xogFhYLcCKdUKricTGwibF"
marinade_stake_pool_beam = "sbm2EQPK9SDWFykkFqUhTV1t1ng2pGgSXESoyXQ5HMF"
spl_stake_pool_beam = "sbm34B5emb8AYFSsuUL9h24f6dGVyjLP39ehELjxd6D"

[registry]
url = "https://api.apr.dev"
Expand Down
73 changes: 73 additions & 0 deletions programs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Sunrise Programs

The Sunrise protocol is made up the following Solana programs:

- gSOL Mint Manager
- Sunbeams:
- SPL Stake Pool Beam
- Marinade Stake Pool Beam
- Marinade Liquidity Pool Beam
- [TODO] Lock & Mint program (?)

## GSol Mint Manager

The gSOL Mint Manager is a program that manages the gSOL token mint. It is responsible for:
- Managing overall sunrise state
- Approving Sunbeams (see below)
- Minting gSOL tokens when users deposit into an approved sunbeam
- Burning gSOL tokens when users withdraw from an approved sunbeam
- Redeeming order-unstake tickets or undelegated stake pools after their cooldown periods

## Sunbeams

### Introduction

Sunbeams are sources of yield for the Sunrise protocol. Each sunbeam proxies some yield source, such as
a stake pool or liquidity pool, and routes accrued yield to a designated yield account, as determined by
the gSOL Mint Manager state.

Users depositing in a sunbeam earn gSOL tokens, minted by the gSOL Mint Manager. gSOL is a
synthetic derivative of SOL, which is redeemable for SOL at a 1:1 ratio. Users can redeem their gSOL
by withdrawing from any sunbeam. However, the gSOL mint manager is responsible for ensuring that
sunbeams remain balanced, and will only allow withdrawals from or deposits to sunbeams that are
not over or under their target balances.

### Withdrawal

While all sunbeams are roughly equivalent when depositing (i.e. they have a similar API),
they differ when withdrawing. Withdrawals from some pools are immediate, but withdrawals from
others have a cooldown period. Additionally, some pools have higher withdrawal fees than others.

Each sunbeam has its own client, and the overall sunrise client is responsible for determining
which sunbeam to withdraw from. The client will first attempt to withdraw from sunbeams with the lowest
fees.

### Sunbeam Types

#### SPL Stake Pool Beam

The SPL Stake Pool beam proxies the SPL Stake Pool program. SPL Stake Pools do not support liquid withdrawal,
and has variable fees, depending on the configuration of the pool.

[TODO] Consider adding unstake.it to this beam to support liquid unstake

#### Marinade Stake Pool Beam

The Marinade Stake Pool beam proxies the Marinade Stake Pool program.
Marinade has support for liquid withdrawal, but charges a high fee (3%).

#### Marinade Liquidity Pool Beam

The Marinade Liquidity Pool beam proxies the Marinade Liquidity Pool, which is an unbalanced mSOL/SOL AMM.

The Marinade program uses this pool to support liquid withdrawal from the Marinade Stake Pool.
It is unbalanced, because the Marinade protocol aims to keep the mSOL balance in the pool to zero.
When users deposit into the stake pool, mSOL is first issued from the liquidity pool, until the mSOL leg
is exhausted, after which new mSOL is minted.

When users liquid withdraw from the stake pool, mSOL is exchanged for SOL in the liquidity pool.
Liquidity providers can deposit SOL into the liquidity pool to earn yield on this swap. Withdrawing liquidity
from this pool has zero fee, unlike the stake pool, however liquidity providers will receive a mix of SOL and
mSOL, depending on the current balance of the pool.

Sunrise uses this liquidity pool in order to allow zero-fee liquid withdrawals up to a certain amount.
19 changes: 19 additions & 0 deletions programs/beams/marinade-liquidity-pool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "marinade-liquidity-pool-beam"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "marinade_liquidity_pool_beam"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = "0.26.0"
2 changes: 2 additions & 0 deletions programs/beams/marinade-liquidity-pool/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
126 changes: 126 additions & 0 deletions programs/beams/marinade-liquidity-pool/src/instructions/deposit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/// TODO PLACEHOLDER ONLY - copied from the legacy program

use crate::state::State;
use crate::utils::seeds::{GSOL_MINT_AUTHORITY, MSOL_ACCOUNT};
use crate::utils::token::mint_to;
use crate::utils::{marinade, marinade::amount_to_be_deposited_in_liq_pool};
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program_option::COption;
use anchor_spl::token::{Mint, Token, TokenAccount};
use marinade_cpi::program::MarinadeFinance;
use marinade_cpi::State as MarinadeState;
use std::ops::Deref;

#[derive(Accounts, Clone)]
pub struct Deposit<'info> {
#[account(mut,has_one = marinade_state)]
pub beam_state: Box<Account<'info, State>>,

#[account(mut)]
pub marinade_state: Box<Account<'info, MarinadeState>>,

#[account(
mut,
constraint = gsol_mint.mint_authority == COption::Some(gsol_mint_authority.key()),
)]
pub gsol_mint: Box<Account<'info, Mint>>,

#[account(
seeds = [
state.key().as_ref(),
GSOL_MINT_AUTHORITY,
],
bump = state.gsol_mint_authority_bump,
)]
pub gsol_mint_authority: SystemAccount<'info>,

#[account(mut)]
pub msol_mint: Box<Account<'info, Mint>>,

#[account(mut)]
pub liq_pool_mint: Box<Account<'info, Mint>>,

#[account(mut)]
/// CHECK: Checked in marinade program
pub liq_pool_sol_leg_pda: UncheckedAccount<'info>,

#[account(mut)]
pub liq_pool_msol_leg: Box<Account<'info, TokenAccount>>,
/// CHECK: Checked in marinade program
pub liq_pool_msol_leg_authority: UncheckedAccount<'info>,

/// CHECK: Checked in marinade program
pub liq_pool_mint_authority: UncheckedAccount<'info>,

#[account(mut)]
/// CHECK: Checked in marinade program
pub reserve_pda: AccountInfo<'info>,

#[account(mut)]
/// CHECK: Checked in marinade program
pub transfer_from: Signer<'info>,

#[account(
mut,
token::mint = msol_mint,
token::authority = msol_token_account_authority,
)]
pub mint_msol_to: Account<'info, TokenAccount>,

#[account(
mut,
token::mint = liq_pool_mint,
token::authority = msol_token_account_authority,
)]
pub mint_liq_pool_to: Box<Account<'info, TokenAccount>>,

#[account(
mut,
token::mint = gsol_mint,
token::authority = transfer_from.key(),
)]
pub mint_gsol_to: Account<'info, TokenAccount>,

/// CHECK: Checked in marinade program
pub msol_mint_authority: AccountInfo<'info>,

#[account(
seeds = [state.key().as_ref(), MSOL_ACCOUNT],
bump = state.msol_authority_bump
)]
pub msol_token_account_authority: SystemAccount<'info>,

pub system_program: Program<'info, System>,
pub token_program: Program<'info, Token>,
pub marinade_program: Program<'info, MarinadeFinance>,
}

pub fn deposit_handler(ctx: Context<Deposit>, lamports: u64) -> Result<()> {
msg!("Checking liq_pool pool balance");
let to_deposit_in_liq_pool = amount_to_be_deposited_in_liq_pool(ctx.accounts, lamports)?;
let to_stake = lamports - to_deposit_in_liq_pool;

if to_deposit_in_liq_pool > 0 {
msg!("Depositing {} in liq_pool pool", to_deposit_in_liq_pool);
let accounts = ctx.accounts.deref().into();
marinade::add_liquidity(&accounts, to_deposit_in_liq_pool)?;
}

if to_stake > 0 {
msg!("Staking {}", to_stake);
marinade::deposit(ctx.accounts, to_stake)?;
}

msg!("Mint {} GSOL", lamports);
mint_to(
lamports,
&ctx.accounts.gsol_mint.to_account_info(),
&ctx.accounts.gsol_mint_authority.to_account_info(),
&ctx.accounts.mint_gsol_to.to_account_info(),
&ctx.accounts.token_program.to_account_info(),
&ctx.accounts.state,
)?;
let state = &mut ctx.accounts.state;
state.marinade_minted_gsol = state.marinade_minted_gsol.checked_add(lamports).unwrap();
Ok(())
}
15 changes: 15 additions & 0 deletions programs/beams/marinade-liquidity-pool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use anchor_lang::prelude::*;

declare_id!("sbm1g72BpYd3pPdHtwna8xogFhYLcCKdUKricTGwibF");

#[program]
pub mod marinade_liquidity_pool_beam {
use super::*;

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}

#[derive(Accounts)]
pub struct Initialize {}
19 changes: 19 additions & 0 deletions programs/beams/marinade-stake-pool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "marinade-stake-pool-beam"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "marinade_stake_pool_beam"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = "0.26.0"
2 changes: 2 additions & 0 deletions programs/beams/marinade-stake-pool/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
15 changes: 15 additions & 0 deletions programs/beams/marinade-stake-pool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use anchor_lang::prelude::*;

declare_id!("sbm2EQPK9SDWFykkFqUhTV1t1ng2pGgSXESoyXQ5HMF");

#[program]
pub mod marinade_stake_pool_beam {
use super::*;

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}

#[derive(Accounts)]
pub struct Initialize {}
19 changes: 19 additions & 0 deletions programs/beams/spl-stake-pool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "spl-stake-pool-beam"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "spl_stake_pool_beam"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = "0.26.0"
2 changes: 2 additions & 0 deletions programs/beams/spl-stake-pool/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
15 changes: 15 additions & 0 deletions programs/beams/spl-stake-pool/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use anchor_lang::prelude::*;

declare_id!("sbm34B5emb8AYFSsuUL9h24f6dGVyjLP39ehELjxd6D");

#[program]
pub mod spl_stake_pool_beam {
use super::*;

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}

#[derive(Accounts)]
pub struct Initialize {}
19 changes: 19 additions & 0 deletions programs/gsol-mint-manager/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "gsol-mint-manager"
version = "0.1.0"
description = "Created with Anchor"
edition = "2021"

[lib]
crate-type = ["cdylib", "lib"]
name = "gsol_mint_manager"

[features]
no-entrypoint = []
no-idl = []
no-log-ix-name = []
cpi = ["no-entrypoint"]
default = []

[dependencies]
anchor-lang = "0.26.0"
2 changes: 2 additions & 0 deletions programs/gsol-mint-manager/Xargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []
15 changes: 15 additions & 0 deletions programs/gsol-mint-manager/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use anchor_lang::prelude::*;

declare_id!("sgmdmefVZeTCzNFaxaUyZxG5HvBjU8VYVc1DR151iXb");

#[program]
pub mod gsol_mint_manager {
use super::*;

pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}

#[derive(Accounts)]
pub struct Initialize {}