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

Scripts for program and origination fees #948

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions packages/marginfi-client-v2/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,3 +237,11 @@ function u16ToArrayBufferLE(value: number): Uint8Array {
// Return the buffer
return new Uint8Array(buffer);
}

/// Derive the global FeeState account, which is unique per-program and uses a static pda seed.
export const deriveGlobalFeeState = (programId: PublicKey) => {
return PublicKey.findProgramAddressSync(
[Buffer.from("feestate", "utf-8")],
programId
);
};
24 changes: 24 additions & 0 deletions packages/scripts-ts/accounts_ref.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
TODO list of common banks and groups...

## PROGRAMS
* Mainnet - MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA
* Staging - stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct

## GROUPS
* Main group - 4qp6Fx6tnZkY5Wropq9wUYgtFxXKwE6viZxFHg3rdAG8
* Staging - FCPfpHA69EbS8f9KKSreTRkXbzFpunsKuYf5qNmnJjpo


## MAIN GROUP BANKS
* BONK - DeyH7QxWvnbbaVB4zFrf4hoq7Q8z1ZT14co42BGwGtfM

## STAGING BANKS
* PYUSD - Fe5QkKPVAh629UPP5aJ8sDZu8HTfe6M26jDQkKyXVhoA

## ORACLES
* PY USD (Pyth push) - A52UBHzxnKrH17zjhajRTgHcWwtxN7KYDAzBgraqFxQJ
* USDT (Pyth push) - HT2PLQBcG5EiCcNSaMHAjSgd9F98ecpATbk4Sk5oYuM

## MINTS
* UXD - 7kbnvuGBxxj8AG9qp8Scn56muWGaRaFqxg1FsRp3PaFT
* USDT - Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
64 changes: 64 additions & 0 deletions packages/scripts-ts/accrue_interest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
Connection,
PublicKey,
Transaction,
sendAndConfirmTransaction,
} from "@solana/web3.js";
import { Marginfi } from "../marginfi-client-v2/src/idl/marginfi-types";
import marginfiIdl from "../marginfi-client-v2/src/idl/marginfi.json";
import { AnchorProvider, Program } from "@coral-xyz/anchor";
import { loadKeypairFromFile } from "./utils";

type Config = {
PROGRAM_ID: string;
BANK_KEY: PublicKey;
GROUP_KEY: PublicKey;
};
const config: Config = {
PROGRAM_ID: "stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct",
BANK_KEY: new PublicKey("Fe5QkKPVAh629UPP5aJ8sDZu8HTfe6M26jDQkKyXVhoA"),
GROUP_KEY: new PublicKey("FCPfpHA69EbS8f9KKSreTRkXbzFpunsKuYf5qNmnJjpo"),
};

async function main() {
marginfiIdl.address = config.PROGRAM_ID;
const connection = new Connection(
"https://api.mainnet-beta.solana.com",
"confirmed"
);
const wallet = loadKeypairFromFile(
process.env.HOME + "/.config/solana/id.json"
);

// @ts-ignore
const provider = new AnchorProvider(connection, wallet, {
preflightCommitment: "confirmed",
});
const program: Program<Marginfi> = new Program(
// @ts-ignore
marginfiIdl as Marginfi,
provider
);
const transaction = new Transaction().add(
await program.methods
.lendingPoolAccrueBankInterest()
.accounts({
bank: config.BANK_KEY,
marginfiGroup: config.GROUP_KEY,
})
.instruction()
);

try {
const signature = await sendAndConfirmTransaction(connection, transaction, [
wallet,
]);
console.log("Transaction signature:", signature);
} catch (error) {
console.error("Transaction failed:", error);
}
}

