diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 0000000..569e91a --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,5 @@ +[profile.default] +retries = { backoff = "exponential", count = 5, delay = "1s", jitter = true, max-delay = "10s" } +test-threads = "num-cpus" +threads-required = 1 +fail-fast = false \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 44894c2..210961a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2553,6 +2553,7 @@ dependencies = [ "jito-restaking-sdk", "jito-tip-router-core", "jito-vault-core", + "jito-vault-program", "jito-vault-sdk", "shank", "solana-program 1.18.26", diff --git a/clients/js/jito_tip_router/accounts/ballotBox.ts b/clients/js/jito_tip_router/accounts/ballotBox.ts new file mode 100644 index 0000000..447e1b5 --- /dev/null +++ b/clients/js/jito_tip_router/accounts/ballotBox.ts @@ -0,0 +1,171 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + assertAccountExists, + assertAccountsExist, + combineCodec, + decodeAccount, + fetchEncodedAccount, + fetchEncodedAccounts, + getAddressDecoder, + getAddressEncoder, + getArrayDecoder, + getArrayEncoder, + getStructDecoder, + getStructEncoder, + getU64Decoder, + getU64Encoder, + getU8Decoder, + getU8Encoder, + type Account, + type Address, + type Codec, + type Decoder, + type EncodedAccount, + type Encoder, + type FetchAccountConfig, + type FetchAccountsConfig, + type MaybeAccount, + type MaybeEncodedAccount, +} from '@solana/web3.js'; +import { + getBallotDecoder, + getBallotEncoder, + getBallotTallyDecoder, + getBallotTallyEncoder, + getOperatorVoteDecoder, + getOperatorVoteEncoder, + type Ballot, + type BallotArgs, + type BallotTally, + type BallotTallyArgs, + type OperatorVote, + type OperatorVoteArgs, +} from '../types'; + +export type BallotBox = { + discriminator: bigint; + ncn: Address; + ncnEpoch: bigint; + bump: number; + slotCreated: bigint; + slotConsensusReached: bigint; + reserved: Array; + operatorsVoted: bigint; + uniqueBallots: bigint; + winningBallot: Ballot; + operatorVotes: Array; + ballotTallies: Array; +}; + +export type BallotBoxArgs = { + discriminator: number | bigint; + ncn: Address; + ncnEpoch: number | bigint; + bump: number; + slotCreated: number | bigint; + slotConsensusReached: number | bigint; + reserved: Array; + operatorsVoted: number | bigint; + uniqueBallots: number | bigint; + winningBallot: BallotArgs; + operatorVotes: Array; + ballotTallies: Array; +}; + +export function getBallotBoxEncoder(): Encoder { + return getStructEncoder([ + ['discriminator', getU64Encoder()], + ['ncn', getAddressEncoder()], + ['ncnEpoch', getU64Encoder()], + ['bump', getU8Encoder()], + ['slotCreated', getU64Encoder()], + ['slotConsensusReached', getU64Encoder()], + ['reserved', getArrayEncoder(getU8Encoder(), { size: 128 })], + ['operatorsVoted', getU64Encoder()], + ['uniqueBallots', getU64Encoder()], + ['winningBallot', getBallotEncoder()], + ['operatorVotes', getArrayEncoder(getOperatorVoteEncoder(), { size: 32 })], + ['ballotTallies', getArrayEncoder(getBallotTallyEncoder(), { size: 32 })], + ]); +} + +export function getBallotBoxDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU64Decoder()], + ['ncn', getAddressDecoder()], + ['ncnEpoch', getU64Decoder()], + ['bump', getU8Decoder()], + ['slotCreated', getU64Decoder()], + ['slotConsensusReached', getU64Decoder()], + ['reserved', getArrayDecoder(getU8Decoder(), { size: 128 })], + ['operatorsVoted', getU64Decoder()], + ['uniqueBallots', getU64Decoder()], + ['winningBallot', getBallotDecoder()], + ['operatorVotes', getArrayDecoder(getOperatorVoteDecoder(), { size: 32 })], + ['ballotTallies', getArrayDecoder(getBallotTallyDecoder(), { size: 32 })], + ]); +} + +export function getBallotBoxCodec(): Codec { + return combineCodec(getBallotBoxEncoder(), getBallotBoxDecoder()); +} + +export function decodeBallotBox( + encodedAccount: EncodedAccount +): Account; +export function decodeBallotBox( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeBallotBox( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): Account | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getBallotBoxDecoder() + ); +} + +export async function fetchBallotBox( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeBallotBox(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeBallotBox( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeBallotBox(maybeAccount); +} + +export async function fetchAllBallotBox( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeBallotBox(rpc, addresses, config); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeBallotBox( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config); + return maybeAccounts.map((maybeAccount) => decodeBallotBox(maybeAccount)); +} diff --git a/clients/js/jito_tip_router/accounts/epochSnapshot.ts b/clients/js/jito_tip_router/accounts/epochSnapshot.ts new file mode 100644 index 0000000..18dcef0 --- /dev/null +++ b/clients/js/jito_tip_router/accounts/epochSnapshot.ts @@ -0,0 +1,176 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + assertAccountExists, + assertAccountsExist, + combineCodec, + decodeAccount, + fetchEncodedAccount, + fetchEncodedAccounts, + getAddressDecoder, + getAddressEncoder, + getArrayDecoder, + getArrayEncoder, + getStructDecoder, + getStructEncoder, + getU128Decoder, + getU128Encoder, + getU64Decoder, + getU64Encoder, + getU8Decoder, + getU8Encoder, + type Account, + type Address, + type Codec, + type Decoder, + type EncodedAccount, + type Encoder, + type FetchAccountConfig, + type FetchAccountsConfig, + type MaybeAccount, + type MaybeEncodedAccount, +} from '@solana/web3.js'; +import { + getFeesDecoder, + getFeesEncoder, + type Fees, + type FeesArgs, +} from '../types'; + +export type EpochSnapshot = { + discriminator: bigint; + ncn: Address; + ncnEpoch: bigint; + bump: number; + slotCreated: bigint; + slotFinalized: bigint; + ncnFees: Fees; + operatorCount: bigint; + vaultCount: bigint; + operatorsRegistered: bigint; + validOperatorVaultDelegations: bigint; + stakeWeight: bigint; + reserved: Array; +}; + +export type EpochSnapshotArgs = { + discriminator: number | bigint; + ncn: Address; + ncnEpoch: number | bigint; + bump: number; + slotCreated: number | bigint; + slotFinalized: number | bigint; + ncnFees: FeesArgs; + operatorCount: number | bigint; + vaultCount: number | bigint; + operatorsRegistered: number | bigint; + validOperatorVaultDelegations: number | bigint; + stakeWeight: number | bigint; + reserved: Array; +}; + +export function getEpochSnapshotEncoder(): Encoder { + return getStructEncoder([ + ['discriminator', getU64Encoder()], + ['ncn', getAddressEncoder()], + ['ncnEpoch', getU64Encoder()], + ['bump', getU8Encoder()], + ['slotCreated', getU64Encoder()], + ['slotFinalized', getU64Encoder()], + ['ncnFees', getFeesEncoder()], + ['operatorCount', getU64Encoder()], + ['vaultCount', getU64Encoder()], + ['operatorsRegistered', getU64Encoder()], + ['validOperatorVaultDelegations', getU64Encoder()], + ['stakeWeight', getU128Encoder()], + ['reserved', getArrayEncoder(getU8Encoder(), { size: 128 })], + ]); +} + +export function getEpochSnapshotDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU64Decoder()], + ['ncn', getAddressDecoder()], + ['ncnEpoch', getU64Decoder()], + ['bump', getU8Decoder()], + ['slotCreated', getU64Decoder()], + ['slotFinalized', getU64Decoder()], + ['ncnFees', getFeesDecoder()], + ['operatorCount', getU64Decoder()], + ['vaultCount', getU64Decoder()], + ['operatorsRegistered', getU64Decoder()], + ['validOperatorVaultDelegations', getU64Decoder()], + ['stakeWeight', getU128Decoder()], + ['reserved', getArrayDecoder(getU8Decoder(), { size: 128 })], + ]); +} + +export function getEpochSnapshotCodec(): Codec< + EpochSnapshotArgs, + EpochSnapshot +> { + return combineCodec(getEpochSnapshotEncoder(), getEpochSnapshotDecoder()); +} + +export function decodeEpochSnapshot( + encodedAccount: EncodedAccount +): Account; +export function decodeEpochSnapshot( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeEpochSnapshot( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): Account | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getEpochSnapshotDecoder() + ); +} + +export async function fetchEpochSnapshot( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeEpochSnapshot(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeEpochSnapshot( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeEpochSnapshot(maybeAccount); +} + +export async function fetchAllEpochSnapshot( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeEpochSnapshot( + rpc, + addresses, + config + ); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeEpochSnapshot( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config); + return maybeAccounts.map((maybeAccount) => decodeEpochSnapshot(maybeAccount)); +} diff --git a/clients/js/jito_tip_router/accounts/index.ts b/clients/js/jito_tip_router/accounts/index.ts index 4c53566..9e306af 100644 --- a/clients/js/jito_tip_router/accounts/index.ts +++ b/clients/js/jito_tip_router/accounts/index.ts @@ -6,6 +6,9 @@ * @see https://github.com/kinobi-so/kinobi */ +export * from './ballotBox'; +export * from './epochSnapshot'; export * from './ncnConfig'; +export * from './operatorSnapshot'; export * from './trackedMints'; export * from './weightTable'; diff --git a/clients/js/jito_tip_router/accounts/operatorSnapshot.ts b/clients/js/jito_tip_router/accounts/operatorSnapshot.ts new file mode 100644 index 0000000..76a521b --- /dev/null +++ b/clients/js/jito_tip_router/accounts/operatorSnapshot.ts @@ -0,0 +1,211 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + assertAccountExists, + assertAccountsExist, + combineCodec, + decodeAccount, + fetchEncodedAccount, + fetchEncodedAccounts, + getAddressDecoder, + getAddressEncoder, + getArrayDecoder, + getArrayEncoder, + getBoolDecoder, + getBoolEncoder, + getStructDecoder, + getStructEncoder, + getU128Decoder, + getU128Encoder, + getU16Decoder, + getU16Encoder, + getU64Decoder, + getU64Encoder, + getU8Decoder, + getU8Encoder, + type Account, + type Address, + type Codec, + type Decoder, + type EncodedAccount, + type Encoder, + type FetchAccountConfig, + type FetchAccountsConfig, + type MaybeAccount, + type MaybeEncodedAccount, +} from '@solana/web3.js'; +import { + getVaultOperatorStakeWeightDecoder, + getVaultOperatorStakeWeightEncoder, + type VaultOperatorStakeWeight, + type VaultOperatorStakeWeightArgs, +} from '../types'; + +export type OperatorSnapshot = { + discriminator: bigint; + operator: Address; + ncn: Address; + ncnEpoch: bigint; + bump: number; + slotCreated: bigint; + slotFinalized: bigint; + isActive: number; + ncnOperatorIndex: bigint; + operatorIndex: bigint; + operatorFeeBps: number; + vaultOperatorDelegationCount: bigint; + vaultOperatorDelegationsRegistered: bigint; + validOperatorVaultDelegations: bigint; + stakeWeight: bigint; + reserved: Array; + vaultOperatorStakeWeight: Array; +}; + +export type OperatorSnapshotArgs = { + discriminator: number | bigint; + operator: Address; + ncn: Address; + ncnEpoch: number | bigint; + bump: number; + slotCreated: number | bigint; + slotFinalized: number | bigint; + isActive: number; + ncnOperatorIndex: number | bigint; + operatorIndex: number | bigint; + operatorFeeBps: number; + vaultOperatorDelegationCount: number | bigint; + vaultOperatorDelegationsRegistered: number | bigint; + validOperatorVaultDelegations: number | bigint; + stakeWeight: number | bigint; + reserved: Array; + vaultOperatorStakeWeight: Array; +}; + +export function getOperatorSnapshotEncoder(): Encoder { + return getStructEncoder([ + ['discriminator', getU64Encoder()], + ['operator', getAddressEncoder()], + ['ncn', getAddressEncoder()], + ['ncnEpoch', getU64Encoder()], + ['bump', getU8Encoder()], + ['slotCreated', getU64Encoder()], + ['slotFinalized', getU64Encoder()], + ['isActive', getBoolEncoder()], + ['ncnOperatorIndex', getU64Encoder()], + ['operatorIndex', getU64Encoder()], + ['operatorFeeBps', getU16Encoder()], + ['vaultOperatorDelegationCount', getU64Encoder()], + ['vaultOperatorDelegationsRegistered', getU64Encoder()], + ['validOperatorVaultDelegations', getU64Encoder()], + ['stakeWeight', getU128Encoder()], + ['reserved', getArrayEncoder(getU8Encoder(), { size: 256 })], + [ + 'vaultOperatorStakeWeight', + getArrayEncoder(getVaultOperatorStakeWeightEncoder(), { size: 32 }), + ], + ]); +} + +export function getOperatorSnapshotDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU64Decoder()], + ['operator', getAddressDecoder()], + ['ncn', getAddressDecoder()], + ['ncnEpoch', getU64Decoder()], + ['bump', getU8Decoder()], + ['slotCreated', getU64Decoder()], + ['slotFinalized', getU64Decoder()], + ['isActive', getBoolDecoder()], + ['ncnOperatorIndex', getU64Decoder()], + ['operatorIndex', getU64Decoder()], + ['operatorFeeBps', getU16Decoder()], + ['vaultOperatorDelegationCount', getU64Decoder()], + ['vaultOperatorDelegationsRegistered', getU64Decoder()], + ['validOperatorVaultDelegations', getU64Decoder()], + ['stakeWeight', getU128Decoder()], + ['reserved', getArrayDecoder(getU8Decoder(), { size: 256 })], + [ + 'vaultOperatorStakeWeight', + getArrayDecoder(getVaultOperatorStakeWeightDecoder(), { size: 32 }), + ], + ]); +} + +export function getOperatorSnapshotCodec(): Codec< + OperatorSnapshotArgs, + OperatorSnapshot +> { + return combineCodec( + getOperatorSnapshotEncoder(), + getOperatorSnapshotDecoder() + ); +} + +export function decodeOperatorSnapshot( + encodedAccount: EncodedAccount +): Account; +export function decodeOperatorSnapshot( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeOperatorSnapshot( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): + | Account + | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getOperatorSnapshotDecoder() + ); +} + +export async function fetchOperatorSnapshot( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeOperatorSnapshot(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeOperatorSnapshot< + TAddress extends string = string, +>( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeOperatorSnapshot(maybeAccount); +} + +export async function fetchAllOperatorSnapshot( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeOperatorSnapshot( + rpc, + addresses, + config + ); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeOperatorSnapshot( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config); + return maybeAccounts.map((maybeAccount) => + decodeOperatorSnapshot(maybeAccount) + ); +} diff --git a/clients/js/jito_tip_router/errors/jitoTipRouter.ts b/clients/js/jito_tip_router/errors/jitoTipRouter.ts index 1053652..74b4496 100644 --- a/clients/js/jito_tip_router/errors/jitoTipRouter.ts +++ b/clients/js/jito_tip_router/errors/jitoTipRouter.ts @@ -53,22 +53,51 @@ export const JITO_TIP_ROUTER_ERROR__TRACKED_MINTS_LOCKED = 0x220c; // 8716 /** VaultIndexAlreadyInUse: Vault index already in use by a different mint */ export const JITO_TIP_ROUTER_ERROR__VAULT_INDEX_ALREADY_IN_USE = 0x220d; // 8717 /** FeeCapExceeded: Fee cap exceeded */ -export const JITO_TIP_ROUTER_ERROR__FEE_CAP_EXCEEDED = 0x2300; // 8960 +export const JITO_TIP_ROUTER_ERROR__FEE_CAP_EXCEEDED = 0x220e; // 8718 /** IncorrectNcnAdmin: Incorrect NCN Admin */ -export const JITO_TIP_ROUTER_ERROR__INCORRECT_NCN_ADMIN = 0x2400; // 9216 +export const JITO_TIP_ROUTER_ERROR__INCORRECT_NCN_ADMIN = 0x220f; // 8719 /** IncorrectNcn: Incorrect NCN */ -export const JITO_TIP_ROUTER_ERROR__INCORRECT_NCN = 0x2401; // 9217 +export const JITO_TIP_ROUTER_ERROR__INCORRECT_NCN = 0x2210; // 8720 /** IncorrectFeeAdmin: Incorrect fee admin */ -export const JITO_TIP_ROUTER_ERROR__INCORRECT_FEE_ADMIN = 0x2402; // 9218 +export const JITO_TIP_ROUTER_ERROR__INCORRECT_FEE_ADMIN = 0x2211; // 8721 +/** WeightTableNotFinalized: Weight table not finalized */ +export const JITO_TIP_ROUTER_ERROR__WEIGHT_TABLE_NOT_FINALIZED = 0x2212; // 8722 +/** WeightNotFound: Weight not found */ +export const JITO_TIP_ROUTER_ERROR__WEIGHT_NOT_FOUND = 0x2213; // 8723 +/** NoOperators: No operators in ncn */ +export const JITO_TIP_ROUTER_ERROR__NO_OPERATORS = 0x2214; // 8724 +/** VaultOperatorDelegationFinalized: Vault operator delegation is already finalized - should not happen */ +export const JITO_TIP_ROUTER_ERROR__VAULT_OPERATOR_DELEGATION_FINALIZED = 0x2215; // 8725 +/** OperatorFinalized: Operator is already finalized - should not happen */ +export const JITO_TIP_ROUTER_ERROR__OPERATOR_FINALIZED = 0x2216; // 8726 +/** TooManyVaultOperatorDelegations: Too many vault operator delegations */ +export const JITO_TIP_ROUTER_ERROR__TOO_MANY_VAULT_OPERATOR_DELEGATIONS = 0x2217; // 8727 +/** DuplicateVaultOperatorDelegation: Duplicate vault operator delegation */ +export const JITO_TIP_ROUTER_ERROR__DUPLICATE_VAULT_OPERATOR_DELEGATION = 0x2218; // 8728 +/** DuplicateVoteCast: Duplicate Vote Cast */ +export const JITO_TIP_ROUTER_ERROR__DUPLICATE_VOTE_CAST = 0x2219; // 8729 +/** OperatorVotesFull: Operator votes full */ +export const JITO_TIP_ROUTER_ERROR__OPERATOR_VOTES_FULL = 0x221a; // 8730 +/** BallotTallyFull: Merkle root tally full */ +export const JITO_TIP_ROUTER_ERROR__BALLOT_TALLY_FULL = 0x221b; // 8731 +/** ConsensusAlreadyReached: Consensus already reached */ +export const JITO_TIP_ROUTER_ERROR__CONSENSUS_ALREADY_REACHED = 0x221c; // 8732 +/** ConsensusNotReached: Consensus not reached */ +export const JITO_TIP_ROUTER_ERROR__CONSENSUS_NOT_REACHED = 0x221d; // 8733 export type JitoTipRouterError = | typeof JITO_TIP_ROUTER_ERROR__ARITHMETIC_OVERFLOW + | typeof JITO_TIP_ROUTER_ERROR__BALLOT_TALLY_FULL | typeof JITO_TIP_ROUTER_ERROR__CANNOT_CREATE_FUTURE_WEIGHT_TABLES | typeof JITO_TIP_ROUTER_ERROR__CAST_TO_IMPRECISE_NUMBER_ERROR | typeof JITO_TIP_ROUTER_ERROR__CONFIG_MINT_LIST_FULL | typeof JITO_TIP_ROUTER_ERROR__CONFIG_MINTS_NOT_UPDATED + | typeof JITO_TIP_ROUTER_ERROR__CONSENSUS_ALREADY_REACHED + | typeof JITO_TIP_ROUTER_ERROR__CONSENSUS_NOT_REACHED | typeof JITO_TIP_ROUTER_ERROR__DENOMINATOR_IS_ZERO | typeof JITO_TIP_ROUTER_ERROR__DUPLICATE_MINTS_IN_TABLE + | typeof JITO_TIP_ROUTER_ERROR__DUPLICATE_VAULT_OPERATOR_DELEGATION + | typeof JITO_TIP_ROUTER_ERROR__DUPLICATE_VOTE_CAST | typeof JITO_TIP_ROUTER_ERROR__FEE_CAP_EXCEEDED | typeof JITO_TIP_ROUTER_ERROR__INCORRECT_FEE_ADMIN | typeof JITO_TIP_ROUTER_ERROR__INCORRECT_NCN @@ -78,24 +107,36 @@ export type JitoTipRouterError = | typeof JITO_TIP_ROUTER_ERROR__MODULO_OVERFLOW | typeof JITO_TIP_ROUTER_ERROR__NEW_PRECISE_NUMBER_ERROR | typeof JITO_TIP_ROUTER_ERROR__NO_MINTS_IN_TABLE + | typeof JITO_TIP_ROUTER_ERROR__NO_OPERATORS + | typeof JITO_TIP_ROUTER_ERROR__OPERATOR_FINALIZED + | typeof JITO_TIP_ROUTER_ERROR__OPERATOR_VOTES_FULL | typeof JITO_TIP_ROUTER_ERROR__TOO_MANY_MINTS_FOR_TABLE + | typeof JITO_TIP_ROUTER_ERROR__TOO_MANY_VAULT_OPERATOR_DELEGATIONS | typeof JITO_TIP_ROUTER_ERROR__TRACKED_MINT_LIST_FULL | typeof JITO_TIP_ROUTER_ERROR__TRACKED_MINTS_LOCKED | typeof JITO_TIP_ROUTER_ERROR__VAULT_INDEX_ALREADY_IN_USE + | typeof JITO_TIP_ROUTER_ERROR__VAULT_OPERATOR_DELEGATION_FINALIZED | typeof JITO_TIP_ROUTER_ERROR__WEIGHT_MINTS_DO_NOT_MATCH_LENGTH | typeof JITO_TIP_ROUTER_ERROR__WEIGHT_MINTS_DO_NOT_MATCH_MINT_HASH - | typeof JITO_TIP_ROUTER_ERROR__WEIGHT_TABLE_ALREADY_INITIALIZED; + | typeof JITO_TIP_ROUTER_ERROR__WEIGHT_NOT_FOUND + | typeof JITO_TIP_ROUTER_ERROR__WEIGHT_TABLE_ALREADY_INITIALIZED + | typeof JITO_TIP_ROUTER_ERROR__WEIGHT_TABLE_NOT_FINALIZED; let jitoTipRouterErrorMessages: Record | undefined; if (process.env.NODE_ENV !== 'production') { jitoTipRouterErrorMessages = { [JITO_TIP_ROUTER_ERROR__ARITHMETIC_OVERFLOW]: `Overflow`, + [JITO_TIP_ROUTER_ERROR__BALLOT_TALLY_FULL]: `Merkle root tally full`, [JITO_TIP_ROUTER_ERROR__CANNOT_CREATE_FUTURE_WEIGHT_TABLES]: `Cannnot create future weight tables`, [JITO_TIP_ROUTER_ERROR__CAST_TO_IMPRECISE_NUMBER_ERROR]: `Cast to imprecise number error`, [JITO_TIP_ROUTER_ERROR__CONFIG_MINT_LIST_FULL]: `NCN config vaults are at capacity`, [JITO_TIP_ROUTER_ERROR__CONFIG_MINTS_NOT_UPDATED]: `Config supported mints do not match NCN Vault Count`, + [JITO_TIP_ROUTER_ERROR__CONSENSUS_ALREADY_REACHED]: `Consensus already reached`, + [JITO_TIP_ROUTER_ERROR__CONSENSUS_NOT_REACHED]: `Consensus not reached`, [JITO_TIP_ROUTER_ERROR__DENOMINATOR_IS_ZERO]: `Zero in the denominator`, [JITO_TIP_ROUTER_ERROR__DUPLICATE_MINTS_IN_TABLE]: `Duplicate mints in table`, + [JITO_TIP_ROUTER_ERROR__DUPLICATE_VAULT_OPERATOR_DELEGATION]: `Duplicate vault operator delegation`, + [JITO_TIP_ROUTER_ERROR__DUPLICATE_VOTE_CAST]: `Duplicate Vote Cast`, [JITO_TIP_ROUTER_ERROR__FEE_CAP_EXCEEDED]: `Fee cap exceeded`, [JITO_TIP_ROUTER_ERROR__INCORRECT_FEE_ADMIN]: `Incorrect fee admin`, [JITO_TIP_ROUTER_ERROR__INCORRECT_NCN]: `Incorrect NCN`, @@ -105,13 +146,20 @@ if (process.env.NODE_ENV !== 'production') { [JITO_TIP_ROUTER_ERROR__MODULO_OVERFLOW]: `Modulo Overflow`, [JITO_TIP_ROUTER_ERROR__NEW_PRECISE_NUMBER_ERROR]: `New precise number error`, [JITO_TIP_ROUTER_ERROR__NO_MINTS_IN_TABLE]: `There are no mints in the table`, + [JITO_TIP_ROUTER_ERROR__NO_OPERATORS]: `No operators in ncn`, + [JITO_TIP_ROUTER_ERROR__OPERATOR_FINALIZED]: `Operator is already finalized - should not happen`, + [JITO_TIP_ROUTER_ERROR__OPERATOR_VOTES_FULL]: `Operator votes full`, [JITO_TIP_ROUTER_ERROR__TOO_MANY_MINTS_FOR_TABLE]: `Too many mints for table`, + [JITO_TIP_ROUTER_ERROR__TOO_MANY_VAULT_OPERATOR_DELEGATIONS]: `Too many vault operator delegations`, [JITO_TIP_ROUTER_ERROR__TRACKED_MINT_LIST_FULL]: `Tracked mints are at capacity`, [JITO_TIP_ROUTER_ERROR__TRACKED_MINTS_LOCKED]: `Tracked mints are locked for the epoch`, [JITO_TIP_ROUTER_ERROR__VAULT_INDEX_ALREADY_IN_USE]: `Vault index already in use by a different mint`, + [JITO_TIP_ROUTER_ERROR__VAULT_OPERATOR_DELEGATION_FINALIZED]: `Vault operator delegation is already finalized - should not happen`, [JITO_TIP_ROUTER_ERROR__WEIGHT_MINTS_DO_NOT_MATCH_LENGTH]: `Weight mints do not match - length`, [JITO_TIP_ROUTER_ERROR__WEIGHT_MINTS_DO_NOT_MATCH_MINT_HASH]: `Weight mints do not match - mint hash`, + [JITO_TIP_ROUTER_ERROR__WEIGHT_NOT_FOUND]: `Weight not found`, [JITO_TIP_ROUTER_ERROR__WEIGHT_TABLE_ALREADY_INITIALIZED]: `Weight table already initialized`, + [JITO_TIP_ROUTER_ERROR__WEIGHT_TABLE_NOT_FINALIZED]: `Weight table not finalized`, }; } diff --git a/clients/js/jito_tip_router/instructions/adminUpdateWeightTable.ts b/clients/js/jito_tip_router/instructions/adminUpdateWeightTable.ts index 36e207f..033d67a 100644 --- a/clients/js/jito_tip_router/instructions/adminUpdateWeightTable.ts +++ b/clients/js/jito_tip_router/instructions/adminUpdateWeightTable.ts @@ -46,7 +46,7 @@ export type AdminUpdateWeightTableInstruction< TAccountWeightTable extends string | IAccountMeta = string, TAccountWeightTableAdmin extends string | IAccountMeta = string, TAccountMint extends string | IAccountMeta = string, - TAccountRestakingProgramId extends string | IAccountMeta = string, + TAccountRestakingProgram extends string | IAccountMeta = string, TRemainingAccounts extends readonly IAccountMeta[] = [], > = IInstruction & IInstructionWithData & @@ -63,9 +63,9 @@ export type AdminUpdateWeightTableInstruction< TAccountMint extends string ? ReadonlyAccount : TAccountMint, - TAccountRestakingProgramId extends string - ? ReadonlyAccount - : TAccountRestakingProgramId, + TAccountRestakingProgram extends string + ? ReadonlyAccount + : TAccountRestakingProgram, ...TRemainingAccounts, ] >; @@ -118,13 +118,13 @@ export type AdminUpdateWeightTableInput< TAccountWeightTable extends string = string, TAccountWeightTableAdmin extends string = string, TAccountMint extends string = string, - TAccountRestakingProgramId extends string = string, + TAccountRestakingProgram extends string = string, > = { ncn: Address; weightTable: Address; weightTableAdmin: TransactionSigner; mint: Address; - restakingProgramId: Address; + restakingProgram: Address; ncnEpoch: AdminUpdateWeightTableInstructionDataArgs['ncnEpoch']; weight: AdminUpdateWeightTableInstructionDataArgs['weight']; }; @@ -134,7 +134,7 @@ export function getAdminUpdateWeightTableInstruction< TAccountWeightTable extends string, TAccountWeightTableAdmin extends string, TAccountMint extends string, - TAccountRestakingProgramId extends string, + TAccountRestakingProgram extends string, TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, >( input: AdminUpdateWeightTableInput< @@ -142,7 +142,7 @@ export function getAdminUpdateWeightTableInstruction< TAccountWeightTable, TAccountWeightTableAdmin, TAccountMint, - TAccountRestakingProgramId + TAccountRestakingProgram >, config?: { programAddress?: TProgramAddress } ): AdminUpdateWeightTableInstruction< @@ -151,7 +151,7 @@ export function getAdminUpdateWeightTableInstruction< TAccountWeightTable, TAccountWeightTableAdmin, TAccountMint, - TAccountRestakingProgramId + TAccountRestakingProgram > { // Program address. const programAddress = @@ -166,8 +166,8 @@ export function getAdminUpdateWeightTableInstruction< isWritable: false, }, mint: { value: input.mint ?? null, isWritable: false }, - restakingProgramId: { - value: input.restakingProgramId ?? null, + restakingProgram: { + value: input.restakingProgram ?? null, isWritable: false, }, }; @@ -186,7 +186,7 @@ export function getAdminUpdateWeightTableInstruction< getAccountMeta(accounts.weightTable), getAccountMeta(accounts.weightTableAdmin), getAccountMeta(accounts.mint), - getAccountMeta(accounts.restakingProgramId), + getAccountMeta(accounts.restakingProgram), ], programAddress, data: getAdminUpdateWeightTableInstructionDataEncoder().encode( @@ -198,7 +198,7 @@ export function getAdminUpdateWeightTableInstruction< TAccountWeightTable, TAccountWeightTableAdmin, TAccountMint, - TAccountRestakingProgramId + TAccountRestakingProgram >; return instruction; @@ -214,7 +214,7 @@ export type ParsedAdminUpdateWeightTableInstruction< weightTable: TAccountMetas[1]; weightTableAdmin: TAccountMetas[2]; mint: TAccountMetas[3]; - restakingProgramId: TAccountMetas[4]; + restakingProgram: TAccountMetas[4]; }; data: AdminUpdateWeightTableInstructionData; }; @@ -244,7 +244,7 @@ export function parseAdminUpdateWeightTableInstruction< weightTable: getNextAccount(), weightTableAdmin: getNextAccount(), mint: getNextAccount(), - restakingProgramId: getNextAccount(), + restakingProgram: getNextAccount(), }, data: getAdminUpdateWeightTableInstructionDataDecoder().decode( instruction.data diff --git a/clients/js/jito_tip_router/instructions/index.ts b/clients/js/jito_tip_router/instructions/index.ts index b46aa8b..5771c79 100644 --- a/clients/js/jito_tip_router/instructions/index.ts +++ b/clients/js/jito_tip_router/instructions/index.ts @@ -7,9 +7,12 @@ */ export * from './adminUpdateWeightTable'; +export * from './initializeEpochSnapshot'; export * from './initializeNCNConfig'; +export * from './initializeOperatorSnapshot'; export * from './initializeTrackedMints'; export * from './initializeWeightTable'; export * from './registerMint'; export * from './setConfigFees'; export * from './setNewAdmin'; +export * from './snapshotVaultOperatorDelegation'; diff --git a/clients/js/jito_tip_router/instructions/initializeEpochSnapshot.ts b/clients/js/jito_tip_router/instructions/initializeEpochSnapshot.ts new file mode 100644 index 0000000..a011b9c --- /dev/null +++ b/clients/js/jito_tip_router/instructions/initializeEpochSnapshot.ts @@ -0,0 +1,314 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + combineCodec, + getOptionDecoder, + getOptionEncoder, + getStructDecoder, + getStructEncoder, + getU64Decoder, + getU64Encoder, + getU8Decoder, + getU8Encoder, + transformEncoder, + type Address, + type Codec, + type Decoder, + type Encoder, + type IAccountMeta, + type IAccountSignerMeta, + type IInstruction, + type IInstructionWithAccounts, + type IInstructionWithData, + type Option, + type OptionOrNullable, + type ReadonlyAccount, + type TransactionSigner, + type WritableAccount, + type WritableSignerAccount, +} from '@solana/web3.js'; +import { JITO_TIP_ROUTER_PROGRAM_ADDRESS } from '../programs'; +import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; + +export const INITIALIZE_EPOCH_SNAPSHOT_DISCRIMINATOR = 5; + +export function getInitializeEpochSnapshotDiscriminatorBytes() { + return getU8Encoder().encode(INITIALIZE_EPOCH_SNAPSHOT_DISCRIMINATOR); +} + +export type InitializeEpochSnapshotInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountNcnConfig extends string | IAccountMeta = string, + TAccountRestakingConfig extends string | IAccountMeta = string, + TAccountNcn extends string | IAccountMeta = string, + TAccountTrackedMints extends string | IAccountMeta = string, + TAccountWeightTable extends string | IAccountMeta = string, + TAccountEpochSnapshot extends string | IAccountMeta = string, + TAccountPayer extends string | IAccountMeta = string, + TAccountRestakingProgram extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountNcnConfig extends string + ? ReadonlyAccount + : TAccountNcnConfig, + TAccountRestakingConfig extends string + ? ReadonlyAccount + : TAccountRestakingConfig, + TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, + TAccountTrackedMints extends string + ? ReadonlyAccount + : TAccountTrackedMints, + TAccountWeightTable extends string + ? ReadonlyAccount + : TAccountWeightTable, + TAccountEpochSnapshot extends string + ? WritableAccount + : TAccountEpochSnapshot, + TAccountPayer extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountPayer, + TAccountRestakingProgram extends string + ? ReadonlyAccount + : TAccountRestakingProgram, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type InitializeEpochSnapshotInstructionData = { + discriminator: number; + firstSlotOfNcnEpoch: Option; +}; + +export type InitializeEpochSnapshotInstructionDataArgs = { + firstSlotOfNcnEpoch: OptionOrNullable; +}; + +export function getInitializeEpochSnapshotInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ]), + (value) => ({ + ...value, + discriminator: INITIALIZE_EPOCH_SNAPSHOT_DISCRIMINATOR, + }) + ); +} + +export function getInitializeEpochSnapshotInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ]); +} + +export function getInitializeEpochSnapshotInstructionDataCodec(): Codec< + InitializeEpochSnapshotInstructionDataArgs, + InitializeEpochSnapshotInstructionData +> { + return combineCodec( + getInitializeEpochSnapshotInstructionDataEncoder(), + getInitializeEpochSnapshotInstructionDataDecoder() + ); +} + +export type InitializeEpochSnapshotInput< + TAccountNcnConfig extends string = string, + TAccountRestakingConfig extends string = string, + TAccountNcn extends string = string, + TAccountTrackedMints extends string = string, + TAccountWeightTable extends string = string, + TAccountEpochSnapshot extends string = string, + TAccountPayer extends string = string, + TAccountRestakingProgram extends string = string, + TAccountSystemProgram extends string = string, +> = { + ncnConfig: Address; + restakingConfig: Address; + ncn: Address; + trackedMints: Address; + weightTable: Address; + epochSnapshot: Address; + payer: TransactionSigner; + restakingProgram: Address; + systemProgram?: Address; + firstSlotOfNcnEpoch: InitializeEpochSnapshotInstructionDataArgs['firstSlotOfNcnEpoch']; +}; + +export function getInitializeEpochSnapshotInstruction< + TAccountNcnConfig extends string, + TAccountRestakingConfig extends string, + TAccountNcn extends string, + TAccountTrackedMints extends string, + TAccountWeightTable extends string, + TAccountEpochSnapshot extends string, + TAccountPayer extends string, + TAccountRestakingProgram extends string, + TAccountSystemProgram extends string, + TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, +>( + input: InitializeEpochSnapshotInput< + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountTrackedMints, + TAccountWeightTable, + TAccountEpochSnapshot, + TAccountPayer, + TAccountRestakingProgram, + TAccountSystemProgram + >, + config?: { programAddress?: TProgramAddress } +): InitializeEpochSnapshotInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountTrackedMints, + TAccountWeightTable, + TAccountEpochSnapshot, + TAccountPayer, + TAccountRestakingProgram, + TAccountSystemProgram +> { + // Program address. + const programAddress = + config?.programAddress ?? JITO_TIP_ROUTER_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + ncnConfig: { value: input.ncnConfig ?? null, isWritable: false }, + restakingConfig: { + value: input.restakingConfig ?? null, + isWritable: false, + }, + ncn: { value: input.ncn ?? null, isWritable: false }, + trackedMints: { value: input.trackedMints ?? null, isWritable: false }, + weightTable: { value: input.weightTable ?? null, isWritable: false }, + epochSnapshot: { value: input.epochSnapshot ?? null, isWritable: true }, + payer: { value: input.payer ?? null, isWritable: true }, + restakingProgram: { + value: input.restakingProgram ?? null, + isWritable: false, + }, + systemProgram: { value: input.systemProgram ?? null, isWritable: false }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + // Resolve default values. + if (!accounts.systemProgram.value) { + accounts.systemProgram.value = + '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>; + } + + const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); + const instruction = { + accounts: [ + getAccountMeta(accounts.ncnConfig), + getAccountMeta(accounts.restakingConfig), + getAccountMeta(accounts.ncn), + getAccountMeta(accounts.trackedMints), + getAccountMeta(accounts.weightTable), + getAccountMeta(accounts.epochSnapshot), + getAccountMeta(accounts.payer), + getAccountMeta(accounts.restakingProgram), + getAccountMeta(accounts.systemProgram), + ], + programAddress, + data: getInitializeEpochSnapshotInstructionDataEncoder().encode( + args as InitializeEpochSnapshotInstructionDataArgs + ), + } as InitializeEpochSnapshotInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountTrackedMints, + TAccountWeightTable, + TAccountEpochSnapshot, + TAccountPayer, + TAccountRestakingProgram, + TAccountSystemProgram + >; + + return instruction; +} + +export type ParsedInitializeEpochSnapshotInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], +> = { + programAddress: Address; + accounts: { + ncnConfig: TAccountMetas[0]; + restakingConfig: TAccountMetas[1]; + ncn: TAccountMetas[2]; + trackedMints: TAccountMetas[3]; + weightTable: TAccountMetas[4]; + epochSnapshot: TAccountMetas[5]; + payer: TAccountMetas[6]; + restakingProgram: TAccountMetas[7]; + systemProgram: TAccountMetas[8]; + }; + data: InitializeEpochSnapshotInstructionData; +}; + +export function parseInitializeEpochSnapshotInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedInitializeEpochSnapshotInstruction { + if (instruction.accounts.length < 9) { + // TODO: Coded error. + throw new Error('Not enough accounts'); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = instruction.accounts![accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { + ncnConfig: getNextAccount(), + restakingConfig: getNextAccount(), + ncn: getNextAccount(), + trackedMints: getNextAccount(), + weightTable: getNextAccount(), + epochSnapshot: getNextAccount(), + payer: getNextAccount(), + restakingProgram: getNextAccount(), + systemProgram: getNextAccount(), + }, + data: getInitializeEpochSnapshotInstructionDataDecoder().decode( + instruction.data + ), + }; +} diff --git a/clients/js/jito_tip_router/instructions/initializeNCNConfig.ts b/clients/js/jito_tip_router/instructions/initializeNCNConfig.ts index 57012dd..ccb9263 100644 --- a/clients/js/jito_tip_router/instructions/initializeNCNConfig.ts +++ b/clients/js/jito_tip_router/instructions/initializeNCNConfig.ts @@ -46,7 +46,7 @@ export type InitializeNCNConfigInstruction< TAccountNcnAdmin extends string | IAccountMeta = string, TAccountFeeWallet extends string | IAccountMeta = string, TAccountTieBreakerAdmin extends string | IAccountMeta = string, - TAccountRestakingProgramId extends string | IAccountMeta = string, + TAccountRestakingProgram extends string | IAccountMeta = string, TAccountSystemProgram extends | string | IAccountMeta = '11111111111111111111111111111111', @@ -72,9 +72,9 @@ export type InitializeNCNConfigInstruction< TAccountTieBreakerAdmin extends string ? ReadonlyAccount : TAccountTieBreakerAdmin, - TAccountRestakingProgramId extends string - ? ReadonlyAccount - : TAccountRestakingProgramId, + TAccountRestakingProgram extends string + ? ReadonlyAccount + : TAccountRestakingProgram, TAccountSystemProgram extends string ? ReadonlyAccount : TAccountSystemProgram, @@ -136,7 +136,7 @@ export type InitializeNCNConfigInput< TAccountNcnAdmin extends string = string, TAccountFeeWallet extends string = string, TAccountTieBreakerAdmin extends string = string, - TAccountRestakingProgramId extends string = string, + TAccountRestakingProgram extends string = string, TAccountSystemProgram extends string = string, > = { restakingConfig: Address; @@ -145,7 +145,7 @@ export type InitializeNCNConfigInput< ncnAdmin: TransactionSigner; feeWallet: Address; tieBreakerAdmin: Address; - restakingProgramId: Address; + restakingProgram: Address; systemProgram?: Address; daoFeeBps: InitializeNCNConfigInstructionDataArgs['daoFeeBps']; ncnFeeBps: InitializeNCNConfigInstructionDataArgs['ncnFeeBps']; @@ -159,7 +159,7 @@ export function getInitializeNCNConfigInstruction< TAccountNcnAdmin extends string, TAccountFeeWallet extends string, TAccountTieBreakerAdmin extends string, - TAccountRestakingProgramId extends string, + TAccountRestakingProgram extends string, TAccountSystemProgram extends string, TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, >( @@ -170,7 +170,7 @@ export function getInitializeNCNConfigInstruction< TAccountNcnAdmin, TAccountFeeWallet, TAccountTieBreakerAdmin, - TAccountRestakingProgramId, + TAccountRestakingProgram, TAccountSystemProgram >, config?: { programAddress?: TProgramAddress } @@ -182,7 +182,7 @@ export function getInitializeNCNConfigInstruction< TAccountNcnAdmin, TAccountFeeWallet, TAccountTieBreakerAdmin, - TAccountRestakingProgramId, + TAccountRestakingProgram, TAccountSystemProgram > { // Program address. @@ -203,8 +203,8 @@ export function getInitializeNCNConfigInstruction< value: input.tieBreakerAdmin ?? null, isWritable: false, }, - restakingProgramId: { - value: input.restakingProgramId ?? null, + restakingProgram: { + value: input.restakingProgram ?? null, isWritable: false, }, systemProgram: { value: input.systemProgram ?? null, isWritable: false }, @@ -232,7 +232,7 @@ export function getInitializeNCNConfigInstruction< getAccountMeta(accounts.ncnAdmin), getAccountMeta(accounts.feeWallet), getAccountMeta(accounts.tieBreakerAdmin), - getAccountMeta(accounts.restakingProgramId), + getAccountMeta(accounts.restakingProgram), getAccountMeta(accounts.systemProgram), ], programAddress, @@ -247,7 +247,7 @@ export function getInitializeNCNConfigInstruction< TAccountNcnAdmin, TAccountFeeWallet, TAccountTieBreakerAdmin, - TAccountRestakingProgramId, + TAccountRestakingProgram, TAccountSystemProgram >; @@ -266,7 +266,7 @@ export type ParsedInitializeNCNConfigInstruction< ncnAdmin: TAccountMetas[3]; feeWallet: TAccountMetas[4]; tieBreakerAdmin: TAccountMetas[5]; - restakingProgramId: TAccountMetas[6]; + restakingProgram: TAccountMetas[6]; systemProgram: TAccountMetas[7]; }; data: InitializeNCNConfigInstructionData; @@ -299,7 +299,7 @@ export function parseInitializeNCNConfigInstruction< ncnAdmin: getNextAccount(), feeWallet: getNextAccount(), tieBreakerAdmin: getNextAccount(), - restakingProgramId: getNextAccount(), + restakingProgram: getNextAccount(), systemProgram: getNextAccount(), }, data: getInitializeNCNConfigInstructionDataDecoder().decode( diff --git a/clients/js/jito_tip_router/instructions/initializeOperatorSnapshot.ts b/clients/js/jito_tip_router/instructions/initializeOperatorSnapshot.ts new file mode 100644 index 0000000..8e70d5c --- /dev/null +++ b/clients/js/jito_tip_router/instructions/initializeOperatorSnapshot.ts @@ -0,0 +1,334 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + combineCodec, + getOptionDecoder, + getOptionEncoder, + getStructDecoder, + getStructEncoder, + getU64Decoder, + getU64Encoder, + getU8Decoder, + getU8Encoder, + transformEncoder, + type Address, + type Codec, + type Decoder, + type Encoder, + type IAccountMeta, + type IAccountSignerMeta, + type IInstruction, + type IInstructionWithAccounts, + type IInstructionWithData, + type Option, + type OptionOrNullable, + type ReadonlyAccount, + type TransactionSigner, + type WritableAccount, + type WritableSignerAccount, +} from '@solana/web3.js'; +import { JITO_TIP_ROUTER_PROGRAM_ADDRESS } from '../programs'; +import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; + +export const INITIALIZE_OPERATOR_SNAPSHOT_DISCRIMINATOR = 6; + +export function getInitializeOperatorSnapshotDiscriminatorBytes() { + return getU8Encoder().encode(INITIALIZE_OPERATOR_SNAPSHOT_DISCRIMINATOR); +} + +export type InitializeOperatorSnapshotInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountNcnConfig extends string | IAccountMeta = string, + TAccountRestakingConfig extends string | IAccountMeta = string, + TAccountNcn extends string | IAccountMeta = string, + TAccountOperator extends string | IAccountMeta = string, + TAccountNcnOperatorState extends string | IAccountMeta = string, + TAccountEpochSnapshot extends string | IAccountMeta = string, + TAccountOperatorSnapshot extends string | IAccountMeta = string, + TAccountPayer extends string | IAccountMeta = string, + TAccountRestakingProgram extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountNcnConfig extends string + ? ReadonlyAccount + : TAccountNcnConfig, + TAccountRestakingConfig extends string + ? ReadonlyAccount + : TAccountRestakingConfig, + TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, + TAccountOperator extends string + ? ReadonlyAccount + : TAccountOperator, + TAccountNcnOperatorState extends string + ? ReadonlyAccount + : TAccountNcnOperatorState, + TAccountEpochSnapshot extends string + ? WritableAccount + : TAccountEpochSnapshot, + TAccountOperatorSnapshot extends string + ? WritableAccount + : TAccountOperatorSnapshot, + TAccountPayer extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountPayer, + TAccountRestakingProgram extends string + ? ReadonlyAccount + : TAccountRestakingProgram, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type InitializeOperatorSnapshotInstructionData = { + discriminator: number; + firstSlotOfNcnEpoch: Option; +}; + +export type InitializeOperatorSnapshotInstructionDataArgs = { + firstSlotOfNcnEpoch: OptionOrNullable; +}; + +export function getInitializeOperatorSnapshotInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ]), + (value) => ({ + ...value, + discriminator: INITIALIZE_OPERATOR_SNAPSHOT_DISCRIMINATOR, + }) + ); +} + +export function getInitializeOperatorSnapshotInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ]); +} + +export function getInitializeOperatorSnapshotInstructionDataCodec(): Codec< + InitializeOperatorSnapshotInstructionDataArgs, + InitializeOperatorSnapshotInstructionData +> { + return combineCodec( + getInitializeOperatorSnapshotInstructionDataEncoder(), + getInitializeOperatorSnapshotInstructionDataDecoder() + ); +} + +export type InitializeOperatorSnapshotInput< + TAccountNcnConfig extends string = string, + TAccountRestakingConfig extends string = string, + TAccountNcn extends string = string, + TAccountOperator extends string = string, + TAccountNcnOperatorState extends string = string, + TAccountEpochSnapshot extends string = string, + TAccountOperatorSnapshot extends string = string, + TAccountPayer extends string = string, + TAccountRestakingProgram extends string = string, + TAccountSystemProgram extends string = string, +> = { + ncnConfig: Address; + restakingConfig: Address; + ncn: Address; + operator: Address; + ncnOperatorState: Address; + epochSnapshot: Address; + operatorSnapshot: Address; + payer: TransactionSigner; + restakingProgram: Address; + systemProgram?: Address; + firstSlotOfNcnEpoch: InitializeOperatorSnapshotInstructionDataArgs['firstSlotOfNcnEpoch']; +}; + +export function getInitializeOperatorSnapshotInstruction< + TAccountNcnConfig extends string, + TAccountRestakingConfig extends string, + TAccountNcn extends string, + TAccountOperator extends string, + TAccountNcnOperatorState extends string, + TAccountEpochSnapshot extends string, + TAccountOperatorSnapshot extends string, + TAccountPayer extends string, + TAccountRestakingProgram extends string, + TAccountSystemProgram extends string, + TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, +>( + input: InitializeOperatorSnapshotInput< + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountOperator, + TAccountNcnOperatorState, + TAccountEpochSnapshot, + TAccountOperatorSnapshot, + TAccountPayer, + TAccountRestakingProgram, + TAccountSystemProgram + >, + config?: { programAddress?: TProgramAddress } +): InitializeOperatorSnapshotInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountOperator, + TAccountNcnOperatorState, + TAccountEpochSnapshot, + TAccountOperatorSnapshot, + TAccountPayer, + TAccountRestakingProgram, + TAccountSystemProgram +> { + // Program address. + const programAddress = + config?.programAddress ?? JITO_TIP_ROUTER_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + ncnConfig: { value: input.ncnConfig ?? null, isWritable: false }, + restakingConfig: { + value: input.restakingConfig ?? null, + isWritable: false, + }, + ncn: { value: input.ncn ?? null, isWritable: false }, + operator: { value: input.operator ?? null, isWritable: false }, + ncnOperatorState: { + value: input.ncnOperatorState ?? null, + isWritable: false, + }, + epochSnapshot: { value: input.epochSnapshot ?? null, isWritable: true }, + operatorSnapshot: { + value: input.operatorSnapshot ?? null, + isWritable: true, + }, + payer: { value: input.payer ?? null, isWritable: true }, + restakingProgram: { + value: input.restakingProgram ?? null, + isWritable: false, + }, + systemProgram: { value: input.systemProgram ?? null, isWritable: false }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + // Resolve default values. + if (!accounts.systemProgram.value) { + accounts.systemProgram.value = + '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>; + } + + const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); + const instruction = { + accounts: [ + getAccountMeta(accounts.ncnConfig), + getAccountMeta(accounts.restakingConfig), + getAccountMeta(accounts.ncn), + getAccountMeta(accounts.operator), + getAccountMeta(accounts.ncnOperatorState), + getAccountMeta(accounts.epochSnapshot), + getAccountMeta(accounts.operatorSnapshot), + getAccountMeta(accounts.payer), + getAccountMeta(accounts.restakingProgram), + getAccountMeta(accounts.systemProgram), + ], + programAddress, + data: getInitializeOperatorSnapshotInstructionDataEncoder().encode( + args as InitializeOperatorSnapshotInstructionDataArgs + ), + } as InitializeOperatorSnapshotInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountOperator, + TAccountNcnOperatorState, + TAccountEpochSnapshot, + TAccountOperatorSnapshot, + TAccountPayer, + TAccountRestakingProgram, + TAccountSystemProgram + >; + + return instruction; +} + +export type ParsedInitializeOperatorSnapshotInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], +> = { + programAddress: Address; + accounts: { + ncnConfig: TAccountMetas[0]; + restakingConfig: TAccountMetas[1]; + ncn: TAccountMetas[2]; + operator: TAccountMetas[3]; + ncnOperatorState: TAccountMetas[4]; + epochSnapshot: TAccountMetas[5]; + operatorSnapshot: TAccountMetas[6]; + payer: TAccountMetas[7]; + restakingProgram: TAccountMetas[8]; + systemProgram: TAccountMetas[9]; + }; + data: InitializeOperatorSnapshotInstructionData; +}; + +export function parseInitializeOperatorSnapshotInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedInitializeOperatorSnapshotInstruction { + if (instruction.accounts.length < 10) { + // TODO: Coded error. + throw new Error('Not enough accounts'); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = instruction.accounts![accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { + ncnConfig: getNextAccount(), + restakingConfig: getNextAccount(), + ncn: getNextAccount(), + operator: getNextAccount(), + ncnOperatorState: getNextAccount(), + epochSnapshot: getNextAccount(), + operatorSnapshot: getNextAccount(), + payer: getNextAccount(), + restakingProgram: getNextAccount(), + systemProgram: getNextAccount(), + }, + data: getInitializeOperatorSnapshotInstructionDataDecoder().decode( + instruction.data + ), + }; +} diff --git a/clients/js/jito_tip_router/instructions/initializeTrackedMints.ts b/clients/js/jito_tip_router/instructions/initializeTrackedMints.ts index a113a59..f9cbd37 100644 --- a/clients/js/jito_tip_router/instructions/initializeTrackedMints.ts +++ b/clients/js/jito_tip_router/instructions/initializeTrackedMints.ts @@ -30,7 +30,7 @@ import { import { JITO_TIP_ROUTER_PROGRAM_ADDRESS } from '../programs'; import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; -export const INITIALIZE_TRACKED_MINTS_DISCRIMINATOR = 6; +export const INITIALIZE_TRACKED_MINTS_DISCRIMINATOR = 9; export function getInitializeTrackedMintsDiscriminatorBytes() { return getU8Encoder().encode(INITIALIZE_TRACKED_MINTS_DISCRIMINATOR); diff --git a/clients/js/jito_tip_router/instructions/initializeWeightTable.ts b/clients/js/jito_tip_router/instructions/initializeWeightTable.ts index 2b1f90a..e54e3af 100644 --- a/clients/js/jito_tip_router/instructions/initializeWeightTable.ts +++ b/clients/js/jito_tip_router/instructions/initializeWeightTable.ts @@ -49,7 +49,7 @@ export type InitializeWeightTableInstruction< TAccountNcn extends string | IAccountMeta = string, TAccountWeightTable extends string | IAccountMeta = string, TAccountPayer extends string | IAccountMeta = string, - TAccountRestakingProgramId extends string | IAccountMeta = string, + TAccountRestakingProgram extends string | IAccountMeta = string, TAccountSystemProgram extends | string | IAccountMeta = '11111111111111111111111111111111', @@ -72,9 +72,9 @@ export type InitializeWeightTableInstruction< ? WritableSignerAccount & IAccountSignerMeta : TAccountPayer, - TAccountRestakingProgramId extends string - ? ReadonlyAccount - : TAccountRestakingProgramId, + TAccountRestakingProgram extends string + ? ReadonlyAccount + : TAccountRestakingProgram, TAccountSystemProgram extends string ? ReadonlyAccount : TAccountSystemProgram, @@ -127,7 +127,7 @@ export type InitializeWeightTableInput< TAccountNcn extends string = string, TAccountWeightTable extends string = string, TAccountPayer extends string = string, - TAccountRestakingProgramId extends string = string, + TAccountRestakingProgram extends string = string, TAccountSystemProgram extends string = string, > = { restakingConfig: Address; @@ -135,7 +135,7 @@ export type InitializeWeightTableInput< ncn: Address; weightTable: Address; payer: TransactionSigner; - restakingProgramId: Address; + restakingProgram: Address; systemProgram?: Address; firstSlotOfNcnEpoch: InitializeWeightTableInstructionDataArgs['firstSlotOfNcnEpoch']; }; @@ -146,7 +146,7 @@ export function getInitializeWeightTableInstruction< TAccountNcn extends string, TAccountWeightTable extends string, TAccountPayer extends string, - TAccountRestakingProgramId extends string, + TAccountRestakingProgram extends string, TAccountSystemProgram extends string, TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, >( @@ -156,7 +156,7 @@ export function getInitializeWeightTableInstruction< TAccountNcn, TAccountWeightTable, TAccountPayer, - TAccountRestakingProgramId, + TAccountRestakingProgram, TAccountSystemProgram >, config?: { programAddress?: TProgramAddress } @@ -167,7 +167,7 @@ export function getInitializeWeightTableInstruction< TAccountNcn, TAccountWeightTable, TAccountPayer, - TAccountRestakingProgramId, + TAccountRestakingProgram, TAccountSystemProgram > { // Program address. @@ -184,8 +184,8 @@ export function getInitializeWeightTableInstruction< ncn: { value: input.ncn ?? null, isWritable: false }, weightTable: { value: input.weightTable ?? null, isWritable: true }, payer: { value: input.payer ?? null, isWritable: true }, - restakingProgramId: { - value: input.restakingProgramId ?? null, + restakingProgram: { + value: input.restakingProgram ?? null, isWritable: false, }, systemProgram: { value: input.systemProgram ?? null, isWritable: false }, @@ -212,7 +212,7 @@ export function getInitializeWeightTableInstruction< getAccountMeta(accounts.ncn), getAccountMeta(accounts.weightTable), getAccountMeta(accounts.payer), - getAccountMeta(accounts.restakingProgramId), + getAccountMeta(accounts.restakingProgram), getAccountMeta(accounts.systemProgram), ], programAddress, @@ -226,7 +226,7 @@ export function getInitializeWeightTableInstruction< TAccountNcn, TAccountWeightTable, TAccountPayer, - TAccountRestakingProgramId, + TAccountRestakingProgram, TAccountSystemProgram >; @@ -244,7 +244,7 @@ export type ParsedInitializeWeightTableInstruction< ncn: TAccountMetas[2]; weightTable: TAccountMetas[3]; payer: TAccountMetas[4]; - restakingProgramId: TAccountMetas[5]; + restakingProgram: TAccountMetas[5]; systemProgram: TAccountMetas[6]; }; data: InitializeWeightTableInstructionData; @@ -276,7 +276,7 @@ export function parseInitializeWeightTableInstruction< ncn: getNextAccount(), weightTable: getNextAccount(), payer: getNextAccount(), - restakingProgramId: getNextAccount(), + restakingProgram: getNextAccount(), systemProgram: getNextAccount(), }, data: getInitializeWeightTableInstructionDataDecoder().decode( diff --git a/clients/js/jito_tip_router/instructions/registerMint.ts b/clients/js/jito_tip_router/instructions/registerMint.ts index c4bd83c..72188f5 100644 --- a/clients/js/jito_tip_router/instructions/registerMint.ts +++ b/clients/js/jito_tip_router/instructions/registerMint.ts @@ -27,7 +27,7 @@ import { import { JITO_TIP_ROUTER_PROGRAM_ADDRESS } from '../programs'; import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; -export const REGISTER_MINT_DISCRIMINATOR = 5; +export const REGISTER_MINT_DISCRIMINATOR = 8; export function getRegisterMintDiscriminatorBytes() { return getU8Encoder().encode(REGISTER_MINT_DISCRIMINATOR); diff --git a/clients/js/jito_tip_router/instructions/setConfigFees.ts b/clients/js/jito_tip_router/instructions/setConfigFees.ts index b6274cd..d5b0dc4 100644 --- a/clients/js/jito_tip_router/instructions/setConfigFees.ts +++ b/clients/js/jito_tip_router/instructions/setConfigFees.ts @@ -50,7 +50,7 @@ export type SetConfigFeesInstruction< TAccountConfig extends string | IAccountMeta = string, TAccountNcn extends string | IAccountMeta = string, TAccountNcnAdmin extends string | IAccountMeta = string, - TAccountRestakingProgramId extends string | IAccountMeta = string, + TAccountRestakingProgram extends string | IAccountMeta = string, TRemainingAccounts extends readonly IAccountMeta[] = [], > = IInstruction & IInstructionWithData & @@ -67,9 +67,9 @@ export type SetConfigFeesInstruction< ? ReadonlySignerAccount & IAccountSignerMeta : TAccountNcnAdmin, - TAccountRestakingProgramId extends string - ? ReadonlyAccount - : TAccountRestakingProgramId, + TAccountRestakingProgram extends string + ? ReadonlyAccount + : TAccountRestakingProgram, ...TRemainingAccounts, ] >; @@ -127,13 +127,13 @@ export type SetConfigFeesInput< TAccountConfig extends string = string, TAccountNcn extends string = string, TAccountNcnAdmin extends string = string, - TAccountRestakingProgramId extends string = string, + TAccountRestakingProgram extends string = string, > = { restakingConfig: Address; config: Address; ncn: Address; ncnAdmin: TransactionSigner; - restakingProgramId: Address; + restakingProgram: Address; newDaoFeeBps: SetConfigFeesInstructionDataArgs['newDaoFeeBps']; newNcnFeeBps: SetConfigFeesInstructionDataArgs['newNcnFeeBps']; newBlockEngineFeeBps: SetConfigFeesInstructionDataArgs['newBlockEngineFeeBps']; @@ -145,7 +145,7 @@ export function getSetConfigFeesInstruction< TAccountConfig extends string, TAccountNcn extends string, TAccountNcnAdmin extends string, - TAccountRestakingProgramId extends string, + TAccountRestakingProgram extends string, TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, >( input: SetConfigFeesInput< @@ -153,7 +153,7 @@ export function getSetConfigFeesInstruction< TAccountConfig, TAccountNcn, TAccountNcnAdmin, - TAccountRestakingProgramId + TAccountRestakingProgram >, config?: { programAddress?: TProgramAddress } ): SetConfigFeesInstruction< @@ -162,7 +162,7 @@ export function getSetConfigFeesInstruction< TAccountConfig, TAccountNcn, TAccountNcnAdmin, - TAccountRestakingProgramId + TAccountRestakingProgram > { // Program address. const programAddress = @@ -177,8 +177,8 @@ export function getSetConfigFeesInstruction< config: { value: input.config ?? null, isWritable: true }, ncn: { value: input.ncn ?? null, isWritable: false }, ncnAdmin: { value: input.ncnAdmin ?? null, isWritable: false }, - restakingProgramId: { - value: input.restakingProgramId ?? null, + restakingProgram: { + value: input.restakingProgram ?? null, isWritable: false, }, }; @@ -197,7 +197,7 @@ export function getSetConfigFeesInstruction< getAccountMeta(accounts.config), getAccountMeta(accounts.ncn), getAccountMeta(accounts.ncnAdmin), - getAccountMeta(accounts.restakingProgramId), + getAccountMeta(accounts.restakingProgram), ], programAddress, data: getSetConfigFeesInstructionDataEncoder().encode( @@ -209,7 +209,7 @@ export function getSetConfigFeesInstruction< TAccountConfig, TAccountNcn, TAccountNcnAdmin, - TAccountRestakingProgramId + TAccountRestakingProgram >; return instruction; @@ -225,7 +225,7 @@ export type ParsedSetConfigFeesInstruction< config: TAccountMetas[1]; ncn: TAccountMetas[2]; ncnAdmin: TAccountMetas[3]; - restakingProgramId: TAccountMetas[4]; + restakingProgram: TAccountMetas[4]; }; data: SetConfigFeesInstructionData; }; @@ -255,7 +255,7 @@ export function parseSetConfigFeesInstruction< config: getNextAccount(), ncn: getNextAccount(), ncnAdmin: getNextAccount(), - restakingProgramId: getNextAccount(), + restakingProgram: getNextAccount(), }, data: getSetConfigFeesInstructionDataDecoder().decode(instruction.data), }; diff --git a/clients/js/jito_tip_router/instructions/setNewAdmin.ts b/clients/js/jito_tip_router/instructions/setNewAdmin.ts index eb14e6c..74c02c5 100644 --- a/clients/js/jito_tip_router/instructions/setNewAdmin.ts +++ b/clients/js/jito_tip_router/instructions/setNewAdmin.ts @@ -48,7 +48,7 @@ export type SetNewAdminInstruction< TAccountNcn extends string | IAccountMeta = string, TAccountNcnAdmin extends string | IAccountMeta = string, TAccountNewAdmin extends string | IAccountMeta = string, - TAccountRestakingProgramId extends string | IAccountMeta = string, + TAccountRestakingProgram extends string | IAccountMeta = string, TRemainingAccounts extends readonly IAccountMeta[] = [], > = IInstruction & IInstructionWithData & @@ -65,9 +65,9 @@ export type SetNewAdminInstruction< TAccountNewAdmin extends string ? ReadonlyAccount : TAccountNewAdmin, - TAccountRestakingProgramId extends string - ? ReadonlyAccount - : TAccountRestakingProgramId, + TAccountRestakingProgram extends string + ? ReadonlyAccount + : TAccountRestakingProgram, ...TRemainingAccounts, ] >; @@ -111,13 +111,13 @@ export type SetNewAdminInput< TAccountNcn extends string = string, TAccountNcnAdmin extends string = string, TAccountNewAdmin extends string = string, - TAccountRestakingProgramId extends string = string, + TAccountRestakingProgram extends string = string, > = { config: Address; ncn: Address; ncnAdmin: TransactionSigner; newAdmin: Address; - restakingProgramId: Address; + restakingProgram: Address; role: SetNewAdminInstructionDataArgs['role']; }; @@ -126,7 +126,7 @@ export function getSetNewAdminInstruction< TAccountNcn extends string, TAccountNcnAdmin extends string, TAccountNewAdmin extends string, - TAccountRestakingProgramId extends string, + TAccountRestakingProgram extends string, TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, >( input: SetNewAdminInput< @@ -134,7 +134,7 @@ export function getSetNewAdminInstruction< TAccountNcn, TAccountNcnAdmin, TAccountNewAdmin, - TAccountRestakingProgramId + TAccountRestakingProgram >, config?: { programAddress?: TProgramAddress } ): SetNewAdminInstruction< @@ -143,7 +143,7 @@ export function getSetNewAdminInstruction< TAccountNcn, TAccountNcnAdmin, TAccountNewAdmin, - TAccountRestakingProgramId + TAccountRestakingProgram > { // Program address. const programAddress = @@ -155,8 +155,8 @@ export function getSetNewAdminInstruction< ncn: { value: input.ncn ?? null, isWritable: false }, ncnAdmin: { value: input.ncnAdmin ?? null, isWritable: false }, newAdmin: { value: input.newAdmin ?? null, isWritable: false }, - restakingProgramId: { - value: input.restakingProgramId ?? null, + restakingProgram: { + value: input.restakingProgram ?? null, isWritable: false, }, }; @@ -175,7 +175,7 @@ export function getSetNewAdminInstruction< getAccountMeta(accounts.ncn), getAccountMeta(accounts.ncnAdmin), getAccountMeta(accounts.newAdmin), - getAccountMeta(accounts.restakingProgramId), + getAccountMeta(accounts.restakingProgram), ], programAddress, data: getSetNewAdminInstructionDataEncoder().encode( @@ -187,7 +187,7 @@ export function getSetNewAdminInstruction< TAccountNcn, TAccountNcnAdmin, TAccountNewAdmin, - TAccountRestakingProgramId + TAccountRestakingProgram >; return instruction; @@ -203,7 +203,7 @@ export type ParsedSetNewAdminInstruction< ncn: TAccountMetas[1]; ncnAdmin: TAccountMetas[2]; newAdmin: TAccountMetas[3]; - restakingProgramId: TAccountMetas[4]; + restakingProgram: TAccountMetas[4]; }; data: SetNewAdminInstructionData; }; @@ -233,7 +233,7 @@ export function parseSetNewAdminInstruction< ncn: getNextAccount(), ncnAdmin: getNextAccount(), newAdmin: getNextAccount(), - restakingProgramId: getNextAccount(), + restakingProgram: getNextAccount(), }, data: getSetNewAdminInstructionDataDecoder().decode(instruction.data), }; diff --git a/clients/js/jito_tip_router/instructions/snapshotVaultOperatorDelegation.ts b/clients/js/jito_tip_router/instructions/snapshotVaultOperatorDelegation.ts new file mode 100644 index 0000000..17fbe18 --- /dev/null +++ b/clients/js/jito_tip_router/instructions/snapshotVaultOperatorDelegation.ts @@ -0,0 +1,368 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + combineCodec, + getOptionDecoder, + getOptionEncoder, + getStructDecoder, + getStructEncoder, + getU64Decoder, + getU64Encoder, + getU8Decoder, + getU8Encoder, + transformEncoder, + type Address, + type Codec, + type Decoder, + type Encoder, + type IAccountMeta, + type IInstruction, + type IInstructionWithAccounts, + type IInstructionWithData, + type Option, + type OptionOrNullable, + type ReadonlyAccount, + type WritableAccount, +} from '@solana/web3.js'; +import { JITO_TIP_ROUTER_PROGRAM_ADDRESS } from '../programs'; +import { getAccountMetaFactory, type ResolvedAccount } from '../shared'; + +export const SNAPSHOT_VAULT_OPERATOR_DELEGATION_DISCRIMINATOR = 7; + +export function getSnapshotVaultOperatorDelegationDiscriminatorBytes() { + return getU8Encoder().encode( + SNAPSHOT_VAULT_OPERATOR_DELEGATION_DISCRIMINATOR + ); +} + +export type SnapshotVaultOperatorDelegationInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountNcnConfig extends string | IAccountMeta = string, + TAccountRestakingConfig extends string | IAccountMeta = string, + TAccountNcn extends string | IAccountMeta = string, + TAccountOperator extends string | IAccountMeta = string, + TAccountVault extends string | IAccountMeta = string, + TAccountVaultNcnTicket extends string | IAccountMeta = string, + TAccountNcnVaultTicket extends string | IAccountMeta = string, + TAccountVaultOperatorDelegation extends + | string + | IAccountMeta = string, + TAccountWeightTable extends string | IAccountMeta = string, + TAccountEpochSnapshot extends string | IAccountMeta = string, + TAccountOperatorSnapshot extends string | IAccountMeta = string, + TAccountVaultProgram extends string | IAccountMeta = string, + TAccountRestakingProgram extends string | IAccountMeta = string, + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountNcnConfig extends string + ? ReadonlyAccount + : TAccountNcnConfig, + TAccountRestakingConfig extends string + ? ReadonlyAccount + : TAccountRestakingConfig, + TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, + TAccountOperator extends string + ? ReadonlyAccount + : TAccountOperator, + TAccountVault extends string + ? ReadonlyAccount + : TAccountVault, + TAccountVaultNcnTicket extends string + ? ReadonlyAccount + : TAccountVaultNcnTicket, + TAccountNcnVaultTicket extends string + ? ReadonlyAccount + : TAccountNcnVaultTicket, + TAccountVaultOperatorDelegation extends string + ? ReadonlyAccount + : TAccountVaultOperatorDelegation, + TAccountWeightTable extends string + ? ReadonlyAccount + : TAccountWeightTable, + TAccountEpochSnapshot extends string + ? WritableAccount + : TAccountEpochSnapshot, + TAccountOperatorSnapshot extends string + ? WritableAccount + : TAccountOperatorSnapshot, + TAccountVaultProgram extends string + ? ReadonlyAccount + : TAccountVaultProgram, + TAccountRestakingProgram extends string + ? ReadonlyAccount + : TAccountRestakingProgram, + ...TRemainingAccounts, + ] + >; + +export type SnapshotVaultOperatorDelegationInstructionData = { + discriminator: number; + firstSlotOfNcnEpoch: Option; +}; + +export type SnapshotVaultOperatorDelegationInstructionDataArgs = { + firstSlotOfNcnEpoch: OptionOrNullable; +}; + +export function getSnapshotVaultOperatorDelegationInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ]), + (value) => ({ + ...value, + discriminator: SNAPSHOT_VAULT_OPERATOR_DELEGATION_DISCRIMINATOR, + }) + ); +} + +export function getSnapshotVaultOperatorDelegationInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ]); +} + +export function getSnapshotVaultOperatorDelegationInstructionDataCodec(): Codec< + SnapshotVaultOperatorDelegationInstructionDataArgs, + SnapshotVaultOperatorDelegationInstructionData +> { + return combineCodec( + getSnapshotVaultOperatorDelegationInstructionDataEncoder(), + getSnapshotVaultOperatorDelegationInstructionDataDecoder() + ); +} + +export type SnapshotVaultOperatorDelegationInput< + TAccountNcnConfig extends string = string, + TAccountRestakingConfig extends string = string, + TAccountNcn extends string = string, + TAccountOperator extends string = string, + TAccountVault extends string = string, + TAccountVaultNcnTicket extends string = string, + TAccountNcnVaultTicket extends string = string, + TAccountVaultOperatorDelegation extends string = string, + TAccountWeightTable extends string = string, + TAccountEpochSnapshot extends string = string, + TAccountOperatorSnapshot extends string = string, + TAccountVaultProgram extends string = string, + TAccountRestakingProgram extends string = string, +> = { + ncnConfig: Address; + restakingConfig: Address; + ncn: Address; + operator: Address; + vault: Address; + vaultNcnTicket: Address; + ncnVaultTicket: Address; + vaultOperatorDelegation: Address; + weightTable: Address; + epochSnapshot: Address; + operatorSnapshot: Address; + vaultProgram: Address; + restakingProgram: Address; + firstSlotOfNcnEpoch: SnapshotVaultOperatorDelegationInstructionDataArgs['firstSlotOfNcnEpoch']; +}; + +export function getSnapshotVaultOperatorDelegationInstruction< + TAccountNcnConfig extends string, + TAccountRestakingConfig extends string, + TAccountNcn extends string, + TAccountOperator extends string, + TAccountVault extends string, + TAccountVaultNcnTicket extends string, + TAccountNcnVaultTicket extends string, + TAccountVaultOperatorDelegation extends string, + TAccountWeightTable extends string, + TAccountEpochSnapshot extends string, + TAccountOperatorSnapshot extends string, + TAccountVaultProgram extends string, + TAccountRestakingProgram extends string, + TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, +>( + input: SnapshotVaultOperatorDelegationInput< + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountOperator, + TAccountVault, + TAccountVaultNcnTicket, + TAccountNcnVaultTicket, + TAccountVaultOperatorDelegation, + TAccountWeightTable, + TAccountEpochSnapshot, + TAccountOperatorSnapshot, + TAccountVaultProgram, + TAccountRestakingProgram + >, + config?: { programAddress?: TProgramAddress } +): SnapshotVaultOperatorDelegationInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountOperator, + TAccountVault, + TAccountVaultNcnTicket, + TAccountNcnVaultTicket, + TAccountVaultOperatorDelegation, + TAccountWeightTable, + TAccountEpochSnapshot, + TAccountOperatorSnapshot, + TAccountVaultProgram, + TAccountRestakingProgram +> { + // Program address. + const programAddress = + config?.programAddress ?? JITO_TIP_ROUTER_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + ncnConfig: { value: input.ncnConfig ?? null, isWritable: false }, + restakingConfig: { + value: input.restakingConfig ?? null, + isWritable: false, + }, + ncn: { value: input.ncn ?? null, isWritable: false }, + operator: { value: input.operator ?? null, isWritable: false }, + vault: { value: input.vault ?? null, isWritable: false }, + vaultNcnTicket: { value: input.vaultNcnTicket ?? null, isWritable: false }, + ncnVaultTicket: { value: input.ncnVaultTicket ?? null, isWritable: false }, + vaultOperatorDelegation: { + value: input.vaultOperatorDelegation ?? null, + isWritable: false, + }, + weightTable: { value: input.weightTable ?? null, isWritable: false }, + epochSnapshot: { value: input.epochSnapshot ?? null, isWritable: true }, + operatorSnapshot: { + value: input.operatorSnapshot ?? null, + isWritable: true, + }, + vaultProgram: { value: input.vaultProgram ?? null, isWritable: false }, + restakingProgram: { + value: input.restakingProgram ?? null, + isWritable: false, + }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + const getAccountMeta = getAccountMetaFactory(programAddress, 'programId'); + const instruction = { + accounts: [ + getAccountMeta(accounts.ncnConfig), + getAccountMeta(accounts.restakingConfig), + getAccountMeta(accounts.ncn), + getAccountMeta(accounts.operator), + getAccountMeta(accounts.vault), + getAccountMeta(accounts.vaultNcnTicket), + getAccountMeta(accounts.ncnVaultTicket), + getAccountMeta(accounts.vaultOperatorDelegation), + getAccountMeta(accounts.weightTable), + getAccountMeta(accounts.epochSnapshot), + getAccountMeta(accounts.operatorSnapshot), + getAccountMeta(accounts.vaultProgram), + getAccountMeta(accounts.restakingProgram), + ], + programAddress, + data: getSnapshotVaultOperatorDelegationInstructionDataEncoder().encode( + args as SnapshotVaultOperatorDelegationInstructionDataArgs + ), + } as SnapshotVaultOperatorDelegationInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountOperator, + TAccountVault, + TAccountVaultNcnTicket, + TAccountNcnVaultTicket, + TAccountVaultOperatorDelegation, + TAccountWeightTable, + TAccountEpochSnapshot, + TAccountOperatorSnapshot, + TAccountVaultProgram, + TAccountRestakingProgram + >; + + return instruction; +} + +export type ParsedSnapshotVaultOperatorDelegationInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], +> = { + programAddress: Address; + accounts: { + ncnConfig: TAccountMetas[0]; + restakingConfig: TAccountMetas[1]; + ncn: TAccountMetas[2]; + operator: TAccountMetas[3]; + vault: TAccountMetas[4]; + vaultNcnTicket: TAccountMetas[5]; + ncnVaultTicket: TAccountMetas[6]; + vaultOperatorDelegation: TAccountMetas[7]; + weightTable: TAccountMetas[8]; + epochSnapshot: TAccountMetas[9]; + operatorSnapshot: TAccountMetas[10]; + vaultProgram: TAccountMetas[11]; + restakingProgram: TAccountMetas[12]; + }; + data: SnapshotVaultOperatorDelegationInstructionData; +}; + +export function parseSnapshotVaultOperatorDelegationInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedSnapshotVaultOperatorDelegationInstruction { + if (instruction.accounts.length < 13) { + // TODO: Coded error. + throw new Error('Not enough accounts'); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = instruction.accounts![accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { + ncnConfig: getNextAccount(), + restakingConfig: getNextAccount(), + ncn: getNextAccount(), + operator: getNextAccount(), + vault: getNextAccount(), + vaultNcnTicket: getNextAccount(), + ncnVaultTicket: getNextAccount(), + vaultOperatorDelegation: getNextAccount(), + weightTable: getNextAccount(), + epochSnapshot: getNextAccount(), + operatorSnapshot: getNextAccount(), + vaultProgram: getNextAccount(), + restakingProgram: getNextAccount(), + }, + data: getSnapshotVaultOperatorDelegationInstructionDataDecoder().decode( + instruction.data + ), + }; +} diff --git a/clients/js/jito_tip_router/programs/jitoTipRouter.ts b/clients/js/jito_tip_router/programs/jitoTipRouter.ts index 5d846c6..3c57061 100644 --- a/clients/js/jito_tip_router/programs/jitoTipRouter.ts +++ b/clients/js/jito_tip_router/programs/jitoTipRouter.ts @@ -14,18 +14,24 @@ import { } from '@solana/web3.js'; import { type ParsedAdminUpdateWeightTableInstruction, + type ParsedInitializeEpochSnapshotInstruction, type ParsedInitializeNCNConfigInstruction, + type ParsedInitializeOperatorSnapshotInstruction, type ParsedInitializeTrackedMintsInstruction, type ParsedInitializeWeightTableInstruction, type ParsedRegisterMintInstruction, type ParsedSetConfigFeesInstruction, type ParsedSetNewAdminInstruction, + type ParsedSnapshotVaultOperatorDelegationInstruction, } from '../instructions'; export const JITO_TIP_ROUTER_PROGRAM_ADDRESS = 'Fv9aHCgvPQSr4jg9W8eTS6Ys1SNmh2qjyATrbsjEMaSH' as Address<'Fv9aHCgvPQSr4jg9W8eTS6Ys1SNmh2qjyATrbsjEMaSH'>; export enum JitoTipRouterAccount { + BallotBox, + EpochSnapshot, + OperatorSnapshot, NcnConfig, TrackedMints, WeightTable, @@ -37,6 +43,9 @@ export enum JitoTipRouterInstruction { SetNewAdmin, InitializeWeightTable, AdminUpdateWeightTable, + InitializeEpochSnapshot, + InitializeOperatorSnapshot, + SnapshotVaultOperatorDelegation, RegisterMint, InitializeTrackedMints, } @@ -61,9 +70,18 @@ export function identifyJitoTipRouterInstruction( return JitoTipRouterInstruction.AdminUpdateWeightTable; } if (containsBytes(data, getU8Encoder().encode(5), 0)) { - return JitoTipRouterInstruction.RegisterMint; + return JitoTipRouterInstruction.InitializeEpochSnapshot; } if (containsBytes(data, getU8Encoder().encode(6), 0)) { + return JitoTipRouterInstruction.InitializeOperatorSnapshot; + } + if (containsBytes(data, getU8Encoder().encode(7), 0)) { + return JitoTipRouterInstruction.SnapshotVaultOperatorDelegation; + } + if (containsBytes(data, getU8Encoder().encode(8), 0)) { + return JitoTipRouterInstruction.RegisterMint; + } + if (containsBytes(data, getU8Encoder().encode(9), 0)) { return JitoTipRouterInstruction.InitializeTrackedMints; } throw new Error( @@ -89,6 +107,15 @@ export type ParsedJitoTipRouterInstruction< | ({ instructionType: JitoTipRouterInstruction.AdminUpdateWeightTable; } & ParsedAdminUpdateWeightTableInstruction) + | ({ + instructionType: JitoTipRouterInstruction.InitializeEpochSnapshot; + } & ParsedInitializeEpochSnapshotInstruction) + | ({ + instructionType: JitoTipRouterInstruction.InitializeOperatorSnapshot; + } & ParsedInitializeOperatorSnapshotInstruction) + | ({ + instructionType: JitoTipRouterInstruction.SnapshotVaultOperatorDelegation; + } & ParsedSnapshotVaultOperatorDelegationInstruction) | ({ instructionType: JitoTipRouterInstruction.RegisterMint; } & ParsedRegisterMintInstruction) diff --git a/clients/js/jito_tip_router/types/ballot.ts b/clients/js/jito_tip_router/types/ballot.ts new file mode 100644 index 0000000..b268678 --- /dev/null +++ b/clients/js/jito_tip_router/types/ballot.ts @@ -0,0 +1,46 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + combineCodec, + fixDecoderSize, + fixEncoderSize, + getBytesDecoder, + getBytesEncoder, + getStructDecoder, + getStructEncoder, + type Codec, + type Decoder, + type Encoder, + type ReadonlyUint8Array, +} from '@solana/web3.js'; + +export type Ballot = { + merkleRoot: ReadonlyUint8Array; + reserved: ReadonlyUint8Array; +}; + +export type BallotArgs = Ballot; + +export function getBallotEncoder(): Encoder { + return getStructEncoder([ + ['merkleRoot', fixEncoderSize(getBytesEncoder(), 32)], + ['reserved', fixEncoderSize(getBytesEncoder(), 64)], + ]); +} + +export function getBallotDecoder(): Decoder { + return getStructDecoder([ + ['merkleRoot', fixDecoderSize(getBytesDecoder(), 32)], + ['reserved', fixDecoderSize(getBytesDecoder(), 64)], + ]); +} + +export function getBallotCodec(): Codec { + return combineCodec(getBallotEncoder(), getBallotDecoder()); +} diff --git a/clients/js/jito_tip_router/types/ballotTally.ts b/clients/js/jito_tip_router/types/ballotTally.ts new file mode 100644 index 0000000..99e151e --- /dev/null +++ b/clients/js/jito_tip_router/types/ballotTally.ts @@ -0,0 +1,67 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + combineCodec, + fixDecoderSize, + fixEncoderSize, + getBytesDecoder, + getBytesEncoder, + getStructDecoder, + getStructEncoder, + getU128Decoder, + getU128Encoder, + getU64Decoder, + getU64Encoder, + type Codec, + type Decoder, + type Encoder, + type ReadonlyUint8Array, +} from '@solana/web3.js'; +import { + getBallotDecoder, + getBallotEncoder, + type Ballot, + type BallotArgs, +} from '.'; + +export type BallotTally = { + ballot: Ballot; + stakeWeight: bigint; + tally: bigint; + reserved: ReadonlyUint8Array; +}; + +export type BallotTallyArgs = { + ballot: BallotArgs; + stakeWeight: number | bigint; + tally: number | bigint; + reserved: ReadonlyUint8Array; +}; + +export function getBallotTallyEncoder(): Encoder { + return getStructEncoder([ + ['ballot', getBallotEncoder()], + ['stakeWeight', getU128Encoder()], + ['tally', getU64Encoder()], + ['reserved', fixEncoderSize(getBytesEncoder(), 64)], + ]); +} + +export function getBallotTallyDecoder(): Decoder { + return getStructDecoder([ + ['ballot', getBallotDecoder()], + ['stakeWeight', getU128Decoder()], + ['tally', getU64Decoder()], + ['reserved', fixDecoderSize(getBytesDecoder(), 64)], + ]); +} + +export function getBallotTallyCodec(): Codec { + return combineCodec(getBallotTallyEncoder(), getBallotTallyDecoder()); +} diff --git a/clients/js/jito_tip_router/types/index.ts b/clients/js/jito_tip_router/types/index.ts index eb8f884..b49207d 100644 --- a/clients/js/jito_tip_router/types/index.ts +++ b/clients/js/jito_tip_router/types/index.ts @@ -6,8 +6,12 @@ * @see https://github.com/kinobi-so/kinobi */ +export * from './ballot'; +export * from './ballotTally'; export * from './configAdminRole'; export * from './fee'; export * from './fees'; export * from './mintEntry'; +export * from './operatorVote'; +export * from './vaultOperatorStakeWeight'; export * from './weightEntry'; diff --git a/clients/js/jito_tip_router/types/operatorVote.ts b/clients/js/jito_tip_router/types/operatorVote.ts new file mode 100644 index 0000000..f594c9d --- /dev/null +++ b/clients/js/jito_tip_router/types/operatorVote.ts @@ -0,0 +1,70 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + combineCodec, + fixDecoderSize, + fixEncoderSize, + getAddressDecoder, + getAddressEncoder, + getBytesDecoder, + getBytesEncoder, + getStructDecoder, + getStructEncoder, + getU128Decoder, + getU128Encoder, + getU16Decoder, + getU16Encoder, + getU64Decoder, + getU64Encoder, + type Address, + type Codec, + type Decoder, + type Encoder, + type ReadonlyUint8Array, +} from '@solana/web3.js'; + +export type OperatorVote = { + operator: Address; + slotVoted: bigint; + stakeWeight: bigint; + ballotIndex: number; + reserved: ReadonlyUint8Array; +}; + +export type OperatorVoteArgs = { + operator: Address; + slotVoted: number | bigint; + stakeWeight: number | bigint; + ballotIndex: number; + reserved: ReadonlyUint8Array; +}; + +export function getOperatorVoteEncoder(): Encoder { + return getStructEncoder([ + ['operator', getAddressEncoder()], + ['slotVoted', getU64Encoder()], + ['stakeWeight', getU128Encoder()], + ['ballotIndex', getU16Encoder()], + ['reserved', fixEncoderSize(getBytesEncoder(), 64)], + ]); +} + +export function getOperatorVoteDecoder(): Decoder { + return getStructDecoder([ + ['operator', getAddressDecoder()], + ['slotVoted', getU64Decoder()], + ['stakeWeight', getU128Decoder()], + ['ballotIndex', getU16Decoder()], + ['reserved', fixDecoderSize(getBytesDecoder(), 64)], + ]); +} + +export function getOperatorVoteCodec(): Codec { + return combineCodec(getOperatorVoteEncoder(), getOperatorVoteDecoder()); +} diff --git a/clients/js/jito_tip_router/types/vaultOperatorStakeWeight.ts b/clients/js/jito_tip_router/types/vaultOperatorStakeWeight.ts new file mode 100644 index 0000000..bd2932c --- /dev/null +++ b/clients/js/jito_tip_router/types/vaultOperatorStakeWeight.ts @@ -0,0 +1,70 @@ +/** + * This code was AUTOGENERATED using the kinobi library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun kinobi to update it. + * + * @see https://github.com/kinobi-so/kinobi + */ + +import { + combineCodec, + fixDecoderSize, + fixEncoderSize, + getAddressDecoder, + getAddressEncoder, + getBytesDecoder, + getBytesEncoder, + getStructDecoder, + getStructEncoder, + getU128Decoder, + getU128Encoder, + getU64Decoder, + getU64Encoder, + type Address, + type Codec, + type Decoder, + type Encoder, + type ReadonlyUint8Array, +} from '@solana/web3.js'; + +export type VaultOperatorStakeWeight = { + vault: Address; + stakeWeight: bigint; + vaultIndex: bigint; + reserved: ReadonlyUint8Array; +}; + +export type VaultOperatorStakeWeightArgs = { + vault: Address; + stakeWeight: number | bigint; + vaultIndex: number | bigint; + reserved: ReadonlyUint8Array; +}; + +export function getVaultOperatorStakeWeightEncoder(): Encoder { + return getStructEncoder([ + ['vault', getAddressEncoder()], + ['stakeWeight', getU128Encoder()], + ['vaultIndex', getU64Encoder()], + ['reserved', fixEncoderSize(getBytesEncoder(), 32)], + ]); +} + +export function getVaultOperatorStakeWeightDecoder(): Decoder { + return getStructDecoder([ + ['vault', getAddressDecoder()], + ['stakeWeight', getU128Decoder()], + ['vaultIndex', getU64Decoder()], + ['reserved', fixDecoderSize(getBytesDecoder(), 32)], + ]); +} + +export function getVaultOperatorStakeWeightCodec(): Codec< + VaultOperatorStakeWeightArgs, + VaultOperatorStakeWeight +> { + return combineCodec( + getVaultOperatorStakeWeightEncoder(), + getVaultOperatorStakeWeightDecoder() + ); +} diff --git a/clients/rust/jito_tip_router/src/generated/accounts/ballot_box.rs b/clients/rust/jito_tip_router/src/generated/accounts/ballot_box.rs new file mode 100644 index 0000000..01c1b21 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/accounts/ballot_box.rs @@ -0,0 +1,76 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +use crate::generated::types::{Ballot, BallotTally, OperatorVote}; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BallotBox { + pub discriminator: u64, + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub ncn: Pubkey, + pub ncn_epoch: u64, + pub bump: u8, + pub slot_created: u64, + pub slot_consensus_reached: u64, + #[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))] + pub reserved: [u8; 128], + pub operators_voted: u64, + pub unique_ballots: u64, + pub winning_ballot: Ballot, + pub operator_votes: [OperatorVote; 32], + pub ballot_tallies: [BallotTally; 32], +} + +impl BallotBox { + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { + let mut data = data; + Self::deserialize(&mut data) + } +} + +impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for BallotBox { + type Error = std::io::Error; + + fn try_from( + account_info: &solana_program::account_info::AccountInfo<'a>, + ) -> Result { + let mut data: &[u8] = &(*account_info.data).borrow(); + Self::deserialize(&mut data) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountDeserialize for BallotBox { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + Ok(Self::deserialize(buf)?) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountSerialize for BallotBox {} + +#[cfg(feature = "anchor")] +impl anchor_lang::Owner for BallotBox { + fn owner() -> Pubkey { + crate::JITO_TIP_ROUTER_ID + } +} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::IdlBuild for BallotBox {} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::Discriminator for BallotBox { + const DISCRIMINATOR: [u8; 8] = [0; 8]; +} diff --git a/clients/rust/jito_tip_router/src/generated/accounts/epoch_snapshot.rs b/clients/rust/jito_tip_router/src/generated/accounts/epoch_snapshot.rs new file mode 100644 index 0000000..991ef77 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/accounts/epoch_snapshot.rs @@ -0,0 +1,77 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +use crate::generated::types::Fees; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct EpochSnapshot { + pub discriminator: u64, + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub ncn: Pubkey, + pub ncn_epoch: u64, + pub bump: u8, + pub slot_created: u64, + pub slot_finalized: u64, + pub ncn_fees: Fees, + pub operator_count: u64, + pub vault_count: u64, + pub operators_registered: u64, + pub valid_operator_vault_delegations: u64, + pub stake_weight: u128, + #[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))] + pub reserved: [u8; 128], +} + +impl EpochSnapshot { + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { + let mut data = data; + Self::deserialize(&mut data) + } +} + +impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for EpochSnapshot { + type Error = std::io::Error; + + fn try_from( + account_info: &solana_program::account_info::AccountInfo<'a>, + ) -> Result { + let mut data: &[u8] = &(*account_info.data).borrow(); + Self::deserialize(&mut data) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountDeserialize for EpochSnapshot { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + Ok(Self::deserialize(buf)?) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountSerialize for EpochSnapshot {} + +#[cfg(feature = "anchor")] +impl anchor_lang::Owner for EpochSnapshot { + fn owner() -> Pubkey { + crate::JITO_TIP_ROUTER_ID + } +} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::IdlBuild for EpochSnapshot {} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::Discriminator for EpochSnapshot { + const DISCRIMINATOR: [u8; 8] = [0; 8]; +} diff --git a/clients/rust/jito_tip_router/src/generated/accounts/mod.rs b/clients/rust/jito_tip_router/src/generated/accounts/mod.rs index e34e139..aa0f253 100644 --- a/clients/rust/jito_tip_router/src/generated/accounts/mod.rs +++ b/clients/rust/jito_tip_router/src/generated/accounts/mod.rs @@ -4,8 +4,14 @@ //! //! +pub(crate) mod r#ballot_box; +pub(crate) mod r#epoch_snapshot; pub(crate) mod r#ncn_config; +pub(crate) mod r#operator_snapshot; pub(crate) mod r#tracked_mints; pub(crate) mod r#weight_table; -pub use self::{r#ncn_config::*, r#tracked_mints::*, r#weight_table::*}; +pub use self::{ + r#ballot_box::*, r#epoch_snapshot::*, r#ncn_config::*, r#operator_snapshot::*, + r#tracked_mints::*, r#weight_table::*, +}; diff --git a/clients/rust/jito_tip_router/src/generated/accounts/operator_snapshot.rs b/clients/rust/jito_tip_router/src/generated/accounts/operator_snapshot.rs new file mode 100644 index 0000000..a30fbde --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/accounts/operator_snapshot.rs @@ -0,0 +1,85 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +use crate::generated::types::VaultOperatorStakeWeight; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct OperatorSnapshot { + pub discriminator: u64, + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub operator: Pubkey, + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub ncn: Pubkey, + pub ncn_epoch: u64, + pub bump: u8, + pub slot_created: u64, + pub slot_finalized: u64, + pub is_active: bool, + pub ncn_operator_index: u64, + pub operator_index: u64, + pub operator_fee_bps: u16, + pub vault_operator_delegation_count: u64, + pub vault_operator_delegations_registered: u64, + pub valid_operator_vault_delegations: u64, + pub stake_weight: u128, + #[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))] + pub reserved: [u8; 256], + pub vault_operator_stake_weight: [VaultOperatorStakeWeight; 32], +} + +impl OperatorSnapshot { + #[inline(always)] + pub fn from_bytes(data: &[u8]) -> Result { + let mut data = data; + Self::deserialize(&mut data) + } +} + +impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for OperatorSnapshot { + type Error = std::io::Error; + + fn try_from( + account_info: &solana_program::account_info::AccountInfo<'a>, + ) -> Result { + let mut data: &[u8] = &(*account_info.data).borrow(); + Self::deserialize(&mut data) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountDeserialize for OperatorSnapshot { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { + Ok(Self::deserialize(buf)?) + } +} + +#[cfg(feature = "anchor")] +impl anchor_lang::AccountSerialize for OperatorSnapshot {} + +#[cfg(feature = "anchor")] +impl anchor_lang::Owner for OperatorSnapshot { + fn owner() -> Pubkey { + crate::JITO_TIP_ROUTER_ID + } +} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::IdlBuild for OperatorSnapshot {} + +#[cfg(feature = "anchor-idl-build")] +impl anchor_lang::Discriminator for OperatorSnapshot { + const DISCRIMINATOR: [u8; 8] = [0; 8]; +} diff --git a/clients/rust/jito_tip_router/src/generated/errors/jito_tip_router.rs b/clients/rust/jito_tip_router/src/generated/errors/jito_tip_router.rs index ddc3395..ce806cb 100644 --- a/clients/rust/jito_tip_router/src/generated/errors/jito_tip_router.rs +++ b/clients/rust/jito_tip_router/src/generated/errors/jito_tip_router.rs @@ -66,18 +66,54 @@ pub enum JitoTipRouterError { /// 8717 - Vault index already in use by a different mint #[error("Vault index already in use by a different mint")] VaultIndexAlreadyInUse = 0x220D, - /// 8960 - Fee cap exceeded + /// 8718 - Fee cap exceeded #[error("Fee cap exceeded")] - FeeCapExceeded = 0x2300, - /// 9216 - Incorrect NCN Admin + FeeCapExceeded = 0x220E, + /// 8719 - Incorrect NCN Admin #[error("Incorrect NCN Admin")] - IncorrectNcnAdmin = 0x2400, - /// 9217 - Incorrect NCN + IncorrectNcnAdmin = 0x220F, + /// 8720 - Incorrect NCN #[error("Incorrect NCN")] - IncorrectNcn = 0x2401, - /// 9218 - Incorrect fee admin + IncorrectNcn = 0x2210, + /// 8721 - Incorrect fee admin #[error("Incorrect fee admin")] - IncorrectFeeAdmin = 0x2402, + IncorrectFeeAdmin = 0x2211, + /// 8722 - Weight table not finalized + #[error("Weight table not finalized")] + WeightTableNotFinalized = 0x2212, + /// 8723 - Weight not found + #[error("Weight not found")] + WeightNotFound = 0x2213, + /// 8724 - No operators in ncn + #[error("No operators in ncn")] + NoOperators = 0x2214, + /// 8725 - Vault operator delegation is already finalized - should not happen + #[error("Vault operator delegation is already finalized - should not happen")] + VaultOperatorDelegationFinalized = 0x2215, + /// 8726 - Operator is already finalized - should not happen + #[error("Operator is already finalized - should not happen")] + OperatorFinalized = 0x2216, + /// 8727 - Too many vault operator delegations + #[error("Too many vault operator delegations")] + TooManyVaultOperatorDelegations = 0x2217, + /// 8728 - Duplicate vault operator delegation + #[error("Duplicate vault operator delegation")] + DuplicateVaultOperatorDelegation = 0x2218, + /// 8729 - Duplicate Vote Cast + #[error("Duplicate Vote Cast")] + DuplicateVoteCast = 0x2219, + /// 8730 - Operator votes full + #[error("Operator votes full")] + OperatorVotesFull = 0x221A, + /// 8731 - Merkle root tally full + #[error("Merkle root tally full")] + BallotTallyFull = 0x221B, + /// 8732 - Consensus already reached + #[error("Consensus already reached")] + ConsensusAlreadyReached = 0x221C, + /// 8733 - Consensus not reached + #[error("Consensus not reached")] + ConsensusNotReached = 0x221D, } impl solana_program::program_error::PrintProgramError for JitoTipRouterError { diff --git a/clients/rust/jito_tip_router/src/generated/instructions/admin_update_weight_table.rs b/clients/rust/jito_tip_router/src/generated/instructions/admin_update_weight_table.rs index 0df738f..6de2772 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/admin_update_weight_table.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/admin_update_weight_table.rs @@ -16,7 +16,7 @@ pub struct AdminUpdateWeightTable { pub mint: solana_program::pubkey::Pubkey, - pub restaking_program_id: solana_program::pubkey::Pubkey, + pub restaking_program: solana_program::pubkey::Pubkey, } impl AdminUpdateWeightTable { @@ -48,7 +48,7 @@ impl AdminUpdateWeightTable { self.mint, false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.restaking_program_id, + self.restaking_program, false, )); accounts.extend_from_slice(remaining_accounts); @@ -98,14 +98,14 @@ pub struct AdminUpdateWeightTableInstructionArgs { /// 1. `[writable]` weight_table /// 2. `[signer]` weight_table_admin /// 3. `[]` mint -/// 4. `[]` restaking_program_id +/// 4. `[]` restaking_program #[derive(Clone, Debug, Default)] pub struct AdminUpdateWeightTableBuilder { ncn: Option, weight_table: Option, weight_table_admin: Option, mint: Option, - restaking_program_id: Option, + restaking_program: Option, ncn_epoch: Option, weight: Option, __remaining_accounts: Vec, @@ -139,11 +139,11 @@ impl AdminUpdateWeightTableBuilder { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: solana_program::pubkey::Pubkey, + restaking_program: solana_program::pubkey::Pubkey, ) -> &mut Self { - self.restaking_program_id = Some(restaking_program_id); + self.restaking_program = Some(restaking_program); self } #[inline(always)] @@ -183,9 +183,9 @@ impl AdminUpdateWeightTableBuilder { .weight_table_admin .expect("weight_table_admin is not set"), mint: self.mint.expect("mint is not set"), - restaking_program_id: self - .restaking_program_id - .expect("restaking_program_id is not set"), + restaking_program: self + .restaking_program + .expect("restaking_program is not set"), }; let args = AdminUpdateWeightTableInstructionArgs { ncn_epoch: self.ncn_epoch.clone().expect("ncn_epoch is not set"), @@ -206,7 +206,7 @@ pub struct AdminUpdateWeightTableCpiAccounts<'a, 'b> { pub mint: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, } /// `admin_update_weight_table` CPI instruction. @@ -222,7 +222,7 @@ pub struct AdminUpdateWeightTableCpi<'a, 'b> { pub mint: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, /// The arguments for the instruction. pub __args: AdminUpdateWeightTableInstructionArgs, } @@ -239,7 +239,7 @@ impl<'a, 'b> AdminUpdateWeightTableCpi<'a, 'b> { weight_table: accounts.weight_table, weight_table_admin: accounts.weight_table_admin, mint: accounts.mint, - restaking_program_id: accounts.restaking_program_id, + restaking_program: accounts.restaking_program, __args: args, } } @@ -294,7 +294,7 @@ impl<'a, 'b> AdminUpdateWeightTableCpi<'a, 'b> { false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.restaking_program_id.key, + *self.restaking_program.key, false, )); remaining_accounts.iter().for_each(|remaining_account| { @@ -321,7 +321,7 @@ impl<'a, 'b> AdminUpdateWeightTableCpi<'a, 'b> { account_infos.push(self.weight_table.clone()); account_infos.push(self.weight_table_admin.clone()); account_infos.push(self.mint.clone()); - account_infos.push(self.restaking_program_id.clone()); + account_infos.push(self.restaking_program.clone()); remaining_accounts .iter() .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); @@ -342,7 +342,7 @@ impl<'a, 'b> AdminUpdateWeightTableCpi<'a, 'b> { /// 1. `[writable]` weight_table /// 2. `[signer]` weight_table_admin /// 3. `[]` mint -/// 4. `[]` restaking_program_id +/// 4. `[]` restaking_program #[derive(Clone, Debug)] pub struct AdminUpdateWeightTableCpiBuilder<'a, 'b> { instruction: Box>, @@ -356,7 +356,7 @@ impl<'a, 'b> AdminUpdateWeightTableCpiBuilder<'a, 'b> { weight_table: None, weight_table_admin: None, mint: None, - restaking_program_id: None, + restaking_program: None, ncn_epoch: None, weight: None, __remaining_accounts: Vec::new(), @@ -390,11 +390,11 @@ impl<'a, 'b> AdminUpdateWeightTableCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + restaking_program: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.restaking_program_id = Some(restaking_program_id); + self.instruction.restaking_program = Some(restaking_program); self } #[inline(always)] @@ -473,10 +473,10 @@ impl<'a, 'b> AdminUpdateWeightTableCpiBuilder<'a, 'b> { mint: self.instruction.mint.expect("mint is not set"), - restaking_program_id: self + restaking_program: self .instruction - .restaking_program_id - .expect("restaking_program_id is not set"), + .restaking_program + .expect("restaking_program is not set"), __args: args, }; instruction.invoke_signed_with_remaining_accounts( @@ -493,7 +493,7 @@ struct AdminUpdateWeightTableCpiBuilderInstruction<'a, 'b> { weight_table: Option<&'b solana_program::account_info::AccountInfo<'a>>, weight_table_admin: Option<&'b solana_program::account_info::AccountInfo<'a>>, mint: Option<&'b solana_program::account_info::AccountInfo<'a>>, - restaking_program_id: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_epoch: Option, weight: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. diff --git a/clients/rust/jito_tip_router/src/generated/instructions/initialize_epoch_snapshot.rs b/clients/rust/jito_tip_router/src/generated/instructions/initialize_epoch_snapshot.rs new file mode 100644 index 0000000..8ee7e54 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/instructions/initialize_epoch_snapshot.rs @@ -0,0 +1,645 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; + +/// Accounts. +pub struct InitializeEpochSnapshot { + pub ncn_config: solana_program::pubkey::Pubkey, + + pub restaking_config: solana_program::pubkey::Pubkey, + + pub ncn: solana_program::pubkey::Pubkey, + + pub tracked_mints: solana_program::pubkey::Pubkey, + + pub weight_table: solana_program::pubkey::Pubkey, + + pub epoch_snapshot: solana_program::pubkey::Pubkey, + + pub payer: solana_program::pubkey::Pubkey, + + pub restaking_program: solana_program::pubkey::Pubkey, + + pub system_program: solana_program::pubkey::Pubkey, +} + +impl InitializeEpochSnapshot { + pub fn instruction( + &self, + args: InitializeEpochSnapshotInstructionArgs, + ) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: InitializeEpochSnapshotInstructionArgs, + remaining_accounts: &[solana_program::instruction::AccountMeta], + ) -> solana_program::instruction::Instruction { + let mut accounts = Vec::with_capacity(9 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.restaking_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn, false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.tracked_mints, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.weight_table, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.epoch_snapshot, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.payer, true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.restaking_program, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.system_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = InitializeEpochSnapshotInstructionData::new() + .try_to_vec() + .unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_program::instruction::Instruction { + program_id: crate::JITO_TIP_ROUTER_ID, + accounts, + data, + } + } +} + +#[derive(BorshDeserialize, BorshSerialize)] +pub struct InitializeEpochSnapshotInstructionData { + discriminator: u8, +} + +impl InitializeEpochSnapshotInstructionData { + pub fn new() -> Self { + Self { discriminator: 5 } + } +} + +impl Default for InitializeEpochSnapshotInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct InitializeEpochSnapshotInstructionArgs { + pub first_slot_of_ncn_epoch: Option, +} + +/// Instruction builder for `InitializeEpochSnapshot`. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[]` restaking_config +/// 2. `[]` ncn +/// 3. `[]` tracked_mints +/// 4. `[]` weight_table +/// 5. `[writable]` epoch_snapshot +/// 6. `[writable, signer]` payer +/// 7. `[]` restaking_program +/// 8. `[optional]` system_program (default to `11111111111111111111111111111111`) +#[derive(Clone, Debug, Default)] +pub struct InitializeEpochSnapshotBuilder { + ncn_config: Option, + restaking_config: Option, + ncn: Option, + tracked_mints: Option, + weight_table: Option, + epoch_snapshot: Option, + payer: Option, + restaking_program: Option, + system_program: Option, + first_slot_of_ncn_epoch: Option, + __remaining_accounts: Vec, +} + +impl InitializeEpochSnapshotBuilder { + pub fn new() -> Self { + Self::default() + } + #[inline(always)] + pub fn ncn_config(&mut self, ncn_config: solana_program::pubkey::Pubkey) -> &mut Self { + self.ncn_config = Some(ncn_config); + self + } + #[inline(always)] + pub fn restaking_config( + &mut self, + restaking_config: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.restaking_config = Some(restaking_config); + self + } + #[inline(always)] + pub fn ncn(&mut self, ncn: solana_program::pubkey::Pubkey) -> &mut Self { + self.ncn = Some(ncn); + self + } + #[inline(always)] + pub fn tracked_mints(&mut self, tracked_mints: solana_program::pubkey::Pubkey) -> &mut Self { + self.tracked_mints = Some(tracked_mints); + self + } + #[inline(always)] + pub fn weight_table(&mut self, weight_table: solana_program::pubkey::Pubkey) -> &mut Self { + self.weight_table = Some(weight_table); + self + } + #[inline(always)] + pub fn epoch_snapshot(&mut self, epoch_snapshot: solana_program::pubkey::Pubkey) -> &mut Self { + self.epoch_snapshot = Some(epoch_snapshot); + self + } + #[inline(always)] + pub fn payer(&mut self, payer: solana_program::pubkey::Pubkey) -> &mut Self { + self.payer = Some(payer); + self + } + #[inline(always)] + pub fn restaking_program( + &mut self, + restaking_program: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.restaking_program = Some(restaking_program); + self + } + /// `[optional account, default to '11111111111111111111111111111111']` + #[inline(always)] + pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self { + self.system_program = Some(system_program); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn first_slot_of_ncn_epoch(&mut self, first_slot_of_ncn_epoch: u64) -> &mut Self { + self.first_slot_of_ncn_epoch = Some(first_slot_of_ncn_epoch); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: solana_program::instruction::AccountMeta, + ) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_program::instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_program::instruction::Instruction { + let accounts = InitializeEpochSnapshot { + ncn_config: self.ncn_config.expect("ncn_config is not set"), + restaking_config: self.restaking_config.expect("restaking_config is not set"), + ncn: self.ncn.expect("ncn is not set"), + tracked_mints: self.tracked_mints.expect("tracked_mints is not set"), + weight_table: self.weight_table.expect("weight_table is not set"), + epoch_snapshot: self.epoch_snapshot.expect("epoch_snapshot is not set"), + payer: self.payer.expect("payer is not set"), + restaking_program: self + .restaking_program + .expect("restaking_program is not set"), + system_program: self + .system_program + .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), + }; + let args = InitializeEpochSnapshotInstructionArgs { + first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `initialize_epoch_snapshot` CPI accounts. +pub struct InitializeEpochSnapshotCpiAccounts<'a, 'b> { + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub tracked_mints: &'b solana_program::account_info::AccountInfo<'a>, + + pub weight_table: &'b solana_program::account_info::AccountInfo<'a>, + + pub epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, +} + +/// `initialize_epoch_snapshot` CPI instruction. +pub struct InitializeEpochSnapshotCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub tracked_mints: &'b solana_program::account_info::AccountInfo<'a>, + + pub weight_table: &'b solana_program::account_info::AccountInfo<'a>, + + pub epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: InitializeEpochSnapshotInstructionArgs, +} + +impl<'a, 'b> InitializeEpochSnapshotCpi<'a, 'b> { + pub fn new( + program: &'b solana_program::account_info::AccountInfo<'a>, + accounts: InitializeEpochSnapshotCpiAccounts<'a, 'b>, + args: InitializeEpochSnapshotInstructionArgs, + ) -> Self { + Self { + __program: program, + ncn_config: accounts.ncn_config, + restaking_config: accounts.restaking_config, + ncn: accounts.ncn, + tracked_mints: accounts.tracked_mints, + weight_table: accounts.weight_table, + epoch_snapshot: accounts.epoch_snapshot, + payer: accounts.payer, + restaking_program: accounts.restaking_program, + system_program: accounts.system_program, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed( + &self, + signers_seeds: &[&[&[u8]]], + ) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> solana_program::entrypoint::ProgramResult { + let mut accounts = Vec::with_capacity(9 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.restaking_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.tracked_mints.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.weight_table.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.epoch_snapshot.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.payer.key, + true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.restaking_program.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.system_program.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_program::instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = InitializeEpochSnapshotInstructionData::new() + .try_to_vec() + .unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_program::instruction::Instruction { + program_id: crate::JITO_TIP_ROUTER_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(9 + 1 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.ncn_config.clone()); + account_infos.push(self.restaking_config.clone()); + account_infos.push(self.ncn.clone()); + account_infos.push(self.tracked_mints.clone()); + account_infos.push(self.weight_table.clone()); + account_infos.push(self.epoch_snapshot.clone()); + account_infos.push(self.payer.clone()); + account_infos.push(self.restaking_program.clone()); + account_infos.push(self.system_program.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_program::program::invoke(&instruction, &account_infos) + } else { + solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `InitializeEpochSnapshot` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[]` restaking_config +/// 2. `[]` ncn +/// 3. `[]` tracked_mints +/// 4. `[]` weight_table +/// 5. `[writable]` epoch_snapshot +/// 6. `[writable, signer]` payer +/// 7. `[]` restaking_program +/// 8. `[]` system_program +#[derive(Clone, Debug)] +pub struct InitializeEpochSnapshotCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> InitializeEpochSnapshotCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(InitializeEpochSnapshotCpiBuilderInstruction { + __program: program, + ncn_config: None, + restaking_config: None, + ncn: None, + tracked_mints: None, + weight_table: None, + epoch_snapshot: None, + payer: None, + restaking_program: None, + system_program: None, + first_slot_of_ncn_epoch: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + #[inline(always)] + pub fn ncn_config( + &mut self, + ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.ncn_config = Some(ncn_config); + self + } + #[inline(always)] + pub fn restaking_config( + &mut self, + restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.restaking_config = Some(restaking_config); + self + } + #[inline(always)] + pub fn ncn(&mut self, ncn: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.ncn = Some(ncn); + self + } + #[inline(always)] + pub fn tracked_mints( + &mut self, + tracked_mints: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.tracked_mints = Some(tracked_mints); + self + } + #[inline(always)] + pub fn weight_table( + &mut self, + weight_table: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.weight_table = Some(weight_table); + self + } + #[inline(always)] + pub fn epoch_snapshot( + &mut self, + epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.epoch_snapshot = Some(epoch_snapshot); + self + } + #[inline(always)] + pub fn payer(&mut self, payer: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.payer = Some(payer); + self + } + #[inline(always)] + pub fn restaking_program( + &mut self, + restaking_program: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.restaking_program = Some(restaking_program); + self + } + #[inline(always)] + pub fn system_program( + &mut self, + system_program: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.system_program = Some(system_program); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn first_slot_of_ncn_epoch(&mut self, first_slot_of_ncn_epoch: u64) -> &mut Self { + self.instruction.first_slot_of_ncn_epoch = Some(first_slot_of_ncn_epoch); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_program::account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed( + &self, + signers_seeds: &[&[&[u8]]], + ) -> solana_program::entrypoint::ProgramResult { + let args = InitializeEpochSnapshotInstructionArgs { + first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + }; + let instruction = InitializeEpochSnapshotCpi { + __program: self.instruction.__program, + + ncn_config: self.instruction.ncn_config.expect("ncn_config is not set"), + + restaking_config: self + .instruction + .restaking_config + .expect("restaking_config is not set"), + + ncn: self.instruction.ncn.expect("ncn is not set"), + + tracked_mints: self + .instruction + .tracked_mints + .expect("tracked_mints is not set"), + + weight_table: self + .instruction + .weight_table + .expect("weight_table is not set"), + + epoch_snapshot: self + .instruction + .epoch_snapshot + .expect("epoch_snapshot is not set"), + + payer: self.instruction.payer.expect("payer is not set"), + + restaking_program: self + .instruction + .restaking_program + .expect("restaking_program is not set"), + + system_program: self + .instruction + .system_program + .expect("system_program is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct InitializeEpochSnapshotCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_program::account_info::AccountInfo<'a>, + ncn_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, + tracked_mints: Option<&'b solana_program::account_info::AccountInfo<'a>>, + weight_table: Option<&'b solana_program::account_info::AccountInfo<'a>>, + epoch_snapshot: Option<&'b solana_program::account_info::AccountInfo<'a>>, + payer: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + first_slot_of_ncn_epoch: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )>, +} diff --git a/clients/rust/jito_tip_router/src/generated/instructions/initialize_n_c_n_config.rs b/clients/rust/jito_tip_router/src/generated/instructions/initialize_n_c_n_config.rs index b46859b..15b32b8 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/initialize_n_c_n_config.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/initialize_n_c_n_config.rs @@ -20,7 +20,7 @@ pub struct InitializeNCNConfig { pub tie_breaker_admin: solana_program::pubkey::Pubkey, - pub restaking_program_id: solana_program::pubkey::Pubkey, + pub restaking_program: solana_program::pubkey::Pubkey, pub system_program: solana_program::pubkey::Pubkey, } @@ -63,7 +63,7 @@ impl InitializeNCNConfig { false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.restaking_program_id, + self.restaking_program, false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( @@ -120,7 +120,7 @@ pub struct InitializeNCNConfigInstructionArgs { /// 3. `[writable, signer]` ncn_admin /// 4. `[]` fee_wallet /// 5. `[]` tie_breaker_admin -/// 6. `[]` restaking_program_id +/// 6. `[]` restaking_program /// 7. `[optional]` system_program (default to `11111111111111111111111111111111`) #[derive(Clone, Debug, Default)] pub struct InitializeNCNConfigBuilder { @@ -130,7 +130,7 @@ pub struct InitializeNCNConfigBuilder { ncn_admin: Option, fee_wallet: Option, tie_breaker_admin: Option, - restaking_program_id: Option, + restaking_program: Option, system_program: Option, dao_fee_bps: Option, ncn_fee_bps: Option, @@ -179,11 +179,11 @@ impl InitializeNCNConfigBuilder { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: solana_program::pubkey::Pubkey, + restaking_program: solana_program::pubkey::Pubkey, ) -> &mut Self { - self.restaking_program_id = Some(restaking_program_id); + self.restaking_program = Some(restaking_program); self } /// `[optional account, default to '11111111111111111111111111111111']` @@ -236,9 +236,9 @@ impl InitializeNCNConfigBuilder { tie_breaker_admin: self .tie_breaker_admin .expect("tie_breaker_admin is not set"), - restaking_program_id: self - .restaking_program_id - .expect("restaking_program_id is not set"), + restaking_program: self + .restaking_program + .expect("restaking_program is not set"), system_program: self .system_program .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), @@ -270,7 +270,7 @@ pub struct InitializeNCNConfigCpiAccounts<'a, 'b> { pub tie_breaker_admin: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, pub system_program: &'b solana_program::account_info::AccountInfo<'a>, } @@ -292,7 +292,7 @@ pub struct InitializeNCNConfigCpi<'a, 'b> { pub tie_breaker_admin: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, pub system_program: &'b solana_program::account_info::AccountInfo<'a>, /// The arguments for the instruction. @@ -313,7 +313,7 @@ impl<'a, 'b> InitializeNCNConfigCpi<'a, 'b> { ncn_admin: accounts.ncn_admin, fee_wallet: accounts.fee_wallet, tie_breaker_admin: accounts.tie_breaker_admin, - restaking_program_id: accounts.restaking_program_id, + restaking_program: accounts.restaking_program, system_program: accounts.system_program, __args: args, } @@ -377,7 +377,7 @@ impl<'a, 'b> InitializeNCNConfigCpi<'a, 'b> { false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.restaking_program_id.key, + *self.restaking_program.key, false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( @@ -410,7 +410,7 @@ impl<'a, 'b> InitializeNCNConfigCpi<'a, 'b> { account_infos.push(self.ncn_admin.clone()); account_infos.push(self.fee_wallet.clone()); account_infos.push(self.tie_breaker_admin.clone()); - account_infos.push(self.restaking_program_id.clone()); + account_infos.push(self.restaking_program.clone()); account_infos.push(self.system_program.clone()); remaining_accounts .iter() @@ -434,7 +434,7 @@ impl<'a, 'b> InitializeNCNConfigCpi<'a, 'b> { /// 3. `[writable, signer]` ncn_admin /// 4. `[]` fee_wallet /// 5. `[]` tie_breaker_admin -/// 6. `[]` restaking_program_id +/// 6. `[]` restaking_program /// 7. `[]` system_program #[derive(Clone, Debug)] pub struct InitializeNCNConfigCpiBuilder<'a, 'b> { @@ -451,7 +451,7 @@ impl<'a, 'b> InitializeNCNConfigCpiBuilder<'a, 'b> { ncn_admin: None, fee_wallet: None, tie_breaker_admin: None, - restaking_program_id: None, + restaking_program: None, system_program: None, dao_fee_bps: None, ncn_fee_bps: None, @@ -506,11 +506,11 @@ impl<'a, 'b> InitializeNCNConfigCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + restaking_program: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.restaking_program_id = Some(restaking_program_id); + self.instruction.restaking_program = Some(restaking_program); self } #[inline(always)] @@ -615,10 +615,10 @@ impl<'a, 'b> InitializeNCNConfigCpiBuilder<'a, 'b> { .tie_breaker_admin .expect("tie_breaker_admin is not set"), - restaking_program_id: self + restaking_program: self .instruction - .restaking_program_id - .expect("restaking_program_id is not set"), + .restaking_program + .expect("restaking_program is not set"), system_program: self .instruction @@ -642,7 +642,7 @@ struct InitializeNCNConfigCpiBuilderInstruction<'a, 'b> { ncn_admin: Option<&'b solana_program::account_info::AccountInfo<'a>>, fee_wallet: Option<&'b solana_program::account_info::AccountInfo<'a>>, tie_breaker_admin: Option<&'b solana_program::account_info::AccountInfo<'a>>, - restaking_program_id: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, dao_fee_bps: Option, ncn_fee_bps: Option, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/initialize_operator_snapshot.rs b/clients/rust/jito_tip_router/src/generated/instructions/initialize_operator_snapshot.rs new file mode 100644 index 0000000..f28c3f0 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/instructions/initialize_operator_snapshot.rs @@ -0,0 +1,692 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; + +/// Accounts. +pub struct InitializeOperatorSnapshot { + pub ncn_config: solana_program::pubkey::Pubkey, + + pub restaking_config: solana_program::pubkey::Pubkey, + + pub ncn: solana_program::pubkey::Pubkey, + + pub operator: solana_program::pubkey::Pubkey, + + pub ncn_operator_state: solana_program::pubkey::Pubkey, + + pub epoch_snapshot: solana_program::pubkey::Pubkey, + + pub operator_snapshot: solana_program::pubkey::Pubkey, + + pub payer: solana_program::pubkey::Pubkey, + + pub restaking_program: solana_program::pubkey::Pubkey, + + pub system_program: solana_program::pubkey::Pubkey, +} + +impl InitializeOperatorSnapshot { + pub fn instruction( + &self, + args: InitializeOperatorSnapshotInstructionArgs, + ) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: InitializeOperatorSnapshotInstructionArgs, + remaining_accounts: &[solana_program::instruction::AccountMeta], + ) -> solana_program::instruction::Instruction { + let mut accounts = Vec::with_capacity(10 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.restaking_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn, false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.operator, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn_operator_state, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.epoch_snapshot, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.operator_snapshot, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.payer, true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.restaking_program, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.system_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = InitializeOperatorSnapshotInstructionData::new() + .try_to_vec() + .unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_program::instruction::Instruction { + program_id: crate::JITO_TIP_ROUTER_ID, + accounts, + data, + } + } +} + +#[derive(BorshDeserialize, BorshSerialize)] +pub struct InitializeOperatorSnapshotInstructionData { + discriminator: u8, +} + +impl InitializeOperatorSnapshotInstructionData { + pub fn new() -> Self { + Self { discriminator: 6 } + } +} + +impl Default for InitializeOperatorSnapshotInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct InitializeOperatorSnapshotInstructionArgs { + pub first_slot_of_ncn_epoch: Option, +} + +/// Instruction builder for `InitializeOperatorSnapshot`. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[]` restaking_config +/// 2. `[]` ncn +/// 3. `[]` operator +/// 4. `[]` ncn_operator_state +/// 5. `[writable]` epoch_snapshot +/// 6. `[writable]` operator_snapshot +/// 7. `[writable, signer]` payer +/// 8. `[]` restaking_program +/// 9. `[optional]` system_program (default to `11111111111111111111111111111111`) +#[derive(Clone, Debug, Default)] +pub struct InitializeOperatorSnapshotBuilder { + ncn_config: Option, + restaking_config: Option, + ncn: Option, + operator: Option, + ncn_operator_state: Option, + epoch_snapshot: Option, + operator_snapshot: Option, + payer: Option, + restaking_program: Option, + system_program: Option, + first_slot_of_ncn_epoch: Option, + __remaining_accounts: Vec, +} + +impl InitializeOperatorSnapshotBuilder { + pub fn new() -> Self { + Self::default() + } + #[inline(always)] + pub fn ncn_config(&mut self, ncn_config: solana_program::pubkey::Pubkey) -> &mut Self { + self.ncn_config = Some(ncn_config); + self + } + #[inline(always)] + pub fn restaking_config( + &mut self, + restaking_config: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.restaking_config = Some(restaking_config); + self + } + #[inline(always)] + pub fn ncn(&mut self, ncn: solana_program::pubkey::Pubkey) -> &mut Self { + self.ncn = Some(ncn); + self + } + #[inline(always)] + pub fn operator(&mut self, operator: solana_program::pubkey::Pubkey) -> &mut Self { + self.operator = Some(operator); + self + } + #[inline(always)] + pub fn ncn_operator_state( + &mut self, + ncn_operator_state: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.ncn_operator_state = Some(ncn_operator_state); + self + } + #[inline(always)] + pub fn epoch_snapshot(&mut self, epoch_snapshot: solana_program::pubkey::Pubkey) -> &mut Self { + self.epoch_snapshot = Some(epoch_snapshot); + self + } + #[inline(always)] + pub fn operator_snapshot( + &mut self, + operator_snapshot: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.operator_snapshot = Some(operator_snapshot); + self + } + #[inline(always)] + pub fn payer(&mut self, payer: solana_program::pubkey::Pubkey) -> &mut Self { + self.payer = Some(payer); + self + } + #[inline(always)] + pub fn restaking_program( + &mut self, + restaking_program: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.restaking_program = Some(restaking_program); + self + } + /// `[optional account, default to '11111111111111111111111111111111']` + #[inline(always)] + pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self { + self.system_program = Some(system_program); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn first_slot_of_ncn_epoch(&mut self, first_slot_of_ncn_epoch: u64) -> &mut Self { + self.first_slot_of_ncn_epoch = Some(first_slot_of_ncn_epoch); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: solana_program::instruction::AccountMeta, + ) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_program::instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_program::instruction::Instruction { + let accounts = InitializeOperatorSnapshot { + ncn_config: self.ncn_config.expect("ncn_config is not set"), + restaking_config: self.restaking_config.expect("restaking_config is not set"), + ncn: self.ncn.expect("ncn is not set"), + operator: self.operator.expect("operator is not set"), + ncn_operator_state: self + .ncn_operator_state + .expect("ncn_operator_state is not set"), + epoch_snapshot: self.epoch_snapshot.expect("epoch_snapshot is not set"), + operator_snapshot: self + .operator_snapshot + .expect("operator_snapshot is not set"), + payer: self.payer.expect("payer is not set"), + restaking_program: self + .restaking_program + .expect("restaking_program is not set"), + system_program: self + .system_program + .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), + }; + let args = InitializeOperatorSnapshotInstructionArgs { + first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `initialize_operator_snapshot` CPI accounts. +pub struct InitializeOperatorSnapshotCpiAccounts<'a, 'b> { + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub operator: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn_operator_state: &'b solana_program::account_info::AccountInfo<'a>, + + pub epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub operator_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, +} + +/// `initialize_operator_snapshot` CPI instruction. +pub struct InitializeOperatorSnapshotCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub operator: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn_operator_state: &'b solana_program::account_info::AccountInfo<'a>, + + pub epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub operator_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: InitializeOperatorSnapshotInstructionArgs, +} + +impl<'a, 'b> InitializeOperatorSnapshotCpi<'a, 'b> { + pub fn new( + program: &'b solana_program::account_info::AccountInfo<'a>, + accounts: InitializeOperatorSnapshotCpiAccounts<'a, 'b>, + args: InitializeOperatorSnapshotInstructionArgs, + ) -> Self { + Self { + __program: program, + ncn_config: accounts.ncn_config, + restaking_config: accounts.restaking_config, + ncn: accounts.ncn, + operator: accounts.operator, + ncn_operator_state: accounts.ncn_operator_state, + epoch_snapshot: accounts.epoch_snapshot, + operator_snapshot: accounts.operator_snapshot, + payer: accounts.payer, + restaking_program: accounts.restaking_program, + system_program: accounts.system_program, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed( + &self, + signers_seeds: &[&[&[u8]]], + ) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> solana_program::entrypoint::ProgramResult { + let mut accounts = Vec::with_capacity(10 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.restaking_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.operator.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn_operator_state.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.epoch_snapshot.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.operator_snapshot.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.payer.key, + true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.restaking_program.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.system_program.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_program::instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = InitializeOperatorSnapshotInstructionData::new() + .try_to_vec() + .unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_program::instruction::Instruction { + program_id: crate::JITO_TIP_ROUTER_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(10 + 1 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.ncn_config.clone()); + account_infos.push(self.restaking_config.clone()); + account_infos.push(self.ncn.clone()); + account_infos.push(self.operator.clone()); + account_infos.push(self.ncn_operator_state.clone()); + account_infos.push(self.epoch_snapshot.clone()); + account_infos.push(self.operator_snapshot.clone()); + account_infos.push(self.payer.clone()); + account_infos.push(self.restaking_program.clone()); + account_infos.push(self.system_program.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_program::program::invoke(&instruction, &account_infos) + } else { + solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `InitializeOperatorSnapshot` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[]` restaking_config +/// 2. `[]` ncn +/// 3. `[]` operator +/// 4. `[]` ncn_operator_state +/// 5. `[writable]` epoch_snapshot +/// 6. `[writable]` operator_snapshot +/// 7. `[writable, signer]` payer +/// 8. `[]` restaking_program +/// 9. `[]` system_program +#[derive(Clone, Debug)] +pub struct InitializeOperatorSnapshotCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> InitializeOperatorSnapshotCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(InitializeOperatorSnapshotCpiBuilderInstruction { + __program: program, + ncn_config: None, + restaking_config: None, + ncn: None, + operator: None, + ncn_operator_state: None, + epoch_snapshot: None, + operator_snapshot: None, + payer: None, + restaking_program: None, + system_program: None, + first_slot_of_ncn_epoch: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + #[inline(always)] + pub fn ncn_config( + &mut self, + ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.ncn_config = Some(ncn_config); + self + } + #[inline(always)] + pub fn restaking_config( + &mut self, + restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.restaking_config = Some(restaking_config); + self + } + #[inline(always)] + pub fn ncn(&mut self, ncn: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.ncn = Some(ncn); + self + } + #[inline(always)] + pub fn operator( + &mut self, + operator: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.operator = Some(operator); + self + } + #[inline(always)] + pub fn ncn_operator_state( + &mut self, + ncn_operator_state: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.ncn_operator_state = Some(ncn_operator_state); + self + } + #[inline(always)] + pub fn epoch_snapshot( + &mut self, + epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.epoch_snapshot = Some(epoch_snapshot); + self + } + #[inline(always)] + pub fn operator_snapshot( + &mut self, + operator_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.operator_snapshot = Some(operator_snapshot); + self + } + #[inline(always)] + pub fn payer(&mut self, payer: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.payer = Some(payer); + self + } + #[inline(always)] + pub fn restaking_program( + &mut self, + restaking_program: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.restaking_program = Some(restaking_program); + self + } + #[inline(always)] + pub fn system_program( + &mut self, + system_program: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.system_program = Some(system_program); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn first_slot_of_ncn_epoch(&mut self, first_slot_of_ncn_epoch: u64) -> &mut Self { + self.instruction.first_slot_of_ncn_epoch = Some(first_slot_of_ncn_epoch); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_program::account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed( + &self, + signers_seeds: &[&[&[u8]]], + ) -> solana_program::entrypoint::ProgramResult { + let args = InitializeOperatorSnapshotInstructionArgs { + first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + }; + let instruction = InitializeOperatorSnapshotCpi { + __program: self.instruction.__program, + + ncn_config: self.instruction.ncn_config.expect("ncn_config is not set"), + + restaking_config: self + .instruction + .restaking_config + .expect("restaking_config is not set"), + + ncn: self.instruction.ncn.expect("ncn is not set"), + + operator: self.instruction.operator.expect("operator is not set"), + + ncn_operator_state: self + .instruction + .ncn_operator_state + .expect("ncn_operator_state is not set"), + + epoch_snapshot: self + .instruction + .epoch_snapshot + .expect("epoch_snapshot is not set"), + + operator_snapshot: self + .instruction + .operator_snapshot + .expect("operator_snapshot is not set"), + + payer: self.instruction.payer.expect("payer is not set"), + + restaking_program: self + .instruction + .restaking_program + .expect("restaking_program is not set"), + + system_program: self + .instruction + .system_program + .expect("system_program is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct InitializeOperatorSnapshotCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_program::account_info::AccountInfo<'a>, + ncn_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, + operator: Option<&'b solana_program::account_info::AccountInfo<'a>>, + ncn_operator_state: Option<&'b solana_program::account_info::AccountInfo<'a>>, + epoch_snapshot: Option<&'b solana_program::account_info::AccountInfo<'a>>, + operator_snapshot: Option<&'b solana_program::account_info::AccountInfo<'a>>, + payer: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + first_slot_of_ncn_epoch: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )>, +} diff --git a/clients/rust/jito_tip_router/src/generated/instructions/initialize_tracked_mints.rs b/clients/rust/jito_tip_router/src/generated/instructions/initialize_tracked_mints.rs index b41c70a..b63869a 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/initialize_tracked_mints.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/initialize_tracked_mints.rs @@ -67,7 +67,7 @@ pub struct InitializeTrackedMintsInstructionData { impl InitializeTrackedMintsInstructionData { pub fn new() -> Self { - Self { discriminator: 6 } + Self { discriminator: 9 } } } diff --git a/clients/rust/jito_tip_router/src/generated/instructions/initialize_weight_table.rs b/clients/rust/jito_tip_router/src/generated/instructions/initialize_weight_table.rs index b46b10c..497686a 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/initialize_weight_table.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/initialize_weight_table.rs @@ -18,7 +18,7 @@ pub struct InitializeWeightTable { pub payer: solana_program::pubkey::Pubkey, - pub restaking_program_id: solana_program::pubkey::Pubkey, + pub restaking_program: solana_program::pubkey::Pubkey, pub system_program: solana_program::pubkey::Pubkey, } @@ -56,7 +56,7 @@ impl InitializeWeightTable { self.payer, true, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.restaking_program_id, + self.restaking_program, false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( @@ -110,7 +110,7 @@ pub struct InitializeWeightTableInstructionArgs { /// 2. `[]` ncn /// 3. `[writable]` weight_table /// 4. `[writable, signer]` payer -/// 5. `[]` restaking_program_id +/// 5. `[]` restaking_program /// 6. `[optional]` system_program (default to `11111111111111111111111111111111`) #[derive(Clone, Debug, Default)] pub struct InitializeWeightTableBuilder { @@ -119,7 +119,7 @@ pub struct InitializeWeightTableBuilder { ncn: Option, weight_table: Option, payer: Option, - restaking_program_id: Option, + restaking_program: Option, system_program: Option, first_slot_of_ncn_epoch: Option, __remaining_accounts: Vec, @@ -158,11 +158,11 @@ impl InitializeWeightTableBuilder { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: solana_program::pubkey::Pubkey, + restaking_program: solana_program::pubkey::Pubkey, ) -> &mut Self { - self.restaking_program_id = Some(restaking_program_id); + self.restaking_program = Some(restaking_program); self } /// `[optional account, default to '11111111111111111111111111111111']` @@ -203,9 +203,9 @@ impl InitializeWeightTableBuilder { ncn: self.ncn.expect("ncn is not set"), weight_table: self.weight_table.expect("weight_table is not set"), payer: self.payer.expect("payer is not set"), - restaking_program_id: self - .restaking_program_id - .expect("restaking_program_id is not set"), + restaking_program: self + .restaking_program + .expect("restaking_program is not set"), system_program: self .system_program .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), @@ -230,7 +230,7 @@ pub struct InitializeWeightTableCpiAccounts<'a, 'b> { pub payer: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, pub system_program: &'b solana_program::account_info::AccountInfo<'a>, } @@ -250,7 +250,7 @@ pub struct InitializeWeightTableCpi<'a, 'b> { pub payer: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, pub system_program: &'b solana_program::account_info::AccountInfo<'a>, /// The arguments for the instruction. @@ -270,7 +270,7 @@ impl<'a, 'b> InitializeWeightTableCpi<'a, 'b> { ncn: accounts.ncn, weight_table: accounts.weight_table, payer: accounts.payer, - restaking_program_id: accounts.restaking_program_id, + restaking_program: accounts.restaking_program, system_program: accounts.system_program, __args: args, } @@ -330,7 +330,7 @@ impl<'a, 'b> InitializeWeightTableCpi<'a, 'b> { true, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.restaking_program_id.key, + *self.restaking_program.key, false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( @@ -362,7 +362,7 @@ impl<'a, 'b> InitializeWeightTableCpi<'a, 'b> { account_infos.push(self.ncn.clone()); account_infos.push(self.weight_table.clone()); account_infos.push(self.payer.clone()); - account_infos.push(self.restaking_program_id.clone()); + account_infos.push(self.restaking_program.clone()); account_infos.push(self.system_program.clone()); remaining_accounts .iter() @@ -385,7 +385,7 @@ impl<'a, 'b> InitializeWeightTableCpi<'a, 'b> { /// 2. `[]` ncn /// 3. `[writable]` weight_table /// 4. `[writable, signer]` payer -/// 5. `[]` restaking_program_id +/// 5. `[]` restaking_program /// 6. `[]` system_program #[derive(Clone, Debug)] pub struct InitializeWeightTableCpiBuilder<'a, 'b> { @@ -401,7 +401,7 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { ncn: None, weight_table: None, payer: None, - restaking_program_id: None, + restaking_program: None, system_program: None, first_slot_of_ncn_epoch: None, __remaining_accounts: Vec::new(), @@ -443,11 +443,11 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + restaking_program: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.restaking_program_id = Some(restaking_program_id); + self.instruction.restaking_program = Some(restaking_program); self } #[inline(always)] @@ -530,10 +530,10 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { payer: self.instruction.payer.expect("payer is not set"), - restaking_program_id: self + restaking_program: self .instruction - .restaking_program_id - .expect("restaking_program_id is not set"), + .restaking_program + .expect("restaking_program is not set"), system_program: self .instruction @@ -556,7 +556,7 @@ struct InitializeWeightTableCpiBuilderInstruction<'a, 'b> { ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, weight_table: Option<&'b solana_program::account_info::AccountInfo<'a>>, payer: Option<&'b solana_program::account_info::AccountInfo<'a>>, - restaking_program_id: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, first_slot_of_ncn_epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. diff --git a/clients/rust/jito_tip_router/src/generated/instructions/mod.rs b/clients/rust/jito_tip_router/src/generated/instructions/mod.rs index 9608d45..06b2b50 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/mod.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/mod.rs @@ -5,14 +5,19 @@ //! pub(crate) mod r#admin_update_weight_table; +pub(crate) mod r#initialize_epoch_snapshot; pub(crate) mod r#initialize_n_c_n_config; +pub(crate) mod r#initialize_operator_snapshot; pub(crate) mod r#initialize_tracked_mints; pub(crate) mod r#initialize_weight_table; pub(crate) mod r#register_mint; pub(crate) mod r#set_config_fees; pub(crate) mod r#set_new_admin; +pub(crate) mod r#snapshot_vault_operator_delegation; pub use self::{ - r#admin_update_weight_table::*, r#initialize_n_c_n_config::*, r#initialize_tracked_mints::*, - r#initialize_weight_table::*, r#register_mint::*, r#set_config_fees::*, r#set_new_admin::*, + r#admin_update_weight_table::*, r#initialize_epoch_snapshot::*, r#initialize_n_c_n_config::*, + r#initialize_operator_snapshot::*, r#initialize_tracked_mints::*, r#initialize_weight_table::*, + r#register_mint::*, r#set_config_fees::*, r#set_new_admin::*, + r#snapshot_vault_operator_delegation::*, }; diff --git a/clients/rust/jito_tip_router/src/generated/instructions/register_mint.rs b/clients/rust/jito_tip_router/src/generated/instructions/register_mint.rs index ed3496c..23781df 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/register_mint.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/register_mint.rs @@ -89,7 +89,7 @@ pub struct RegisterMintInstructionData { impl RegisterMintInstructionData { pub fn new() -> Self { - Self { discriminator: 5 } + Self { discriminator: 8 } } } diff --git a/clients/rust/jito_tip_router/src/generated/instructions/set_config_fees.rs b/clients/rust/jito_tip_router/src/generated/instructions/set_config_fees.rs index 71a4cb2..877c04f 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/set_config_fees.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/set_config_fees.rs @@ -17,7 +17,7 @@ pub struct SetConfigFees { pub ncn_admin: solana_program::pubkey::Pubkey, - pub restaking_program_id: solana_program::pubkey::Pubkey, + pub restaking_program: solana_program::pubkey::Pubkey, } impl SetConfigFees { @@ -50,7 +50,7 @@ impl SetConfigFees { true, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.restaking_program_id, + self.restaking_program, false, )); accounts.extend_from_slice(remaining_accounts); @@ -100,14 +100,14 @@ pub struct SetConfigFeesInstructionArgs { /// 1. `[writable]` config /// 2. `[]` ncn /// 3. `[signer]` ncn_admin -/// 4. `[]` restaking_program_id +/// 4. `[]` restaking_program #[derive(Clone, Debug, Default)] pub struct SetConfigFeesBuilder { restaking_config: Option, config: Option, ncn: Option, ncn_admin: Option, - restaking_program_id: Option, + restaking_program: Option, new_dao_fee_bps: Option, new_ncn_fee_bps: Option, new_block_engine_fee_bps: Option, @@ -143,11 +143,11 @@ impl SetConfigFeesBuilder { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: solana_program::pubkey::Pubkey, + restaking_program: solana_program::pubkey::Pubkey, ) -> &mut Self { - self.restaking_program_id = Some(restaking_program_id); + self.restaking_program = Some(restaking_program); self } /// `[optional argument]` @@ -199,9 +199,9 @@ impl SetConfigFeesBuilder { config: self.config.expect("config is not set"), ncn: self.ncn.expect("ncn is not set"), ncn_admin: self.ncn_admin.expect("ncn_admin is not set"), - restaking_program_id: self - .restaking_program_id - .expect("restaking_program_id is not set"), + restaking_program: self + .restaking_program + .expect("restaking_program is not set"), }; let args = SetConfigFeesInstructionArgs { new_dao_fee_bps: self.new_dao_fee_bps.clone(), @@ -224,7 +224,7 @@ pub struct SetConfigFeesCpiAccounts<'a, 'b> { pub ncn_admin: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, } /// `set_config_fees` CPI instruction. @@ -240,7 +240,7 @@ pub struct SetConfigFeesCpi<'a, 'b> { pub ncn_admin: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, /// The arguments for the instruction. pub __args: SetConfigFeesInstructionArgs, } @@ -257,7 +257,7 @@ impl<'a, 'b> SetConfigFeesCpi<'a, 'b> { config: accounts.config, ncn: accounts.ncn, ncn_admin: accounts.ncn_admin, - restaking_program_id: accounts.restaking_program_id, + restaking_program: accounts.restaking_program, __args: args, } } @@ -312,7 +312,7 @@ impl<'a, 'b> SetConfigFeesCpi<'a, 'b> { true, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.restaking_program_id.key, + *self.restaking_program.key, false, )); remaining_accounts.iter().for_each(|remaining_account| { @@ -337,7 +337,7 @@ impl<'a, 'b> SetConfigFeesCpi<'a, 'b> { account_infos.push(self.config.clone()); account_infos.push(self.ncn.clone()); account_infos.push(self.ncn_admin.clone()); - account_infos.push(self.restaking_program_id.clone()); + account_infos.push(self.restaking_program.clone()); remaining_accounts .iter() .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); @@ -358,7 +358,7 @@ impl<'a, 'b> SetConfigFeesCpi<'a, 'b> { /// 1. `[writable]` config /// 2. `[]` ncn /// 3. `[signer]` ncn_admin -/// 4. `[]` restaking_program_id +/// 4. `[]` restaking_program #[derive(Clone, Debug)] pub struct SetConfigFeesCpiBuilder<'a, 'b> { instruction: Box>, @@ -372,7 +372,7 @@ impl<'a, 'b> SetConfigFeesCpiBuilder<'a, 'b> { config: None, ncn: None, ncn_admin: None, - restaking_program_id: None, + restaking_program: None, new_dao_fee_bps: None, new_ncn_fee_bps: None, new_block_engine_fee_bps: None, @@ -411,11 +411,11 @@ impl<'a, 'b> SetConfigFeesCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + restaking_program: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.restaking_program_id = Some(restaking_program_id); + self.instruction.restaking_program = Some(restaking_program); self } /// `[optional argument]` @@ -503,10 +503,10 @@ impl<'a, 'b> SetConfigFeesCpiBuilder<'a, 'b> { ncn_admin: self.instruction.ncn_admin.expect("ncn_admin is not set"), - restaking_program_id: self + restaking_program: self .instruction - .restaking_program_id - .expect("restaking_program_id is not set"), + .restaking_program + .expect("restaking_program is not set"), __args: args, }; instruction.invoke_signed_with_remaining_accounts( @@ -523,7 +523,7 @@ struct SetConfigFeesCpiBuilderInstruction<'a, 'b> { config: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_admin: Option<&'b solana_program::account_info::AccountInfo<'a>>, - restaking_program_id: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, new_dao_fee_bps: Option, new_ncn_fee_bps: Option, new_block_engine_fee_bps: Option, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/set_new_admin.rs b/clients/rust/jito_tip_router/src/generated/instructions/set_new_admin.rs index c46086d..66b1443 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/set_new_admin.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/set_new_admin.rs @@ -18,7 +18,7 @@ pub struct SetNewAdmin { pub new_admin: solana_program::pubkey::Pubkey, - pub restaking_program_id: solana_program::pubkey::Pubkey, + pub restaking_program: solana_program::pubkey::Pubkey, } impl SetNewAdmin { @@ -51,7 +51,7 @@ impl SetNewAdmin { false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - self.restaking_program_id, + self.restaking_program, false, )); accounts.extend_from_slice(remaining_accounts); @@ -98,14 +98,14 @@ pub struct SetNewAdminInstructionArgs { /// 1. `[]` ncn /// 2. `[signer]` ncn_admin /// 3. `[]` new_admin -/// 4. `[]` restaking_program_id +/// 4. `[]` restaking_program #[derive(Clone, Debug, Default)] pub struct SetNewAdminBuilder { config: Option, ncn: Option, ncn_admin: Option, new_admin: Option, - restaking_program_id: Option, + restaking_program: Option, role: Option, __remaining_accounts: Vec, } @@ -135,11 +135,11 @@ impl SetNewAdminBuilder { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: solana_program::pubkey::Pubkey, + restaking_program: solana_program::pubkey::Pubkey, ) -> &mut Self { - self.restaking_program_id = Some(restaking_program_id); + self.restaking_program = Some(restaking_program); self } #[inline(always)] @@ -172,9 +172,9 @@ impl SetNewAdminBuilder { ncn: self.ncn.expect("ncn is not set"), ncn_admin: self.ncn_admin.expect("ncn_admin is not set"), new_admin: self.new_admin.expect("new_admin is not set"), - restaking_program_id: self - .restaking_program_id - .expect("restaking_program_id is not set"), + restaking_program: self + .restaking_program + .expect("restaking_program is not set"), }; let args = SetNewAdminInstructionArgs { role: self.role.clone().expect("role is not set"), @@ -194,7 +194,7 @@ pub struct SetNewAdminCpiAccounts<'a, 'b> { pub new_admin: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, } /// `set_new_admin` CPI instruction. @@ -210,7 +210,7 @@ pub struct SetNewAdminCpi<'a, 'b> { pub new_admin: &'b solana_program::account_info::AccountInfo<'a>, - pub restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, /// The arguments for the instruction. pub __args: SetNewAdminInstructionArgs, } @@ -227,7 +227,7 @@ impl<'a, 'b> SetNewAdminCpi<'a, 'b> { ncn: accounts.ncn, ncn_admin: accounts.ncn_admin, new_admin: accounts.new_admin, - restaking_program_id: accounts.restaking_program_id, + restaking_program: accounts.restaking_program, __args: args, } } @@ -282,7 +282,7 @@ impl<'a, 'b> SetNewAdminCpi<'a, 'b> { false, )); accounts.push(solana_program::instruction::AccountMeta::new_readonly( - *self.restaking_program_id.key, + *self.restaking_program.key, false, )); remaining_accounts.iter().for_each(|remaining_account| { @@ -307,7 +307,7 @@ impl<'a, 'b> SetNewAdminCpi<'a, 'b> { account_infos.push(self.ncn.clone()); account_infos.push(self.ncn_admin.clone()); account_infos.push(self.new_admin.clone()); - account_infos.push(self.restaking_program_id.clone()); + account_infos.push(self.restaking_program.clone()); remaining_accounts .iter() .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); @@ -328,7 +328,7 @@ impl<'a, 'b> SetNewAdminCpi<'a, 'b> { /// 1. `[]` ncn /// 2. `[signer]` ncn_admin /// 3. `[]` new_admin -/// 4. `[]` restaking_program_id +/// 4. `[]` restaking_program #[derive(Clone, Debug)] pub struct SetNewAdminCpiBuilder<'a, 'b> { instruction: Box>, @@ -342,7 +342,7 @@ impl<'a, 'b> SetNewAdminCpiBuilder<'a, 'b> { ncn: None, ncn_admin: None, new_admin: None, - restaking_program_id: None, + restaking_program: None, role: None, __remaining_accounts: Vec::new(), }); @@ -378,11 +378,11 @@ impl<'a, 'b> SetNewAdminCpiBuilder<'a, 'b> { self } #[inline(always)] - pub fn restaking_program_id( + pub fn restaking_program( &mut self, - restaking_program_id: &'b solana_program::account_info::AccountInfo<'a>, + restaking_program: &'b solana_program::account_info::AccountInfo<'a>, ) -> &mut Self { - self.instruction.restaking_program_id = Some(restaking_program_id); + self.instruction.restaking_program = Some(restaking_program); self } #[inline(always)] @@ -445,10 +445,10 @@ impl<'a, 'b> SetNewAdminCpiBuilder<'a, 'b> { new_admin: self.instruction.new_admin.expect("new_admin is not set"), - restaking_program_id: self + restaking_program: self .instruction - .restaking_program_id - .expect("restaking_program_id is not set"), + .restaking_program + .expect("restaking_program is not set"), __args: args, }; instruction.invoke_signed_with_remaining_accounts( @@ -465,7 +465,7 @@ struct SetNewAdminCpiBuilderInstruction<'a, 'b> { ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_admin: Option<&'b solana_program::account_info::AccountInfo<'a>>, new_admin: Option<&'b solana_program::account_info::AccountInfo<'a>>, - restaking_program_id: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, role: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( diff --git a/clients/rust/jito_tip_router/src/generated/instructions/snapshot_vault_operator_delegation.rs b/clients/rust/jito_tip_router/src/generated/instructions/snapshot_vault_operator_delegation.rs new file mode 100644 index 0000000..86b411b --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/instructions/snapshot_vault_operator_delegation.rs @@ -0,0 +1,815 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; + +/// Accounts. +pub struct SnapshotVaultOperatorDelegation { + pub ncn_config: solana_program::pubkey::Pubkey, + + pub restaking_config: solana_program::pubkey::Pubkey, + + pub ncn: solana_program::pubkey::Pubkey, + + pub operator: solana_program::pubkey::Pubkey, + + pub vault: solana_program::pubkey::Pubkey, + + pub vault_ncn_ticket: solana_program::pubkey::Pubkey, + + pub ncn_vault_ticket: solana_program::pubkey::Pubkey, + + pub vault_operator_delegation: solana_program::pubkey::Pubkey, + + pub weight_table: solana_program::pubkey::Pubkey, + + pub epoch_snapshot: solana_program::pubkey::Pubkey, + + pub operator_snapshot: solana_program::pubkey::Pubkey, + + pub vault_program: solana_program::pubkey::Pubkey, + + pub restaking_program: solana_program::pubkey::Pubkey, +} + +impl SnapshotVaultOperatorDelegation { + pub fn instruction( + &self, + args: SnapshotVaultOperatorDelegationInstructionArgs, + ) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: SnapshotVaultOperatorDelegationInstructionArgs, + remaining_accounts: &[solana_program::instruction::AccountMeta], + ) -> solana_program::instruction::Instruction { + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.restaking_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn, false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.operator, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.vault, false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.vault_ncn_ticket, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn_vault_ticket, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.vault_operator_delegation, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.weight_table, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.epoch_snapshot, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.operator_snapshot, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.vault_program, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.restaking_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = SnapshotVaultOperatorDelegationInstructionData::new() + .try_to_vec() + .unwrap(); + let mut args = args.try_to_vec().unwrap(); + data.append(&mut args); + + solana_program::instruction::Instruction { + program_id: crate::JITO_TIP_ROUTER_ID, + accounts, + data, + } + } +} + +#[derive(BorshDeserialize, BorshSerialize)] +pub struct SnapshotVaultOperatorDelegationInstructionData { + discriminator: u8, +} + +impl SnapshotVaultOperatorDelegationInstructionData { + pub fn new() -> Self { + Self { discriminator: 7 } + } +} + +impl Default for SnapshotVaultOperatorDelegationInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SnapshotVaultOperatorDelegationInstructionArgs { + pub first_slot_of_ncn_epoch: Option, +} + +/// Instruction builder for `SnapshotVaultOperatorDelegation`. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[]` restaking_config +/// 2. `[]` ncn +/// 3. `[]` operator +/// 4. `[]` vault +/// 5. `[]` vault_ncn_ticket +/// 6. `[]` ncn_vault_ticket +/// 7. `[]` vault_operator_delegation +/// 8. `[]` weight_table +/// 9. `[writable]` epoch_snapshot +/// 10. `[writable]` operator_snapshot +/// 11. `[]` vault_program +/// 12. `[]` restaking_program +#[derive(Clone, Debug, Default)] +pub struct SnapshotVaultOperatorDelegationBuilder { + ncn_config: Option, + restaking_config: Option, + ncn: Option, + operator: Option, + vault: Option, + vault_ncn_ticket: Option, + ncn_vault_ticket: Option, + vault_operator_delegation: Option, + weight_table: Option, + epoch_snapshot: Option, + operator_snapshot: Option, + vault_program: Option, + restaking_program: Option, + first_slot_of_ncn_epoch: Option, + __remaining_accounts: Vec, +} + +impl SnapshotVaultOperatorDelegationBuilder { + pub fn new() -> Self { + Self::default() + } + #[inline(always)] + pub fn ncn_config(&mut self, ncn_config: solana_program::pubkey::Pubkey) -> &mut Self { + self.ncn_config = Some(ncn_config); + self + } + #[inline(always)] + pub fn restaking_config( + &mut self, + restaking_config: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.restaking_config = Some(restaking_config); + self + } + #[inline(always)] + pub fn ncn(&mut self, ncn: solana_program::pubkey::Pubkey) -> &mut Self { + self.ncn = Some(ncn); + self + } + #[inline(always)] + pub fn operator(&mut self, operator: solana_program::pubkey::Pubkey) -> &mut Self { + self.operator = Some(operator); + self + } + #[inline(always)] + pub fn vault(&mut self, vault: solana_program::pubkey::Pubkey) -> &mut Self { + self.vault = Some(vault); + self + } + #[inline(always)] + pub fn vault_ncn_ticket( + &mut self, + vault_ncn_ticket: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.vault_ncn_ticket = Some(vault_ncn_ticket); + self + } + #[inline(always)] + pub fn ncn_vault_ticket( + &mut self, + ncn_vault_ticket: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.ncn_vault_ticket = Some(ncn_vault_ticket); + self + } + #[inline(always)] + pub fn vault_operator_delegation( + &mut self, + vault_operator_delegation: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.vault_operator_delegation = Some(vault_operator_delegation); + self + } + #[inline(always)] + pub fn weight_table(&mut self, weight_table: solana_program::pubkey::Pubkey) -> &mut Self { + self.weight_table = Some(weight_table); + self + } + #[inline(always)] + pub fn epoch_snapshot(&mut self, epoch_snapshot: solana_program::pubkey::Pubkey) -> &mut Self { + self.epoch_snapshot = Some(epoch_snapshot); + self + } + #[inline(always)] + pub fn operator_snapshot( + &mut self, + operator_snapshot: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.operator_snapshot = Some(operator_snapshot); + self + } + #[inline(always)] + pub fn vault_program(&mut self, vault_program: solana_program::pubkey::Pubkey) -> &mut Self { + self.vault_program = Some(vault_program); + self + } + #[inline(always)] + pub fn restaking_program( + &mut self, + restaking_program: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.restaking_program = Some(restaking_program); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn first_slot_of_ncn_epoch(&mut self, first_slot_of_ncn_epoch: u64) -> &mut Self { + self.first_slot_of_ncn_epoch = Some(first_slot_of_ncn_epoch); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: solana_program::instruction::AccountMeta, + ) -> &mut Self { + self.__remaining_accounts.push(account); + self + } + /// Add additional accounts to the instruction. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[solana_program::instruction::AccountMeta], + ) -> &mut Self { + self.__remaining_accounts.extend_from_slice(accounts); + self + } + #[allow(clippy::clone_on_copy)] + pub fn instruction(&self) -> solana_program::instruction::Instruction { + let accounts = SnapshotVaultOperatorDelegation { + ncn_config: self.ncn_config.expect("ncn_config is not set"), + restaking_config: self.restaking_config.expect("restaking_config is not set"), + ncn: self.ncn.expect("ncn is not set"), + operator: self.operator.expect("operator is not set"), + vault: self.vault.expect("vault is not set"), + vault_ncn_ticket: self.vault_ncn_ticket.expect("vault_ncn_ticket is not set"), + ncn_vault_ticket: self.ncn_vault_ticket.expect("ncn_vault_ticket is not set"), + vault_operator_delegation: self + .vault_operator_delegation + .expect("vault_operator_delegation is not set"), + weight_table: self.weight_table.expect("weight_table is not set"), + epoch_snapshot: self.epoch_snapshot.expect("epoch_snapshot is not set"), + operator_snapshot: self + .operator_snapshot + .expect("operator_snapshot is not set"), + vault_program: self.vault_program.expect("vault_program is not set"), + restaking_program: self + .restaking_program + .expect("restaking_program is not set"), + }; + let args = SnapshotVaultOperatorDelegationInstructionArgs { + first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `snapshot_vault_operator_delegation` CPI accounts. +pub struct SnapshotVaultOperatorDelegationCpiAccounts<'a, 'b> { + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub operator: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault_ncn_ticket: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault_operator_delegation: &'b solana_program::account_info::AccountInfo<'a>, + + pub weight_table: &'b solana_program::account_info::AccountInfo<'a>, + + pub epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub operator_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault_program: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, +} + +/// `snapshot_vault_operator_delegation` CPI instruction. +pub struct SnapshotVaultOperatorDelegationCpi<'a, 'b> { + /// The program to invoke. + pub __program: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub operator: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault_ncn_ticket: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault_operator_delegation: &'b solana_program::account_info::AccountInfo<'a>, + + pub weight_table: &'b solana_program::account_info::AccountInfo<'a>, + + pub epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub operator_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + + pub vault_program: &'b solana_program::account_info::AccountInfo<'a>, + + pub restaking_program: &'b solana_program::account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: SnapshotVaultOperatorDelegationInstructionArgs, +} + +impl<'a, 'b> SnapshotVaultOperatorDelegationCpi<'a, 'b> { + pub fn new( + program: &'b solana_program::account_info::AccountInfo<'a>, + accounts: SnapshotVaultOperatorDelegationCpiAccounts<'a, 'b>, + args: SnapshotVaultOperatorDelegationInstructionArgs, + ) -> Self { + Self { + __program: program, + ncn_config: accounts.ncn_config, + restaking_config: accounts.restaking_config, + ncn: accounts.ncn, + operator: accounts.operator, + vault: accounts.vault, + vault_ncn_ticket: accounts.vault_ncn_ticket, + ncn_vault_ticket: accounts.ncn_vault_ticket, + vault_operator_delegation: accounts.vault_operator_delegation, + weight_table: accounts.weight_table, + epoch_snapshot: accounts.epoch_snapshot, + operator_snapshot: accounts.operator_snapshot, + vault_program: accounts.vault_program, + restaking_program: accounts.restaking_program, + __args: args, + } + } + #[inline(always)] + pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], &[]) + } + #[inline(always)] + pub fn invoke_with_remaining_accounts( + &self, + remaining_accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(&[], remaining_accounts) + } + #[inline(always)] + pub fn invoke_signed( + &self, + signers_seeds: &[&[&[u8]]], + ) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed_with_remaining_accounts(signers_seeds, &[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed_with_remaining_accounts( + &self, + signers_seeds: &[&[&[u8]]], + remaining_accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> solana_program::entrypoint::ProgramResult { + let mut accounts = Vec::with_capacity(13 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.restaking_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.operator.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.vault.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.vault_ncn_ticket.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn_vault_ticket.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.vault_operator_delegation.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.weight_table.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.epoch_snapshot.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.operator_snapshot.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.vault_program.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.restaking_program.key, + false, + )); + remaining_accounts.iter().for_each(|remaining_account| { + accounts.push(solana_program::instruction::AccountMeta { + pubkey: *remaining_account.0.key, + is_signer: remaining_account.1, + is_writable: remaining_account.2, + }) + }); + let mut data = SnapshotVaultOperatorDelegationInstructionData::new() + .try_to_vec() + .unwrap(); + let mut args = self.__args.try_to_vec().unwrap(); + data.append(&mut args); + + let instruction = solana_program::instruction::Instruction { + program_id: crate::JITO_TIP_ROUTER_ID, + accounts, + data, + }; + let mut account_infos = Vec::with_capacity(13 + 1 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.ncn_config.clone()); + account_infos.push(self.restaking_config.clone()); + account_infos.push(self.ncn.clone()); + account_infos.push(self.operator.clone()); + account_infos.push(self.vault.clone()); + account_infos.push(self.vault_ncn_ticket.clone()); + account_infos.push(self.ncn_vault_ticket.clone()); + account_infos.push(self.vault_operator_delegation.clone()); + account_infos.push(self.weight_table.clone()); + account_infos.push(self.epoch_snapshot.clone()); + account_infos.push(self.operator_snapshot.clone()); + account_infos.push(self.vault_program.clone()); + account_infos.push(self.restaking_program.clone()); + remaining_accounts + .iter() + .for_each(|remaining_account| account_infos.push(remaining_account.0.clone())); + + if signers_seeds.is_empty() { + solana_program::program::invoke(&instruction, &account_infos) + } else { + solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds) + } + } +} + +/// Instruction builder for `SnapshotVaultOperatorDelegation` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[]` restaking_config +/// 2. `[]` ncn +/// 3. `[]` operator +/// 4. `[]` vault +/// 5. `[]` vault_ncn_ticket +/// 6. `[]` ncn_vault_ticket +/// 7. `[]` vault_operator_delegation +/// 8. `[]` weight_table +/// 9. `[writable]` epoch_snapshot +/// 10. `[writable]` operator_snapshot +/// 11. `[]` vault_program +/// 12. `[]` restaking_program +#[derive(Clone, Debug)] +pub struct SnapshotVaultOperatorDelegationCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> SnapshotVaultOperatorDelegationCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(SnapshotVaultOperatorDelegationCpiBuilderInstruction { + __program: program, + ncn_config: None, + restaking_config: None, + ncn: None, + operator: None, + vault: None, + vault_ncn_ticket: None, + ncn_vault_ticket: None, + vault_operator_delegation: None, + weight_table: None, + epoch_snapshot: None, + operator_snapshot: None, + vault_program: None, + restaking_program: None, + first_slot_of_ncn_epoch: None, + __remaining_accounts: Vec::new(), + }); + Self { instruction } + } + #[inline(always)] + pub fn ncn_config( + &mut self, + ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.ncn_config = Some(ncn_config); + self + } + #[inline(always)] + pub fn restaking_config( + &mut self, + restaking_config: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.restaking_config = Some(restaking_config); + self + } + #[inline(always)] + pub fn ncn(&mut self, ncn: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.ncn = Some(ncn); + self + } + #[inline(always)] + pub fn operator( + &mut self, + operator: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.operator = Some(operator); + self + } + #[inline(always)] + pub fn vault(&mut self, vault: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.vault = Some(vault); + self + } + #[inline(always)] + pub fn vault_ncn_ticket( + &mut self, + vault_ncn_ticket: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault_ncn_ticket = Some(vault_ncn_ticket); + self + } + #[inline(always)] + pub fn ncn_vault_ticket( + &mut self, + ncn_vault_ticket: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.ncn_vault_ticket = Some(ncn_vault_ticket); + self + } + #[inline(always)] + pub fn vault_operator_delegation( + &mut self, + vault_operator_delegation: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault_operator_delegation = Some(vault_operator_delegation); + self + } + #[inline(always)] + pub fn weight_table( + &mut self, + weight_table: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.weight_table = Some(weight_table); + self + } + #[inline(always)] + pub fn epoch_snapshot( + &mut self, + epoch_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.epoch_snapshot = Some(epoch_snapshot); + self + } + #[inline(always)] + pub fn operator_snapshot( + &mut self, + operator_snapshot: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.operator_snapshot = Some(operator_snapshot); + self + } + #[inline(always)] + pub fn vault_program( + &mut self, + vault_program: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.vault_program = Some(vault_program); + self + } + #[inline(always)] + pub fn restaking_program( + &mut self, + restaking_program: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.restaking_program = Some(restaking_program); + self + } + /// `[optional argument]` + #[inline(always)] + pub fn first_slot_of_ncn_epoch(&mut self, first_slot_of_ncn_epoch: u64) -> &mut Self { + self.instruction.first_slot_of_ncn_epoch = Some(first_slot_of_ncn_epoch); + self + } + /// Add an additional account to the instruction. + #[inline(always)] + pub fn add_remaining_account( + &mut self, + account: &'b solana_program::account_info::AccountInfo<'a>, + is_writable: bool, + is_signer: bool, + ) -> &mut Self { + self.instruction + .__remaining_accounts + .push((account, is_writable, is_signer)); + self + } + /// Add additional accounts to the instruction. + /// + /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not, + /// and a `bool` indicating whether the account is a signer or not. + #[inline(always)] + pub fn add_remaining_accounts( + &mut self, + accounts: &[( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )], + ) -> &mut Self { + self.instruction + .__remaining_accounts + .extend_from_slice(accounts); + self + } + #[inline(always)] + pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult { + self.invoke_signed(&[]) + } + #[allow(clippy::clone_on_copy)] + #[allow(clippy::vec_init_then_push)] + pub fn invoke_signed( + &self, + signers_seeds: &[&[&[u8]]], + ) -> solana_program::entrypoint::ProgramResult { + let args = SnapshotVaultOperatorDelegationInstructionArgs { + first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + }; + let instruction = SnapshotVaultOperatorDelegationCpi { + __program: self.instruction.__program, + + ncn_config: self.instruction.ncn_config.expect("ncn_config is not set"), + + restaking_config: self + .instruction + .restaking_config + .expect("restaking_config is not set"), + + ncn: self.instruction.ncn.expect("ncn is not set"), + + operator: self.instruction.operator.expect("operator is not set"), + + vault: self.instruction.vault.expect("vault is not set"), + + vault_ncn_ticket: self + .instruction + .vault_ncn_ticket + .expect("vault_ncn_ticket is not set"), + + ncn_vault_ticket: self + .instruction + .ncn_vault_ticket + .expect("ncn_vault_ticket is not set"), + + vault_operator_delegation: self + .instruction + .vault_operator_delegation + .expect("vault_operator_delegation is not set"), + + weight_table: self + .instruction + .weight_table + .expect("weight_table is not set"), + + epoch_snapshot: self + .instruction + .epoch_snapshot + .expect("epoch_snapshot is not set"), + + operator_snapshot: self + .instruction + .operator_snapshot + .expect("operator_snapshot is not set"), + + vault_program: self + .instruction + .vault_program + .expect("vault_program is not set"), + + restaking_program: self + .instruction + .restaking_program + .expect("restaking_program is not set"), + __args: args, + }; + instruction.invoke_signed_with_remaining_accounts( + signers_seeds, + &self.instruction.__remaining_accounts, + ) + } +} + +#[derive(Clone, Debug)] +struct SnapshotVaultOperatorDelegationCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_program::account_info::AccountInfo<'a>, + ncn_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, + operator: Option<&'b solana_program::account_info::AccountInfo<'a>>, + vault: Option<&'b solana_program::account_info::AccountInfo<'a>>, + vault_ncn_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, + ncn_vault_ticket: Option<&'b solana_program::account_info::AccountInfo<'a>>, + vault_operator_delegation: Option<&'b solana_program::account_info::AccountInfo<'a>>, + weight_table: Option<&'b solana_program::account_info::AccountInfo<'a>>, + epoch_snapshot: Option<&'b solana_program::account_info::AccountInfo<'a>>, + operator_snapshot: Option<&'b solana_program::account_info::AccountInfo<'a>>, + vault_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + first_slot_of_ncn_epoch: Option, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )>, +} diff --git a/clients/rust/jito_tip_router/src/generated/types/ballot.rs b/clients/rust/jito_tip_router/src/generated/types/ballot.rs new file mode 100644 index 0000000..b954536 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/types/ballot.rs @@ -0,0 +1,15 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Ballot { + pub merkle_root: [u8; 32], + #[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))] + pub reserved: [u8; 64], +} diff --git a/clients/rust/jito_tip_router/src/generated/types/ballot_tally.rs b/clients/rust/jito_tip_router/src/generated/types/ballot_tally.rs new file mode 100644 index 0000000..265dcf1 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/types/ballot_tally.rs @@ -0,0 +1,19 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; + +use crate::generated::types::Ballot; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BallotTally { + pub ballot: Ballot, + pub stake_weight: u128, + pub tally: u64, + #[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))] + pub reserved: [u8; 64], +} diff --git a/clients/rust/jito_tip_router/src/generated/types/mod.rs b/clients/rust/jito_tip_router/src/generated/types/mod.rs index 36abae2..82ebe6f 100644 --- a/clients/rust/jito_tip_router/src/generated/types/mod.rs +++ b/clients/rust/jito_tip_router/src/generated/types/mod.rs @@ -4,10 +4,17 @@ //! //! +pub(crate) mod r#ballot; +pub(crate) mod r#ballot_tally; pub(crate) mod r#config_admin_role; pub(crate) mod r#fee; pub(crate) mod r#fees; pub(crate) mod r#mint_entry; +pub(crate) mod r#operator_vote; +pub(crate) mod r#vault_operator_stake_weight; pub(crate) mod r#weight_entry; -pub use self::{r#config_admin_role::*, r#fee::*, r#fees::*, r#mint_entry::*, r#weight_entry::*}; +pub use self::{ + r#ballot::*, r#ballot_tally::*, r#config_admin_role::*, r#fee::*, r#fees::*, r#mint_entry::*, + r#operator_vote::*, r#vault_operator_stake_weight::*, r#weight_entry::*, +}; diff --git a/clients/rust/jito_tip_router/src/generated/types/operator_vote.rs b/clients/rust/jito_tip_router/src/generated/types/operator_vote.rs new file mode 100644 index 0000000..341cb17 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/types/operator_vote.rs @@ -0,0 +1,23 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct OperatorVote { + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub operator: Pubkey, + pub slot_voted: u64, + pub stake_weight: u128, + pub ballot_index: u16, + #[cfg_attr(feature = "serde", serde(with = "serde_with::As::"))] + pub reserved: [u8; 64], +} diff --git a/clients/rust/jito_tip_router/src/generated/types/vault_operator_stake_weight.rs b/clients/rust/jito_tip_router/src/generated/types/vault_operator_stake_weight.rs new file mode 100644 index 0000000..e45eb5b --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/types/vault_operator_stake_weight.rs @@ -0,0 +1,21 @@ +//! This code was AUTOGENERATED using the kinobi library. +//! Please DO NOT EDIT THIS FILE, instead use visitors +//! to add features, then rerun kinobi to update it. +//! +//! + +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct VaultOperatorStakeWeight { + #[cfg_attr( + feature = "serde", + serde(with = "serde_with::As::") + )] + pub vault: Pubkey, + pub stake_weight: u128, + pub vault_index: u64, + pub reserved: [u8; 32], +} diff --git a/core/src/ballot_box.rs b/core/src/ballot_box.rs new file mode 100644 index 0000000..0895acc --- /dev/null +++ b/core/src/ballot_box.rs @@ -0,0 +1,397 @@ +use bytemuck::{Pod, Zeroable}; +use jito_bytemuck::{ + types::{PodU128, PodU16, PodU64}, + AccountDeserialize, Discriminator, +}; +use shank::{ShankAccount, ShankType}; +use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey}; +use spl_math::precise_number::PreciseNumber; + +use crate::{constants::PRECISE_CONSENSUS, discriminators::Discriminators, error::TipRouterError}; + +#[derive(Debug, Clone, PartialEq, Eq, Copy, Zeroable, ShankType, Pod, ShankType)] +#[repr(C)] +pub struct Ballot { + merkle_root: [u8; 32], + reserved: [u8; 64], +} + +impl Default for Ballot { + fn default() -> Self { + Self { + merkle_root: [0; 32], + reserved: [0; 64], + } + } +} + +impl Ballot { + pub const fn new(root: [u8; 32]) -> Self { + Self { + merkle_root: root, + reserved: [0; 64], + } + } + + pub const fn root(&self) -> [u8; 32] { + self.merkle_root + } + + pub fn is_valid(&self) -> bool { + self.merkle_root.iter().any(|&b| b != 0) + } +} + +#[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, ShankType)] +#[repr(C)] +pub struct BallotTally { + ballot: Ballot, + stake_weight: PodU128, + tally: PodU64, + reserved: [u8; 64], +} + +impl Default for BallotTally { + fn default() -> Self { + Self { + ballot: Ballot::default(), + stake_weight: PodU128::from(0), + tally: PodU64::from(0), + reserved: [0; 64], + } + } +} + +impl BallotTally { + pub fn new(ballot: Ballot, stake_weight: u128) -> Self { + Self { + ballot, + stake_weight: PodU128::from(stake_weight), + tally: PodU64::from(1), + reserved: [0; 64], + } + } + + pub const fn ballot(&self) -> Ballot { + self.ballot + } + + pub fn stake_weight(&self) -> u128 { + self.stake_weight.into() + } + + pub fn tally(&self) -> u64 { + self.tally.into() + } + + pub fn is_empty(&self) -> bool { + self.stake_weight() == 0 + } + + pub fn increment_tally(&mut self, stake_weight: u128) -> Result<(), TipRouterError> { + self.stake_weight = PodU128::from( + self.stake_weight() + .checked_add(stake_weight) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + self.tally = PodU64::from( + self.tally() + .checked_add(1) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, ShankType)] +#[repr(C)] +pub struct OperatorVote { + operator: Pubkey, + slot_voted: PodU64, + stake_weight: PodU128, + ballot_index: PodU16, + reserved: [u8; 64], +} + +impl Default for OperatorVote { + fn default() -> Self { + Self { + operator: Pubkey::default(), + slot_voted: PodU64::from(0), + stake_weight: PodU128::from(0), + ballot_index: PodU16::from(0), + reserved: [0; 64], + } + } +} + +impl OperatorVote { + pub fn new( + ballot_index: usize, + operator: Pubkey, + current_slot: u64, + stake_weight: u128, + ) -> Self { + Self { + operator, + ballot_index: PodU16::from(ballot_index as u16), + slot_voted: PodU64::from(current_slot), + stake_weight: PodU128::from(stake_weight), + reserved: [0; 64], + } + } + + pub const fn operator(&self) -> Pubkey { + self.operator + } + + pub fn slot_voted(&self) -> u64 { + self.slot_voted.into() + } + + pub fn stake_weight(&self) -> u128 { + self.stake_weight.into() + } + + pub fn ballot_index(&self) -> u16 { + self.ballot_index.into() + } + + pub fn is_empty(&self) -> bool { + self.stake_weight() == 0 + } +} + +// PDA'd ["epoch_snapshot", NCN, NCN_EPOCH_SLOT] +#[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, AccountDeserialize, ShankAccount)] +#[repr(C)] +pub struct BallotBox { + ncn: Pubkey, + + ncn_epoch: PodU64, + + bump: u8, + + slot_created: PodU64, + slot_consensus_reached: PodU64, + + reserved: [u8; 128], + + operators_voted: PodU64, + unique_ballots: PodU64, + + winning_ballot: Ballot, + + //TODO fix 32 -> MAX_OPERATORS + operator_votes: [OperatorVote; 32], + ballot_tallies: [BallotTally; 32], +} + +impl Discriminator for BallotBox { + const DISCRIMINATOR: u8 = Discriminators::EpochSnapshot as u8; +} + +impl BallotBox { + pub fn new(ncn: Pubkey, ncn_epoch: u64, bump: u8, current_slot: u64) -> Self { + Self { + ncn, + ncn_epoch: PodU64::from(ncn_epoch), + bump, + slot_created: PodU64::from(current_slot), + slot_consensus_reached: PodU64::from(0), + operators_voted: PodU64::from(0), + unique_ballots: PodU64::from(0), + winning_ballot: Ballot::default(), + //TODO fix 32 -> MAX_OPERATORS + operator_votes: [OperatorVote::default(); 32], + ballot_tallies: [BallotTally::default(); 32], + reserved: [0; 128], + } + } + + pub fn seeds(ncn: &Pubkey, ncn_epoch: u64) -> Vec> { + Vec::from_iter( + [ + b"ballot_box".to_vec(), + ncn.to_bytes().to_vec(), + ncn_epoch.to_le_bytes().to_vec(), + ] + .iter() + .cloned(), + ) + } + + pub fn find_program_address( + program_id: &Pubkey, + ncn: &Pubkey, + ncn_epoch: u64, + ) -> (Pubkey, u8, Vec>) { + let seeds = Self::seeds(ncn, ncn_epoch); + let seeds_iter: Vec<_> = seeds.iter().map(|s| s.as_slice()).collect(); + let (pda, bump) = Pubkey::find_program_address(&seeds_iter, program_id); + (pda, bump, seeds) + } + + pub fn load( + program_id: &Pubkey, + ncn: &Pubkey, + ncn_epoch: u64, + epoch_snapshot: &AccountInfo, + expect_writable: bool, + ) -> Result<(), ProgramError> { + if epoch_snapshot.owner.ne(program_id) { + msg!("Ballot box account has an invalid owner"); + return Err(ProgramError::InvalidAccountOwner); + } + if epoch_snapshot.data_is_empty() { + msg!("Ballot box account data is empty"); + return Err(ProgramError::InvalidAccountData); + } + if expect_writable && !epoch_snapshot.is_writable { + msg!("Ballot box account is not writable"); + return Err(ProgramError::InvalidAccountData); + } + if epoch_snapshot.data.borrow()[0].ne(&Self::DISCRIMINATOR) { + msg!("Ballot box account discriminator is invalid"); + return Err(ProgramError::InvalidAccountData); + } + if epoch_snapshot + .key + .ne(&Self::find_program_address(program_id, ncn, ncn_epoch).0) + { + msg!("Ballot box account is not at the correct PDA"); + return Err(ProgramError::InvalidAccountData); + } + Ok(()) + } + + pub fn slot_consensus_reached(&self) -> u64 { + self.slot_consensus_reached.into() + } + + pub fn unique_ballots(&self) -> u64 { + self.unique_ballots.into() + } + + pub fn operators_voted(&self) -> u64 { + self.operators_voted.into() + } + + pub fn is_consensus_reached(&self) -> bool { + self.slot_consensus_reached() > 0 + } + + pub fn get_winning_ballot(&self) -> Result { + if self.winning_ballot.is_valid() { + Ok(self.winning_ballot) + } else { + Err(TipRouterError::ConsensusNotReached) + } + } + + fn increment_or_create_ballot_tally( + &mut self, + ballot: &Ballot, + stake_weight: u128, + ) -> Result { + let mut tally_index: usize = 0; + for tally in self.ballot_tallies.iter_mut() { + if tally.ballot.eq(ballot) { + tally.increment_tally(stake_weight)?; + return Ok(tally_index); + } + + if tally.is_empty() { + *tally = BallotTally::new(*ballot, stake_weight); + + self.unique_ballots = PodU64::from( + self.unique_ballots() + .checked_add(1) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + + return Ok(tally_index); + } + + tally_index = tally_index + .checked_add(1) + .ok_or(TipRouterError::ArithmeticOverflow)?; + } + + Err(TipRouterError::BallotTallyFull) + } + + pub fn cast_vote( + &mut self, + operator: Pubkey, + ballot: Ballot, + stake_weight: u128, + current_slot: u64, + ) -> Result<(), TipRouterError> { + let ballot_index = self.increment_or_create_ballot_tally(&ballot, stake_weight)?; + + for vote in self.operator_votes.iter_mut() { + if vote.operator().eq(&operator) { + return Err(TipRouterError::DuplicateVoteCast); + } + + if vote.is_empty() { + let operator_vote = + OperatorVote::new(ballot_index, operator, current_slot, stake_weight); + *vote = operator_vote; + + self.operators_voted = PodU64::from( + self.operators_voted() + .checked_add(1) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + + return Ok(()); + } + } + + Err(TipRouterError::OperatorVotesFull) + } + + // Should be called anytime a new vote is cast + pub fn tally_votes( + &mut self, + total_stake_weight: u128, + current_slot: u64, + ) -> Result<(), TipRouterError> { + if self.slot_consensus_reached() != 0 { + return Ok(()); + } + + let max_tally = self + .ballot_tallies + .iter() + .max_by_key(|t| t.stake_weight()) + .unwrap(); + + let ballot_stake_weight = max_tally.stake_weight(); + let precise_ballot_stake_weight = + PreciseNumber::new(ballot_stake_weight).ok_or(TipRouterError::NewPreciseNumberError)?; + let precise_total_stake_weight = + PreciseNumber::new(total_stake_weight).ok_or(TipRouterError::NewPreciseNumberError)?; + + let ballot_percentage_of_total = precise_ballot_stake_weight + .checked_div(&precise_total_stake_weight) + .ok_or(TipRouterError::DenominatorIsZero)?; + + let target_precise_percentage = + PreciseNumber::new(PRECISE_CONSENSUS).ok_or(TipRouterError::NewPreciseNumberError)?; + + let consensus_reached = + ballot_percentage_of_total.greater_than_or_equal(&target_precise_percentage); + + if consensus_reached { + self.slot_consensus_reached = PodU64::from(current_slot); + + self.winning_ballot = max_tally.ballot(); + } + + Ok(()) + } +} diff --git a/core/src/constants.rs b/core/src/constants.rs new file mode 100644 index 0000000..df8fbe0 --- /dev/null +++ b/core/src/constants.rs @@ -0,0 +1,4 @@ +pub const MAX_FEE_BPS: u64 = 10_000; +pub const MAX_OPERATORS: usize = 256; +pub const MAX_VAULT_OPERATOR_DELEGATIONS: usize = 64; +pub const PRECISE_CONSENSUS: u128 = 666_666_666_666; diff --git a/core/src/discriminators.rs b/core/src/discriminators.rs index 2196c1a..15b8bda 100644 --- a/core/src/discriminators.rs +++ b/core/src/discriminators.rs @@ -1,6 +1,13 @@ #[repr(u8)] pub enum Discriminators { - Config = 1, - WeightTable = 2, - TrackedMints = 3, + // Configs + NCNConfig = 0x01, + TrackedMints = 0x02, + // Snapshots + WeightTable = 0x10, + EpochSnapshot = 0x11, + OperatorSnapshot = 0x12, + // Voting + BallotBox = 0x20, + // Distribution } diff --git a/core/src/epoch_snapshot.rs b/core/src/epoch_snapshot.rs new file mode 100644 index 0000000..6427801 --- /dev/null +++ b/core/src/epoch_snapshot.rs @@ -0,0 +1,533 @@ +use bytemuck::{Pod, Zeroable}; +use jito_bytemuck::{ + types::{PodBool, PodU128, PodU16, PodU64}, + AccountDeserialize, Discriminator, +}; +use jito_vault_core::vault_operator_delegation::VaultOperatorDelegation; +use shank::{ShankAccount, ShankType}; +use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey}; +use spl_math::precise_number::PreciseNumber; + +use crate::{ + discriminators::Discriminators, error::TipRouterError, fees::Fees, weight_table::WeightTable, +}; + +// PDA'd ["epoch_snapshot", NCN, NCN_EPOCH_SLOT] +#[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, AccountDeserialize, ShankAccount)] +#[repr(C)] +pub struct EpochSnapshot { + /// The NCN on-chain program is the signer to create and update this account, + /// this pushes the responsibility of managing the account to the NCN program. + ncn: Pubkey, + + /// The NCN epoch for which the Epoch snapshot is valid + ncn_epoch: PodU64, + + /// Bump seed for the PDA + bump: u8, + + /// Slot Epoch snapshot was created + slot_created: PodU64, + slot_finalized: PodU64, + + ncn_fees: Fees, + + operator_count: PodU64, + vault_count: PodU64, + operators_registered: PodU64, + valid_operator_vault_delegations: PodU64, + + /// Counted as each delegate gets added + ///TODO What happens if `finalized() && total_votes() == 0`? + stake_weight: PodU128, + + /// Reserved space + reserved: [u8; 128], +} + +impl Discriminator for EpochSnapshot { + const DISCRIMINATOR: u8 = Discriminators::EpochSnapshot as u8; +} + +impl EpochSnapshot { + pub fn new( + ncn: Pubkey, + ncn_epoch: u64, + bump: u8, + current_slot: u64, + ncn_fees: Fees, + operator_count: u64, + vault_count: u64, + ) -> Self { + Self { + ncn, + ncn_epoch: PodU64::from(ncn_epoch), + slot_created: PodU64::from(current_slot), + slot_finalized: PodU64::from(0), + bump, + ncn_fees, + operator_count: PodU64::from(operator_count), + vault_count: PodU64::from(vault_count), + operators_registered: PodU64::from(0), + valid_operator_vault_delegations: PodU64::from(0), + stake_weight: PodU128::from(0), + reserved: [0; 128], + } + } + + pub fn seeds(ncn: &Pubkey, ncn_epoch: u64) -> Vec> { + Vec::from_iter( + [ + b"epoch_snapshot".to_vec(), + ncn.to_bytes().to_vec(), + ncn_epoch.to_le_bytes().to_vec(), + ] + .iter() + .cloned(), + ) + } + + pub fn find_program_address( + program_id: &Pubkey, + ncn: &Pubkey, + ncn_epoch: u64, + ) -> (Pubkey, u8, Vec>) { + let seeds = Self::seeds(ncn, ncn_epoch); + let seeds_iter: Vec<_> = seeds.iter().map(|s| s.as_slice()).collect(); + let (pda, bump) = Pubkey::find_program_address(&seeds_iter, program_id); + (pda, bump, seeds) + } + + pub fn load( + program_id: &Pubkey, + ncn: &Pubkey, + ncn_epoch: u64, + epoch_snapshot: &AccountInfo, + expect_writable: bool, + ) -> Result<(), ProgramError> { + if epoch_snapshot.owner.ne(program_id) { + msg!("Epoch Snapshot account has an invalid owner"); + return Err(ProgramError::InvalidAccountOwner); + } + if epoch_snapshot.data_is_empty() { + msg!("Epoch Snapshot account data is empty"); + return Err(ProgramError::InvalidAccountData); + } + if expect_writable && !epoch_snapshot.is_writable { + msg!("Epoch Snapshot account is not writable"); + return Err(ProgramError::InvalidAccountData); + } + if epoch_snapshot.data.borrow()[0].ne(&Self::DISCRIMINATOR) { + msg!("Epoch Snapshot account discriminator is invalid"); + return Err(ProgramError::InvalidAccountData); + } + if epoch_snapshot + .key + .ne(&Self::find_program_address(program_id, ncn, ncn_epoch).0) + { + msg!("Epoch Snapshot account is not at the correct PDA"); + return Err(ProgramError::InvalidAccountData); + } + Ok(()) + } + + pub fn operator_count(&self) -> u64 { + self.operator_count.into() + } + + pub fn vault_count(&self) -> u64 { + self.vault_count.into() + } + + pub fn operators_registered(&self) -> u64 { + self.operators_registered.into() + } + + pub fn valid_operator_vault_delegations(&self) -> u64 { + self.valid_operator_vault_delegations.into() + } + + pub fn stake_weight(&self) -> u128 { + self.stake_weight.into() + } + + pub fn finalized(&self) -> bool { + self.operators_registered() == self.operator_count() + } + + pub fn increment_operator_registration( + &mut self, + current_slot: u64, + vault_operator_delegations: u64, + stake_weight: u128, + ) -> Result<(), TipRouterError> { + if self.finalized() { + return Err(TipRouterError::OperatorFinalized); + } + + self.operators_registered = PodU64::from( + self.operators_registered() + .checked_add(1) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + + self.valid_operator_vault_delegations = PodU64::from( + self.valid_operator_vault_delegations() + .checked_add(vault_operator_delegations) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + + self.stake_weight = PodU128::from( + self.stake_weight() + .checked_add(stake_weight) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + + if self.finalized() { + self.slot_finalized = PodU64::from(current_slot); + } + + Ok(()) + } +} + +// PDA'd ["operator_snapshot", OPERATOR, NCN, NCN_EPOCH_SLOT] +#[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, AccountDeserialize, ShankAccount)] +#[repr(C)] +pub struct OperatorSnapshot { + operator: Pubkey, + ncn: Pubkey, + ncn_epoch: PodU64, + bump: u8, + + slot_created: PodU64, + slot_finalized: PodU64, + + is_active: PodBool, + + ncn_operator_index: PodU64, + operator_index: PodU64, + operator_fee_bps: PodU16, + + vault_operator_delegation_count: PodU64, + vault_operator_delegations_registered: PodU64, + valid_operator_vault_delegations: PodU64, + + stake_weight: PodU128, + reserved: [u8; 256], + + //TODO change to 64 + vault_operator_stake_weight: [VaultOperatorStakeWeight; 32], +} + +#[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, ShankType)] +#[repr(C)] +pub struct VaultOperatorStakeWeight { + vault: Pubkey, + stake_weight: PodU128, + vault_index: PodU64, + reserved: [u8; 32], +} + +impl Default for VaultOperatorStakeWeight { + fn default() -> Self { + Self { + vault: Pubkey::default(), + vault_index: PodU64::from(u64::MAX), + stake_weight: PodU128::from(0), + reserved: [0; 32], + } + } +} + +impl VaultOperatorStakeWeight { + pub fn new(vault: Pubkey, stake_weight: u128, vault_index: u64) -> Self { + Self { + vault, + vault_index: PodU64::from(vault_index), + stake_weight: PodU128::from(stake_weight), + reserved: [0; 32], + } + } + + pub fn is_empty(&self) -> bool { + self.vault_index() == u64::MAX + } + + pub fn vault_index(&self) -> u64 { + self.vault_index.into() + } + + pub fn stake_weight(&self) -> u128 { + self.stake_weight.into() + } +} + +impl Discriminator for OperatorSnapshot { + const DISCRIMINATOR: u8 = Discriminators::OperatorSnapshot as u8; +} + +impl OperatorSnapshot { + pub const MAX_VAULT_OPERATOR_STAKE_WEIGHT: usize = 64; + + #[allow(clippy::too_many_arguments)] + pub fn new( + operator: Pubkey, + ncn: Pubkey, + ncn_epoch: u64, + bump: u8, + current_slot: u64, + is_active: bool, + ncn_operator_index: u64, + operator_index: u64, + operator_fee_bps: u16, + vault_operator_delegation_count: u64, + ) -> Result { + if vault_operator_delegation_count > Self::MAX_VAULT_OPERATOR_STAKE_WEIGHT as u64 { + return Err(TipRouterError::TooManyVaultOperatorDelegations); + } + + Ok(Self { + operator, + ncn, + ncn_epoch: PodU64::from(ncn_epoch), + bump, + slot_created: PodU64::from(current_slot), + slot_finalized: PodU64::from(0), + is_active: PodBool::from(is_active), + ncn_operator_index: PodU64::from(ncn_operator_index), + operator_index: PodU64::from(operator_index), + operator_fee_bps: PodU16::from(operator_fee_bps), + vault_operator_delegation_count: PodU64::from(vault_operator_delegation_count), + vault_operator_delegations_registered: PodU64::from(0), + valid_operator_vault_delegations: PodU64::from(0), + stake_weight: PodU128::from(0), + reserved: [0; 256], + vault_operator_stake_weight: [VaultOperatorStakeWeight::default(); 32], + }) + } + + #[allow(clippy::too_many_arguments)] + pub fn new_active( + operator: Pubkey, + ncn: Pubkey, + ncn_epoch: u64, + bump: u8, + current_slot: u64, + ncn_operator_index: u64, + operator_index: u64, + operator_fee_bps: u16, + vault_count: u64, + ) -> Result { + Self::new( + operator, + ncn, + ncn_epoch, + bump, + current_slot, + true, + ncn_operator_index, + operator_index, + operator_fee_bps, + vault_count, + ) + } + + pub fn new_inactive( + operator: Pubkey, + ncn: Pubkey, + ncn_epoch: u64, + bump: u8, + current_slot: u64, + ncn_operator_index: u64, + operator_index: u64, + ) -> Result { + let mut snapshot = Self::new( + operator, + ncn, + ncn_epoch, + bump, + current_slot, + false, + ncn_operator_index, + operator_index, + 0, + 0, + )?; + + snapshot.slot_finalized = PodU64::from(current_slot); + Ok(snapshot) + } + + pub fn seeds(operator: &Pubkey, ncn: &Pubkey, ncn_epoch: u64) -> Vec> { + Vec::from_iter( + [ + b"operator_snapshot".to_vec(), + operator.to_bytes().to_vec(), + ncn.to_bytes().to_vec(), + ncn_epoch.to_le_bytes().to_vec(), + ] + .iter() + .cloned(), + ) + } + + pub fn find_program_address( + program_id: &Pubkey, + operator: &Pubkey, + ncn: &Pubkey, + ncn_epoch: u64, + ) -> (Pubkey, u8, Vec>) { + let seeds = Self::seeds(operator, ncn, ncn_epoch); + let seeds_iter: Vec<_> = seeds.iter().map(|s| s.as_slice()).collect(); + let (pda, bump) = Pubkey::find_program_address(&seeds_iter, program_id); + (pda, bump, seeds) + } + + pub fn load( + program_id: &Pubkey, + operator: &Pubkey, + ncn: &Pubkey, + ncn_epoch: u64, + operator_snapshot: &AccountInfo, + expect_writable: bool, + ) -> Result<(), ProgramError> { + if operator_snapshot.owner.ne(program_id) { + msg!("Operator Snapshot account has an invalid owner"); + return Err(ProgramError::InvalidAccountOwner); + } + if operator_snapshot.data_is_empty() { + msg!("Operator Snapshot account data is empty"); + return Err(ProgramError::InvalidAccountData); + } + if expect_writable && !operator_snapshot.is_writable { + msg!("Operator Snapshot account is not writable"); + return Err(ProgramError::InvalidAccountData); + } + if operator_snapshot.data.borrow()[0].ne(&Self::DISCRIMINATOR) { + msg!("Operator Snapshot account discriminator is invalid"); + return Err(ProgramError::InvalidAccountData); + } + if operator_snapshot + .key + .ne(&Self::find_program_address(program_id, operator, ncn, ncn_epoch).0) + { + msg!("Operator Snapshot account is not at the correct PDA"); + return Err(ProgramError::InvalidAccountData); + } + Ok(()) + } + + pub fn vault_operator_delegation_count(&self) -> u64 { + self.vault_operator_delegation_count.into() + } + + pub fn vault_operator_delegations_registered(&self) -> u64 { + self.vault_operator_delegations_registered.into() + } + + pub fn valid_operator_vault_delegations(&self) -> u64 { + self.valid_operator_vault_delegations.into() + } + + pub fn stake_weight(&self) -> u128 { + self.stake_weight.into() + } + + pub fn finalized(&self) -> bool { + self.vault_operator_delegations_registered() == self.vault_operator_delegation_count() + } + + pub fn contains_vault_index(&self, vault_index: u64) -> bool { + self.vault_operator_stake_weight + .iter() + .any(|v| v.vault_index() == vault_index) + } + + pub fn insert_vault_operator_stake_weight( + &mut self, + vault: Pubkey, + vault_index: u64, + stake_weight: u128, + ) -> Result<(), TipRouterError> { + if self.vault_operator_delegations_registered() + > Self::MAX_VAULT_OPERATOR_STAKE_WEIGHT as u64 + { + return Err(TipRouterError::TooManyVaultOperatorDelegations); + } + + if self.contains_vault_index(vault_index) { + return Err(TipRouterError::DuplicateVaultOperatorDelegation); + } + + self.vault_operator_stake_weight[self.vault_operator_delegations_registered() as usize] = + VaultOperatorStakeWeight::new(vault, stake_weight, vault_index); + + Ok(()) + } + + pub fn increment_vault_operator_delegation_registration( + &mut self, + current_slot: u64, + vault: Pubkey, + vault_index: u64, + stake_weight: u128, + ) -> Result<(), TipRouterError> { + if self.finalized() { + return Err(TipRouterError::VaultOperatorDelegationFinalized); + } + + self.insert_vault_operator_stake_weight(vault, vault_index, stake_weight)?; + + self.vault_operator_delegations_registered = PodU64::from( + self.vault_operator_delegations_registered() + .checked_add(1) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + + if stake_weight > 0 { + self.valid_operator_vault_delegations = PodU64::from( + self.valid_operator_vault_delegations() + .checked_add(1) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + } + + self.stake_weight = PodU128::from( + self.stake_weight() + .checked_add(stake_weight) + .ok_or(TipRouterError::ArithmeticOverflow)?, + ); + + if self.finalized() { + self.slot_finalized = PodU64::from(current_slot); + } + + Ok(()) + } + + pub fn calculate_total_stake_weight( + vault_operator_delegation: &VaultOperatorDelegation, + weight_table: &WeightTable, + st_mint: &Pubkey, + ) -> Result { + let total_security = vault_operator_delegation + .delegation_state + .total_security()?; + + let precise_total_security = PreciseNumber::new(total_security as u128) + .ok_or(TipRouterError::NewPreciseNumberError)?; + + let precise_weight = weight_table.get_precise_weight(st_mint)?; + + let precise_total_stake_weight = precise_total_security + .checked_mul(&precise_weight) + .ok_or(TipRouterError::ArithmeticOverflow)?; + + let total_stake_weight = precise_total_stake_weight + .to_imprecise() + .ok_or(TipRouterError::CastToImpreciseNumberError)?; + + Ok(total_stake_weight) + } +} diff --git a/core/src/error.rs b/core/src/error.rs index 49ab208..4c5f7da 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -43,13 +43,37 @@ pub enum TipRouterError { #[error("Vault index already in use by a different mint")] VaultIndexAlreadyInUse, #[error("Fee cap exceeded")] - FeeCapExceeded = 0x2300, + FeeCapExceeded, #[error("Incorrect NCN Admin")] - IncorrectNcnAdmin = 0x2400, + IncorrectNcnAdmin, #[error("Incorrect NCN")] - IncorrectNcn = 0x2401, + IncorrectNcn, #[error("Incorrect fee admin")] - IncorrectFeeAdmin = 0x2402, + IncorrectFeeAdmin, + #[error("Weight table not finalized")] + WeightTableNotFinalized, + #[error("Weight not found")] + WeightNotFound, + #[error("No operators in ncn")] + NoOperators, + #[error("Vault operator delegation is already finalized - should not happen")] + VaultOperatorDelegationFinalized, + #[error("Operator is already finalized - should not happen")] + OperatorFinalized, + #[error("Too many vault operator delegations")] + TooManyVaultOperatorDelegations, + #[error("Duplicate vault operator delegation")] + DuplicateVaultOperatorDelegation, + #[error("Duplicate Vote Cast")] + DuplicateVoteCast, + #[error("Operator votes full")] + OperatorVotesFull, + #[error("Merkle root tally full")] + BallotTallyFull, + #[error("Consensus already reached")] + ConsensusAlreadyReached, + #[error("Consensus not reached")] + ConsensusNotReached, } impl DecodeError for TipRouterError { diff --git a/core/src/fees.rs b/core/src/fees.rs index ab3285e..2ec6982 100644 --- a/core/src/fees.rs +++ b/core/src/fees.rs @@ -4,7 +4,7 @@ use shank::ShankType; use solana_program::{msg, pubkey::Pubkey}; use spl_math::precise_number::PreciseNumber; -use crate::{error::TipRouterError, MAX_FEE_BPS}; +use crate::{constants::MAX_FEE_BPS, error::TipRouterError}; /// Fee account. Allows for fee updates to take place in a future epoch without requiring an update. /// This is important so all operators calculate the same Merkle root regardless of when fee changes take place. diff --git a/core/src/instruction.rs b/core/src/instruction.rs index 3241eb6..a1f4b87 100644 --- a/core/src/instruction.rs +++ b/core/src/instruction.rs @@ -10,7 +10,7 @@ pub enum ConfigAdminRole { #[rustfmt::skip] #[derive(Debug, BorshSerialize, BorshDeserialize, ShankInstruction)] -pub enum WeightTableInstruction { +pub enum TipRouterInstruction { /// Initialize the global configuration for this NCN @@ -20,7 +20,7 @@ pub enum WeightTableInstruction { #[account(3, writable, signer, name = "ncn_admin")] #[account(4, name = "fee_wallet")] #[account(5, name = "tie_breaker_admin")] - #[account(6, name = "restaking_program_id")] + #[account(6, name = "restaking_program")] #[account(7, name = "system_program")] InitializeNCNConfig { dao_fee_bps: u64, @@ -33,7 +33,7 @@ pub enum WeightTableInstruction { #[account(1, writable, name = "config")] #[account(2, name = "ncn")] #[account(3, signer, name = "ncn_admin")] - #[account(4, name = "restaking_program_id")] + #[account(4, name = "restaking_program")] SetConfigFees { new_dao_fee_bps: Option, new_ncn_fee_bps: Option, @@ -46,7 +46,7 @@ pub enum WeightTableInstruction { #[account(1, name = "ncn")] #[account(2, signer, name = "ncn_admin")] #[account(3, name = "new_admin")] - #[account(4, name = "restaking_program_id")] + #[account(4, name = "restaking_program")] SetNewAdmin { role: ConfigAdminRole, }, @@ -57,7 +57,7 @@ pub enum WeightTableInstruction { #[account(2, name = "ncn")] #[account(3, writable, name = "weight_table")] #[account(4, writable, signer, name = "payer")] - #[account(5, name = "restaking_program_id")] + #[account(5, name = "restaking_program")] #[account(6, name = "system_program")] InitializeWeightTable{ first_slot_of_ncn_epoch: Option, @@ -68,12 +68,58 @@ pub enum WeightTableInstruction { #[account(1, writable, name = "weight_table")] #[account(2, signer, name = "weight_table_admin")] #[account(3, name = "mint")] - #[account(4, name = "restaking_program_id")] + #[account(4, name = "restaking_program")] AdminUpdateWeightTable{ ncn_epoch: u64, weight: u128, }, + /// Initializes the Epoch Snapshot + #[account(0, name = "ncn_config")] + #[account(1, name = "restaking_config")] + #[account(2, name = "ncn")] + #[account(3, name = "tracked_mints")] + #[account(4, name = "weight_table")] + #[account(5, writable, name = "epoch_snapshot")] + #[account(6, writable, signer, name = "payer")] + #[account(7, name = "restaking_program")] + #[account(8, name = "system_program")] + InitializeEpochSnapshot{ + first_slot_of_ncn_epoch: Option, + }, + + /// Initializes the Operator Snapshot + #[account(0, name = "ncn_config")] + #[account(1, name = "restaking_config")] + #[account(2, name = "ncn")] + #[account(3, name = "operator")] + #[account(4, name = "ncn_operator_state")] + #[account(5, writable, name = "epoch_snapshot")] + #[account(6, writable, name = "operator_snapshot")] + #[account(7, writable, signer, name = "payer")] + #[account(8, name = "restaking_program")] + #[account(9, name = "system_program")] + InitializeOperatorSnapshot{ + first_slot_of_ncn_epoch: Option, + }, + + /// Initializes the Vault Operator Delegation Snapshot + #[account(0, name = "ncn_config")] + #[account(1, name = "restaking_config")] + #[account(2, name = "ncn")] + #[account(3, name = "operator")] + #[account(4, name = "vault")] + #[account(5, name = "vault_ncn_ticket")] + #[account(6, name = "ncn_vault_ticket")] + #[account(7, name = "vault_operator_delegation")] + #[account(8, name = "weight_table")] + #[account(9, writable, name = "epoch_snapshot")] + #[account(10, writable, name = "operator_snapshot")] + #[account(11, name = "vault_program")] + #[account(12, name = "restaking_program")] + SnapshotVaultOperatorDelegation{ + first_slot_of_ncn_epoch: Option, + }, /// Registers a mint with the NCN config #[account(0, name = "restaking_config")] #[account(1, writable, name = "tracked_mints")] diff --git a/core/src/lib.rs b/core/src/lib.rs index 817bbad..ac78271 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,10 +1,12 @@ +pub mod ballot_box; +pub mod constants; pub mod discriminators; +pub mod epoch_snapshot; pub mod error; pub mod fees; pub mod instruction; +pub mod loaders; pub mod ncn_config; pub mod tracked_mints; pub mod weight_entry; pub mod weight_table; - -pub const MAX_FEE_BPS: u64 = 10_000; diff --git a/core/src/loaders.rs b/core/src/loaders.rs new file mode 100644 index 0000000..8d4f9a3 --- /dev/null +++ b/core/src/loaders.rs @@ -0,0 +1,33 @@ +use jito_bytemuck::AccountDeserialize; +use jito_restaking_core::config::Config; +use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError}; + +use crate::error::TipRouterError; + +pub fn load_ncn_epoch( + restaking_config: &AccountInfo, + current_slot: u64, + first_slot_of_ncn_epoch: Option, +) -> Result<(u64, u64), ProgramError> { + let ncn_epoch_length = { + let config_data = restaking_config.data.borrow(); + let config = Config::try_from_slice_unchecked(&config_data)?; + config.epoch_length() + }; + + let current_ncn_epoch = current_slot + .checked_div(ncn_epoch_length) + .ok_or(TipRouterError::DenominatorIsZero)?; + + let ncn_epoch_slot = first_slot_of_ncn_epoch.unwrap_or(current_slot); + let ncn_epoch = ncn_epoch_slot + .checked_div(ncn_epoch_length) + .ok_or(TipRouterError::DenominatorIsZero)?; + + if ncn_epoch > current_ncn_epoch { + msg!("Epoch snapshots can only be initialized for current or past epochs"); + return Err(TipRouterError::CannotCreateFutureWeightTables.into()); + } + + Ok((ncn_epoch, ncn_epoch_length)) +} diff --git a/core/src/ncn_config.rs b/core/src/ncn_config.rs index 63bd08d..2ca4808 100644 --- a/core/src/ncn_config.rs +++ b/core/src/ncn_config.rs @@ -24,7 +24,7 @@ pub struct NcnConfig { } impl Discriminator for NcnConfig { - const DISCRIMINATOR: u8 = Discriminators::Config as u8; + const DISCRIMINATOR: u8 = Discriminators::NCNConfig as u8; } impl NcnConfig { diff --git a/core/src/tracked_mints.rs b/core/src/tracked_mints.rs index 16d9402..fc6bcbc 100644 --- a/core/src/tracked_mints.rs +++ b/core/src/tracked_mints.rs @@ -103,11 +103,11 @@ impl TrackedMints { Ok(()) } - pub fn mint_count(&self) -> usize { + pub fn mint_count(&self) -> u64 { self.st_mint_list .iter() .filter(|m| m.st_mint != Pubkey::default()) - .count() + .count() as u64 } pub fn get_unique_mints(&self) -> Vec { @@ -185,7 +185,7 @@ mod tests { assert!(tracked_mints.add_mint(mint2, 1).is_err()); // Adding to a full list should fail - for i in tracked_mints.mint_count()..tracked_mints.st_mint_list.len() { + for i in (tracked_mints.mint_count() as usize)..tracked_mints.st_mint_list.len() { tracked_mints .add_mint(Pubkey::new_unique(), i as u64) .unwrap(); diff --git a/core/src/weight_table.rs b/core/src/weight_table.rs index 8ec499a..13d5d9e 100644 --- a/core/src/weight_table.rs +++ b/core/src/weight_table.rs @@ -4,6 +4,7 @@ use bytemuck::{Pod, Zeroable}; use jito_bytemuck::{types::PodU64, AccountDeserialize, Discriminator}; use shank::{ShankAccount, ShankType}; use solana_program::{account_info::AccountInfo, msg, program_error::ProgramError, pubkey::Pubkey}; +use spl_math::precise_number::PreciseNumber; use crate::{discriminators::Discriminators, error::TipRouterError, weight_entry::WeightEntry}; @@ -134,6 +135,11 @@ impl WeightTable { }) } + pub fn get_precise_weight(&self, mint: &Pubkey) -> Result { + let weight = self.get_weight(mint)?; + PreciseNumber::new(weight).ok_or(TipRouterError::NewPreciseNumberError) + } + pub fn get_mints(&self) -> Vec { self.table .iter() @@ -142,19 +148,12 @@ impl WeightTable { .collect() } - pub fn find_weight(&self, mint: &Pubkey) -> Option { - self.table - .iter() - .find(|entry| entry.mint() == *mint) - .map(|entry| entry.weight()) - } - pub fn mint_count(&self) -> usize { self.table.iter().filter(|entry| !entry.is_empty()).count() } pub fn weight_count(&self) -> usize { - self.table.iter().filter(|entry| !entry.is_set()).count() + self.table.iter().filter(|entry| entry.is_set()).count() } pub const fn ncn(&self) -> Pubkey { diff --git a/idl/jito_tip_router.json b/idl/jito_tip_router.json index 869203b..a21afea 100644 --- a/idl/jito_tip_router.json +++ b/idl/jito_tip_router.json @@ -36,7 +36,7 @@ "isSigner": false }, { - "name": "restakingProgramId", + "name": "restakingProgram", "isMut": false, "isSigner": false }, @@ -89,7 +89,7 @@ "isSigner": true }, { - "name": "restakingProgramId", + "name": "restakingProgram", "isMut": false, "isSigner": false } @@ -149,7 +149,7 @@ "isSigner": false }, { - "name": "restakingProgramId", + "name": "restakingProgram", "isMut": false, "isSigner": false } @@ -196,7 +196,7 @@ "isSigner": true }, { - "name": "restakingProgramId", + "name": "restakingProgram", "isMut": false, "isSigner": false }, @@ -243,7 +243,7 @@ "isSigner": false }, { - "name": "restakingProgramId", + "name": "restakingProgram", "isMut": false, "isSigner": false } @@ -263,6 +263,217 @@ "value": 4 } }, + { + "name": "InitializeEpochSnapshot", + "accounts": [ + { + "name": "ncnConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "restakingConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "ncn", + "isMut": false, + "isSigner": false + }, + { + "name": "trackedMints", + "isMut": false, + "isSigner": false + }, + { + "name": "weightTable", + "isMut": false, + "isSigner": false + }, + { + "name": "epochSnapshot", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "restakingProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "firstSlotOfNcnEpoch", + "type": { + "option": "u64" + } + } + ], + "discriminant": { + "type": "u8", + "value": 5 + } + }, + { + "name": "InitializeOperatorSnapshot", + "accounts": [ + { + "name": "ncnConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "restakingConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "ncn", + "isMut": false, + "isSigner": false + }, + { + "name": "operator", + "isMut": false, + "isSigner": false + }, + { + "name": "ncnOperatorState", + "isMut": false, + "isSigner": false + }, + { + "name": "epochSnapshot", + "isMut": true, + "isSigner": false + }, + { + "name": "operatorSnapshot", + "isMut": true, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "restakingProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "firstSlotOfNcnEpoch", + "type": { + "option": "u64" + } + } + ], + "discriminant": { + "type": "u8", + "value": 6 + } + }, + { + "name": "SnapshotVaultOperatorDelegation", + "accounts": [ + { + "name": "ncnConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "restakingConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "ncn", + "isMut": false, + "isSigner": false + }, + { + "name": "operator", + "isMut": false, + "isSigner": false + }, + { + "name": "vault", + "isMut": false, + "isSigner": false + }, + { + "name": "vaultNcnTicket", + "isMut": false, + "isSigner": false + }, + { + "name": "ncnVaultTicket", + "isMut": false, + "isSigner": false + }, + { + "name": "vaultOperatorDelegation", + "isMut": false, + "isSigner": false + }, + { + "name": "weightTable", + "isMut": false, + "isSigner": false + }, + { + "name": "epochSnapshot", + "isMut": true, + "isSigner": false + }, + { + "name": "operatorSnapshot", + "isMut": true, + "isSigner": false + }, + { + "name": "vaultProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "restakingProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "firstSlotOfNcnEpoch", + "type": { + "option": "u64" + } + } + ], + "discriminant": { + "type": "u8", + "value": 7 + } + }, { "name": "RegisterMint", "accounts": [ @@ -315,46 +526,314 @@ "args": [], "discriminant": { "type": "u8", - "value": 5 + "value": 8 + } + }, + { + "name": "InitializeTrackedMints", + "accounts": [ + { + "name": "ncnConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "trackedMints", + "isMut": true, + "isSigner": false + }, + { + "name": "ncn", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 9 + } + } + ], + "accounts": [ + { + "name": "BallotBox", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ncn", + "type": "publicKey" + }, + { + "name": "ncnEpoch", + "type": { + "defined": "PodU64" + } + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "slotCreated", + "type": { + "defined": "PodU64" + } + }, + { + "name": "slotConsensusReached", + "type": { + "defined": "PodU64" + } + }, + { + "name": "reserved", + "type": { + "array": [ + "u8", + 128 + ] + } + }, + { + "name": "operatorsVoted", + "type": { + "defined": "PodU64" + } + }, + { + "name": "uniqueBallots", + "type": { + "defined": "PodU64" + } + }, + { + "name": "winningBallot", + "type": { + "defined": "Ballot" + } + }, + { + "name": "operatorVotes", + "type": { + "array": [ + { + "defined": "OperatorVote" + }, + 32 + ] + } + }, + { + "name": "ballotTallies", + "type": { + "array": [ + { + "defined": "BallotTally" + }, + 32 + ] + } + } + ] + } + }, + { + "name": "EpochSnapshot", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ncn", + "type": "publicKey" + }, + { + "name": "ncnEpoch", + "type": { + "defined": "PodU64" + } + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "slotCreated", + "type": { + "defined": "PodU64" + } + }, + { + "name": "slotFinalized", + "type": { + "defined": "PodU64" + } + }, + { + "name": "ncnFees", + "type": { + "defined": "Fees" + } + }, + { + "name": "operatorCount", + "type": { + "defined": "PodU64" + } + }, + { + "name": "vaultCount", + "type": { + "defined": "PodU64" + } + }, + { + "name": "operatorsRegistered", + "type": { + "defined": "PodU64" + } + }, + { + "name": "validOperatorVaultDelegations", + "type": { + "defined": "PodU64" + } + }, + { + "name": "stakeWeight", + "type": { + "defined": "PodU128" + } + }, + { + "name": "reserved", + "type": { + "array": [ + "u8", + 128 + ] + } + } + ] + } + }, + { + "name": "OperatorSnapshot", + "type": { + "kind": "struct", + "fields": [ + { + "name": "operator", + "type": "publicKey" + }, + { + "name": "ncn", + "type": "publicKey" + }, + { + "name": "ncnEpoch", + "type": { + "defined": "PodU64" + } + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "slotCreated", + "type": { + "defined": "PodU64" + } + }, + { + "name": "slotFinalized", + "type": { + "defined": "PodU64" + } + }, + { + "name": "isActive", + "type": { + "defined": "PodBool" + } + }, + { + "name": "ncnOperatorIndex", + "type": { + "defined": "PodU64" + } + }, + { + "name": "operatorIndex", + "type": { + "defined": "PodU64" + } + }, + { + "name": "operatorFeeBps", + "type": { + "defined": "PodU16" + } + }, + { + "name": "vaultOperatorDelegationCount", + "type": { + "defined": "PodU64" + } + }, + { + "name": "vaultOperatorDelegationsRegistered", + "type": { + "defined": "PodU64" + } + }, + { + "name": "validOperatorVaultDelegations", + "type": { + "defined": "PodU64" + } + }, + { + "name": "stakeWeight", + "type": { + "defined": "PodU128" + } + }, + { + "name": "reserved", + "type": { + "array": [ + "u8", + 256 + ] + } + }, + { + "name": "vaultOperatorStakeWeight", + "type": { + "array": [ + { + "defined": "VaultOperatorStakeWeight" + }, + 32 + ] + } + } + ] } }, - { - "name": "InitializeTrackedMints", - "accounts": [ - { - "name": "ncnConfig", - "isMut": false, - "isSigner": false - }, - { - "name": "trackedMints", - "isMut": true, - "isSigner": false - }, - { - "name": "ncn", - "isMut": false, - "isSigner": false - }, - { - "name": "payer", - "isMut": true, - "isSigner": true - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [], - "discriminant": { - "type": "u8", - "value": 6 - } - } - ], - "accounts": [ { "name": "NcnConfig", "type": { @@ -480,6 +959,139 @@ } ], "types": [ + { + "name": "Ballot", + "type": { + "kind": "struct", + "fields": [ + { + "name": "merkleRoot", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "reserved", + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "BallotTally", + "type": { + "kind": "struct", + "fields": [ + { + "name": "ballot", + "type": { + "defined": "Ballot" + } + }, + { + "name": "stakeWeight", + "type": { + "defined": "PodU128" + } + }, + { + "name": "tally", + "type": { + "defined": "PodU64" + } + }, + { + "name": "reserved", + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "OperatorVote", + "type": { + "kind": "struct", + "fields": [ + { + "name": "operator", + "type": "publicKey" + }, + { + "name": "slotVoted", + "type": { + "defined": "PodU64" + } + }, + { + "name": "stakeWeight", + "type": { + "defined": "PodU128" + } + }, + { + "name": "ballotIndex", + "type": { + "defined": "PodU16" + } + }, + { + "name": "reserved", + "type": { + "array": [ + "u8", + 64 + ] + } + } + ] + } + }, + { + "name": "VaultOperatorStakeWeight", + "type": { + "kind": "struct", + "fields": [ + { + "name": "vault", + "type": "publicKey" + }, + { + "name": "stakeWeight", + "type": { + "defined": "PodU128" + } + }, + { + "name": "vaultIndex", + "type": { + "defined": "PodU64" + } + }, + { + "name": "reserved", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, { "name": "Fees", "type": { @@ -714,24 +1326,84 @@ "msg": "Vault index already in use by a different mint" }, { - "code": 8960, + "code": 8718, "name": "FeeCapExceeded", "msg": "Fee cap exceeded" }, { - "code": 9216, + "code": 8719, "name": "IncorrectNcnAdmin", "msg": "Incorrect NCN Admin" }, { - "code": 9217, + "code": 8720, "name": "IncorrectNcn", "msg": "Incorrect NCN" }, { - "code": 9218, + "code": 8721, "name": "IncorrectFeeAdmin", "msg": "Incorrect fee admin" + }, + { + "code": 8722, + "name": "WeightTableNotFinalized", + "msg": "Weight table not finalized" + }, + { + "code": 8723, + "name": "WeightNotFound", + "msg": "Weight not found" + }, + { + "code": 8724, + "name": "NoOperators", + "msg": "No operators in ncn" + }, + { + "code": 8725, + "name": "VaultOperatorDelegationFinalized", + "msg": "Vault operator delegation is already finalized - should not happen" + }, + { + "code": 8726, + "name": "OperatorFinalized", + "msg": "Operator is already finalized - should not happen" + }, + { + "code": 8727, + "name": "TooManyVaultOperatorDelegations", + "msg": "Too many vault operator delegations" + }, + { + "code": 8728, + "name": "DuplicateVaultOperatorDelegation", + "msg": "Duplicate vault operator delegation" + }, + { + "code": 8729, + "name": "DuplicateVoteCast", + "msg": "Duplicate Vote Cast" + }, + { + "code": 8730, + "name": "OperatorVotesFull", + "msg": "Operator votes full" + }, + { + "code": 8731, + "name": "BallotTallyFull", + "msg": "Merkle root tally full" + }, + { + "code": 8732, + "name": "ConsensusAlreadyReached", + "msg": "Consensus already reached" + }, + { + "code": 8733, + "name": "ConsensusNotReached", + "msg": "Consensus not reached" } ], "metadata": { diff --git a/integration_tests/tests/fixtures/restaking_client.rs b/integration_tests/tests/fixtures/restaking_client.rs index ab0d6b1..d914a42 100644 --- a/integration_tests/tests/fixtures/restaking_client.rs +++ b/integration_tests/tests/fixtures/restaking_client.rs @@ -21,7 +21,7 @@ use solana_program::{ instruction::InstructionError, native_token::sol_to_lamports, pubkey::Pubkey, system_instruction::transfer, }; -use solana_program_test::BanksClient; +use solana_program_test::{BanksClient, ProgramTestBanksClientExt}; use solana_sdk::{ commitment_config::CommitmentLevel, signature::{Keypair, Signer}, @@ -36,12 +36,30 @@ pub struct NcnRoot { pub ncn_admin: Keypair, } +impl Clone for NcnRoot { + fn clone(&self) -> Self { + Self { + ncn_pubkey: self.ncn_pubkey, + ncn_admin: self.ncn_admin.insecure_clone(), + } + } +} + #[derive(Debug)] pub struct OperatorRoot { pub operator_pubkey: Pubkey, pub operator_admin: Keypair, } +impl Clone for OperatorRoot { + fn clone(&self) -> Self { + Self { + operator_pubkey: self.operator_pubkey, + operator_admin: self.operator_admin.insecure_clone(), + } + } +} + pub struct RestakingProgramClient { banks_client: BanksClient, payer: Keypair, @@ -55,6 +73,7 @@ impl RestakingProgramClient { } } + #[allow(dead_code)] pub async fn get_ncn(&mut self, ncn: &Pubkey) -> TestResult { let account = self .banks_client @@ -70,6 +89,14 @@ impl RestakingProgramClient { Ok(*Config::try_from_slice_unchecked(account.data.as_slice())?) } + pub async fn get_ncn_epoch(&mut self, slot: u64) -> TestResult { + let restaking_config_address = + Config::find_program_address(&jito_restaking_program::id()).0; + let config = self.get_config(&restaking_config_address).await.unwrap(); + Ok(config.get_epoch_from_slot(slot).unwrap()) + } + + #[allow(dead_code)] pub async fn get_ncn_vault_ticket( &mut self, ncn: &Pubkey, @@ -83,6 +110,7 @@ impl RestakingProgramClient { )?) } + #[allow(dead_code)] pub async fn get_ncn_operator_state( &mut self, ncn: &Pubkey, @@ -96,6 +124,7 @@ impl RestakingProgramClient { )?) } + #[allow(dead_code)] pub async fn get_operator(&mut self, account: &Pubkey) -> TestResult { let account = self.banks_client.get_account(*account).await?.unwrap(); Ok(*Operator::try_from_slice_unchecked( @@ -103,6 +132,7 @@ impl RestakingProgramClient { )?) } + #[allow(dead_code)] pub async fn get_operator_vault_ticket( &mut self, operator: &Pubkey, @@ -258,8 +288,14 @@ impl RestakingProgramClient { .await } - pub async fn do_initialize_ncn(&mut self) -> TestResult { - let ncn_admin = Keypair::new(); + pub async fn do_initialize_ncn(&mut self, ncn_admin: Option) -> TestResult { + let ncn_admin = { + if let Some(ncn_admin) = ncn_admin { + ncn_admin + } else { + self.payer.insecure_clone() + } + }; let ncn_base = Keypair::new(); self.airdrop(&ncn_admin.pubkey(), 1.0).await?; @@ -419,6 +455,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn do_ncn_cooldown_operator( &mut self, ncn_root: &NcnRoot, @@ -537,6 +574,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn do_operator_cooldown_ncn( &mut self, operator_root: &OperatorRoot, @@ -606,6 +644,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn do_initialize_ncn_vault_slasher_ticket( &mut self, ncn_root: &NcnRoot, @@ -641,6 +680,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn do_warmup_ncn_vault_slasher_ticket( &mut self, ncn_root: &NcnRoot, @@ -817,6 +857,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn ncn_set_admin( &mut self, ncn: &Pubkey, @@ -839,6 +880,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn operator_set_admin( &mut self, operator: &Pubkey, @@ -861,6 +903,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn operator_set_secondary_admin( &mut self, operator: &Pubkey, @@ -939,6 +982,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn operator_set_fee( &mut self, config: &Pubkey, @@ -963,6 +1007,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn ncn_delegate_token_account( &mut self, ncn_pubkey: &Pubkey, @@ -990,6 +1035,7 @@ impl RestakingProgramClient { .await } + #[allow(dead_code)] pub async fn operator_delegate_token_account( &mut self, operator_pubkey: &Pubkey, @@ -1029,13 +1075,18 @@ impl RestakingProgramClient { pub async fn airdrop(&mut self, to: &Pubkey, sol: f64) -> TestResult<()> { let blockhash = self.banks_client.get_latest_blockhash().await?; + let new_blockhash = self + .banks_client + .get_new_latest_blockhash(&blockhash) + .await + .unwrap(); self.banks_client .process_transaction_with_preflight_and_commitment( Transaction::new_signed_with_payer( &[transfer(&self.payer.pubkey(), to, sol_to_lamports(sol))], Some(&self.payer.pubkey()), &[&self.payer], - blockhash, + new_blockhash, ), CommitmentLevel::Processed, ) @@ -1043,6 +1094,7 @@ impl RestakingProgramClient { Ok(()) } + #[allow(dead_code)] pub async fn set_config_admin( &mut self, config: &Pubkey, @@ -1067,6 +1119,7 @@ impl RestakingProgramClient { #[track_caller] #[inline(always)] +#[allow(dead_code)] pub fn assert_restaking_error( test_error: Result, restaking_error: RestakingError, diff --git a/integration_tests/tests/fixtures/test_builder.rs b/integration_tests/tests/fixtures/test_builder.rs index 4df80d4..e1d181e 100644 --- a/integration_tests/tests/fixtures/test_builder.rs +++ b/integration_tests/tests/fixtures/test_builder.rs @@ -1,15 +1,39 @@ use std::fmt::{Debug, Formatter}; -use solana_program::clock::Clock; +use jito_restaking_core::{config::Config, ncn_vault_ticket::NcnVaultTicket}; +use jito_vault_core::vault_ncn_ticket::VaultNcnTicket; +use solana_program::{ + clock::Clock, native_token::sol_to_lamports, pubkey::Pubkey, system_instruction::transfer, +}; use solana_program_test::{processor, BanksClientError, ProgramTest, ProgramTestContext}; +use solana_sdk::{commitment_config::CommitmentLevel, signature::Signer, transaction::Transaction}; -use super::{ - restaking_client::{NcnRoot, RestakingProgramClient}, - tip_router_client::TipRouterClient, - vault_client::VaultProgramClient, +use super::{restaking_client::NcnRoot, tip_router_client::TipRouterClient}; +use crate::fixtures::{ + restaking_client::{OperatorRoot, RestakingProgramClient}, + vault_client::{VaultProgramClient, VaultRoot}, TestResult, }; +pub struct TestNcn { + pub ncn_root: NcnRoot, + pub operators: Vec, + pub vaults: Vec, +} + +//TODO implement for more fine-grained relationship control +#[allow(dead_code)] + +pub struct TestNcnNode { + pub ncn_root: NcnRoot, + pub operator_root: OperatorRoot, + pub vault_root: VaultRoot, + + pub ncn_vault_connected: bool, + pub operator_vault_connected: bool, + pub delegation: u64, +} + pub struct TestBuilder { context: ProgramTestContext, } @@ -80,14 +104,353 @@ impl TestBuilder { ) } + pub fn vault_program_client(&self) -> VaultProgramClient { + VaultProgramClient::new( + self.context.banks_client.clone(), + self.context.payer.insecure_clone(), + ) + } + + #[allow(dead_code)] + pub async fn transfer(&mut self, to: &Pubkey, sol: f64) -> Result<(), BanksClientError> { + let blockhash = self.context.banks_client.get_latest_blockhash().await?; + self.context + .banks_client + .process_transaction_with_preflight_and_commitment( + Transaction::new_signed_with_payer( + &[transfer( + &self.context.payer.pubkey(), + to, + sol_to_lamports(sol), + )], + Some(&self.context.payer.pubkey()), + &[&self.context.payer], + blockhash, + ), + CommitmentLevel::Processed, + ) + .await + } + pub async fn setup_ncn(&mut self) -> TestResult { let mut restaking_program_client = self.restaking_program_client(); + let mut vault_program_client = self.vault_program_client(); + vault_program_client.do_initialize_config().await?; restaking_program_client.do_initialize_config().await?; - let ncn_root = restaking_program_client.do_initialize_ncn().await?; + let ncn_root = restaking_program_client + .do_initialize_ncn(Some(self.context.payer.insecure_clone())) + .await?; Ok(ncn_root) } - // Extend this to setup operators, vaults and relationships as neede + // 1. Setup NCN + pub async fn create_test_ncn(&mut self) -> TestResult { + let mut restaking_program_client = self.restaking_program_client(); + let mut vault_program_client = self.vault_program_client(); + let mut tip_router_client = self.tip_router_client(); + + vault_program_client.do_initialize_config().await?; + restaking_program_client.do_initialize_config().await?; + let ncn_root = restaking_program_client + .do_initialize_ncn(Some(self.context.payer.insecure_clone())) + .await?; + + tip_router_client.setup_tip_router(&ncn_root).await?; + + Ok(TestNcn { + ncn_root: ncn_root.clone(), + operators: vec![], + vaults: vec![], + }) + } + + // 2. Setup Operators + pub async fn add_operators_to_test_ncn( + &mut self, + test_ncn: &mut TestNcn, + operator_count: usize, + ) -> TestResult<()> { + let mut restaking_program_client = self.restaking_program_client(); + + for _ in 0..operator_count { + let operator_root = restaking_program_client.do_initialize_operator().await?; + + // ncn <> operator + restaking_program_client + .do_initialize_ncn_operator_state( + &test_ncn.ncn_root, + &operator_root.operator_pubkey, + ) + .await?; + self.warp_slot_incremental(1).await.unwrap(); + restaking_program_client + .do_ncn_warmup_operator(&test_ncn.ncn_root, &operator_root.operator_pubkey) + .await?; + restaking_program_client + .do_operator_warmup_ncn(&operator_root, &test_ncn.ncn_root.ncn_pubkey) + .await?; + + test_ncn.operators.push(operator_root); + } + + Ok(()) + } + + // 3. Setup Vaults + pub async fn add_vaults_to_test_ncn( + &mut self, + test_ncn: &mut TestNcn, + vault_count: usize, + ) -> TestResult<()> { + let mut vault_program_client = self.vault_program_client(); + let mut restaking_program_client = self.restaking_program_client(); + + const DEPOSIT_FEE_BPS: u16 = 0; + const WITHDRAWAL_FEE_BPS: u16 = 0; + const REWARD_FEE_BPS: u16 = 0; + const MINT_AMOUNT: u64 = 1_000_000; + + for _ in 0..vault_count { + let vault_root = vault_program_client + .do_initialize_vault( + DEPOSIT_FEE_BPS, + WITHDRAWAL_FEE_BPS, + REWARD_FEE_BPS, + 9, + &self.context.payer.pubkey(), + ) + .await?; + + // vault <> ncn + restaking_program_client + .do_initialize_ncn_vault_ticket(&test_ncn.ncn_root, &vault_root.vault_pubkey) + .await?; + self.warp_slot_incremental(1).await.unwrap(); + restaking_program_client + .do_warmup_ncn_vault_ticket(&test_ncn.ncn_root, &vault_root.vault_pubkey) + .await?; + vault_program_client + .do_initialize_vault_ncn_ticket(&vault_root, &test_ncn.ncn_root.ncn_pubkey) + .await?; + self.warp_slot_incremental(1).await.unwrap(); + vault_program_client + .do_warmup_vault_ncn_ticket(&vault_root, &test_ncn.ncn_root.ncn_pubkey) + .await?; + + for operator_root in test_ncn.operators.iter() { + // vault <> operator + restaking_program_client + .do_initialize_operator_vault_ticket(&operator_root, &vault_root.vault_pubkey) + .await?; + self.warp_slot_incremental(1).await.unwrap(); + restaking_program_client + .do_warmup_operator_vault_ticket(&operator_root, &vault_root.vault_pubkey) + .await?; + vault_program_client + .do_initialize_vault_operator_delegation( + &vault_root, + &operator_root.operator_pubkey, + ) + .await?; + } + + let depositor_keypair = self.context.payer.insecure_clone(); + let depositor = depositor_keypair.pubkey(); + vault_program_client + .configure_depositor(&vault_root, &depositor, MINT_AMOUNT) + .await?; + vault_program_client + .do_mint_to(&vault_root, &depositor_keypair, MINT_AMOUNT, MINT_AMOUNT) + .await + .unwrap(); + + test_ncn.vaults.push(vault_root); + } + + Ok(()) + } + + // 4. Setup Delegations + pub async fn add_delegation_in_test_ncn( + &mut self, + test_ncn: &TestNcn, + delegation_amount: usize, + ) -> TestResult<()> { + let mut vault_program_client = self.vault_program_client(); + + for vault_root in test_ncn.vaults.iter() { + for operator_root in test_ncn.operators.iter() { + vault_program_client + .do_add_delegation( + &vault_root, + &operator_root.operator_pubkey, + delegation_amount as u64, + ) + .await + .unwrap(); + } + } + + Ok(()) + } + + // 5. Setup Tracked Mints + pub async fn add_tracked_mints_to_test_ncn(&mut self, test_ncn: &TestNcn) -> TestResult<()> { + let mut tip_router_client = self.tip_router_client(); + let mut restaking_client = self.restaking_program_client(); + let mut vault_client = self.vault_program_client(); + + let restaking_config_address = + Config::find_program_address(&jito_restaking_program::id()).0; + let restaking_config = restaking_client + .get_config(&restaking_config_address) + .await?; + + let epoch_length = restaking_config.epoch_length(); + + self.warp_slot_incremental(epoch_length * 2).await.unwrap(); + + for vault in test_ncn.vaults.iter() { + let ncn = test_ncn.ncn_root.ncn_pubkey; + let vault = vault.vault_pubkey; + + let operators = test_ncn + .operators + .iter() + .map(|operator| operator.operator_pubkey) + .collect::>(); + + vault_client + .do_full_vault_update(&vault, &operators) + .await?; + + let vault_ncn_ticket = + VaultNcnTicket::find_program_address(&jito_vault_program::id(), &vault, &ncn).0; + + let ncn_vault_ticket = + NcnVaultTicket::find_program_address(&jito_restaking_program::id(), &ncn, &vault).0; + + tip_router_client + .do_register_mint(ncn, vault, vault_ncn_ticket, ncn_vault_ticket) + .await?; + } + + Ok(()) + } + + // Intermission: setup just NCN + pub async fn create_initial_test_ncn( + &mut self, + operator_count: usize, + vault_count: usize, + ) -> TestResult { + let mut test_ncn = self.create_test_ncn().await?; + self.add_operators_to_test_ncn(&mut test_ncn, operator_count) + .await?; + self.add_vaults_to_test_ncn(&mut test_ncn, vault_count) + .await?; + self.add_delegation_in_test_ncn(&test_ncn, 100).await?; + self.add_tracked_mints_to_test_ncn(&test_ncn).await?; + + Ok(test_ncn) + } + + // 6. Set weights + pub async fn add_weights_for_test_ncn(&mut self, test_ncn: &TestNcn) -> TestResult<()> { + let mut tip_router_client = self.tip_router_client(); + let mut vault_client = self.vault_program_client(); + + const WEIGHT: u128 = 100; + + // Not sure if this is needed + self.warp_slot_incremental(1000).await?; + + let slot = self.clock().await.slot; + tip_router_client + .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .await?; + + for vault_root in test_ncn.vaults.iter() { + let vault = vault_client.get_vault(&vault_root.vault_pubkey).await?; + + let mint = vault.supported_mint; + + tip_router_client + .do_admin_update_weight_table(test_ncn.ncn_root.ncn_pubkey, slot, mint, WEIGHT) + .await?; + } + + Ok(()) + } + + // 7. Create Epoch Snapshot + pub async fn add_epoch_snapshot_to_test_ncn(&mut self, test_ncn: &TestNcn) -> TestResult<()> { + let mut tip_router_client = self.tip_router_client(); + + let slot = self.clock().await.slot; + + tip_router_client + .do_initialize_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, slot) + .await?; + + Ok(()) + } + + // 8. Create all operator snapshots + pub async fn add_operator_snapshots_to_test_ncn( + &mut self, + test_ncn: &TestNcn, + ) -> TestResult<()> { + let mut tip_router_client = self.tip_router_client(); + + let slot = self.clock().await.slot; + let ncn = test_ncn.ncn_root.ncn_pubkey; + + for operator_root in test_ncn.operators.iter() { + let operator = operator_root.operator_pubkey; + + tip_router_client + .do_initalize_operator_snapshot(operator, ncn, slot) + .await?; + } + + Ok(()) + } + + // 9. Take all VaultOperatorDelegation snapshots + pub async fn add_vault_operator_delegation_snapshots_to_test_ncn( + &mut self, + test_ncn: &TestNcn, + ) -> TestResult<()> { + let mut tip_router_client = self.tip_router_client(); + + let slot = self.clock().await.slot; + let ncn = test_ncn.ncn_root.ncn_pubkey; + + for operator_root in test_ncn.operators.iter() { + let operator = operator_root.operator_pubkey; + for vault_root in test_ncn.vaults.iter() { + let vault = vault_root.vault_pubkey; + + tip_router_client + .do_snapshot_vault_operator_delegation(vault, operator, ncn, slot) + .await?; + } + } + + Ok(()) + } + + // Intermission 2 - all snapshots are taken + pub async fn snapshot_test_ncn(&mut self, test_ncn: &TestNcn) -> TestResult<()> { + self.add_weights_for_test_ncn(&test_ncn).await?; + self.add_epoch_snapshot_to_test_ncn(&test_ncn).await?; + self.add_operator_snapshots_to_test_ncn(&test_ncn).await?; + self.add_vault_operator_delegation_snapshots_to_test_ncn(&test_ncn) + .await?; + + Ok(()) + } } diff --git a/integration_tests/tests/fixtures/tip_router_client.rs b/integration_tests/tests/fixtures/tip_router_client.rs index b6ffb94..c4dea87 100644 --- a/integration_tests/tests/fixtures/tip_router_client.rs +++ b/integration_tests/tests/fixtures/tip_router_client.rs @@ -1,22 +1,31 @@ use jito_bytemuck::AccountDeserialize; -use jito_restaking_core::config::Config; +use jito_restaking_core::{ + config::Config, ncn_operator_state::NcnOperatorState, ncn_vault_ticket::NcnVaultTicket, +}; use jito_tip_router_client::{ instructions::{ - AdminUpdateWeightTableBuilder, InitializeNCNConfigBuilder, InitializeTrackedMintsBuilder, + AdminUpdateWeightTableBuilder, InitializeEpochSnapshotBuilder, InitializeNCNConfigBuilder, + InitializeOperatorSnapshotBuilder, InitializeTrackedMintsBuilder, InitializeWeightTableBuilder, RegisterMintBuilder, SetConfigFeesBuilder, - SetNewAdminBuilder, + SetNewAdminBuilder, SnapshotVaultOperatorDelegationBuilder, }, types::ConfigAdminRole, }; use jito_tip_router_core::{ - error::TipRouterError, ncn_config::NcnConfig, tracked_mints::TrackedMints, + epoch_snapshot::{EpochSnapshot, OperatorSnapshot}, + error::TipRouterError, + ncn_config::NcnConfig, + tracked_mints::TrackedMints, weight_table::WeightTable, }; +use jito_vault_core::{ + vault_ncn_ticket::VaultNcnTicket, vault_operator_delegation::VaultOperatorDelegation, +}; use solana_program::{ instruction::InstructionError, native_token::sol_to_lamports, pubkey::Pubkey, system_instruction::transfer, }; -use solana_program_test::BanksClient; +use solana_program_test::{BanksClient, ProgramTestBanksClientExt}; use solana_sdk::{ clock::Clock, commitment_config::CommitmentLevel, @@ -53,13 +62,18 @@ impl TipRouterClient { pub async fn airdrop(&mut self, to: &Pubkey, sol: f64) -> TestResult<()> { let blockhash = self.banks_client.get_latest_blockhash().await?; + let new_blockhash = self + .banks_client + .get_new_latest_blockhash(&blockhash) + .await + .unwrap(); self.banks_client .process_transaction_with_preflight_and_commitment( Transaction::new_signed_with_payer( &[transfer(&self.payer.pubkey(), to, sol_to_lamports(sol))], Some(&self.payer.pubkey()), &[&self.payer], - blockhash, + new_blockhash, ), CommitmentLevel::Processed, ) @@ -103,6 +117,60 @@ impl TipRouterClient { Ok(*TrackedMints::try_from_slice_unchecked(tracked_mints.data.as_slice()).unwrap()) } + #[allow(dead_code)] + pub async fn get_weight_table( + &mut self, + ncn: Pubkey, + ncn_epoch: u64, + ) -> TestResult { + let address = + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + + let raw_account = self.banks_client.get_account(address).await?.unwrap(); + + let account = WeightTable::try_from_slice_unchecked(raw_account.data.as_slice()).unwrap(); + + Ok(*account) + } + + pub async fn get_epoch_snapshot( + &mut self, + ncn: Pubkey, + ncn_epoch: u64, + ) -> TestResult { + let address = + EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + + let raw_account = self.banks_client.get_account(address).await?.unwrap(); + + let account = EpochSnapshot::try_from_slice_unchecked(raw_account.data.as_slice()).unwrap(); + + Ok(*account) + } + + #[allow(dead_code)] + pub async fn get_operator_snapshot( + &mut self, + operator: Pubkey, + ncn: Pubkey, + ncn_epoch: u64, + ) -> TestResult { + let address = OperatorSnapshot::find_program_address( + &jito_tip_router_program::id(), + &operator, + &ncn, + ncn_epoch, + ) + .0; + + let raw_account = self.banks_client.get_account(address).await?.unwrap(); + + let account = + OperatorSnapshot::try_from_slice_unchecked(raw_account.data.as_slice()).unwrap(); + + Ok(*account) + } + pub async fn do_initialize_config( &mut self, ncn: Pubkey, @@ -135,7 +203,7 @@ impl TipRouterClient { .ncn_admin(ncn_admin.pubkey()) .fee_wallet(fee_wallet) .tie_breaker_admin(tie_breaker_admin) - .restaking_program_id(jito_restaking_program::id()) + .restaking_program(jito_restaking_program::id()) .dao_fee_bps(dao_fee_bps) .ncn_fee_bps(ncn_fee_bps) .block_engine_fee_bps(block_engine_fee_bps) @@ -189,7 +257,7 @@ impl TipRouterClient { .config(config_pda) .ncn(ncn_root.ncn_pubkey) .ncn_admin(ncn_root.ncn_admin.pubkey()) - .restaking_program_id(jito_restaking_program::id()) + .restaking_program(jito_restaking_program::id()) .new_dao_fee_bps(dao_fee_bps) .new_ncn_fee_bps(ncn_fee_bps) .new_block_engine_fee_bps(block_engine_fee_bps) @@ -231,7 +299,7 @@ impl TipRouterClient { .ncn(ncn_root.ncn_pubkey) .ncn_admin(ncn_root.ncn_admin.pubkey()) .new_admin(new_admin) - .restaking_program_id(jito_restaking_program::id()) + .restaking_program(jito_restaking_program::id()) .role(role) .instruction(); @@ -274,7 +342,7 @@ impl TipRouterClient { .ncn(ncn) .weight_table(weight_table) .payer(self.payer.pubkey()) - .restaking_program_id(jito_restaking_program::id()) + .restaking_program(jito_restaking_program::id()) .system_program(system_program::id()) .instruction(); @@ -317,8 +385,9 @@ impl TipRouterClient { .weight_table(weight_table) .weight_table_admin(self.payer.pubkey()) .mint(mint) - .restaking_program_id(jito_restaking_program::id()) + .restaking_program(jito_restaking_program::id()) .weight(weight) + .ncn_epoch(ncn_epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -425,6 +494,188 @@ impl TipRouterClient { )) .await } + + pub async fn do_initialize_epoch_snapshot(&mut self, ncn: Pubkey, slot: u64) -> TestResult<()> { + self.initialize_epoch_snapshot(ncn, slot).await + } + + pub async fn initialize_epoch_snapshot(&mut self, ncn: Pubkey, slot: u64) -> TestResult<()> { + let restaking_config = Config::find_program_address(&jito_restaking_program::id()).0; + + let restaking_config_account = self.get_restaking_config().await?; + let ncn_epoch = slot / restaking_config_account.epoch_length(); + + let config_pda = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0; + let tracked_mints = + TrackedMints::find_program_address(&jito_tip_router_program::id(), &ncn).0; + let weight_table = + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + + let epoch_snapshot = + EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + + let ix = InitializeEpochSnapshotBuilder::new() + .ncn_config(config_pda) + .restaking_config(restaking_config) + .ncn(ncn) + .tracked_mints(tracked_mints) + .weight_table(weight_table) + .epoch_snapshot(epoch_snapshot) + .payer(self.payer.pubkey()) + .restaking_program(jito_restaking_program::id()) + .system_program(system_program::id()) + .first_slot_of_ncn_epoch(slot) + .instruction(); + + let blockhash = self.banks_client.get_latest_blockhash().await?; + self.process_transaction(&Transaction::new_signed_with_payer( + &[ix], + Some(&self.payer.pubkey()), + &[&self.payer], + blockhash, + )) + .await + } + + pub async fn do_initalize_operator_snapshot( + &mut self, + operator: Pubkey, + ncn: Pubkey, + slot: u64, + ) -> TestResult<()> { + self.initalize_operator_snapshot(operator, ncn, slot).await + } + + pub async fn initalize_operator_snapshot( + &mut self, + operator: Pubkey, + ncn: Pubkey, + slot: u64, + ) -> TestResult<()> { + let restaking_config = Config::find_program_address(&jito_restaking_program::id()).0; + + let restaking_config_account = self.get_restaking_config().await?; + let ncn_epoch = slot / restaking_config_account.epoch_length(); + + let config_pda = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0; + + let ncn_operator_state = + NcnOperatorState::find_program_address(&jito_restaking_program::id(), &ncn, &operator) + .0; + + let epoch_snapshot = + EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + + let operator_snapshot = OperatorSnapshot::find_program_address( + &jito_tip_router_program::id(), + &operator, + &ncn, + ncn_epoch, + ) + .0; + + let ix = InitializeOperatorSnapshotBuilder::new() + .ncn_config(config_pda) + .restaking_config(restaking_config) + .ncn(ncn) + .operator(operator) + .ncn_operator_state(ncn_operator_state) + .epoch_snapshot(epoch_snapshot) + .operator_snapshot(operator_snapshot) + .payer(self.payer.pubkey()) + .restaking_program(jito_restaking_program::id()) + .system_program(system_program::id()) + .first_slot_of_ncn_epoch(slot) + .instruction(); + + let blockhash = self.banks_client.get_latest_blockhash().await?; + self.process_transaction(&Transaction::new_signed_with_payer( + &[ix], + Some(&self.payer.pubkey()), + &[&self.payer], + blockhash, + )) + .await + } + + pub async fn do_snapshot_vault_operator_delegation( + &mut self, + vault: Pubkey, + operator: Pubkey, + ncn: Pubkey, + slot: u64, + ) -> TestResult<()> { + self.snapshot_vault_operator_delegation(vault, operator, ncn, slot) + .await + } + + pub async fn snapshot_vault_operator_delegation( + &mut self, + vault: Pubkey, + operator: Pubkey, + ncn: Pubkey, + slot: u64, + ) -> TestResult<()> { + let restaking_config = Config::find_program_address(&jito_restaking_program::id()).0; + + let restaking_config_account = self.get_restaking_config().await?; + let ncn_epoch = slot / restaking_config_account.epoch_length(); + + let config_pda = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0; + + let epoch_snapshot = + EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + + let operator_snapshot = OperatorSnapshot::find_program_address( + &jito_tip_router_program::id(), + &operator, + &ncn, + ncn_epoch, + ) + .0; + + let vault_ncn_ticket = + VaultNcnTicket::find_program_address(&jito_vault_program::id(), &vault, &ncn).0; + + let ncn_vault_ticket = + NcnVaultTicket::find_program_address(&jito_restaking_program::id(), &ncn, &vault).0; + + let vault_operator_delegation = VaultOperatorDelegation::find_program_address( + &jito_vault_program::id(), + &vault, + &operator, + ) + .0; + + let weight_table = + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + + let ix = SnapshotVaultOperatorDelegationBuilder::new() + .ncn_config(config_pda) + .restaking_config(restaking_config) + .ncn(ncn) + .operator(operator) + .vault(vault) + .vault_ncn_ticket(vault_ncn_ticket) + .ncn_vault_ticket(ncn_vault_ticket) + .vault_operator_delegation(vault_operator_delegation) + .weight_table(weight_table) + .epoch_snapshot(epoch_snapshot) + .operator_snapshot(operator_snapshot) + .vault_program(jito_vault_program::id()) + .restaking_program(jito_restaking_program::id()) + .first_slot_of_ncn_epoch(slot) + .instruction(); + + let blockhash = self.banks_client.get_latest_blockhash().await?; + self.process_transaction(&Transaction::new_signed_with_payer( + &[ix], + Some(&self.payer.pubkey()), + &[&self.payer], + blockhash, + )) + .await + } } #[inline(always)] diff --git a/integration_tests/tests/fixtures/vault_client.rs b/integration_tests/tests/fixtures/vault_client.rs index 7f86a88..73074ce 100644 --- a/integration_tests/tests/fixtures/vault_client.rs +++ b/integration_tests/tests/fixtures/vault_client.rs @@ -1,6 +1,5 @@ use std::{fmt, fmt::Debug}; -use borsh::BorshDeserialize; use jito_bytemuck::AccountDeserialize; use jito_restaking_core::{ ncn_vault_slasher_ticket::NcnVaultSlasherTicket, ncn_vault_ticket::NcnVaultTicket, @@ -30,7 +29,7 @@ use solana_program::{ rent::Rent, system_instruction::{create_account, transfer}, }; -use solana_program_test::{BanksClient, BanksClientError}; +use solana_program_test::{BanksClient, BanksClientError, ProgramTestBanksClientExt}; use solana_sdk::{ commitment_config::CommitmentLevel, instruction::InstructionError, @@ -49,6 +48,15 @@ pub struct VaultRoot { pub vault_admin: Keypair, } +impl Clone for VaultRoot { + fn clone(&self) -> Self { + Self { + vault_pubkey: self.vault_pubkey, + vault_admin: self.vault_admin.insecure_clone(), + } + } +} + impl Debug for VaultRoot { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( @@ -60,6 +68,7 @@ impl Debug for VaultRoot { } #[derive(Debug)] +#[allow(dead_code)] pub struct VaultStakerWithdrawalTicketRoot { pub base: Pubkey, } @@ -103,6 +112,7 @@ impl VaultProgramClient { Ok(*Vault::try_from_slice_unchecked(account.data.as_slice())?) } + #[allow(dead_code)] pub async fn get_vault_ncn_ticket( &mut self, vault: &Pubkey, @@ -115,6 +125,7 @@ impl VaultProgramClient { )?) } + #[allow(dead_code)] pub async fn get_vault_operator_delegation( &mut self, vault: &Pubkey, @@ -132,6 +143,7 @@ impl VaultProgramClient { )?) } + #[allow(dead_code)] pub async fn get_vault_staker_withdrawal_ticket( &mut self, vault: &Pubkey, @@ -151,6 +163,7 @@ impl VaultProgramClient { Ok(withdrawal_ticket) } + #[allow(dead_code)] pub async fn get_vault_ncn_slasher_ticket( &mut self, vault: &Pubkey, @@ -194,6 +207,7 @@ impl VaultProgramClient { )?) } + #[allow(dead_code)] pub async fn get_vault_update_state_tracker( &mut self, vault: &Pubkey, @@ -244,6 +258,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn setup_config_and_vault( &mut self, deposit_fee_bps: u16, @@ -346,6 +361,7 @@ impl VaultProgramClient { Ok(()) } + #[allow(dead_code)] pub async fn set_capacity( &mut self, config: &Pubkey, @@ -498,6 +514,7 @@ impl VaultProgramClient { Ok(()) } + #[allow(dead_code)] pub async fn do_initialize_vault_ncn_slasher_ticket( &mut self, vault_root: &VaultRoot, @@ -534,6 +551,7 @@ impl VaultProgramClient { Ok(()) } + #[allow(dead_code)] pub async fn do_warmup_vault_ncn_slasher_ticket( &mut self, vault_root: &VaultRoot, @@ -708,6 +726,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn delegate_token_account( &mut self, config: &Pubkey, @@ -737,6 +756,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn set_admin( &mut self, config: &Pubkey, @@ -760,6 +780,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn set_secondary_admin( &mut self, config: &Pubkey, @@ -785,6 +806,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn set_fees( &mut self, config: &Pubkey, @@ -812,6 +834,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn set_program_fee( &mut self, config_admin: &Keypair, @@ -832,6 +855,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn do_enqueue_withdrawal( &mut self, vault_root: &VaultRoot, @@ -876,6 +900,7 @@ impl VaultProgramClient { }) } + #[allow(dead_code)] pub async fn do_cooldown_delegation( &mut self, vault_root: &VaultRoot, @@ -976,6 +1001,7 @@ impl VaultProgramClient { Ok(()) } + #[allow(dead_code)] pub async fn do_crank_vault_update_state_tracker( &mut self, vault: &Pubkey, @@ -1134,6 +1160,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn do_burn_withdrawal_ticket( &mut self, vault_root: &VaultRoot, @@ -1356,6 +1383,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn create_token_metadata( &mut self, vault: &Pubkey, @@ -1388,6 +1416,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn update_token_metadata( &mut self, vault: &Pubkey, @@ -1429,13 +1458,18 @@ impl VaultProgramClient { pub async fn airdrop(&mut self, to: &Pubkey, sol: f64) -> Result<(), TestError> { let blockhash = self.banks_client.get_latest_blockhash().await?; + let new_blockhash = self + .banks_client + .get_new_latest_blockhash(&blockhash) + .await + .unwrap(); self.banks_client .process_transaction_with_preflight_and_commitment( Transaction::new_signed_with_payer( &[transfer(&self.payer.pubkey(), to, sol_to_lamports(sol))], Some(&self.payer.pubkey()), &[&self.payer], - blockhash, + new_blockhash, ), CommitmentLevel::Processed, ) @@ -1539,6 +1573,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn get_reward_fee_token_account( &mut self, vault: &Pubkey, @@ -1558,6 +1593,7 @@ impl VaultProgramClient { Ok(SPLTokenAccount::unpack(&account.data).unwrap()) } + #[allow(dead_code)] pub async fn create_and_fund_reward_vault( &mut self, vault: &Pubkey, @@ -1602,6 +1638,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn set_program_fee_wallet( &mut self, program_fee_admin: &Keypair, @@ -1622,6 +1659,7 @@ impl VaultProgramClient { .await } + #[allow(dead_code)] pub async fn set_is_paused( &mut self, vault: &Pubkey, @@ -1644,7 +1682,8 @@ impl VaultProgramClient { .await } - pub async fn set_config_admin( + #[allow(dead_code)] + pub async fn set_confsig_admin( &mut self, config: &Pubkey, old_admin: &Keypair, @@ -1668,6 +1707,7 @@ impl VaultProgramClient { #[inline(always)] #[track_caller] +#[allow(dead_code)] pub fn assert_vault_error(test_error: Result, vault_error: VaultError) { assert!(test_error.is_err()); assert_eq!( diff --git a/integration_tests/tests/tip_router/admin_update_weight_table.rs b/integration_tests/tests/tip_router/admin_update_weight_table.rs index adc7d32..58897d2 100644 --- a/integration_tests/tests/tip_router/admin_update_weight_table.rs +++ b/integration_tests/tests/tip_router/admin_update_weight_table.rs @@ -1,34 +1,34 @@ #[cfg(test)] mod tests { - use solana_sdk::pubkey::Pubkey; - use crate::fixtures::{test_builder::TestBuilder, TestResult}; #[tokio::test] async fn test_admin_update_weight_table() -> TestResult<()> { let mut fixture = TestBuilder::new().await; + let mut vault_client = fixture.vault_program_client(); let mut tip_router_client = fixture.tip_router_client(); - let ncn_root = fixture.setup_ncn().await?; + + let test_ncn = fixture.create_initial_test_ncn(1, 1).await?; fixture.warp_slot_incremental(1000).await?; let slot = fixture.clock().await.slot; - tip_router_client.setup_tip_router(&ncn_root).await?; - tip_router_client - .do_initialize_weight_table(ncn_root.ncn_pubkey, slot) + .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) .await?; - let mint = Pubkey::new_unique(); + let vault_root = test_ncn.vaults[0].clone(); + let vault = vault_client.get_vault(&vault_root.vault_pubkey).await?; + + let mint = vault.supported_mint; let weight = 100; tip_router_client - .do_admin_update_weight_table(ncn_root.ncn_pubkey, slot, mint, weight) + .do_admin_update_weight_table(test_ncn.ncn_root.ncn_pubkey, slot, mint, weight) .await?; - //TODO add functionality to update weight table Ok(()) } } diff --git a/integration_tests/tests/tip_router/initialize_epoch_snapshot.rs b/integration_tests/tests/tip_router/initialize_epoch_snapshot.rs new file mode 100644 index 0000000..706e7b5 --- /dev/null +++ b/integration_tests/tests/tip_router/initialize_epoch_snapshot.rs @@ -0,0 +1,38 @@ +#[cfg(test)] +mod tests { + + use crate::fixtures::{test_builder::TestBuilder, TestResult}; + + #[tokio::test] + async fn test_initialize_epoch_snapshot_ok() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + let mut vault_client = fixture.vault_program_client(); + let mut tip_router_client = fixture.tip_router_client(); + + let test_ncn = fixture.create_initial_test_ncn(1, 1).await?; + + fixture.warp_slot_incremental(1000).await?; + + let slot = fixture.clock().await.slot; + + tip_router_client + .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .await?; + + let vault_root = test_ncn.vaults[0].clone(); + let vault = vault_client.get_vault(&vault_root.vault_pubkey).await?; + + let mint = vault.supported_mint; + let weight = 100; + + tip_router_client + .do_admin_update_weight_table(test_ncn.ncn_root.ncn_pubkey, slot, mint, weight) + .await?; + + tip_router_client + .do_initialize_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, slot) + .await?; + + Ok(()) + } +} diff --git a/integration_tests/tests/tip_router/initialize_operator_snapshot.rs b/integration_tests/tests/tip_router/initialize_operator_snapshot.rs new file mode 100644 index 0000000..0c1f202 --- /dev/null +++ b/integration_tests/tests/tip_router/initialize_operator_snapshot.rs @@ -0,0 +1,46 @@ +#[cfg(test)] +mod tests { + + use crate::fixtures::{test_builder::TestBuilder, TestResult}; + + #[tokio::test] + async fn test_initialize_operator_snapshot() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + let mut vault_client = fixture.vault_program_client(); + let mut tip_router_client = fixture.tip_router_client(); + + let test_ncn = fixture.create_initial_test_ncn(1, 1).await?; + + fixture.warp_slot_incremental(1000).await?; + + let slot = fixture.clock().await.slot; + + tip_router_client + .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .await?; + + let ncn = test_ncn.ncn_root.ncn_pubkey; + + let vault_root = test_ncn.vaults[0].clone(); + let vault = vault_client.get_vault(&vault_root.vault_pubkey).await?; + + let mint = vault.supported_mint; + let weight = 100; + + tip_router_client + .do_admin_update_weight_table(ncn, slot, mint, weight) + .await?; + + tip_router_client + .do_initialize_epoch_snapshot(ncn, slot) + .await?; + + let operator = test_ncn.operators[0].operator_pubkey; + + tip_router_client + .do_initalize_operator_snapshot(operator, ncn, slot) + .await?; + + Ok(()) + } +} diff --git a/integration_tests/tests/tip_router/initialize_weight_table.rs b/integration_tests/tests/tip_router/initialize_weight_table.rs index 3d70c7e..62a8d00 100644 --- a/integration_tests/tests/tip_router/initialize_weight_table.rs +++ b/integration_tests/tests/tip_router/initialize_weight_table.rs @@ -7,17 +7,17 @@ mod tests { async fn test_initialize_weight_table_ok() -> TestResult<()> { let mut fixture = TestBuilder::new().await; let mut tip_router_client = fixture.tip_router_client(); - let ncn_root = fixture.setup_ncn().await?; + + let test_ncn = fixture.create_initial_test_ncn(1, 1).await?; fixture.warp_slot_incremental(1000).await?; let slot = fixture.clock().await.slot; - tip_router_client.setup_tip_router(&ncn_root).await?; - tip_router_client - .do_initialize_weight_table(ncn_root.ncn_pubkey, slot) + .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) .await?; + Ok(()) } } diff --git a/integration_tests/tests/tip_router/meta_tests.rs b/integration_tests/tests/tip_router/meta_tests.rs new file mode 100644 index 0000000..2cc4ee8 --- /dev/null +++ b/integration_tests/tests/tip_router/meta_tests.rs @@ -0,0 +1,137 @@ +#[cfg(test)] +mod tests { + + use crate::fixtures::{test_builder::TestBuilder, TestResult}; + + #[tokio::test] + async fn test_all_test_ncn_functions() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + + const OPERATOR_COUNT: usize = 1; + const VAULT_COUNT: usize = 1; + + let mut test_ncn = fixture.create_test_ncn().await?; + fixture + .add_operators_to_test_ncn(&mut test_ncn, OPERATOR_COUNT) + .await?; + fixture + .add_vaults_to_test_ncn(&mut test_ncn, VAULT_COUNT) + .await?; + fixture.add_delegation_in_test_ncn(&test_ncn, 100).await?; + fixture.add_tracked_mints_to_test_ncn(&test_ncn).await?; + fixture.add_weights_for_test_ncn(&test_ncn).await?; + fixture.add_epoch_snapshot_to_test_ncn(&test_ncn).await?; + fixture + .add_operator_snapshots_to_test_ncn(&test_ncn) + .await?; + fixture + .add_vault_operator_delegation_snapshots_to_test_ncn(&test_ncn) + .await?; + + Ok(()) + } + + #[tokio::test] + async fn test_intermission_test_ncn_functions() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + let mut tip_router_client = fixture.tip_router_client(); + let mut restaking_client = fixture.restaking_program_client(); + + const OPERATOR_COUNT: usize = 1; + const VAULT_COUNT: usize = 1; + + let test_ncn = fixture + .create_initial_test_ncn(OPERATOR_COUNT, VAULT_COUNT) + .await?; + fixture.snapshot_test_ncn(&test_ncn).await?; + + let slot = fixture.clock().await.slot; + let ncn_epoch = restaking_client.get_ncn_epoch(slot).await?; + + let epoch_snapshot = tip_router_client + .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .await?; + + assert!(epoch_snapshot.finalized()); + + Ok(()) + } + + #[tokio::test] + async fn test_multiple_operators() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + let mut tip_router_client = fixture.tip_router_client(); + let mut restaking_client = fixture.restaking_program_client(); + + const OPERATOR_COUNT: usize = 10; + const VAULT_COUNT: usize = 1; + + let test_ncn = fixture + .create_initial_test_ncn(OPERATOR_COUNT, VAULT_COUNT) + .await?; + fixture.snapshot_test_ncn(&test_ncn).await?; + + let slot = fixture.clock().await.slot; + let ncn_epoch = restaking_client.get_ncn_epoch(slot).await?; + + let epoch_snapshot = tip_router_client + .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .await?; + + assert!(epoch_snapshot.finalized()); + + Ok(()) + } + + #[tokio::test] + async fn test_multiple_vaults() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + let mut tip_router_client = fixture.tip_router_client(); + let mut restaking_client = fixture.restaking_program_client(); + + const OPERATOR_COUNT: usize = 1; + const VAULT_COUNT: usize = 10; + + let test_ncn = fixture + .create_initial_test_ncn(OPERATOR_COUNT, VAULT_COUNT) + .await?; + fixture.snapshot_test_ncn(&test_ncn).await?; + + let slot = fixture.clock().await.slot; + let ncn_epoch = restaking_client.get_ncn_epoch(slot).await?; + + let epoch_snapshot = tip_router_client + .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .await?; + + assert!(epoch_snapshot.finalized()); + + Ok(()) + } + + #[tokio::test] + async fn test_multiple_operators_and_vaults() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + let mut tip_router_client = fixture.tip_router_client(); + let mut restaking_client = fixture.restaking_program_client(); + + const OPERATOR_COUNT: usize = 10; + const VAULT_COUNT: usize = 10; + + let test_ncn = fixture + .create_initial_test_ncn(OPERATOR_COUNT, VAULT_COUNT) + .await?; + fixture.snapshot_test_ncn(&test_ncn).await?; + + let slot = fixture.clock().await.slot; + let ncn_epoch = restaking_client.get_ncn_epoch(slot).await?; + + let epoch_snapshot = tip_router_client + .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .await?; + + assert!(epoch_snapshot.finalized()); + + Ok(()) + } +} diff --git a/integration_tests/tests/tip_router/mod.rs b/integration_tests/tests/tip_router/mod.rs index 85e3e93..43d86a4 100644 --- a/integration_tests/tests/tip_router/mod.rs +++ b/integration_tests/tests/tip_router/mod.rs @@ -1,7 +1,11 @@ mod admin_update_weight_table; +mod initialize_epoch_snapshot; mod initialize_ncn_config; +mod initialize_operator_snapshot; mod initialize_tracked_mints; mod initialize_weight_table; +mod meta_tests; mod register_mint; mod set_config_fees; mod set_new_admin; +mod snapshot_vault_operator_delegation; diff --git a/integration_tests/tests/tip_router/register_mint.rs b/integration_tests/tests/tip_router/register_mint.rs index 72d7381..7f47ae0 100644 --- a/integration_tests/tests/tip_router/register_mint.rs +++ b/integration_tests/tests/tip_router/register_mint.rs @@ -13,12 +13,10 @@ mod tests { let mut vault_client = fixture.vault_client(); let mut restaking_client = fixture.restaking_program_client(); let ncn_root = fixture.setup_ncn().await?; - - // Setup initial state + // // Setup initial state tip_router_client.setup_tip_router(&ncn_root).await?; - // Setup vault and tickets - let _ = vault_client.do_initialize_config().await?; + // // Setup vault and tickets let vault_root = vault_client .do_initialize_vault(0, 0, 0, 9, &ncn_root.ncn_pubkey) .await?; @@ -114,7 +112,6 @@ mod tests { tip_router_client.setup_tip_router(&ncn_root).await?; // Setup vault and tickets - let _ = vault_client.do_initialize_config().await?; let vault_root = vault_client .do_initialize_vault(0, 0, 0, 9, &ncn_root.ncn_pubkey) .await?; @@ -195,7 +192,6 @@ mod tests { tip_router_client.setup_tip_router(&ncn_root).await?; - let _ = vault_client.do_initialize_config().await?; let vault_root = vault_client .do_initialize_vault(0, 0, 0, 9, &ncn_root.ncn_pubkey) .await?; diff --git a/integration_tests/tests/tip_router/set_new_admin.rs b/integration_tests/tests/tip_router/set_new_admin.rs index af6801c..c0d5813 100644 --- a/integration_tests/tests/tip_router/set_new_admin.rs +++ b/integration_tests/tests/tip_router/set_new_admin.rs @@ -55,7 +55,7 @@ mod tests { fixture.warp_slot_incremental(1).await?; let mut restaking_program_client = fixture.restaking_program_client(); - let wrong_ncn_root = restaking_program_client.do_initialize_ncn().await?; + let wrong_ncn_root = restaking_program_client.do_initialize_ncn(None).await?; let result = tip_router_client .set_new_admin( diff --git a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs new file mode 100644 index 0000000..7987350 --- /dev/null +++ b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs @@ -0,0 +1,51 @@ +#[cfg(test)] +mod tests { + + use crate::fixtures::{test_builder::TestBuilder, TestResult}; + + #[tokio::test] + async fn test_initialize_operator_snapshot() -> TestResult<()> { + let mut fixture = TestBuilder::new().await; + let mut vault_client = fixture.vault_program_client(); + let mut tip_router_client = fixture.tip_router_client(); + + let test_ncn = fixture.create_initial_test_ncn(1, 1).await?; + + fixture.warp_slot_incremental(1000).await?; + + let slot = fixture.clock().await.slot; + + tip_router_client + .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .await?; + + let ncn = test_ncn.ncn_root.ncn_pubkey; + + let vault_root = test_ncn.vaults[0].clone(); + let vault_address = vault_root.vault_pubkey; + let vault = vault_client.get_vault(&vault_address).await?; + + let mint = vault.supported_mint; + let weight = 100; + + tip_router_client + .do_admin_update_weight_table(ncn, slot, mint, weight) + .await?; + + tip_router_client + .do_initialize_epoch_snapshot(ncn, slot) + .await?; + + let operator = test_ncn.operators[0].operator_pubkey; + + tip_router_client + .do_initalize_operator_snapshot(operator, ncn, slot) + .await?; + + tip_router_client + .do_snapshot_vault_operator_delegation(vault_address, operator, ncn, slot) + .await?; + + Ok(()) + } +} diff --git a/program/Cargo.toml b/program/Cargo.toml index a7819c4..78f6afb 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -36,6 +36,7 @@ jito-restaking-program = { workspace = true } jito-restaking-sdk = { workspace = true } jito-tip-router-core = { workspace = true } jito-vault-core = { workspace = true } +jito-vault-program = { workspace = true } jito-vault-sdk = { workspace = true } shank = { workspace = true } solana-program = { workspace = true } diff --git a/program/src/admin_update_weight_table.rs b/program/src/admin_update_weight_table.rs index 6919bb6..345a324 100644 --- a/program/src/admin_update_weight_table.rs +++ b/program/src/admin_update_weight_table.rs @@ -14,11 +14,11 @@ pub fn process_admin_update_weight_table( ncn_epoch: u64, weight: u128, ) -> ProgramResult { - let [ncn, weight_table, weight_table_admin, mint, restaking_program_id] = accounts else { + let [ncn, weight_table, weight_table_admin, mint, restaking_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; - Ncn::load(restaking_program_id.key, ncn, false)?; + Ncn::load(restaking_program.key, ncn, false)?; let ncn_weight_table_admin = { let ncn_data = ncn.data.borrow(); let ncn = Ncn::try_from_slice_unchecked(&ncn_data)?; @@ -29,7 +29,7 @@ pub fn process_admin_update_weight_table( load_token_mint(mint)?; WeightTable::load(program_id, weight_table, ncn, ncn_epoch, true)?; - if restaking_program_id.key.ne(&jito_restaking_program::id()) { + if restaking_program.key.ne(&jito_restaking_program::id()) { msg!("Incorrect restaking program ID"); return Err(ProgramError::InvalidAccountData); } diff --git a/program/src/finalize_weight_table.rs b/program/src/finalize_weight_table.rs deleted file mode 100644 index 76583c0..0000000 --- a/program/src/finalize_weight_table.rs +++ /dev/null @@ -1,51 +0,0 @@ -use jito_bytemuck::AccountDeserialize; -use jito_jsm_core::loader::load_signer; -use jito_restaking_core::ncn::Ncn; -use jito_tip_router_core::{error::TipRouterError, weight_table::WeightTable}; -use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, -}; - -/// Initializes a Weight Table -pub fn process_finalize_weight_table( - program_id: &Pubkey, - accounts: &[AccountInfo], - ncn_epoch: u64, - mint_hash: u64, - mint_count: u8, -) -> ProgramResult { - let [ncn, weight_table, weight_table_admin, restaking_program_id] = accounts else { - return Err(ProgramError::NotEnoughAccountKeys); - }; - - Ncn::load(restaking_program_id.key, ncn, false)?; - let ncn_weight_table_admin = { - let ncn_data = ncn.data.borrow(); - let ncn = Ncn::try_from_slice_unchecked(&ncn_data)?; - ncn.weight_table_admin - }; - - load_signer(weight_table_admin, true)?; - WeightTable::load(program_id, weight_table, ncn, ncn_epoch, true)?; - - if restaking_program_id.key.ne(&jito_restaking_program::id()) { - msg!("Incorrect restaking program ID"); - return Err(ProgramError::InvalidAccountData); - } - - if ncn_weight_table_admin.ne(weight_table_admin.key) { - msg!("Vault update delegations ticket is not at the correct PDA"); - return Err(TipRouterError::IncorrectWeightTableAdmin.into()); - } - - let mut weight_table_data = weight_table.try_borrow_mut_data()?; - let weight_table_account = WeightTable::try_from_slice_unchecked_mut(&mut weight_table_data)?; - - weight_table_account.check_mints_okay(mint_hash, mint_count)?; - - let current_slot = Clock::get()?.slot; - weight_table_account.finalize(current_slot); - - Ok(()) -} diff --git a/program/src/initialize_epoch_snapshot.rs b/program/src/initialize_epoch_snapshot.rs new file mode 100644 index 0000000..1814bb4 --- /dev/null +++ b/program/src/initialize_epoch_snapshot.rs @@ -0,0 +1,126 @@ +use jito_bytemuck::{AccountDeserialize, Discriminator}; +use jito_jsm_core::{ + create_account, + loader::{load_signer, load_system_account, load_system_program}, +}; +use jito_restaking_core::{config::Config, ncn::Ncn}; +use jito_tip_router_core::{ + epoch_snapshot::EpochSnapshot, error::TipRouterError, fees, loaders::load_ncn_epoch, + ncn_config::NcnConfig, tracked_mints::TrackedMints, weight_table::WeightTable, +}; +use solana_program::{ + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, + program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, +}; + +/// Initializes an Epoch Snapshot +pub fn process_initialize_epoch_snapshot( + program_id: &Pubkey, + accounts: &[AccountInfo], + first_slot_of_ncn_epoch: Option, +) -> ProgramResult { + let [ncn_config, restaking_config, ncn, tracked_mints, weight_table, epoch_snapshot, payer, restaking_program, system_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if restaking_program.key.ne(&jito_restaking_program::id()) { + msg!("Incorrect restaking program ID"); + return Err(ProgramError::InvalidAccountData); + } + + NcnConfig::load(program_id, ncn.key, ncn_config, false)?; + Config::load(restaking_program.key, restaking_config, false)?; + Ncn::load(restaking_program.key, ncn, false)?; + TrackedMints::load(program_id, ncn.key, tracked_mints, false)?; + + load_system_account(epoch_snapshot, true)?; + load_system_program(system_program)?; + //TODO check that it is not writable + load_signer(payer, false)?; + + let current_slot = Clock::get()?.slot; + let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; + + WeightTable::load(program_id, weight_table, ncn, ncn_epoch, false)?; + + // Weight table needs to be finalized before the snapshot can be taken + { + let weight_table_data = weight_table.data.borrow(); + let weight_table_account = WeightTable::try_from_slice_unchecked(&weight_table_data)?; + + if !weight_table_account.finalized() { + msg!("Weight table must be finalized before initializing epoch snapshot"); + return Err(TipRouterError::WeightTableNotFinalized.into()); + } + } + + let (epoch_snapshot_pubkey, epoch_snapshot_bump, mut epoch_snapshot_seeds) = + EpochSnapshot::find_program_address(program_id, ncn.key, ncn_epoch); + epoch_snapshot_seeds.push(vec![epoch_snapshot_bump]); + + if epoch_snapshot_pubkey.ne(epoch_snapshot.key) { + msg!("Incorrect epoch snapshot PDA"); + return Err(ProgramError::InvalidAccountData); + } + + msg!( + "Initializing Epoch snapshot {} for NCN: {} at epoch: {}", + epoch_snapshot.key, + ncn.key, + ncn_epoch + ); + create_account( + payer, + epoch_snapshot, + system_program, + program_id, + &Rent::get()?, + 8_u64 + .checked_add(std::mem::size_of::() as u64) + .unwrap(), + &epoch_snapshot_seeds, + )?; + + let ncn_fees: fees::Fees = { + let ncn_config_data = ncn_config.data.borrow(); + let ncn_config_account = NcnConfig::try_from_slice_unchecked(&ncn_config_data)?; + ncn_config_account.fees + }; + + let operator_count: u64 = { + let ncn_data = ncn.data.borrow(); + let ncn_account = Ncn::try_from_slice_unchecked(&ncn_data)?; + ncn_account.operator_count() + }; + + let vault_count: u64 = { + let tracked_mints_data = tracked_mints.data.borrow(); + let tracked_mints_account = TrackedMints::try_from_slice_unchecked(&tracked_mints_data)?; + tracked_mints_account.mint_count() + }; + + if operator_count == 0 { + msg!("No operators to snapshot"); + return Err(TipRouterError::NoOperators.into()); + } + + let mut epoch_snapshot_data: std::cell::RefMut<'_, &mut [u8]> = + epoch_snapshot.try_borrow_mut_data()?; + epoch_snapshot_data[0] = EpochSnapshot::DISCRIMINATOR; + let epoch_snapshot_account = + EpochSnapshot::try_from_slice_unchecked_mut(&mut epoch_snapshot_data)?; + + *epoch_snapshot_account = EpochSnapshot::new( + *ncn.key, + ncn_epoch, + epoch_snapshot_bump, + current_slot, + ncn_fees, + operator_count, + vault_count, + ); + + Ok(()) +} diff --git a/program/src/initialize_ncn_config.rs b/program/src/initialize_ncn_config.rs index fb49d3b..15d8149 100644 --- a/program/src/initialize_ncn_config.rs +++ b/program/src/initialize_ncn_config.rs @@ -4,7 +4,9 @@ use jito_jsm_core::{ loader::{load_signer, load_system_account, load_system_program}, }; use jito_restaking_core::{config::Config, ncn::Ncn}; -use jito_tip_router_core::{error::TipRouterError, fees::Fees, ncn_config::NcnConfig, MAX_FEE_BPS}; +use jito_tip_router_core::{ + constants::MAX_FEE_BPS, error::TipRouterError, fees::Fees, ncn_config::NcnConfig, +}; use solana_program::{ account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, @@ -17,7 +19,7 @@ pub fn process_initialize_ncn_config( ncn_fee_bps: u64, block_engine_fee_bps: u64, ) -> ProgramResult { - let [restaking_config, ncn_config, ncn_account, fee_wallet, ncn_admin, tie_breaker_admin, restaking_program_id, system_program] = + let [restaking_config, ncn_config, ncn_account, fee_wallet, ncn_admin, tie_breaker_admin, restaking_program, system_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); @@ -27,8 +29,8 @@ pub fn process_initialize_ncn_config( load_system_program(system_program)?; load_signer(ncn_admin, true)?; - Ncn::load(restaking_program_id.key, ncn_account, false)?; - Config::load(restaking_program_id.key, restaking_config, false)?; + Ncn::load(restaking_program.key, ncn_account, false)?; + Config::load(restaking_program.key, restaking_config, false)?; let ncn_epoch_length = { let config_data = restaking_config.data.borrow(); diff --git a/program/src/initialize_operator_snapshot.rs b/program/src/initialize_operator_snapshot.rs new file mode 100644 index 0000000..a2d72c8 --- /dev/null +++ b/program/src/initialize_operator_snapshot.rs @@ -0,0 +1,160 @@ +use jito_bytemuck::{AccountDeserialize, Discriminator}; +use jito_jsm_core::{ + create_account, + loader::{load_signer, load_system_account, load_system_program}, +}; +use jito_restaking_core::{ + config::Config, ncn::Ncn, ncn_operator_state::NcnOperatorState, operator::Operator, +}; +use jito_tip_router_core::{ + epoch_snapshot::{EpochSnapshot, OperatorSnapshot}, + loaders::load_ncn_epoch, + ncn_config::NcnConfig, +}; +use solana_program::{ + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, + program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, +}; + +/// Initializes an Operator Snapshot +pub fn process_initialize_operator_snapshot( + program_id: &Pubkey, + accounts: &[AccountInfo], + first_slot_of_ncn_epoch: Option, +) -> ProgramResult { + let [ncn_config, restaking_config, ncn, operator, ncn_operator_state, epoch_snapshot, operator_snapshot, payer, restaking_program, system_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if restaking_program.key.ne(&jito_restaking_program::id()) { + msg!("Incorrect restaking program ID"); + return Err(ProgramError::InvalidAccountData); + } + + NcnConfig::load(program_id, ncn.key, ncn_config, false)?; + Config::load(restaking_program.key, restaking_config, false)?; + Ncn::load(restaking_program.key, ncn, false)?; + Operator::load(restaking_program.key, operator, false)?; + NcnOperatorState::load( + restaking_program.key, + ncn_operator_state, + ncn, + operator, + false, + )?; + + load_system_account(operator_snapshot, true)?; + load_system_program(system_program)?; + //TODO check that it is not writable + load_signer(payer, false)?; + + let current_slot = Clock::get()?.slot; + let (ncn_epoch, ncn_epoch_length) = + load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; + + EpochSnapshot::load(program_id, ncn.key, ncn_epoch, epoch_snapshot, true)?; + + let (operator_snapshot_pubkey, operator_snapshot_bump, mut operator_snapshot_seeds) = + OperatorSnapshot::find_program_address(program_id, operator.key, ncn.key, ncn_epoch); + operator_snapshot_seeds.push(vec![operator_snapshot_bump]); + + if operator_snapshot_pubkey.ne(operator_snapshot.key) { + msg!("Incorrect epoch snapshot PDA"); + return Err(ProgramError::InvalidAccountData); + } + + msg!( + "Initializing Operator snapshot {} for NCN: {} at epoch: {}", + epoch_snapshot.key, + ncn.key, + ncn_epoch + ); + create_account( + payer, + operator_snapshot, + system_program, + program_id, + &Rent::get()?, + 8_u64 + .checked_add(std::mem::size_of::() as u64) + .unwrap(), + &operator_snapshot_seeds, + )?; + + //TODO move to helper function + let (is_active, ncn_operator_index): (bool, u64) = { + let ncn_operator_state_data = ncn_operator_state.data.borrow(); + let ncn_operator_state_account = + NcnOperatorState::try_from_slice_unchecked(&ncn_operator_state_data)?; + + let ncn_operator_okay = ncn_operator_state_account + .ncn_opt_in_state + .is_active(current_slot, ncn_epoch_length); + + let operator_ncn_okay = ncn_operator_state_account + .operator_opt_in_state + .is_active(current_slot, ncn_epoch_length); + + let ncn_operator_index = ncn_operator_state_account.index(); + + (ncn_operator_okay && operator_ncn_okay, ncn_operator_index) + }; + + let vault_count = { + let epoch_snapshot_data = epoch_snapshot.data.borrow(); + let epoch_snapshot_account = EpochSnapshot::try_from_slice_unchecked(&epoch_snapshot_data)?; + epoch_snapshot_account.vault_count() + }; + + let (operator_fee_bps, operator_index): (u16, u64) = { + let operator_data = operator.data.borrow(); + let operator_account = Operator::try_from_slice_unchecked(&operator_data)?; + ( + operator_account.operator_fee_bps.into(), + operator_account.index(), + ) + }; + + let mut operator_snapshot_data: std::cell::RefMut<'_, &mut [u8]> = + operator_snapshot.try_borrow_mut_data()?; + operator_snapshot_data[0] = OperatorSnapshot::DISCRIMINATOR; + let operator_snapshot_account = + OperatorSnapshot::try_from_slice_unchecked_mut(&mut operator_snapshot_data)?; + + *operator_snapshot_account = if is_active { + OperatorSnapshot::new_active( + *operator.key, + *ncn.key, + ncn_epoch, + operator_snapshot_bump, + current_slot, + ncn_operator_index, + operator_index, + operator_fee_bps, + vault_count, + )? + } else { + OperatorSnapshot::new_inactive( + *operator.key, + *ncn.key, + ncn_epoch, + operator_snapshot_bump, + current_slot, + ncn_operator_index, + operator_index, + )? + }; + + // Increment operator registration for an inactive operator + if !is_active { + let mut epoch_snapshot_data = epoch_snapshot.try_borrow_mut_data()?; + let epoch_snapshot_account = + EpochSnapshot::try_from_slice_unchecked_mut(&mut epoch_snapshot_data)?; + + epoch_snapshot_account.increment_operator_registration(current_slot, 0, 0)?; + } + + Ok(()) +} diff --git a/program/src/initialize_weight_table.rs b/program/src/initialize_weight_table.rs index f893b63..4f98643 100644 --- a/program/src/initialize_weight_table.rs +++ b/program/src/initialize_weight_table.rs @@ -7,7 +7,7 @@ use jito_jsm_core::{ }; use jito_restaking_core::{config::Config, ncn::Ncn}; use jito_tip_router_core::{ - error::TipRouterError, tracked_mints::TrackedMints, weight_table::WeightTable, + loaders::load_ncn_epoch, tracked_mints::TrackedMints, weight_table::WeightTable, }; use solana_program::{ account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, @@ -21,21 +21,27 @@ pub fn process_initialize_weight_table( accounts: &[AccountInfo], first_slot_of_ncn_epoch: Option, ) -> ProgramResult { - let [restaking_config, tracked_mints, ncn, weight_table, payer, restaking_program_id, system_program] = + let [restaking_config, tracked_mints, ncn, weight_table, payer, restaking_program, system_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; + if restaking_program.key.ne(&jito_restaking_program::id()) { + msg!("Incorrect restaking program ID"); + return Err(ProgramError::InvalidAccountData); + } + TrackedMints::load(program_id, ncn.key, tracked_mints, false)?; - Config::load(restaking_program_id.key, restaking_config, false)?; - Ncn::load(restaking_program_id.key, ncn, false)?; + Config::load(restaking_program.key, restaking_config, false)?; + Ncn::load(restaking_program.key, ncn, false)?; - let ncn_epoch_length = { - let config_data = restaking_config.data.borrow(); - let config = Config::try_from_slice_unchecked(&config_data)?; - config.epoch_length() - }; + load_system_account(weight_table, true)?; + load_system_program(system_program)?; + load_signer(payer, true)?; + + let current_slot = Clock::get()?.slot; + let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; let vault_count = { let ncn_data = ncn.data.borrow(); @@ -43,33 +49,17 @@ pub fn process_initialize_weight_table( ncn.vault_count() }; - let tracked_mints_data: std::cell::Ref<'_, &mut [u8]> = tracked_mints.data.borrow(); - let tracked_mints = TrackedMints::try_from_slice_unchecked(&tracked_mints_data)?; - - load_system_account(weight_table, true)?; - load_system_program(system_program)?; - load_signer(payer, true)?; + let (tracked_mint_count, unique_mints) = { + let tracked_mints_data = tracked_mints.data.borrow(); + let tracked_mints = TrackedMints::try_from_slice_unchecked(&tracked_mints_data)?; + (tracked_mints.mint_count(), tracked_mints.get_unique_mints()) + }; - if restaking_program_id.key.ne(&jito_restaking_program::id()) { - msg!("Incorrect restaking program ID"); + if vault_count != tracked_mint_count { + msg!("Vault count does not match supported mint count"); return Err(ProgramError::InvalidAccountData); } - let current_slot = Clock::get()?.slot; - let current_ncn_epoch = current_slot - .checked_div(ncn_epoch_length) - .ok_or(TipRouterError::DenominatorIsZero)?; - - let ncn_epoch_slot = first_slot_of_ncn_epoch.unwrap_or(current_slot); - let ncn_epoch = ncn_epoch_slot - .checked_div(ncn_epoch_length) - .ok_or(TipRouterError::DenominatorIsZero)?; - - if ncn_epoch > current_ncn_epoch { - msg!("Weight tables can only be initialized for current or past epochs"); - return Err(TipRouterError::CannotCreateFutureWeightTables.into()); - } - let (weight_table_pubkey, weight_table_bump, mut weight_table_seeds) = WeightTable::find_program_address(program_id, ncn.key, ncn_epoch); weight_table_seeds.push(vec![weight_table_bump]); @@ -79,11 +69,6 @@ pub fn process_initialize_weight_table( return Err(ProgramError::InvalidAccountData); } - if vault_count as usize != tracked_mints.mint_count() { - msg!("Vault count does not match supported mint count"); - return Err(ProgramError::InvalidAccountData); - } - msg!( "Initializing Weight Table {} for NCN: {} at epoch: {}", weight_table.key, @@ -106,7 +91,7 @@ pub fn process_initialize_weight_table( *weight_table_account = WeightTable::new(*ncn.key, ncn_epoch, current_slot, weight_table_bump); - weight_table_account.initalize_weight_table(&tracked_mints.get_unique_mints())?; + weight_table_account.initalize_weight_table(&unique_mints)?; Ok(()) } diff --git a/program/src/lib.rs b/program/src/lib.rs index 68c4a4d..df0b144 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -1,14 +1,17 @@ mod admin_update_weight_table; +mod initialize_epoch_snapshot; mod initialize_ncn_config; +mod initialize_operator_snapshot; mod initialize_tracked_mints; mod initialize_weight_table; mod register_mint; mod set_config_fees; mod set_new_admin; +mod snapshot_vault_operator_delegation; use borsh::BorshDeserialize; use const_str_to_pubkey::str_to_pubkey; -use jito_tip_router_core::instruction::WeightTableInstruction; +use jito_tip_router_core::instruction::TipRouterInstruction; use set_new_admin::process_set_new_admin; use solana_program::{ account_info::AccountInfo, declare_id, entrypoint::ProgramResult, msg, @@ -19,10 +22,13 @@ use solana_security_txt::security_txt; use crate::{ admin_update_weight_table::process_admin_update_weight_table, + initialize_epoch_snapshot::process_initialize_epoch_snapshot, initialize_ncn_config::process_initialize_ncn_config, + initialize_operator_snapshot::process_initialize_operator_snapshot, initialize_tracked_mints::process_initialize_tracked_mints, initialize_weight_table::process_initialize_weight_table, register_mint::process_register_mint, set_config_fees::process_set_config_fees, + snapshot_vault_operator_delegation::process_snapshot_vault_operator_delegation, }; declare_id!(str_to_pubkey(env!("TIP_ROUTER_PROGRAM_ID"))); @@ -51,13 +57,13 @@ pub fn process_instruction( return Err(ProgramError::IncorrectProgramId); } - let instruction = WeightTableInstruction::try_from_slice(instruction_data)?; + let instruction = TipRouterInstruction::try_from_slice(instruction_data)?; match instruction { // ------------------------------------------ // Initialization // ------------------------------------------ - WeightTableInstruction::InitializeNCNConfig { + TipRouterInstruction::InitializeNCNConfig { dao_fee_bps, ncn_fee_bps, block_engine_fee_bps, @@ -71,20 +77,42 @@ pub fn process_instruction( block_engine_fee_bps, ) } - WeightTableInstruction::InitializeWeightTable { + TipRouterInstruction::InitializeWeightTable { first_slot_of_ncn_epoch, } => { msg!("Instruction: InitializeWeightTable"); process_initialize_weight_table(program_id, accounts, first_slot_of_ncn_epoch) } + TipRouterInstruction::InitializeEpochSnapshot { + first_slot_of_ncn_epoch, + } => { + msg!("Instruction: InitializeEpochSnapshot"); + process_initialize_epoch_snapshot(program_id, accounts, first_slot_of_ncn_epoch) + } + TipRouterInstruction::InitializeOperatorSnapshot { + first_slot_of_ncn_epoch, + } => { + msg!("Instruction: InitializeOperatorSnapshot"); + process_initialize_operator_snapshot(program_id, accounts, first_slot_of_ncn_epoch) + } + TipRouterInstruction::SnapshotVaultOperatorDelegation { + first_slot_of_ncn_epoch, + } => { + msg!("Instruction: InitializeVaultOperatorDelegationSnapshot"); + process_snapshot_vault_operator_delegation( + program_id, + accounts, + first_slot_of_ncn_epoch, + ) + } // ------------------------------------------ // Update // ------------------------------------------ - WeightTableInstruction::AdminUpdateWeightTable { ncn_epoch, weight } => { + TipRouterInstruction::AdminUpdateWeightTable { ncn_epoch, weight } => { msg!("Instruction: UpdateWeightTable"); process_admin_update_weight_table(program_id, accounts, ncn_epoch, weight) } - WeightTableInstruction::SetConfigFees { + TipRouterInstruction::SetConfigFees { new_dao_fee_bps, new_ncn_fee_bps, new_block_engine_fee_bps, @@ -100,15 +128,15 @@ pub fn process_instruction( new_fee_wallet, ) } - WeightTableInstruction::SetNewAdmin { role } => { + TipRouterInstruction::SetNewAdmin { role } => { msg!("Instruction: SetNewAdmin"); process_set_new_admin(program_id, accounts, role) } - WeightTableInstruction::RegisterMint => { + TipRouterInstruction::RegisterMint => { msg!("Instruction: RegisterMint"); process_register_mint(program_id, accounts) } - WeightTableInstruction::InitializeTrackedMints => { + TipRouterInstruction::InitializeTrackedMints => { msg!("Instruction: InitializeTrackedMints"); process_initialize_tracked_mints(program_id, accounts) } diff --git a/program/src/set_config_fees.rs b/program/src/set_config_fees.rs index 595cfff..9f09098 100644 --- a/program/src/set_config_fees.rs +++ b/program/src/set_config_fees.rs @@ -15,15 +15,15 @@ pub fn process_set_config_fees( new_block_engine_fee_bps: Option, new_fee_wallet: Option, ) -> ProgramResult { - let [restaking_config, config, ncn_account, fee_admin, restaking_program_id] = accounts else { + let [restaking_config, config, ncn_account, fee_admin, restaking_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; load_signer(fee_admin, false)?; NcnConfig::load(program_id, ncn_account.key, config, true)?; - Ncn::load(restaking_program_id.key, ncn_account, false)?; - Config::load(restaking_program_id.key, restaking_config, false)?; + Ncn::load(restaking_program.key, ncn_account, false)?; + Config::load(restaking_program.key, restaking_config, false)?; let ncn_epoch_length = { let config_data = restaking_config.data.borrow(); diff --git a/program/src/set_new_admin.rs b/program/src/set_new_admin.rs index be58580..a8715a4 100644 --- a/program/src/set_new_admin.rs +++ b/program/src/set_new_admin.rs @@ -14,14 +14,14 @@ pub fn process_set_new_admin( accounts: &[AccountInfo], role: ConfigAdminRole, ) -> ProgramResult { - let [config, ncn_account, ncn_admin, new_admin, restaking_program_id] = accounts else { + let [config, ncn_account, ncn_admin, new_admin, restaking_program] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); }; load_signer(ncn_admin, false)?; NcnConfig::load(program_id, ncn_account.key, config, true)?; - Ncn::load(restaking_program_id.key, ncn_account, false)?; + Ncn::load(restaking_program.key, ncn_account, false)?; let mut config_data = config.try_borrow_mut_data()?; if config_data[0] != NcnConfig::DISCRIMINATOR { diff --git a/program/src/snapshot_vault_operator_delegation.rs b/program/src/snapshot_vault_operator_delegation.rs new file mode 100644 index 0000000..fdb2dba --- /dev/null +++ b/program/src/snapshot_vault_operator_delegation.rs @@ -0,0 +1,154 @@ +use jito_bytemuck::AccountDeserialize; +use jito_restaking_core::{ + config::Config, ncn::Ncn, ncn_vault_ticket::NcnVaultTicket, operator::Operator, +}; +use jito_tip_router_core::{ + epoch_snapshot::{EpochSnapshot, OperatorSnapshot}, + loaders::load_ncn_epoch, + ncn_config::NcnConfig, + weight_table::WeightTable, +}; +use jito_vault_core::{ + vault::Vault, vault_ncn_ticket::VaultNcnTicket, + vault_operator_delegation::VaultOperatorDelegation, +}; +use solana_program::{ + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, + program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, +}; + +pub fn process_snapshot_vault_operator_delegation( + program_id: &Pubkey, + accounts: &[AccountInfo], + first_slot_of_ncn_epoch: Option, +) -> ProgramResult { + let [ncn_config, restaking_config, ncn, operator, vault, vault_ncn_ticket, ncn_vault_ticket, vault_operator_delegation, weight_table, epoch_snapshot, operator_snapshot, vault_program, restaking_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + if vault_program.key.ne(&jito_vault_program::id()) { + msg!("Incorrect vault program ID"); + return Err(ProgramError::InvalidAccountData); + } + + if restaking_program.key.ne(&jito_restaking_program::id()) { + msg!("Incorrect restaking program ID"); + return Err(ProgramError::InvalidAccountData); + } + + NcnConfig::load(program_id, ncn.key, ncn_config, false)?; + Config::load(restaking_program.key, restaking_config, false)?; + Ncn::load(restaking_program.key, ncn, false)?; + Operator::load(restaking_program.key, operator, false)?; + Vault::load(vault_program.key, vault, false)?; + + VaultOperatorDelegation::load( + vault_program.key, + vault_operator_delegation, + vault, + operator, + false, + )?; + VaultNcnTicket::load(vault_program.key, vault_ncn_ticket, vault, ncn, false)?; + NcnVaultTicket::load(restaking_program.key, ncn_vault_ticket, ncn, vault, false)?; + + if !vault_operator_delegation.data_is_empty() { + VaultOperatorDelegation::load( + vault_program.key, + vault_operator_delegation, + vault, + operator, + false, + )?; + } + + let current_slot = Clock::get()?.slot; + let (ncn_epoch, ncn_epoch_length) = + load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; + + WeightTable::load(program_id, weight_table, ncn, ncn_epoch, false)?; + EpochSnapshot::load(program_id, ncn.key, ncn_epoch, epoch_snapshot, true)?; + OperatorSnapshot::load( + program_id, + operator.key, + ncn.key, + ncn_epoch, + operator_snapshot, + true, + )?; + + let (vault_index, st_mint) = { + let vault_data = vault.data.borrow(); + let vault_account = Vault::try_from_slice_unchecked(&vault_data)?; + (vault_account.vault_index(), vault_account.supported_mint) + }; + + //TODO move to helper function + let is_active: bool = { + let vault_ncn_ticket_data = vault_ncn_ticket.data.borrow(); + let vault_ncn_ticket_account = + VaultNcnTicket::try_from_slice_unchecked(&vault_ncn_ticket_data)?; + + let ncn_vault_ticket_data = ncn_vault_ticket.data.borrow(); + let ncn_vault_ticket_account = + NcnVaultTicket::try_from_slice_unchecked(&ncn_vault_ticket_data)?; + + let vault_ncn_okay = vault_ncn_ticket_account + .state + .is_active(current_slot, ncn_epoch_length); + + let ncn_vault_okay = ncn_vault_ticket_account + .state + .is_active(current_slot, ncn_epoch_length); + + let delegation_dne = vault_operator_delegation.data_is_empty(); + + vault_ncn_okay && ncn_vault_okay && !delegation_dne + }; + + let total_votes: u128 = if is_active { + let vault_operator_delegation_data = vault_operator_delegation.data.borrow(); + let vault_operator_delegation_account = + VaultOperatorDelegation::try_from_slice_unchecked(&vault_operator_delegation_data)?; + + let weight_table_data = weight_table.data.borrow(); + let weight_table_account = WeightTable::try_from_slice_unchecked(&weight_table_data)?; + + OperatorSnapshot::calculate_total_stake_weight( + vault_operator_delegation_account, + weight_table_account, + &st_mint, + )? + } else { + 0u128 + }; + + // Increment vault operator delegation + let mut operator_snapshot_data = operator_snapshot.try_borrow_mut_data()?; + let operator_snapshot_account = + OperatorSnapshot::try_from_slice_unchecked_mut(&mut operator_snapshot_data)?; + + operator_snapshot_account.increment_vault_operator_delegation_registration( + current_slot, + *vault.key, + vault_index, + total_votes, + )?; + + // If operator is finalized, increment operator registration + if operator_snapshot_account.finalized() { + let mut epoch_snapshot_data = epoch_snapshot.try_borrow_mut_data()?; + let epoch_snapshot_account = + EpochSnapshot::try_from_slice_unchecked_mut(&mut epoch_snapshot_data)?; + + epoch_snapshot_account.increment_operator_registration( + current_slot, + operator_snapshot_account.valid_operator_vault_delegations(), + operator_snapshot_account.stake_weight(), + )?; + } + + Ok(()) +} diff --git a/scripts/generate-clients.js b/scripts/generate-clients.js index 73a0d1e..3119c70 100644 --- a/scripts/generate-clients.js +++ b/scripts/generate-clients.js @@ -81,6 +81,22 @@ weightTableKinobi.update(kinobi.bottomUpTransformerVisitor([ }; }, }, + { + // PodBool -> bool + select: (node) => { + return ( + kinobi.isNode(node, "structFieldTypeNode") && + node.type.name === "podBool" + ); + }, + transform: (node) => { + kinobi.assertIsNode(node, "structFieldTypeNode"); + return { + ...node, + type: kinobi.numberTypeNode("bool"), + }; + }, + }, // add 8 byte discriminator to accountNode { select: (node) => {