Skip to content

Commit

Permalink
Tests for propagation
Browse files Browse the repository at this point in the history
  • Loading branch information
jgur-psyops committed Oct 31, 2024
1 parent 99e0768 commit 8c5d59b
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ pub fn propagate_staked_settings(ctx: Context<PropagateStakedSettings>) -> Resul
let settings = ctx.accounts.staked_settings.load()?;
let mut bank = ctx.accounts.bank.load_mut()?;

// Only validate the oracle if it has changed
if settings.oracle != bank.config.oracle_keys[0] {
bank.config
.validate_staked_oracle_setup(ctx.remaining_accounts)?;
}

bank.config.oracle_keys[0] = settings.oracle;
bank.config.asset_weight_init = settings.asset_weight_init;
bank.config.asset_weight_maint = settings.asset_weight_maint;
Expand All @@ -18,7 +24,6 @@ pub fn propagate_staked_settings(ctx: Context<PropagateStakedSettings>) -> Resul
bank.config.risk_tier = settings.risk_tier;

bank.config.validate()?;
bank.config.validate_staked_oracle_setup(ctx.remaining_accounts)?;
// ...Possibly emit event.

Ok(())
Expand All @@ -37,7 +42,7 @@ pub struct PropagateStakedSettings<'info> {
mut,
constraint = {
let bank = bank.load()?;
bank.group == marginfi_group.key() &&
bank.group == marginfi_group.key() &&
bank.config.asset_tag == ASSET_TAG_STAKED
}
)]
Expand Down
5 changes: 0 additions & 5 deletions programs/marginfi/src/state/price.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,11 +365,6 @@ impl OraclePriceFeedAdapter {
_ => err!(MarginfiError::StakePoolValidationFailed),
}?;

check!(
oracle_ais[0].key == &bank_config.oracle_keys[0],
MarginfiError::InvalidOracleAccount
);

