Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Staked collateral #248

Merged
merged 59 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
2fd6e2c
More anchor e2e happy path tests
jgur-psyops Aug 30, 2024
74384fe
Add PyUSD bank (with emissions) to test
jgur-psyops Sep 4, 2024
4065aea
Boilerplate for staking collatizer program
jgur-psyops Sep 6, 2024
9b4f5aa
WIP decoding stake structs manually
jgur-psyops Sep 7, 2024
d16dd06
Proof of concept for warping stake status ahead with bankrun
jgur-psyops Sep 8, 2024
2f12095
Stake holding account and basic CPI structure
jgur-psyops Sep 10, 2024
ae17eb8
WIP deposit stake ix PoC
jgur-psyops Sep 11, 2024
dc6f55a
WIP integrate spl-single-pool
jgur-psyops Sep 12, 2024
ef6c5a2
Strip out collatizer program
jgur-psyops Sep 13, 2024
b37bc26
WIP rewrite spl single token deposit
jgur-psyops Sep 13, 2024
ac09ed9
Slot warping wizard magic
jgur-psyops Sep 13, 2024
79669f8
Finalized slot warping magic to end the rewards period
jgur-psyops Sep 14, 2024
951af44
PoC of deposit of stake to an spl single pool
jgur-psyops Sep 18, 2024
d4bd1f7
Merge branch 'anchor-test-cont' into staking-collat-init
jgur-psyops Sep 18, 2024
04856e3
Merge with additional anchor tests in progress
jgur-psyops Sep 18, 2024
011a172
Merge branch 'pyusd-bank-test' into staking-collat-init
jgur-psyops Sep 18, 2024
4b08d98
user init and deposit happy paths
jgur-psyops Sep 18, 2024
e406b96
Tests for deposit/borrow complete, now bypasses oracle age checks in …
jgur-psyops Sep 19, 2024
88b68dc
WIP add asset tag to banks
jgur-psyops Sep 19, 2024
364034c
Complete asset tag feature, validate that Staked accounts cannot comi…
jgur-psyops Sep 20, 2024
4489f7c
Updated readme
jgur-psyops Sep 20, 2024
4751da9
Updated anchor.toml to run the full test suite. All tests pass
jgur-psyops Sep 20, 2024
8249881
Fix CI attempt 1
jgur-psyops Sep 24, 2024
530deb1
Fix CI attempt 2
jgur-psyops Sep 24, 2024
8a4261d
Fix CI attempt 3
jgur-psyops Sep 24, 2024
1c7dd35
Fix CI attempt 4, add boilerplate for sol price appreciation as LST g…
jgur-psyops Sep 24, 2024
5c7261e
Fix CI attempt 5, add instruction to cache increases in LST value due…
jgur-psyops Sep 24, 2024
3e0a0bd
Now uses SOL price appreciation when determining the value of staked …
jgur-psyops Sep 24, 2024
fc853ed
Fix CI attempt 6
jgur-psyops Sep 24, 2024
2f1e1ca
Fix CI attempt 7
jgur-psyops Sep 24, 2024
1c1d22d
Fix CI attempt 8
jgur-psyops Sep 24, 2024
88b3f5c
Fix CI attempt 9
jgur-psyops Sep 24, 2024
73e74ce
Fix CI attempt 10
jgur-psyops Sep 25, 2024
62698c4
Resolve merge with main, all tests pass
jgur-psyops Oct 21, 2024
4d42c32
Merge branch 'main' into staking-collat-init
jgur-psyops Oct 23, 2024
1128807
Resolve merge with main, add test of origination fees to TS suite
jgur-psyops Oct 23, 2024
3338852
Kludge for lint issue
jgur-psyops Oct 25, 2024
94e2a29
Begin permissionless add pool buildout. Removing the sol_ex_rate cach…
jgur-psyops Oct 28, 2024
414f18a
TODO for propagate
jgur-psyops Oct 29, 2024
16c98f5
Boilerplate to init staked banks with group-level default settings
jgur-psyops Oct 29, 2024
b5e0b94
Boilerplate tests for init/edit satked settings
jgur-psyops Oct 30, 2024
07d2f3d
Permissionless pool init with default settings happy path test
jgur-psyops Oct 30, 2024
64b9f00
Unhappy path tests for permissionless account validation
jgur-psyops Oct 31, 2024
8107742
Happy path validation for permissionless bank creation
jgur-psyops Oct 31, 2024
4eece77
Fix Pyth push regression
jgur-psyops Oct 31, 2024
99e0768
Borrow against staked assets functions as intended, adding sol apprec…
jgur-psyops Oct 31, 2024
8c5d59b
Tests for propagation
jgur-psyops Oct 31, 2024
f711b76
Fix lint 1
jgur-psyops Nov 1, 2024
259fd83
Consolidate validation functions, remove appreciation rate
jgur-psyops Nov 7, 2024
3a36bb8
Macro for checking if program is a localnet build or not
jgur-psyops Nov 7, 2024
60e02ae
Fix lint 1
jgur-psyops Nov 7, 2024
e6fac42
Various fixes
jgur-psyops Dec 10, 2024
d6e7a1a
Add test for fix
jgur-psyops Dec 10, 2024
85b3cd0
Fix CI pipeline 1
jgur-psyops Dec 10, 2024
989f21c
Merge pull request #105 from mrgnlabs/staked-collat-fixes
jgur-psyops Dec 12, 2024
907fa7e
Reconcile merge with main
jgur-psyops Dec 12, 2024
2074ff6
Merge branch 'main' into staking-collat-init
jgur-psyops Dec 23, 2024
de33b65
Reconcile merge with main 1
jgur-psyops Dec 23, 2024
7d5fe37
Reconcile merge with main 2
jgur-psyops Dec 23, 2024
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
94 changes: 60 additions & 34 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,45 +115,71 @@ jobs:
- name: Pass after fuzzing
run: echo "Fuzzing completed"