main().catch((err) => {
console.error(err);
});
163 changes: 163 additions & 0 deletions packages/scripts-ts/add_bank.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { AccountMeta, Connection, PublicKey, Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
import { Program, AnchorProvider, Wallet, BN } from "@coral-xyz/anchor";
import { Marginfi } from "../marginfi-client-v2/src/idl/marginfi-types";
import marginfiIdl from "../marginfi-client-v2/src/idl/marginfi.json";
import { I80F48_ONE, loadKeypairFromFile } from "./utils";
import { assertI80F48Approx, assertKeysEqual } from "./softTests";
import { bigNumberToWrappedI80F48, TOKEN_PROGRAM_ID } from "@mrgnlabs/mrgn-common";
// TODO move to package import after update
import { InterestRateConfigRaw, BankConfigCompactRaw } from "../marginfi-client-v2/src/models/bank";

const verbose = true;

type Config = {
PROGRAM_ID: string;
GROUP_KEY: PublicKey;
ORACLE: PublicKey;
ADMIN: PublicKey;
FEE_PAYER: PublicKey;
BANK_MINT: PublicKey;
SEED: number;
};

const config: Config = {
PROGRAM_ID: "stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct",
GROUP_KEY: new PublicKey("FCPfpHA69EbS8f9KKSreTRkXbzFpunsKuYf5qNmnJjpo"),
ORACLE: new PublicKey("HT2PLQBcG5EiCcNSaMHAjSgd9F98ecpATbk4Sk5oYuM"),
ADMIN: new PublicKey("mfC1LoEk4mpM5yx1LjwR9QLZQ49AitxxWkK5Aciw7ZC"),
FEE_PAYER: new PublicKey("mfC1LoEk4mpM5yx1LjwR9QLZQ49AitxxWkK5Aciw7ZC"),
BANK_MINT: new PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"),
SEED: 0,
};

const deriveGlobalFeeState = (programId: PublicKey) => {
return PublicKey.findProgramAddressSync([Buffer.from("feestate", "utf-8")], programId);
};

async function main() {
marginfiIdl.address = config.PROGRAM_ID;
const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed");
const wallet = loadKeypairFromFile(process.env.HOME + "/keys/staging-deploy.json");

// @ts-ignore
const provider = new AnchorProvider(connection, wallet, {
preflightCommitment: "confirmed",
});

const program = new Program<Marginfi>(
// @ts-ignore
marginfiIdl as Marginfi,
provider
);

const [feeStateKey] = deriveGlobalFeeState(program.programId);
const feeState = await program.account.feeState.fetch(feeStateKey);
const feeWalletBefore = await provider.connection.getAccountInfo(feeState.globalFeeWallet);
if (verbose) {
console.log("flat sol init fee: " + feeState.bankInitFlatSolFee);
console.log("fee wallet lamports before: " + feeWalletBefore.lamports);
}

const transaction = new Transaction();

const rate: InterestRateConfigRaw = {
optimalUtilizationRate: bigNumberToWrappedI80F48(0.5),
plateauInterestRate: bigNumberToWrappedI80F48(0.6),
maxInterestRate: bigNumberToWrappedI80F48(3),
insuranceFeeFixedApr: bigNumberToWrappedI80F48(0.01),
insuranceIrFee: bigNumberToWrappedI80F48(0.02),
protocolFixedFeeApr: bigNumberToWrappedI80F48(0.03),
protocolIrFee: bigNumberToWrappedI80F48(0.04),
protocolOriginationFee: bigNumberToWrappedI80F48(0.1),
};

let bankConfig: BankConfigCompactRaw = {
assetWeightInit: I80F48_ONE,
assetWeightMaint: I80F48_ONE,
liabilityWeightInit: I80F48_ONE,
liabilityWeightMaint: I80F48_ONE,
depositLimit: new BN(1_000_000_000),
interestRateConfig: rate,
operationalState: {
operational: undefined,
},
oracleSetup: {
pythPushOracle: undefined,
},
oracleKey: new PublicKey(Buffer.from("K4m53I/fnzRwmlsQa0cvDzm7bKnOBLD9fy6XFoji5Ts=", "base64")),
borrowLimit: new BN(1_000_000_000),
riskTier: {
collateral: undefined,
},
totalAssetValueInitLimit: new BN(100_000_000_000),
oracleMaxAge: 100,
};

const oracleMeta: AccountMeta = {
pubkey: config.ORACLE,
isSigner: false,
isWritable: false,
};
console.log("oracle: " + bankConfig.oracleKey);

// Note: the BN used by `BankConfigCompactRaw` is different from the kind used in the anchor
// version here which requires this stupid hack where the BN is re-declared (or just TS-ignore it)
const ix = await program.methods
.lendingPoolAddBankWithSeed(
{
assetWeightInit: bankConfig.assetWeightInit,
assetWeightMaint: bankConfig.assetWeightMaint,
liabilityWeightInit: bankConfig.liabilityWeightInit,
liabilityWeightMaint: bankConfig.liabilityWeightMaint,
depositLimit: new BN(bankConfig.depositLimit.toString()),
interestRateConfig: bankConfig.interestRateConfig,
operationalState: bankConfig.operationalState,
oracleSetup: bankConfig.oracleSetup,
oracleKey: bankConfig.oracleKey,
borrowLimit: new BN(bankConfig.borrowLimit.toString()),
riskTier: bankConfig.riskTier,
pad0: [0, 0, 0, 0, 0, 0, 0],
totalAssetValueInitLimit: new BN(bankConfig.totalAssetValueInitLimit.toString()),
oracleMaxAge: bankConfig.oracleMaxAge,
},
new BN(config.SEED)
)
.accounts({
marginfiGroup: config.GROUP_KEY,
admin: config.ADMIN,
feePayer: config.FEE_PAYER,
bankMint: config.BANK_MINT,
// bank: // derived from mint/seed
// globalFeeState: deriveGlobalFeeState(id),
// globalFeeWallet: args.globalFeeWallet,
// liquidityVaultAuthority = deriveLiquidityVaultAuthority(id, bank);
// liquidityVault = deriveLiquidityVault(id, bank);
// insuranceVaultAuthority = deriveInsuranceVaultAuthority(id, bank);
// insuranceVault = deriveInsuranceVault(id, bank);
// feeVaultAuthority = deriveFeeVaultAuthority(id, bank);
// feeVault = deriveFeeVault(id, bank);
// rent = SYSVAR_RENT_PUBKEY
tokenProgram: TOKEN_PROGRAM_ID,
// systemProgram: SystemProgram.programId,
})
.remainingAccounts([oracleMeta])
.instruction();

transaction.add(ix);

try {
const signature = await sendAndConfirmTransaction(connection, transaction, [wallet]);
console.log("Transaction signature:", signature);
} catch (error) {
console.error("Transaction failed:", error);
}

const feeWalletAfter = await provider.connection.getAccountInfo(feeState.globalFeeWallet);
if (verbose) {
console.log("fee wallet lamports after: " + feeWalletAfter.lamports);
}
}

main().catch((err) => {
console.error(err);
});
60 changes: 60 additions & 0 deletions packages/scripts-ts/close_account.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Connection, PublicKey, Transaction, sendAndConfirmTransaction } from "@solana/web3.js";
import { Program, AnchorProvider } from "@coral-xyz/anchor";
import { Marginfi } from "../marginfi-client-v2/src/idl/marginfi-types";
import marginfiIdl from "../marginfi-client-v2/src/idl/marginfi.json";
import { loadKeypairFromFile } from "./utils";

