Skip to content

Commit

Permalink
Fix padding, add anchor tests (#236)
Browse files Browse the repository at this point in the history
* Basic framework for TS integration tests

* TS tests for init and config ixes, addBank test wip

* Mock oracle, wip add bank happy path testing

* WIP decoding bank from raw buffer

* Banks now decode with anchor

* WIP decoding bank config with enum alignment

* Add silly hack to risk tier representation in Rust to force anchor to properly deserialize it.

* cleanup msg

* Various minor fixes

* Restore one-byte RiskTier, WIP test mainnet structs decode correctly

* WIP mainnet decode test

* Taste the rainbow (in console output)

* chore: fmt

* lint: revert obsolete changes

* Manually check fields in mainnet decode test

* Attempt to add anchor test to CI jobs

* Anchor test CI attempt 2

* Anchor test CI attempt 3

* Anchor test CI attempt 4

* Anchor test CI attempt 5

* Anchor test CI attempt 6

* feat: combine bank padding fields

* Mainnet test accounts, CI attempt 6ish

* Anchor test CI attempt 8

* Anchor test CI attempt 9

* test: add comprehensive struct regression test with latest fields as of t22 commit

* test: add missing account data file

* fmt: remove comments for diff clarity

* fmt: remove comments for diff clarity

* fmt: remove comments for diff clarity

* ci: silence wip localnet tests

* chore: cleanup

* cli: switchboard inspect command

* cli: swb pull variant for oracle setup

* cli: clearer variant name

* chore: utility scripts

* ci: revert script change

---------

Co-authored-by: man0s <[email protected]>
  • Loading branch information
jgur-psyops and losman0s authored Aug 22, 2024
1 parent 063fcbf commit 9bd878f
Show file tree
Hide file tree
Showing 52 changed files with 3,466 additions and 332 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,46 @@ jobs:
cargo +nightly-2024-06-05 fuzz run lend -Zbuild-std --strip-dead-code --no-cfg-fuzzing -- -max_total_time=300
- 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

# - name: Build liquidity incentive program
# run: anchor build -p liquidity_incentive_program -- --no-default-features

# - name: Build mocks program
# run: anchor build -p mocks

# - 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

# - name: Run tests
# run: anchor test --skip-build --skip-local-validator
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ test-ledger/
*.profraw

# keypairs
*.json
*.json

# Allow specific json files
!tests/fixtures/**/*.json
!programs/marginfi/tests/fixtures/**/*.json
16 changes: 13 additions & 3 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ skip-lint = false

[programs.localnet]
liquidity_incentive_program = "Lip1111111111111111111111111111111111111111"
marginfi = "Mfi1111111111111111111111111111111111111111"
marginfi = "2jGhuVUuy3umdzByFx8sNWUAaf5vaeuDm78RDPEnhrMr"
mocks = "5XaaR94jBubdbrRrNW7DtRvZeWvLhSHkEGU3jHTEXV3C"

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

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

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/*.spec.ts --exit --require tests/rootHooks.ts"

[test]
startup_wait = 5000
Expand All @@ -34,6 +36,14 @@ bind_address = "0.0.0.0"
ledger = ".anchor/test-ledger"
rpc_port = 8899

[[test.validator.account]]
address = "DeyH7QxWvnbbaVB4zFrf4hoq7Q8z1ZT14co42BGwGtfM"
filename = "tests/fixtures/bonk_bank.json"

[[test.validator.account]]
address = "4kNXetv8hSv9PzvzPZzEs1CTH6ARRRi2b8h6jk1ad1nP"
filename = "tests/fixtures/cloud_bank.json"

[[test.validator.account]]
address = "8FRFC6MoGGkMFQwngccyu69VnYbzykGeez7ignHVAFSN"
filename = "tests/fixtures/localnet_usdc.json"
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Marginfi is a decentralized liquidity aggregation protocol built on the Solana blockchain that allows users to access a range of lending markets through a single platform, supporting cryptocurrencies such as SOL, USDC, USDT, wBTC (Portal), ETH (Portal), and BONK. The platform pools liquidity from various sources, offering competitive interest rates to lenders and lower interest rates to borrowers. Marginfi plans to introduce cross-composing in the future, enabling users to trade between different assets on the platform, further enhancing liquidity and providing more opportunities for investment returns.

## Installation

> :warning: marginfi-v2 only compiles on the x86_64 architecture. This is to
> ensure struct sizes are always backwards compatible between the SVM and local
> development. Ensure the x86_64 arch is enabled before compiling the project.
Expand Down Expand Up @@ -68,16 +69,38 @@ Install the `solana-verify` tool [here](https://github.com/Ellipsis-Labs/solana-
Run `./scripts/verify_mainnet.sh`

## Testing

Integration tests for the on-chain marginfi programs are located under
`/programs/marginfi/tests`. To run the tests, use `cargo test-bpf`. Be sure to
`/programs/marginfi/tests`. To run the tests, use `cargo test-bpf`. Be sure to
use an x86 toolchain when compiling and running the tests.

## Rust Tests

Run the full test suite with `.scripts/test-program.sh <program_to_test>`
* e.g. `.scripts/test-program.sh all --sane`

- e.g. `.scripts/test-program.sh all --sane`

Run a single test:
`.scripts/test-program.sh <program_to_test> <name_of_test>`
* e.g. `.scripts/test-program.sh marginfi configure_bank_success --verbose`

- e.g. `.scripts/test-program.sh marginfi configure_bank_success --verbose`

## Localnet Anchor Tests

Build the program with:

`anchor build -p marginfi -- --no-default-features`

You may also need to build the liquidity incentive program and mock program:

- `anchor build -p mocks`
- `anchor build -p liquidity_incentive_program -- --no-default-features`

Remember to `yarn install`

Run the tests:

`anchor test --skip-build`

## Footguns

Expand Down
1 change: 1 addition & 0 deletions clients/rust/marginfi-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ anchor-spl = { workspace = true, features = ["token_2022"] }

pyth-sdk-solana = { workspace = true }
switchboard-solana = { workspace = true }
switchboard-on-demand = "0.1.14"
borsh = "0.10.3"

marginfi = { path = "../../../programs/marginfi", version = "0.1.0", features = [
Expand Down
19 changes: 16 additions & 3 deletions clients/rust/marginfi-cli/src/entrypoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ pub enum Command {
FindPythPull {
feed_id: String,
},
InspectSwbPullFeed {
address: Pubkey,
},
}

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -170,16 +173,18 @@ impl From<RiskTierArg> for RiskTier {
#[derive(Clone, Copy, Debug, Parser, ArgEnum)]
pub enum OracleTypeArg {
PythLegacy,
Switchboard,
SwitchboardLegacy,
PythPushOracle,
SwitchboardPull,
}

impl From<OracleTypeArg> for OracleSetup {
fn from(value: OracleTypeArg) -> Self {
match value {
OracleTypeArg::PythLegacy => OracleSetup::PythLegacy,
OracleTypeArg::Switchboard => OracleSetup::SwitchboardV2,
OracleTypeArg::SwitchboardLegacy => OracleSetup::SwitchboardV2,
OracleTypeArg::PythPushOracle => OracleSetup::PythPushOracle,
OracleTypeArg::SwitchboardPull => OracleSetup::SwitchboardPull,
}
}
}
Expand Down Expand Up @@ -445,7 +450,7 @@ pub fn entry(opts: Opts) -> Result<()> {
let profile = load_profile()?;
let config = profile.get_config(Some(&opts.cfg_override))?;

processor::inspect_pyth_push_feed(&config, pyth_feed)?;
processor::oracle::inspect_pyth_push_feed(&config, pyth_feed)?;

Ok(())
}
Expand All @@ -458,6 +463,14 @@ pub fn entry(opts: Opts) -> Result<()> {

find_pyth_push_oracles_for_feed_id(&rpc, feed_id)?;

Ok(())
}
Command::InspectSwbPullFeed { address } => {
let profile = load_profile()?;
let config = profile.get_config(Some(&opts.cfg_override))?;

processor::oracle::inspect_swb_pull_feed(&config, address)?;

Ok(())
}
}
Expand Down
22 changes: 0 additions & 22 deletions clients/rust/marginfi-cli/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use {
},
anchor_spl::token_2022::spl_token_2022,
anyhow::{anyhow, bail, Result},
borsh::BorshDeserialize,
fixed::types::I80F48,
log::info,
marginfi::{
Expand All @@ -40,7 +39,6 @@ use {
utils::NumTraitsWithTolerance,
},
pyth_sdk_solana::state::{load_price_account, SolanaPriceAccount},
pyth_solana_receiver_sdk::price_update::PriceUpdateV2,
solana_client::{
rpc_client::RpcClient,
rpc_filter::{Memcmp, RpcFilterType},
Expand Down Expand Up @@ -2418,23 +2416,3 @@ fn timestamp_to_string(timestamp: i64) -> String {
.format("%Y-%m-%d %H:%M:%S")
.to_string()
}

pub fn inspect_pyth_push_feed(config: &Config, address: Pubkey) -> anyhow::Result<()> {
let mut account = config.mfi_program.rpc().get_account(&address)?;
let ai = (&address, &mut account).into_account_info();

let mut data = &ai.try_borrow_data()?[8..];
let price_update = PriceUpdateV2::deserialize(&mut data)?;

println!("Pyth Push Feed: {}", address);
let feed = PythPushOraclePriceFeed::load_unchecked(&ai)?;

println!(
"Price: {}",
feed.get_price_of_type(marginfi::state::price::OraclePriceType::RealTime, None)?
);

println!("Feed id: {:?}", price_update.price_message.feed_id);

Ok(())
}
62 changes: 61 additions & 1 deletion clients/rust/marginfi-cli/src/processor/oracle.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
use pyth_solana_receiver_sdk::price_update::FeedId;
use crate::config::Config;
use borsh::BorshDeserialize;
use chrono::{DateTime, Local, TimeZone};
use fixed::types::I80F48;
use marginfi::{
constants::EXP_10_I80F48,
state::price::{PriceAdapter, PythPushOraclePriceFeed},
};
use pyth_solana_receiver_sdk::price_update::{FeedId, PriceUpdateV2};
use solana_account_decoder::UiAccountEncoding;
use solana_client::rpc_client::RpcClient;
use solana_client::rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig};
use solana_client::rpc_filter::{Memcmp, RpcFilterType};
use solana_sdk::account_info::IntoAccountInfo;
use solana_sdk::pubkey::Pubkey;
use std::time::{SystemTime, UNIX_EPOCH};
use switchboard_on_demand::PullFeedAccountData;

pub fn find_pyth_push_oracles_for_feed_id(
rpc_client: &RpcClient,
Expand Down Expand Up @@ -45,3 +55,53 @@ pub fn find_pyth_push_oracles_for_feed_id(

Ok(())
}

pub fn inspect_pyth_push_feed(config: &Config, address: Pubkey) -> anyhow::Result<()> {
let mut account = config.mfi_program.rpc().get_account(&address)?;
let ai = (&address, &mut account).into_account_info();

let mut data = &ai.try_borrow_data()?[8..];
let price_update = PriceUpdateV2::deserialize(&mut data)?;

println!("Pyth Push Feed: {}", address);
let feed = PythPushOraclePriceFeed::load_unchecked(&ai)?;

println!(
"Price: {}",
feed.get_price_of_type(marginfi::state::price::OraclePriceType::RealTime, None)?
);

let feed_id = price_update.price_message.feed_id;

println!("Feed id: {:?}", feed_id);
println!("Feed id hex: 0x{}", hex::encode(feed_id));

Ok(())
}

pub fn inspect_swb_pull_feed(config: &Config, address: Pubkey) -> anyhow::Result<()> {
let mut account = config.mfi_program.rpc().get_account(&address)?;

let ai = (&address, &mut account).into_account_info();
let feed = PullFeedAccountData::parse(ai.data.borrow())?;

let price: I80F48 = I80F48::from_num(feed.result.value)
.checked_div(EXP_10_I80F48[switchboard_on_demand::PRECISION as usize])
.unwrap();

let last_updated = feed.last_update_timestamp;
let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64;
let age = current_timestamp.saturating_sub(last_updated);
let datetime: DateTime<Local> = Local.timestamp_opt(last_updated, 0).unwrap();

println!("price: {}", price);
println!(
"last updated: {} (ts: {}; slot {})",
datetime,
last_updated,
feed.result.result_slot().unwrap_or(0)
);
println!("age: {}s", age);

Ok(())
}
42 changes: 24 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
{
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
},
"dependencies": {
"@project-serum/anchor": "^0.25.0",
"@pythnetwork/client": "^2.9.0"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.0.0",
"chai": "^4.3.4",
"mocha": "^9.0.3",
"prettier": "^2.6.2",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.5"
}
"scripts": {
"lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w",
"lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check"
},
"dependencies": {
"@coral-xyz/anchor": "^0.30.1",
"@coral-xyz/spl-token": "^0.30.1",
"@solana/spl-token": "^0.4.8",
"@solana/web3.js": "^1.95.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": {
"@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"
}
}
Loading

0 comments on commit 9bd878f

Please sign in to comment.