# localnet-test-marginfi:
# name: Anchor localnet tests marginfi
# runs-on: ubuntu-latest

# steps:
# - uses: actions/checkout@v3

# - uses: ./.github/actions/setup-common/
# - uses: ./.github/actions/setup-anchor-cli/

# - uses: ./.github/actions/build-workspace/

# - name: Install Node.js dependencies
# run: yarn install

# - name: Build marginfi program
# run: anchor build -p marginfi -- --no-default-features
localnet-test-marginfi:
name: Anchor localnet tests marginfi
runs-on: ubuntu-latest

# - name: Build liquidity incentive program
# run: anchor build -p liquidity_incentive_program -- --no-default-features
steps:
- uses: actions/checkout@v3

# - name: Build mocks program
# run: anchor build -p mocks
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "20.10.0"

# - name: Start Solana Test Validator
# run: |
# solana-test-validator --reset --limit-ledger-size 1000 \
- uses: ./.github/actions/setup-common/
- uses: ./.github/actions/setup-anchor-cli/

# - name: Wait for Validator to Start
# run: sleep 60
- uses: ./.github/actions/build-workspace/

# - name: Deploy Liquidity Incentive Program
# run: solana program deploy --program-id Lip1111111111111111111111111111111111111111 target/deploy/liquidity_incentive_program.so
- name: Install Node.js dependencies
run: yarn install

# - name: Deploy Marginfi Program
# run: solana program deploy --program-id 2jGhuVUuy3umdzByFx8sNWUAaf5vaeuDm78RDPEnhrMr target/deploy/marginfi.so
- name: Build marginfi program
run: anchor build -p marginfi -- --no-default-features

# - name: Deploy Mocks Program
# run: solana program deploy --program-id 5XaaR94jBubdbrRrNW7DtRvZeWvLhSHkEGU3jHTEXV3C target/deploy/mocks.so
- name: Build mocks program
run: anchor build -p mocks

# - name: Run tests
# run: anchor test --skip-build --skip-local-validator
# Handles extraneous (os error 2) that appears during testing in some versions of solana. See:
# https://solana.stackexchange.com/questions/1648/error-no-such-file-or-directory-os-error-2-error-from-anchor-test
- name: Run Anchor tests
run: |
set +e
anchor test --skip-build 2>&1 | tee test_output.log
ANCHOR_EXIT_CODE=$?
set -e

if grep -q "failing" test_output.log; then
echo "Real test failure detected."
exit 1
fi

if grep -q "No such file or directory (os error 2)" test_output.log; then
echo "Extraneous error detected, ignoring it..."
exit 0
fi

if [ $ANCHOR_EXIT_CODE -ne 0 ]; then
echo "Anchor test exited with code $ANCHOR_EXIT_CODE due to an unexpected error."
exit 1
else
echo "Test run completed successfully without extraneous errors."
exit 0
fi

# - name: Start Solana Test Validator
# run: |
# solana-test-validator --reset --limit-ledger-size 1000 \

# - name: Wait for Validator to Start
# run: sleep 60