type Config = {
PROGRAM_ID: string;
ACCOUNT_KEY: PublicKey;
AUTHORITY_KEY: PublicKey;
FEE_PAYER_KEY: PublicKey;
};

const config: Config = {
PROGRAM_ID: "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA",
// Fill your marginfi account key here....
ACCOUNT_KEY: new PublicKey("stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct"),
// Fill your wallet here...
AUTHORITY_KEY: new PublicKey("stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct"),
// Fill your account to get the rent here...
FEE_PAYER_KEY: new PublicKey("stag8sTKds2h4KzjUw3zKTsxbqvT4XKHdaR9X9E6Rct"),
};

async function main() {
marginfiIdl.address = config.PROGRAM_ID;
const connection = new Connection("https://api.mainnet-beta.solana.com", "confirmed");
const wallet = loadKeypairFromFile(process.env.HOME + "/.config/solana/id.json");

// @ts-ignore
const provider = new AnchorProvider(connection, wallet, {
preflightCommitment: "confirmed",
});

const program = new Program<Marginfi>(
// @ts-ignore
marginfiIdl as Marginfi,
provider
);
const transaction = new Transaction().add(
await program.methods
.marginfiAccountClose()
.accounts({
marginfiAccount: config.ACCOUNT_KEY,
feePayer: config.FEE_PAYER_KEY,
authority: config.AUTHORITY_KEY,
})
.instruction()
);

try {
const signature = await sendAndConfirmTransaction(connection, transaction, [wallet]);
console.log("Transaction signature:", signature);
} catch (error) {
console.error("Transaction failed:", error);
}
}

main().catch((err) => {
console.error(err);
});
Loading
Loading