check!(oracle_ais.len() == 1, MarginfiError::InvalidOracleAccount);
// Note: mainnet/staging/devnet use push oracles, localnet uses legacy push
if cfg!(any(
Expand Down
5 changes: 4 additions & 1 deletion tests/01_initGroup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ describe("Init group", () => {
assert.ok(failed, "Transaction succeeded when it should have failed");
});

// Note: there are no Staked Collateral positions in the end to end test suite (those are in the
// BankRun suite e.g. s01) so these settings do nothing. Many of the these settings as also wrong
// or don't make sense (e.g. weights > 0 with isolated risk teir) and would fail at propagation

it("(admin) Edit staked settings for group", async () => {
const settings: StakedSettingsEdit = {
oracle: PublicKey.default,
Expand Down Expand Up @@ -232,5 +236,4 @@ describe("Init group", () => {

assert.equal(settingsAcc.oracleMaxAge, 60);
});

});
182 changes: 182 additions & 0 deletions tests/s06_propagateSets.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { workspace, Program } from "@coral-xyz/anchor";
import { PublicKey, Transaction } from "@solana/web3.js";
import BN from "bn.js";
import { Marginfi } from "../target/types/marginfi";
import {
marginfiGroup,
validators,
groupAdmin,
oracles,
bankrunContext,
banksClient,
bankrunProgram,
} from "./rootHooks";
import {
editStakedSettings,
propagateStakedSettings,
} from "./utils/group-instructions";
import { deriveBankWithSeed, deriveStakedSettings } from "./utils/pdas";
import { getBankrunBlockhash } from "./utils/spl-staking-utils";
import { bigNumberToWrappedI80F48 } from "@mrgnlabs/mrgn-common";
import { assert } from "chai";
import {
assertKeysEqual,
assertI80F48Approx,
assertBNEqual,
assertBankrunTxFailed,
} from "./utils/genericTests";
import {
defaultStakedInterestSettings,
StakedSettingsEdit,
} from "./utils/types";

describe("Edit and propagate staked settings", () => {
const program = workspace.Marginfi as Program<Marginfi>;

let settingsKey: PublicKey;
let bankKey: PublicKey;

before(async () => {
[settingsKey] = deriveStakedSettings(
program.programId,
marginfiGroup.publicKey
);
[bankKey] = deriveBankWithSeed(
program.programId,
marginfiGroup.publicKey,
validators[0].splMint,
new BN(0)
);
});

it("(admin) edits some settings - happy path", async () => {
const settings: StakedSettingsEdit = {
oracle: oracles.usdcOracle.publicKey,
assetWeightInit: bigNumberToWrappedI80F48(0.2),
assetWeightMaint: bigNumberToWrappedI80F48(0.3),
depositLimit: new BN(42),
totalAssetValueInitLimit: new BN(43),
oracleMaxAge: 44,
riskTier: {
collateral: undefined,
},
};
let tx = new Transaction().add(
await editStakedSettings(groupAdmin.userMarginProgram, {
settingsKey: settingsKey,
settings: settings,
})
);
tx.recentBlockhash = await getBankrunBlockhash(bankrunContext);
tx.sign(groupAdmin.wallet);
await banksClient.processTransaction(tx);

let settingsAcc = await bankrunProgram.account.stakedSettings.fetch(
settingsKey
);
assertKeysEqual(settingsAcc.key, settingsKey);
assertKeysEqual(settingsAcc.oracle, oracles.usdcOracle.publicKey);
assertI80F48Approx(settingsAcc.assetWeightInit, 0.2);
assertI80F48Approx(settingsAcc.assetWeightMaint, 0.3);
assertBNEqual(settingsAcc.depositLimit, 42);
assertBNEqual(settingsAcc.totalAssetValueInitLimit, 43);
assert.equal(settingsAcc.oracleMaxAge, 44);
assert.deepEqual(settingsAcc.riskTier, { collateral: {} });
});

it("(permissionless) Propagate staked settings to a bank - happy path", async () => {
let tx = new Transaction();
tx.add(
await propagateStakedSettings(program, {
settings: settingsKey,
bank: bankKey,
oracle: oracles.usdcOracle.publicKey,
})
);
tx.recentBlockhash = await getBankrunBlockhash(bankrunContext);
tx.sign(groupAdmin.wallet); // just to the pay the fee
let result = await banksClient.tryProcessTransaction(tx);

const bank = await bankrunProgram.account.bank.fetch(bankKey);
const config = bank.config;
assertKeysEqual(config.oracleKeys[0], oracles.usdcOracle.publicKey);
assertI80F48Approx(config.assetWeightInit, 0.2);
assertI80F48Approx(config.assetWeightMaint, 0.3);
assertBNEqual(config.depositLimit, 42);
assertBNEqual(config.totalAssetValueInitLimit, 43);
assert.equal(config.oracleMaxAge, 44);
assert.deepEqual(config.riskTier, { collateral: {} });
});

it("(admin) sets a bad oracle - fails at propagation", async () => {
const settings: StakedSettingsEdit = {
oracle: PublicKey.default,
assetWeightInit: null,
assetWeightMaint: null,
depositLimit: null,
totalAssetValueInitLimit: null,
oracleMaxAge: null,
riskTier: null,
};
let tx = new Transaction().add(
await editStakedSettings(groupAdmin.userMarginProgram, {
settingsKey: settingsKey,
settings: settings,
})
);
tx.recentBlockhash = await getBankrunBlockhash(bankrunContext);
tx.sign(groupAdmin.wallet);
await banksClient.processTransaction(tx);

let settingsAcc = await bankrunProgram.account.stakedSettings.fetch(
settingsKey
);
assertKeysEqual(settingsAcc.oracle, PublicKey.default);

tx = new Transaction();
tx.add(
await propagateStakedSettings(program, {
settings: settingsKey,
bank: bankKey,
oracle: PublicKey.default,
})
);
tx.recentBlockhash = await getBankrunBlockhash(bankrunContext);
tx.sign(groupAdmin.wallet); // just to the pay the fee
let result = await banksClient.tryProcessTransaction(tx);

// 6007 (InvalidOracleAccount)
assertBankrunTxFailed(result, "0x1777");
});

it("(admin) restores default settings - happy path", async () => {
const defaultSettings = defaultStakedInterestSettings(
oracles.wsolOracle.publicKey
);
const settings: StakedSettingsEdit = {
oracle: defaultSettings.oracle,
assetWeightInit: defaultSettings.assetWeightInit,
assetWeightMaint: defaultSettings.assetWeightMaint,
depositLimit: defaultSettings.depositLimit,
totalAssetValueInitLimit: defaultSettings.totalAssetValueInitLimit,
oracleMaxAge: defaultSettings.oracleMaxAge,
riskTier: defaultSettings.riskTier,
};
// Note you can pack propagates into the edit tx, so with a LUT you can easily propagate
// hundreds of banks in the same ts as edit
let tx = new Transaction().add(
await editStakedSettings(groupAdmin.userMarginProgram, {
settingsKey: settingsKey,
settings: settings,
}),
await propagateStakedSettings(program, {
settings: settingsKey,
bank: bankKey,
oracle: defaultSettings.oracle,
})
);
tx.recentBlockhash = await getBankrunBlockhash(bankrunContext);
tx.sign(groupAdmin.wallet);
await banksClient.processTransaction(tx);
});
});
38 changes: 38 additions & 0 deletions tests/utils/group-instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ export const editGlobalFeeState = (
return ix;
};

// TODO propagate fee state and test

export type InitStakedSettingsArgs = {
group: PublicKey;
feePayer: PublicKey;
Expand Down Expand Up @@ -342,6 +344,42 @@ export const editStakedSettings = (
return ix;
};

/**
* oracle - required only if settings updates the oracle key
*/
export type PropagateStakedSettingsArgs = {
settings: PublicKey;
bank: PublicKey;
oracle?: PublicKey;
};

export const propagateStakedSettings = (
program: Program<Marginfi>,
args: PropagateStakedSettingsArgs
) => {
const remainingAccounts = args.oracle
? [
{
pubkey: args.oracle,
isSigner: false,
isWritable: false,
} as AccountMeta,
]
: [];

const ix = program.methods
.propagateStakedSettings()
.accounts({
// marginfiGroup: args.group, // implied from stakedSettings
stakedSettings: args.settings,
bank: args.bank,
})
.remainingAccounts(remainingAccounts)
.instruction();

return ix;
};

export type AddBankPermissionlessArgs = {
marginfiGroup: PublicKey;
feePayer: PublicKey;
Expand Down

0 comments on commit 8c5d59b

Please sign in to comment.