# - name: Deploy Liquidity Incentive Program
# run: solana program deploy --program-id Lip1111111111111111111111111111111111111111 target/deploy/liquidity_incentive_program.so

# - name: Deploy Marginfi Program
# run: solana program deploy --program-id 2jGhuVUuy3umdzByFx8sNWUAaf5vaeuDm78RDPEnhrMr target/deploy/marginfi.so

# - name: Deploy Mocks Program
# run: solana program deploy --program-id 5XaaR94jBubdbrRrNW7DtRvZeWvLhSHkEGU3jHTEXV3C target/deploy/mocks.so
24 changes: 19 additions & 5 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ resolution = true
skip-lint = false

[programs.localnet]
liquidity_incentive_program = "Lip1111111111111111111111111111111111111111"
# liquidity_incentive_program = "Lip1111111111111111111111111111111111111111"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No comments in main

marginfi = "2jGhuVUuy3umdzByFx8sNWUAaf5vaeuDm78RDPEnhrMr"
mocks = "5XaaR94jBubdbrRrNW7DtRvZeWvLhSHkEGU3jHTEXV3C"
spl_single_pool = "SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE" # cloned from solana-labs repo (see below)

[programs.mainnet]
liquidity_incentive_program = "LipsxuAkFkwa4RKNzn51wAsW7Dedzt1RNHMkTkDEZUW"
Expand All @@ -19,15 +20,18 @@ marginfi = "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA"
url = "https://api.apr.dev"

[provider]
cluster = "localnet"
# cluster = "https://devnet.rpcpool.com/"
cluster = "Localnet"
wallet = "~/.config/solana/id.json"

# (remove RUST_LOG= to see bankRun logs)
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/*.spec.ts --exit --require tests/rootHooks.ts"
test = "RUST_LOG= yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/*.spec.ts --exit --require tests/rootHooks.ts"

# Staked collateral tests only
# test = "RUST_LOG= yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/s*.spec.ts --exit --require tests/rootHooks.ts"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No comments in main

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any alternative location where they can stay? As the TS suite grows it can take several minutes to run and it's nice to have a documented method to run a portion of the suite. Most programs do not support running a portion of anchor tests, so generally people would be unfamiliar with that option unless it's documented.


[test]
startup_wait = 5000
startup_wait = 60000
shutdown_wait = 2000
upgradeable = false

Expand All @@ -44,6 +48,16 @@ filename = "tests/fixtures/bonk_bank.json"
address = "4kNXetv8hSv9PzvzPZzEs1CTH6ARRRi2b8h6jk1ad1nP"
filename = "tests/fixtures/cloud_bank.json"

[[test.validator.account]]
address = "Fe5QkKPVAh629UPP5aJ8sDZu8HTfe6M26jDQkKyXVhoA"
filename = "tests/fixtures/pyusd_bank.json"

[[test.validator.account]]
address = "8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN"
filename = "tests/fixtures/localnet_usdc.json"

# To update:
# clone https://github.com/solana-labs/solana-program-library/tree/master and run cargo build-sbf in spl_single_pool
[[test.genesis]]
address = "SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE" # spl single pool program
program = "tests/fixtures/spl_single_pool.so"
5 changes: 5 additions & 0 deletions clients/rust/marginfi-cli/src/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ impl From<BankOperationalStateArg> for BankOperationalState {
}
}

#[allow(clippy::large_enum_variant)]
#[derive(Debug, Parser)]
pub enum BankCommand {
Get {
Expand Down Expand Up @@ -261,6 +262,8 @@ pub enum BankCommand {
pf_or: Option<f64>,
#[clap(long, arg_enum, help = "Bank risk tier")]
risk_tier: Option<RiskTierArg>,
#[clap(long, help = "0 = default, 1 = SOL, 2 = Staked SOL LST")]
asset_tag: Option<u8>,
#[clap(long, arg_enum, help = "Bank oracle type")]
oracle_type: Option<OracleTypeArg>,
#[clap(long, help = "Bank oracle account")]
Expand Down Expand Up @@ -673,6 +676,7 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> {
pf_ir,
pf_or,
risk_tier,
asset_tag,
oracle_type,
oracle_key,
usd_init_limit,
Expand Down Expand Up @@ -724,6 +728,7 @@ fn bank(subcmd: BankCommand, global_options: &GlobalOptions) -> Result<()> {
protocol_origination_fee: pf_or.map(|x| I80F48::from_num(x).into()),
}),
risk_tier: risk_tier.map(|x| x.into()),
asset_tag,
total_asset_value_init_limit: usd_init_limit,
oracle_max_age,
permissionless_bad_debt_settlement,
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@
"@coral-xyz/spl-token": "^0.30.1",
"@solana/spl-token": "^0.4.8",
"@solana/web3.js": "^1.95.2",
"@solana/spl-single-pool-classic": "^1.0.2",
"@mrgnlabs/mrgn-common": "^1.7.0",
"@mrgnlabs/marginfi-client-v2": "^3.1.0",
"mocha": "^10.2.0",
"ts-mocha": "^10.0.0",
"bignumber.js": "^9.1.2"
},
"devDependencies": {
"anchor-bankrun": "^0.4.0",
"solana-bankrun": "^0.3.0",
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"prettier": "^2.6.2",
"ts-node": "^10.9.1",
"typescript": "^4.3.5"
"typescript": "^4.3.5",
"big.js": "^6.2.1"
}
}
7 changes: 7 additions & 0 deletions programs/brick/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ pub mod brick {
) -> Result<()> {
Err(ErrorCode::ProgramDisabled.into())
}

pub fn initialize(_ctx: Context<Initialize>, _val: u64) -> Result<()> {
Ok(())
}
jgur-psyops marked this conversation as resolved.
Show resolved Hide resolved
}

#[error_code]
pub enum ErrorCode {
#[msg("This program is temporarily disabled.")]
ProgramDisabled,
}

#[derive(Accounts)]
pub struct Initialize {}
18 changes: 18 additions & 0 deletions programs/marginfi/fuzz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,21 @@ Before the invoke we also copy to a local cache and revert the state if the inst
### Actions

The framework uses the arbitrary library to generate a random sequence of actions that are then processed on the same state.

### How to Run

Run `python3 ./generate_corpus.py`. You may use python if you don't have python3 installed, or you may need to install python.

Build with `cargo build`.

If this fails, you probably need to update your Rust toolchain:

`rustup install nightly-2024-06-05`

And possibly:

`rustup component add rust-src --toolchain nightly-2024-06-05-x86_64-unknown-linux-gnu`

Run with `cargo +nightly-2024-06-05 fuzz run lend -Zbuild-std --strip-dead-code --no-cfg-fuzzing -- -max_total_time=300`

To rerun some tests after a failure: `cargo +nightly-2024-06-05 fuzz run -Zbuild-std lend artifacts/lend/crash-ae5084b9433152babdaf7dcd75781eacd7ea55c7`, replacing the hash after crash- with the one you see in the terminal.
23 changes: 23 additions & 0 deletions programs/marginfi/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub const INSURANCE_VAULT_SEED: &str = "insurance_vault";
pub const FEE_VAULT_SEED: &str = "fee_vault";

pub const FEE_STATE_SEED: &str = "feestate";
pub const STAKED_SETTINGS_SEED: &str = "staked_settings";

pub const EMISSIONS_AUTH_SEED: &str = "emissions_auth_seed";
pub const EMISSIONS_TOKEN_ACCOUNT_SEED: &str = "emissions_token_account_seed";
Expand All @@ -28,6 +29,17 @@ cfg_if::cfg_if! {
}
}

// TODO update to the actual deployment key on mainnet/devnet/staging
cfg_if::cfg_if! {
if #[cfg(feature = "devnet")] {
pub const SPL_SINGLE_POOL_ID: Pubkey = pubkey!("SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE");
} else if #[cfg(any(feature = "mainnet-beta", feature = "staging"))] {
pub const SPL_SINGLE_POOL_ID: Pubkey = pubkey!("SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE");
} else {
pub const SPL_SINGLE_POOL_ID: Pubkey = pubkey!("SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE");
}
}

cfg_if::cfg_if! {
if #[cfg(feature = "devnet")] {
pub const SWITCHBOARD_PULL_ID: Pubkey = pubkey!("Aio4gaXjXzJNVLtzwtNVmSqGKpANtXhybbkhtAC94ji2");
Expand All @@ -36,6 +48,8 @@ cfg_if::cfg_if! {
}
}

pub const NATIVE_STAKE_ID: Pubkey = pubkey!("Stake11111111111111111111111111111111111111");

/// TODO: Make these variable per bank
pub const LIQUIDATION_LIQUIDATOR_FEE: I80F48 = I80F48!(0.025);
pub const LIQUIDATION_INSURANCE_FEE: I80F48 = I80F48!(0.025);
Expand Down Expand Up @@ -149,3 +163,12 @@ pub const PROTOCOL_FEE_FIXED_DEFAULT: I80F48 = I80F48!(0.01);
pub const MIN_PYTH_PUSH_VERIFICATION_LEVEL: VerificationLevel = VerificationLevel::Full;
pub const PYTH_PUSH_PYTH_SPONSORED_SHARD_ID: u16 = 0;
pub const PYTH_PUSH_MARGINFI_SPONSORED_SHARD_ID: u16 = 3301;

/// A regular asset that can be comingled with any other regular asset or with `ASSET_TAG_SOL`
pub const ASSET_TAG_DEFAULT: u8 = 0;
/// Accounts with a SOL position can comingle with **either** `ASSET_TAG_DEFAULT` or
/// `ASSET_TAG_STAKED` positions, but not both
pub const ASSET_TAG_SOL: u8 = 1;
/// Staked SOL assets. Accounts with a STAKED position can only deposit other STAKED assets or SOL
/// (`ASSET_TAG_SOL`) and can only borrow SOL (`ASSET_TAG_SOL`)
pub const ASSET_TAG_STAKED: u8 = 2;
20 changes: 13 additions & 7 deletions programs/marginfi/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub enum MarginfiError {
BankAssetCapacityExceeded,
#[msg("Invalid transfer")] // 6004
InvalidTransfer,
#[msg("Missing Pyth or Bank account")] // 6005
#[msg("Missing Oracle, Bank, LST mint, or Sol Pool")] // 6005
MissingPythOrBankAccount,
#[msg("Missing Pyth account")] // 6006
MissingPythAccount,
Expand Down Expand Up @@ -86,18 +86,24 @@ pub enum MarginfiError {
IllegalFlashloan,
#[msg("Illegal flag")] // 6041
IllegalFlag,
#[msg("Illegal balance state")] // 6043
#[msg("Illegal balance state")] // 6042
IllegalBalanceState,
#[msg("Illegal account authority transfer")] // 6044
#[msg("Illegal account authority transfer")] // 6043
IllegalAccountAuthorityTransfer,
#[msg("Unauthorized")] // 6045
#[msg("Unauthorized")] // 6044
Unauthorized,
#[msg("Invalid account authority")] // 6046
#[msg("Invalid account authority")] // 6045
IllegalAction,
#[msg("Token22 Banks require mint account as first remaining account")] // 6047
#[msg("Token22 Banks require mint account as first remaining account")] // 6046
T22MintRequired,
#[msg("Invalid ATA for global fee account")] // 6048
#[msg("Staked SOL accounts can only deposit staked assets and borrow SOL")] // 6047
AssetTagMismatch,
#[msg("Stake pool validation failed: check the stake pool, mint, or sol pool")] // 6048
StakePoolValidationFailed,
#[msg("Invalid ATA for global fee account")] // 6049
InvalidFeeAta,
#[msg("Use add pool permissionless instead")] // 6050
AddedStakedPoolManually,
}

impl From<MarginfiError> for ProgramError {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
marginfi_account::{BankAccountWrapper, MarginfiAccount, RiskEngine, DISABLED_FLAG},
marginfi_group::{Bank, BankVaultType},
},
utils,
utils::{self, validate_asset_tags},
};
use anchor_lang::prelude::*;
use anchor_spl::token_interface::{TokenAccount, TokenInterface};
Expand Down Expand Up @@ -63,6 +63,8 @@ pub fn lending_account_borrow<'info>(
{
let mut bank = bank_loader.load_mut()?;

validate_asset_tags(&bank, &marginfi_account)?;

let liquidity_vault_authority_bump = bank.liquidity_vault_authority_bump;
let origination_fee_rate: I80F48 = bank
.config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
marginfi_account::{BankAccountWrapper, MarginfiAccount, DISABLED_FLAG},
marginfi_group::Bank,
},
utils,
utils::{self, validate_asset_tags},
};
use anchor_lang::prelude::*;
use anchor_spl::token_interface::TokenInterface;
Expand Down Expand Up @@ -45,6 +45,8 @@ pub fn lending_account_deposit<'info>(
let mut bank = bank_loader.load_mut()?;
let mut marginfi_account = marginfi_account_loader.load_mut()?;

validate_asset_tags(&bank, &marginfi_account)?;

check!(
!marginfi_account.get_flag(DISABLED_FLAG),
MarginfiError::AccountDisabled
Expand Down
Loading
Loading