From e11a049933af4be69d209ca4b892ee03b2e5431f Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Thu, 12 Dec 2024 18:56:16 -0500 Subject: [PATCH 01/10] Realloc instructions --- .../jito_tip_router/accounts/weightTable.ts | 4 +- .../js/jito_tip_router/instructions/index.ts | 4 + .../initializeBaseRewardRouter.ts | 14 +- .../instructions/initializeEpochSnapshot.ts | 14 +- .../instructions/initializeNcnRewardRouter.ts | 14 +- .../initializeOperatorSnapshot.ts | 14 +- .../instructions/initializeWeightTable.ts | 14 +- .../instructions/reallocBallotBox.ts | 241 ++++++ .../instructions/reallocBaseRewardRouter.ts | 251 +++++++ .../instructions/reallocOperatorSnapshot.ts | 330 +++++++++ .../instructions/reallocWeightTable.ts | 257 +++++++ .../snapshotVaultOperatorDelegation.ts | 14 +- .../jito_tip_router/programs/jitoTipRouter.ts | 34 +- .../src/generated/accounts/weight_table.rs | 3 +- .../initialize_base_reward_router.rs | 22 +- .../instructions/initialize_epoch_snapshot.rs | 22 +- .../initialize_ncn_reward_router.rs | 22 +- .../initialize_operator_snapshot.rs | 22 +- .../instructions/initialize_weight_table.rs | 22 +- .../src/generated/instructions/mod.rs | 9 +- .../instructions/realloc_ballot_box.rs | 468 ++++++++++++ .../realloc_base_reward_router.rs | 480 ++++++++++++ .../instructions/realloc_operator_snapshot.rs | 690 ++++++++++++++++++ .../instructions/realloc_weight_table.rs | 515 +++++++++++++ .../snapshot_vault_operator_delegation.rs | 22 +- core/src/ballot_box.rs | 5 + core/src/base_reward_router.rs | 5 +- core/src/constants.rs | 2 + core/src/epoch_snapshot.rs | 14 +- core/src/instruction.rs | 60 +- core/src/lib.rs | 1 + core/src/ncn_config.rs | 4 + core/src/ncn_reward_router.rs | 3 + core/src/tracked_mints.rs | 4 +- core/src/utils.rs | 67 ++ core/src/weight_table.rs | 35 +- idl/jito_tip_router.json | 228 +++++- .../tests/fixtures/test_builder.rs | 135 +++- .../tests/fixtures/tip_router_client.rs | 444 ++++++++--- .../tip_router/admin_update_weight_table.rs | 7 +- .../tests/tip_router/cast_vote.rs | 9 +- .../tests/tip_router/distribute_rewards.rs | 17 +- .../tests/tip_router/initialize_ballot_box.rs | 23 +- .../initialize_base_reward_router.rs | 34 +- .../tip_router/initialize_epoch_snapshot.rs | 2 +- .../initialize_ncn_reward_router.rs | 12 +- .../initialize_operator_snapshot.rs | 50 +- .../tip_router/initialize_weight_table.rs | 31 +- .../tests/tip_router/meta_tests.rs | 33 +- .../tests/tip_router/register_mint.rs | 1 + .../tests/tip_router/set_tie_breaker.rs | 14 +- .../set_tracked_mint_ncn_fee_group.rs | 2 +- .../snapshot_vault_operator_delegation.rs | 30 +- program/src/cast_vote.rs | 7 +- program/src/initialize_ballot_box.rs | 18 +- program/src/initialize_base_reward_router.rs | 32 +- program/src/initialize_epoch_snapshot.rs | 4 +- program/src/initialize_ncn_reward_router.rs | 15 +- program/src/initialize_operator_snapshot.rs | 97 +-- program/src/initialize_weight_table.rs | 21 +- program/src/lib.rs | 69 +- program/src/realloc_ballot_box.rs | 53 ++ program/src/realloc_base_reward_router.rs | 56 ++ program/src/realloc_operator_snapshot.rs | 154 ++++ program/src/realloc_weight_table.rs | 70 ++ .../src/snapshot_vault_operator_delegation.rs | 11 +- 66 files changed, 4810 insertions(+), 571 deletions(-) create mode 100644 clients/js/jito_tip_router/instructions/reallocBallotBox.ts create mode 100644 clients/js/jito_tip_router/instructions/reallocBaseRewardRouter.ts create mode 100644 clients/js/jito_tip_router/instructions/reallocOperatorSnapshot.ts create mode 100644 clients/js/jito_tip_router/instructions/reallocWeightTable.ts create mode 100644 clients/rust/jito_tip_router/src/generated/instructions/realloc_ballot_box.rs create mode 100644 clients/rust/jito_tip_router/src/generated/instructions/realloc_base_reward_router.rs create mode 100644 clients/rust/jito_tip_router/src/generated/instructions/realloc_operator_snapshot.rs create mode 100644 clients/rust/jito_tip_router/src/generated/instructions/realloc_weight_table.rs create mode 100644 core/src/utils.rs create mode 100644 program/src/realloc_ballot_box.rs create mode 100644 program/src/realloc_base_reward_router.rs create mode 100644 program/src/realloc_operator_snapshot.rs create mode 100644 program/src/realloc_weight_table.rs diff --git a/clients/js/jito_tip_router/accounts/weightTable.ts b/clients/js/jito_tip_router/accounts/weightTable.ts index 2e051e99..98614be1 100644 --- a/clients/js/jito_tip_router/accounts/weightTable.ts +++ b/clients/js/jito_tip_router/accounts/weightTable.ts @@ -69,7 +69,7 @@ export function getWeightTableEncoder(): Encoder { ['slotCreated', getU64Encoder()], ['bump', getU8Encoder()], ['reserved', getArrayEncoder(getU8Encoder(), { size: 128 })], - ['table', getArrayEncoder(getWeightEntryEncoder(), { size: 32 })], + ['table', getArrayEncoder(getWeightEntryEncoder(), { size: 64 })], ]); } @@ -81,7 +81,7 @@ export function getWeightTableDecoder(): Decoder { ['slotCreated', getU64Decoder()], ['bump', getU8Decoder()], ['reserved', getArrayDecoder(getU8Decoder(), { size: 128 })], - ['table', getArrayDecoder(getWeightEntryDecoder(), { size: 32 })], + ['table', getArrayDecoder(getWeightEntryDecoder(), { size: 64 })], ]); } diff --git a/clients/js/jito_tip_router/instructions/index.ts b/clients/js/jito_tip_router/instructions/index.ts index fc6d7517..87ffbe28 100644 --- a/clients/js/jito_tip_router/instructions/index.ts +++ b/clients/js/jito_tip_router/instructions/index.ts @@ -20,6 +20,10 @@ export * from './initializeNcnRewardRouter'; export * from './initializeOperatorSnapshot'; export * from './initializeTrackedMints'; export * from './initializeWeightTable'; +export * from './reallocBallotBox'; +export * from './reallocBaseRewardRouter'; +export * from './reallocOperatorSnapshot'; +export * from './reallocWeightTable'; export * from './registerMint'; export * from './routeBaseRewards'; export * from './routeNcnRewards'; diff --git a/clients/js/jito_tip_router/instructions/initializeBaseRewardRouter.ts b/clients/js/jito_tip_router/instructions/initializeBaseRewardRouter.ts index e7482776..9cce92f6 100644 --- a/clients/js/jito_tip_router/instructions/initializeBaseRewardRouter.ts +++ b/clients/js/jito_tip_router/instructions/initializeBaseRewardRouter.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -26,8 +24,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type TransactionSigner, type WritableAccount, @@ -80,18 +76,18 @@ export type InitializeBaseRewardRouterInstruction< export type InitializeBaseRewardRouterInstructionData = { discriminator: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type InitializeBaseRewardRouterInstructionDataArgs = { - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getInitializeBaseRewardRouterInstructionDataEncoder(): Encoder { return transformEncoder( getStructEncoder([ ['discriminator', getU8Encoder()], - ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ['epoch', getU64Encoder()], ]), (value) => ({ ...value, @@ -103,7 +99,7 @@ export function getInitializeBaseRewardRouterInstructionDataEncoder(): Encoder { return getStructDecoder([ ['discriminator', getU8Decoder()], - ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ['epoch', getU64Decoder()], ]); } @@ -131,7 +127,7 @@ export type InitializeBaseRewardRouterInput< payer: TransactionSigner; restakingProgram: Address; systemProgram?: Address; - firstSlotOfNcnEpoch: InitializeBaseRewardRouterInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: InitializeBaseRewardRouterInstructionDataArgs['epoch']; }; export function getInitializeBaseRewardRouterInstruction< diff --git a/clients/js/jito_tip_router/instructions/initializeEpochSnapshot.ts b/clients/js/jito_tip_router/instructions/initializeEpochSnapshot.ts index 7abc93a9..9dee8292 100644 --- a/clients/js/jito_tip_router/instructions/initializeEpochSnapshot.ts +++ b/clients/js/jito_tip_router/instructions/initializeEpochSnapshot.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -26,8 +24,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type TransactionSigner, type WritableAccount, @@ -92,18 +88,18 @@ export type InitializeEpochSnapshotInstruction< export type InitializeEpochSnapshotInstructionData = { discriminator: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type InitializeEpochSnapshotInstructionDataArgs = { - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getInitializeEpochSnapshotInstructionDataEncoder(): Encoder { return transformEncoder( getStructEncoder([ ['discriminator', getU8Encoder()], - ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ['epoch', getU64Encoder()], ]), (value) => ({ ...value, @@ -115,7 +111,7 @@ export function getInitializeEpochSnapshotInstructionDataEncoder(): Encoder { return getStructDecoder([ ['discriminator', getU8Decoder()], - ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ['epoch', getU64Decoder()], ]); } @@ -149,7 +145,7 @@ export type InitializeEpochSnapshotInput< payer: TransactionSigner; restakingProgram: Address; systemProgram?: Address; - firstSlotOfNcnEpoch: InitializeEpochSnapshotInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: InitializeEpochSnapshotInstructionDataArgs['epoch']; }; export function getInitializeEpochSnapshotInstruction< diff --git a/clients/js/jito_tip_router/instructions/initializeNcnRewardRouter.ts b/clients/js/jito_tip_router/instructions/initializeNcnRewardRouter.ts index 72f39946..33c6d45e 100644 --- a/clients/js/jito_tip_router/instructions/initializeNcnRewardRouter.ts +++ b/clients/js/jito_tip_router/instructions/initializeNcnRewardRouter.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -26,8 +24,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type TransactionSigner, type WritableAccount, @@ -85,12 +81,12 @@ export type InitializeNcnRewardRouterInstruction< export type InitializeNcnRewardRouterInstructionData = { discriminator: number; ncnFeeGroup: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type InitializeNcnRewardRouterInstructionDataArgs = { ncnFeeGroup: number; - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getInitializeNcnRewardRouterInstructionDataEncoder(): Encoder { @@ -98,7 +94,7 @@ export function getInitializeNcnRewardRouterInstructionDataEncoder(): Encoder ({ ...value, @@ -111,7 +107,7 @@ export function getInitializeNcnRewardRouterInstructionDataDecoder(): Decoder; systemProgram?: Address; ncnFeeGroup: InitializeNcnRewardRouterInstructionDataArgs['ncnFeeGroup']; - firstSlotOfNcnEpoch: InitializeNcnRewardRouterInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: InitializeNcnRewardRouterInstructionDataArgs['epoch']; }; export function getInitializeNcnRewardRouterInstruction< diff --git a/clients/js/jito_tip_router/instructions/initializeOperatorSnapshot.ts b/clients/js/jito_tip_router/instructions/initializeOperatorSnapshot.ts index ab51067e..80f15fac 100644 --- a/clients/js/jito_tip_router/instructions/initializeOperatorSnapshot.ts +++ b/clients/js/jito_tip_router/instructions/initializeOperatorSnapshot.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -26,8 +24,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type TransactionSigner, type WritableAccount, @@ -96,18 +92,18 @@ export type InitializeOperatorSnapshotInstruction< export type InitializeOperatorSnapshotInstructionData = { discriminator: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type InitializeOperatorSnapshotInstructionDataArgs = { - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getInitializeOperatorSnapshotInstructionDataEncoder(): Encoder { return transformEncoder( getStructEncoder([ ['discriminator', getU8Encoder()], - ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ['epoch', getU64Encoder()], ]), (value) => ({ ...value, @@ -119,7 +115,7 @@ export function getInitializeOperatorSnapshotInstructionDataEncoder(): Encoder { return getStructDecoder([ ['discriminator', getU8Decoder()], - ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ['epoch', getU64Decoder()], ]); } @@ -155,7 +151,7 @@ export type InitializeOperatorSnapshotInput< payer: TransactionSigner; restakingProgram: Address; systemProgram?: Address; - firstSlotOfNcnEpoch: InitializeOperatorSnapshotInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: InitializeOperatorSnapshotInstructionDataArgs['epoch']; }; export function getInitializeOperatorSnapshotInstruction< diff --git a/clients/js/jito_tip_router/instructions/initializeWeightTable.ts b/clients/js/jito_tip_router/instructions/initializeWeightTable.ts index 4b948ed1..0f4efe75 100644 --- a/clients/js/jito_tip_router/instructions/initializeWeightTable.ts +++ b/clients/js/jito_tip_router/instructions/initializeWeightTable.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -26,8 +24,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type TransactionSigner, type WritableAccount, @@ -84,18 +80,18 @@ export type InitializeWeightTableInstruction< export type InitializeWeightTableInstructionData = { discriminator: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type InitializeWeightTableInstructionDataArgs = { - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getInitializeWeightTableInstructionDataEncoder(): Encoder { return transformEncoder( getStructEncoder([ ['discriminator', getU8Encoder()], - ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ['epoch', getU64Encoder()], ]), (value) => ({ ...value, @@ -107,7 +103,7 @@ export function getInitializeWeightTableInstructionDataEncoder(): Encoder { return getStructDecoder([ ['discriminator', getU8Decoder()], - ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ['epoch', getU64Decoder()], ]); } @@ -137,7 +133,7 @@ export type InitializeWeightTableInput< payer: TransactionSigner; restakingProgram: Address; systemProgram?: Address; - firstSlotOfNcnEpoch: InitializeWeightTableInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: InitializeWeightTableInstructionDataArgs['epoch']; }; export function getInitializeWeightTableInstruction< diff --git a/clients/js/jito_tip_router/instructions/reallocBallotBox.ts b/clients/js/jito_tip_router/instructions/reallocBallotBox.ts new file mode 100644 index 00000000..ce036fbc --- /dev/null +++ b/clients/js/jito_tip_router/instructions/reallocBallotBox.ts @@ -0,0 +1,241 @@ +/** + * 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, + 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 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 REALLOC_BALLOT_BOX_DISCRIMINATOR = 23; + +export function getReallocBallotBoxDiscriminatorBytes() { + return getU8Encoder().encode(REALLOC_BALLOT_BOX_DISCRIMINATOR); +} + +export type ReallocBallotBoxInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountNcnConfig extends string | IAccountMeta = string, + TAccountBallotBox extends string | IAccountMeta = string, + TAccountNcn extends string | IAccountMeta = string, + TAccountPayer extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountNcnConfig extends string + ? ReadonlyAccount + : TAccountNcnConfig, + TAccountBallotBox extends string + ? WritableAccount + : TAccountBallotBox, + TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, + TAccountPayer extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountPayer, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type ReallocBallotBoxInstructionData = { + discriminator: number; + epoch: bigint; +}; + +export type ReallocBallotBoxInstructionDataArgs = { epoch: number | bigint }; + +export function getReallocBallotBoxInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['epoch', getU64Encoder()], + ]), + (value) => ({ ...value, discriminator: REALLOC_BALLOT_BOX_DISCRIMINATOR }) + ); +} + +export function getReallocBallotBoxInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['epoch', getU64Decoder()], + ]); +} + +export function getReallocBallotBoxInstructionDataCodec(): Codec< + ReallocBallotBoxInstructionDataArgs, + ReallocBallotBoxInstructionData +> { + return combineCodec( + getReallocBallotBoxInstructionDataEncoder(), + getReallocBallotBoxInstructionDataDecoder() + ); +} + +export type ReallocBallotBoxInput< + TAccountNcnConfig extends string = string, + TAccountBallotBox extends string = string, + TAccountNcn extends string = string, + TAccountPayer extends string = string, + TAccountSystemProgram extends string = string, +> = { + ncnConfig: Address; + ballotBox: Address; + ncn: Address; + payer: TransactionSigner; + systemProgram?: Address; + epoch: ReallocBallotBoxInstructionDataArgs['epoch']; +}; + +export function getReallocBallotBoxInstruction< + TAccountNcnConfig extends string, + TAccountBallotBox extends string, + TAccountNcn extends string, + TAccountPayer extends string, + TAccountSystemProgram extends string, + TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, +>( + input: ReallocBallotBoxInput< + TAccountNcnConfig, + TAccountBallotBox, + TAccountNcn, + TAccountPayer, + TAccountSystemProgram + >, + config?: { programAddress?: TProgramAddress } +): ReallocBallotBoxInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountBallotBox, + TAccountNcn, + TAccountPayer, + TAccountSystemProgram +> { + // Program address. + const programAddress = + config?.programAddress ?? JITO_TIP_ROUTER_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + ncnConfig: { value: input.ncnConfig ?? null, isWritable: false }, + ballotBox: { value: input.ballotBox ?? null, isWritable: true }, + ncn: { value: input.ncn ?? null, isWritable: false }, + payer: { value: input.payer ?? null, isWritable: true }, + 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.ballotBox), + getAccountMeta(accounts.ncn), + getAccountMeta(accounts.payer), + getAccountMeta(accounts.systemProgram), + ], + programAddress, + data: getReallocBallotBoxInstructionDataEncoder().encode( + args as ReallocBallotBoxInstructionDataArgs + ), + } as ReallocBallotBoxInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountBallotBox, + TAccountNcn, + TAccountPayer, + TAccountSystemProgram + >; + + return instruction; +} + +export type ParsedReallocBallotBoxInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], +> = { + programAddress: Address; + accounts: { + ncnConfig: TAccountMetas[0]; + ballotBox: TAccountMetas[1]; + ncn: TAccountMetas[2]; + payer: TAccountMetas[3]; + systemProgram: TAccountMetas[4]; + }; + data: ReallocBallotBoxInstructionData; +}; + +export function parseReallocBallotBoxInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedReallocBallotBoxInstruction { + if (instruction.accounts.length < 5) { + // 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(), + ballotBox: getNextAccount(), + ncn: getNextAccount(), + payer: getNextAccount(), + systemProgram: getNextAccount(), + }, + data: getReallocBallotBoxInstructionDataDecoder().decode(instruction.data), + }; +} diff --git a/clients/js/jito_tip_router/instructions/reallocBaseRewardRouter.ts b/clients/js/jito_tip_router/instructions/reallocBaseRewardRouter.ts new file mode 100644 index 00000000..9dbed083 --- /dev/null +++ b/clients/js/jito_tip_router/instructions/reallocBaseRewardRouter.ts @@ -0,0 +1,251 @@ +/** + * 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, + 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 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 REALLOC_BASE_REWARD_ROUTER_DISCRIMINATOR = 25; + +export function getReallocBaseRewardRouterDiscriminatorBytes() { + return getU8Encoder().encode(REALLOC_BASE_REWARD_ROUTER_DISCRIMINATOR); +} + +export type ReallocBaseRewardRouterInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountNcnConfig extends string | IAccountMeta = string, + TAccountBaseRewardRouter extends string | IAccountMeta = string, + TAccountNcn extends string | IAccountMeta = string, + TAccountPayer extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountNcnConfig extends string + ? ReadonlyAccount + : TAccountNcnConfig, + TAccountBaseRewardRouter extends string + ? WritableAccount + : TAccountBaseRewardRouter, + TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, + TAccountPayer extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountPayer, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type ReallocBaseRewardRouterInstructionData = { + discriminator: number; + epoch: bigint; +}; + +export type ReallocBaseRewardRouterInstructionDataArgs = { + epoch: number | bigint; +}; + +export function getReallocBaseRewardRouterInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['epoch', getU64Encoder()], + ]), + (value) => ({ + ...value, + discriminator: REALLOC_BASE_REWARD_ROUTER_DISCRIMINATOR, + }) + ); +} + +export function getReallocBaseRewardRouterInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['epoch', getU64Decoder()], + ]); +} + +export function getReallocBaseRewardRouterInstructionDataCodec(): Codec< + ReallocBaseRewardRouterInstructionDataArgs, + ReallocBaseRewardRouterInstructionData +> { + return combineCodec( + getReallocBaseRewardRouterInstructionDataEncoder(), + getReallocBaseRewardRouterInstructionDataDecoder() + ); +} + +export type ReallocBaseRewardRouterInput< + TAccountNcnConfig extends string = string, + TAccountBaseRewardRouter extends string = string, + TAccountNcn extends string = string, + TAccountPayer extends string = string, + TAccountSystemProgram extends string = string, +> = { + ncnConfig: Address; + baseRewardRouter: Address; + ncn: Address; + payer: TransactionSigner; + systemProgram?: Address; + epoch: ReallocBaseRewardRouterInstructionDataArgs['epoch']; +}; + +export function getReallocBaseRewardRouterInstruction< + TAccountNcnConfig extends string, + TAccountBaseRewardRouter extends string, + TAccountNcn extends string, + TAccountPayer extends string, + TAccountSystemProgram extends string, + TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, +>( + input: ReallocBaseRewardRouterInput< + TAccountNcnConfig, + TAccountBaseRewardRouter, + TAccountNcn, + TAccountPayer, + TAccountSystemProgram + >, + config?: { programAddress?: TProgramAddress } +): ReallocBaseRewardRouterInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountBaseRewardRouter, + TAccountNcn, + TAccountPayer, + TAccountSystemProgram +> { + // Program address. + const programAddress = + config?.programAddress ?? JITO_TIP_ROUTER_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + ncnConfig: { value: input.ncnConfig ?? null, isWritable: false }, + baseRewardRouter: { + value: input.baseRewardRouter ?? null, + isWritable: true, + }, + ncn: { value: input.ncn ?? null, isWritable: false }, + payer: { value: input.payer ?? null, isWritable: true }, + 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.baseRewardRouter), + getAccountMeta(accounts.ncn), + getAccountMeta(accounts.payer), + getAccountMeta(accounts.systemProgram), + ], + programAddress, + data: getReallocBaseRewardRouterInstructionDataEncoder().encode( + args as ReallocBaseRewardRouterInstructionDataArgs + ), + } as ReallocBaseRewardRouterInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountBaseRewardRouter, + TAccountNcn, + TAccountPayer, + TAccountSystemProgram + >; + + return instruction; +} + +export type ParsedReallocBaseRewardRouterInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], +> = { + programAddress: Address; + accounts: { + ncnConfig: TAccountMetas[0]; + baseRewardRouter: TAccountMetas[1]; + ncn: TAccountMetas[2]; + payer: TAccountMetas[3]; + systemProgram: TAccountMetas[4]; + }; + data: ReallocBaseRewardRouterInstructionData; +}; + +export function parseReallocBaseRewardRouterInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedReallocBaseRewardRouterInstruction { + if (instruction.accounts.length < 5) { + // 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(), + baseRewardRouter: getNextAccount(), + ncn: getNextAccount(), + payer: getNextAccount(), + systemProgram: getNextAccount(), + }, + data: getReallocBaseRewardRouterInstructionDataDecoder().decode( + instruction.data + ), + }; +} diff --git a/clients/js/jito_tip_router/instructions/reallocOperatorSnapshot.ts b/clients/js/jito_tip_router/instructions/reallocOperatorSnapshot.ts new file mode 100644 index 00000000..a44af737 --- /dev/null +++ b/clients/js/jito_tip_router/instructions/reallocOperatorSnapshot.ts @@ -0,0 +1,330 @@ +/** + * 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, + 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 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 REALLOC_OPERATOR_SNAPSHOT_DISCRIMINATOR = 24; + +export function getReallocOperatorSnapshotDiscriminatorBytes() { + return getU8Encoder().encode(REALLOC_OPERATOR_SNAPSHOT_DISCRIMINATOR); +} + +export type ReallocOperatorSnapshotInstruction< + 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 ReallocOperatorSnapshotInstructionData = { + discriminator: number; + epoch: bigint; +}; + +export type ReallocOperatorSnapshotInstructionDataArgs = { + epoch: number | bigint; +}; + +export function getReallocOperatorSnapshotInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['epoch', getU64Encoder()], + ]), + (value) => ({ + ...value, + discriminator: REALLOC_OPERATOR_SNAPSHOT_DISCRIMINATOR, + }) + ); +} + +export function getReallocOperatorSnapshotInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['epoch', getU64Decoder()], + ]); +} + +export function getReallocOperatorSnapshotInstructionDataCodec(): Codec< + ReallocOperatorSnapshotInstructionDataArgs, + ReallocOperatorSnapshotInstructionData +> { + return combineCodec( + getReallocOperatorSnapshotInstructionDataEncoder(), + getReallocOperatorSnapshotInstructionDataDecoder() + ); +} + +export type ReallocOperatorSnapshotInput< + 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; + epoch: ReallocOperatorSnapshotInstructionDataArgs['epoch']; +}; + +export function getReallocOperatorSnapshotInstruction< + 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: ReallocOperatorSnapshotInput< + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountOperator, + TAccountNcnOperatorState, + TAccountEpochSnapshot, + TAccountOperatorSnapshot, + TAccountPayer, + TAccountRestakingProgram, + TAccountSystemProgram + >, + config?: { programAddress?: TProgramAddress } +): ReallocOperatorSnapshotInstruction< + 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: getReallocOperatorSnapshotInstructionDataEncoder().encode( + args as ReallocOperatorSnapshotInstructionDataArgs + ), + } as ReallocOperatorSnapshotInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountRestakingConfig, + TAccountNcn, + TAccountOperator, + TAccountNcnOperatorState, + TAccountEpochSnapshot, + TAccountOperatorSnapshot, + TAccountPayer, + TAccountRestakingProgram, + TAccountSystemProgram + >; + + return instruction; +} + +export type ParsedReallocOperatorSnapshotInstruction< + 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: ReallocOperatorSnapshotInstructionData; +}; + +export function parseReallocOperatorSnapshotInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedReallocOperatorSnapshotInstruction { + 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: getReallocOperatorSnapshotInstructionDataDecoder().decode( + instruction.data + ), + }; +} diff --git a/clients/js/jito_tip_router/instructions/reallocWeightTable.ts b/clients/js/jito_tip_router/instructions/reallocWeightTable.ts new file mode 100644 index 00000000..70e66059 --- /dev/null +++ b/clients/js/jito_tip_router/instructions/reallocWeightTable.ts @@ -0,0 +1,257 @@ +/** + * 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, + 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 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 REALLOC_WEIGHT_TABLE_DISCRIMINATOR = 26; + +export function getReallocWeightTableDiscriminatorBytes() { + return getU8Encoder().encode(REALLOC_WEIGHT_TABLE_DISCRIMINATOR); +} + +export type ReallocWeightTableInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountNcnConfig extends string | IAccountMeta = string, + TAccountWeightTable extends string | IAccountMeta = string, + TAccountNcn extends string | IAccountMeta = string, + TAccountTrackedMints extends string | IAccountMeta = string, + TAccountPayer extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountNcnConfig extends string + ? ReadonlyAccount + : TAccountNcnConfig, + TAccountWeightTable extends string + ? WritableAccount + : TAccountWeightTable, + TAccountNcn extends string ? ReadonlyAccount : TAccountNcn, + TAccountTrackedMints extends string + ? ReadonlyAccount + : TAccountTrackedMints, + TAccountPayer extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountPayer, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type ReallocWeightTableInstructionData = { + discriminator: number; + epoch: bigint; +}; + +export type ReallocWeightTableInstructionDataArgs = { epoch: number | bigint }; + +export function getReallocWeightTableInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['epoch', getU64Encoder()], + ]), + (value) => ({ ...value, discriminator: REALLOC_WEIGHT_TABLE_DISCRIMINATOR }) + ); +} + +export function getReallocWeightTableInstructionDataDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['epoch', getU64Decoder()], + ]); +} + +export function getReallocWeightTableInstructionDataCodec(): Codec< + ReallocWeightTableInstructionDataArgs, + ReallocWeightTableInstructionData +> { + return combineCodec( + getReallocWeightTableInstructionDataEncoder(), + getReallocWeightTableInstructionDataDecoder() + ); +} + +export type ReallocWeightTableInput< + TAccountNcnConfig extends string = string, + TAccountWeightTable extends string = string, + TAccountNcn extends string = string, + TAccountTrackedMints extends string = string, + TAccountPayer extends string = string, + TAccountSystemProgram extends string = string, +> = { + ncnConfig: Address; + weightTable: Address; + ncn: Address; + trackedMints: Address; + payer: TransactionSigner; + systemProgram?: Address; + epoch: ReallocWeightTableInstructionDataArgs['epoch']; +}; + +export function getReallocWeightTableInstruction< + TAccountNcnConfig extends string, + TAccountWeightTable extends string, + TAccountNcn extends string, + TAccountTrackedMints extends string, + TAccountPayer extends string, + TAccountSystemProgram extends string, + TProgramAddress extends Address = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, +>( + input: ReallocWeightTableInput< + TAccountNcnConfig, + TAccountWeightTable, + TAccountNcn, + TAccountTrackedMints, + TAccountPayer, + TAccountSystemProgram + >, + config?: { programAddress?: TProgramAddress } +): ReallocWeightTableInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountWeightTable, + TAccountNcn, + TAccountTrackedMints, + TAccountPayer, + TAccountSystemProgram +> { + // Program address. + const programAddress = + config?.programAddress ?? JITO_TIP_ROUTER_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + ncnConfig: { value: input.ncnConfig ?? null, isWritable: false }, + weightTable: { value: input.weightTable ?? null, isWritable: true }, + ncn: { value: input.ncn ?? null, isWritable: false }, + trackedMints: { value: input.trackedMints ?? null, isWritable: false }, + payer: { value: input.payer ?? null, isWritable: true }, + 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.weightTable), + getAccountMeta(accounts.ncn), + getAccountMeta(accounts.trackedMints), + getAccountMeta(accounts.payer), + getAccountMeta(accounts.systemProgram), + ], + programAddress, + data: getReallocWeightTableInstructionDataEncoder().encode( + args as ReallocWeightTableInstructionDataArgs + ), + } as ReallocWeightTableInstruction< + TProgramAddress, + TAccountNcnConfig, + TAccountWeightTable, + TAccountNcn, + TAccountTrackedMints, + TAccountPayer, + TAccountSystemProgram + >; + + return instruction; +} + +export type ParsedReallocWeightTableInstruction< + TProgram extends string = typeof JITO_TIP_ROUTER_PROGRAM_ADDRESS, + TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[], +> = { + programAddress: Address; + accounts: { + ncnConfig: TAccountMetas[0]; + weightTable: TAccountMetas[1]; + ncn: TAccountMetas[2]; + trackedMints: TAccountMetas[3]; + payer: TAccountMetas[4]; + systemProgram: TAccountMetas[5]; + }; + data: ReallocWeightTableInstructionData; +}; + +export function parseReallocWeightTableInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedReallocWeightTableInstruction { + if (instruction.accounts.length < 6) { + // 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(), + weightTable: getNextAccount(), + ncn: getNextAccount(), + trackedMints: getNextAccount(), + payer: getNextAccount(), + systemProgram: getNextAccount(), + }, + data: getReallocWeightTableInstructionDataDecoder().decode( + instruction.data + ), + }; +} diff --git a/clients/js/jito_tip_router/instructions/snapshotVaultOperatorDelegation.ts b/clients/js/jito_tip_router/instructions/snapshotVaultOperatorDelegation.ts index ef6b735c..5d41d838 100644 --- a/clients/js/jito_tip_router/instructions/snapshotVaultOperatorDelegation.ts +++ b/clients/js/jito_tip_router/instructions/snapshotVaultOperatorDelegation.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -25,8 +23,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type WritableAccount, } from '@solana/web3.js'; @@ -110,18 +106,18 @@ export type SnapshotVaultOperatorDelegationInstruction< export type SnapshotVaultOperatorDelegationInstructionData = { discriminator: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type SnapshotVaultOperatorDelegationInstructionDataArgs = { - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getSnapshotVaultOperatorDelegationInstructionDataEncoder(): Encoder { return transformEncoder( getStructEncoder([ ['discriminator', getU8Encoder()], - ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ['epoch', getU64Encoder()], ]), (value) => ({ ...value, @@ -133,7 +129,7 @@ export function getSnapshotVaultOperatorDelegationInstructionDataEncoder(): Enco export function getSnapshotVaultOperatorDelegationInstructionDataDecoder(): Decoder { return getStructDecoder([ ['discriminator', getU8Decoder()], - ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ['epoch', getU64Decoder()], ]); } @@ -177,7 +173,7 @@ export type SnapshotVaultOperatorDelegationInput< operatorSnapshot: Address; vaultProgram: Address; restakingProgram: Address; - firstSlotOfNcnEpoch: SnapshotVaultOperatorDelegationInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: SnapshotVaultOperatorDelegationInstructionDataArgs['epoch']; }; export function getSnapshotVaultOperatorDelegationInstruction< diff --git a/clients/js/jito_tip_router/programs/jitoTipRouter.ts b/clients/js/jito_tip_router/programs/jitoTipRouter.ts index a024c0a3..fe1eff91 100644 --- a/clients/js/jito_tip_router/programs/jitoTipRouter.ts +++ b/clients/js/jito_tip_router/programs/jitoTipRouter.ts @@ -27,6 +27,10 @@ import { type ParsedInitializeOperatorSnapshotInstruction, type ParsedInitializeTrackedMintsInstruction, type ParsedInitializeWeightTableInstruction, + type ParsedReallocBallotBoxInstruction, + type ParsedReallocBaseRewardRouterInstruction, + type ParsedReallocOperatorSnapshotInstruction, + type ParsedReallocWeightTableInstruction, type ParsedRegisterMintInstruction, type ParsedRouteBaseRewardsInstruction, type ParsedRouteNcnRewardsInstruction, @@ -76,6 +80,10 @@ export enum JitoTipRouterInstruction { CastVote, SetMerkleRoot, SetTieBreaker, + ReallocBallotBox, + ReallocOperatorSnapshot, + ReallocBaseRewardRouter, + ReallocWeightTable, } export function identifyJitoTipRouterInstruction( @@ -151,6 +159,18 @@ export function identifyJitoTipRouterInstruction( if (containsBytes(data, getU8Encoder().encode(22), 0)) { return JitoTipRouterInstruction.SetTieBreaker; } + if (containsBytes(data, getU8Encoder().encode(23), 0)) { + return JitoTipRouterInstruction.ReallocBallotBox; + } + if (containsBytes(data, getU8Encoder().encode(24), 0)) { + return JitoTipRouterInstruction.ReallocOperatorSnapshot; + } + if (containsBytes(data, getU8Encoder().encode(25), 0)) { + return JitoTipRouterInstruction.ReallocBaseRewardRouter; + } + if (containsBytes(data, getU8Encoder().encode(26), 0)) { + return JitoTipRouterInstruction.ReallocWeightTable; + } throw new Error( 'The provided instruction could not be identified as a jitoTipRouter instruction.' ); @@ -227,4 +247,16 @@ export type ParsedJitoTipRouterInstruction< } & ParsedSetMerkleRootInstruction) | ({ instructionType: JitoTipRouterInstruction.SetTieBreaker; - } & ParsedSetTieBreakerInstruction); + } & ParsedSetTieBreakerInstruction) + | ({ + instructionType: JitoTipRouterInstruction.ReallocBallotBox; + } & ParsedReallocBallotBoxInstruction) + | ({ + instructionType: JitoTipRouterInstruction.ReallocOperatorSnapshot; + } & ParsedReallocOperatorSnapshotInstruction) + | ({ + instructionType: JitoTipRouterInstruction.ReallocBaseRewardRouter; + } & ParsedReallocBaseRewardRouterInstruction) + | ({ + instructionType: JitoTipRouterInstruction.ReallocWeightTable; + } & ParsedReallocWeightTableInstruction); diff --git a/clients/rust/jito_tip_router/src/generated/accounts/weight_table.rs b/clients/rust/jito_tip_router/src/generated/accounts/weight_table.rs index 9625c302..b272b189 100644 --- a/clients/rust/jito_tip_router/src/generated/accounts/weight_table.rs +++ b/clients/rust/jito_tip_router/src/generated/accounts/weight_table.rs @@ -23,7 +23,8 @@ pub struct WeightTable { pub bump: u8, #[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))] pub reserved: [u8; 128], - pub table: [WeightEntry; 32], + #[cfg_attr(feature = "serde", serde(with = "serde_big_array::BigArray"))] + pub table: [WeightEntry; 64], } impl WeightTable { diff --git a/clients/rust/jito_tip_router/src/generated/instructions/initialize_base_reward_router.rs b/clients/rust/jito_tip_router/src/generated/instructions/initialize_base_reward_router.rs index 2a1e5ede..7cac86f3 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/initialize_base_reward_router.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/initialize_base_reward_router.rs @@ -92,7 +92,7 @@ impl Default for InitializeBaseRewardRouterInstructionData { #[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InitializeBaseRewardRouterInstructionArgs { - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `InitializeBaseRewardRouter`. @@ -113,7 +113,7 @@ pub struct InitializeBaseRewardRouterBuilder { payer: Option, restaking_program: Option, system_program: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -161,10 +161,9 @@ impl InitializeBaseRewardRouterBuilder { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -202,7 +201,7 @@ impl InitializeBaseRewardRouterBuilder { .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), }; let args = InitializeBaseRewardRouterInstructionArgs { - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -382,7 +381,7 @@ impl<'a, 'b> InitializeBaseRewardRouterCpiBuilder<'a, 'b> { payer: None, restaking_program: None, system_program: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -429,10 +428,9 @@ impl<'a, 'b> InitializeBaseRewardRouterCpiBuilder<'a, 'b> { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -477,7 +475,7 @@ impl<'a, 'b> InitializeBaseRewardRouterCpiBuilder<'a, 'b> { signers_seeds: &[&[&[u8]]], ) -> solana_program::entrypoint::ProgramResult { let args = InitializeBaseRewardRouterInstructionArgs { - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = InitializeBaseRewardRouterCpi { __program: self.instruction.__program, @@ -523,7 +521,7 @@ struct InitializeBaseRewardRouterCpiBuilderInstruction<'a, 'b> { 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, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, 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 index 9660486a..c88b51fe 100644 --- 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 @@ -110,7 +110,7 @@ impl Default for InitializeEpochSnapshotInstructionData { #[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, + pub epoch: u64, } /// Instruction builder for `InitializeEpochSnapshot`. @@ -137,7 +137,7 @@ pub struct InitializeEpochSnapshotBuilder { payer: Option, restaking_program: Option, system_program: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -197,10 +197,9 @@ impl InitializeEpochSnapshotBuilder { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -239,7 +238,7 @@ impl InitializeEpochSnapshotBuilder { .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), }; let args = InitializeEpochSnapshotInstructionArgs { - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -455,7 +454,7 @@ impl<'a, 'b> InitializeEpochSnapshotCpiBuilder<'a, 'b> { payer: None, restaking_program: None, system_program: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -526,10 +525,9 @@ impl<'a, 'b> InitializeEpochSnapshotCpiBuilder<'a, 'b> { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -574,7 +572,7 @@ impl<'a, 'b> InitializeEpochSnapshotCpiBuilder<'a, 'b> { signers_seeds: &[&[&[u8]]], ) -> solana_program::entrypoint::ProgramResult { let args = InitializeEpochSnapshotInstructionArgs { - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = InitializeEpochSnapshotCpi { __program: self.instruction.__program, @@ -635,7 +633,7 @@ struct InitializeEpochSnapshotCpiBuilderInstruction<'a, 'b> { 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, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/initialize_ncn_reward_router.rs b/clients/rust/jito_tip_router/src/generated/instructions/initialize_ncn_reward_router.rs index 8ba880fd..b19738a5 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/initialize_ncn_reward_router.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/initialize_ncn_reward_router.rs @@ -99,7 +99,7 @@ impl Default for InitializeNcnRewardRouterInstructionData { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InitializeNcnRewardRouterInstructionArgs { pub ncn_fee_group: u8, - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `InitializeNcnRewardRouter`. @@ -123,7 +123,7 @@ pub struct InitializeNcnRewardRouterBuilder { restaking_program: Option, system_program: Option, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -181,10 +181,9 @@ impl InitializeNcnRewardRouterBuilder { self.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -227,7 +226,7 @@ impl InitializeNcnRewardRouterBuilder { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -420,7 +419,7 @@ impl<'a, 'b> InitializeNcnRewardRouterCpiBuilder<'a, 'b> { restaking_program: None, system_program: None, ncn_fee_group: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -480,10 +479,9 @@ impl<'a, 'b> InitializeNcnRewardRouterCpiBuilder<'a, 'b> { self.instruction.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -533,7 +531,7 @@ impl<'a, 'b> InitializeNcnRewardRouterCpiBuilder<'a, 'b> { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = InitializeNcnRewardRouterCpi { __program: self.instruction.__program, @@ -583,7 +581,7 @@ struct InitializeNcnRewardRouterCpiBuilderInstruction<'a, 'b> { restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, 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 index a8218ee9..e3340c9c 100644 --- 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 @@ -116,7 +116,7 @@ impl Default for InitializeOperatorSnapshotInstructionData { #[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, + pub epoch: u64, } /// Instruction builder for `InitializeOperatorSnapshot`. @@ -145,7 +145,7 @@ pub struct InitializeOperatorSnapshotBuilder { payer: Option, restaking_program: Option, system_program: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -216,10 +216,9 @@ impl InitializeOperatorSnapshotBuilder { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -263,7 +262,7 @@ impl InitializeOperatorSnapshotBuilder { .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), }; let args = InitializeOperatorSnapshotInstructionArgs { - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -491,7 +490,7 @@ impl<'a, 'b> InitializeOperatorSnapshotCpiBuilder<'a, 'b> { payer: None, restaking_program: None, system_program: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -570,10 +569,9 @@ impl<'a, 'b> InitializeOperatorSnapshotCpiBuilder<'a, 'b> { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -618,7 +616,7 @@ impl<'a, 'b> InitializeOperatorSnapshotCpiBuilder<'a, 'b> { signers_seeds: &[&[&[u8]]], ) -> solana_program::entrypoint::ProgramResult { let args = InitializeOperatorSnapshotInstructionArgs { - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = InitializeOperatorSnapshotCpi { __program: self.instruction.__program, @@ -682,7 +680,7 @@ struct InitializeOperatorSnapshotCpiBuilderInstruction<'a, 'b> { 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, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, 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 b6bb6f54..32f48a60 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 @@ -98,7 +98,7 @@ impl Default for InitializeWeightTableInstructionData { #[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct InitializeWeightTableInstructionArgs { - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `InitializeWeightTable`. @@ -121,7 +121,7 @@ pub struct InitializeWeightTableBuilder { payer: Option, restaking_program: Option, system_program: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -171,10 +171,9 @@ impl InitializeWeightTableBuilder { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -211,7 +210,7 @@ impl InitializeWeightTableBuilder { .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), }; let args = InitializeWeightTableInstructionArgs { - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -403,7 +402,7 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { payer: None, restaking_program: None, system_program: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -458,10 +457,9 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -506,7 +504,7 @@ impl<'a, 'b> InitializeWeightTableCpiBuilder<'a, 'b> { signers_seeds: &[&[&[u8]]], ) -> solana_program::entrypoint::ProgramResult { let args = InitializeWeightTableInstructionArgs { - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = InitializeWeightTableCpi { __program: self.instruction.__program, @@ -558,7 +556,7 @@ struct InitializeWeightTableCpiBuilderInstruction<'a, 'b> { 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, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, 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 43ace98f..a7e9a92e 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/mod.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/mod.rs @@ -18,6 +18,10 @@ pub(crate) mod r#initialize_ncn_reward_router; 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#realloc_ballot_box; +pub(crate) mod r#realloc_base_reward_router; +pub(crate) mod r#realloc_operator_snapshot; +pub(crate) mod r#realloc_weight_table; pub(crate) mod r#register_mint; pub(crate) mod r#route_base_rewards; pub(crate) mod r#route_ncn_rewards; @@ -35,7 +39,8 @@ pub use self::{ r#initialize_base_reward_router::*, r#initialize_epoch_snapshot::*, r#initialize_n_c_n_config::*, r#initialize_ncn_reward_router::*, r#initialize_operator_snapshot::*, r#initialize_tracked_mints::*, r#initialize_weight_table::*, - r#register_mint::*, r#route_base_rewards::*, r#route_ncn_rewards::*, r#set_config_fees::*, - r#set_merkle_root::*, r#set_new_admin::*, r#set_tie_breaker::*, + r#realloc_ballot_box::*, r#realloc_base_reward_router::*, r#realloc_operator_snapshot::*, + r#realloc_weight_table::*, r#register_mint::*, r#route_base_rewards::*, r#route_ncn_rewards::*, + r#set_config_fees::*, r#set_merkle_root::*, r#set_new_admin::*, r#set_tie_breaker::*, r#set_tracked_mint_ncn_fee_group::*, r#snapshot_vault_operator_delegation::*, }; diff --git a/clients/rust/jito_tip_router/src/generated/instructions/realloc_ballot_box.rs b/clients/rust/jito_tip_router/src/generated/instructions/realloc_ballot_box.rs new file mode 100644 index 00000000..89280b83 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/instructions/realloc_ballot_box.rs @@ -0,0 +1,468 @@ +//! 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 ReallocBallotBox { + pub ncn_config: solana_program::pubkey::Pubkey, + + pub ballot_box: solana_program::pubkey::Pubkey, + + pub ncn: solana_program::pubkey::Pubkey, + + pub payer: solana_program::pubkey::Pubkey, + + pub system_program: solana_program::pubkey::Pubkey, +} + +impl ReallocBallotBox { + pub fn instruction( + &self, + args: ReallocBallotBoxInstructionArgs, + ) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: ReallocBallotBoxInstructionArgs, + remaining_accounts: &[solana_program::instruction::AccountMeta], + ) -> solana_program::instruction::Instruction { + let mut accounts = Vec::with_capacity(5 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.ballot_box, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn, false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.payer, true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.system_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = ReallocBallotBoxInstructionData::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 ReallocBallotBoxInstructionData { + discriminator: u8, +} + +impl ReallocBallotBoxInstructionData { + pub fn new() -> Self { + Self { discriminator: 23 } + } +} + +impl Default for ReallocBallotBoxInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ReallocBallotBoxInstructionArgs { + pub epoch: u64, +} + +/// Instruction builder for `ReallocBallotBox`. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[writable]` ballot_box +/// 2. `[]` ncn +/// 3. `[writable, signer]` payer +/// 4. `[optional]` system_program (default to `11111111111111111111111111111111`) +#[derive(Clone, Debug, Default)] +pub struct ReallocBallotBoxBuilder { + ncn_config: Option, + ballot_box: Option, + ncn: Option, + payer: Option, + system_program: Option, + epoch: Option, + __remaining_accounts: Vec, +} + +impl ReallocBallotBoxBuilder { + 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 ballot_box(&mut self, ballot_box: solana_program::pubkey::Pubkey) -> &mut Self { + self.ballot_box = Some(ballot_box); + self + } + #[inline(always)] + pub fn ncn(&mut self, ncn: solana_program::pubkey::Pubkey) -> &mut Self { + self.ncn = Some(ncn); + self + } + #[inline(always)] + pub fn payer(&mut self, payer: solana_program::pubkey::Pubkey) -> &mut Self { + self.payer = Some(payer); + 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 + } + #[inline(always)] + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(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 = ReallocBallotBox { + ncn_config: self.ncn_config.expect("ncn_config is not set"), + ballot_box: self.ballot_box.expect("ballot_box is not set"), + ncn: self.ncn.expect("ncn is not set"), + payer: self.payer.expect("payer is not set"), + system_program: self + .system_program + .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), + }; + let args = ReallocBallotBoxInstructionArgs { + epoch: self.epoch.clone().expect("epoch is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `realloc_ballot_box` CPI accounts. +pub struct ReallocBallotBoxCpiAccounts<'a, 'b> { + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub ballot_box: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, +} + +/// `realloc_ballot_box` CPI instruction. +pub struct ReallocBallotBoxCpi<'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 ballot_box: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: ReallocBallotBoxInstructionArgs, +} + +impl<'a, 'b> ReallocBallotBoxCpi<'a, 'b> { + pub fn new( + program: &'b solana_program::account_info::AccountInfo<'a>, + accounts: ReallocBallotBoxCpiAccounts<'a, 'b>, + args: ReallocBallotBoxInstructionArgs, + ) -> Self { + Self { + __program: program, + ncn_config: accounts.ncn_config, + ballot_box: accounts.ballot_box, + ncn: accounts.ncn, + payer: accounts.payer, + 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(5 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.ballot_box.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.payer.key, + true, + )); + 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 = ReallocBallotBoxInstructionData::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(5 + 1 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.ncn_config.clone()); + account_infos.push(self.ballot_box.clone()); + account_infos.push(self.ncn.clone()); + account_infos.push(self.payer.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 `ReallocBallotBox` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[writable]` ballot_box +/// 2. `[]` ncn +/// 3. `[writable, signer]` payer +/// 4. `[]` system_program +#[derive(Clone, Debug)] +pub struct ReallocBallotBoxCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> ReallocBallotBoxCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(ReallocBallotBoxCpiBuilderInstruction { + __program: program, + ncn_config: None, + ballot_box: None, + ncn: None, + payer: None, + system_program: None, + 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 ballot_box( + &mut self, + ballot_box: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.ballot_box = Some(ballot_box); + 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 payer(&mut self, payer: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.payer = Some(payer); + 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 + } + #[inline(always)] + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(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 = ReallocBallotBoxInstructionArgs { + epoch: self.instruction.epoch.clone().expect("epoch is not set"), + }; + let instruction = ReallocBallotBoxCpi { + __program: self.instruction.__program, + + ncn_config: self.instruction.ncn_config.expect("ncn_config is not set"), + + ballot_box: self.instruction.ballot_box.expect("ballot_box is not set"), + + ncn: self.instruction.ncn.expect("ncn is not set"), + + payer: self.instruction.payer.expect("payer 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 ReallocBallotBoxCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_program::account_info::AccountInfo<'a>, + ncn_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + ballot_box: Option<&'b solana_program::account_info::AccountInfo<'a>>, + ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, + payer: Option<&'b solana_program::account_info::AccountInfo<'a>>, + system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + 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/realloc_base_reward_router.rs b/clients/rust/jito_tip_router/src/generated/instructions/realloc_base_reward_router.rs new file mode 100644 index 00000000..648359ca --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/instructions/realloc_base_reward_router.rs @@ -0,0 +1,480 @@ +//! 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 ReallocBaseRewardRouter { + pub ncn_config: solana_program::pubkey::Pubkey, + + pub base_reward_router: solana_program::pubkey::Pubkey, + + pub ncn: solana_program::pubkey::Pubkey, + + pub payer: solana_program::pubkey::Pubkey, + + pub system_program: solana_program::pubkey::Pubkey, +} + +impl ReallocBaseRewardRouter { + pub fn instruction( + &self, + args: ReallocBaseRewardRouterInstructionArgs, + ) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: ReallocBaseRewardRouterInstructionArgs, + remaining_accounts: &[solana_program::instruction::AccountMeta], + ) -> solana_program::instruction::Instruction { + let mut accounts = Vec::with_capacity(5 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.base_reward_router, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn, false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.payer, true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.system_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = ReallocBaseRewardRouterInstructionData::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 ReallocBaseRewardRouterInstructionData { + discriminator: u8, +} + +impl ReallocBaseRewardRouterInstructionData { + pub fn new() -> Self { + Self { discriminator: 25 } + } +} + +impl Default for ReallocBaseRewardRouterInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ReallocBaseRewardRouterInstructionArgs { + pub epoch: u64, +} + +/// Instruction builder for `ReallocBaseRewardRouter`. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[writable]` base_reward_router +/// 2. `[]` ncn +/// 3. `[writable, signer]` payer +/// 4. `[optional]` system_program (default to `11111111111111111111111111111111`) +#[derive(Clone, Debug, Default)] +pub struct ReallocBaseRewardRouterBuilder { + ncn_config: Option, + base_reward_router: Option, + ncn: Option, + payer: Option, + system_program: Option, + epoch: Option, + __remaining_accounts: Vec, +} + +impl ReallocBaseRewardRouterBuilder { + 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 base_reward_router( + &mut self, + base_reward_router: solana_program::pubkey::Pubkey, + ) -> &mut Self { + self.base_reward_router = Some(base_reward_router); + self + } + #[inline(always)] + pub fn ncn(&mut self, ncn: solana_program::pubkey::Pubkey) -> &mut Self { + self.ncn = Some(ncn); + self + } + #[inline(always)] + pub fn payer(&mut self, payer: solana_program::pubkey::Pubkey) -> &mut Self { + self.payer = Some(payer); + 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 + } + #[inline(always)] + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(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 = ReallocBaseRewardRouter { + ncn_config: self.ncn_config.expect("ncn_config is not set"), + base_reward_router: self + .base_reward_router + .expect("base_reward_router is not set"), + ncn: self.ncn.expect("ncn is not set"), + payer: self.payer.expect("payer is not set"), + system_program: self + .system_program + .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), + }; + let args = ReallocBaseRewardRouterInstructionArgs { + epoch: self.epoch.clone().expect("epoch is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `realloc_base_reward_router` CPI accounts. +pub struct ReallocBaseRewardRouterCpiAccounts<'a, 'b> { + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub base_reward_router: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, +} + +/// `realloc_base_reward_router` CPI instruction. +pub struct ReallocBaseRewardRouterCpi<'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 base_reward_router: &'b solana_program::account_info::AccountInfo<'a>, + + pub ncn: &'b solana_program::account_info::AccountInfo<'a>, + + pub payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: ReallocBaseRewardRouterInstructionArgs, +} + +impl<'a, 'b> ReallocBaseRewardRouterCpi<'a, 'b> { + pub fn new( + program: &'b solana_program::account_info::AccountInfo<'a>, + accounts: ReallocBaseRewardRouterCpiAccounts<'a, 'b>, + args: ReallocBaseRewardRouterInstructionArgs, + ) -> Self { + Self { + __program: program, + ncn_config: accounts.ncn_config, + base_reward_router: accounts.base_reward_router, + ncn: accounts.ncn, + payer: accounts.payer, + 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(5 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.base_reward_router.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.payer.key, + true, + )); + 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 = ReallocBaseRewardRouterInstructionData::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(5 + 1 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.ncn_config.clone()); + account_infos.push(self.base_reward_router.clone()); + account_infos.push(self.ncn.clone()); + account_infos.push(self.payer.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 `ReallocBaseRewardRouter` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[writable]` base_reward_router +/// 2. `[]` ncn +/// 3. `[writable, signer]` payer +/// 4. `[]` system_program +#[derive(Clone, Debug)] +pub struct ReallocBaseRewardRouterCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> ReallocBaseRewardRouterCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(ReallocBaseRewardRouterCpiBuilderInstruction { + __program: program, + ncn_config: None, + base_reward_router: None, + ncn: None, + payer: None, + system_program: None, + 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 base_reward_router( + &mut self, + base_reward_router: &'b solana_program::account_info::AccountInfo<'a>, + ) -> &mut Self { + self.instruction.base_reward_router = Some(base_reward_router); + 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 payer(&mut self, payer: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.payer = Some(payer); + 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 + } + #[inline(always)] + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(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 = ReallocBaseRewardRouterInstructionArgs { + epoch: self.instruction.epoch.clone().expect("epoch is not set"), + }; + let instruction = ReallocBaseRewardRouterCpi { + __program: self.instruction.__program, + + ncn_config: self.instruction.ncn_config.expect("ncn_config is not set"), + + base_reward_router: self + .instruction + .base_reward_router + .expect("base_reward_router is not set"), + + ncn: self.instruction.ncn.expect("ncn is not set"), + + payer: self.instruction.payer.expect("payer 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 ReallocBaseRewardRouterCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_program::account_info::AccountInfo<'a>, + ncn_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + base_reward_router: Option<&'b solana_program::account_info::AccountInfo<'a>>, + ncn: Option<&'b solana_program::account_info::AccountInfo<'a>>, + payer: Option<&'b solana_program::account_info::AccountInfo<'a>>, + system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + 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/realloc_operator_snapshot.rs b/clients/rust/jito_tip_router/src/generated/instructions/realloc_operator_snapshot.rs new file mode 100644 index 00000000..719ef4ba --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/instructions/realloc_operator_snapshot.rs @@ -0,0 +1,690 @@ +//! 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 ReallocOperatorSnapshot { + 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 ReallocOperatorSnapshot { + pub fn instruction( + &self, + args: ReallocOperatorSnapshotInstructionArgs, + ) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: ReallocOperatorSnapshotInstructionArgs, + 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 = ReallocOperatorSnapshotInstructionData::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 ReallocOperatorSnapshotInstructionData { + discriminator: u8, +} + +impl ReallocOperatorSnapshotInstructionData { + pub fn new() -> Self { + Self { discriminator: 24 } + } +} + +impl Default for ReallocOperatorSnapshotInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ReallocOperatorSnapshotInstructionArgs { + pub epoch: u64, +} + +/// Instruction builder for `ReallocOperatorSnapshot`. +/// +/// ### 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 ReallocOperatorSnapshotBuilder { + 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, + epoch: Option, + __remaining_accounts: Vec, +} + +impl ReallocOperatorSnapshotBuilder { + 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 + } + #[inline(always)] + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(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 = ReallocOperatorSnapshot { + 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 = ReallocOperatorSnapshotInstructionArgs { + epoch: self.epoch.clone().expect("epoch is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `realloc_operator_snapshot` CPI accounts. +pub struct ReallocOperatorSnapshotCpiAccounts<'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>, +} + +/// `realloc_operator_snapshot` CPI instruction. +pub struct ReallocOperatorSnapshotCpi<'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: ReallocOperatorSnapshotInstructionArgs, +} + +impl<'a, 'b> ReallocOperatorSnapshotCpi<'a, 'b> { + pub fn new( + program: &'b solana_program::account_info::AccountInfo<'a>, + accounts: ReallocOperatorSnapshotCpiAccounts<'a, 'b>, + args: ReallocOperatorSnapshotInstructionArgs, + ) -> 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 = ReallocOperatorSnapshotInstructionData::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 `ReallocOperatorSnapshot` 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 ReallocOperatorSnapshotCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> ReallocOperatorSnapshotCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(ReallocOperatorSnapshotCpiBuilderInstruction { + __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, + 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 + } + #[inline(always)] + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(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 = ReallocOperatorSnapshotInstructionArgs { + epoch: self.instruction.epoch.clone().expect("epoch is not set"), + }; + let instruction = ReallocOperatorSnapshotCpi { + __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 ReallocOperatorSnapshotCpiBuilderInstruction<'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>>, + 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/realloc_weight_table.rs b/clients/rust/jito_tip_router/src/generated/instructions/realloc_weight_table.rs new file mode 100644 index 00000000..68119885 --- /dev/null +++ b/clients/rust/jito_tip_router/src/generated/instructions/realloc_weight_table.rs @@ -0,0 +1,515 @@ +//! 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 ReallocWeightTable { + pub ncn_config: solana_program::pubkey::Pubkey, + + pub weight_table: solana_program::pubkey::Pubkey, + + pub ncn: solana_program::pubkey::Pubkey, + + pub tracked_mints: solana_program::pubkey::Pubkey, + + pub payer: solana_program::pubkey::Pubkey, + + pub system_program: solana_program::pubkey::Pubkey, +} + +impl ReallocWeightTable { + pub fn instruction( + &self, + args: ReallocWeightTableInstructionArgs, + ) -> solana_program::instruction::Instruction { + self.instruction_with_remaining_accounts(args, &[]) + } + #[allow(clippy::vec_init_then_push)] + pub fn instruction_with_remaining_accounts( + &self, + args: ReallocWeightTableInstructionArgs, + remaining_accounts: &[solana_program::instruction::AccountMeta], + ) -> solana_program::instruction::Instruction { + let mut accounts = Vec::with_capacity(6 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.ncn_config, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + self.weight_table, + 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( + self.payer, true, + )); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + self.system_program, + false, + )); + accounts.extend_from_slice(remaining_accounts); + let mut data = ReallocWeightTableInstructionData::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 ReallocWeightTableInstructionData { + discriminator: u8, +} + +impl ReallocWeightTableInstructionData { + pub fn new() -> Self { + Self { discriminator: 26 } + } +} + +impl Default for ReallocWeightTableInstructionData { + fn default() -> Self { + Self::new() + } +} + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ReallocWeightTableInstructionArgs { + pub epoch: u64, +} + +/// Instruction builder for `ReallocWeightTable`. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[writable]` weight_table +/// 2. `[]` ncn +/// 3. `[]` tracked_mints +/// 4. `[writable, signer]` payer +/// 5. `[optional]` system_program (default to `11111111111111111111111111111111`) +#[derive(Clone, Debug, Default)] +pub struct ReallocWeightTableBuilder { + ncn_config: Option, + weight_table: Option, + ncn: Option, + tracked_mints: Option, + payer: Option, + system_program: Option, + epoch: Option, + __remaining_accounts: Vec, +} + +impl ReallocWeightTableBuilder { + 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 weight_table(&mut self, weight_table: solana_program::pubkey::Pubkey) -> &mut Self { + self.weight_table = Some(weight_table); + 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 payer(&mut self, payer: solana_program::pubkey::Pubkey) -> &mut Self { + self.payer = Some(payer); + 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 + } + #[inline(always)] + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(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 = ReallocWeightTable { + ncn_config: self.ncn_config.expect("ncn_config is not set"), + weight_table: self.weight_table.expect("weight_table is not set"), + ncn: self.ncn.expect("ncn is not set"), + tracked_mints: self.tracked_mints.expect("tracked_mints is not set"), + payer: self.payer.expect("payer is not set"), + system_program: self + .system_program + .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")), + }; + let args = ReallocWeightTableInstructionArgs { + epoch: self.epoch.clone().expect("epoch is not set"), + }; + + accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) + } +} + +/// `realloc_weight_table` CPI accounts. +pub struct ReallocWeightTableCpiAccounts<'a, 'b> { + pub ncn_config: &'b solana_program::account_info::AccountInfo<'a>, + + pub weight_table: &'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 payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, +} + +/// `realloc_weight_table` CPI instruction. +pub struct ReallocWeightTableCpi<'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 weight_table: &'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 payer: &'b solana_program::account_info::AccountInfo<'a>, + + pub system_program: &'b solana_program::account_info::AccountInfo<'a>, + /// The arguments for the instruction. + pub __args: ReallocWeightTableInstructionArgs, +} + +impl<'a, 'b> ReallocWeightTableCpi<'a, 'b> { + pub fn new( + program: &'b solana_program::account_info::AccountInfo<'a>, + accounts: ReallocWeightTableCpiAccounts<'a, 'b>, + args: ReallocWeightTableInstructionArgs, + ) -> Self { + Self { + __program: program, + ncn_config: accounts.ncn_config, + weight_table: accounts.weight_table, + ncn: accounts.ncn, + tracked_mints: accounts.tracked_mints, + payer: accounts.payer, + 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(6 + remaining_accounts.len()); + accounts.push(solana_program::instruction::AccountMeta::new_readonly( + *self.ncn_config.key, + false, + )); + accounts.push(solana_program::instruction::AccountMeta::new( + *self.weight_table.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( + *self.payer.key, + true, + )); + 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 = ReallocWeightTableInstructionData::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(6 + 1 + remaining_accounts.len()); + account_infos.push(self.__program.clone()); + account_infos.push(self.ncn_config.clone()); + account_infos.push(self.weight_table.clone()); + account_infos.push(self.ncn.clone()); + account_infos.push(self.tracked_mints.clone()); + account_infos.push(self.payer.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 `ReallocWeightTable` via CPI. +/// +/// ### Accounts: +/// +/// 0. `[]` ncn_config +/// 1. `[writable]` weight_table +/// 2. `[]` ncn +/// 3. `[]` tracked_mints +/// 4. `[writable, signer]` payer +/// 5. `[]` system_program +#[derive(Clone, Debug)] +pub struct ReallocWeightTableCpiBuilder<'a, 'b> { + instruction: Box>, +} + +impl<'a, 'b> ReallocWeightTableCpiBuilder<'a, 'b> { + pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self { + let instruction = Box::new(ReallocWeightTableCpiBuilderInstruction { + __program: program, + ncn_config: None, + weight_table: None, + ncn: None, + tracked_mints: None, + payer: None, + system_program: None, + 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 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 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 payer(&mut self, payer: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self { + self.instruction.payer = Some(payer); + 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 + } + #[inline(always)] + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(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 = ReallocWeightTableInstructionArgs { + epoch: self.instruction.epoch.clone().expect("epoch is not set"), + }; + let instruction = ReallocWeightTableCpi { + __program: self.instruction.__program, + + ncn_config: self.instruction.ncn_config.expect("ncn_config is not set"), + + weight_table: self + .instruction + .weight_table + .expect("weight_table is not set"), + + ncn: self.instruction.ncn.expect("ncn is not set"), + + tracked_mints: self + .instruction + .tracked_mints + .expect("tracked_mints is not set"), + + payer: self.instruction.payer.expect("payer 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 ReallocWeightTableCpiBuilderInstruction<'a, 'b> { + __program: &'b solana_program::account_info::AccountInfo<'a>, + ncn_config: Option<&'b solana_program::account_info::AccountInfo<'a>>, + weight_table: 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>>, + payer: Option<&'b solana_program::account_info::AccountInfo<'a>>, + system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, + 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/snapshot_vault_operator_delegation.rs b/clients/rust/jito_tip_router/src/generated/instructions/snapshot_vault_operator_delegation.rs index d1f5f094..ba8c3f6d 100644 --- 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 @@ -140,7 +140,7 @@ impl Default for SnapshotVaultOperatorDelegationInstructionData { #[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, + pub epoch: u64, } /// Instruction builder for `SnapshotVaultOperatorDelegation`. @@ -177,7 +177,7 @@ pub struct SnapshotVaultOperatorDelegationBuilder { operator_snapshot: Option, vault_program: Option, restaking_program: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -273,10 +273,9 @@ impl SnapshotVaultOperatorDelegationBuilder { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -322,7 +321,7 @@ impl SnapshotVaultOperatorDelegationBuilder { .expect("restaking_program is not set"), }; let args = SnapshotVaultOperatorDelegationInstructionArgs { - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -598,7 +597,7 @@ impl<'a, 'b> SnapshotVaultOperatorDelegationCpiBuilder<'a, 'b> { operator_snapshot: None, vault_program: None, restaking_program: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -709,10 +708,9 @@ impl<'a, 'b> SnapshotVaultOperatorDelegationCpiBuilder<'a, 'b> { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -757,7 +755,7 @@ impl<'a, 'b> SnapshotVaultOperatorDelegationCpiBuilder<'a, 'b> { signers_seeds: &[&[&[u8]]], ) -> solana_program::entrypoint::ProgramResult { let args = SnapshotVaultOperatorDelegationInstructionArgs { - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = SnapshotVaultOperatorDelegationCpi { __program: self.instruction.__program, @@ -845,7 +843,7 @@ struct SnapshotVaultOperatorDelegationCpiBuilderInstruction<'a, 'b> { 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, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/core/src/ballot_box.rs b/core/src/ballot_box.rs index 5df3e4cb..17e54449 100644 --- a/core/src/ballot_box.rs +++ b/core/src/ballot_box.rs @@ -1,3 +1,5 @@ +use std::mem::size_of; + use bytemuck::{Pod, Zeroable}; use jito_bytemuck::{ types::{PodBool, PodU16, PodU64}, @@ -221,6 +223,8 @@ impl Discriminator for BallotBox { } impl BallotBox { + pub const SIZE: usize = 8 + size_of::(); + pub fn new(ncn: Pubkey, epoch: u64, bump: u8, current_slot: u64) -> Self { Self { ncn, @@ -591,6 +595,7 @@ mod tests { let ballot_box = BallotBox::new(Pubkey::default(), 0, 0, 0); assert_eq!(ballot_box.operator_votes.len(), MAX_OPERATORS); assert_eq!(ballot_box.ballot_tallies.len(), MAX_OPERATORS); + println!("expected_total: {}", expected_total); } #[test] diff --git a/core/src/base_reward_router.rs b/core/src/base_reward_router.rs index 6ab8be50..27c8ab19 100644 --- a/core/src/base_reward_router.rs +++ b/core/src/base_reward_router.rs @@ -44,6 +44,8 @@ impl Discriminator for BaseRewardRouter { } impl BaseRewardRouter { + pub const SIZE: usize = 8 + size_of::(); + pub fn new(ncn: Pubkey, ncn_epoch: u64, bump: u8, slot_created: u64) -> Self { Self { ncn, @@ -79,7 +81,7 @@ impl BaseRewardRouter { ncn: &Pubkey, ncn_epoch: u64, ) -> (Pubkey, u8, Vec>) { - let seeds = Self::seeds(ncn, ncn_epoch); + let seeds: Vec> = 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) @@ -675,6 +677,7 @@ mod tests { + size_of::() * MAX_OPERATORS; // ncn_fee_group_reward_routes assert_eq!(size_of::(), expected_total); + println!("expected_total: {}", expected_total); } #[test] diff --git a/core/src/constants.rs b/core/src/constants.rs index d25c003c..9b552807 100644 --- a/core/src/constants.rs +++ b/core/src/constants.rs @@ -1,3 +1,4 @@ +use solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE; use spl_math::precise_number::PreciseNumber; use crate::error::TipRouterError; @@ -18,3 +19,4 @@ pub fn precise_consensus() -> Result { } pub const DEFAULT_CONSENSUS_REACHED_SLOT: u64 = u64::MAX; +pub const MAX_REALLOC_BYTES: u64 = MAX_PERMITTED_DATA_INCREASE as u64; // TODO just use this? diff --git a/core/src/epoch_snapshot.rs b/core/src/epoch_snapshot.rs index 2978cf43..0aa3d9e9 100644 --- a/core/src/epoch_snapshot.rs +++ b/core/src/epoch_snapshot.rs @@ -1,3 +1,5 @@ +use std::mem::size_of; + use bytemuck::{Pod, Zeroable}; use jito_bytemuck::{ types::{PodBool, PodU16, PodU64}, @@ -50,6 +52,8 @@ impl Discriminator for EpochSnapshot { } impl EpochSnapshot { + pub const SIZE: usize = 8 + size_of::(); + pub fn new( ncn: Pubkey, ncn_epoch: u64, @@ -223,6 +227,7 @@ impl Discriminator for OperatorSnapshot { impl OperatorSnapshot { pub const MAX_VAULT_OPERATOR_STAKE_WEIGHT: usize = 64; + pub const SIZE: usize = 8 + size_of::(); #[allow(clippy::too_many_arguments)] pub fn new( @@ -373,6 +378,14 @@ impl OperatorSnapshot { Ok(()) } + pub fn operator(&self) -> Pubkey { + self.operator + } + + pub fn ncn(&self) -> Pubkey { + self.ncn + } + pub fn operator_fee_bps(&self) -> u16 { self.operator_fee_bps.into() } @@ -579,6 +592,5 @@ mod tests { assert_eq!(size_of::(), expected_total); println!("expected_total: {}", expected_total); - assert!(false); } } diff --git a/core/src/instruction.rs b/core/src/instruction.rs index 8931c51c..0738f334 100644 --- a/core/src/instruction.rs +++ b/core/src/instruction.rs @@ -70,7 +70,7 @@ pub enum TipRouterInstruction { #[account(5, name = "restaking_program")] #[account(6, name = "system_program")] InitializeWeightTable{ - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Updates the weight table @@ -95,7 +95,7 @@ pub enum TipRouterInstruction { #[account(7, name = "restaking_program")] #[account(8, name = "system_program")] InitializeEpochSnapshot{ - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Initializes the Operator Snapshot @@ -110,7 +110,7 @@ pub enum TipRouterInstruction { #[account(8, name = "restaking_program")] #[account(9, name = "system_program")] InitializeOperatorSnapshot{ - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Initializes the Vault Operator Delegation Snapshot @@ -129,7 +129,7 @@ pub enum TipRouterInstruction { #[account(12, name = "vault_program")] #[account(13, name = "restaking_program")] SnapshotVaultOperatorDelegation{ - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Registers a mint with the NCN config #[account(0, name = "restaking_config")] @@ -152,7 +152,7 @@ pub enum TipRouterInstruction { #[account(4, name = "restaking_program")] #[account(5, name = "system_program")] InitializeBaseRewardRouter{ - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Initializes the ncn reward router @@ -165,7 +165,7 @@ pub enum TipRouterInstruction { #[account(6, name = "system_program")] InitializeNcnRewardRouter{ ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, }, @@ -304,5 +304,51 @@ pub enum TipRouterInstruction { SetTieBreaker { meta_merkle_root: [u8; 32], epoch: u64, - } + }, + + /// Resizes the ballot box account + #[account(0, name = "ncn_config")] + #[account(1, writable, name = "ballot_box")] + #[account(2, name = "ncn")] + #[account(3, writable, signer, name = "payer")] + #[account(4, name = "system_program")] + ReallocBallotBox { + epoch: u64, + }, + + /// Resizes the operator snapshot account + #[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")] + ReallocOperatorSnapshot { + epoch: u64, + }, + + /// Resizes the base reward router account + #[account(0, name = "ncn_config")] + #[account(1, writable, name = "base_reward_router")] + #[account(2, name = "ncn")] + #[account(3, writable, signer, name = "payer")] + #[account(4, name = "system_program")] + ReallocBaseRewardRouter { + epoch: u64, + }, + + /// Resizes the weight table account + #[account(0, name = "ncn_config")] + #[account(1, writable, name = "weight_table")] + #[account(2, name = "ncn")] + #[account(3, name = "tracked_mints")] + #[account(4, writable, signer, name = "payer")] + #[account(5, name = "system_program")] + ReallocWeightTable { + epoch: u64, + }, } diff --git a/core/src/lib.rs b/core/src/lib.rs index e5633bea..7ef1baef 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -13,5 +13,6 @@ pub mod ncn_fee_group; pub mod ncn_reward_router; pub mod stake_weight; pub mod tracked_mints; +pub mod utils; pub mod weight_entry; pub mod weight_table; diff --git a/core/src/ncn_config.rs b/core/src/ncn_config.rs index 670a3626..da5d2ff0 100644 --- a/core/src/ncn_config.rs +++ b/core/src/ncn_config.rs @@ -1,3 +1,5 @@ +use std::mem::size_of; + use bytemuck::{Pod, Zeroable}; use jito_bytemuck::{types::PodU64, AccountDeserialize, Discriminator}; use shank::{ShankAccount, ShankType}; @@ -34,6 +36,8 @@ impl Discriminator for NcnConfig { } impl NcnConfig { + pub const SIZE: usize = 8 + size_of::(); + pub fn new( ncn: Pubkey, tie_breaker_admin: Pubkey, diff --git a/core/src/ncn_reward_router.rs b/core/src/ncn_reward_router.rs index a2b83881..63b668fa 100644 --- a/core/src/ncn_reward_router.rs +++ b/core/src/ncn_reward_router.rs @@ -48,6 +48,8 @@ impl Discriminator for NcnRewardRouter { } impl NcnRewardRouter { + pub const SIZE: usize = 8 + size_of::(); + pub fn new( ncn_fee_group: NcnFeeGroup, operator: Pubkey, @@ -569,6 +571,7 @@ mod tests { + size_of::() * MAX_VAULT_OPERATOR_DELEGATIONS; // vault_reward_routes assert_eq!(size_of::(), expected_total); + println!("expected_total: {}", expected_total); } #[test] diff --git a/core/src/tracked_mints.rs b/core/src/tracked_mints.rs index 82cc25a8..ca7571a3 100644 --- a/core/src/tracked_mints.rs +++ b/core/src/tracked_mints.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, mem::size_of}; use bytemuck::{Pod, Zeroable}; use jito_bytemuck::{types::PodU64, AccountDeserialize, Discriminator}; @@ -66,6 +66,8 @@ impl Discriminator for TrackedMints { } impl TrackedMints { + pub const SIZE: usize = 8 + size_of::(); + pub fn new(ncn: Pubkey, bump: u8) -> Self { Self { ncn, diff --git a/core/src/utils.rs b/core/src/utils.rs new file mode 100644 index 00000000..e9a6272f --- /dev/null +++ b/core/src/utils.rs @@ -0,0 +1,67 @@ +use solana_program::{ + account_info::AccountInfo, + entrypoint::ProgramResult, + program::{invoke, invoke_signed}, + program_error::ProgramError, + pubkey::Pubkey, + rent::Rent, + system_instruction, +}; + +use crate::constants::MAX_REALLOC_BYTES; + +/// Calculate new size for reallocation, capped at target size +/// Returns the minimum of (current_size + MAX_REALLOC_BYTES) and target_size +pub fn get_new_size(current_size: usize, target_size: usize) -> Result { + Ok(current_size + .checked_add(MAX_REALLOC_BYTES as usize) + .ok_or(ProgramError::ArithmeticOverflow)? + .min(target_size)) +} + +pub fn realloc_account<'a, 'info>( + account: &'a AccountInfo<'info>, + payer: &'a AccountInfo<'info>, + system_program: &'a AccountInfo<'info>, + rent: &Rent, + target_size: u64, + seeds: &[Vec], +) -> ProgramResult { + let current_size = account.data_len(); + + // If account is already over target size, don't try to shrink + if current_size >= target_size as usize { + return Ok(()); + } + + // Calculate new size, capped at target_size + let new_size = current_size + .checked_add(MAX_REALLOC_BYTES as usize) + .ok_or(ProgramError::ArithmeticOverflow)? + .min(target_size as usize); + + // Calculate required lamports for new size + let new_minimum_balance = rent.minimum_balance(new_size); + let lamports_diff = new_minimum_balance.saturating_sub(account.lamports()); + + // Transfer lamports if needed + if lamports_diff > 0 { + invoke( + &system_instruction::transfer(payer.key, account.key, lamports_diff), + &[payer.clone(), account.clone(), system_program.clone()], + )?; + } + + // Reallocate space + invoke_signed( + &system_instruction::allocate(account.key, new_size as u64), + &[account.clone(), system_program.clone()], + &[seeds + .iter() + .map(|seed| seed.as_slice()) + .collect::>() + .as_slice()], + )?; + + Ok(()) +} diff --git a/core/src/weight_table.rs b/core/src/weight_table.rs index 13d5d9eb..da32fd39 100644 --- a/core/src/weight_table.rs +++ b/core/src/weight_table.rs @@ -1,4 +1,4 @@ -use std::collections::HashSet; +use std::{collections::HashSet, mem::size_of}; use bytemuck::{Pod, Zeroable}; use jito_bytemuck::{types::PodU64, AccountDeserialize, Discriminator}; @@ -6,7 +6,10 @@ 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}; +use crate::{ + constants::MAX_VAULT_OPERATOR_DELEGATIONS, discriminators::Discriminators, + error::TipRouterError, weight_entry::WeightEntry, +}; // PDA'd ["WEIGHT_TABLE", NCN, NCN_EPOCH_SLOT] #[derive(Debug, Clone, Copy, Zeroable, ShankType, Pod, AccountDeserialize, ShankAccount)] @@ -29,7 +32,7 @@ pub struct WeightTable { reserved: [u8; 128], /// The weight table - table: [WeightEntry; 32], + table: [WeightEntry; 64], } impl Discriminator for WeightTable { @@ -37,7 +40,7 @@ impl Discriminator for WeightTable { } impl WeightTable { - pub const MAX_TABLE_ENTRIES: usize = 32; + pub const SIZE: usize = 8 + size_of::(); pub fn new(ncn: Pubkey, ncn_epoch: u64, slot_created: u64, bump: u8) -> Self { Self { @@ -46,7 +49,7 @@ impl WeightTable { slot_created: PodU64::from(slot_created), bump, reserved: [0; 128], - table: [WeightEntry::default(); Self::MAX_TABLE_ENTRIES], + table: [WeightEntry::default(); MAX_VAULT_OPERATOR_DELEGATIONS], } } @@ -87,7 +90,7 @@ impl WeightTable { } // Check if vector exceeds maximum allowed entries - if config_supported_mints.len() > Self::MAX_TABLE_ENTRIES { + if config_supported_mints.len() > MAX_VAULT_OPERATOR_DELEGATIONS { return Err(TipRouterError::TooManyMintsForTable); } @@ -217,6 +220,8 @@ impl WeightTable { #[cfg(test)] mod tests { + use std::mem::size_of; + use solana_program::pubkey::Pubkey; use super::*; @@ -225,6 +230,18 @@ mod tests { (0..count).map(|_| Pubkey::new_unique()).collect() } + #[test] + fn test_len() { + let expected_total = size_of::() // ncn + + size_of::() // ncn_epoch + + size_of::() // slot_created + + 1 // bump + + 128 // reserved + + size_of::<[WeightEntry; MAX_VAULT_OPERATOR_DELEGATIONS]>(); // weight table + + assert_eq!(size_of::(), expected_total); + } + #[test] fn test_initialize_table_success() { let ncn = Pubkey::new_unique(); @@ -240,7 +257,7 @@ mod tests { fn test_initialize_table_too_many() { let ncn = Pubkey::new_unique(); let mut table = WeightTable::new(ncn, 0, 0, 0); - let many_mints = get_test_pubkeys(WeightTable::MAX_TABLE_ENTRIES + 1); + let many_mints = get_test_pubkeys(MAX_VAULT_OPERATOR_DELEGATIONS + 1); assert_eq!( table.initalize_weight_table(&many_mints), Err(TipRouterError::TooManyMintsForTable) @@ -251,9 +268,9 @@ mod tests { fn test_initialize_table_max() { let ncn = Pubkey::new_unique(); let mut table = WeightTable::new(ncn, 0, 0, 0); - let max_mints = get_test_pubkeys(WeightTable::MAX_TABLE_ENTRIES); + let max_mints = get_test_pubkeys(MAX_VAULT_OPERATOR_DELEGATIONS); table.initalize_weight_table(&max_mints).unwrap(); - assert_eq!(table.mint_count(), WeightTable::MAX_TABLE_ENTRIES); + assert_eq!(table.mint_count(), MAX_VAULT_OPERATOR_DELEGATIONS); } #[test] diff --git a/idl/jito_tip_router.json b/idl/jito_tip_router.json index 78bce846..f705ad05 100644 --- a/idl/jito_tip_router.json +++ b/idl/jito_tip_router.json @@ -255,10 +255,8 @@ ], "args": [ { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -361,10 +359,8 @@ ], "args": [ { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -428,10 +424,8 @@ ], "args": [ { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -515,10 +509,8 @@ ], "args": [ { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -617,10 +609,8 @@ ], "args": [ { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -673,10 +663,8 @@ "type": "u8" }, { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -1280,6 +1268,196 @@ "type": "u8", "value": 22 } + }, + { + "name": "ReallocBallotBox", + "accounts": [ + { + "name": "ncnConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "ballotBox", + "isMut": true, + "isSigner": false + }, + { + "name": "ncn", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "epoch", + "type": "u64" + } + ], + "discriminant": { + "type": "u8", + "value": 23 + } + }, + { + "name": "ReallocOperatorSnapshot", + "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": "epoch", + "type": "u64" + } + ], + "discriminant": { + "type": "u8", + "value": 24 + } + }, + { + "name": "ReallocBaseRewardRouter", + "accounts": [ + { + "name": "ncnConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "baseRewardRouter", + "isMut": true, + "isSigner": false + }, + { + "name": "ncn", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "epoch", + "type": "u64" + } + ], + "discriminant": { + "type": "u8", + "value": 25 + } + }, + { + "name": "ReallocWeightTable", + "accounts": [ + { + "name": "ncnConfig", + "isMut": false, + "isSigner": false + }, + { + "name": "weightTable", + "isMut": true, + "isSigner": false + }, + { + "name": "ncn", + "isMut": false, + "isSigner": false + }, + { + "name": "trackedMints", + "isMut": false, + "isSigner": false + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "epoch", + "type": "u64" + } + ], + "discriminant": { + "type": "u8", + "value": 26 + } } ], "accounts": [ @@ -1849,7 +2027,7 @@ { "defined": "WeightEntry" }, - 32 + 64 ] } } diff --git a/integration_tests/tests/fixtures/test_builder.rs b/integration_tests/tests/fixtures/test_builder.rs index dd313606..87235de4 100644 --- a/integration_tests/tests/fixtures/test_builder.rs +++ b/integration_tests/tests/fixtures/test_builder.rs @@ -14,8 +14,14 @@ use solana_program::{ }; use solana_program_test::{processor, BanksClientError, ProgramTest, ProgramTestContext}; use solana_sdk::{ - account::Account, commitment_config::CommitmentLevel, epoch_schedule::EpochSchedule, - native_token::lamports_to_sol, signature::Signer, transaction::Transaction, + account::Account, + clock::Slot, + commitment_config::CommitmentLevel, + epoch_schedule::EpochSchedule, + native_token::{lamports_to_sol, LAMPORTS_PER_SOL}, + signature::{Keypair, Signer}, + system_instruction, + transaction::Transaction, }; use super::{ @@ -57,11 +63,21 @@ impl Debug for TestBuilder { } } +pub fn system_account(lamports: u64) -> Account { + Account { + lamports, + owner: solana_program::system_program::ID, + executable: false, + rent_epoch: 0, + data: vec![], + } +} + impl TestBuilder { pub async fn new() -> Self { let run_as_bpf = std::env::vars().any(|(key, _)| key.eq("SBF_OUT_DIR")); - let program_test = if run_as_bpf { + let mut program_test = if run_as_bpf { let mut program_test = ProgramTest::new( "jito_tip_router_program", jito_tip_router_program::id(), @@ -95,15 +111,59 @@ impl TestBuilder { program_test }; - Self { - context: program_test.start_with_context().await, - } + // Pre-fund payer with 1M SOL + let whale = Keypair::new(); + program_test.add_account(whale.pubkey(), system_account(1_000_000 * LAMPORTS_PER_SOL)); + let mut context = program_test.start_with_context().await; + let transaction = Transaction::new_signed_with_payer( + &[system_instruction::transfer( + &whale.pubkey(), + &context.payer.pubkey(), + 999_999 * LAMPORTS_PER_SOL, + )], + Some(&whale.pubkey()), + &[&whale], + context.last_blockhash, + ); + + context + .banks_client + .process_transaction(transaction) + .await + .expect("failed to pre-fund payer"); + + Self { context } } pub async fn get_balance(&mut self, pubkey: &Pubkey) -> Result { Ok(self.context.banks_client.get_balance(*pubkey).await?) } + pub async fn get_account( + &mut self, + address: &Pubkey, + ) -> Result, BanksClientError> { + self.context.banks_client.get_account(*address).await + } + + // pub async fn airdrop(&mut self, to: &Pubkey, lamports: u64) -> Result<(), BanksClientError> { + // let transaction = Transaction::new_signed_with_payer( + // &[system_instruction::transfer( + // &self.whale.pubkey(), + // to, + // lamports, + // )], + // Some(&self.whale.pubkey()), + // &[&self.whale], + // self.context.last_blockhash, + // ); + + // self.context + // .banks_client + // .process_transaction(transaction) + // .await + // } + pub async fn warp_slot_incremental( &mut self, incremental_slots: u64, @@ -433,9 +493,10 @@ impl TestBuilder { // Not sure if this is needed self.warp_slot_incremental(1000).await?; - let slot = self.clock().await.slot; + let clock = self.clock().await; + let epoch = clock.epoch; tip_router_client - .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .do_full_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; for vault_root in test_ncn.vaults.iter() { @@ -444,7 +505,7 @@ impl TestBuilder { let mint = vault.supported_mint; tip_router_client - .do_admin_update_weight_table(test_ncn.ncn_root.ncn_pubkey, slot, mint, WEIGHT) + .do_admin_update_weight_table(test_ncn.ncn_root.ncn_pubkey, epoch, mint, WEIGHT) .await?; } @@ -455,10 +516,11 @@ impl TestBuilder { 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; + let clock = self.clock().await; + let epoch = clock.epoch; tip_router_client - .do_initialize_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, slot) + .do_initialize_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; Ok(()) @@ -471,14 +533,16 @@ impl TestBuilder { ) -> TestResult<()> { let mut tip_router_client = self.tip_router_client(); - let slot = self.clock().await.slot; + let clock = self.clock().await; + let epoch = clock.epoch; + 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_initialize_operator_snapshot(operator, ncn, slot) + .do_full_initialize_operator_snapshot(operator, ncn, epoch) .await?; } @@ -492,7 +556,8 @@ impl TestBuilder { ) -> TestResult<()> { let mut tip_router_client = self.tip_router_client(); - let slot = self.clock().await.slot; + let clock = self.clock().await; + let epoch = clock.epoch; let ncn = test_ncn.ncn_root.ncn_pubkey; for operator_root in test_ncn.operators.iter() { @@ -501,7 +566,7 @@ impl TestBuilder { let vault = vault_root.vault_pubkey; tip_router_client - .do_snapshot_vault_operator_delegation(vault, operator, ncn, slot) + .do_snapshot_vault_operator_delegation(vault, operator, ncn, epoch) .await?; } } @@ -523,15 +588,13 @@ impl TestBuilder { // 10 - Initialize Ballot Box pub async fn add_ballot_box_to_test_ncn(&mut self, test_ncn: &TestNcn) -> TestResult<()> { let mut tip_router_client = self.tip_router_client(); - let mut restaking_program_client = self.restaking_program_client(); - let slot = self.clock().await.slot; + let clock = self.clock().await; + let epoch = clock.epoch; let ncn = test_ncn.ncn_root.ncn_pubkey; - let ncn_epoch = restaking_program_client.get_ncn_epoch(slot).await?; - tip_router_client - .do_initialize_ballot_box(ncn, ncn_epoch) + .do_full_initialize_ballot_box(ncn, epoch) .await?; Ok(()) @@ -540,13 +603,11 @@ impl TestBuilder { // 11 - Cast all votes pub async fn cast_votes_for_test_ncn(&mut self, test_ncn: &TestNcn) -> TestResult<()> { let mut tip_router_client = self.tip_router_client(); - let mut restaking_program_client = self.restaking_program_client(); - let slot = self.clock().await.slot; + let clock = self.clock().await; + let epoch = clock.epoch; let ncn = test_ncn.ncn_root.ncn_pubkey; - let ncn_epoch = restaking_program_client.get_ncn_epoch(slot).await?; - let meta_merkle_root = [1u8; 32]; for operator_root in test_ncn.operators.iter() { @@ -558,7 +619,7 @@ impl TestBuilder { operator, &operator_root.operator_admin, meta_merkle_root, - ncn_epoch, + epoch, ) .await?; } @@ -579,18 +640,36 @@ impl TestBuilder { let mut tip_router_client = self.tip_router_client(); let ncn: Pubkey = test_ncn.ncn_root.ncn_pubkey; - let slot = self.clock().await.slot; + let clock = self.clock().await; + let slot = clock.slot; + let epoch = clock.epoch; tip_router_client - .do_initialize_base_reward_router(ncn, slot) + .do_full_initialize_base_reward_router(ncn, slot) .await?; + println!("Payer pubkey: {}", self.context.payer.pubkey()); + println!( + "Payer funds: {}", + self.context + .banks_client + .get_balance(self.context.payer.pubkey()) + .await? + ); for operator_root in test_ncn.operators.iter() { let operator = operator_root.operator_pubkey; for group in NcnFeeGroup::all_groups().iter() { + println!("Payer pubkey: {}", self.context.payer.pubkey()); + println!( + "Payer funds: {}", + self.context + .banks_client + .get_balance(self.context.payer.pubkey()) + .await? + ); tip_router_client - .do_initialize_ncn_reward_router(*group, ncn, operator, slot) + .do_initialize_ncn_reward_router(*group, ncn, operator, epoch) .await?; } } diff --git a/integration_tests/tests/fixtures/tip_router_client.rs b/integration_tests/tests/fixtures/tip_router_client.rs index d5a6f951..1854f2ec 100644 --- a/integration_tests/tests/fixtures/tip_router_client.rs +++ b/integration_tests/tests/fixtures/tip_router_client.rs @@ -11,10 +11,11 @@ use jito_tip_router_client::{ InitializeBaseRewardRouterBuilder, InitializeEpochSnapshotBuilder, InitializeNCNConfigBuilder, InitializeNcnRewardRouterBuilder, InitializeOperatorSnapshotBuilder, InitializeTrackedMintsBuilder, - InitializeWeightTableBuilder, RegisterMintBuilder, RouteBaseRewardsBuilder, - RouteNcnRewardsBuilder, SetConfigFeesBuilder, SetMerkleRootBuilder, SetNewAdminBuilder, - SetTieBreakerBuilder, SetTrackedMintNcnFeeGroupBuilder, - SnapshotVaultOperatorDelegationBuilder, + InitializeWeightTableBuilder, ReallocBallotBoxBuilder, ReallocBaseRewardRouterBuilder, + ReallocOperatorSnapshotBuilder, ReallocWeightTableBuilder, RegisterMintBuilder, + RouteBaseRewardsBuilder, RouteNcnRewardsBuilder, SetConfigFeesBuilder, + SetMerkleRootBuilder, SetNewAdminBuilder, SetTieBreakerBuilder, + SetTrackedMintNcnFeeGroupBuilder, SnapshotVaultOperatorDelegationBuilder, }, types::ConfigAdminRole, }; @@ -22,6 +23,7 @@ use jito_tip_router_core::{ ballot_box::BallotBox, base_fee_group::BaseFeeGroup, base_reward_router::BaseRewardRouter, + constants::MAX_REALLOC_BYTES, epoch_snapshot::{EpochSnapshot, OperatorSnapshot}, error::TipRouterError, ncn_config::NcnConfig, @@ -410,28 +412,28 @@ impl TipRouterClient { .await } - pub async fn do_initialize_weight_table( + pub async fn do_full_initialize_weight_table( &mut self, ncn: Pubkey, - current_slot: u64, + epoch: u64, ) -> TestResult<()> { - self.initialize_weight_table(ncn, current_slot).await + self.do_initialize_weight_table(ncn, epoch).await?; + let num_reallocs = (WeightTable::SIZE as f64 / MAX_REALLOC_BYTES as f64).ceil() as u64 - 1; + self.do_realloc_weight_table(ncn, epoch, num_reallocs) + .await?; + Ok(()) } - pub async fn initialize_weight_table( - &mut self, - ncn: Pubkey, - current_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 = current_slot / restaking_config_account.epoch_length(); + pub async fn do_initialize_weight_table(&mut self, ncn: Pubkey, epoch: u64) -> TestResult<()> { + self.initialize_weight_table(ncn, epoch).await + } + pub async fn initialize_weight_table(&mut self, ncn: Pubkey, epoch: u64) -> TestResult<()> { + let restaking_config = Config::find_program_address(&jito_restaking_program::id()).0; let tracked_mints_pda = 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; + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; let ix = InitializeWeightTableBuilder::new() .restaking_config(restaking_config) @@ -441,6 +443,7 @@ impl TipRouterClient { .payer(self.payer.pubkey()) .restaking_program(jito_restaking_program::id()) .system_program(system_program::id()) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -456,26 +459,23 @@ impl TipRouterClient { pub async fn do_admin_update_weight_table( &mut self, ncn: Pubkey, - current_slot: u64, + epoch: u64, mint: Pubkey, weight: u128, ) -> TestResult<()> { - self.admin_update_weight_table(ncn, current_slot, mint, weight) + self.admin_update_weight_table(ncn, epoch, mint, weight) .await } pub async fn admin_update_weight_table( &mut self, ncn: Pubkey, - current_slot: u64, + epoch: u64, mint: Pubkey, weight: u128, ) -> TestResult<()> { - let restaking_config_account = self.get_restaking_config().await?; - let ncn_epoch = current_slot / restaking_config_account.epoch_length(); - let weight_table = - WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; let ix = AdminUpdateWeightTableBuilder::new() .ncn(ncn) @@ -484,7 +484,7 @@ impl TipRouterClient { .mint(mint) .restaking_program(jito_restaking_program::id()) .weight(weight) - .ncn_epoch(ncn_epoch) + .ncn_epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -597,9 +597,9 @@ impl TipRouterClient { ncn: Pubkey, vault_index: u64, ncn_fee_group: NcnFeeGroup, - slot: u64, + epoch: u64, ) -> TestResult<()> { - self.set_tracked_mint_ncn_fee_group(ncn, vault_index, ncn_fee_group, slot) + self.set_tracked_mint_ncn_fee_group(ncn, vault_index, ncn_fee_group, epoch) .await } @@ -608,12 +608,12 @@ impl TipRouterClient { ncn: Pubkey, vault_index: u64, ncn_fee_group: NcnFeeGroup, - slot: u64, + epoch: 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 ncn_epoch = epoch / restaking_config_account.epoch_length(); let tracked_mints = TrackedMints::find_program_address(&jito_tip_router_program::id(), &ncn).0; @@ -649,24 +649,23 @@ 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 do_initialize_epoch_snapshot( + &mut self, + ncn: Pubkey, + epoch: u64, + ) -> TestResult<()> { + self.initialize_epoch_snapshot(ncn, epoch).await } - pub async fn initialize_epoch_snapshot(&mut self, ncn: Pubkey, slot: u64) -> TestResult<()> { + pub async fn initialize_epoch_snapshot(&mut self, ncn: Pubkey, epoch: 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; - + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; let epoch_snapshot = - EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; let ix = InitializeEpochSnapshotBuilder::new() .ncn_config(config_pda) @@ -678,7 +677,7 @@ impl TipRouterClient { .payer(self.payer.pubkey()) .restaking_program(jito_restaking_program::id()) .system_program(system_program::id()) - .first_slot_of_ncn_epoch(slot) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -691,40 +690,49 @@ impl TipRouterClient { .await } + pub async fn do_full_initialize_operator_snapshot( + &mut self, + operator: Pubkey, + ncn: Pubkey, + epoch: u64, + ) -> TestResult<()> { + self.do_initialize_operator_snapshot(operator, ncn, epoch) + .await?; + let num_reallocs = + (OperatorSnapshot::SIZE as f64 / MAX_REALLOC_BYTES as f64).ceil() as u64 - 1; + self.do_realloc_operator_snapshot(operator, ncn, epoch, num_reallocs) + .await?; + Ok(()) + } + pub async fn do_initialize_operator_snapshot( &mut self, operator: Pubkey, ncn: Pubkey, - slot: u64, + epoch: u64, ) -> TestResult<()> { - self.initialize_operator_snapshot(operator, ncn, slot).await + self.initialize_operator_snapshot(operator, ncn, epoch) + .await } pub async fn initialize_operator_snapshot( &mut self, operator: Pubkey, ncn: Pubkey, - slot: u64, + epoch: 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; - + EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; let operator_snapshot = OperatorSnapshot::find_program_address( &jito_tip_router_program::id(), &operator, &ncn, - ncn_epoch, + epoch, ) .0; @@ -739,7 +747,7 @@ impl TipRouterClient { .payer(self.payer.pubkey()) .restaking_program(jito_restaking_program::id()) .system_program(system_program::id()) - .first_slot_of_ncn_epoch(slot) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -757,9 +765,9 @@ impl TipRouterClient { vault: Pubkey, operator: Pubkey, ncn: Pubkey, - slot: u64, + epoch: u64, ) -> TestResult<()> { - self.snapshot_vault_operator_delegation(vault, operator, ncn, slot) + self.snapshot_vault_operator_delegation(vault, operator, ncn, epoch) .await } @@ -768,23 +776,19 @@ impl TipRouterClient { vault: Pubkey, operator: Pubkey, ncn: Pubkey, - slot: u64, + epoch: 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; - + EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; let operator_snapshot = OperatorSnapshot::find_program_address( &jito_tip_router_program::id(), &operator, &ncn, - ncn_epoch, + epoch, ) .0; @@ -802,7 +806,7 @@ impl TipRouterClient { .0; let weight_table = - WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; let tracked_mints = TrackedMints::find_program_address(&jito_tip_router_program::id(), &ncn).0; @@ -822,7 +826,7 @@ impl TipRouterClient { .operator_snapshot(operator_snapshot) .vault_program(jito_vault_program::id()) .restaking_program(jito_restaking_program::id()) - .first_slot_of_ncn_epoch(slot) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -835,10 +839,21 @@ impl TipRouterClient { .await } + pub async fn do_full_initialize_ballot_box( + &mut self, + ncn: Pubkey, + epoch: u64, + ) -> TestResult<()> { + self.do_initialize_ballot_box(ncn, epoch).await?; + let num_reallocs = (BallotBox::SIZE as f64 / MAX_REALLOC_BYTES as f64).ceil() as u64 - 1; + self.do_realloc_ballot_box(ncn, epoch, num_reallocs).await?; + Ok(()) + } + pub async fn do_initialize_ballot_box( &mut self, ncn: Pubkey, - ncn_epoch: u64, + epoch: u64, ) -> Result<(), TestError> { let ncn_config = jito_tip_router_core::ncn_config::NcnConfig::find_program_address( &jito_tip_router_program::id(), @@ -849,11 +864,11 @@ impl TipRouterClient { let ballot_box = jito_tip_router_core::ballot_box::BallotBox::find_program_address( &jito_tip_router_program::id(), &ncn, - ncn_epoch, + epoch, ) .0; - self.initialize_ballot_box(ncn_config, ballot_box, ncn, ncn_epoch) + self.initialize_ballot_box(ncn_config, ballot_box, ncn, epoch) .await } @@ -882,13 +897,64 @@ impl TipRouterClient { .await } + pub async fn do_realloc_ballot_box( + &mut self, + ncn: Pubkey, + epoch: u64, + num_reallocations: u64, + ) -> Result<(), TestError> { + let ncn_config = jito_tip_router_core::ncn_config::NcnConfig::find_program_address( + &jito_tip_router_program::id(), + &ncn, + ) + .0; + + let ballot_box = jito_tip_router_core::ballot_box::BallotBox::find_program_address( + &jito_tip_router_program::id(), + &ncn, + epoch, + ) + .0; + + self.realloc_ballot_box(ncn_config, ballot_box, ncn, epoch, num_reallocations) + .await + } + + pub async fn realloc_ballot_box( + &mut self, + ncn_config: Pubkey, + ballot_box: Pubkey, + ncn: Pubkey, + epoch: u64, + num_reallocations: u64, + ) -> Result<(), TestError> { + let ix = ReallocBallotBoxBuilder::new() + .ncn_config(ncn_config) + .ballot_box(ballot_box) + .ncn(ncn) + .epoch(epoch) + .payer(self.payer.pubkey()) + .instruction(); + + let ixs = vec![ix; num_reallocations as usize]; + + let blockhash = self.banks_client.get_latest_blockhash().await?; + self.process_transaction(&Transaction::new_signed_with_payer( + &ixs, + Some(&self.payer.pubkey()), + &[&self.payer], + blockhash, + )) + .await + } + pub async fn do_cast_vote( &mut self, ncn: Pubkey, operator: Pubkey, operator_admin: &Keypair, meta_merkle_root: [u8; 32], - ncn_epoch: u64, + epoch: u64, ) -> Result<(), TestError> { let ncn_config = jito_tip_router_core::ncn_config::NcnConfig::find_program_address( &jito_tip_router_program::id(), @@ -899,7 +965,7 @@ impl TipRouterClient { let ballot_box = jito_tip_router_core::ballot_box::BallotBox::find_program_address( &jito_tip_router_program::id(), &ncn, - ncn_epoch, + epoch, ) .0; @@ -907,7 +973,7 @@ impl TipRouterClient { jito_tip_router_core::epoch_snapshot::EpochSnapshot::find_program_address( &jito_tip_router_program::id(), &ncn, - ncn_epoch, + epoch, ) .0; @@ -916,7 +982,7 @@ impl TipRouterClient { &jito_tip_router_program::id(), &operator, &ncn, - ncn_epoch, + epoch, ) .0; @@ -929,7 +995,7 @@ impl TipRouterClient { operator, operator_admin, meta_merkle_root, - ncn_epoch, + epoch, ) .await } @@ -1119,20 +1185,30 @@ impl TipRouterClient { .await } + pub async fn do_full_initialize_base_reward_router( + &mut self, + ncn: Pubkey, + epoch: u64, + ) -> TestResult<()> { + self.do_initialize_base_reward_router(ncn, epoch).await?; + let num_reallocs = + (BaseRewardRouter::SIZE as f64 / MAX_REALLOC_BYTES as f64).ceil() as u64 - 1; + self.do_realloc_base_reward_router(ncn, epoch, num_reallocs) + .await?; + Ok(()) + } + pub async fn do_initialize_base_reward_router( &mut self, ncn: Pubkey, - slot: u64, + epoch: 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 (base_reward_router, _, _) = - BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); + BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, epoch); - self.initialize_base_reward_router(restaking_config, ncn, base_reward_router) + self.initialize_base_reward_router(restaking_config, ncn, base_reward_router, epoch) .await } @@ -1141,6 +1217,7 @@ impl TipRouterClient { restaking_config: Pubkey, ncn: Pubkey, base_reward_router: Pubkey, + epoch: u64, ) -> TestResult<()> { let ix = InitializeBaseRewardRouterBuilder::new() .restaking_config(restaking_config) @@ -1149,6 +1226,7 @@ impl TipRouterClient { .payer(self.payer.pubkey()) .restaking_program(jito_restaking_program::id()) .system_program(system_program::id()) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -1166,19 +1244,16 @@ impl TipRouterClient { ncn_fee_group: NcnFeeGroup, ncn: Pubkey, operator: Pubkey, - slot: u64, + epoch: 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 (ncn_reward_router, _, _) = NcnRewardRouter::find_program_address( &jito_tip_router_program::id(), ncn_fee_group, &operator, &ncn, - ncn_epoch, + epoch, ); self.initialize_ncn_reward_router( @@ -1187,6 +1262,7 @@ impl TipRouterClient { operator, restaking_config, ncn_reward_router, + epoch, ) .await } @@ -1198,6 +1274,7 @@ impl TipRouterClient { operator: Pubkey, restaking_config: Pubkey, ncn_reward_router: Pubkey, + epoch: u64, ) -> TestResult<()> { let ix = InitializeNcnRewardRouterBuilder::new() .restaking_config(restaking_config) @@ -1208,6 +1285,7 @@ impl TipRouterClient { .restaking_program(jito_restaking_program::id()) .system_program(system_program::id()) .ncn_fee_group(ncn_fee_group.group) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -1220,11 +1298,11 @@ impl TipRouterClient { .await } - pub async fn do_route_base_rewards(&mut self, ncn: Pubkey, slot: u64) -> TestResult<()> { + pub async fn do_route_base_rewards(&mut self, ncn: Pubkey, epoch: 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 ncn_epoch = epoch / restaking_config_account.epoch_length(); let (epoch_snapshot, _, _) = EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); @@ -1277,12 +1355,12 @@ impl TipRouterClient { ncn_fee_group: NcnFeeGroup, ncn: Pubkey, operator: Pubkey, - slot: u64, + epoch: 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 ncn_epoch = epoch / restaking_config_account.epoch_length(); let (operator_snapshot, _, _) = OperatorSnapshot::find_program_address( &jito_tip_router_program::id(), @@ -1343,12 +1421,12 @@ impl TipRouterClient { &mut self, base_fee_group: BaseFeeGroup, ncn: Pubkey, - slot: u64, + epoch: 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 ncn_epoch = epoch / restaking_config_account.epoch_length(); let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); @@ -1407,12 +1485,12 @@ impl TipRouterClient { ncn_fee_group: NcnFeeGroup, operator: Pubkey, ncn: Pubkey, - slot: u64, + epoch: 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 ncn_epoch = epoch / restaking_config_account.epoch_length(); let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); @@ -1476,12 +1554,12 @@ impl TipRouterClient { ncn_fee_group: NcnFeeGroup, operator: Pubkey, ncn: Pubkey, - slot: u64, + epoch: 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 ncn_epoch = epoch / restaking_config_account.epoch_length(); let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); @@ -1540,12 +1618,12 @@ impl TipRouterClient { vault: Pubkey, operator: Pubkey, ncn: Pubkey, - slot: u64, + epoch: 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 ncn_epoch = epoch / restaking_config_account.epoch_length(); let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); @@ -1599,6 +1677,182 @@ impl TipRouterClient { )) .await } + pub async fn do_realloc_operator_snapshot( + &mut self, + operator: Pubkey, + ncn: Pubkey, + epoch: u64, + num_reallocations: u64, + ) -> Result<(), TestError> { + let ncn_config = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0; + let restaking_config = Config::find_program_address(&jito_restaking_program::id()).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, epoch).0; + let operator_snapshot = OperatorSnapshot::find_program_address( + &jito_tip_router_program::id(), + &operator, + &ncn, + epoch, + ) + .0; + + self.realloc_operator_snapshot( + ncn_config, + restaking_config, + ncn, + operator, + ncn_operator_state, + epoch_snapshot, + operator_snapshot, + epoch, + num_reallocations, + ) + .await + } + + pub async fn realloc_operator_snapshot( + &mut self, + ncn_config: Pubkey, + restaking_config: Pubkey, + ncn: Pubkey, + operator: Pubkey, + ncn_operator_state: Pubkey, + epoch_snapshot: Pubkey, + operator_snapshot: Pubkey, + epoch: u64, + num_reallocations: u64, + ) -> Result<(), TestError> { + let ix = ReallocOperatorSnapshotBuilder::new() + .ncn_config(ncn_config) + .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()) + .epoch(epoch) + .instruction(); + + let ixs = vec![ix; num_reallocations as usize]; + + let blockhash = self.banks_client.get_latest_blockhash().await?; + self.process_transaction(&Transaction::new_signed_with_payer( + &ixs, + Some(&self.payer.pubkey()), + &[&self.payer], + blockhash, + )) + .await + } + + pub async fn do_realloc_base_reward_router( + &mut self, + ncn: Pubkey, + epoch: u64, + num_reallocations: u64, + ) -> Result<(), TestError> { + let ncn_config = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0; + let base_reward_router = + BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; + + self.realloc_base_reward_router( + ncn_config, + base_reward_router, + ncn, + epoch, + num_reallocations, + ) + .await + } + + pub async fn realloc_base_reward_router( + &mut self, + ncn_config: Pubkey, + base_reward_router: Pubkey, + ncn: Pubkey, + epoch: u64, + num_reallocations: u64, + ) -> Result<(), TestError> { + let ix = ReallocBaseRewardRouterBuilder::new() + .ncn_config(ncn_config) + .base_reward_router(base_reward_router) + .ncn(ncn) + .epoch(epoch) + .payer(self.payer.pubkey()) + .system_program(system_program::id()) + .instruction(); + + let ixs = vec![ix; num_reallocations as usize]; + + let blockhash = self.banks_client.get_latest_blockhash().await?; + self.process_transaction(&Transaction::new_signed_with_payer( + &ixs, + Some(&self.payer.pubkey()), + &[&self.payer], + blockhash, + )) + .await + } + + pub async fn do_realloc_weight_table( + &mut self, + ncn: Pubkey, + epoch: u64, + num_reallocations: u64, + ) -> Result<(), TestError> { + let ncn_config = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0; + let weight_table = + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; + let tracked_mints = + TrackedMints::find_program_address(&jito_tip_router_program::id(), &ncn).0; + + self.realloc_weight_table( + ncn_config, + weight_table, + ncn, + tracked_mints, + epoch, + num_reallocations, + ) + .await + } + + pub async fn realloc_weight_table( + &mut self, + ncn_config: Pubkey, + weight_table: Pubkey, + ncn: Pubkey, + tracked_mints: Pubkey, + epoch: u64, + num_reallocations: u64, + ) -> Result<(), TestError> { + let ix = ReallocWeightTableBuilder::new() + .ncn_config(ncn_config) + .weight_table(weight_table) + .ncn(ncn) + .tracked_mints(tracked_mints) + .epoch(epoch) + .payer(self.payer.pubkey()) + .system_program(system_program::id()) + .instruction(); + + let ixs = vec![ix; num_reallocations as usize]; + + let blockhash = self.banks_client.get_latest_blockhash().await?; + self.process_transaction(&Transaction::new_signed_with_payer( + &ixs, + Some(&self.payer.pubkey()), + &[&self.payer], + blockhash, + )) + .await + } } #[inline(always)] 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 b3c472c1..b7bf0b98 100644 --- a/integration_tests/tests/tip_router/admin_update_weight_table.rs +++ b/integration_tests/tests/tip_router/admin_update_weight_table.rs @@ -13,10 +13,11 @@ mod tests { fixture.warp_slot_incremental(1000).await?; - let slot = fixture.clock().await.slot; + let clock = fixture.clock().await; + let epoch = clock.epoch; tip_router_client - .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .do_full_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; let vault_root = test_ncn.vaults[0].clone(); @@ -26,7 +27,7 @@ mod tests { let weight = 100; tip_router_client - .do_admin_update_weight_table(test_ncn.ncn_root.ncn_pubkey, slot, mint, weight) + .do_admin_update_weight_table(test_ncn.ncn_root.ncn_pubkey, epoch, mint, weight) .await?; Ok(()) diff --git a/integration_tests/tests/tip_router/cast_vote.rs b/integration_tests/tests/tip_router/cast_vote.rs index a1c4eb4d..327c7e97 100644 --- a/integration_tests/tests/tip_router/cast_vote.rs +++ b/integration_tests/tests/tip_router/cast_vote.rs @@ -21,11 +21,10 @@ mod tests { let slot = clock.slot; let ncn = test_ncn.ncn_root.ncn_pubkey; let operator = test_ncn.operators[0].operator_pubkey; - let restaking_config_account = tip_router_client.get_restaking_config().await?; - let ncn_epoch = slot / restaking_config_account.epoch_length(); + let epoch = clock.epoch; tip_router_client - .do_initialize_ballot_box(ncn, ncn_epoch) + .do_full_initialize_ballot_box(ncn, epoch) .await?; let meta_merkle_root = [1u8; 32]; @@ -33,10 +32,10 @@ mod tests { let operator_admin = &test_ncn.operators[0].operator_admin; tip_router_client - .do_cast_vote(ncn, operator, operator_admin, meta_merkle_root, ncn_epoch) + .do_cast_vote(ncn, operator, operator_admin, meta_merkle_root, epoch) .await?; - let ballot_box = tip_router_client.get_ballot_box(ncn, ncn_epoch).await?; + let ballot_box = tip_router_client.get_ballot_box(ncn, epoch).await?; assert!(ballot_box.has_ballot(&Ballot::new(meta_merkle_root))); assert_eq!(ballot_box.slot_consensus_reached(), slot); diff --git a/integration_tests/tests/tip_router/distribute_rewards.rs b/integration_tests/tests/tip_router/distribute_rewards.rs index 5e221dc6..23106873 100644 --- a/integration_tests/tests/tip_router/distribute_rewards.rs +++ b/integration_tests/tests/tip_router/distribute_rewards.rs @@ -83,6 +83,8 @@ mod tests { let restaking_config_account = tip_router_client.get_restaking_config().await?; let ncn_epoch = slot / restaking_config_account.epoch_length(); let ncn_config = tip_router_client.get_ncn_config(ncn).await?; + let clock = fixture.clock().await; + let epoch = clock.epoch; let dao_initial_balance = fixture .get_balance( @@ -111,7 +113,7 @@ mod tests { // Route in 3_000 lamports let (base_reward_router, _, _) = - BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); + BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, epoch); // Send rewards to base reward router let sol_rewards = lamports_to_sol(3_000); @@ -124,9 +126,8 @@ mod tests { tip_router_client.do_route_base_rewards(ncn, slot).await?; // Check Rewards - let base_reward_router_account = tip_router_client - .get_base_reward_router(ncn, ncn_epoch) - .await?; + let base_reward_router_account = + tip_router_client.get_base_reward_router(ncn, epoch).await?; let dao_rewards = base_reward_router_account .base_fee_group_reward(BaseFeeGroup::default()) @@ -194,12 +195,12 @@ mod tests { let mut total_rewards = 0; for group in NcnFeeGroup::all_groups().iter() { tip_router_client - .do_route_ncn_rewards(*group, ncn, operator, slot) + .do_route_ncn_rewards(*group, ncn, operator, epoch) .await?; // Distribute to operators tip_router_client - .do_distribute_ncn_operator_rewards(*group, operator, ncn, slot) + .do_distribute_ncn_operator_rewards(*group, operator, ncn, epoch) .await?; // Distribute to vaults @@ -208,7 +209,7 @@ mod tests { { let ncn_reward_router = tip_router_client - .get_ncn_reward_router(*group, operator, ncn, ncn_epoch) + .get_ncn_reward_router(*group, operator, ncn, epoch) .await?; // Skip if the vault is not in the reward route @@ -223,7 +224,7 @@ mod tests { } let ncn_router = tip_router_client - .get_ncn_reward_router(*group, operator, ncn, ncn_epoch) + .get_ncn_reward_router(*group, operator, ncn, epoch) .await?; println!("\nTotal Rewards: {}", ncn_router.total_rewards()); diff --git a/integration_tests/tests/tip_router/initialize_ballot_box.rs b/integration_tests/tests/tip_router/initialize_ballot_box.rs index b6c06c67..5fb7476a 100644 --- a/integration_tests/tests/tip_router/initialize_ballot_box.rs +++ b/integration_tests/tests/tip_router/initialize_ballot_box.rs @@ -1,7 +1,12 @@ #[cfg(test)] mod tests { - use jito_tip_router_core::constants::DEFAULT_CONSENSUS_REACHED_SLOT; + use std::borrow::Borrow; + + use jito_tip_router_core::{ + ballot_box::BallotBox, + constants::{DEFAULT_CONSENSUS_REACHED_SLOT, MAX_REALLOC_BYTES}, + }; use crate::fixtures::{test_builder::TestBuilder, TestResult}; @@ -18,10 +23,26 @@ mod tests { let ncn = test_ncn.ncn_root.ncn_pubkey; + let num_reallocs = (jito_tip_router_core::ballot_box::BallotBox::SIZE as f64 + / jito_tip_router_core::constants::MAX_REALLOC_BYTES as f64) + .ceil() as u64 + - 1; + tip_router_client .do_initialize_ballot_box(ncn, epoch) .await?; + let address = + BallotBox::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; + let raw_account = fixture.get_account(&address).await?.unwrap(); + assert_eq!(raw_account.data.len(), MAX_REALLOC_BYTES as usize); + assert_eq!(raw_account.owner, jito_tip_router_program::id()); + assert_eq!(raw_account.data[0], 0); + + tip_router_client + .do_realloc_ballot_box(ncn, epoch, num_reallocs) + .await?; + let ballot_box = tip_router_client.get_ballot_box(ncn, epoch).await?; assert_eq!(ballot_box.epoch(), epoch); diff --git a/integration_tests/tests/tip_router/initialize_base_reward_router.rs b/integration_tests/tests/tip_router/initialize_base_reward_router.rs index 8806585d..836b4b4c 100644 --- a/integration_tests/tests/tip_router/initialize_base_reward_router.rs +++ b/integration_tests/tests/tip_router/initialize_base_reward_router.rs @@ -1,6 +1,10 @@ #[cfg(test)] mod tests { + use jito_tip_router_core::{ + base_reward_router::BaseRewardRouter, constants::MAX_REALLOC_BYTES, + }; + use crate::fixtures::{test_builder::TestBuilder, TestResult}; #[tokio::test] @@ -17,27 +21,41 @@ mod tests { fixture.vote_test_ncn(&test_ncn).await?; ////// - let slot = fixture.clock().await.slot; + let clock = fixture.clock().await; + let epoch = clock.epoch; + let slot = clock.slot; let ncn = test_ncn.ncn_root.ncn_pubkey; // Initialize base reward router tip_router_client - .do_initialize_base_reward_router(ncn, slot) + .do_initialize_base_reward_router(ncn, epoch) .await?; - // Get base reward router and verify it was initialized correctly - let restaking_config_account = tip_router_client.get_restaking_config().await?; - let ncn_epoch = slot / restaking_config_account.epoch_length(); - let base_reward_router = tip_router_client - .get_base_reward_router(ncn, ncn_epoch) + // Check initial size is MAX_REALLOC_BYTES + let address = + BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; + let raw_account = fixture.get_account(&address).await?.unwrap(); + assert_eq!(raw_account.data.len(), MAX_REALLOC_BYTES as usize); + assert_eq!(raw_account.owner, jito_tip_router_program::id()); + assert_eq!(raw_account.data[0], 0); + + // Calculate number of reallocs needed + let num_reallocs = + (BaseRewardRouter::SIZE as f64 / MAX_REALLOC_BYTES as f64).ceil() as u64 - 1; + + // Realloc to full size + tip_router_client + .do_realloc_base_reward_router(ncn, epoch, num_reallocs) .await?; + // Get base reward router and verify it was initialized correctly + let base_reward_router = tip_router_client.get_base_reward_router(ncn, epoch).await?; + // Verify initial state assert_eq!(base_reward_router.reward_pool(), 0); assert_eq!(base_reward_router.rewards_processed(), 0); assert_eq!(base_reward_router.total_rewards(), 0); assert_eq!(base_reward_router.ncn(), &ncn); - assert_eq!(base_reward_router.ncn_epoch(), ncn_epoch); assert_eq!(base_reward_router.slot_created(), slot); Ok(()) diff --git a/integration_tests/tests/tip_router/initialize_epoch_snapshot.rs b/integration_tests/tests/tip_router/initialize_epoch_snapshot.rs index 2a417c6c..1d6e977a 100644 --- a/integration_tests/tests/tip_router/initialize_epoch_snapshot.rs +++ b/integration_tests/tests/tip_router/initialize_epoch_snapshot.rs @@ -16,7 +16,7 @@ mod tests { let slot = fixture.clock().await.slot; tip_router_client - .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .do_full_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) .await?; let vault_root = test_ncn.vaults[0].clone(); diff --git a/integration_tests/tests/tip_router/initialize_ncn_reward_router.rs b/integration_tests/tests/tip_router/initialize_ncn_reward_router.rs index 828355b7..9c057a5b 100644 --- a/integration_tests/tests/tip_router/initialize_ncn_reward_router.rs +++ b/integration_tests/tests/tip_router/initialize_ncn_reward_router.rs @@ -20,27 +20,27 @@ mod tests { fixture.vote_test_ncn(&test_ncn).await?; ////// - let slot = fixture.clock().await.slot; + let clock = fixture.clock().await; + let slot = clock.slot; + let epoch = clock.epoch; let ncn = test_ncn.ncn_root.ncn_pubkey; let operator = test_ncn.operators[0].operator_pubkey; let ncn_fee_group = NcnFeeGroup::default(); // Initialize NCN reward router tip_router_client - .do_initialize_ncn_reward_router(ncn_fee_group, ncn, operator, slot) + .do_initialize_ncn_reward_router(ncn_fee_group, ncn, operator, epoch) .await?; // Get NCN reward router and verify initialization - let restaking_config_account = tip_router_client.get_restaking_config().await?; - let ncn_epoch = slot / restaking_config_account.epoch_length(); let ncn_reward_router = tip_router_client - .get_ncn_reward_router(ncn_fee_group, operator, ncn, ncn_epoch) + .get_ncn_reward_router(ncn_fee_group, operator, ncn, epoch) .await?; // Verify initial state assert_eq!(ncn_reward_router.ncn(), ncn); assert_eq!(ncn_reward_router.operator(), operator); - assert_eq!(ncn_reward_router.ncn_epoch(), ncn_epoch); + assert_eq!(ncn_reward_router.ncn_epoch(), epoch); assert_eq!(ncn_reward_router.slot_created(), slot); assert_eq!(ncn_reward_router.reward_pool(), 0); assert_eq!(ncn_reward_router.rewards_processed(), 0); diff --git a/integration_tests/tests/tip_router/initialize_operator_snapshot.rs b/integration_tests/tests/tip_router/initialize_operator_snapshot.rs index 989db819..41416d48 100644 --- a/integration_tests/tests/tip_router/initialize_operator_snapshot.rs +++ b/integration_tests/tests/tip_router/initialize_operator_snapshot.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod tests { + use jito_tip_router_core::{constants::MAX_REALLOC_BYTES, epoch_snapshot::OperatorSnapshot}; + use crate::fixtures::{test_builder::TestBuilder, TestResult}; #[tokio::test] @@ -13,33 +15,49 @@ mod tests { fixture.warp_slot_incremental(1000).await?; - let slot = fixture.clock().await.slot; + fixture.add_weights_for_test_ncn(&test_ncn).await?; + fixture.add_epoch_snapshot_to_test_ncn(&test_ncn).await?; + + let clock = fixture.clock().await; + let epoch = clock.epoch; + let ncn = test_ncn.ncn_root.ncn_pubkey; + let operator = test_ncn.operators[0].operator_pubkey; + // Initialize operator snapshot tip_router_client - .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .do_initialize_operator_snapshot(operator, ncn, epoch) .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?; + // Check initial size is MAX_REALLOC_BYTES + let address = OperatorSnapshot::find_program_address( + &jito_tip_router_program::id(), + &operator, + &ncn, + epoch, + ) + .0; + let raw_account = fixture.get_account(&address).await?.unwrap(); + assert_eq!(raw_account.data.len(), MAX_REALLOC_BYTES as usize); + assert_eq!(raw_account.owner, jito_tip_router_program::id()); + assert_eq!(raw_account.data[0], 0); - let mint = vault.supported_mint; - let weight = 100; + // Calculate number of reallocs needed + let num_reallocs = + (OperatorSnapshot::SIZE as f64 / MAX_REALLOC_BYTES as f64).ceil() as u64 - 1; + // Realloc to full size tip_router_client - .do_admin_update_weight_table(ncn, slot, mint, weight) + .do_realloc_operator_snapshot(operator, ncn, epoch, num_reallocs) .await?; - tip_router_client - .do_initialize_epoch_snapshot(ncn, slot) + // Get operator snapshot and verify it was initialized correctly + let operator_snapshot = tip_router_client + .get_operator_snapshot(operator, ncn, epoch) .await?; - let operator = test_ncn.operators[0].operator_pubkey; - - tip_router_client - .do_initialize_operator_snapshot(operator, ncn, slot) - .await?; + // Verify initial state + assert_eq!(operator_snapshot.operator(), operator); + assert_eq!(operator_snapshot.ncn(), ncn); Ok(()) } diff --git a/integration_tests/tests/tip_router/initialize_weight_table.rs b/integration_tests/tests/tip_router/initialize_weight_table.rs index 18a0e36d..cfc1a6f1 100644 --- a/integration_tests/tests/tip_router/initialize_weight_table.rs +++ b/integration_tests/tests/tip_router/initialize_weight_table.rs @@ -1,5 +1,7 @@ #[cfg(test)] mod tests { + use jito_bytemuck::Discriminator; + use jito_tip_router_core::{constants::MAX_REALLOC_BYTES, weight_table::WeightTable}; use crate::fixtures::{test_builder::TestBuilder, TestResult}; @@ -12,12 +14,37 @@ mod tests { fixture.warp_slot_incremental(1000).await?; - let slot = fixture.clock().await.slot; + let clock = fixture.clock().await; + let epoch = clock.epoch; + let ncn = test_ncn.ncn_root.ncn_pubkey; tip_router_client - .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .do_initialize_weight_table(ncn, epoch) .await?; + let address = + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; + let raw_account = fixture.get_account(&address).await?.unwrap(); + assert_eq!(raw_account.data.len(), MAX_REALLOC_BYTES as usize); + assert_eq!(raw_account.owner, jito_tip_router_program::id()); + assert_eq!(raw_account.data[0], 0); + + let num_reallocs = (WeightTable::SIZE as f64 / MAX_REALLOC_BYTES as f64).ceil() as u64 - 1; + + tip_router_client + .do_realloc_weight_table(ncn, epoch, num_reallocs) + .await?; + + let raw_account = fixture.get_account(&address).await?.unwrap(); + assert_eq!(raw_account.data.len(), WeightTable::SIZE as usize); + assert_eq!(raw_account.owner, jito_tip_router_program::id()); + assert_eq!(raw_account.data[0], WeightTable::DISCRIMINATOR); + + let weight_table = tip_router_client.get_weight_table(ncn, epoch).await?; + + assert_eq!(weight_table.ncn(), ncn); + assert_eq!(weight_table.ncn_epoch(), epoch); + Ok(()) } } diff --git a/integration_tests/tests/tip_router/meta_tests.rs b/integration_tests/tests/tip_router/meta_tests.rs index 2c2874c8..eaee63ce 100644 --- a/integration_tests/tests/tip_router/meta_tests.rs +++ b/integration_tests/tests/tip_router/meta_tests.rs @@ -52,11 +52,11 @@ mod tests { .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 clock = fixture.clock().await; + let epoch = clock.epoch; let epoch_snapshot = tip_router_client - .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; assert!(epoch_snapshot.finalized()); @@ -64,7 +64,7 @@ mod tests { fixture.vote_test_ncn(&test_ncn).await?; let ballot_box = tip_router_client - .get_ballot_box(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .get_ballot_box(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; assert!(ballot_box.has_winning_ballot()); @@ -88,11 +88,11 @@ mod tests { .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 clock = fixture.clock().await; + let epoch = clock.epoch; let epoch_snapshot = tip_router_client - .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; assert!(epoch_snapshot.finalized()); @@ -100,7 +100,7 @@ mod tests { fixture.vote_test_ncn(&test_ncn).await?; let ballot_box = tip_router_client - .get_ballot_box(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .get_ballot_box(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; assert!(ballot_box.has_winning_ballot()); @@ -124,11 +124,11 @@ mod tests { .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 clock = fixture.clock().await; + let epoch = clock.epoch; let epoch_snapshot = tip_router_client - .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; assert!(epoch_snapshot.finalized()); @@ -136,7 +136,7 @@ mod tests { fixture.vote_test_ncn(&test_ncn).await?; let ballot_box = tip_router_client - .get_ballot_box(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .get_ballot_box(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; assert!(ballot_box.has_winning_ballot()); @@ -150,7 +150,6 @@ mod tests { 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; @@ -160,11 +159,11 @@ mod tests { .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 clock = fixture.clock().await; + let epoch = clock.epoch; let epoch_snapshot = tip_router_client - .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .get_epoch_snapshot(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; assert!(epoch_snapshot.finalized()); @@ -172,7 +171,7 @@ mod tests { fixture.vote_test_ncn(&test_ncn).await?; let ballot_box = tip_router_client - .get_ballot_box(test_ncn.ncn_root.ncn_pubkey, ncn_epoch) + .get_ballot_box(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; assert!(ballot_box.has_winning_ballot()); diff --git a/integration_tests/tests/tip_router/register_mint.rs b/integration_tests/tests/tip_router/register_mint.rs index 7f47ae05..4605a82f 100644 --- a/integration_tests/tests/tip_router/register_mint.rs +++ b/integration_tests/tests/tip_router/register_mint.rs @@ -183,6 +183,7 @@ mod tests { } #[tokio::test] + // TODO: FLAKY!!!! >:( async fn test_register_mint_fails_with_weight_table() -> TestResult<()> { let mut fixture = TestBuilder::new().await; let mut tip_router_client = fixture.tip_router_client(); diff --git a/integration_tests/tests/tip_router/set_tie_breaker.rs b/integration_tests/tests/tip_router/set_tie_breaker.rs index dde9af5b..9e6fbef3 100644 --- a/integration_tests/tests/tip_router/set_tie_breaker.rs +++ b/integration_tests/tests/tip_router/set_tie_breaker.rs @@ -17,13 +17,11 @@ mod tests { fixture.snapshot_test_ncn(&test_ncn).await?; let clock = fixture.clock().await; - let slot = clock.slot; - let restaking_config_account = tip_router_client.get_restaking_config().await?; - let ncn_epoch = slot / restaking_config_account.epoch_length(); + let epoch = clock.epoch; let ncn = test_ncn.ncn_root.ncn_pubkey; tip_router_client - .do_initialize_ballot_box(ncn, ncn_epoch) + .do_full_initialize_ballot_box(ncn, epoch) .await?; let meta_merkle_root = [1; 32]; @@ -34,10 +32,10 @@ mod tests { // Cast a vote so that this vote is one of the valid options // Gets to 50% consensus weight tip_router_client - .do_cast_vote(ncn, operator, operator_admin, meta_merkle_root, ncn_epoch) + .do_cast_vote(ncn, operator, operator_admin, meta_merkle_root, epoch) .await?; - let ballot_box = tip_router_client.get_ballot_box(ncn, ncn_epoch).await?; + let ballot_box = tip_router_client.get_ballot_box(ncn, epoch).await?; assert!(ballot_box.has_ballot(&Ballot::new(meta_merkle_root))); assert_eq!( ballot_box.slot_consensus_reached(), @@ -49,10 +47,10 @@ mod tests { fixture.warp_slot_incremental(1000000).await?; tip_router_client - .do_set_tie_breaker(ncn, meta_merkle_root, ncn_epoch) + .do_set_tie_breaker(ncn, meta_merkle_root, epoch) .await?; - let ballot_box = tip_router_client.get_ballot_box(ncn, ncn_epoch).await?; + let ballot_box = tip_router_client.get_ballot_box(ncn, epoch).await?; let ballot = Ballot::new(meta_merkle_root); assert!(ballot_box.has_ballot(&ballot)); diff --git a/integration_tests/tests/tip_router/set_tracked_mint_ncn_fee_group.rs b/integration_tests/tests/tip_router/set_tracked_mint_ncn_fee_group.rs index ec789ce7..d5b61707 100644 --- a/integration_tests/tests/tip_router/set_tracked_mint_ncn_fee_group.rs +++ b/integration_tests/tests/tip_router/set_tracked_mint_ncn_fee_group.rs @@ -182,7 +182,7 @@ mod tests { } tip_router_client - .initialize_weight_table(ncn_root.ncn_pubkey, fixture.clock().await.slot) + .do_full_initialize_weight_table(ncn_root.ncn_pubkey, fixture.clock().await.epoch) .await?; // Should fail diff --git a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs index b85083f4..f995e7bd 100644 --- a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs +++ b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs @@ -1,10 +1,12 @@ #[cfg(test)] mod tests { + use jito_tip_router_core::weight_table::WeightTable; + use crate::fixtures::{test_builder::TestBuilder, TestResult}; #[tokio::test] - async fn test_initialize_operator_snapshot() -> TestResult<()> { + async fn test_snapshot_vault_operator_delegation() -> TestResult<()> { let mut fixture = TestBuilder::new().await; let mut vault_client = fixture.vault_program_client(); let mut tip_router_client = fixture.tip_router_client(); @@ -13,12 +15,26 @@ mod tests { fixture.warp_slot_incremental(1000).await?; - let slot = fixture.clock().await.slot; + let epoch = fixture.clock().await.epoch; tip_router_client - .do_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, slot) + .do_full_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; + let weight_table_pda = WeightTable::find_program_address( + &jito_tip_router_program::id(), + &test_ncn.ncn_root.ncn_pubkey, + epoch, + ) + .0; + let raw_account = fixture.get_account(&weight_table_pda).await?; + println!("raw_account: {:?}", raw_account); + // let weight_table_account = tip_router_client + // .get_weight_table(test_ncn.ncn_root.ncn_pubkey, epoch) + // .await?; + + // println!("weight_table_account: {:?}", weight_table_account); + let ncn = test_ncn.ncn_root.ncn_pubkey; let vault_root = test_ncn.vaults[0].clone(); @@ -29,21 +45,21 @@ mod tests { let weight = 100; tip_router_client - .do_admin_update_weight_table(ncn, slot, mint, weight) + .do_admin_update_weight_table(ncn, epoch, mint, weight) .await?; tip_router_client - .do_initialize_epoch_snapshot(ncn, slot) + .do_initialize_epoch_snapshot(ncn, epoch) .await?; let operator = test_ncn.operators[0].operator_pubkey; tip_router_client - .do_initialize_operator_snapshot(operator, ncn, slot) + .do_full_initialize_operator_snapshot(operator, ncn, epoch) .await?; tip_router_client - .do_snapshot_vault_operator_delegation(vault_address, operator, ncn, slot) + .do_snapshot_vault_operator_delegation(vault_address, operator, ncn, epoch) .await?; Ok(()) diff --git a/program/src/cast_vote.rs b/program/src/cast_vote.rs index d292d020..6e4b60c4 100644 --- a/program/src/cast_vote.rs +++ b/program/src/cast_vote.rs @@ -28,11 +28,16 @@ pub fn process_cast_vote( load_signer(operator_admin, false)?; NcnConfig::load(program_id, ncn.key, ncn_config, false)?; + msg!("Loaded NcnConfig"); Ncn::load(restaking_program.key, ncn, false)?; + msg!("Loaded Ncn"); Operator::load(restaking_program.key, operator, false)?; + msg!("Loaded Operator"); BallotBox::load(program_id, ncn.key, epoch, ballot_box, true)?; + msg!("Loaded BallotBox"); EpochSnapshot::load(program_id, ncn.key, epoch, epoch_snapshot, false)?; + msg!("Loaded EpochSnapshot"); OperatorSnapshot::load( program_id, operator.key, @@ -41,7 +46,7 @@ pub fn process_cast_vote( operator_snapshot, false, )?; - + msg!("Loaded OperatorSnapshot"); let operator_data = operator.data.borrow(); let operator_account = Operator::try_from_slice_unchecked(&operator_data)?; diff --git a/program/src/initialize_ballot_box.rs b/program/src/initialize_ballot_box.rs index 550fe19c..54482e5e 100644 --- a/program/src/initialize_ballot_box.rs +++ b/program/src/initialize_ballot_box.rs @@ -3,10 +3,12 @@ use jito_jsm_core::{ create_account, loader::{load_signer, load_system_account, load_system_program}, }; -use jito_tip_router_core::{ballot_box::BallotBox, ncn_config::NcnConfig}; +use jito_tip_router_core::{ + ballot_box::BallotBox, constants::MAX_REALLOC_BYTES, ncn_config::NcnConfig, +}; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, - program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; pub fn process_initialize_ballot_box( @@ -40,17 +42,9 @@ pub fn process_initialize_ballot_box( system_program, program_id, &Rent::get()?, - 8_u64 - .checked_add(std::mem::size_of::() as u64) - .unwrap(), + MAX_REALLOC_BYTES, &ballot_box_seeds, )?; - let mut ballot_box_data = ballot_box.try_borrow_mut_data()?; - ballot_box_data[0] = BallotBox::DISCRIMINATOR; - let ballot_box_account = BallotBox::try_from_slice_unchecked_mut(&mut ballot_box_data)?; - - ballot_box_account.initialize(*ncn_account.key, epoch, ballot_box_bump, Clock::get()?.slot); - Ok(()) } diff --git a/program/src/initialize_base_reward_router.rs b/program/src/initialize_base_reward_router.rs index e7eeaa5c..ec17348e 100644 --- a/program/src/initialize_base_reward_router.rs +++ b/program/src/initialize_base_reward_router.rs @@ -1,22 +1,19 @@ -use std::mem::size_of; - -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::{base_reward_router::BaseRewardRouter, loaders::load_ncn_epoch}; +use jito_tip_router_core::{base_reward_router::BaseRewardRouter, constants::MAX_REALLOC_BYTES}; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; /// Can be backfilled for previous epochs pub fn process_initialize_base_reward_router( program_id: &Pubkey, accounts: &[AccountInfo], - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, ncn, base_reward_router, payer, restaking_program, system_program] = accounts @@ -36,15 +33,12 @@ pub fn process_initialize_base_reward_router( 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 (base_reward_router_pubkey, base_reward_router_bump, mut base_reward_router_seeds) = - BaseRewardRouter::find_program_address(program_id, ncn.key, ncn_epoch); + BaseRewardRouter::find_program_address(program_id, ncn.key, epoch); base_reward_router_seeds.push(vec![base_reward_router_bump]); if base_reward_router_pubkey.ne(base_reward_router.key) { - msg!("Incorrect epoch reward router PDA"); + msg!("Incorrect base reward router PDA"); return Err(ProgramError::InvalidAccountData); } @@ -52,7 +46,7 @@ pub fn process_initialize_base_reward_router( "Initializing Base Reward Router {} for NCN: {} at epoch: {}", base_reward_router.key, ncn.key, - ncn_epoch + epoch ); create_account( payer, @@ -60,19 +54,9 @@ pub fn process_initialize_base_reward_router( system_program, program_id, &Rent::get()?, - 8_u64 - .checked_add(size_of::() as u64) - .unwrap(), + MAX_REALLOC_BYTES, &base_reward_router_seeds, )?; - let mut base_reward_router_data = base_reward_router.try_borrow_mut_data()?; - base_reward_router_data[0] = BaseRewardRouter::DISCRIMINATOR; - let base_reward_router_account = - BaseRewardRouter::try_from_slice_unchecked_mut(&mut base_reward_router_data)?; - - *base_reward_router_account = - BaseRewardRouter::new(*ncn.key, ncn_epoch, base_reward_router_bump, current_slot); - Ok(()) } diff --git a/program/src/initialize_epoch_snapshot.rs b/program/src/initialize_epoch_snapshot.rs index 3c3ca6ae..32e9f87b 100644 --- a/program/src/initialize_epoch_snapshot.rs +++ b/program/src/initialize_epoch_snapshot.rs @@ -17,7 +17,7 @@ use solana_program::{ pub fn process_initialize_epoch_snapshot( program_id: &Pubkey, accounts: &[AccountInfo], - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [ncn_config, restaking_config, ncn, tracked_mints, weight_table, epoch_snapshot, payer, restaking_program, system_program] = accounts @@ -41,7 +41,7 @@ pub fn process_initialize_epoch_snapshot( 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)?; + let ncn_epoch = epoch; WeightTable::load(program_id, weight_table, ncn, ncn_epoch, false)?; diff --git a/program/src/initialize_ncn_reward_router.rs b/program/src/initialize_ncn_reward_router.rs index 1387617f..8b58f69f 100644 --- a/program/src/initialize_ncn_reward_router.rs +++ b/program/src/initialize_ncn_reward_router.rs @@ -6,9 +6,7 @@ use jito_jsm_core::{ loader::{load_signer, load_system_account, load_system_program}, }; use jito_restaking_core::{config::Config, ncn::Ncn, operator::Operator}; -use jito_tip_router_core::{ - loaders::load_ncn_epoch, ncn_fee_group::NcnFeeGroup, ncn_reward_router::NcnRewardRouter, -}; +use jito_tip_router_core::{ncn_fee_group::NcnFeeGroup, ncn_reward_router::NcnRewardRouter}; use solana_program::{ account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, @@ -19,7 +17,7 @@ pub fn process_initialize_ncn_reward_router( program_id: &Pubkey, accounts: &[AccountInfo], ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, ncn, operator, ncn_reward_router, payer, restaking_program, system_program] = accounts @@ -43,7 +41,6 @@ pub fn process_initialize_ncn_reward_router( let ncn_fee_group = NcnFeeGroup::try_from(ncn_fee_group)?; let current_slot = Clock::get()?.slot; - let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; let (ncn_reward_router_pubkey, ncn_reward_router_bump, mut ncn_reward_router_seeds) = NcnRewardRouter::find_program_address( @@ -51,7 +48,7 @@ pub fn process_initialize_ncn_reward_router( ncn_fee_group, operator.key, ncn.key, - ncn_epoch, + epoch, ); ncn_reward_router_seeds.push(vec![ncn_reward_router_bump]); @@ -64,8 +61,9 @@ pub fn process_initialize_ncn_reward_router( "Initializing Epoch Reward Router {} for NCN: {} at epoch: {}", ncn_reward_router.key, ncn.key, - ncn_epoch + epoch ); + msg!("Payer lamports: {}", payer.lamports()); create_account( payer, ncn_reward_router, @@ -77,6 +75,7 @@ pub fn process_initialize_ncn_reward_router( .unwrap(), &ncn_reward_router_seeds, )?; + msg!("Payer lamports post: {}", payer.lamports()); let mut ncn_reward_router_data = ncn_reward_router.try_borrow_mut_data()?; ncn_reward_router_data[0] = NcnRewardRouter::DISCRIMINATOR; @@ -87,7 +86,7 @@ pub fn process_initialize_ncn_reward_router( ncn_fee_group, *operator.key, *ncn.key, - ncn_epoch, + epoch, ncn_reward_router_bump, current_slot, ); diff --git a/program/src/initialize_operator_snapshot.rs b/program/src/initialize_operator_snapshot.rs index 998f383e..c93c576a 100644 --- a/program/src/initialize_operator_snapshot.rs +++ b/program/src/initialize_operator_snapshot.rs @@ -7,6 +7,7 @@ use jito_restaking_core::{ config::Config, ncn::Ncn, ncn_operator_state::NcnOperatorState, operator::Operator, }; use jito_tip_router_core::{ + constants::MAX_REALLOC_BYTES, epoch_snapshot::{EpochSnapshot, OperatorSnapshot}, loaders::load_ncn_epoch, ncn_config::NcnConfig, @@ -21,7 +22,7 @@ use solana_program::{ pub fn process_initialize_operator_snapshot( program_id: &Pubkey, accounts: &[AccountInfo], - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [ncn_config, restaking_config, ncn, operator, ncn_operator_state, epoch_snapshot, operator_snapshot, payer, restaking_program, system_program] = accounts @@ -45,24 +46,19 @@ pub fn process_initialize_operator_snapshot( operator, false, )?; + EpochSnapshot::load(program_id, ncn.key, epoch, epoch_snapshot, true)?; 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); + OperatorSnapshot::find_program_address(program_id, operator.key, ncn.key, epoch); operator_snapshot_seeds.push(vec![operator_snapshot_bump]); if operator_snapshot_pubkey.ne(operator_snapshot.key) { - msg!("Incorrect epoch snapshot PDA"); + msg!("Operator snapshot account is not at the correct PDA"); return Err(ProgramError::InvalidAccountData); } @@ -70,7 +66,7 @@ pub fn process_initialize_operator_snapshot( "Initializing Operator snapshot {} for NCN: {} at epoch: {}", epoch_snapshot.key, ncn.key, - ncn_epoch + epoch ); create_account( @@ -79,88 +75,9 @@ pub fn process_initialize_operator_snapshot( system_program, program_id, &Rent::get()?, - 8_u64 - .checked_add(std::mem::size_of::() as u64) - .unwrap(), + MAX_REALLOC_BYTES, &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, - &StakeWeights::default(), - )?; - } - Ok(()) } diff --git a/program/src/initialize_weight_table.rs b/program/src/initialize_weight_table.rs index 4f98643f..200b0b31 100644 --- a/program/src/initialize_weight_table.rs +++ b/program/src/initialize_weight_table.rs @@ -7,7 +7,8 @@ use jito_jsm_core::{ }; use jito_restaking_core::{config::Config, ncn::Ncn}; use jito_tip_router_core::{ - loaders::load_ncn_epoch, tracked_mints::TrackedMints, weight_table::WeightTable, + constants::MAX_REALLOC_BYTES, loaders::load_ncn_epoch, tracked_mints::TrackedMints, + weight_table::WeightTable, }; use solana_program::{ account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, @@ -19,7 +20,7 @@ use solana_program::{ pub fn process_initialize_weight_table( program_id: &Pubkey, accounts: &[AccountInfo], - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, tracked_mints, ncn, weight_table, payer, restaking_program, system_program] = accounts @@ -41,7 +42,7 @@ pub fn process_initialize_weight_table( 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 ncn_epoch = epoch; let vault_count = { let ncn_data = ncn.data.borrow(); @@ -49,10 +50,10 @@ pub fn process_initialize_weight_table( ncn.vault_count() }; - let (tracked_mint_count, unique_mints) = { + let tracked_mint_count = { 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()) + tracked_mints.mint_count() }; if vault_count != tracked_mint_count { @@ -81,17 +82,9 @@ pub fn process_initialize_weight_table( system_program, program_id, &Rent::get()?, - 8_u64.checked_add(size_of::() as u64).unwrap(), + MAX_REALLOC_BYTES, &weight_table_seeds, )?; - let mut weight_table_data = weight_table.try_borrow_mut_data()?; - weight_table_data[0] = WeightTable::DISCRIMINATOR; - let weight_table_account = WeightTable::try_from_slice_unchecked_mut(&mut weight_table_data)?; - - *weight_table_account = WeightTable::new(*ncn.key, ncn_epoch, current_slot, weight_table_bump); - - weight_table_account.initalize_weight_table(&unique_mints)?; - Ok(()) } diff --git a/program/src/lib.rs b/program/src/lib.rs index 4e680be8..e4cd271d 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -12,6 +12,10 @@ mod initialize_ncn_reward_router; mod initialize_operator_snapshot; mod initialize_tracked_mints; mod initialize_weight_table; +mod realloc_ballot_box; +mod realloc_base_reward_router; +mod realloc_operator_snapshot; +mod realloc_weight_table; mod register_mint; mod route_base_rewards; mod route_ncn_rewards; @@ -46,7 +50,11 @@ use crate::{ initialize_ncn_reward_router::process_initialize_ncn_reward_router, 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, + initialize_weight_table::process_initialize_weight_table, + realloc_ballot_box::process_realloc_ballot_box, + realloc_base_reward_router::process_realloc_base_reward_router, + realloc_operator_snapshot::process_realloc_operator_snapshot, + realloc_weight_table::process_realloc_weight_table, register_mint::process_register_mint, route_base_rewards::process_route_base_rewards, route_ncn_rewards::process_route_ncn_rewards, set_config_fees::process_set_config_fees, set_merkle_root::process_set_merkle_root, set_tie_breaker::process_set_tie_breaker, @@ -104,55 +112,36 @@ pub fn process_instruction( msg!("Instruction: InitializeTrackedMints"); process_initialize_tracked_mints(program_id, accounts) } - TipRouterInstruction::InitializeWeightTable { - first_slot_of_ncn_epoch, - } => { + TipRouterInstruction::InitializeWeightTable { epoch } => { msg!("Instruction: InitializeWeightTable"); - process_initialize_weight_table(program_id, accounts, first_slot_of_ncn_epoch) + process_initialize_weight_table(program_id, accounts, epoch) } - TipRouterInstruction::InitializeEpochSnapshot { - first_slot_of_ncn_epoch, - } => { + TipRouterInstruction::InitializeEpochSnapshot { epoch } => { msg!("Instruction: InitializeEpochSnapshot"); - process_initialize_epoch_snapshot(program_id, accounts, first_slot_of_ncn_epoch) + process_initialize_epoch_snapshot(program_id, accounts, epoch) } - TipRouterInstruction::InitializeOperatorSnapshot { - first_slot_of_ncn_epoch, - } => { + TipRouterInstruction::InitializeOperatorSnapshot { epoch } => { msg!("Instruction: InitializeOperatorSnapshot"); - process_initialize_operator_snapshot(program_id, accounts, first_slot_of_ncn_epoch) + process_initialize_operator_snapshot(program_id, accounts, epoch) } - TipRouterInstruction::InitializeBaseRewardRouter { - first_slot_of_ncn_epoch, - } => { + TipRouterInstruction::InitializeBaseRewardRouter { epoch } => { msg!("Instruction: InitializeBaseRewardRouter"); - process_initialize_base_reward_router(program_id, accounts, first_slot_of_ncn_epoch) + process_initialize_base_reward_router(program_id, accounts, epoch) } TipRouterInstruction::InitializeNcnRewardRouter { ncn_fee_group, - first_slot_of_ncn_epoch, + epoch, } => { msg!("Instruction: InitializeNcnRewardRouter"); - process_initialize_ncn_reward_router( - program_id, - accounts, - ncn_fee_group, - first_slot_of_ncn_epoch, - ) + process_initialize_ncn_reward_router(program_id, accounts, ncn_fee_group, epoch) } // ------------------------------------------ // Cranks // ------------------------------------------ - TipRouterInstruction::SnapshotVaultOperatorDelegation { - first_slot_of_ncn_epoch, - } => { + TipRouterInstruction::SnapshotVaultOperatorDelegation { epoch } => { msg!("Instruction: SnapshotVaultOperatorDelegation"); - process_snapshot_vault_operator_delegation( - program_id, - accounts, - first_slot_of_ncn_epoch, - ) + process_snapshot_vault_operator_delegation(program_id, accounts, epoch) } TipRouterInstruction::RouteBaseRewards { first_slot_of_ncn_epoch, @@ -293,5 +282,21 @@ pub fn process_instruction( msg!("Instruction: SetTieBreaker"); process_set_tie_breaker(program_id, accounts, meta_merkle_root, epoch) } + TipRouterInstruction::ReallocBallotBox { epoch } => { + msg!("Instruction: ReallocBallotBox"); + process_realloc_ballot_box(program_id, accounts, epoch) + } + TipRouterInstruction::ReallocOperatorSnapshot { epoch } => { + msg!("Instruction: ReallocOperatorSnapshot"); + process_realloc_operator_snapshot(program_id, accounts, epoch) + } + TipRouterInstruction::ReallocBaseRewardRouter { epoch } => { + msg!("Instruction: ReallocBaseRewardRouter"); + process_realloc_base_reward_router(program_id, accounts, epoch) + } + TipRouterInstruction::ReallocWeightTable { epoch } => { + msg!("Instruction: ReallocWeightTable"); + process_realloc_weight_table(program_id, accounts, epoch) + } } } diff --git a/program/src/realloc_ballot_box.rs b/program/src/realloc_ballot_box.rs new file mode 100644 index 00000000..e201ca39 --- /dev/null +++ b/program/src/realloc_ballot_box.rs @@ -0,0 +1,53 @@ +use jito_bytemuck::{AccountDeserialize, Discriminator}; +use jito_jsm_core::{ + loader::{load_signer, load_system_program}, + realloc, +}; +use jito_tip_router_core::{ballot_box::BallotBox, ncn_config::NcnConfig, utils::get_new_size}; +use solana_program::{ + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, + program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, +}; + +/// Reallocates the ballot box account to its full size. +/// This is needed due to Solana's account size limits during initialization. +pub fn process_realloc_ballot_box( + program_id: &Pubkey, + accounts: &[AccountInfo], + epoch: u64, +) -> ProgramResult { + let [ncn_config, ballot_box, ncn, payer, system_program] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + load_system_program(system_program)?; + load_signer(payer, false)?; + NcnConfig::load(program_id, ncn.key, ncn_config, false)?; + + let (ballot_box_pda, ballot_box_bump, _) = + BallotBox::find_program_address(program_id, ncn.key, epoch); + + if ballot_box_pda != *ballot_box.key { + msg!("Ballot box account is not at the correct PDA"); + return Err(ProgramError::InvalidAccountData); + } + + if ballot_box.data_len() < BallotBox::SIZE { + let new_size = get_new_size(ballot_box.data_len(), BallotBox::SIZE)?; + msg!( + "Reallocating ballot box from {} bytes to {} bytes", + ballot_box.data_len(), + new_size + ); + realloc(ballot_box, new_size, payer, &Rent::get()?)?; + } + + if ballot_box.data_len() >= BallotBox::SIZE { + let mut ballot_box_data = ballot_box.try_borrow_mut_data()?; + ballot_box_data[0] = BallotBox::DISCRIMINATOR; + let ballot_box_account = BallotBox::try_from_slice_unchecked_mut(&mut ballot_box_data)?; + ballot_box_account.initialize(*ncn.key, epoch, ballot_box_bump, Clock::get()?.slot); + } + + Ok(()) +} diff --git a/program/src/realloc_base_reward_router.rs b/program/src/realloc_base_reward_router.rs new file mode 100644 index 00000000..c469d2ad --- /dev/null +++ b/program/src/realloc_base_reward_router.rs @@ -0,0 +1,56 @@ +use jito_bytemuck::{AccountDeserialize, Discriminator}; +use jito_jsm_core::{ + loader::{load_signer, load_system_program}, + realloc, +}; +use jito_tip_router_core::{ + base_reward_router::BaseRewardRouter, ncn_config::NcnConfig, utils::get_new_size, +}; +use solana_program::{ + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, + program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, +}; + +pub fn process_realloc_base_reward_router( + program_id: &Pubkey, + accounts: &[AccountInfo], + epoch: u64, +) -> ProgramResult { + let [ncn_config, base_reward_router, ncn, payer, system_program] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + load_system_program(system_program)?; + load_signer(payer, false)?; + NcnConfig::load(program_id, ncn.key, ncn_config, false)?; + + let (base_reward_router_pda, base_reward_router_bump, _) = + BaseRewardRouter::find_program_address(program_id, ncn.key, epoch); + + if base_reward_router_pda != *base_reward_router.key { + msg!("Base reward router account is not at the correct PDA"); + return Err(ProgramError::InvalidAccountData); + } + + if base_reward_router.data_len() < BaseRewardRouter::SIZE { + let new_size = get_new_size(base_reward_router.data_len(), BaseRewardRouter::SIZE)?; + msg!( + "Reallocating base reward router from {} bytes to {} bytes", + base_reward_router.data_len(), + new_size + ); + realloc(base_reward_router, new_size, payer, &Rent::get()?)?; + } + + if base_reward_router.data_len() >= BaseRewardRouter::SIZE { + let mut base_reward_router_data = base_reward_router.try_borrow_mut_data()?; + base_reward_router_data[0] = BaseRewardRouter::DISCRIMINATOR; + let base_reward_router_account = + BaseRewardRouter::try_from_slice_unchecked_mut(&mut base_reward_router_data)?; + + *base_reward_router_account = + BaseRewardRouter::new(*ncn.key, epoch, base_reward_router_bump, Clock::get()?.slot); + } + + Ok(()) +} diff --git a/program/src/realloc_operator_snapshot.rs b/program/src/realloc_operator_snapshot.rs new file mode 100644 index 00000000..c47a4473 --- /dev/null +++ b/program/src/realloc_operator_snapshot.rs @@ -0,0 +1,154 @@ +use jito_bytemuck::{AccountDeserialize, Discriminator}; +use jito_jsm_core::{ + loader::{load_signer, load_system_program}, + realloc, +}; +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, + stake_weight::StakeWeights, + utils::get_new_size, +}; +use solana_program::{ + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, + program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, +}; + +pub fn process_realloc_operator_snapshot( + program_id: &Pubkey, + accounts: &[AccountInfo], + epoch: u64, +) -> 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, + )?; + EpochSnapshot::load(program_id, ncn.key, epoch, epoch_snapshot, true)?; + + load_system_program(system_program)?; + load_signer(payer, false)?; + + let (operator_snapshot_pda, operator_snapshot_bump, _) = + OperatorSnapshot::find_program_address(program_id, operator.key, ncn.key, epoch); + + if operator_snapshot_pda != *operator_snapshot.key { + msg!("Operator snapshot account is not at the correct PDA"); + return Err(ProgramError::InvalidAccountData); + } + + if operator_snapshot.data_len() < OperatorSnapshot::SIZE { + let new_size = get_new_size(operator_snapshot.data_len(), OperatorSnapshot::SIZE)?; + msg!( + "Reallocating operator snapshot from {} bytes to {} bytes", + operator_snapshot.data_len(), + new_size + ); + realloc(operator_snapshot, new_size, payer, &Rent::get()?)?; + } + + if operator_snapshot.data_len() >= OperatorSnapshot::SIZE { + let current_slot = Clock::get()?.slot; + let (_, ncn_epoch_length) = load_ncn_epoch(restaking_config, current_slot, None)?; + + //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 = 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, + epoch, + operator_snapshot_bump, + current_slot, + ncn_operator_index, + operator_index, + operator_fee_bps, + vault_count, + )? + } else { + OperatorSnapshot::new_inactive( + *operator.key, + *ncn.key, + 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, + &StakeWeights::default(), + )?; + } + } + + Ok(()) +} diff --git a/program/src/realloc_weight_table.rs b/program/src/realloc_weight_table.rs new file mode 100644 index 00000000..7b36c5e5 --- /dev/null +++ b/program/src/realloc_weight_table.rs @@ -0,0 +1,70 @@ +use jito_bytemuck::{AccountDeserialize, Discriminator}; +use jito_jsm_core::{ + loader::{load_signer, load_system_program}, + realloc, +}; +use jito_tip_router_core::{ + ncn_config::NcnConfig, tracked_mints::TrackedMints, utils::get_new_size, + weight_table::WeightTable, +}; +use solana_program::{ + account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, + program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, +}; + +pub fn process_realloc_weight_table( + program_id: &Pubkey, + accounts: &[AccountInfo], + epoch: u64, +) -> ProgramResult { + let [ncn_config, weight_table, ncn, tracked_mints, payer, system_program] = accounts else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + load_system_program(system_program)?; + load_signer(payer, false)?; + NcnConfig::load(program_id, ncn.key, ncn_config, false)?; + TrackedMints::load(program_id, ncn.key, tracked_mints, false)?; + + let (weight_table_pda, weight_table_bump, _) = + WeightTable::find_program_address(program_id, ncn.key, epoch); + + if weight_table_pda != *weight_table.key { + msg!("Weight table account is not at the correct PDA"); + return Err(ProgramError::InvalidAccountData); + } + + if weight_table.data_len() < WeightTable::SIZE { + let new_size = get_new_size(weight_table.data_len(), WeightTable::SIZE)?; + msg!( + "Reallocating weight table from {} bytes to {} bytes", + weight_table.data_len(), + new_size + ); + realloc(weight_table, new_size, payer, &Rent::get()?)?; + msg!("New size: {}", weight_table.data_len()); + msg!("Data is empty? {}", weight_table.data_is_empty()); + } + + if weight_table.data_len() >= WeightTable::SIZE { + msg!("actually Initializing weight table"); + let unique_mints = { + let tracked_mints_data = tracked_mints.data.borrow(); + let tracked_mints = TrackedMints::try_from_slice_unchecked(&tracked_mints_data)?; + tracked_mints.get_unique_mints() + }; + + let mut weight_table_data = weight_table.try_borrow_mut_data()?; + weight_table_data[0] = WeightTable::DISCRIMINATOR; + let weight_table_account = + WeightTable::try_from_slice_unchecked_mut(&mut weight_table_data)?; + + *weight_table_account = + WeightTable::new(*ncn.key, epoch, Clock::get()?.slot, weight_table_bump); + + weight_table_account.initalize_weight_table(&unique_mints)?; + msg!("INTIIALIZZING"); + } + + Ok(()) +} diff --git a/program/src/snapshot_vault_operator_delegation.rs b/program/src/snapshot_vault_operator_delegation.rs index dda4afa4..7cb3055d 100644 --- a/program/src/snapshot_vault_operator_delegation.rs +++ b/program/src/snapshot_vault_operator_delegation.rs @@ -22,7 +22,7 @@ use solana_program::{ pub fn process_snapshot_vault_operator_delegation( program_id: &Pubkey, accounts: &[AccountInfo], - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [ncn_config, restaking_config, tracked_mints, ncn, operator, vault, vault_ncn_ticket, ncn_vault_ticket, vault_operator_delegation, weight_table, epoch_snapshot, operator_snapshot, vault_program, restaking_program] = accounts @@ -67,17 +67,16 @@ pub fn process_snapshot_vault_operator_delegation( } let current_slot = Clock::get()?.slot; - let (ncn_epoch, ncn_epoch_length) = - load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; + let (_, ncn_epoch_length) = load_ncn_epoch(restaking_config, current_slot, None)?; TrackedMints::load(program_id, ncn.key, tracked_mints, false)?; - WeightTable::load(program_id, weight_table, ncn, ncn_epoch, false)?; - EpochSnapshot::load(program_id, ncn.key, ncn_epoch, epoch_snapshot, true)?; + WeightTable::load(program_id, weight_table, ncn, epoch, false)?; + EpochSnapshot::load(program_id, ncn.key, epoch, epoch_snapshot, true)?; OperatorSnapshot::load( program_id, operator.key, ncn.key, - ncn_epoch, + epoch, operator_snapshot, true, )?; From 03e5c533f8199fcae805f66266c80dd0a09f429a Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Fri, 13 Dec 2024 15:00:58 -0500 Subject: [PATCH 02/10] Formatting everything --- .../distributeBaseNcnRewardRoute.ts | 14 ++-- .../instructions/distributeBaseRewards.ts | 14 ++-- .../distributeNcnOperatorRewards.ts | 14 ++-- .../instructions/distributeNcnVaultRewards.ts | 14 ++-- .../instructions/routeBaseRewards.ts | 16 ++--- .../instructions/routeNcnRewards.ts | 14 ++-- .../distribute_base_ncn_reward_route.rs | 22 +++--- .../instructions/distribute_base_rewards.rs | 22 +++--- .../distribute_ncn_operator_rewards.rs | 22 +++--- .../distribute_ncn_vault_rewards.rs | 22 +++--- .../instructions/route_base_rewards.rs | 22 +++--- .../instructions/route_ncn_rewards.rs | 22 +++--- core/src/ballot_box.rs | 6 +- core/src/epoch_snapshot.rs | 4 +- core/src/instruction.rs | 12 ++-- core/src/utils.rs | 1 - idl/jito_tip_router.json | 36 ++++------ integration_tests/tests/fixtures/mod.rs | 4 +- .../tests/fixtures/test_builder.rs | 72 +++++++------------ .../tests/fixtures/tip_distribution_client.rs | 2 +- .../tests/fixtures/tip_router_client.rs | 69 +++++++++--------- integration_tests/tests/helpers/ballot_box.rs | 6 +- .../tests/tip_router/distribute_rewards.rs | 22 +++--- .../tests/tip_router/initialize_ballot_box.rs | 2 - .../initialize_operator_snapshot.rs | 1 - .../tip_router/initialize_weight_table.rs | 2 +- .../tests/tip_router/meta_tests.rs | 3 - .../tests/tip_router/register_mint.rs | 4 +- .../set_tracked_mint_ncn_fee_group.rs | 30 ++------ .../src/distribute_base_ncn_reward_route.rs | 16 ++--- program/src/distribute_base_rewards.rs | 13 ++-- .../src/distribute_ncn_operator_rewards.rs | 14 ++-- program/src/distribute_ncn_vault_rewards.rs | 14 ++-- program/src/initialize_ballot_box.rs | 1 - program/src/initialize_epoch_snapshot.rs | 4 +- program/src/initialize_operator_snapshot.rs | 7 +- program/src/initialize_weight_table.rs | 18 ++--- program/src/lib.rs | 46 ++++-------- program/src/register_mint.rs | 12 ++-- program/src/route_base_rewards.rs | 16 ++--- program/src/route_ncn_rewards.rs | 14 ++-- program/src/set_tracked_mint_ncn_fee_group.rs | 15 +--- 42 files changed, 260 insertions(+), 424 deletions(-) diff --git a/clients/js/jito_tip_router/instructions/distributeBaseNcnRewardRoute.ts b/clients/js/jito_tip_router/instructions/distributeBaseNcnRewardRoute.ts index 358712f8..e3865cac 100644 --- a/clients/js/jito_tip_router/instructions/distributeBaseNcnRewardRoute.ts +++ b/clients/js/jito_tip_router/instructions/distributeBaseNcnRewardRoute.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -25,8 +23,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type WritableAccount, } from '@solana/web3.js'; @@ -79,12 +75,12 @@ export type DistributeBaseNcnRewardRouteInstruction< export type DistributeBaseNcnRewardRouteInstructionData = { discriminator: number; ncnFeeGroup: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type DistributeBaseNcnRewardRouteInstructionDataArgs = { ncnFeeGroup: number; - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getDistributeBaseNcnRewardRouteInstructionDataEncoder(): Encoder { @@ -92,7 +88,7 @@ export function getDistributeBaseNcnRewardRouteInstructionDataEncoder(): Encoder getStructEncoder([ ['discriminator', getU8Encoder()], ['ncnFeeGroup', getU8Encoder()], - ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ['epoch', getU64Encoder()], ]), (value) => ({ ...value, @@ -105,7 +101,7 @@ export function getDistributeBaseNcnRewardRouteInstructionDataDecoder(): Decoder return getStructDecoder([ ['discriminator', getU8Decoder()], ['ncnFeeGroup', getU8Decoder()], - ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ['epoch', getU64Decoder()], ]); } @@ -136,7 +132,7 @@ export type DistributeBaseNcnRewardRouteInput< ncnRewardRouter: Address; restakingProgram: Address; ncnFeeGroup: DistributeBaseNcnRewardRouteInstructionDataArgs['ncnFeeGroup']; - firstSlotOfNcnEpoch: DistributeBaseNcnRewardRouteInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: DistributeBaseNcnRewardRouteInstructionDataArgs['epoch']; }; export function getDistributeBaseNcnRewardRouteInstruction< diff --git a/clients/js/jito_tip_router/instructions/distributeBaseRewards.ts b/clients/js/jito_tip_router/instructions/distributeBaseRewards.ts index 951c63af..264b54c5 100644 --- a/clients/js/jito_tip_router/instructions/distributeBaseRewards.ts +++ b/clients/js/jito_tip_router/instructions/distributeBaseRewards.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -25,8 +23,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type WritableAccount, } from '@solana/web3.js'; @@ -75,12 +71,12 @@ export type DistributeBaseRewardsInstruction< export type DistributeBaseRewardsInstructionData = { discriminator: number; baseFeeGroup: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type DistributeBaseRewardsInstructionDataArgs = { baseFeeGroup: number; - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getDistributeBaseRewardsInstructionDataEncoder(): Encoder { @@ -88,7 +84,7 @@ export function getDistributeBaseRewardsInstructionDataEncoder(): Encoder ({ ...value, @@ -101,7 +97,7 @@ export function getDistributeBaseRewardsInstructionDataDecoder(): Decoder; restakingProgram: Address; baseFeeGroup: DistributeBaseRewardsInstructionDataArgs['baseFeeGroup']; - firstSlotOfNcnEpoch: DistributeBaseRewardsInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: DistributeBaseRewardsInstructionDataArgs['epoch']; }; export function getDistributeBaseRewardsInstruction< diff --git a/clients/js/jito_tip_router/instructions/distributeNcnOperatorRewards.ts b/clients/js/jito_tip_router/instructions/distributeNcnOperatorRewards.ts index 3e347ddb..80e6bf07 100644 --- a/clients/js/jito_tip_router/instructions/distributeNcnOperatorRewards.ts +++ b/clients/js/jito_tip_router/instructions/distributeNcnOperatorRewards.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -25,8 +23,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type WritableAccount, } from '@solana/web3.js'; @@ -75,12 +71,12 @@ export type DistributeNcnOperatorRewardsInstruction< export type DistributeNcnOperatorRewardsInstructionData = { discriminator: number; ncnFeeGroup: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type DistributeNcnOperatorRewardsInstructionDataArgs = { ncnFeeGroup: number; - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getDistributeNcnOperatorRewardsInstructionDataEncoder(): Encoder { @@ -88,7 +84,7 @@ export function getDistributeNcnOperatorRewardsInstructionDataEncoder(): Encoder getStructEncoder([ ['discriminator', getU8Encoder()], ['ncnFeeGroup', getU8Encoder()], - ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ['epoch', getU64Encoder()], ]), (value) => ({ ...value, @@ -101,7 +97,7 @@ export function getDistributeNcnOperatorRewardsInstructionDataDecoder(): Decoder return getStructDecoder([ ['discriminator', getU8Decoder()], ['ncnFeeGroup', getU8Decoder()], - ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ['epoch', getU64Decoder()], ]); } @@ -130,7 +126,7 @@ export type DistributeNcnOperatorRewardsInput< ncnRewardRouter: Address; restakingProgram: Address; ncnFeeGroup: DistributeNcnOperatorRewardsInstructionDataArgs['ncnFeeGroup']; - firstSlotOfNcnEpoch: DistributeNcnOperatorRewardsInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: DistributeNcnOperatorRewardsInstructionDataArgs['epoch']; }; export function getDistributeNcnOperatorRewardsInstruction< diff --git a/clients/js/jito_tip_router/instructions/distributeNcnVaultRewards.ts b/clients/js/jito_tip_router/instructions/distributeNcnVaultRewards.ts index 3b592c79..72872900 100644 --- a/clients/js/jito_tip_router/instructions/distributeNcnVaultRewards.ts +++ b/clients/js/jito_tip_router/instructions/distributeNcnVaultRewards.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -25,8 +23,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type WritableAccount, } from '@solana/web3.js'; @@ -75,12 +71,12 @@ export type DistributeNcnVaultRewardsInstruction< export type DistributeNcnVaultRewardsInstructionData = { discriminator: number; ncnFeeGroup: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type DistributeNcnVaultRewardsInstructionDataArgs = { ncnFeeGroup: number; - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getDistributeNcnVaultRewardsInstructionDataEncoder(): Encoder { @@ -88,7 +84,7 @@ export function getDistributeNcnVaultRewardsInstructionDataEncoder(): Encoder ({ ...value, @@ -101,7 +97,7 @@ export function getDistributeNcnVaultRewardsInstructionDataDecoder(): Decoder; ncnRewardRouter: Address; ncnFeeGroup: DistributeNcnVaultRewardsInstructionDataArgs['ncnFeeGroup']; - firstSlotOfNcnEpoch: DistributeNcnVaultRewardsInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: DistributeNcnVaultRewardsInstructionDataArgs['epoch']; }; export function getDistributeNcnVaultRewardsInstruction< diff --git a/clients/js/jito_tip_router/instructions/routeBaseRewards.ts b/clients/js/jito_tip_router/instructions/routeBaseRewards.ts index ade2a95b..90d5d40a 100644 --- a/clients/js/jito_tip_router/instructions/routeBaseRewards.ts +++ b/clients/js/jito_tip_router/instructions/routeBaseRewards.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -25,8 +23,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type WritableAccount, } from '@solana/web3.js'; @@ -74,18 +70,16 @@ export type RouteBaseRewardsInstruction< export type RouteBaseRewardsInstructionData = { discriminator: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; -export type RouteBaseRewardsInstructionDataArgs = { - firstSlotOfNcnEpoch: OptionOrNullable; -}; +export type RouteBaseRewardsInstructionDataArgs = { epoch: number | bigint }; export function getRouteBaseRewardsInstructionDataEncoder(): Encoder { return transformEncoder( getStructEncoder([ ['discriminator', getU8Encoder()], - ['firstSlotOfNcnEpoch', getOptionEncoder(getU64Encoder())], + ['epoch', getU64Encoder()], ]), (value) => ({ ...value, discriminator: ROUTE_BASE_REWARDS_DISCRIMINATOR }) ); @@ -94,7 +88,7 @@ export function getRouteBaseRewardsInstructionDataEncoder(): Encoder { return getStructDecoder([ ['discriminator', getU8Decoder()], - ['firstSlotOfNcnEpoch', getOptionDecoder(getU64Decoder())], + ['epoch', getU64Decoder()], ]); } @@ -122,7 +116,7 @@ export type RouteBaseRewardsInput< ballotBox: Address; baseRewardRouter: Address; restakingProgram: Address; - firstSlotOfNcnEpoch: RouteBaseRewardsInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: RouteBaseRewardsInstructionDataArgs['epoch']; }; export function getRouteBaseRewardsInstruction< diff --git a/clients/js/jito_tip_router/instructions/routeNcnRewards.ts b/clients/js/jito_tip_router/instructions/routeNcnRewards.ts index 5304ce92..94de21a7 100644 --- a/clients/js/jito_tip_router/instructions/routeNcnRewards.ts +++ b/clients/js/jito_tip_router/instructions/routeNcnRewards.ts @@ -8,8 +8,6 @@ import { combineCodec, - getOptionDecoder, - getOptionEncoder, getStructDecoder, getStructEncoder, getU64Decoder, @@ -25,8 +23,6 @@ import { type IInstruction, type IInstructionWithAccounts, type IInstructionWithData, - type Option, - type OptionOrNullable, type ReadonlyAccount, type WritableAccount, } from '@solana/web3.js'; @@ -75,12 +71,12 @@ export type RouteNcnRewardsInstruction< export type RouteNcnRewardsInstructionData = { discriminator: number; ncnFeeGroup: number; - firstSlotOfNcnEpoch: Option; + epoch: bigint; }; export type RouteNcnRewardsInstructionDataArgs = { ncnFeeGroup: number; - firstSlotOfNcnEpoch: OptionOrNullable; + epoch: number | bigint; }; export function getRouteNcnRewardsInstructionDataEncoder(): Encoder { @@ -88,7 +84,7 @@ export function getRouteNcnRewardsInstructionDataEncoder(): Encoder ({ ...value, discriminator: ROUTE_NCN_REWARDS_DISCRIMINATOR }) ); @@ -98,7 +94,7 @@ export function getRouteNcnRewardsInstructionDataDecoder(): Decoder; restakingProgram: Address; ncnFeeGroup: RouteNcnRewardsInstructionDataArgs['ncnFeeGroup']; - firstSlotOfNcnEpoch: RouteNcnRewardsInstructionDataArgs['firstSlotOfNcnEpoch']; + epoch: RouteNcnRewardsInstructionDataArgs['epoch']; }; export function getRouteNcnRewardsInstruction< diff --git a/clients/rust/jito_tip_router/src/generated/instructions/distribute_base_ncn_reward_route.rs b/clients/rust/jito_tip_router/src/generated/instructions/distribute_base_ncn_reward_route.rs index cd5eb392..53692cdc 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/distribute_base_ncn_reward_route.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/distribute_base_ncn_reward_route.rs @@ -100,7 +100,7 @@ impl Default for DistributeBaseNcnRewardRouteInstructionData { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DistributeBaseNcnRewardRouteInstructionArgs { pub ncn_fee_group: u8, - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `DistributeBaseNcnRewardRoute`. @@ -124,7 +124,7 @@ pub struct DistributeBaseNcnRewardRouteBuilder { ncn_reward_router: Option, restaking_program: Option, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -184,10 +184,9 @@ impl DistributeBaseNcnRewardRouteBuilder { self.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -230,7 +229,7 @@ impl DistributeBaseNcnRewardRouteBuilder { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -423,7 +422,7 @@ impl<'a, 'b> DistributeBaseNcnRewardRouteCpiBuilder<'a, 'b> { ncn_reward_router: None, restaking_program: None, ncn_fee_group: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -486,10 +485,9 @@ impl<'a, 'b> DistributeBaseNcnRewardRouteCpiBuilder<'a, 'b> { self.instruction.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -539,7 +537,7 @@ impl<'a, 'b> DistributeBaseNcnRewardRouteCpiBuilder<'a, 'b> { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = DistributeBaseNcnRewardRouteCpi { __program: self.instruction.__program, @@ -589,7 +587,7 @@ struct DistributeBaseNcnRewardRouteCpiBuilderInstruction<'a, 'b> { ncn_reward_router: Option<&'b solana_program::account_info::AccountInfo<'a>>, restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/distribute_base_rewards.rs b/clients/rust/jito_tip_router/src/generated/instructions/distribute_base_rewards.rs index ec1e0f77..50f5853a 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/distribute_base_rewards.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/distribute_base_rewards.rs @@ -94,7 +94,7 @@ impl Default for DistributeBaseRewardsInstructionData { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DistributeBaseRewardsInstructionArgs { pub base_fee_group: u8, - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `DistributeBaseRewards`. @@ -116,7 +116,7 @@ pub struct DistributeBaseRewardsBuilder { base_fee_wallet: Option, restaking_program: Option, base_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -171,10 +171,9 @@ impl DistributeBaseRewardsBuilder { self.base_fee_group = Some(base_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -214,7 +213,7 @@ impl DistributeBaseRewardsBuilder { .base_fee_group .clone() .expect("base_fee_group is not set"), - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -395,7 +394,7 @@ impl<'a, 'b> DistributeBaseRewardsCpiBuilder<'a, 'b> { base_fee_wallet: None, restaking_program: None, base_fee_group: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -450,10 +449,9 @@ impl<'a, 'b> DistributeBaseRewardsCpiBuilder<'a, 'b> { self.instruction.base_fee_group = Some(base_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -503,7 +501,7 @@ impl<'a, 'b> DistributeBaseRewardsCpiBuilder<'a, 'b> { .base_fee_group .clone() .expect("base_fee_group is not set"), - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = DistributeBaseRewardsCpi { __program: self.instruction.__program, @@ -550,7 +548,7 @@ struct DistributeBaseRewardsCpiBuilderInstruction<'a, 'b> { base_fee_wallet: Option<&'b solana_program::account_info::AccountInfo<'a>>, restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, base_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/distribute_ncn_operator_rewards.rs b/clients/rust/jito_tip_router/src/generated/instructions/distribute_ncn_operator_rewards.rs index 130c2388..40ce5748 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/distribute_ncn_operator_rewards.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/distribute_ncn_operator_rewards.rs @@ -94,7 +94,7 @@ impl Default for DistributeNcnOperatorRewardsInstructionData { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DistributeNcnOperatorRewardsInstructionArgs { pub ncn_fee_group: u8, - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `DistributeNcnOperatorRewards`. @@ -116,7 +116,7 @@ pub struct DistributeNcnOperatorRewardsBuilder { ncn_reward_router: Option, restaking_program: Option, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -168,10 +168,9 @@ impl DistributeNcnOperatorRewardsBuilder { self.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -211,7 +210,7 @@ impl DistributeNcnOperatorRewardsBuilder { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -392,7 +391,7 @@ impl<'a, 'b> DistributeNcnOperatorRewardsCpiBuilder<'a, 'b> { ncn_reward_router: None, restaking_program: None, ncn_fee_group: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -447,10 +446,9 @@ impl<'a, 'b> DistributeNcnOperatorRewardsCpiBuilder<'a, 'b> { self.instruction.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -500,7 +498,7 @@ impl<'a, 'b> DistributeNcnOperatorRewardsCpiBuilder<'a, 'b> { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = DistributeNcnOperatorRewardsCpi { __program: self.instruction.__program, @@ -544,7 +542,7 @@ struct DistributeNcnOperatorRewardsCpiBuilderInstruction<'a, 'b> { ncn_reward_router: Option<&'b solana_program::account_info::AccountInfo<'a>>, restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/distribute_ncn_vault_rewards.rs b/clients/rust/jito_tip_router/src/generated/instructions/distribute_ncn_vault_rewards.rs index 9b6f36ad..fe6660a3 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/distribute_ncn_vault_rewards.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/distribute_ncn_vault_rewards.rs @@ -93,7 +93,7 @@ impl Default for DistributeNcnVaultRewardsInstructionData { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DistributeNcnVaultRewardsInstructionArgs { pub ncn_fee_group: u8, - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `DistributeNcnVaultRewards`. @@ -115,7 +115,7 @@ pub struct DistributeNcnVaultRewardsBuilder { vault: Option, ncn_reward_router: Option, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -164,10 +164,9 @@ impl DistributeNcnVaultRewardsBuilder { self.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -205,7 +204,7 @@ impl DistributeNcnVaultRewardsBuilder { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -386,7 +385,7 @@ impl<'a, 'b> DistributeNcnVaultRewardsCpiBuilder<'a, 'b> { vault: None, ncn_reward_router: None, ncn_fee_group: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -438,10 +437,9 @@ impl<'a, 'b> DistributeNcnVaultRewardsCpiBuilder<'a, 'b> { self.instruction.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -491,7 +489,7 @@ impl<'a, 'b> DistributeNcnVaultRewardsCpiBuilder<'a, 'b> { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = DistributeNcnVaultRewardsCpi { __program: self.instruction.__program, @@ -532,7 +530,7 @@ struct DistributeNcnVaultRewardsCpiBuilderInstruction<'a, 'b> { vault: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_reward_router: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/route_base_rewards.rs b/clients/rust/jito_tip_router/src/generated/instructions/route_base_rewards.rs index 77e8e24e..63607331 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/route_base_rewards.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/route_base_rewards.rs @@ -91,7 +91,7 @@ impl Default for RouteBaseRewardsInstructionData { #[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct RouteBaseRewardsInstructionArgs { - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `RouteBaseRewards`. @@ -112,7 +112,7 @@ pub struct RouteBaseRewardsBuilder { ballot_box: Option, base_reward_router: Option, restaking_program: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -159,10 +159,9 @@ impl RouteBaseRewardsBuilder { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -198,7 +197,7 @@ impl RouteBaseRewardsBuilder { .expect("restaking_program is not set"), }; let args = RouteBaseRewardsInstructionArgs { - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -376,7 +375,7 @@ impl<'a, 'b> RouteBaseRewardsCpiBuilder<'a, 'b> { ballot_box: None, base_reward_router: None, restaking_program: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -426,10 +425,9 @@ impl<'a, 'b> RouteBaseRewardsCpiBuilder<'a, 'b> { 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -474,7 +472,7 @@ impl<'a, 'b> RouteBaseRewardsCpiBuilder<'a, 'b> { signers_seeds: &[&[&[u8]]], ) -> solana_program::entrypoint::ProgramResult { let args = RouteBaseRewardsInstructionArgs { - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = RouteBaseRewardsCpi { __program: self.instruction.__program, @@ -520,7 +518,7 @@ struct RouteBaseRewardsCpiBuilderInstruction<'a, 'b> { ballot_box: Option<&'b solana_program::account_info::AccountInfo<'a>>, base_reward_router: Option<&'b solana_program::account_info::AccountInfo<'a>>, restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, - first_slot_of_ncn_epoch: Option, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/clients/rust/jito_tip_router/src/generated/instructions/route_ncn_rewards.rs b/clients/rust/jito_tip_router/src/generated/instructions/route_ncn_rewards.rs index a7c892de..8cc2f069 100644 --- a/clients/rust/jito_tip_router/src/generated/instructions/route_ncn_rewards.rs +++ b/clients/rust/jito_tip_router/src/generated/instructions/route_ncn_rewards.rs @@ -92,7 +92,7 @@ impl Default for RouteNcnRewardsInstructionData { #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct RouteNcnRewardsInstructionArgs { pub ncn_fee_group: u8, - pub first_slot_of_ncn_epoch: Option, + pub epoch: u64, } /// Instruction builder for `RouteNcnRewards`. @@ -114,7 +114,7 @@ pub struct RouteNcnRewardsBuilder { ncn_reward_router: Option, restaking_program: Option, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, __remaining_accounts: Vec, } @@ -169,10 +169,9 @@ impl RouteNcnRewardsBuilder { self.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -214,7 +213,7 @@ impl RouteNcnRewardsBuilder { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.first_slot_of_ncn_epoch.clone(), + epoch: self.epoch.clone().expect("epoch is not set"), }; accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts) @@ -393,7 +392,7 @@ impl<'a, 'b> RouteNcnRewardsCpiBuilder<'a, 'b> { ncn_reward_router: None, restaking_program: None, ncn_fee_group: None, - first_slot_of_ncn_epoch: None, + epoch: None, __remaining_accounts: Vec::new(), }); Self { instruction } @@ -448,10 +447,9 @@ impl<'a, 'b> RouteNcnRewardsCpiBuilder<'a, 'b> { self.instruction.ncn_fee_group = Some(ncn_fee_group); 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); + pub fn epoch(&mut self, epoch: u64) -> &mut Self { + self.instruction.epoch = Some(epoch); self } /// Add an additional account to the instruction. @@ -501,7 +499,7 @@ impl<'a, 'b> RouteNcnRewardsCpiBuilder<'a, 'b> { .ncn_fee_group .clone() .expect("ncn_fee_group is not set"), - first_slot_of_ncn_epoch: self.instruction.first_slot_of_ncn_epoch.clone(), + epoch: self.instruction.epoch.clone().expect("epoch is not set"), }; let instruction = RouteNcnRewardsCpi { __program: self.instruction.__program, @@ -548,7 +546,7 @@ struct RouteNcnRewardsCpiBuilderInstruction<'a, 'b> { ncn_reward_router: Option<&'b solana_program::account_info::AccountInfo<'a>>, restaking_program: Option<&'b solana_program::account_info::AccountInfo<'a>>, ncn_fee_group: Option, - first_slot_of_ncn_epoch: Option, + epoch: Option, /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. __remaining_accounts: Vec<( &'b solana_program::account_info::AccountInfo<'a>, diff --git a/core/src/ballot_box.rs b/core/src/ballot_box.rs index 17e54449..08451950 100644 --- a/core/src/ballot_box.rs +++ b/core/src/ballot_box.rs @@ -751,13 +751,13 @@ mod tests { assert_eq!(ballot_box.ballot_tallies[1].ballot(), ballot2); // Test error when ballot tallies are full - for i in 3..=16 { - let ballot = Ballot::new([i as u8; 32]); + for _ in 3..=ballot_box.ballot_tallies.len() { + let ballot = Ballot::new(Pubkey::new_unique().to_bytes()); ballot_box .increment_or_create_ballot_tally(&ballot, &stake_weights) .unwrap(); } - let ballot_full = Ballot::new([33u8; 32]); + let ballot_full = Ballot::new(Pubkey::new_unique().to_bytes()); let result = ballot_box.increment_or_create_ballot_tally(&ballot_full, &stake_weights); assert!(matches!(result, Err(TipRouterError::BallotTallyFull))); } diff --git a/core/src/epoch_snapshot.rs b/core/src/epoch_snapshot.rs index 0aa3d9e9..3dca72b4 100644 --- a/core/src/epoch_snapshot.rs +++ b/core/src/epoch_snapshot.rs @@ -378,11 +378,11 @@ impl OperatorSnapshot { Ok(()) } - pub fn operator(&self) -> Pubkey { + pub const fn operator(&self) -> Pubkey { self.operator } - pub fn ncn(&self) -> Pubkey { + pub const fn ncn(&self) -> Pubkey { self.ncn } diff --git a/core/src/instruction.rs b/core/src/instruction.rs index 0738f334..5095ffa3 100644 --- a/core/src/instruction.rs +++ b/core/src/instruction.rs @@ -177,7 +177,7 @@ pub enum TipRouterInstruction { #[account(4, writable, name = "base_reward_router")] #[account(5, name = "restaking_program")] RouteBaseRewards{ - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Routes ncn reward router @@ -189,7 +189,7 @@ pub enum TipRouterInstruction { #[account(5, name = "restaking_program")] RouteNcnRewards{ ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Distributes base rewards @@ -201,7 +201,7 @@ pub enum TipRouterInstruction { #[account(5, name = "restaking_program")] DistributeBaseRewards{ base_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Distributes base ncn reward routes @@ -214,7 +214,7 @@ pub enum TipRouterInstruction { #[account(6, name = "restaking_program")] DistributeBaseNcnRewardRoute{ ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Distributes ncn operator rewards @@ -226,7 +226,7 @@ pub enum TipRouterInstruction { #[account(5, name = "restaking_program")] DistributeNcnOperatorRewards{ ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Distributes ncn vault rewards @@ -238,7 +238,7 @@ pub enum TipRouterInstruction { #[account(5, writable, name = "ncn_reward_router")] DistributeNcnVaultRewards{ ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, }, /// Sets the NCN fee group for a mint diff --git a/core/src/utils.rs b/core/src/utils.rs index e9a6272f..0962dcf7 100644 --- a/core/src/utils.rs +++ b/core/src/utils.rs @@ -3,7 +3,6 @@ use solana_program::{ entrypoint::ProgramResult, program::{invoke, invoke_signed}, program_error::ProgramError, - pubkey::Pubkey, rent::Rent, system_instruction, }; diff --git a/idl/jito_tip_router.json b/idl/jito_tip_router.json index f705ad05..a4ecb2a7 100644 --- a/idl/jito_tip_router.json +++ b/idl/jito_tip_router.json @@ -708,10 +708,8 @@ ], "args": [ { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -759,10 +757,8 @@ "type": "u8" }, { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -810,10 +806,8 @@ "type": "u8" }, { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -866,10 +860,8 @@ "type": "u8" }, { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -917,10 +909,8 @@ "type": "u8" }, { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { @@ -968,10 +958,8 @@ "type": "u8" }, { - "name": "firstSlotOfNcnEpoch", - "type": { - "option": "u64" - } + "name": "epoch", + "type": "u64" } ], "discriminant": { diff --git a/integration_tests/tests/fixtures/mod.rs b/integration_tests/tests/fixtures/mod.rs index 7103bbdd..7aa8d15a 100644 --- a/integration_tests/tests/fixtures/mod.rs +++ b/integration_tests/tests/fixtures/mod.rs @@ -31,12 +31,12 @@ pub enum TestError { impl TestError { pub fn to_transaction_error(&self) -> Option { match self { - TestError::BanksClientError(e) => match e { + Self::BanksClientError(e) => match e { BanksClientError::TransactionError(e) => Some(e.clone()), BanksClientError::SimulationError { err, .. } => Some(err.clone()), _ => None, }, - TestError::ProgramError(_) => None, + Self::ProgramError(_) => None, _ => None, } } diff --git a/integration_tests/tests/fixtures/test_builder.rs b/integration_tests/tests/fixtures/test_builder.rs index 87235de4..d5f9917c 100644 --- a/integration_tests/tests/fixtures/test_builder.rs +++ b/integration_tests/tests/fixtures/test_builder.rs @@ -15,7 +15,6 @@ use solana_program::{ use solana_program_test::{processor, BanksClientError, ProgramTest, ProgramTestContext}; use solana_sdk::{ account::Account, - clock::Slot, commitment_config::CommitmentLevel, epoch_schedule::EpochSchedule, native_token::{lamports_to_sol, LAMPORTS_PER_SOL}, @@ -63,7 +62,7 @@ impl Debug for TestBuilder { } } -pub fn system_account(lamports: u64) -> Account { +pub const fn system_account(lamports: u64) -> Account { Account { lamports, owner: solana_program::system_program::ID, @@ -136,7 +135,7 @@ impl TestBuilder { } pub async fn get_balance(&mut self, pubkey: &Pubkey) -> Result { - Ok(self.context.banks_client.get_balance(*pubkey).await?) + self.context.banks_client.get_balance(*pubkey).await } pub async fn get_account( @@ -367,11 +366,11 @@ impl TestBuilder { for operator_root in test_ncn.operators.iter() { // vault <> operator restaking_program_client - .do_initialize_operator_vault_ticket(&operator_root, &vault_root.vault_pubkey) + .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) + .do_warmup_operator_vault_ticket(operator_root, &vault_root.vault_pubkey) .await?; vault_program_client .do_initialize_vault_operator_delegation( @@ -409,7 +408,7 @@ impl TestBuilder { for operator_root in test_ncn.operators.iter() { vault_program_client .do_add_delegation( - &vault_root, + vault_root, &operator_root.operator_pubkey, delegation_amount as u64, ) @@ -576,10 +575,10 @@ impl TestBuilder { // 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) + 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(()) @@ -629,8 +628,8 @@ impl TestBuilder { // Intermission 3 - come to consensus pub async fn vote_test_ncn(&mut self, test_ncn: &TestNcn) -> TestResult<()> { - self.add_ballot_box_to_test_ncn(&test_ncn).await?; - self.cast_votes_for_test_ncn(&test_ncn).await?; + self.add_ballot_box_to_test_ncn(test_ncn).await?; + self.cast_votes_for_test_ncn(test_ncn).await?; Ok(()) } @@ -641,33 +640,16 @@ impl TestBuilder { let ncn: Pubkey = test_ncn.ncn_root.ncn_pubkey; let clock = self.clock().await; - let slot = clock.slot; let epoch = clock.epoch; tip_router_client - .do_full_initialize_base_reward_router(ncn, slot) + .do_full_initialize_base_reward_router(ncn, epoch) .await?; - println!("Payer pubkey: {}", self.context.payer.pubkey()); - println!( - "Payer funds: {}", - self.context - .banks_client - .get_balance(self.context.payer.pubkey()) - .await? - ); for operator_root in test_ncn.operators.iter() { let operator = operator_root.operator_pubkey; for group in NcnFeeGroup::all_groups().iter() { - println!("Payer pubkey: {}", self.context.payer.pubkey()); - println!( - "Payer funds: {}", - self.context - .banks_client - .get_balance(self.context.payer.pubkey()) - .await? - ); tip_router_client .do_initialize_ncn_reward_router(*group, ncn, operator, epoch) .await?; @@ -684,14 +666,12 @@ impl TestBuilder { rewards: u64, ) -> TestResult<()> { let mut tip_router_client = self.tip_router_client(); - let mut restaking_program_client = self.restaking_program_client(); let ncn = test_ncn.ncn_root.ncn_pubkey; - let slot = self.clock().await.slot; - let ncn_epoch = restaking_program_client.get_ncn_epoch(slot).await?; + let epoch = self.clock().await.epoch; let (base_reward_router, _, _) = - BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); + BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, epoch); let sol_rewards = lamports_to_sol(rewards); @@ -701,11 +681,9 @@ impl TestBuilder { .await?; // route rewards - tip_router_client.do_route_base_rewards(ncn, slot).await?; + tip_router_client.do_route_base_rewards(ncn, epoch).await?; - let base_reward_router = tip_router_client - .get_base_reward_router(ncn, ncn_epoch) - .await?; + let base_reward_router = tip_router_client.get_base_reward_router(ncn, epoch).await?; // Base Rewards for group in BaseFeeGroup::all_groups().iter() { @@ -716,7 +694,7 @@ impl TestBuilder { } tip_router_client - .do_distribute_base_rewards(*group, ncn, slot) + .do_distribute_base_rewards(*group, ncn, epoch) .await?; } @@ -735,7 +713,7 @@ impl TestBuilder { } tip_router_client - .do_distribute_base_ncn_reward_route(*group, operator, ncn, slot) + .do_distribute_base_ncn_reward_route(*group, operator, ncn, epoch) .await?; } } @@ -750,29 +728,27 @@ impl TestBuilder { test_ncn: &TestNcn, ) -> TestResult<()> { let mut tip_router_client = self.tip_router_client(); - let mut restaking_program_client = self.restaking_program_client(); let ncn = test_ncn.ncn_root.ncn_pubkey; - let slot = self.clock().await.slot; - let ncn_epoch = restaking_program_client.get_ncn_epoch(slot).await?; + let epoch = self.clock().await.epoch; for operator_root in test_ncn.operators.iter() { let operator = operator_root.operator_pubkey; for group in NcnFeeGroup::all_groups().iter() { tip_router_client - .do_route_ncn_rewards(*group, ncn, operator, slot) + .do_route_ncn_rewards(*group, ncn, operator, epoch) .await?; let ncn_reward_router = tip_router_client - .get_ncn_reward_router(*group, operator, ncn, ncn_epoch) + .get_ncn_reward_router(*group, operator, ncn, epoch) .await?; let operator_rewards = ncn_reward_router.operator_rewards(); if operator_rewards > 0 { tip_router_client - .do_distribute_ncn_operator_rewards(*group, operator, ncn, slot) + .do_distribute_ncn_operator_rewards(*group, operator, ncn, epoch) .await?; } @@ -786,7 +762,9 @@ impl TestBuilder { if vault_rewards > 0 { tip_router_client - .do_distribute_ncn_vault_rewards(*group, vault, operator, ncn, slot) + .do_distribute_ncn_vault_rewards( + *group, vault, operator, ncn, epoch, + ) .await?; } } diff --git a/integration_tests/tests/fixtures/tip_distribution_client.rs b/integration_tests/tests/fixtures/tip_distribution_client.rs index eae7c8a2..055866ba 100644 --- a/integration_tests/tests/fixtures/tip_distribution_client.rs +++ b/integration_tests/tests/fixtures/tip_distribution_client.rs @@ -96,7 +96,7 @@ impl TipDistributionClient { &self.payer.pubkey(), &vote_keypair.pubkey(), &vote_init, - 1 * LAMPORTS_PER_SOL, + LAMPORTS_PER_SOL, CreateVoteAccountConfig { space: VoteStateVersions::vote_state_size_of(true) as u64, with_seed: None, diff --git a/integration_tests/tests/fixtures/tip_router_client.rs b/integration_tests/tests/fixtures/tip_router_client.rs index 1854f2ec..665c44df 100644 --- a/integration_tests/tests/fixtures/tip_router_client.rs +++ b/integration_tests/tests/fixtures/tip_router_client.rs @@ -309,7 +309,7 @@ impl TipRouterClient { new_base_fee_bps, ncn_fee_group, new_ncn_fee_bps, - &ncn_root, + ncn_root, ) .await } @@ -542,11 +542,9 @@ impl TipRouterClient { let tracked_mints = TrackedMints::find_program_address(&jito_tip_router_program::id(), &ncn).0; - let restaking_config = self.get_restaking_config().await?; - let current_slot = self.banks_client.get_sysvar::().await?.slot; - let ncn_epoch = current_slot / restaking_config.epoch_length(); + let epoch = self.banks_client.get_sysvar::().await?.epoch; let weight_table = - WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch).0; + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; self.register_mint( restaking_config_address, @@ -612,14 +610,11 @@ impl TipRouterClient { ) -> 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 = epoch / restaking_config_account.epoch_length(); - 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; + WeightTable::find_program_address(&jito_tip_router_program::id(), &ncn, epoch).0; let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); @@ -1301,17 +1296,14 @@ impl TipRouterClient { pub async fn do_route_base_rewards(&mut self, ncn: Pubkey, epoch: 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 = epoch / restaking_config_account.epoch_length(); - let (epoch_snapshot, _, _) = - EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); + EpochSnapshot::find_program_address(&jito_tip_router_program::id(), &ncn, epoch); let (ballot_box, _, _) = - BallotBox::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); + BallotBox::find_program_address(&jito_tip_router_program::id(), &ncn, epoch); let (base_reward_router, _, _) = - BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); + BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, epoch); self.route_base_rewards( ncn, @@ -1319,6 +1311,7 @@ impl TipRouterClient { epoch_snapshot, ballot_box, base_reward_router, + epoch, ) .await } @@ -1330,6 +1323,7 @@ impl TipRouterClient { epoch_snapshot: Pubkey, ballot_box: Pubkey, base_reward_router: Pubkey, + epoch: u64, ) -> TestResult<()> { let ix = RouteBaseRewardsBuilder::new() .restaking_config(restaking_config) @@ -1338,6 +1332,7 @@ impl TipRouterClient { .ballot_box(ballot_box) .base_reward_router(base_reward_router) .restaking_program(jito_restaking_program::id()) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -1359,14 +1354,11 @@ impl TipRouterClient { ) -> 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 = epoch / restaking_config_account.epoch_length(); - let (operator_snapshot, _, _) = OperatorSnapshot::find_program_address( &jito_tip_router_program::id(), &operator, &ncn, - ncn_epoch, + epoch, ); let (ncn_reward_router, _, _) = NcnRewardRouter::find_program_address( @@ -1374,7 +1366,7 @@ impl TipRouterClient { ncn_fee_group, &operator, &ncn, - ncn_epoch, + epoch, ); self.route_ncn_rewards( @@ -1384,6 +1376,7 @@ impl TipRouterClient { restaking_config, operator_snapshot, ncn_reward_router, + epoch, ) .await } @@ -1396,6 +1389,7 @@ impl TipRouterClient { restaking_config: Pubkey, operator_snapshot: Pubkey, ncn_reward_router: Pubkey, + epoch: u64, ) -> TestResult<()> { let ix = RouteNcnRewardsBuilder::new() .restaking_config(restaking_config) @@ -1405,6 +1399,7 @@ impl TipRouterClient { .ncn_reward_router(ncn_reward_router) .restaking_program(jito_restaking_program::id()) .ncn_fee_group(ncn_fee_group.group) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -1425,14 +1420,11 @@ impl TipRouterClient { ) -> 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 = epoch / restaking_config_account.epoch_length(); - let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); let (base_reward_router, _, _) = - BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); + BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, epoch); let ncn_config_account = self.get_ncn_config(ncn).await?; let base_fee_wallet = ncn_config_account @@ -1447,6 +1439,7 @@ impl TipRouterClient { ncn_config, base_reward_router, base_fee_wallet, + epoch, ) .await } @@ -1459,6 +1452,7 @@ impl TipRouterClient { ncn_config: Pubkey, base_reward_router: Pubkey, base_fee_wallet: Pubkey, + epoch: u64, ) -> TestResult<()> { let ix = DistributeBaseRewardsBuilder::new() .restaking_config(restaking_config) @@ -1468,6 +1462,7 @@ impl TipRouterClient { .base_fee_wallet(base_fee_wallet) .restaking_program(jito_restaking_program::id()) .base_fee_group(base_fee_group.group) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -1489,21 +1484,18 @@ impl TipRouterClient { ) -> 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 = epoch / restaking_config_account.epoch_length(); - let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); let (base_reward_router, _, _) = - BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, ncn_epoch); + BaseRewardRouter::find_program_address(&jito_tip_router_program::id(), &ncn, epoch); let (ncn_reward_router, _, _) = NcnRewardRouter::find_program_address( &jito_tip_router_program::id(), ncn_fee_group, &operator, &ncn, - ncn_epoch, + epoch, ); self.distribute_base_ncn_reward_route( @@ -1514,6 +1506,7 @@ impl TipRouterClient { ncn_config, base_reward_router, ncn_reward_router, + epoch, ) .await } @@ -1527,6 +1520,7 @@ impl TipRouterClient { ncn_config: Pubkey, base_reward_router: Pubkey, ncn_reward_router: Pubkey, + epoch: u64, ) -> TestResult<()> { let ix = DistributeBaseNcnRewardRouteBuilder::new() .restaking_config(restaking_config) @@ -1537,6 +1531,7 @@ impl TipRouterClient { .ncn_reward_router(ncn_reward_router) .restaking_program(jito_restaking_program::id()) .ncn_fee_group(ncn_fee_group.group) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -1558,9 +1553,6 @@ impl TipRouterClient { ) -> 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 = epoch / restaking_config_account.epoch_length(); - let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); @@ -1569,7 +1561,7 @@ impl TipRouterClient { ncn_fee_group, &operator, &ncn, - ncn_epoch, + epoch, ); self.distribute_ncn_operator_rewards( @@ -1579,6 +1571,7 @@ impl TipRouterClient { restaking_config, ncn_config, ncn_reward_router, + epoch, ) .await } @@ -1591,6 +1584,7 @@ impl TipRouterClient { restaking_config: Pubkey, ncn_config: Pubkey, ncn_reward_router: Pubkey, + epoch: u64, ) -> TestResult<()> { let ix = DistributeNcnOperatorRewardsBuilder::new() .restaking_config(restaking_config) @@ -1600,6 +1594,7 @@ impl TipRouterClient { .ncn_reward_router(ncn_reward_router) .restaking_program(jito_restaking_program::id()) .ncn_fee_group(ncn_fee_group.group) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; @@ -1622,9 +1617,6 @@ impl TipRouterClient { ) -> 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 = epoch / restaking_config_account.epoch_length(); - let (ncn_config, _, _) = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn); @@ -1633,7 +1625,7 @@ impl TipRouterClient { ncn_fee_group, &operator, &ncn, - ncn_epoch, + epoch, ); self.distribute_ncn_vault_rewards( @@ -1644,6 +1636,7 @@ impl TipRouterClient { restaking_config, ncn_config, ncn_reward_router, + epoch, ) .await } @@ -1657,6 +1650,7 @@ impl TipRouterClient { restaking_config: Pubkey, ncn_config: Pubkey, ncn_reward_router: Pubkey, + epoch: u64, ) -> TestResult<()> { let ix = DistributeNcnVaultRewardsBuilder::new() .restaking_config(restaking_config) @@ -1666,6 +1660,7 @@ impl TipRouterClient { .vault(vault) .ncn_reward_router(ncn_reward_router) .ncn_fee_group(ncn_fee_group.group) + .epoch(epoch) .instruction(); let blockhash = self.banks_client.get_latest_blockhash().await?; diff --git a/integration_tests/tests/helpers/ballot_box.rs b/integration_tests/tests/helpers/ballot_box.rs index 38b84742..ff8ab317 100644 --- a/integration_tests/tests/helpers/ballot_box.rs +++ b/integration_tests/tests/helpers/ballot_box.rs @@ -8,13 +8,11 @@ pub fn serialized_ballot_box_account(ballot_box: &BallotBox) -> Account { data.extend_from_slice(&[0; 7]); data.extend_from_slice(bytemuck::bytes_of(ballot_box)); - let account = Account { + Account { lamports: LAMPORTS_PER_SOL * 5, data, owner: jito_tip_router_program::id(), executable: false, rent_epoch: 0, - }; - - account + } } diff --git a/integration_tests/tests/tip_router/distribute_rewards.rs b/integration_tests/tests/tip_router/distribute_rewards.rs index 23106873..a43b153b 100644 --- a/integration_tests/tests/tip_router/distribute_rewards.rs +++ b/integration_tests/tests/tip_router/distribute_rewards.rs @@ -55,13 +55,13 @@ mod tests { .await?; // Set tracked mint NCN fee group - let slot = fixture.clock().await.slot; + let epoch = fixture.clock().await.epoch; tip_router_client .do_set_tracked_mint_ncn_fee_group( test_ncn.ncn_root.ncn_pubkey, 1, NcnFeeGroup::new(NcnFeeGroupType::JTO), - slot, + epoch, ) .await?; @@ -73,18 +73,14 @@ mod tests { fixture.vote_test_ncn(&test_ncn).await?; ////// - let slot = fixture.clock().await.slot; let ncn = test_ncn.ncn_root.ncn_pubkey; // Initialize the routers fixture.add_routers_for_tests_ncn(&test_ncn).await?; // Get initial balances - let restaking_config_account = tip_router_client.get_restaking_config().await?; - let ncn_epoch = slot / restaking_config_account.epoch_length(); let ncn_config = tip_router_client.get_ncn_config(ncn).await?; - let clock = fixture.clock().await; - let epoch = clock.epoch; + let epoch = fixture.clock().await.epoch; let dao_initial_balance = fixture .get_balance( @@ -123,7 +119,7 @@ mod tests { .await?; // Route rewards - tip_router_client.do_route_base_rewards(ncn, slot).await?; + tip_router_client.do_route_base_rewards(ncn, epoch).await?; // Check Rewards let base_reward_router_account = @@ -162,7 +158,7 @@ mod tests { // Distribute base rewards (DAO fee) tip_router_client - .do_distribute_base_rewards(BaseFeeGroup::default(), ncn, slot) + .do_distribute_base_rewards(BaseFeeGroup::default(), ncn, epoch) .await?; // Distribute base NCN rewards (operator rewards) @@ -171,7 +167,7 @@ mod tests { for group in NcnFeeGroup::all_groups().iter() { tip_router_client - .do_distribute_base_ncn_reward_route(*group, operator, ncn, slot) + .do_distribute_base_ncn_reward_route(*group, operator, ncn, epoch) .await?; } } @@ -218,7 +214,9 @@ mod tests { } tip_router_client - .do_distribute_ncn_vault_rewards(*group, vault, operator, ncn, slot) + .do_distribute_ncn_vault_rewards( + *group, vault, operator, ncn, epoch, + ) .await?; } } @@ -247,7 +245,7 @@ mod tests { assert_eq!(dao_reward, 2_700); // NCN Reward Routes - assert_eq!(*operator_total_rewards.get(0).unwrap(), 150); + assert_eq!(*operator_total_rewards.first().unwrap(), 150); assert_eq!(*operator_total_rewards.get(1).unwrap(), 150); // Operator 1 Rewards diff --git a/integration_tests/tests/tip_router/initialize_ballot_box.rs b/integration_tests/tests/tip_router/initialize_ballot_box.rs index 5fb7476a..1aa265f4 100644 --- a/integration_tests/tests/tip_router/initialize_ballot_box.rs +++ b/integration_tests/tests/tip_router/initialize_ballot_box.rs @@ -1,8 +1,6 @@ #[cfg(test)] mod tests { - use std::borrow::Borrow; - use jito_tip_router_core::{ ballot_box::BallotBox, constants::{DEFAULT_CONSENSUS_REACHED_SLOT, MAX_REALLOC_BYTES}, diff --git a/integration_tests/tests/tip_router/initialize_operator_snapshot.rs b/integration_tests/tests/tip_router/initialize_operator_snapshot.rs index 41416d48..81979534 100644 --- a/integration_tests/tests/tip_router/initialize_operator_snapshot.rs +++ b/integration_tests/tests/tip_router/initialize_operator_snapshot.rs @@ -8,7 +8,6 @@ mod tests { #[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, None).await?; diff --git a/integration_tests/tests/tip_router/initialize_weight_table.rs b/integration_tests/tests/tip_router/initialize_weight_table.rs index cfc1a6f1..a5a09cfc 100644 --- a/integration_tests/tests/tip_router/initialize_weight_table.rs +++ b/integration_tests/tests/tip_router/initialize_weight_table.rs @@ -36,7 +36,7 @@ mod tests { .await?; let raw_account = fixture.get_account(&address).await?.unwrap(); - assert_eq!(raw_account.data.len(), WeightTable::SIZE as usize); + assert_eq!(raw_account.data.len(), { WeightTable::SIZE }); assert_eq!(raw_account.owner, jito_tip_router_program::id()); assert_eq!(raw_account.data[0], WeightTable::DISCRIMINATOR); diff --git a/integration_tests/tests/tip_router/meta_tests.rs b/integration_tests/tests/tip_router/meta_tests.rs index eaee63ce..0c1b3f9a 100644 --- a/integration_tests/tests/tip_router/meta_tests.rs +++ b/integration_tests/tests/tip_router/meta_tests.rs @@ -42,7 +42,6 @@ mod tests { 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; @@ -78,7 +77,6 @@ mod tests { 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; @@ -114,7 +112,6 @@ mod tests { 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; diff --git a/integration_tests/tests/tip_router/register_mint.rs b/integration_tests/tests/tip_router/register_mint.rs index 4605a82f..f011e333 100644 --- a/integration_tests/tests/tip_router/register_mint.rs +++ b/integration_tests/tests/tip_router/register_mint.rs @@ -183,7 +183,6 @@ mod tests { } #[tokio::test] - // TODO: FLAKY!!!! >:( async fn test_register_mint_fails_with_weight_table() -> TestResult<()> { let mut fixture = TestBuilder::new().await; let mut tip_router_client = fixture.tip_router_client(); @@ -241,8 +240,9 @@ mod tests { ) .await?; + let epoch = fixture.clock().await.epoch; tip_router_client - .initialize_weight_table(ncn_root.ncn_pubkey, fixture.clock().await.slot) + .initialize_weight_table(ncn_root.ncn_pubkey, epoch) .await?; let result = tip_router_client diff --git a/integration_tests/tests/tip_router/set_tracked_mint_ncn_fee_group.rs b/integration_tests/tests/tip_router/set_tracked_mint_ncn_fee_group.rs index d5b61707..dd1341e1 100644 --- a/integration_tests/tests/tip_router/set_tracked_mint_ncn_fee_group.rs +++ b/integration_tests/tests/tip_router/set_tracked_mint_ncn_fee_group.rs @@ -71,17 +71,12 @@ mod tests { .get_tracked_mints(ncn_root.ncn_pubkey) .await?; assert_eq!(tracked_mints.mint_count(), 1); - let current_slot = fixture.clock().await.slot; + let epoch = fixture.clock().await.epoch; let new_ncn_fee_group = NcnFeeGroup::new(NcnFeeGroupType::Reserved7); tip_router_client - .do_set_tracked_mint_ncn_fee_group( - ncn_root.ncn_pubkey, - 0, - new_ncn_fee_group, - current_slot, - ) + .do_set_tracked_mint_ncn_fee_group(ncn_root.ncn_pubkey, 0, new_ncn_fee_group, epoch) .await?; let tracked_mints = tip_router_client @@ -155,19 +150,13 @@ mod tests { ) .await?; + let epoch = fixture.clock().await.epoch; // Is Okay { - let current_slot = fixture.clock().await.slot; - let new_ncn_fee_group = NcnFeeGroup::new(NcnFeeGroupType::Reserved7); tip_router_client - .do_set_tracked_mint_ncn_fee_group( - ncn_root.ncn_pubkey, - 0, - new_ncn_fee_group, - current_slot, - ) + .do_set_tracked_mint_ncn_fee_group(ncn_root.ncn_pubkey, 0, new_ncn_fee_group, epoch) .await?; let tracked_mints = tip_router_client @@ -182,22 +171,15 @@ mod tests { } tip_router_client - .do_full_initialize_weight_table(ncn_root.ncn_pubkey, fixture.clock().await.epoch) + .do_full_initialize_weight_table(ncn_root.ncn_pubkey, epoch) .await?; // Should fail { - let current_slot = fixture.clock().await.slot; - let new_ncn_fee_group = NcnFeeGroup::new(NcnFeeGroupType::Reserved5); let result = tip_router_client - .do_set_tracked_mint_ncn_fee_group( - ncn_root.ncn_pubkey, - 0, - new_ncn_fee_group, - current_slot, - ) + .do_set_tracked_mint_ncn_fee_group(ncn_root.ncn_pubkey, 0, new_ncn_fee_group, epoch) .await; assert!(result.is_err()); diff --git a/program/src/distribute_base_ncn_reward_route.rs b/program/src/distribute_base_ncn_reward_route.rs index b5b7e753..e606cb2d 100644 --- a/program/src/distribute_base_ncn_reward_route.rs +++ b/program/src/distribute_base_ncn_reward_route.rs @@ -1,12 +1,12 @@ use jito_bytemuck::AccountDeserialize; use jito_restaking_core::{config::Config, ncn::Ncn, operator::Operator}; use jito_tip_router_core::{ - base_reward_router::BaseRewardRouter, error::TipRouterError, loaders::load_ncn_epoch, - ncn_config::NcnConfig, ncn_fee_group::NcnFeeGroup, ncn_reward_router::NcnRewardRouter, + base_reward_router::BaseRewardRouter, error::TipRouterError, ncn_config::NcnConfig, + ncn_fee_group::NcnFeeGroup, ncn_reward_router::NcnRewardRouter, }; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, }; /// Can be backfilled for previous epochs @@ -14,7 +14,7 @@ pub fn process_distribute_base_ncn_reward_route( program_id: &Pubkey, accounts: &[AccountInfo], ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, ncn_config, ncn, operator, base_reward_router, ncn_reward_router, restaking_program] = accounts @@ -31,18 +31,16 @@ pub fn process_distribute_base_ncn_reward_route( Ncn::load(restaking_program.key, ncn, false)?; Operator::load(restaking_program.key, operator, false)?; - let current_slot = Clock::get()?.slot; - let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; let ncn_fee_group = NcnFeeGroup::try_from(ncn_fee_group)?; NcnConfig::load(program_id, ncn.key, ncn_config, false)?; - BaseRewardRouter::load(program_id, ncn.key, ncn_epoch, base_reward_router, true)?; + BaseRewardRouter::load(program_id, ncn.key, epoch, base_reward_router, true)?; NcnRewardRouter::load( program_id, ncn_fee_group, operator.key, ncn.key, - ncn_epoch, + epoch, ncn_reward_router, true, )?; diff --git a/program/src/distribute_base_rewards.rs b/program/src/distribute_base_rewards.rs index d0c7633f..c1a62098 100644 --- a/program/src/distribute_base_rewards.rs +++ b/program/src/distribute_base_rewards.rs @@ -2,11 +2,11 @@ use jito_bytemuck::AccountDeserialize; use jito_restaking_core::{config::Config, ncn::Ncn}; use jito_tip_router_core::{ base_fee_group::BaseFeeGroup, base_reward_router::BaseRewardRouter, error::TipRouterError, - loaders::load_ncn_epoch, ncn_config::NcnConfig, + ncn_config::NcnConfig, }; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, }; /// Can be backfilled for previous epochs @@ -14,7 +14,7 @@ pub fn process_distribute_base_rewards( program_id: &Pubkey, accounts: &[AccountInfo], base_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, ncn_config, ncn, base_reward_router, base_fee_wallet, restaking_program] = accounts @@ -35,12 +35,9 @@ pub fn process_distribute_base_rewards( Config::load(restaking_program.key, restaking_config, false)?; Ncn::load(restaking_program.key, ncn, false)?; - let current_slot = Clock::get()?.slot; - let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; - NcnConfig::load(program_id, ncn.key, ncn_config, false)?; - BaseRewardRouter::load(program_id, ncn.key, ncn_epoch, base_reward_router, true)?; + BaseRewardRouter::load(program_id, ncn.key, epoch, base_reward_router, true)?; let group = BaseFeeGroup::try_from(base_fee_group)?; diff --git a/program/src/distribute_ncn_operator_rewards.rs b/program/src/distribute_ncn_operator_rewards.rs index 3a6f854e..2507653a 100644 --- a/program/src/distribute_ncn_operator_rewards.rs +++ b/program/src/distribute_ncn_operator_rewards.rs @@ -1,12 +1,12 @@ use jito_bytemuck::AccountDeserialize; use jito_restaking_core::{config::Config, ncn::Ncn, operator::Operator}; use jito_tip_router_core::{ - error::TipRouterError, loaders::load_ncn_epoch, ncn_config::NcnConfig, - ncn_fee_group::NcnFeeGroup, ncn_reward_router::NcnRewardRouter, + error::TipRouterError, ncn_config::NcnConfig, ncn_fee_group::NcnFeeGroup, + ncn_reward_router::NcnRewardRouter, }; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, }; /// Can be backfilled for previous epochs @@ -14,7 +14,7 @@ pub fn process_distribute_ncn_operator_rewards( program_id: &Pubkey, accounts: &[AccountInfo], ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, ncn_config, ncn, operator, ncn_reward_router, restaking_program] = accounts @@ -31,8 +31,6 @@ pub fn process_distribute_ncn_operator_rewards( Ncn::load(restaking_program.key, ncn, false)?; Operator::load(restaking_program.key, operator, true)?; - let current_slot = Clock::get()?.slot; - let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; let ncn_fee_group = NcnFeeGroup::try_from(ncn_fee_group)?; NcnConfig::load(program_id, ncn.key, ncn_config, false)?; @@ -41,7 +39,7 @@ pub fn process_distribute_ncn_operator_rewards( ncn_fee_group, operator.key, ncn.key, - ncn_epoch, + epoch, ncn_reward_router, true, )?; diff --git a/program/src/distribute_ncn_vault_rewards.rs b/program/src/distribute_ncn_vault_rewards.rs index 8ea4cc32..5e399dd4 100644 --- a/program/src/distribute_ncn_vault_rewards.rs +++ b/program/src/distribute_ncn_vault_rewards.rs @@ -1,13 +1,13 @@ use jito_bytemuck::AccountDeserialize; use jito_restaking_core::{config::Config, ncn::Ncn, operator::Operator}; use jito_tip_router_core::{ - error::TipRouterError, loaders::load_ncn_epoch, ncn_config::NcnConfig, - ncn_fee_group::NcnFeeGroup, ncn_reward_router::NcnRewardRouter, + error::TipRouterError, ncn_config::NcnConfig, ncn_fee_group::NcnFeeGroup, + ncn_reward_router::NcnRewardRouter, }; use jito_vault_core::vault::Vault; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, - program_error::ProgramError, pubkey::Pubkey, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, + pubkey::Pubkey, }; /// Can be backfilled for previous epochs @@ -15,7 +15,7 @@ pub fn process_distribute_ncn_vault_rewards( program_id: &Pubkey, accounts: &[AccountInfo], ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, ncn_config, ncn, operator, vault, ncn_reward_router] = accounts else { return Err(ProgramError::NotEnoughAccountKeys); @@ -29,8 +29,6 @@ pub fn process_distribute_ncn_vault_rewards( Operator::load(&restaking_program, operator, false)?; Vault::load(&vault_program, vault, true)?; - let current_slot = Clock::get()?.slot; - let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; let ncn_fee_group = NcnFeeGroup::try_from(ncn_fee_group)?; NcnConfig::load(program_id, ncn.key, ncn_config, false)?; @@ -39,7 +37,7 @@ pub fn process_distribute_ncn_vault_rewards( ncn_fee_group, operator.key, ncn.key, - ncn_epoch, + epoch, ncn_reward_router, true, )?; diff --git a/program/src/initialize_ballot_box.rs b/program/src/initialize_ballot_box.rs index 54482e5e..fdf28e58 100644 --- a/program/src/initialize_ballot_box.rs +++ b/program/src/initialize_ballot_box.rs @@ -1,4 +1,3 @@ -use jito_bytemuck::{AccountDeserialize, Discriminator}; use jito_jsm_core::{ create_account, loader::{load_signer, load_system_account, load_system_program}, diff --git a/program/src/initialize_epoch_snapshot.rs b/program/src/initialize_epoch_snapshot.rs index 32e9f87b..c94392bd 100644 --- a/program/src/initialize_epoch_snapshot.rs +++ b/program/src/initialize_epoch_snapshot.rs @@ -5,8 +5,8 @@ use jito_jsm_core::{ }; 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, + epoch_snapshot::EpochSnapshot, error::TipRouterError, fees, ncn_config::NcnConfig, + tracked_mints::TrackedMints, weight_table::WeightTable, }; use solana_program::{ account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, diff --git a/program/src/initialize_operator_snapshot.rs b/program/src/initialize_operator_snapshot.rs index c93c576a..88ea6472 100644 --- a/program/src/initialize_operator_snapshot.rs +++ b/program/src/initialize_operator_snapshot.rs @@ -1,4 +1,3 @@ -use jito_bytemuck::{AccountDeserialize, Discriminator}; use jito_jsm_core::{ create_account, loader::{load_signer, load_system_account, load_system_program}, @@ -9,13 +8,11 @@ use jito_restaking_core::{ use jito_tip_router_core::{ constants::MAX_REALLOC_BYTES, epoch_snapshot::{EpochSnapshot, OperatorSnapshot}, - loaders::load_ncn_epoch, ncn_config::NcnConfig, - stake_weight::StakeWeights, }; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; /// Initializes an Operator Snapshot diff --git a/program/src/initialize_weight_table.rs b/program/src/initialize_weight_table.rs index 200b0b31..90f27386 100644 --- a/program/src/initialize_weight_table.rs +++ b/program/src/initialize_weight_table.rs @@ -1,18 +1,15 @@ -use std::mem::size_of; - -use jito_bytemuck::{AccountDeserialize, Discriminator}; +use jito_bytemuck::AccountDeserialize; 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::{ - constants::MAX_REALLOC_BYTES, loaders::load_ncn_epoch, tracked_mints::TrackedMints, - weight_table::WeightTable, + constants::MAX_REALLOC_BYTES, 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, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; /// Initializes a Weight Table @@ -41,9 +38,6 @@ pub fn process_initialize_weight_table( load_system_program(system_program)?; load_signer(payer, true)?; - let current_slot = Clock::get()?.slot; - let ncn_epoch = epoch; - let vault_count = { let ncn_data = ncn.data.borrow(); let ncn = Ncn::try_from_slice_unchecked(&ncn_data)?; @@ -62,7 +56,7 @@ pub fn process_initialize_weight_table( } let (weight_table_pubkey, weight_table_bump, mut weight_table_seeds) = - WeightTable::find_program_address(program_id, ncn.key, ncn_epoch); + WeightTable::find_program_address(program_id, ncn.key, epoch); weight_table_seeds.push(vec![weight_table_bump]); if weight_table_pubkey.ne(weight_table.key) { @@ -74,7 +68,7 @@ pub fn process_initialize_weight_table( "Initializing Weight Table {} for NCN: {} at epoch: {}", weight_table.key, ncn.key, - ncn_epoch + epoch ); create_account( payer, diff --git a/program/src/lib.rs b/program/src/lib.rs index e4cd271d..2a746f13 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -143,66 +143,44 @@ pub fn process_instruction( msg!("Instruction: SnapshotVaultOperatorDelegation"); process_snapshot_vault_operator_delegation(program_id, accounts, epoch) } - TipRouterInstruction::RouteBaseRewards { - first_slot_of_ncn_epoch, - } => { + TipRouterInstruction::RouteBaseRewards { epoch } => { msg!("Instruction: RouteBaseRewards"); - process_route_base_rewards(program_id, accounts, first_slot_of_ncn_epoch) + process_route_base_rewards(program_id, accounts, epoch) } TipRouterInstruction::RouteNcnRewards { ncn_fee_group, - first_slot_of_ncn_epoch, + epoch, } => { msg!("Instruction: RouteNcnRewards"); - process_route_ncn_rewards(program_id, accounts, ncn_fee_group, first_slot_of_ncn_epoch) + process_route_ncn_rewards(program_id, accounts, ncn_fee_group, epoch) } TipRouterInstruction::DistributeBaseRewards { base_fee_group, - first_slot_of_ncn_epoch, + epoch, } => { msg!("Instruction: DistributeBaseRewards"); - process_distribute_base_rewards( - program_id, - accounts, - base_fee_group, - first_slot_of_ncn_epoch, - ) + process_distribute_base_rewards(program_id, accounts, base_fee_group, epoch) } TipRouterInstruction::DistributeBaseNcnRewardRoute { ncn_fee_group, - first_slot_of_ncn_epoch, + epoch, } => { msg!("Instruction: DistributeBaseNcnRewardRoute"); - process_distribute_base_ncn_reward_route( - program_id, - accounts, - ncn_fee_group, - first_slot_of_ncn_epoch, - ) + process_distribute_base_ncn_reward_route(program_id, accounts, ncn_fee_group, epoch) } TipRouterInstruction::DistributeNcnOperatorRewards { ncn_fee_group, - first_slot_of_ncn_epoch, + epoch, } => { msg!("Instruction: DistributeNcnOperatorRewards"); - process_distribute_ncn_operator_rewards( - program_id, - accounts, - ncn_fee_group, - first_slot_of_ncn_epoch, - ) + process_distribute_ncn_operator_rewards(program_id, accounts, ncn_fee_group, epoch) } TipRouterInstruction::DistributeNcnVaultRewards { ncn_fee_group, - first_slot_of_ncn_epoch, + epoch, } => { msg!("Instruction: DistributeNcnVaultRewards"); - process_distribute_ncn_vault_rewards( - program_id, - accounts, - ncn_fee_group, - first_slot_of_ncn_epoch, - ) + process_distribute_ncn_vault_rewards(program_id, accounts, ncn_fee_group, epoch) } // ------------------------------------------ // Update diff --git a/program/src/register_mint.rs b/program/src/register_mint.rs index 9e0c7e35..28af9ff3 100644 --- a/program/src/register_mint.rs +++ b/program/src/register_mint.rs @@ -39,16 +39,14 @@ pub fn process_register_mint(program_id: &Pubkey, accounts: &[AccountInfo]) -> P Config::try_from_slice_unchecked(&restaking_config_data)?.epoch_length() }; - let slot = Clock::get()?.slot; - - let ncn_epoch = slot - .checked_div(epoch_length) - .ok_or(TipRouterError::DenominatorIsZero)?; + let clock = Clock::get()?; + let slot = clock.slot; + let epoch = clock.epoch; // Once tracked_mints.mint_count() == ncn.vault_count, the weight table can be initialized // Once the weight table is initialized, you can't add any more mints if weight_table.owner.eq(&system_program::ID) { - let expected_pubkey = WeightTable::find_program_address(program_id, ncn.key, ncn_epoch).0; + let expected_pubkey = WeightTable::find_program_address(program_id, ncn.key, epoch).0; if weight_table.key.ne(&expected_pubkey) { msg!("Weight table incorrect PDA"); return Err(ProgramError::InvalidAccountData); @@ -57,7 +55,7 @@ pub fn process_register_mint(program_id: &Pubkey, accounts: &[AccountInfo]) -> P } if weight_table.owner.eq(program_id) { - WeightTable::load(program_id, weight_table, ncn, ncn_epoch, false)?; + WeightTable::load(program_id, weight_table, ncn, epoch, false)?; return Err(TipRouterError::TrackedMintsLocked.into()); } diff --git a/program/src/route_base_rewards.rs b/program/src/route_base_rewards.rs index 1be3b1a3..ac29ecef 100644 --- a/program/src/route_base_rewards.rs +++ b/program/src/route_base_rewards.rs @@ -2,18 +2,17 @@ use jito_bytemuck::AccountDeserialize; use jito_restaking_core::{config::Config, ncn::Ncn}; use jito_tip_router_core::{ ballot_box::BallotBox, base_reward_router::BaseRewardRouter, epoch_snapshot::EpochSnapshot, - loaders::load_ncn_epoch, }; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; /// Can be backfilled for previous epochs pub fn process_route_base_rewards( program_id: &Pubkey, accounts: &[AccountInfo], - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, ncn, epoch_snapshot, ballot_box, base_reward_router, restaking_program] = accounts @@ -29,12 +28,9 @@ pub fn process_route_base_rewards( Config::load(restaking_program.key, restaking_config, false)?; Ncn::load(restaking_program.key, ncn, false)?; - let current_slot = Clock::get()?.slot; - let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; - - EpochSnapshot::load(program_id, ncn.key, ncn_epoch, epoch_snapshot, false)?; - BaseRewardRouter::load(program_id, ncn.key, ncn_epoch, base_reward_router, true)?; - BallotBox::load(program_id, ncn.key, ncn_epoch, ballot_box, false)?; + EpochSnapshot::load(program_id, ncn.key, epoch, epoch_snapshot, false)?; + BaseRewardRouter::load(program_id, ncn.key, epoch, base_reward_router, true)?; + BallotBox::load(program_id, ncn.key, epoch, ballot_box, false)?; let epoch_snapshot_data = epoch_snapshot.try_borrow_data()?; let epoch_snapshot_account = EpochSnapshot::try_from_slice_unchecked(&epoch_snapshot_data)?; diff --git a/program/src/route_ncn_rewards.rs b/program/src/route_ncn_rewards.rs index 28525854..4f73656e 100644 --- a/program/src/route_ncn_rewards.rs +++ b/program/src/route_ncn_rewards.rs @@ -1,12 +1,12 @@ use jito_bytemuck::AccountDeserialize; use jito_restaking_core::{config::Config, ncn::Ncn, operator::Operator}; use jito_tip_router_core::{ - epoch_snapshot::OperatorSnapshot, loaders::load_ncn_epoch, ncn_fee_group::NcnFeeGroup, + epoch_snapshot::OperatorSnapshot, ncn_fee_group::NcnFeeGroup, ncn_reward_router::NcnRewardRouter, }; use solana_program::{ - account_info::AccountInfo, clock::Clock, entrypoint::ProgramResult, msg, - program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; /// Can be backfilled for previous epochs @@ -14,7 +14,7 @@ pub fn process_route_ncn_rewards( program_id: &Pubkey, accounts: &[AccountInfo], ncn_fee_group: u8, - first_slot_of_ncn_epoch: Option, + epoch: u64, ) -> ProgramResult { let [restaking_config, ncn, operator, operator_snapshot, ncn_reward_router, restaking_program] = accounts @@ -31,15 +31,13 @@ pub fn process_route_ncn_rewards( Ncn::load(restaking_program.key, ncn, false)?; Operator::load(restaking_program.key, operator, false)?; - let current_slot = Clock::get()?.slot; - let (ncn_epoch, _) = load_ncn_epoch(restaking_config, current_slot, first_slot_of_ncn_epoch)?; let ncn_fee_group = NcnFeeGroup::try_from(ncn_fee_group)?; OperatorSnapshot::load( program_id, operator.key, ncn.key, - ncn_epoch, + epoch, operator_snapshot, false, )?; @@ -48,7 +46,7 @@ pub fn process_route_ncn_rewards( ncn_fee_group, operator.key, ncn.key, - ncn_epoch, + epoch, ncn_reward_router, true, )?; diff --git a/program/src/set_tracked_mint_ncn_fee_group.rs b/program/src/set_tracked_mint_ncn_fee_group.rs index 0fdf3f2d..b40fc148 100644 --- a/program/src/set_tracked_mint_ncn_fee_group.rs +++ b/program/src/set_tracked_mint_ncn_fee_group.rs @@ -44,21 +44,12 @@ pub fn process_set_tracked_mint_ncn_fee_group( } } - let epoch_length = { - let restaking_config_data = restaking_config.data.borrow(); - Config::try_from_slice_unchecked(&restaking_config_data)?.epoch_length() - }; - - let slot = Clock::get()?.slot; - - let ncn_epoch = slot - .checked_div(epoch_length) - .ok_or(TipRouterError::DenominatorIsZero)?; + let epoch = Clock::get()?.epoch; // Once tracked_mints.mint_count() == ncn.vault_count, the weight table can be initialized // Once the weight table is initialized, you can't add any more mints if weight_table.owner.eq(&system_program::ID) { - let expected_pubkey = WeightTable::find_program_address(program_id, ncn.key, ncn_epoch).0; + let expected_pubkey = WeightTable::find_program_address(program_id, ncn.key, epoch).0; if weight_table.key.ne(&expected_pubkey) { msg!("Weight table incorrect PDA"); return Err(ProgramError::InvalidAccountData); @@ -67,7 +58,7 @@ pub fn process_set_tracked_mint_ncn_fee_group( } if weight_table.owner.eq(program_id) { - WeightTable::load(program_id, weight_table, ncn, ncn_epoch, false)?; + WeightTable::load(program_id, weight_table, ncn, epoch, false)?; return Err(TipRouterError::TrackedMintsLocked.into()); } From e1c9a1804a4dcf0e7cd5dfd6f2b86445f9f2977a Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Fri, 13 Dec 2024 15:01:53 -0500 Subject: [PATCH 03/10] prints --- core/src/ballot_box.rs | 1 - core/src/base_reward_router.rs | 1 - core/src/epoch_snapshot.rs | 1 - core/src/ncn_reward_router.rs | 1 - integration_tests/tests/tip_router/distribute_rewards.rs | 1 - .../tip_router/snapshot_vault_operator_delegation.rs | 8 -------- 6 files changed, 13 deletions(-) diff --git a/core/src/ballot_box.rs b/core/src/ballot_box.rs index 08451950..25f04cdd 100644 --- a/core/src/ballot_box.rs +++ b/core/src/ballot_box.rs @@ -595,7 +595,6 @@ mod tests { let ballot_box = BallotBox::new(Pubkey::default(), 0, 0, 0); assert_eq!(ballot_box.operator_votes.len(), MAX_OPERATORS); assert_eq!(ballot_box.ballot_tallies.len(), MAX_OPERATORS); - println!("expected_total: {}", expected_total); } #[test] diff --git a/core/src/base_reward_router.rs b/core/src/base_reward_router.rs index 27c8ab19..8d303cab 100644 --- a/core/src/base_reward_router.rs +++ b/core/src/base_reward_router.rs @@ -677,7 +677,6 @@ mod tests { + size_of::() * MAX_OPERATORS; // ncn_fee_group_reward_routes assert_eq!(size_of::(), expected_total); - println!("expected_total: {}", expected_total); } #[test] diff --git a/core/src/epoch_snapshot.rs b/core/src/epoch_snapshot.rs index 3dca72b4..c03ca692 100644 --- a/core/src/epoch_snapshot.rs +++ b/core/src/epoch_snapshot.rs @@ -591,6 +591,5 @@ mod tests { + size_of::() * MAX_VAULT_OPERATOR_DELEGATIONS; // vault_operator_stake_weight assert_eq!(size_of::(), expected_total); - println!("expected_total: {}", expected_total); } } diff --git a/core/src/ncn_reward_router.rs b/core/src/ncn_reward_router.rs index 63b668fa..573cc808 100644 --- a/core/src/ncn_reward_router.rs +++ b/core/src/ncn_reward_router.rs @@ -571,7 +571,6 @@ mod tests { + size_of::() * MAX_VAULT_OPERATOR_DELEGATIONS; // vault_reward_routes assert_eq!(size_of::(), expected_total); - println!("expected_total: {}", expected_total); } #[test] diff --git a/integration_tests/tests/tip_router/distribute_rewards.rs b/integration_tests/tests/tip_router/distribute_rewards.rs index a43b153b..e575df9a 100644 --- a/integration_tests/tests/tip_router/distribute_rewards.rs +++ b/integration_tests/tests/tip_router/distribute_rewards.rs @@ -225,7 +225,6 @@ mod tests { .get_ncn_reward_router(*group, operator, ncn, epoch) .await?; - println!("\nTotal Rewards: {}", ncn_router.total_rewards()); total_rewards += ncn_router.total_rewards(); } diff --git a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs index f995e7bd..2b3e4e92 100644 --- a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs +++ b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs @@ -27,14 +27,6 @@ mod tests { epoch, ) .0; - let raw_account = fixture.get_account(&weight_table_pda).await?; - println!("raw_account: {:?}", raw_account); - // let weight_table_account = tip_router_client - // .get_weight_table(test_ncn.ncn_root.ncn_pubkey, epoch) - // .await?; - - // println!("weight_table_account: {:?}", weight_table_account); - let ncn = test_ncn.ncn_root.ncn_pubkey; let vault_root = test_ncn.vaults[0].clone(); From ee2e516bed96e997c866e6c5441ee6c8e2907594 Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Fri, 13 Dec 2024 15:03:33 -0500 Subject: [PATCH 04/10] nit --- core/src/utils.rs | 56 +------------------ .../tests/fixtures/restaking_client.rs | 7 --- .../snapshot_vault_operator_delegation.rs | 6 -- 3 files changed, 1 insertion(+), 68 deletions(-) diff --git a/core/src/utils.rs b/core/src/utils.rs index 0962dcf7..caa39ea4 100644 --- a/core/src/utils.rs +++ b/core/src/utils.rs @@ -1,11 +1,4 @@ -use solana_program::{ - account_info::AccountInfo, - entrypoint::ProgramResult, - program::{invoke, invoke_signed}, - program_error::ProgramError, - rent::Rent, - system_instruction, -}; +use solana_program::program_error::ProgramError; use crate::constants::MAX_REALLOC_BYTES; @@ -17,50 +10,3 @@ pub fn get_new_size(current_size: usize, target_size: usize) -> Result( - account: &'a AccountInfo<'info>, - payer: &'a AccountInfo<'info>, - system_program: &'a AccountInfo<'info>, - rent: &Rent, - target_size: u64, - seeds: &[Vec], -) -> ProgramResult { - let current_size = account.data_len(); - - // If account is already over target size, don't try to shrink - if current_size >= target_size as usize { - return Ok(()); - } - - // Calculate new size, capped at target_size - let new_size = current_size - .checked_add(MAX_REALLOC_BYTES as usize) - .ok_or(ProgramError::ArithmeticOverflow)? - .min(target_size as usize); - - // Calculate required lamports for new size - let new_minimum_balance = rent.minimum_balance(new_size); - let lamports_diff = new_minimum_balance.saturating_sub(account.lamports()); - - // Transfer lamports if needed - if lamports_diff > 0 { - invoke( - &system_instruction::transfer(payer.key, account.key, lamports_diff), - &[payer.clone(), account.clone(), system_program.clone()], - )?; - } - - // Reallocate space - invoke_signed( - &system_instruction::allocate(account.key, new_size as u64), - &[account.clone(), system_program.clone()], - &[seeds - .iter() - .map(|seed| seed.as_slice()) - .collect::>() - .as_slice()], - )?; - - Ok(()) -} diff --git a/integration_tests/tests/fixtures/restaking_client.rs b/integration_tests/tests/fixtures/restaking_client.rs index fa50a83e..4b1d7a04 100644 --- a/integration_tests/tests/fixtures/restaking_client.rs +++ b/integration_tests/tests/fixtures/restaking_client.rs @@ -89,13 +89,6 @@ 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, diff --git a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs index 2b3e4e92..ae7b1f28 100644 --- a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs +++ b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs @@ -21,12 +21,6 @@ mod tests { .do_full_initialize_weight_table(test_ncn.ncn_root.ncn_pubkey, epoch) .await?; - let weight_table_pda = WeightTable::find_program_address( - &jito_tip_router_program::id(), - &test_ncn.ncn_root.ncn_pubkey, - epoch, - ) - .0; let ncn = test_ncn.ncn_root.ncn_pubkey; let vault_root = test_ncn.vaults[0].clone(); From b6887d0f848a5e6659b3b21c90d830f7b7005a1c Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Fri, 13 Dec 2024 15:19:27 -0500 Subject: [PATCH 05/10] done --- .../tests/tip_router/snapshot_vault_operator_delegation.rs | 2 -- program/src/realloc_ballot_box.rs | 6 +++++- program/src/realloc_base_reward_router.rs | 4 +++- program/src/realloc_operator_snapshot.rs | 4 +++- program/src/realloc_weight_table.rs | 4 +++- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs index ae7b1f28..0f23d56f 100644 --- a/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs +++ b/integration_tests/tests/tip_router/snapshot_vault_operator_delegation.rs @@ -1,8 +1,6 @@ #[cfg(test)] mod tests { - use jito_tip_router_core::weight_table::WeightTable; - use crate::fixtures::{test_builder::TestBuilder, TestResult}; #[tokio::test] diff --git a/program/src/realloc_ballot_box.rs b/program/src/realloc_ballot_box.rs index e201ca39..3277df50 100644 --- a/program/src/realloc_ballot_box.rs +++ b/program/src/realloc_ballot_box.rs @@ -1,3 +1,5 @@ +use std::borrow::Borrow; + use jito_bytemuck::{AccountDeserialize, Discriminator}; use jito_jsm_core::{ loader::{load_signer, load_system_program}, @@ -42,7 +44,9 @@ pub fn process_realloc_ballot_box( realloc(ballot_box, new_size, payer, &Rent::get()?)?; } - if ballot_box.data_len() >= BallotBox::SIZE { + if ballot_box.data_len() >= BallotBox::SIZE + && ballot_box.try_borrow_data()?[0] != BallotBox::DISCRIMINATOR + { let mut ballot_box_data = ballot_box.try_borrow_mut_data()?; ballot_box_data[0] = BallotBox::DISCRIMINATOR; let ballot_box_account = BallotBox::try_from_slice_unchecked_mut(&mut ballot_box_data)?; diff --git a/program/src/realloc_base_reward_router.rs b/program/src/realloc_base_reward_router.rs index c469d2ad..a9f7e050 100644 --- a/program/src/realloc_base_reward_router.rs +++ b/program/src/realloc_base_reward_router.rs @@ -42,7 +42,9 @@ pub fn process_realloc_base_reward_router( realloc(base_reward_router, new_size, payer, &Rent::get()?)?; } - if base_reward_router.data_len() >= BaseRewardRouter::SIZE { + if base_reward_router.data_len() >= BaseRewardRouter::SIZE + && base_reward_router.try_borrow_data()?[0] != BaseRewardRouter::DISCRIMINATOR + { let mut base_reward_router_data = base_reward_router.try_borrow_mut_data()?; base_reward_router_data[0] = BaseRewardRouter::DISCRIMINATOR; let base_reward_router_account = diff --git a/program/src/realloc_operator_snapshot.rs b/program/src/realloc_operator_snapshot.rs index c47a4473..0a729901 100644 --- a/program/src/realloc_operator_snapshot.rs +++ b/program/src/realloc_operator_snapshot.rs @@ -68,7 +68,9 @@ pub fn process_realloc_operator_snapshot( realloc(operator_snapshot, new_size, payer, &Rent::get()?)?; } - if operator_snapshot.data_len() >= OperatorSnapshot::SIZE { + if operator_snapshot.data_len() >= OperatorSnapshot::SIZE + && operator_snapshot.try_borrow_data()?[0] != OperatorSnapshot::DISCRIMINATOR + { let current_slot = Clock::get()?.slot; let (_, ncn_epoch_length) = load_ncn_epoch(restaking_config, current_slot, None)?; diff --git a/program/src/realloc_weight_table.rs b/program/src/realloc_weight_table.rs index 7b36c5e5..21e80224 100644 --- a/program/src/realloc_weight_table.rs +++ b/program/src/realloc_weight_table.rs @@ -46,7 +46,9 @@ pub fn process_realloc_weight_table( msg!("Data is empty? {}", weight_table.data_is_empty()); } - if weight_table.data_len() >= WeightTable::SIZE { + if weight_table.data_len() >= WeightTable::SIZE + && weight_table.try_borrow_data()?[0] != WeightTable::DISCRIMINATOR + { msg!("actually Initializing weight table"); let unique_mints = { let tracked_mints_data = tracked_mints.data.borrow(); From ee322382a580d5799d15a3af6bd99616f250cd6c Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Fri, 13 Dec 2024 15:28:21 -0500 Subject: [PATCH 06/10] asdfasdfasf --- program/src/realloc_ballot_box.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/program/src/realloc_ballot_box.rs b/program/src/realloc_ballot_box.rs index 3277df50..067edfcb 100644 --- a/program/src/realloc_ballot_box.rs +++ b/program/src/realloc_ballot_box.rs @@ -1,5 +1,3 @@ -use std::borrow::Borrow; - use jito_bytemuck::{AccountDeserialize, Discriminator}; use jito_jsm_core::{ loader::{load_signer, load_system_program}, From b3ce9e95d0e2ef28705c50cbf24bef7df5a80240 Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Mon, 16 Dec 2024 20:11:19 -0500 Subject: [PATCH 07/10] BPF constraints fixed --- .github/workflows/ci.yaml | 4 +- core/src/ballot_box.rs | 13 ++++- core/src/base_reward_router.rs | 17 ++++++ core/src/epoch_snapshot.rs | 54 ++++++++++++++++-- core/src/tracked_mints.rs | 8 +++ core/src/weight_table.rs | 41 +++++++++---- .../jito_tip_router_program-keypair.json | 1 + .../tests/fixtures/jito_tip_router_program.so | Bin 360184 -> 395744 bytes .../tests/fixtures/test_builder.rs | 18 ------ .../tests/fixtures/tip_router_client.rs | 7 ++- .../tests/tip_router/bpf/set_merkle_root.rs | 2 +- program/src/cast_vote.rs | 6 -- program/src/initialize_ncn_reward_router.rs | 2 - program/src/initialize_tracked_mints.rs | 2 +- program/src/realloc_ballot_box.rs | 7 ++- program/src/realloc_base_reward_router.rs | 15 +++-- program/src/realloc_operator_snapshot.rs | 42 ++++++-------- program/src/realloc_weight_table.rs | 22 +++---- program/src/route_ncn_rewards.rs | 8 ++- 19 files changed, 173 insertions(+), 96 deletions(-) create mode 100644 integration_tests/tests/fixtures/jito_tip_router_program-keypair.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ed4c49bc..bf36ede0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -154,9 +154,7 @@ jobs: name: jito_tip_router_program.so path: integration_tests/tests/fixtures/ - uses: taiki-e/install-action@nextest - # Test the non-BPF tests and the BPF tests separately - - run: cargo nextest run --all-features -E 'not test(bpf)' - - run: cargo nextest run --all-features -E 'test(bpf)' + - run: cargo nextest run --all-features env: SBF_OUT_DIR: ${{ github.workspace }}/integration_tests/tests/fixtures diff --git a/core/src/ballot_box.rs b/core/src/ballot_box.rs index 25f04cdd..518e28c9 100644 --- a/core/src/ballot_box.rs +++ b/core/src/ballot_box.rs @@ -242,7 +242,18 @@ impl BallotBox { } pub fn initialize(&mut self, ncn: Pubkey, epoch: u64, bump: u8, current_slot: u64) { - *self = Self::new(ncn, epoch, bump, current_slot); + // Avoids overflowing stack + self.ncn = ncn; + self.epoch = PodU64::from(epoch); + self.bump = bump; + self.slot_created = PodU64::from(current_slot); + self.slot_consensus_reached = PodU64::from(DEFAULT_CONSENSUS_REACHED_SLOT); + self.operators_voted = PodU64::from(0); + self.unique_ballots = PodU64::from(0); + self.winning_ballot = Ballot::default(); + self.operator_votes = [OperatorVote::default(); MAX_OPERATORS]; + self.ballot_tallies = [BallotTally::default(); MAX_OPERATORS]; + self.reserved = [0; 128]; } pub fn seeds(ncn: &Pubkey, epoch: u64) -> Vec> { diff --git a/core/src/base_reward_router.rs b/core/src/base_reward_router.rs index 8d303cab..7031f071 100644 --- a/core/src/base_reward_router.rs +++ b/core/src/base_reward_router.rs @@ -64,6 +64,23 @@ impl BaseRewardRouter { } } + pub fn initialize(&mut self, ncn: Pubkey, ncn_epoch: u64, bump: u8, current_slot: u64) { + // Initializes field by field to avoid overflowing stack + self.ncn = ncn; + self.ncn_epoch = PodU64::from(ncn_epoch); + self.bump = bump; + self.slot_created = PodU64::from(current_slot); + self.total_rewards = PodU64::from(0); + self.reward_pool = PodU64::from(0); + self.rewards_processed = PodU64::from(0); + self.reserved = [0; 128]; + self.base_fee_group_rewards = + [BaseRewardRouterRewards::default(); NcnFeeGroup::FEE_GROUP_COUNT]; + self.ncn_fee_group_rewards = + [BaseRewardRouterRewards::default(); NcnFeeGroup::FEE_GROUP_COUNT]; + self.ncn_fee_group_reward_routes = [NcnRewardRoute::default(); MAX_OPERATORS]; + } + pub fn seeds(ncn: &Pubkey, ncn_epoch: u64) -> Vec> { Vec::from_iter( [ diff --git a/core/src/epoch_snapshot.rs b/core/src/epoch_snapshot.rs index c03ca692..cfe56b99 100644 --- a/core/src/epoch_snapshot.rs +++ b/core/src/epoch_snapshot.rs @@ -226,7 +226,6 @@ impl Discriminator for OperatorSnapshot { } impl OperatorSnapshot { - pub const MAX_VAULT_OPERATOR_STAKE_WEIGHT: usize = 64; pub const SIZE: usize = 8 + size_of::(); #[allow(clippy::too_many_arguments)] @@ -242,7 +241,7 @@ impl OperatorSnapshot { operator_fee_bps: u16, vault_operator_delegation_count: u64, ) -> Result { - if vault_operator_delegation_count > Self::MAX_VAULT_OPERATOR_STAKE_WEIGHT as u64 { + if vault_operator_delegation_count > MAX_VAULT_OPERATOR_DELEGATIONS as u64 { return Err(TipRouterError::TooManyVaultOperatorDelegations); } @@ -319,6 +318,53 @@ impl OperatorSnapshot { Ok(snapshot) } + #[allow(clippy::too_many_arguments)] + pub fn initialize( + &mut self, + 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<(), TipRouterError> { + if vault_operator_delegation_count > MAX_VAULT_OPERATOR_DELEGATIONS as u64 { + return Err(TipRouterError::TooManyVaultOperatorDelegations); + } + let slot_finalized = if !is_active { current_slot } else { 0 }; + let operator_fee_bps_val = if is_active { operator_fee_bps } else { 0 }; + let vault_operator_delegation_count_val = if is_active { + vault_operator_delegation_count + } else { + 0 + }; + + // Initializes field by field to avoid overflowing stack + self.operator = operator; + self.ncn = ncn; + self.ncn_epoch = PodU64::from(ncn_epoch); + self.bump = bump; + self.slot_created = PodU64::from(current_slot); + self.slot_finalized = PodU64::from(slot_finalized); + self.is_active = PodBool::from(is_active); + self.ncn_operator_index = PodU64::from(ncn_operator_index); + self.operator_index = PodU64::from(operator_index); + self.operator_fee_bps = PodU16::from(operator_fee_bps_val); + self.vault_operator_delegation_count = PodU64::from(vault_operator_delegation_count_val); + self.vault_operator_delegations_registered = PodU64::from(0); + self.valid_operator_vault_delegations = PodU64::from(0); + self.stake_weights = StakeWeights::default(); + self.reserved = [0; 256]; + self.vault_operator_stake_weight = + [VaultOperatorStakeWeight::default(); MAX_VAULT_OPERATOR_DELEGATIONS]; + + Ok(()) + } + pub fn seeds(operator: &Pubkey, ncn: &Pubkey, ncn_epoch: u64) -> Vec> { Vec::from_iter( [ @@ -427,9 +473,7 @@ impl OperatorSnapshot { ncn_fee_group: NcnFeeGroup, stake_weights: &StakeWeights, ) -> Result<(), TipRouterError> { - if self.vault_operator_delegations_registered() - > Self::MAX_VAULT_OPERATOR_STAKE_WEIGHT as u64 - { + if self.vault_operator_delegations_registered() > MAX_VAULT_OPERATOR_DELEGATIONS as u64 { return Err(TipRouterError::TooManyVaultOperatorDelegations); } diff --git a/core/src/tracked_mints.rs b/core/src/tracked_mints.rs index ca7571a3..8307a156 100644 --- a/core/src/tracked_mints.rs +++ b/core/src/tracked_mints.rs @@ -77,6 +77,14 @@ impl TrackedMints { } } + pub fn initialize(&mut self, ncn: Pubkey, bump: u8) { + // Initializes field by field to avoid overflowing stack + self.ncn = ncn; + self.bump = bump; + self.reserved = [0; 127]; + self.st_mint_list = [MintEntry::default(); MAX_VAULT_OPERATOR_DELEGATIONS]; + } + pub fn seeds(ncn: &Pubkey) -> Vec> { Vec::from_iter( [b"tracked_mints".to_vec(), ncn.to_bytes().to_vec()] diff --git a/core/src/weight_table.rs b/core/src/weight_table.rs index da32fd39..8e5a0c86 100644 --- a/core/src/weight_table.rs +++ b/core/src/weight_table.rs @@ -76,7 +76,26 @@ impl WeightTable { (pda, bump, seeds) } - pub fn initalize_weight_table( + pub fn initialize( + &mut self, + ncn: Pubkey, + ncn_epoch: u64, + slot_created: u64, + bump: u8, + config_supported_mints: &[Pubkey], + ) -> Result<(), TipRouterError> { + // Initializes field by field to avoid overflowing stack + self.ncn = ncn; + self.ncn_epoch = PodU64::from(ncn_epoch); + self.slot_created = PodU64::from(slot_created); + self.bump = bump; + self.reserved = [0; 128]; + self.table = [WeightEntry::default(); MAX_VAULT_OPERATOR_DELEGATIONS]; + self.set_supported_mints(config_supported_mints)?; + Ok(()) + } + + fn set_supported_mints( &mut self, config_supported_mints: &[Pubkey], ) -> Result<(), TipRouterError> { @@ -249,7 +268,7 @@ mod tests { assert_eq!(table.mint_count(), 0); let mints = get_test_pubkeys(2); - table.initalize_weight_table(&mints).unwrap(); + table.set_supported_mints(&mints).unwrap(); assert_eq!(table.mint_count(), 2); } @@ -259,7 +278,7 @@ mod tests { let mut table = WeightTable::new(ncn, 0, 0, 0); let many_mints = get_test_pubkeys(MAX_VAULT_OPERATOR_DELEGATIONS + 1); assert_eq!( - table.initalize_weight_table(&many_mints), + table.set_supported_mints(&many_mints), Err(TipRouterError::TooManyMintsForTable) ); } @@ -269,7 +288,7 @@ mod tests { let ncn = Pubkey::new_unique(); let mut table = WeightTable::new(ncn, 0, 0, 0); let max_mints = get_test_pubkeys(MAX_VAULT_OPERATOR_DELEGATIONS); - table.initalize_weight_table(&max_mints).unwrap(); + table.set_supported_mints(&max_mints).unwrap(); assert_eq!(table.mint_count(), MAX_VAULT_OPERATOR_DELEGATIONS); } @@ -278,11 +297,11 @@ mod tests { let ncn = Pubkey::new_unique(); let mut table = WeightTable::new(ncn, 0, 0, 0); let first_mints = get_test_pubkeys(2); - table.initalize_weight_table(&first_mints).unwrap(); + table.set_supported_mints(&first_mints).unwrap(); let second_mints = get_test_pubkeys(3); assert_eq!( - table.initalize_weight_table(&second_mints), + table.set_supported_mints(&second_mints), Err(TipRouterError::WeightTableAlreadyInitialized) ); } @@ -294,7 +313,7 @@ mod tests { let mints = get_test_pubkeys(2); let mint = mints[0]; - table.initalize_weight_table(&mints).unwrap(); + table.set_supported_mints(&mints).unwrap(); table.set_weight(&mint, 100, 1).unwrap(); assert_eq!(table.get_weight(&mint).unwrap(), 100); @@ -306,7 +325,7 @@ mod tests { let mut table = WeightTable::new(ncn, 0, 0, 0); let mints = get_test_pubkeys(2); - table.initalize_weight_table(&mints).unwrap(); + table.set_supported_mints(&mints).unwrap(); let invalid_mint = Pubkey::new_unique(); assert_eq!( @@ -322,7 +341,7 @@ mod tests { let mints = get_test_pubkeys(2); let mint = mints[0]; - table.initalize_weight_table(&mints).unwrap(); + table.set_supported_mints(&mints).unwrap(); table.set_weight(&mint, 100, 1).unwrap(); assert_eq!(table.get_weight(&mint).unwrap(), 100); @@ -339,7 +358,7 @@ mod tests { let mint1 = mints[0]; let mint2 = mints[1]; - table.initalize_weight_table(&mints).unwrap(); + table.set_supported_mints(&mints).unwrap(); table.set_weight(&mint1, 100, 1).unwrap(); table.set_weight(&mint2, 200, 1).unwrap(); @@ -355,7 +374,7 @@ mod tests { let mints = get_test_pubkeys(2); let mint = mints[0]; - table.initalize_weight_table(&mints).unwrap(); + table.set_supported_mints(&mints).unwrap(); table.set_weight(&mint, 100, 1).unwrap(); assert_eq!(table.get_weight(&mint).unwrap(), 100); diff --git a/integration_tests/tests/fixtures/jito_tip_router_program-keypair.json b/integration_tests/tests/fixtures/jito_tip_router_program-keypair.json new file mode 100644 index 00000000..44bd7c4b --- /dev/null +++ b/integration_tests/tests/fixtures/jito_tip_router_program-keypair.json @@ -0,0 +1 @@ +[165,11,30,77,191,210,41,183,26,163,99,7,95,182,36,159,153,122,235,90,176,179,26,20,120,197,213,118,186,223,78,241,132,57,56,184,252,97,61,211,46,207,84,250,171,50,205,240,68,77,96,245,177,133,183,26,32,242,50,128,251,144,168,67] \ No newline at end of file diff --git a/integration_tests/tests/fixtures/jito_tip_router_program.so b/integration_tests/tests/fixtures/jito_tip_router_program.so index c1c1d1cd63f610337d15b100083b679c5327b6e3..d64f75fbddb5feba116cc9e1918c76754659656d 100755 GIT binary patch literal 395744 zcmd?S3!GI~bw7TFrDE=$IKuWHL%SAV~-sg^)zVs*pGaqa#CzN)n9>qzRkj zV@Nc?Xm#>XG1iuQhsdB-v5#NySNor7t%~;7ijP)oZA7J(3l2hd8`KNRy)8+nf*AFv!sq^$=Z!F{gOI~tWsTZc6t@PKpLx?j_2>!{vG%BYnj z)8+Z$M;>`3Z9R~|8C}F@_?xzl)s#P`<&S3N4?HEuFKs=Ny7vO`w z>66JBz?rL0^v^(Qe0qoF;rW4xu4w~|MB@LA}}d_u6&g6v*#rF`I;%>=cXyv&jaJ|Q^aRd{5<_p!p|3-BtI{l zB7R;Yrgkdz^Y!EKbF$;-XW#q3WBh#ho~ao>XH2oZy?h*gPIml!$D@RwCp&)rH|nIQ&F>Vlw*qM~@PIzW5~d^UH%% z;}1=BJYn8A{G9CkbL*popC>#2{KF~Y=VxRBF_rU`qcV>=UVLb>^Uq5jCHy?u`RB{0 zh@Wew*xr6<9DYhja8ma6k@q}0#?Na{(%wF0iun1_?y1$!TgKt%WcBkCj}m^KtbTs~ z?Nc*;9zAnv`MIo$pG!m=lO$eWSu~D*&KS(sFA(2<%cF#!FF8s5d}vbq#OhS-`by6! zrzSr;1C0RS3OGjc(Qu=`YGZg^zua3$BKMBDD$S=|Ebl-@%-5$ zAJ2J|@bP5**(;`qkML)wf{$+>hmYg=Jw-kqdEkGC-}Bfh;v@W?so>*_#^Iwhb6o$m z$j7~p5)l;p1FP zF6_d7f-s*6--m~Gj>^8Xhq#R_c}S*=@M9M)#$Vx^O(A3sc}ggH_`u5>Z) zVGj4XrHgqFFfMqG!9p(V4-~gw;jJso$1Z$JlEnMqw1WIW)4}SGbTAkD2>%oGF}y%0 zP-0;R{z^MWx1Lj(9+{)rx^MA6WBN?9=ZoyQ^t*~K9c%!2qa^48R6v(cfi9Bv3njCD zN@BzHNpjO7(gpOpF5L2Rl-=6C<>l!j+Yc!D&wtG=?a1Hos#mqAhR1KRJbd6d@{l&O zU!WK0ls21uMEq}&e9)uF{~z6*^WWtLbe^`Oe*TNo`uUgF$#?!wmv(;uyq4L%&>>04 zg?!y-x@^-J(!lTNJ$alaUA8&Pf67bZ{B>FW!K(6WG(T?lI)R%mTc!Ds+sA2oc~<{P zRdAPP`NLIkyKCS=1k+`m8C7U6`DyD6!V&(VmuYLC&)*j11Fz0^*5ZZy=M2=(zf|<2*bkJy zO5z%xe=_>FsGxuEmi5h8|2`>cZ>P~c*f{3?O)xt!UHUKP=8<%z5JRq>Q>o`+3CF?w zIn!Z2%VE6HdW)IvImPPDw|d!gtJl0-3x?S;DU>UiKa;^RzafKTepvq8;B;tpeGfz1 zZ)Uou$=dI*_C+4}oN1@aZwwvg7#)7?eAui63$r)z{@=uhk+aFS!pLH^Z_6UV84N1?v@DQ=%O5bnrv>ZgVJkkFcO>Nxec z7V3+tPOAP*h5Gb|jGhy~e`BFO?d!zrzobwfF41KAe|4cgGgZuo{1u+R4=auR{GZCt3eL3iVqiS^ukr`k3-e2LFF5)Q8ER zO#Lqu>SJm#nfiZIsDI8R>wmmZzjc!J|GZHD36rewu;^vb_|N3s6Md^{1qbP1$P?iT-RnZ|dmd!}LZ&FA&NqX}sl6mf4k^*lVz z-a`i7{Spr>C7qhzVsX4u^6(?b2v5`OtiHz!g89?oX&CJ=ABWURJ8V9A^%78%^A`Of zf8bYy%b8N_2lyNL-tNe9=8f3Y>D&vM2L8A_{s=tKpM@5eb-a2Iu#{`$MS4r-3LIj{ z_7}b!qf;$j>Gdo>a-i*H^TbU1f&6D>A(@lh$Zj%hT(rtY1{2f`rw8xoI@3(}` zJx%Z~1iyB@B`p>gEhUh8$u~dO<34fy3#2~$N57Pw#vjZNlL0d`Kms5Izeu6g)4Y(DJ`@# z>Dp5wy?Rc$O!Hm7MxLxmk=u357dgk}dQ?itP-CM$0}4TZ2^|4;fC=Qcs5A-v*<3QR zna|F(erEC~XsX<8HhNj-duA{_Vsh&Iy^COy^_CBOtcvQx!oKHdi7LIYI>0OKVQo@e)WO1}Fl5dbByOds+GXXuSqL;_} zui*2M7xI3t%@Z?vNw(7Kg+?zHOM0B%S6cn^c)!cqUvK%4>neI(KVJ)$gk!i=?)6u} zzWm1E3vad-&&=K!y++ubWV+xjtV>4lM=ppX`rmRIso?UE8r^-IDcNeucyQ8o0VY>^ zA@4pu)QmH5qT~wK^X~QoaSm`2aV%WEUgq&ww;$I?KTxEngwa^~^K88(uapiUf8oUh z;P%$>fZcdePfrg@%dNC&tohKt&9cto z>GW2@NzWoa?_e_9Pj~Wkw((;UbT$-lJ9RudJAGd%^o42_>HA8d5Ay5L7aW`neJ`CL zeJ`I3eJ$SCatda5A}^+Q;49*9p+mEie$&JBGV*EUOy;AU_QUM$)@%2Y|0VhpE#imZ zc}Z8Bs22da@IvGwe}Uc+7v5i3C-|#3QJ+w9tLa^8`d&(wX+77st!FvBi>mVdd=`|$ zP#B*dGb3Jqs&h`vnDjqwr91<_>T_Q#j#AdQ(*G(63Al|T2lgY-5KrN}?#q)E*&|fNF`_F9G{H<34y6EwlupUzG zO8U8BHVt%o7G~>`e-WZK!aqBI27CZroL@Qrv{*Jz@cp#G{Qs6FgFm0;`h9%3kZFt$ zfSX>$bZqa1JddPTQEs=cHu*gDR4w0gDg`^o15hBB>_82}be_Y<3{?3n5@+Gx9HFj;8+20{$ zN3?%p)fjT%FKvqL`?57z{*ic}?6Rd<{;I0--I^b_+b(e9eGafF>9XZoF5d5OxC;K< z41Q-7{8_9Q?RQ{&IbpjG3UIl6zKi(B|B2vY{Sy`&e*VXze2gF8y*H1y1RLK&K_4*B zg+ya~#^v+gK=Dpen=fHLf)aoWI~vZHA995?dQ0%Vlt{%c}LPd_1xF zJ?u3~Hr{{JIfj~$pU!xldKu*bbk{gww7+FlwEmnf%lSQ{s(vlMxgOA%-`x>hw2SiK z_cNk=@FiY{2Ol7p&d;{_j2oOgAH5QJ>gN4oxq)&%FBzd4Ch%ABbFRRFUyssq8(FxK zbO&Ah!cUKb)y#uE&SG*mV)AtEO4iQi!Qz%cZWdmRzk(0l6xkoc^a$NK#S{IyM)30e zHAv$E9t$_pAuZyUZqy>WZxVbT@w8MzeeZWTpY|jyVEJ^b-LGao_CtbRE_#q!Yc)IZeURYa-|ZAUz!14cI_+-bM$f5FR*xa9b@^FG{aHI> z3?|PtRC5`wJB2~Zxe20Aykba3?J)r%E-pBc2i0$}3ZRcw#d54yFJq#5q z=lh#nADavMXm+EN{2uGy!{_MVZ{7JJENv&Bz2(hs>0R`7doBpeh39X3(YJNB_{{~s zxUzTAzv*+==Wsu%igNq^{;Z#%&I>nuW^M11uV}dzJL9*MFdfR2JKvWXIP4^0i1ORE z5?-9pO$Jd&T#L;wLAS#%t)?qN(@s~#XI_$Qv^W^bN0-p~)$G@qMwcP#Pw&bfSU&q3 zh6Xgb1A^cInT9 z)$O%35y4z8M+qmBW70SE_Y2IQ$@=N#?kkj=PcD{{iwLh`boBD`t^8KKbcJgw}(Z4e8SC`x9>rrM`Fb;szDcK`-wN>#! zd)p$v7x;a0C+$k{T;Yzp#U5VFR>OfMY#cWu*j+B%^3u-5&Y&6*zB z#`@S-)-G+w{2r8mjnfU|1TNt7=`DiK>GGdh_&X5Fg9@+h9`p+G^m@IAt$lhg!VML} zYuP5^VfNbnX|!|B8jKr7xH6c?<^C7^$v#PkEo-#lVml+8_GIhFfP2Bsg1gw;y7=4s zC#B>SXr`*2F{I0yO+NtixtD3T6M+A|{v7XOJ8c>KYqg@wC*+}9a?(cQV6oi?*>2cJ zIyxR<$mn@Jp97ySN~pJpM@H8SpH(&Z3>NS~foxoEfY0&pc+ayZLKg~j*g95&&uG-X zk3XsHe@<_-dEhvB`Fdv9Lpp&z9jk>tLjNjxsF>Vk{x`?>aI30q;{F`yig6vt$}RHO zFA|81d*X+W-(h1rvXgKfTPw;F@ag;!)`51ZUIfDf<7(RWTfLwUF1 zO}PI4koU*qzAk?b-{W3k$m;L0`b&f$a-}wIr(}b)_iD`#b2@oHPYjq`y-WUGc@gGx zTR!XeF8No@cRQBt!=6W~_BQ{MKKJ#;Qo?ll0QD;b!U^~A-k9&4f93Yw>fP5)eC}<` z_bYxp8NRDamV~)|gll;D{31Qa+6~LAp6CA9QO$=QL4LFH6TtIk_#1mnKFvNC?MrUw z=;x<*_o-Z@tEqQk+iv2uV?28PgOfR>5>9CTH^QCseZ9BM zr@(Dw?(L;c*GP_}%i^CH zy*KclmACnSDWN|cnomb@nYXSZzO(80OMHdtM(u4oq;wAB%E2xzHyJtDC2|dW`UA*; zeGkIt=QZ|pk)khlh4Vnno8Krc7VR_a$Drf`?yKG{_On61;(Ct9V|PgTy}N0j>^l`L zOG=o9aU zP`}gto3i;ccMvY{8xVh{cK-4-Eg*krlMD4?zfR&(njbt) zB<)wliB?eFecS?mRdJ#f-%+}|TxM}1fm#(OI+go6bH9Y?wCgbWnZ@Z3F}<$V&xhXl zydzz9gzXAGxWK0^H%b4d%YSC!ugq7EYPq(4P!IFf+w>lWSl{QBvHZvF#PhxNuM$4h zz%&0ef;T8FOa$J))Llm~e6#t;2F2IyH~M+t`2t_&<+w&nZmyB;p@%trSugBiKf|g% z6xV%d-EuGD7b@h#h}qwGety2RSM>WbI%M<8-%yy4u z&-;_!O}WH=|1l|_Zs&T_xc#|L!OQ+O(i#1ku~z6RNYs_r9pEP9wL|Yk_@To70nV7j z&co0#>Wz(0p<;4$yTjplHL_H}$$P2jz0@oR9$mxU7i({|qTgUO@`JXhoHaj-^An`Q#J=1haL)Tvv zKl5A99n@kX--aigA-Vj$X!!l0Lu%zA2P~<@21?08+Ai{!7yN{uze&nUerF;7r;;C! z@28mWYQ zsU7FJ?g2b;g*TE~_**^Be?#e(k3&rNFQT8;(j9Rq69*@skZWO+VU61+b`=@*Fd6704hFGuOxF5rh;r0p24kwR$bG!?~gcsi2 zr|=P9@0MRRcyH0iiQB`GH!>ZMvrC^RxCV3jB9(3QZCA-|K_I@$IZ5EoO`PRJl^<*b^!m2|Aq0*;M%_Xwrt$Z^)U7y zoX+bR_DOCNy9kYfJ=rYxBHz)@!qu{$*xIu02;AIFO0Q@@f=`Jwid=t}+DSU-}lQ7IbA-`D)K^>6vS z%=v)NS3{ZW!Tuf5wDmn!?rTEq*XzP`Ml&*yws@A-RtOos-1i1qE$;_5cmw|RshW>9SUzEdUYYzH5%&G6BQ2V8tav>#~1agh&S+k!Akg#^e2719ABTLqFmap z{?b2<{XiDi*&=>!JdNXKDLG5&mfp$&u7|lF*sRaPB=iiVNHaYm1HYv0LT)w}{JlSyF&z2p3i$^lfAW6dV--;!*H1jobGO0)UT3iD_$zkn zE=`4o{@)noZU^PM*8eNSc^dkElZ^9RQ|q5-{N;8cUoXyc;SECf8b9x-;-C9x*R#J- zT&7QQVtW#|6Z?DfZp64&gm*3B?W0~!H~*>;Jogg-S6HFr$!-Z1fiG45ozK&=c#k-N za>etz?c!>V(EMcQ!PWX#te3@mp53Q#u>ZCj@XDIhaUK26;w)R4AN!%-e6HYF}CI2KaSeCJ#UvbIQ8QweFz2)QSE}nnbwVQgF#SeBd{bG(MfEULlQhRR| z`SJRN&r6UW$3uL+f&4gL^=O5n^uk$_b zyK}yT{Hxdf-OGKQ59Oy{^{!Kp|EnLn z-2UbBS{i(>UoQ@#<6;HXIj==~aXZhH@wXPPG%i;RcMxf%kie0CjtBQ+uc`;Pw+8O$ zIB*XNzHxj=n%42)!!M}^e=XtPqc<)0RvG`Y_=M%xj<>8Aw)L^T=pwGh<1OX2-ulW< zS5IWTmEek8ke90Qwz!_ra2s`k$rgZOV>-*~5VOs*3ik4;~)bpVskQex{H!!{l_DChH9**B>6 z9CThS@{oc{V4Xcbj{Urpg$-CIz9#Sqk?{i@~D=}^;3)`;91-n@JEp@BbVb0 z;m^`~fICCr*1}n#G#y?p*cyC?RXa0+zrcEXUgbHAdc{rKb(z(1_@($?jAm-TK-FSYbR zOLtqE{773nHQir6!u-lHmh*jql`%`NvFArEy-M%sk6$2dn8y_6^^d%PzmeZEjRnDH z8P*^1eg3FVO=EmE3{##KUhp`r4E>XTS^Nupd-D#;b$z>Q{XRHDvJpB3iK=zqH1+JCba&-NLGN~hx0*35Co;j}dI+!N?ODXH}aO34x0C)+0! zD&1PYx7o&N+fN!QOD+FLS{`t51=|lDhA3yf^BEV<*N1E!Vf75HAJYf=0{R?FBfhi_ zYPnoqcUk(d-eupLr75p1O<0XZedg!uDXiz?o$psKC4a^KdimC!2G7d-dp5Q&I&Iyi z&x?4w{Q&;&5}W1t%Ki<)8zO#)x5a*Y?@BRRa-|O(`vvZhmCGS?M(R(+_Ld>9>O2DTHo zr+7&nT&O?onAls3L&?03{j>E0na^oDu0JgJPUQWZHLynyvYk*dx(%@(m@mO{q5Uhj z(eA|j4V5)2U-|puA5uUu9xnIi%f99~zaF2$P$`W?$bU!h#P@-5fbmmW_J zNFIIfsN3+nvmghQE7C8PmmN~BMz7~XaS#}oNIjWcu6)-Y2rYrc35hJA=0-;&IQ2b=fs75q!Z36(fr7cQch@JTrPmm zKFC8jCW!fV-A4K&(lxe5s`%0Rw z2d`h@ov!$W@&VcpvnTQ%xTPH)@jD*8odU16Uu?gv`NM`6@y-1h!q5E}mM_gXmYHuDT{>De_^Ni!3ZHuOAeezA(4*FN^hu?1P%RnO++K=P0y^B0< zAcAbq<2Y{bqQd&*eUu9LRdF1+cV(mX$+@Icq(>^(u7{BwK>N_AJueqL%`ZWoTmwE& z$oHFHrw@D_LcFzGa*A@~_TJxHb^C#K244Zcaa8fk;a)?yB%AI(>Z=1c?V_E5J_^Yd z?`P_Mwj#Xe)xaC83vY(v0leDk!ns`Gz+V`wQ{Ts(7@wbu%UUb%hF`WWdWpgfW(UF^ z%5Ra5p+Y_azZQ}Clb+x1J)-@UxSZb8)^(O1v-BEEAGP!&>V*|bhY8PoAMJ%hWA>#XYdQA<}D-^KIELstDxs zPqPbag)nmE^H1)h%I2R}(H`XU&u=Ln`TUdV^ji8`PIs^G?@^&&N29oyoVTEGP)<}# z{5|e-@%OM|e!`xlt%F(~aH`+?qWr|=+qFF9FMWXYLcQTd+Ex7{>q{ng2YEkZl=4z$ z8O*;h&d2LV@FRSE1M>6>tUKF$$oFj_Uy4=DJAC|{F8)RqANiHf!$(YBc3VGp>OFhU ze4_Jkv*YO3`!)lA(sTTIxV)qzSGwK&u6d-ZpMTbH9zI=ygmT6FEY1_YyiMlen?F!f zj(#5fs8DAf{vMh0Hk#jsebiSUPtf15!Cpl2K!1FO(h<*(;Q6qkq3?_8xVM98w;!=T zcZSf?=R=vjaX)PV>&-md>PPLW9BySk+MTKI!85M(tDBdb9~_O}Vv2Meyju1bL!W3F z6fb#Skp5miFaMxEi0S8guvpfS;(YRr_SpFrhUR8;o&ml^bXIyeog?}D&^Y{hKb|-6 zzvJ<>+xq!M@#$hb(Qf8fNBsC_f%|Klmq-4*^P@xf;rkg&3H`*z^YTIEOSnsKRsB;v zgFD{I_%H5|x0^)Yd_R0BlP`_u<9wdY$G5N@pLh9rh=6;qz>W2B4eVyW;dMLjD7Uqs zXEFang?fu}Hy8NduKddN^~U4z-|&p}_5K3?q5t*drwyZD)${Km%@6%E_*~krnt!h_Kf7N9U9NHW2j9X`73bYn z&A(q*fBqdV>{Gb0+zrD2?OMwCkE~VVO0Vr^`f5wBUaskQ-Sq8O3VzZJVxZ*)d|fAB zM;_1zG9S?T_2%VhKl6Wt9=M?A4{n5=9HboV5JJjTN@VboD-0VvX}?d>#r`-vj|aFt z0^Iwq7o24pSQqbXALtEeM&^&!j^H&|ryZFGX}`h{=@rxG@R|t!4DQPaw_qy+!%abKt#1P|E0$!Fyp1yaB0Dq=)PmP{awm$?`Ls6QOw&Q+CKELGt=U5&`rA84S_eY$+{vILle1FeG`rBdsouzn$GU*!5KBW1<^;`Ha zZimjj=U-ytAtW1M>0bB1%2_ z6>IkIzQX%jE36#w1wGH4*La_euiuxF)e3*2>%m^{9okFjuh9u~K)V?o_K?5D{yV>1 zA3=|sFBLdtlbhB}#t)z8_F0Kk5NY~!x zhZ$#BVe(w*Q@Fmr!Sys;M){WW7x~=Y`%72P&~!@^2MXAo?)4Tl6O`a@;?1{G@>p%h z;mQ0)(`u*g&&&HdD9VA-!hbF2=a$EG*Kx9y>llDj9akdX&g5wqx*0~ci_?T`Up6jkq zkL<6}e85|GGvJZVpxPQAOei+YxRUT}zE`15$2*upgQ!>)+a*e;;7xF18o9tN7i)e9-6r)A*;y zp@vm1Ty8z?8`lRNd|f}YSAO1E)W3Gn>p`|7>k_2%5c>fVA`daYl&=}Ta=GmVzxEOT;(ni)uj}gamF>lTS-17~ z_IiA+GY@@Tp&a;@@i*e%oxdXf?G!z#;on~C*H+Rc=HsU&&*MI&gaHa!2T;1FU4y(Y z+o|_b@=UR~BCo@9HC-=`Rn+4-!Z*kp^wZ@pl!;HhI2Pr=@HIJfe#m}MZ8wf%T~d$> zm0yz!*H7q6EBHOk{$rg_$OZV9+nd}j%qo-?UTP&*2SN+Tjtu~Ii2yTn2*q} zcpUt>ECq+X=KoJv4xgiV_&GBz)2_C(-gLj^Z27s>#m1IeJv#iNXPOn_CMSALi?J}PiZep z$w#yt@E9w^yFg*!2|Ecooi(p% z(yx@fSL-{!Fz>{L{d=nl^Xl26!syRnA%DSZnZK=&f1c&{74kbI-{U~}d*K`Oya|aX z{}k=#-+`OO^feY=&CZvbtNFf90&$COsTb?<0KO;O)v4tmAA-8l{YA1LuXcT5;OA&J zUthQi$s~r46`or7GZa4f`83_cO`s!#|9w!iVN|-iN4HVTmSX+~d7%G0VZ;f%7fX zo}dq42WQVao;_Zhl}m&qjo%0Utjcg0hwi0hmBP!$FOiOxAcL-dYApMywA~UHTFX0XKIhqWe2q!`0#s| z!A_X`wyx6WnLM^Gx3tM)>rzXbJhpagx_{qp>OuJc^`N!S%I&xGI!g~*dX1*R7x2mX z1$@qu`HJkTRQ$FMvL9iO!(+XA`*PR(RO+o}|Koj>Wq~)6!LjEn2zP-rK39H*eZqV{wKdZYc)wek$SBx__`Ocob3gYrvs0#D@;xBRVYcs< zeCchr^A6VvACGrm!@~26@tQTs# zvJPYI8~oOtj-Ndr(0j3;GB445$p00(f8WOy8+V4-pW1Z^3GT=hga>+`Z}(>2i|?VP z%MK`iT29%j1*-56{ed2SMtmyZu}zA|x{Q`zIO`)d<@ZD1S`+rKLcYTv^ZJ(v-(vW? zW#E_hIu%~r-#f-cKg))%(3kO~{Z?*9MwizY%Bg?VO8jJP={(c#E zLWOYSazj$i?WWrUw*w7JSYG8ydM7-7WhwjB3`jxch=ME`;@#%BYk!` zg8clJ;PtDQAMz#aV}E1$5%#M5^jFEx^C=&~e_WwLJ8*6X(>`x=e`>t-gjmifCvH!x zr`!Uaj39DU_ppu)R=W`!V0Y^_FV8 zMER&4AHG&7N4cyWAO5vaZk^!ibPqNU0o|QnON3wW6Xucx_$%uO+$U0R9Rd9TeV-xr zx0KAGUsoQWKV^0zl($)$_CofzT6&Y}V@z*{H(TM+-^{=BO}i7yeFb>gK9+{?It5phm%TlQVeiNsr- zF4uia_*agPnIT}Lb}0ZzHH?<&pK zq}Xrzo9++0Jx`ahy>uDlV?}ya#aTrjv+-zXF6p^vO!0(V?YI=@+`B(~mNtAt`F5R$ zHJ;}_M$3uZDZJcow(%-Ee~0`pB{%50R2U*ZOUVdBI z&Zp+TmXh@fr@y?z=8d$Mj=!v1D}FJ3XvbzSzhZD@Fn*)x8SKPo5AFu44)Z41K?ovOk2lTuEX&;W@*Pa(ekl8fqw3RD95C?up+fyZ`FB6F-uDKzT8*79B_C9Mh5e4M>^%DX zym|Wf-e1QP@yF4^{P294ANu#5JT4x6U**5hPWIioyT-IxkLv?o)%ylh_P+nk+79|z z9gn=p+NZpr{i^p3{C$60cf<4JzVGkv8%VjT_YIcRf8QYO+NSgf*?Pc$repn{g>!ed zD4I#V{rY=)W$YDsphebl17cr)h+p|Q^&EZR_3OQ75Vw;q+pKuFe+9W3d>Q1w2v>wn zu8}wEP35u=>9VDT-Z9+R-$Mz&ef)KT`}79*XZ!dUDZuoBO{7oM-k?0GY0u_I&?AU8 z+UedH;h({MvcmQK8HgXWONAo5w5w0s1)Q06;j}3nJ)fu^KDX=RZ2ShC|CD}b^vv)% zQwydKtkd>nJtJ%PGs^`p;WIARd&HU0zx@62IecE$o7xr7AMLFc845#lRW7{UmL?8N zV%L~o>C}5oT>m1%E9og6z?b2iL zTSmHQJ@E%vy1E`TTKquLkz|!L<=V#~@x%1F&m-mBC8oP-zXzaloXsbxw=U1=>NQ#q zbpFC+XxHf2I?VQ+Uah+=ZS-p0Woe^V>!7Cl_jVhfR*@gAqgHOYr4LwosipU88ul6T z;PWBnyZEu`_Eq{kJNIF)!(+YPv$-x4%l&@YS0?)ov>xF6gTM*9nf7%Pj~|6S#HZ$b z!13bAqW8cz-R}6TW4~(R$)rnL^J&Uz?`KOBq(f|n0Qp#9pZmFzUrH`j*~rgpAiaB= ze`5KpFY_vG*W<}P&gF4q^09aRnAXenjp_7S(k0#At#H!SOZDz>eE%-+obe9Q8hB6U z+y=;B<8vD*2QmL}eoxx!aGV(H_Kyf(q2E6fM+WVeH)CJ@`yozM=dpn=K2C%R|omc7PQ+fS%CKi8M{N7dXm|X z%2LwV=rNIV8xHGxT%2#pIk1$w>^#MGO~-h*3q5?k>Gs3zeq3%1=w^1_<74hO#qsAS zic1MSs`9&P&TaT(B}5G0<^N%sm&f_wm*}g~!TA-2wqFuuaSyvO(z`6VNDG`Ku8a>=X+o-gL)S6w)k7i zwC7k_Z#qw1BtIt-XLkC||AveUd(Ho`I6>n$^V8T~WexQpi{~9Bf3o?*(+w6r)Va{2jaA%io(=QbYb~J-;+>EQQx6=KQKSGzGS;-WSk{_*?wNiUG4`{ zj->oA75{figL3)v{JW87=yTaOrSg)+0d6(Ac^uouoh*KB^L5!LqxF5>6UPacM)Mx@ zclS2wufdDY0hR8`IV1y3z%Ppn3jM0$f>U-5$zP)xwI^uTu7OhWm-_QJ=^PTgALQSw z7+FG0(`#oby+^KQdW346#OG}MQr1Ii_X&?`y?meWA1DBiXVi-iR=uAj{J;f1p7|1t z`$c&7>+=|1pX9`LG=?YZK9rjX?zEQ*-qYm2Lef72Svwsn9x}hbOCOBvVj;{w2IXg? zc49vsHGu!?*XQ^b+sofW*scxu_ljJ;v-!h)`dIaTRDRa9@AC;8Ps1Me7wun)`0ext z{mkNmH+uU@r(oxf*34rfI*dsT$F~taGG8S;I|my0+#*Ym8J)BCueJ7%{Q^H?dbbNJ zYT!ry05}D>1AX=2z5w8=_NVW!1263w({=%8eO)+LDjfJZ#r^H7HxC>k-uG%T9pCUv z>E9=`mo}^9tYY$I!}}GV|N65+T<~d9GK&lPcV@D2^C0<>t@lz6 zig9fJer?V9uRGWunI9{h-sZyjueLv2{C+;Seb9M4(awKeO?k=f%x?8hAg61hada~A zL)C*5Joi=NWu!~%A-w~S>iD6{GwCP$3sp~KeOSxKdZB(%h5W;M3;ZJ0LyMQzpzQl#&_Q&+S#N3{D;Q6q%D`r9cf*nkI+$5fVK39hP{5^T<$;bkhpK0&o#O*0w zTlpOLtkpO*=M{ebjLY3re}^UZH$>ERym^O&bZf^O#>et~s~=K?<9@i@-}23Bde-BU ztn(?~(BIYIQ$$~-htW6Y(-`b`#Q!3nzJurcNJosb$|su#czhz}(^f(ldB4<9|3ie$b_ebNn;YQ&{9yb)EB%asn9{}|Do{N!`T-}N92(f-8o!!HQ@SP$1=o@sjMd;y*R6#Scj9DKMS z2kpwYTwgy>cn&#`evq$b*J3&N`#Y=U0CMGc#pMo3xf;EmPkB4u`4?XjeDZy(zbg45 zKf+$?r`eM@F4YP-GP?WwP$wNfbU8DBI@YHuy*If4pN~NvB6)&5q^;(+8yu9|GC_HG zb%EbSdAI@3_mWTW-_$N-;{e-@`F!65<>76Gayo; z^Q?G3Pq*a9@`U&J<2Y`UJO>^B`ej0Y;eXXU%Fidw=8@0RhF$;R|BcpxFZ)Wf`CeJz z%KlmQH-p0sDXGot!d-oY8_7NJ4Et!e9AB3kKd&;5lYIH<_2kFe&E|m%>bB$X)9r^j z&y4(y8&(2;%AxGH)PCgtBkf7bfD1Aq})$ue)7wE8? z_-FI2U6vNdPoI<*NXI7Ken9EramIt$ba)2;&J?eH-UbXlT1(cn!H$?W~L1W zal1PIFh9}fN5Jn7bpG%2vR}=94tXDvbR1&8;(5To-%!u~7w2UU)$qsl;V-{lO<(ej z^l`og`!1*IkMKLaoBW&z|DI9cPmzC13(v_v8~^I??>gaMBff+o;src!s^ZJk`Pbbd zUp~JH6{FK$!#DD;F){&<4+(#A|N3pp2ali1`RueC%ewVmQm;bfbB7j_c^CN}_gD2# z1~=$YtcO1lKR(^=^EBeqc%EkXKFD@*`7*vn>lsfHe#d@tqj}mR7>_2P&p$)CNT0_F z`uyXT{iWT6;kr)Q_(tH&3Xi_Z8Fo`J}_lIlRaCLGQuGj$eyBkuSmG#_spU z^M&iH7X3O`gIxPBH|KMt{)lV+FZT`7!y9?fgRpzJu((J8_Tiwzm+W&l^9M3Bx zed_URL0$X2P4&Ir{0{v=JHIV-v{kT%om4us=_;62`Br< zX?L=9oB>NaJmM$&`ZT{Dye9~}+VxqM&(~+4O!N3Q zQbGmI-!ELqel!yf_zinIk$oRdkG6kD{AHK+FZg#2vi7(!APgPmxVxARLE1IU@{0v= zxzaBBjbSnUzO>8WE!M~Ke3103FpYU5+7CmA6fXL|VuirDlg~@ZoAX?8s zKVEl>=-DNF?(_3_e}t&2oR>AG1+x7S0;g(!#FV`^^;gGlnd;a zu6l2(!FN)_eSO;g2)7v|)1_Xw~`{es2(q*$q|B_Zy`?KnDp}(MSE-D@j_4y&Mh^jn~clqb<15$i9n=4jJ6nYC#`o>b)mLdMDa%;Co|TICSh{IHPs& zO1q9_;}7%*@K)7@Cxe1q;a!_~_xJQfzhu}`TQ9fh{Ix&Ra+t?bKLxN{$NO#w`i1u2 zeFS#yp!QGvRlP&*V}7({=dbk=?(A+fBmSrPBZeQ^2Y%W2I^MOM<;TT;x0X8*{C_kl z{IAe<(C?3|5c)L2yUc#IW#=eesrAykt-s@ZAJ^j$XlJ@KDeG`b*L-|FWNF%Mr@xP< zE~g$3>mNQs{!I2exiXxCQNDj5j%QUcuY=K*Yb(`D&P!is?J&;J3qg=8)-TL2(68T? z_^t0N&gRLD_C;7cDJ|}c5IY6@(!0G~qc4ZoB;2FT>M|sc3OUivQJ}2Wa`E-$VSS~N`<7XrJ@pJir*Ukc7zpZ#Z zm3ZC5aVFny@ioh5d-;Bgf75&)XZ<|Ie80s%>+^iS1=Hzq_FL?V#^>MI{T4QFmiOqC z53wA=|F8GH)=ZIWU-zEKev5NtUYzUSpvk}M=PpYhrW~~Hwlwj{&n07i?vJsa%ZtbP zOUa)opCA`UBKkw0wn*$0`3DR6uaW%pff?4WwKE0#Gd{8b{boP^JN9RMU52>E`!hIB zdb}6@yYJ7~E9+tL{7&T{i#yYPfS%uneJVq18UG)HUv2BY~H-)GOoPU^hR?2Y?D z@jUVDJ7gW#{Hkode*x>yH2WF1r+8Vs2ko`V{)T)#cfZS(%EQ#{&mi4m|4mGx`WYKu zN%@M$_1f?8en>kM_rv{!k~Q4m@Y6i*H`nY~o%3jvpM384yB^frpYewRKi0!Fu*ar{?gxXv0|ox69GITP za*!6}pk4WvD87JmTj4q6@_73*$^|)q{ySc=UjM$7tI=z^70FD|d0a7_fj{sW`nAXp z`Ek7c8Q*w?;AeE7vi%u*P0lQ?7Wbce&v>iL1?chDkcWMw7xq!9JQVCH%008dN6<63 zBf0&)q)@IX4;SM3UTbf9_5O@{d|qEi9zyi}_ih0N{H`sOiC6T-_i75g)W1YT{w`m|qd%@623__}50S8vk%-0xv| zIagNmv-r;fMKgO(rsvd4xliNT0hoG-XPME zUjTTr&Z70y&yeSkgC2n|>$U}WPXV|j?CR5d5suqSU;pzsPP`vu@5S}x#oEdCV?1d> z@WUR`3-~Ub2Yd$<-_*WWDDU%H+Q+xemgf3j|K6RN4i&bacCBLio^EYF%+XuYwwOPB z{@60DO&>r$p0c5O-t|;@0(^w5DnD@D>>4QPR|MGq`uivh-%Oqi-)!Hy;c4$z+4crG zXFwbbxxx_p)jR*&>_@X8qWSE9?|hzDoemu`}{l7~5QJKFeK5|Z&-d*m(UZY3UZ?E@~8*)0!IT);; z=?VLt>k;YXcA%x{G0HeO4_oWy^F7wf?H=KllJ^t-5c}2WeE(ANKEuP{wz5B=Y;d#l z1qoN)x7G47oxmUW+op?3D&1@y#n+v}o~2q3bowRir|_eC+}f|t^ZnXu*x$X~Y|qAX z)c^c?*#j@pn{JWcQX-4Za>e+7-fnkM{zp>Y?UB46s_??lCcPKW6KPv1h{+}2bJcs8 zPyKE=WgXLE>u~kk-R%_K?~`U9Nf+@s^mw-Xl<#_JMzO#0U2$#S`ItVL?WEj>C-t`S zx#`)+U4(nQetvts^mCZ}9#5Zp7k4Q@w_h#OR#=+tU_1i;6Ir)%`Tpv^ieBt9`r7!_ z`2D{pvb}yvf%swCUyp0Ko$&WX<-9_BPW)Vdiu3@#zqCo{@L)9_`MF$1pUME~>*q@P z`jo6S;L7-49%Z>~TsUB9v-{=!nl2^JVE&zgfY!fP)1kulviE-9X?(VE#P?HV@n=(t zQt~vdAKM?;DYvr{e3e)>2BJKy=vd>jRwv-@rS zCZOVS%M0b2rQB5dRnLF1*aRVup^T0Uxc!?7zp6{%*TOq_zv{K3vqk;(aWuDQC*W5- z8Tgu<{u=$N2Md1H5@7`70`?94#CU7h!2cw_YWjN0{n`x@AI#QYYW=EdQm$xsM&Mk? z1-ps*pw~4kz(2D?=I5S_U-e%v`|tIu)bQ|MO#|H=4OSN_XyhF|r;R|*~e7y4BP&Xa!cP_>zqU$rx`mlzMeBkxz& z^S^c%%6(JH`TMuNUkT?sBW@9v4v2>5_`tue+NadPc?;S>!ImbjTf;8Q?#=x?_;pW` z=Z(kp_n;ZIC)M$iU7GLj>!bau^PM})zS?{HcwTkBa|gd)=5z@5{(j9qrOO(e?|kji zUjl(}VV~j=kHeecAA8&wPK#XWwMVqPpXZXU9@BIj7rR_S8nx#=bKK6)d%i{C$$oRK zU++8@ZU1&HmdN@0Y$w`p^x~@oZyTp_xDphQOWvy@T#5JeNxG(A1~*<`Lp|{C*;fnx zLgy;G@9kvgy2xf9xnleWCEciT5ynm#b9<+F8ni?7!7+x2Iub|VRUtltmj{r>iE0q>paN6Gtl=1=)NWk$jON;KWS zZ;<&zqy=y$>h-Q#*Fb*_sbKR{#8wS5)JX&DU-IoyBpj zeVc!0aa@b@ggwN!cgYyx%@@MT751=Sy-V!;5uZII8G3axAA8#1@Xg>tt@xf(u+T;ale1NBkv)6W85JiftxjyvCIovV1K z+o|s{elM4H(=*!jdEEY7$e+ze!H?=VF8%7XbzM!pcCEKY^K*MJn598dIp0ImfO{Ys z7d^g*X*!1E^(wON=5$V5 z*`Iu#&VJ|m(MR}ZkMsEk^F98T?VHc!lyLJnF8eip9M|BsvOgKz*5!r=;ij!iYx)QN z#Bp5EEmTY{wnEyV2edn&(-rH)4hui10s44A&bd>#`F!ImthcvTzRnT(!hGW;BERxJ zftD-MJCjSZOKA7$QqSq_@wH%fW#aMNHo;ur%+qEk6whszqK)SrzpMRre%8iwx75(v zzdy`Vqr34q8g+Bf}e zg?l1&`_N&b+l1}>?b=S5{Vlya-8{~Y_JLovZ#t8Q-K;mRJbY2hok%}cet9zeFh9BZ zh_)x^Dk!~gn0<_C-yhk4KehTpdjvW8xVXk)FYW8Z>AGIq&DT$cEv+{lmtij^GS7E? zxcw`Wp~qUbx5wnWLb+XU{*%wEcd*OEKW!lB@A^3T5T{d1(Oys>F8_(R|&E7BC zNjTa2MMkge{i1F5oc)*YQZilHt$a$mI>+!t-rKcwx24I)w5#3H)H8X{MAIWsZnpIe;1V(`ImXJsO=-|ul2Vmp985b39^ zPqN=W&(7wZjJL%5PgVYQ5U#JM!EYawEWk}(5BV^7Hjd#rE}Not8hhkbj@#H(tM7ot!49%6DiKFW*l9 z`!Xi^v3&UY_y6gU_6u@WyWV)25+H^*S_fXeePQ(oK+OLA1g3fQg9(qGM`+gc9H>ZBxl=%33FY{jten%?c_bU*QR)He~YdBkTT`_rUp!BYa0 zauxesN*J$je>NNMFVf;t?=__V-HaoJUAy%`h}M1NwFfODw>K>8NcDNdwEB>R!n@QBN!-B2Na zQSTAqYoqnw{5#VUbdf8c53aMc-qgOK#}k;(JZ99x{fW z$~hR!xAm_${(MB_%jcuT{d}PFE*bx7{d}9Z?I0aRkmvX1ns+@o-W&|_2Tm*cF9s_Z98|dox7z}?K8~q+bdu6ug^v>kc@p&VkgU;(# z=KU?=ll$*WwSs>q<9O}23uPL%e=pk&`$%`^JMj4WTA_v1t>Slu_9P#tZ5`F(6k&r@ zlnZzZTa}k`dqWr|pB85CzjSK3OuyVdZEV;4a2;DM;tjkm{0I1B2NkxQXQB7Te(Q?~ z@0>Nf8-8dP`1!b7ubroF6h1s??U)^HX*!c|3jSc)$$qDpn97x|)0^nSQfY~FtlrhHa{9dCBH3p2RwyYl%F=<}e2aOHcUq|dFxS|My;y%sw!74iZA z?w5oO#Ji{I=d5RR>S_9!rd__AFFn)jJG8qG>+@LN2L-O2OG!93|7&UbrXt|qfeszS zC-m{&HcNtpayectO(x&hZX%pp72TwfeC2ZWHwx!O~ zeq`T20)C{jkD@2xeg){$I|}pJbEO{2K_2m3Oom?rsVSeweZr89gG;4?T$#LevWnBE zDBq=o^bh8bgg)}Sch=`fU-Mf+AK~`S`a8|f_})%L(ld;&eN2b1;PiY0fee68>&bH|t=oeiB5?|9~*w(F2+EnatVyp}CRe|M|i=J=eg@E||bSWOPNe3TLf zhTXr;)+>BJj@j|l_U)u|w7$p(;d=S7hkj$a(&WkLn35b)-s0zGziQ;~Jd-~TuTp-x zHWX}r;`rv{h)?JszL{M5^9JjeZXZw{_SomGrR0U$9_TouFy212 ziS}x&;FtfY~h#nF=s^A;hE@Qq%M z&(FgU`P94kVYcJ`%@Et|U3^UQecqG5uS-087yVG5_e`T))%4r=?CUtt`_Ei;yz%1@ z@$mk;eRqG$#u1-y`1tz{?Iq!UuV9B;&99v)$6C;1)W@!oTDIu!BA=b>9NJn+Nu&~tf_-aepk{aor3!DE9c zSnc_d#-Hq*#|?@{4g7mGTlI6pI_b|olk4(P_P1hiw!nJ9ey!KLzXyE0^=il|_;tY- zVSf#t$xTr{bGbysN$9eyzTEpb2e{7#zC#W0&CaP6W2f+_PZPoS;X3%{_=$2UJjz22 zJ;`pR=lQ^|Z1`2I|3gO4JDi@+R(N5)-jcxZt@NLA;p0g0z3$_+yA16P*QZM{4p5Yv z-2YxmypH4VIe$N{em(o=`(`uykjdX-g;&HIe0+|GX8xZ4LCT%cE&C31`55&iTZbVX zTbdZy%f@eyllb^$abNdqOR_S8%lYE*jja8wJmLF(6(8SxUxJ@=;QLfciH+YC(mmTp za}mc6hf_-0wEf2GPVR4x2S*4em*Zc%F7jADw|H|czQjxRQIM|LzKU&@uEx93dq-}k zV?Pq(eCB7GKa}~4?4H<>zc)S~HolHp`jDj$Seo+SepoC&yD<(9X*c!zyjX9G=QnQ? zR^MR$eoIqH3*Jz!DBO+4XN>#NHBd@;&Sa3>9--`Vimk3~A zk3hXQ)~zQ4VpJ|vc51sQUlN-DYgoN6>=Z2zyyxmK{UdmPM(Y*(<8rt0H1e~Qd{WER z_*+ZI6p-`zSLbhiEW^Lr-}+kf+$qIH07!MRa8u^;ws8H#G*y+!F5`*nR2 zgg1AM;98KY;NyQ5mwto759M8%oc=&IocQ;J{XE=j1P*$b$GccB^AGPwF9rYm`t+X1 zzkJ<_^!Ic7;5QCRNjV2}3>9kVQ{y)hzjQUnQRvejbxQxn^&2-TKB`B;UrorL^~S{8 zL_8s(z!&{XTe&|pU+*A(`92`Z5B$ARl;3Xs+RlD9>PK;0c023qGO<7W+(nTbWaI5> zX{`>OB7bkb7?(!=UX6d2`BSpkB-cdzsK>&O+Iv)Kn91o5Pz`=$ef(nk_Y2tv;p22@ z=s#Vl@WMR3W$#h=dR=H{KJ2lu^O3ZJ=_1|ZcAG($as5gO8pt)Wl<+1(kN=A}UeOOO z$YVZlpd*<2dSjXGU>t?rfjrOa1AR!}EIu}2zlr|X#Qi1)KAj)#cVz94<2N-LmzZB) z&!mgV#ydG4l#(B-95wd8en2@Z_%rFPtncGsDPg?W@vrrp9;1YA?4McOt=LY%&+~CK zi>sOa@OVb8KlG2LCx_Y3)~WJ`1{EIU<(>Kzm0>M1Kg?6B4 zeh+1~@A?%i?u+MFh~297Zy5In{4*v2-`}?e-1T+gHu9T3t^H~wpSj=k2`%UPKEiN# z(sSxHV#_cNkS<~EnlaGB-#ZLzS6RA`bX>dK(%qJ(u)^^+T<@UVO(yJ`pZs|3a z=J@09(S@}fSNwdr+@INO<=vmzr0Mv3po4;UY!`=xj~-`2{7_h;^V;S$GLDKGRQol< z3a>32e`pxP9eq+u;J#6Bb-(AK?Oey_>9Tf(ldkU6d%VA*Zz1emv7M)AMIRTleH3?& zGQX@h-4`6U6Z=t{#ZRk++kV{o-d+snW*O3I;he2-!p>Q|yZ>tA5i*6$(fe3ugUrcD zG;Sfo*U!W4?RcEDOl#3{;SO@4UvwK+&#AMOPv=j2nywJQPk{x*y#LVKbA5%Lylyl7 zq1X@i@iAXtIG}uR{ms`G_LEQ9`aAjJ^FE(1=YHKmEe}0A8sXvRHlm!6MD)hsn*EFA zQI?hDeP7}OIHRKHe))c~xco6Ke>7|Nz*F-6`~EV(>A5z}H~YKnnAVHo4MHC(Y!Uth zsy4 zOB2`E<-A*Kf0TENSEja3mHK+q9mKzs{Ee1_KP;l**MP@4eO=1&DJ8UjEvL=XvPJyj zeTv`i5j^)AKH2&d<)avX%HvIpi?|=va>_@v9rxe*JBT9Y6GAqj+Y{^Jp>&7exu=>= zMZZ7yvrIpIAFjz%xQy*J`rZcQwOL3}N~m}$=Ypf_$wt$Q;u-c!w=ts*FLW&D}X zc1y{-$RBB^ia!<~C?ywY!B87+gLt`U6j;FXpi%B0r| ztbTqTfcZCf)9(r8K7}X!8UrNx&YPv_*M#yaOV6<9YbQNvawPAm{1WM~&g5XN`?>3wpZU3~EKPYx{d>__+-^CaZ|w%S zD*twt=#~4o>9Rg8H*$*>OJcusw&+RizTR0%mwaFEGQuy!@xqqc{k&*5_8Z&PZ!E$y zeEW$HU@`7 zk5kiL1ozdeptqe$ADI`C{-%dAFR`@S&ux}wyK=q?)7wVb-tBrz+B&$w-tAsXQ@cI@ z{_6|#L^1ab_Zx_wD|{b+;1?X<|E2x#db$5=_-FpF;ctGs`@eoZnXF4_yS>d^?{~j2 zR7ejWhn=tf-n{r@tT+FN(lZ|)m=3pYRyz2;M)XI-OUueFnpZrm<+6B$uajNZuK6v~ zer9lJ&H8QJ?m2}X>-p2pA9p_SUpMFWA?@l`9Dq-izu!eV!H&p3dC%YI*hP7X{cHWc zNf+r4I92|>o%a!!-z^K11z?Gzbq^k0CLIs1BAPn z_T1?c7_O7&q+h7e?xkHE_ddNT_=m}L$L4F0srU7K7wnc7^EjZ zhv1!BF}-oQ(#hXz1pcG)MB)z2FKaQKXDj@BnRdGa_~*z}z5jibi+mj4t@ZP9ewU?} zDjj5BqNZK%-G2nU_shET);??JeGZ4^d(YyWF6Jk`E9-t2>R&7q%8^0VUvQe(Liri? zvHkRcK5cjA4Sb%SPJTEZ={W*GuGIaC{xMC5X6jGcb%1=d{ak6+eoH%FIUfqMwVKe8 z^5_0%4(DhA&XEEflfw+oAwJ(>c(hWl+}^mq=yu8dSdUlc=ct){wYr~W@|C?;v_|RU z<55g!ewT>lV>yI=#&W3owTJ!3d)%>Hf0zgz8B zEC;;nSU)cJ8Yzc$EN~`nA9#ieK4 z?)Ms>%x{5TB3;vd{rQXQ`8~r*$K3BZPdggJ2faX-Ki7TBnavn zKq|;pO6IEkVq6CvcFBHRgVW3$AMdjIHlCG)TPT70(N$AF_rJNW(BJ%hE#5Qjce45y z6#NSC4gCVXpl|y;u@{C{f_UL~}LJ*OSY_8o2`74g1UpZtvR85KIj`T&xHKF^o^ zfwlhfsN$K+%`+8nsNJsKB)cl{lRV5Q4mW>r#76&w?4|W$#ZRdYXR}SlK2+e z2c0XQN1V&r^Y;P7PSWygy@5Q=CBiC~uRrDSt@AbC?L|uy-@`!~@hy*MHj;0b(^Ar_ zaNu7Z-=8)*S)4T21NI}|uTH)?{LG&oGWl${kDl*i)!GlMXY}^(#D$9C<8VD*S`$B8 zq5TW~z1l2(X5+8z1J2`T+}{K`;>x~Po5j!gUQzDPGo7v;r2K^CtmpfSg|4tW$dp^; z*UO+H7wjOKa=#?p*N*J0-Fot~>~k1js^ztDd{zcjz?;*~`|Wy50?ogilS;qF*NMaq zv)qu;caPD(;rldXjDP1QoJoD@UGgK+!}t=;gf=Hh?-H)}qCH&M`KPm&YPz@MKTQvJ z>AmF?t}B8cvnL_f!RVijGv@b=hz-RRR`8zD|4gP!$v(E-s@&AC{ecT58*gm>_b343f3f-BSv+fi`FHgx0yXq_GV${9&(~cM&%y;f z9+DvqcDjlVzOLGV-a$SYfA;-ckne8EkNr!Os~3M#zC4Jh(l6VEvpz-0g*Lm(H(7{VP01c>u#R0^T66d9u?7AW`IE2`iI&FSZxP?Z8iS1{;+C|Rt-K{lWN=eXhoy)Ti^9v z>)dnCoeRT5qy2Y(VD{a6uf5mWYp=cb+Rr14Z{mEvHQC?2_b}u8)Jyhv)8qM{Ung}G zwKZrxJH4I-mh-TcBlh`L`B}x+N0{#V27H|T`ZE1xyM}@h?A)!}si>cbr?#DVDgFI! zEjZC1RqHa_H9f3hElM^IDc1Qu@9dMV%N)L)?S0~Lnf;`^PAwG_C-$k*TppMASy+=J zAD3PJy`MwQE7pDN-W1@36(ToQ?u)(8d(SbgPrALgN&}_mt-k+yv;+L$c;b4)^-3c1$w>%Zil zT2C9($9+xH!5_F>#Bg}HO~bBt%l6Q(XgunRelrxi#@lHs?BdKmd2Ri|{ke4edpVQe zRPNt->2Gk}AIkf#hsh_qpBTG+Q*~dH3b>rl+QIG3`$lT{et*>Yk>~}pJ8}`vD}`N# z_pbGt&-JvdCo4UWm*1Q)*m@r6fLv7Q#f)6!qW{ox0GD!A@>8BiQ!W7iY-W7h*eSRq zK06Qn2N@d>es@()o*_Ox_q8#EBT&`i#A@~-J#_Ad=cwPtrC-NU)Tu) zt63`_7YEo)wV!=PepjE{Ccn$~hNlVTQtn+W2Y3!6+-G4)1)kX6U-5Zz8ZQb!JI#?P zYch{b&V`?+<%dvEKU?mAjYmGtr^mf7UnBI3lC5Z8pC>2d`^&W4__j^T$Mn6pjzQ{? z9Y+)V_p!cd{2#qGRi9*>RW#AS-Gs;b-1CFraeE;>&R#9&vmhrG_35|vNcO3|rv-|{mAY$Ci*M$-a%R)rS-9#BVsvIslURx ze6YCvQad-A%-hCj{o604-uLz`&)ZNVnb$RQJ*)iP3WjUp&-M%OXYGoz{FT3R@D%Hr z%=e!jyxvuMd%xCC&QJa`|*>si~LBz$?j%=C@h zFFoXk+b@335p<-S@VQb?F`e@wz^x?v204c!8!_Viy^?Y{#&=uCD$i5co~{pl|0~h= zCg1XXTP;`V$n@{mb$~CuY~S6R)XznasGpU$&E4u>S}MMUGCq_K>CbLcg#onmp}A38`_({rTljN z5jIfY;J)PDFZn_Lr5zJ{m&2&M$Nv0J09gK>-!TpQd;;yiNVaf5A33AN_WFCJ<@K$b zmC~?_`LVv$igNoDUhPxGzlz~_-m}Cb^??JPl~+U0tkwEt@s;shUxf!Xt&Qhs6`t3L z$($|U@s{!YN)?{NC&cq~6`qeR97=s&RfXsI!SHO5NOAHV!74l-#5j4<_L)LFeWbJ2 z`e(ZRdL>5{Uwynz=y^)%k$pq;(T>yS4W-_+qvc$H<{Jtgs7w=Q4Vb}Gx;zia!-K32lVC*_K2c!GMRX!reO96lkPHR-bU z3cIS?>-kp;pQKI|{oCI0Zy)=d(v>Y2_4@2`p>NlE>M`rL#kwyK3NQCwN%tWA{^@Bc z{$$p<%j?l!G5*jlk^L7fFYG?1KGrQd1R_i4L7H2U>%Fjx`Yx^CUNO6jzl`t0iqGHG ziSM!fiK^YI{WuQ}vlY7;EPRZ&ekw@jf?1zzm{{C?v z%ZDAPde!@-73+zc-BY6Jtcz#9K&bmfwCJ zC*k*Q@*DEQDR(@ypot5;ASGEk{J>j)&B{sgP0tj z^taKYU{iI1Zt5-qi=i%=`%jmrjHK}CGkPl)-DUjobz zzFd7@*!p`dd{z6)Y^QQRV>lP>Cq6I7_2z}b7;*8XV#vZz^bPg9^CjM3>85Xzd6$h3 zxoDO0vDDXXdghqo>)uQLrqAzx5%S%y==6No?P4ztbTW+nBUrp>ulw(;>?dzPzE#z5 zZxgt6_q>k!+3csDrbQO!Jl5}HlOLk;t>Hr&iC;-CogbqHo@(atOGTlYNRxxbiuN?Fygt=hfCPj1T^9LN0pL;EbQ% zkKF#q*b_OS1a7Ew)T(bTx<9GUzRZ5cwe2i_YbO3Gi{F%qZ?pK0O#Evteqknlp2fFj z;;)eSP|)h>dAu+8O>9PJTHVZ#P3oxEgoE-IO^}>ZEl`i(+`-1?PrnU7&38TV&GrtS z?+yj>9qHd`{p$CSe)Heoe^Yx0?{lC$xGI29U4Qy90f_#b^I$m1CaJ+^6eW1(Ir5RC=o_jWG{;r0N(Mu)V z)v#GxxU1nyx&x4JSgI1|_s`-*J5>+Kd&lbgI!nBwSHn1$tr>`>H=cKha1swE?}TN- z+ZmSi9XNlVAm8u@dIBr#CO*FpmzR!%mXP;J6-|ERqndBPHYD)t#~;-4aUbf(Qf~KZ%0WEO=HWZYSDX_8ytjwT zNj%!-^Ro(CPdV47zOT2W_3dFPKRu3gNErI=8*=`=Wc$^{y4NT@r6>Nkwp;1RkElPC zavn^TQ{GRI#WB!r_Xy+L7Ak$EC;6^M==*|}pR7MT!Sny6WPfli>*f2JO>EE3KGNm) zq)UAtvvMd8-Fb@F^1Dx~sp~Jb z_T;t<|4{NSHt-cEV-_R*Xc@fzTt26#hrXZcOCW((Qg{X&qaTs8S^8>cogUTTGWpA zn`zN!>+sv}0d+OB>bZq{!~5!hKi_bTrlUM)zFhRj@>lwc{Fg2gwQMC}ReAzI;^NWN z6Mla%+`@S5C;dR=J)Xz@1U*8{s_DJCXa~Bb)NgKtAv53be*J<|zTt+tyoT#EUf=+x z^nj9*OfoknkWkqRpw z{ZaP6^@InOXQUp}qH~7fx9{I#ygaZ~n^*zSufKh>q<1o(uLGs$N$g`l+WNl_GNwEUFJW>i?hN1^uBK{y3P3iX8i(4zTtCva|U#PbGc}pQkox0y-_V! z2TZQ`9#2=pYPHky4WHI8kf0olfMR!RIpz9rU;b>=ha;>OFY<(49>~$7y6>HDSflzI zbfA@T(NYD(H~r6a7-VxmNzyU`0o-nuAHkn3+|cQ8GUI{_IA9pDX#} zd6c(we5=Nrez_4?#O4tA8REzDsJGMb1&!~ZJg49x(eb=CjgMDsQh%cmh5qK_xYq~e z6EF2@I-cbpVEG(|l^&Gu_a`<=H_>vfUu~35q4m3GtL7`)58E}Ii*{PO+#g}*9_<0U zBo|GVziB=57{`;WJ)%%jk5kYyDBqmO1<#x28{VNO<$)i1)!#oW%O&mCtUMqc?o{xj zd~<^TC;AikA$R$P-4V`d_<8ks)={lD#)E~AlqD&iby(y5oPIp(De`C5)9R<~ihYpB zSx1s`o}X>l#f$C!e;R(H!tY}_PY@RN&O;KPi;k;7ROU-3@t%ctp@vm_!j~`V58x4z z6ur*x_@uwH{S%=F=^&_O{SwmqE(wUk`u+=&-m?A;L!)mi^L4wvUk$9XR%B;r;zg8u z==dLkKcj8jDmMwhd*c;{*gqaaQ2ML&hqBz)>IXcpnjd+E(u;b*C30o-m5e^n0lU}f z=-FX*?I>-Ys5t7N+S9PF(ZRBEi}98<1eUU1!ukc=$?SHY4`s_O*OTKu@K8f)vAw4| zKgdOUwSfG{acpPfhx1`t^pu{*mwv11`H?TL0e8gUMkjox9WzqHRrOJwrEZ6fZXAKY9*qq>5ra#MgK7H2kK<}CpJd5;ae&pY2`PKGKpUKfj zG#zpeRnYSI0O3>fQ|O^ipP!Cq|IbG&wWG#6te-e$@zDi?g>l~_>SX}&c>e~@=f~hf zulx^tS+3ugXjxCVgEAN+J>FvD`GXP=w?>!>dVT$1@+OT=-sy{XP~Ro`V+ZwGc#`og z+o@-K{vIdkge#6RI(u0z-ff3n0Ec?Cj9fGvzp?(|4)L*tK{s5yh2vH{n&Vs8%X-B3 z*!U;#1%Yc>#`?>5F4-Ou2MVws1pa5`UEVWNxVWiR{ao}bTA{LBJ;3y{Fs@b1<7|B- zZCAAlo$I3A7yKMTHL9mAaPjaK{50uNVw6jNfKxcr>0`B1q(4&Fb*PV4_r z%Hr0D54|AwMcH2;(CBEHC=;CWStEWyq87`V0-UdhE|YvPWY&mwg$i8;Eh`a=Wo-e@ z?=LPBy{P4g54l;qMw6q(*U_ISh(!CKc^Fxu!r__cn(${Sf;Jdfe9i+Zda&v4keMBza` z=9yv7LJcQ;kn(3p{9^Ip!p`MPm)}4dk7gM8!S4ia*IJe<)#Ln}VfkKXCZ6H2b5ljR z;Yp6`{@&{{jN@nrns3ss9fX%gQaZLUyhg$W(6LzCvHO?Mhy0;ETXGl`X%Au@&G{OhVEq#Gnc)O=RqN;W`RK!{i1V^e1`3k#*Y*{Xer@A%=o_i!_cR?Pp1v{a%Xtn9 zPqgq6`nNqrf5l<-vHte-3GP3{I~G2VnuO#$-VzHhv2xoiyj;V}q&%g&X|BcZGq_d@ zAF%KY4Ogsh>%MBi;1(Jj-;E7@Z1-~cV+?MF!SP&Xcw(-Gwf_s&el}>Y<1I910M8mIsakxv3)E%7n__f?G#{feji!x$B}N^4?;NyWj`Pny~*gE z&voJGGh7#rM%P20UGDupO)Yw*->c4vS8&}R-m;M4d)m}TeHKFrn?rptKk)jb+qX^X z=lkjMp0w4I?N(L4M_Axs^}9h9#N|Al)$ej`U*~f^`V`mWqqtt0K6xL_-)Bj;!{c3* zbiXo9xAWQP@b^nxE(YF8`cTi?WuYS%wHQ5J`qDr?8m3?L9f~}BHX{$PNSuFXLmu8E z@9D^QmX$u|Q&-ecH&O72EYwdB-{oL1K0T0W-wRXpCF}G<;L}^a1v*9=pH|jk9X%i2 zR1a03#qV^c+v|&&_PXda>6jw#hRSz~e=OOn))i3Ig2b10a$S3k{BD*1i_b+q(&PC1W68kgWvR(BEzUwwu(quoM`f%@h z#tYvy-qzKf@32=je%^A0OUHx%$-aTL6X0iPJjbClJo=IE8*G%{Xdj;krS0Q_!g2gd z#{)jQFOZA+m9BjB4|O2H+c6)VUk_7tz~n4zAEf)sD>MD&0?Frk#qV3hTR4s;-*et0 zK;pb#pJ-ehEAJ@eqTkVelwF5M7>10mw_{y?v_gMA(aq^^?&Rmq~Q)D3?(bh`HFV<)UwL( zUwmBPN`%Wr9m-df2d-g#27EfmEk@}}kMk@*8TK&j^UB7zv)t~j=zVeyF-O01E93pi zC!e3jIdE3W*-5_i{0`iRo$^Nf}p&vp3x zX|lJEos&R4{z>>1+c_NPTXr5aY00_T-i;3^I+fpP@)Pr*g7__ukJfa#*P;9gU93l` zgR2fbzTVgYEhzFbdJN$@pw$qbsNqrv>l=@z{_9+;$;mm3?r!okUcvV5S@Bz1-*r2F zL;clTHd+0dzh}jR7Pj*=E8eDIA2%_t#rLD~x$nME5LEfxcV1Baomo%H=l=bLR!I-5 z0VVX#8%g?C0~%qTZw;#fg>aQk)6?;wB=&L3?11w4w6F@kpztfv^U+`He)3T4zhiF_BF#bX7v#5(XRcTM z{@sb}^^4UWKKKHP%14KFKRJs(J&t|~Mw2<+alJE5!7%DvZo!xQ{`XKJ4L z8#-_4sA|8C8tpd)_Kye7^(4L<Arin$h^ zXyI1ozikHn(FOI>e7&qjyJNj!phmm<{h;!9o~ggf-=`W${Ptahw44;6-y${gLRO6UfUKzYBiNsA|7Is?mNkf5z>%U)PDoSIKX8n$H{t^_&g#&Y8&X zL^<#!^Z%;)|ENa&r=OtyzK&PcLzU}DL(xCq`cKgFUq3?2rseDpYt;Wt`|s3ef$v$x zm)5`AQ|qrjpMySAetZ9gF(A(MhJ2sN)xegpXKR($fFoysGQ ztrnz+&`&#cjr7A-2`?VYa^)VB!YyuQ*w68!9|)+sUir?g!j;e4Z?dqT$8Xbc=-;mX zfR%^+4GesK{(qwsIuhYoggZE#DEu6}-=MG0Fy)8pVF8SjwZbDi+z3C@?p-orXR2Y-*T?pJxNza4Q!NyON zmyDY>&+zy7foFrPr~5hw_{y23)?4m%9n$hAjUA;C@@@d}**o+84y%7pSiak0>CdX4 zu9v7|t=FgI-duhJZG5cL3JJ0XKt?0nDq*a{2>9{h2ym@@{@mXi3mcAU`2n3k9L`Yy zKAyvHahODi^Y0^+)6u#TZ+yo)_?oX-qoZQIy%%Qp70c<&my7sb7}kH?;b*vnA-tOv z_3}63{awz1OBpwC75&NM=sVvr|3^(*u3~)=hJ)O|6>WcXz|P1%Xu`kJ=+^ZL;k0% z6?`2`cYX|5`((@Y`>s{(?De-r^MUy+Z?d&lQh#sf`J^jpXRp7vv(3MfcAn1is_Rb% zX6f|$dwpTwz@fg~gVfi*W90Id{w~J$ZWBhyedYKSSRE>H*VJh(~|+c0{=Crm~!S`ysx#rW}vHOWDdF#4!a=Vd$nX*Cb-w<6 zQ2H7A75MTYDZ}FjRxv-)3ziNX3E%f{T=M?ZX#HkjDf4X3}cD&ov@<@E6;-x>v$rw2vQ+NEFwY!MA@k<9!`#qu^6MS~)0J`DpSra5LcnSCF5O z7x;_4-lh+cUctvs0TQ>rj($RywNIne18%?J9hlBA%29qyV;K09AA{hl;ByvVo0TVC zLHZvC^dSpwqr z8$Sn(kFeLZeI!l$K><(#=k+uF;Nvm$Q9;rN762+G-(wlxUogLo{^RtL^;p<_730K( z27f7iqz^hynEV}-bhvn)jT7^1oS2sxCl*n!R*e(Z|MySza!zfWI2+@`4+Qmz#)t1o z`u~g%Y@dP4=qGY5?O%|n{}~^QKW4Z5&-g%o{LlD6KK#;+4{o>3FuAtzI6so-Cc=DJ z*D^-caiH<wFT$I3% zD8nPgzXzC%ug0$d8&4Z;JVp6%7&q~=WdS3699?Gd#bJ{bE_AAw*wbqoEZ=p^H*g)( zeZGcyF;dd&IT9S+(qnie9d4SeONdFP=Yrt-{yXd))I49u`iEOtPuS&%@p+if2Z0wQ z`@Aqc|2=~F*ltM_*C24dUiN1a@85CA>*+KZU;Al4gubVB-h*~MUu-T9$9JH)Aus>eL$1T{46nyWe0mue&x{dN+(qaW2oWlcfowJLhY3w78T0b<*x= zI$uY6EhfMSZ&okz@=8`7KUIOh9@lk*U_pnAGntA18e9vPOz}9e8~9}X_kUSxt;)h2roAN z?0--K;{5v&I44?=a5A5JJ5nXx=t4R~2<-^YEWRJ9BFFn!pZ!LU(;weO{v~017tGhW z8jU|;kMWo4Fqsd>&gWZ+2YlA~gUBm1>te&V-`X|qBEKe2CA{x%;ob}+3i=1@D6gIK zs|pbBpgkX!k)Pf!VY$)0gynhrhnrchp9gdK+$en2_LpL`{h2K}&$xfH(Zk^&O6Fl! zU+`1g-{da6o>7pjnE%7~ds<-+`Q128BE^LweLwfQpWQJzKekbl(QbZE7xJd<$7)B7 zCNF-UbAK<(Ydnv>=fgL|;k4U^ z^p?NV;QhK~>Jjz}lb0BZR9u|1c6yu@ijB`Mm*GjabCsSW`lw}_G=Mm7PoKYbnp_=} zG(h-woD#ja&CXeFMsBIE*SneFME|jP?Ki5o<}=;()^!rB(0^N5569>9G@4wM3?I6^ zmS=dqJm**3HCGYkA_6qNnt0>K+w{ld3`RcA%J(;vzy4l`kB0#luf@e%IDWzWK#9&T z=vVYFAD@f1zV73a!$Dp^uh%E^Q;x$Fi4^DGp~U$9HgSl9kM-O-aR|fr^@p$oF+zv* z4>(^R+bh4Nn;(bVgRYfr@<&`_hk7C}^c%!S%^IX+z?a_#`@Ztwpu{7+WwW9d8{yNM z(f1>f59xj$0OHu%#c>qndHwSv|2OmXs24p5CWynm{9bYR4IlX6{QFX_@4X+PK4?NS zcM|;qI-uA59b`Gz!+rv#BF_2UQwM5AU)Xo?Tn{5ZP)|1cLO_%_q)omxz^SCD`&uPQO@=ON_#B}#82#WiZQ@_ijX`2Oh=te3BI zhg~c`?55nlQ;J1bbGtIEVtogu65mSWyN}y$w}z`N{W1E!4p1D<{tdmM?R**2eSLT3 zG=n!i-e~$eTORgDkv~7&=)acveBX4h5DmVpyw%FHbj-8SE|8O=IHbZ~`Fn($oPh`2 zCE}0@-{GKlpjE&tDoe{MHTzLfO3T`<|?(Dxf$4l&PyoVDDzN&A!Q z3tuO0T+MPyR$hxfS}lNc@T3CA{y;JKY-vkFYqpNPHbH zB>>2laj2E_k7o5mKU;m~aKN8z@$oo8D9*?2VDXLjGM}Fp$VGFMp5*>bKhr@6@9|jq z=d%0;;WH>eJHgNEYo-h%=Q4{g*3-cA?`9|RHWjs!IvHHHp00m43K+_7xfFlFx4il- z7fMEPp5NCMK#%fg0r6se9r6ZP@g}!i=;bgyJwHDx0A3%o)7?vCT-eEZcrIFjA}ZdW z$9`)Xe!apUP+Yl)9X-9T4S9i`^6WIyj`9u)9}D6G-{)lA2HYiu@Q z-E!^aNf`CaMeLZ+hv4WH;zLf*K7#JJ?}LE9D0lIN@<&`c9@GpLFGUO*54?lHOBDu< zFQnpK@1kAO`Zc|e2|8C!l|SNKkAZJ#J?iwYH2vcGcBSd5#s$oumLt^1`zOk)7@w>h z#Ha1XS9$qDFUA|lFZlYlsa8IlL(emTZq!Sf(dDh7W$Mcna9}OrVF!bMKL5tLgs7Ov z?{oOQFqf;AcTpM;hFc~+Vv6-bh&bTGyKwjovRWPyK@pdh>%)@Zldy^SEA+ z^Klw>`!zobvr;K%n|j*MK{xWhSspg>c}CIf!!$mub2&cm@9A@|7bt$(Z~k3%U$2Rq z%K9G!rS0UG-zwo%qzC<8&>RQ7V#3IIzFwu@*QZ)K$sgE5_;r!^=oh}v?{;(8&3cE0 zEH^BmehYg@N51|enh?+0spZF`*Q@XI0J%r3;qWl??-gP|7tZUzK1VG4l~gyn4w;sF z^lP|WG)w)^zfa3s`G^LkUJQqw#E&|l{P5&l@hCjWddH(#Z}iJF--@aYC7-w3d~3IzY@b}TMa#{$Lva)t9v&uMZ!d2jx4XTc z%6oar=KJy z0sSyu9x(U)d$bDVAGzSmxm)&^-R!R`T^_B!`FSCqf68~_)%iTEI1IgA^sv+yE$H&@ zDsQdnO-$crQ$2v)#5>?wrexT<@X(DL;))^tXzBl8Ue3r^P!zbJ6RSA2H3kC@e6&<@Z$WJ1~=}-lBLoe?SiE zNE-h3=IhU)KC$xGNFw@~%Q5uJObO@fU#f7EQGht;BlIs{7xQry{q6oA;oSWpJ&zvS zIg^21mQVPo_WL}_yX$2?uk7e?a+vB0GL- z#*T+XsC^{+so8e_A3Gk>RNZfXN$mK^L$TwftNmQ<_NjY{r+K% zPwe*qMt_YLZDRh16)bXluxh+>`&|f5k6&)T%lkCOhYPi!ns)pi+Oxh6S!KsB zARRs))U@MGKM%!@SGmlTo3`VTp77DeS#L+T@2c$b6|4u|(M|KCXzzF=?J=7JSlC0p z|H9ej8_4fpSiAfZ(wDZ&(OzzECi^Sr5YE@xyxn|#7UR-0(%-Qj4Lcmr@a5fNm0P#h z!%oUEUx^mEuzuv{Yu&y|?D89lx29cga_Z|lKHp62a?5|lcDd9mEr%+gT~{w?GOtik*M z4)cyH#Rom_?UZ-@N&+CR6x5UNdvRQl?_()n{d`BM!~)_b>YH*rxFh4Rtv{CIg+Ut6 zal9OlS_7VXsvOUCow#XJ2Jd8pZ`Jrx=>iMSurOW)KzRiVpKoExMXB@(3y-n%CJT?W zFbxBFhtt9w@BAD@sl){rzxP%ujko;I8axHm@8gw9=UV#HmOjqHM=Z<^F5e@zFdNwK z?UhQDbGa{V>AD3g^*g}urk87ZrS)9jExncRTbJ&73FCXXe_h(Zb(T_z{iuWmBXRLP zbG4ks-rh}Z44Zuv&sxYZCb58@w}jzWTX@lOhOf740n5^XS~^cedJfqc=iLo*Vi+i{lL#@^o%D1 z{CsQAcvi&E#rBM6g}ps{#<2CnJ^mePyFc19p8deTyV^6J{lLGI-ZP&4 zA@p@9zk0^AUxYr&YtMN0kI=Wo(%DZ!ALYGgyf9Q;=xeid_M6Z**TU>S!M-EhGu_sq z`+J!_oqA2)AFwd>U+CXzVd}}yzum&rr#{c~hV!kN^!xeZ(9H7uTsP)lGRz#0$IsS;_~x1F_gp)J;mZr^C+{jg@Il5u@DcUX z=ZHQoG3oK8L&6wej>wOA3)>^!!gh$akpKQ}a(dsXSLjKX(`)5?Ov_oTd~tme+xvG1 z(GMiW_v0ELluY8>-p2ShQ~sy-aTfxQG{+%<1D!W_$oqxw`l>+5I-NB0*8y++Elp3G zqyzrXJGh>Qhcb#IrGLl|?|0yr($gu4;(Q+F`b5_UUl4rvG9C0?Ea5bsW0C|s`#MOE z;%WF##e@BgS4+C{Kb^1F^7To+L|!hJNOAtInvZK}fAj$4m!FF73-XS%e_ybOi3idp z5$S*fJm|XW4UE5Ylhk7ZXj?Ks= z_!2q_8g@MK_!%0GO&_6Oq6DO?pQ7Wpm5ygf$CE~nw_8&F+@yTukH@!ZI3C}szMoTy z?YtlGphaWnQvF!lLx2QS+xc;{pVbTPT9A0?1yA3Gbfe4LJ)QneN%wx1uIEB4 z|0}PTem6q`;@loj>$}6EhX&?Y`c?EH2M&u<luOb{^Iw0{5%=xopmk7 zZ{~M741HYJ^nMchzNzIW<6(ECrRk38!cUr@r`K5_YW`z4XXKDJLhp7NTH!G(s3D?x6e^)(RgX z?ZS@&u!%pOe*1_Q$ln6Ek9hj+BiKI359i+*59bh1dneCF7xlJ-$laDhDauDbyhwbM2f0DIu>i@v z{++k{NShCpW+GnBnS(39OFkgqJ8r8icZ#H^%ax{b*Bkb+zP=9>Y@QbKug=x!}PSNvnaNEN=_HP?{EOa=Kp_W_;j(RqZNd%PLnTt-=n?R z`fn%s66`&!_GZdY=%hUPx_hv5=k3kUQ!e+J|0LxxZ|6M*9#nK{p8=!GH|#Z(*dTfj z{4b7@2yvnRSuHPLPdT_q_zt*4->3TFXXQhPIlDjU?^fmOjb3ATTJL{K;Ylath&58Q ziJas|ke^8Zn+*S;QESPW+b!vF1tml7;jpj1d`DSsQO-*jOT6G&pNXF$_JpqomBMqH zzc`9{b-bG*_FFRxMi}kBu35nn`!TT>S)Itg69_&2uL7Ejq!4jo!>85va}Y2eFA=-e z*NNMwUTAP%sDgV<1>9s!_xgsOuWA_M#Z`cVkc(@d&3S9F{$(0=J>%z*)Asi3B%iO# zwm(AoKzXm1cz+iU?R%w^1b)7GrRZ<@U6B79&Ci=Yg8i-b_z1}WyI=Trfv~{m&AI4v zDt9NM2ljp-+oI6(als&OTh95s=JR&QMc>eHBM1=(IiHDqc8(`DJJI=+lh?Na7cY96 z_)Q=Cy`IuIwtu*RdO6%=@6jzfLijv#OZiJ@t6!9RtmsdE&H&@W6rs5|e4(Y+X?lJb z`;g5$ZXwXSIC)Q27`hq25cwRmwN@ zit{5pO6^fC-?Uq@^aD=i+4@1Tp6eoRUpU_OsV~)f_&v|YwXC1(KbL>klWFnizSZ?1!Nl&=$eLlUy^{(hL>qY9T{)kb9M zntxV0Ko=r%Hh)dav9_<__4P-8KhpONec#i|alZRK4Ckw#S4RJS6moMJs1V=Y@eHk| z2X17%zhC8gujMi&J9%$z(v1xJKB3FYGSe@9-=|pr`&v&Qk6@fP)T`ykoR z?Iay(z4ji^&EYnY`!Ln?(gLH~zF_6<~S1;Qa=2? zf8+gD{-yL=t}}j^$uj9Kc=!6{%BA> z6f~o^hu?vI&(g}iSpq6N%#P~X+EHSL*c`R2IWKR8S~+9 z2Ia%tv&@HiReS*6G#^mEq441ogYu#6jQQ}HLHV%oEc4;KDn0;jnh&VoQ26jqgYsd? z8S~-mgYsedS?0sBpQ6CDegNJyA5g!c@Znp7@}YzDo`IhH-k^L~%W}_}e)xP9AAmQ_ z2h?vUe0X6{KCC}uJ`59ks(QTGbe8$>7gc-!-ZURjzoGEqC7`!z-0VGLK8zcb4-cJX zK0IE<2jETf0rgAxaD$?c8duSWy#f0-*tqKUu8)UVyVu9<;xK;iJ{Rp${IG8(Pbtsu z{9bOcezT@G+G3sWUjYv+y|BydO*{V?+x}*-`$oxnnV%1`{mo!@jMvNOkMa2HF`Os+ zWWci&cm|mV{NtM?-saWo#@jxi%sX`-u#^08`w;wSlPJtHE^Vj%f4l&@l;68g<})<6 z1)tT+_iHE6fC|1}JK;)25PZLO!W9-)FN!C;ieX=8p1^rmdVaG^AO@?~htl<$@J%fT z^Bl}TJpW+zI#24Ayt6R-gUlzqlYcsoho8uU$PFCYy{@_3?#246w7uhT%#U>PiSPV- zIOJ`9lbf&en|Rhl=9^cbzliVk`}+WC`wsKNcvh>zrQ;ukKAR4zq@H8xL%!dEc#%yw zUw_7Oy{_lvMnxljDCzr%KkOy`&`^*(oPfpKA-i~}`S9`zAzPC211Eom4d(=zLhh)o5@(F&p zNru<=k^DS~?{6mI1%xjSqy3M0$yA{$-a@|k`d3(en*xSatJELpV?Ol5r)3_C{&R4e z)TfW-6z$zH$VINXynpTIP5gXR+(h~D{u(ze(e@8^PQ}kv#Z3z>p8YRwnoBv!(Bbnf z=NIVOouVsW#{r?5jt(oI>mB~xi)uR7X6RTxI34}QpL)5^B^Qv>_kWg47<9FZOgq2w zb-$t+s_E*@(6!aVT(2(k=b;Q;n+Bx|`a%Bbya)(EM{kNh#bjTF>yKr6maDxg-}OI;Cq>(77%}XTFZ#g(%baUMp{t;qi8K z`u^C`AF_0(Z@0zwTD;Tun8mMGKTRJLTl)OT41{-qo3gGeM6b90(5&qnY5sJ7h@?N{ zBCZdFzJlR7VC^^2!uu>d#=?KAVfk)=#gDZ34_o|p_0x22g532RUBb`pCU<`(e9Ws9 z$U6EXT))cKLn^5Ej~77S!&<)U%XlXBI`(BgE^t`?LylxWbd?0eL63Ea54mc1Gs=;3 zF5-N@JapRlP`5zGhf*i;7wg&p7KN^Y`pG)Ld=2XQOP%~|$>Xo!8LnP=oRTn7io@BD zpm#2uBJi{4DPY<@0sVgNHO;S`)^6M6{4Ml2+`vBi(2F0D^itnp)@K6ab5TKmq{pR8 zQsYwCwNK&O=Lj+4(r~Y-fIDDtJlE&rIP~jPfICOC%Q$|BaCVO)*)Qp2dgC_wX*{o! z{Kq|gerA6dLp9_{*stHsfjye6H}^}TIM@SdztFdx<)-BDu{WjV zntH+CclUeW$@x3eV~IW2)S=}i_FU6)3lp!5SESqIg|Sg@ykjom<~tnqh`&qg=c2=W zshBuFKON>zw6N)^rQF|fc@0bbyF**G{Iq-?minjr`&@)KYjMiIAIU~9)~O4M?~vw~ z?;op=c=;!EP+rjvD^l$sdZ!BC(F{KD-|;~YNRueumr0j|or-?va+2(C+$WLZ(7q#O zBQ^AsZ+=hN-?^N$O%#r}HBx9!d_ULec9YvRDCb=Hj@m#6pjlt&Ge4(*dQP7rbdc?d z96?{yMKh-?lXO(Ht8RpjKV9+_Q}88Ucdq=Uos4i!zp6^Pit1)g$wgEMU3H_CzI@#n zJud>g8uaEOF8l#clmk3FbiVFo$|t}H%LI<=@d`H#aNPIFOS#|x;LwBpT!6wg0FM1y z!%~l&Vm11PBaG55T#qnnVc`)orzHK!=5tAZIL-5@wa%Y<`$PX=Byc^4c7)8K{q945 zmFi%|=kR{+G7aA$`2qi%0uT8@xY+Q+Xyl9)=k{y{l^D&Ma>(RoS0 z18&kn$tSdRZNPa+i4X_cJ2%|A62CXx`PRGQigT1bU5H<~p=;xb&Qb0Hei>)rPIQj) zWrF`_agOp)!GEH2ls~;r^urL&QEov!*j?ou<$SFV##80D@F~mp`v<}6`+(s0e+b@g zJD;=8&okP7d2*hyq-av-8Ocbr6Y%+YF+UdudI}PcboE8A$WLwG4MI;o`ghfLFXp+* zPNn0^q~qs!u2S1a`lIvXHnitvVU9TTmje7$`mZD1><*-Z^Xc?elJ5Ei{MK}V6ERkP z|EaX^>71*)U&b4>Bg%U*&sDxs+erp5aj+Ai5q^=+RUU7{`1#9ouCgg5KcSCyOIkmy zgZ$8J`fqWra?jkdz2fUy7+*%;4oiX#{cMK(bbA5mb28~zX@vfiZgNKFD&Lf{1A%u| zrd&}4?vnFzzsTn*TZLM;pU&pF%Fk!`bcy1*NO|+$;augD-#N8&mAe%Fc^$tXpVmde zk3>$==PGw)_=ly@wEU*!%+F1v#}yO|`Gmu{%AJ{Y+^=3wJMB;={s*Ez!kGD*FP!^o z_5J->|6Z4$qxJ7w`gzx7V&_QJl68pUh$hVsJG{PG)*miotta28(em8}8rVq3ykyc; z&MPKQWqKF`t621z)gybJp``$9g3naJGVv?$=Xg#$Znkn3D0uSDqL0hjb0N!wUnr+I zjELiA^1svZ|!b=#KPNEKDwJ9 zvoPnG-OayeVa`XpXKdGSck>R5=e)JM`MnnAxX|7FJ_{eVa(7zzDGT>o_=tsfTli@W z$FnwB{1X;`RKuZ@{P1&%X*&jdgPdM0@_r)w`Q6vbxwxRk%J|!>p*gieobWPoIPSxS;g)Z9h-x@7el!{(PPFbFn|s&j$*mv&GN7--S{n-uoYnYH@xp zDd~UKkNx`!lc^4(_&V!9%WfXlYCzxy7~qr9Pgzb*eFzh1yfGMVYovoV+J*s!> z23WrplF4=g^_PEW9$n(b7t2rv&~JMCmUjV7O}+9e|=Cu@__Y>zwzV7^lPPWfGG~ds^U3eZbKI3-jIZ3-fx}@JF?Q+LiZ5On| zZ&kEIhqVLyh3l_uzwq{HspEOXQ|7x2#_jhrfaRGP(3~uzn8L z?@hYCNc7iK>u**4;;NJIUl6DG@A|^;M@<{?8f}kk|8V>#>lfhv8!PzVYW%03%GVWu zUfW;ChYcD`&JBd!?4K?3XKB7f@0)&&7i9F*9OkdmQ>Wb?uOR=d+|z9b-)Aq@@g2>0 z>U<@%dOYDk=l!9&Jw}~zdz@d<9zXpl2r2i2g3<;2CvJ~pn(yb|9?vP>Gwm1U_IQTn z)oc$L0L9gAkI(-UvSRN-nd7AC<(HE&b4xZ)qc>hV-1Kd7Qd+a&m_Bd40 z9y?C6Jsx|J+T-nKxjo_&w#Q)ehKK({)|+J<8i{rivkOBK#(~=<-sd;@s7=ew&VT&e zU_2JBJcu1^@60E9F*V=WoN71h|H0*=Rf@;Y{b4_Cl>GN|Rb{P@VETVTCH^gAGfOXV|KnGG^60+us0IVF51QMR7I2Azd7}B|7tC7=<}9e)qdme z{NS7bRuZy$&FB3m9sh0K;qw->{~J>6Q>=Rx>udIKzU~68u#Y=s{$ETuyH7pYI~RzxO2kubg*G8}S=wo&WEw;QyUNm#B>bmbFt@35TA zIPCj|zOFvlyyB<-#&ayk3$R;g|H=7wEzj5OeLo}~OS>>k`M6fx&q@0CVEvu!ma%Hq zN4`&))LXiv)_cS=q}MiOlX`R8Flndcp1rg&@@4PWFSGb!{pVQTg!xJr@S#4r=pQtG zV7f-j{Dk*P@UCKEikwUCeohneqmC-U$vv4)!ed_ZukzFHdmta)XURpBtFY?R3Yfk> zs^_mSVgB~1&H5v(`a%_)p1)4u&ewFb4_sLNRrP^Ceg3+A_5_WuI(J>t=!)~*73Z!q z@6^aSSGAil&$?2wVjl6}&4Zo0=6ftS-zdkdqkO}M>MG7#4}!Piy!C3z$)q`7)b&1} zx8@?gn>N^d_7RcC6YXP0SIPWo73-&7^{hnR2uP66JwBM)Wac)`8H&?~u z;7Z(dq$>Vujen{texJtAP`;+i|B~E$ka=+xJl+kJ_x3bC^ieL;a)k4z@w~0fx0?0D z{f?O$->dQEdG|gI$Fm+{yvPynvu$Gh=mYBexiFVA`3{E0`}y6_&oJhxf+m8zQ!g;T z_eal{ujdPBfW!Vz$>x#XuJJ6&SxYnRpwP#1fM2pyoYT8Q+C3ND3ACbzm~V}Qq{^ZX zSU;bi#j}nn-G2Wr>?Pmjd!-tmujhGwmvfxkh9w+!kw5;vMm+Cnmeapo{c`#18Q(|# z`FGe+Pw+pvzqgC+5brpu@ZGZyf&8d_n3RTvcD*57*ak4UM>tn~*Qbl;Yf$z{ z$v2$qL;o8vIcU6(>G_c#KaO_xcTD10V^sd)MHA`Id)Dg5dV2Ygn_ZI6?{&mYBPqAd zEEnnTsYs8Q-d9e?dHc{$enXGIL63a&0@Qn__V?u8kZO_v8=TBkW>(fIs~gr1>L^7MF{-AMX4(Q$ECJ zT>nj(`uAq)|4^p>TQyu=f67hRMfpPg@4cX0|9sttRUUqE>b>3SxrlT5cP=PJ9qi=d*|N2{NB0uFDV1A+`p_} z_8(?H9c$rjR!@6xH+hHIzklKV(#DtIv!xnbFJ!N5UuOlaIuS3>8pSU31 zKJU|d_Zpai{?A(2>;JrlE93<7 z*{{{pdu&fz{r}em9REiM?IItVub8a=kDy%iEzx+4iwy!-toyOa%R-G0ds>;!;^mzD z494#((0`KSX1;!^=0iC;PwP8Id1JSbd-)R;zGdtWn6IDV$;Q{vPk7(o@OewvyOrha z<2b1D-NE%pkd%vlO)2yFjgS9fszyit?(n3m#DEaz`x5?MXth@AL4S+bH-2BFQL!po%0n;>>BdJ=U+}| z%egGa@q)kT*UO~;Bc(X(@0Fjd|TrETp8-? z0x+z4ND02jc&hboEMRaem$waKof7x#%Hn|ADzo_j9;zPc9R@ ze2-6lh${|;9pmP|MD96pUvPumcdW` zo4fpxQKRj&U;3w!;ozc!7rc;`c;;VL@%wVFCENCQ{c z4~K1>_~r%ab}>7~-r?}~I(=Qqzk5`s``HW~N2};qo3Ue#X6QI-bPQ8E)@pvAH$ndD zMB^p*O*NcrUTyN|avu6NX}-A0zU%AuRo_DvKUaM}Hyt)2PPAhMrmH)Jbms{A|bBToyTY8)NY5e;#<9nOopQHHqseI<^*e|7jX}Fk# z4`|r+=Q>+Q%S8_;9`bjX@R#otGdx1VpGl=RNchvF%lPi^XrkPuo$Oz0=1CtRopk=6*D{EK{@w(&9ri^5G@kMe#MU&e6W^i`NP zUjbWgqW+qE4fDk}Fg~8|`U|BKFYJ}%-D`=illXPBHMy)uIgL-wX=7C!BV#cqgm#QBu(r@2nFc?>M#+#d0Em^SQXdh^@)?dvLyb}lenHcB)4dl~Un4Cnp3 zpmsj2<+2f4zTZ=c=gik|ywv$k2A12I^dMZpe`z+PtYh|s|H*p`GqqS5=dJz%j2rJL z2l>!1>%S#>&iFFecWVF-xZB%!pQq<7o$Dnk>REfI+`DEyupZg9{?65iU%RsBEhoBn z{r!Ixe5~dP?_K{|o3zt5wokG?;PWP*clbQO=N)E;`8o;uCs3v9`whW=qV?9rf_EtQ zuKx-3VD&4$U!(jTgwLNF1h1dl2Y=g8-x3Rq$Iw5~_iKRXRPI^-o#6jP_vI@Re~C%um?UDsW5AJ)eS@5e*`F3ojXR{Gs~Awk{; zWj&Jb(%es!$^D$G z@GE#-sEUJdQ0gm20BYQ95z%f?4v=MDCLXnXU&QUAXP->doQbEkH% z`k2B$uk?a%)+NE#-u#Ha(D+>Rr>rl8Kpe&kINXaqlDYr;4DkMS#-FXVrye)7)h6&g&& zy_UDK0?B-#llWo(BBeO&bDANZ#d&O8VtK(1O2>=HCx5p%`A$KP;Sq6Ey3LMH-urjE zJWqRB_Sq;OQ!omO9$e1+jW(V(ZeqCQ?M5%fJeste;emHCeVO4&z7x>!SDFyd=E82g z+3<}$M>B?N*nbD68a}h@^*o0}`TV10j^Rz{K9Bhmx+x`RkM1XoZqgZVHhg27jqZY` zpE}($Gjx-|XOHgRHM&V>yxH)Loo#fto+jP=?nYTo=CYi#CMOiIcsA*bHyggO^NsGd z)1>?Q48J)*o;`kdDqZnx(iv|ye7^2{&63lk`<4vdWc1midy&z-RPn@{4d2*K;=QKh zH0kcn(7l%BoHaS&fu?vi>5MlUzOf~vd;MwB9Wr!N9?u@XdHyP%O*-SvhHvaW@1D?2eHtCC- zi3j7{@q*;H?;N#HXa0EGY=D>7L(FIe{COB(Z)trV>C;AC7qy8Wmd?;{SW-~m=l@~J zL<@8L4a>(EoP7tZ$oC<`MH5k66yHrhTr|eQ^utB0XnZ$)KgSbKSFk8~hkbfMBZ9xv zK7EFTt-R^27B+m-=V}<^5?pa4>yPmYF8;mS)lcG?EbEN7X?*hD^7vI2WU5m zz%A-yehcU2__?Zh{8EjNM_jLd&&>-NzP642-M?Y}UF!QiAlR)ijonUyy|GoouwzS7 zWEu`zdTBTq5P-W~;ILl<``+v8`TYIDcrzDF<88032LbW63(Y^@{BzX@J-;Hq(s=&F z=vfIo@*8r7bAexw-^GUS9&fumC10$5l&rg5BtYVP{s#V?BVm*;Kb8JRpB;Q13UH|3 zPhTta?w}nMZ(;vHIpAcEy~_uCRF>O1B`R7k3v&KGAL8#^j~5eV-gMWU8{*N{u2L=> z$|=B4(X(0T8Oplcw}dXY=aY50Q#`*k82%FQo80<)>9P+?JFwH_-OmqnvLeYjr%v{( z*w0}oM}&Xpp|-3qAky_+U4#FC1OGn-{@Xh$Nk2*Pufxi7_*+0f)s)OFd5;|7}`)s{W=It$x|> zU!z@A9wc4;RQ(G|hkR#GT-bY*ekoi25{*DTkUt*3T*LABh3d;byXH%OUwb&tz#NrT|Yt2oWGwH{<<6`>zMblo~X|>$dT#c zP+)vmX7-bxhc)}`=l31%MkOTPaftnN7UeNH?{Tfsd5hV5$vfct6yE!l%K85idms{l z5SM;G^4>daF-GFiuV51Rel6ts$xONwqw)%QLb|a;V)0#W>X-Z|-cP7rCq-+pzkENl zt~XQSL8q51P3NvRJjwd!qStF&vW|Ds?{dq>k;R>K{qt4@lW~mv%}4V!qUO4%(Sz@C zj}=JgXRUW5{rfIq58DOx?3Rj#PRGl00?GHeg-ycRigs9%X@@zQj@FWXXm&+;z4OH1 z=Y~DXe9#h-KUwdj9T)bRpZz}f-3qSXc|Z}qzl~{+>KoBJXup$wpWF3)s(*G!|4hEm zy^8uR`98PtDLrniPQ~BWdU$eNu*hm=V{KH@C-JU%389|A51KY4o0Q zzd7NBJ=yrM1jGgZp4UvyUy|>6&Ez~LnJ3QVd?uMk&g8tN=N8OrWE{Jm^B~_} z!npNX`S0@+Ki4*}3eZBY(79E7@PEZ(f!nOnS`Yk+XZ}6Qo%uQQzij?Ls~^U)J%9(C za{2Oc5@9&-y{@@@J~O_D@+s@g)XUA*uD7yd`MD4uKNnx6#mM(Iqa6`1Wxg;Dvr?(=W$HzZZ>3+g{%hZ*^?7#~_uC5S<45B{%fE?!%YBAl z`Z3(3xeRC5sf!KVPl%V2j(9W64dZB#gf3|iasFNL#+x*#^I(+d`vEOi89wVu;k@@M zU|7xG?06fmVE)Es^jo+Lq~#%I(z}e#w==!`E+Wr$#`8&6T(a`V?N-3#yE2zD-_Ys& zW9DByL;W+S^S3ES^GR1+vhv6MnbFxwJf}?OhmB5A7UIsF&W%PV>55BM{C8tT}myFKk%>OR+qSKd8d;e}e>55BM{A7(>P3w*2ZHnSfGukkOxIHoKj{I62TVVg_0r3Y zpQJ19WcddrkFFDsyB*o1_Bo>z&evDs=A{}hc9O;qu)9P9S7>x{eh1@On?$A0oBH?o z@J3U2@;yH41%HPs3-9ZdJ`Q4>{i_Kw-dp~B9cBXvKg;?wz1Muoi?18VcluZld!ILM z^6#thT&&-N_wUHT9vL|W=isM_AtLoKd(qF?hp``5ymGIU<&OP`g-J&k`#}ray2v?y z!mzz79PZU%G^tq_B@Xz|Qf|-seMIL=`&c`#xmg$>&eu)a$I=c!evQ9(BK=*Yt9@$u z{=;EODc13w-bU+>p@$RBu;Codm*qeB{ktiC`}tqsujST0tdD=UFIi7a+N;Ch$XEZ)RhI7by`@%}52Ww6Es-$lHBPEtmLua^l^ku# z(Djgo$zOk0yevQKH9bwwMBwiyH_$KqJA7|fzD`tld`Af`^vz)T{RQ=52a_^Q?v1U# z$J0k@gs)fTqR%S4%26}q=u=5}goZ!Ku;voGbab04rg-#R^IOfIp?=b@U(F2J`9QoP zQaFE?IP7G;_8exTQjck*FYIKw?K!R^glQ(fJBhbF_fmz2-h>Ylpq0uYEns$;)?B z;b4zqm229tx@e5dA$;D8c>_eQN9qcPaN`uDvtZY&x#+*hhe6=S2)v)yb~$yu6VDr| z`Qmwz`pbqJ9mAhL4m!dv@}o@OziGUjCzjjNm3nXm&fQBMIG@h}Pp^a_mzTd0a!^lv zO%vHJO(V(gKBJ@Q0*ik+<86IN?h|P~U!REmK8Kx0lKb424(uWaO{`C{PjQ;}OZ_}m z+|*{}n|!+7^?eWfE?pVl@(ev4nfz-lOu3Q!)E1_kB;Ttu{haj2q`nVj@^8)L-)>>& z$4(17{rfWc4`lKm%H%(s$^TRa{%H$)eU4=EAI;={Hk1EYCjavqPV1XlKaurhk#D%P zTzyNHqSo!O{@urPtk2H+aixC30*cDLq(L6vCiw=s3BOUg;avIIv*S;Gf*9XNl>0j> z=NoMuX7z3F*L2yBqx_o^+bC=WXV-1{BTesV`a_nJqUQrO=>Lq+(X->0p91gd+luPT zIHKjRYnr3sbxmKSzvV_tXTMz6w7}B;)#BNo;wHY^82Z?c;-*EG?)~mk4aZGivUu-z z^DO>rjPGatduH8Wa9-YbE$sNd&2S>Gv#kHRyv?z&%g4M-|6ZWsbbHT`_Cq^;80{f# z2DeOn=t&$mJ6?buwDIK$(?^{Ort9;_k{+K>9@_sOM21lAmBM$oFM4dTH5a`}^a*6>b^yw!lpr_R{2D2sE>QM5pe~90*e=;Bhl6?DFpCToJ~-AJ^vy1JcI_*Y9D%y;?xHndck^)+-rNd>i{&%NjAv z#f34m6k$sT$CCkDhiq9-{Jx(#(4pW_c*6SWayu8Xh5g*uks74AfjQp7{u`F_Tt&Ra z^uuyHUt#;%ex4%UV)|jZ?F-z(a}_Ne%bK8sgz=<1?6LaXOu`59q!a5u zfEZZ9aLXFjC+s9$@hs}Ecpmjyyy%d&XWVo^{g%58eh=YY{(QX~ag&eNH~-EJ<|`1KjpCz+EE8HG*Ts6dy(eM6PlfRq;RnTsTX_@x(j)Zq z^|U|0AIQbD;YF3hfdx!oBjo~>zq8}_mBJ(BYi#lv9-%zOCZFLE%42Nu86F`&;;B-g zIIqt#AztYod9Ko#oX4B0U{T9zt`n!fkBfE*d6T;?me*qIX`UbTft?d}Q7*Kf34f*y zd$rm}<@#R1erJ4YTtK{@Z{3`SG{Wii`9+!E=bswg=NjF&8r`hv_p|z6nzF4Y`ztY4xcZ#Txjh&PQmm$+!gIQ&G>T@N-LKWuO>YD4N_cR`Fmyl zePHNc@WJ&P;9I^S<2fS54fefGBmr*6tGQoL@jVb|Y*EAwU@2mQMx{#~i~9<(y%CnM>v7^6Pyci7qS ziZ;g22dFMVOlPZ_-tc98nY8RCIJtu$9 zp?fB`*k|?e^C=V9z{&eZ6GRZi`FewYA2xaa zXo4n}&oTLTcio;JZ~5Kcw)uZDA253%bg~?uuls&s?BA{Sb4vbQYd^i>|YSJk_p{PgvN!PYk}6yEFl zS+lfnKjjSYB0`Crgng9Xu*>APj&?&XdV+X26A$EG^^C|H@C@S-R=EWI-of3x5!`DEc$Y@Myvd1`|kqu)2&Q@oSx$4y6!RU zZzu<0*AuzuKQ;bDhCYtVV()RjyNmtU-)Rl~?9XARy@O)?-P05Mp{`#k>#nCE zpY*R`I~ZTLMSXwYsCyU({O;ku$9S{Hx`(}w@VVb%{AKDzjn|kjb%g7#-%WqmW9s`n zpu7IPj355njK5I5sBx+Jj;DU7MsyD&W6SxWb)@_ptOl_|r>PhD`x6Nr()MuRJM8*t zxliaiBErWDXy1>M|30qH6oo4;Y^4wVD$9!sBi`Fu#@wRmJ}-m)AphjNujPBBpy~Pg zW$ODo0wo*wi!wn3{IvR4s=ea+9_!N2z8fyXB4Io|;#biVJBw%?8^{Id!Cp^MA(#j}nk@yEK;@lR{K zzx$9LXSYgu>GJnU81=k%uJC*BR?QdoZKvPAQ+@9jfD`cJ`35r52TX240CMJT(K2ET6#*in})8)z6nNUvDEbDYN_JeYIYE|0b>_^|v z0DTjIi7vtC`U&X^Gv|Wv^fcm+WaRM`^`7`1K-Ba7Jl{d{H45WM*Y;s`yuiUnuYn!cKN$Z z&uF}#4=;(bL&m{MU_%NSZCYXHw z{LWLqObLkZIi&m@;P715Q>;!ja4qQ^m`@-4{c~Z(VCSbNzPUV}L2e6@B^R}5M(D*4 zy`?h!B1u7cB2I&xpPr@p@}s1|#g$ggWLU-}IM>@K_m!D)r)c^G3Kwn_1mgUjIL2?_ zL!5YTYq9%_xkxrU#rgQ<^V8Vg#|&)+rr)Pt($DhI0*wf5Y)4aPQG65e;#@Q+&PD7P z$$7{=mVy_x+MbilcuFQ1qGmhv>b2=7Du>=qy3@5 z(QdC;B>DPS9{S%5fgcQRdDZ;KuxD@cOkn}K_%2Hchm z+<_W!Z_mIzT?4K+0~ft8czxpx+>9D9U+(eu!v2i&)E-cT$wbg*TF$1@@2HYDna9eA@U7vwFR0Hnn4BXKga93vF zMrO`2`8YT)1J_yu?y?Nr@)~fh8MxjWa2IFb_SJx!o`E}31MdH^_bzZ&U)8<%Z)P~d z!|Eg?2MD*wnE+ujcI-e%LQroeU=pP54M{jojLx4|5Q3U83<+$0Z8MN}s#o%+@lj?5 z0@2<|+S*`i9ol%W=)GFhUPb@5;;W+7mh?$|<*x5q>$}hI{N~IslZTqN`vbGj-oLfi zUVH7e*IxVGoP#?x4BRi~;7T$qUw7TCvL&g`i@cmm3Zs9O+|CEDUH4NO}=iu%c2JX8#xP!yMeJcldY#6xX z0w?>l)$Zf|Fpj6vDP~{tKD>0wL^S~8{?sXE|8iWDPBD8rzVk8Ve1rFU4#ERIu6)q! z$Z&_9J5H|Tp^XEVcQ>-UU8X*(~FwTKTjq}#HCYYFd&Zm7-5%QBjId) z?U69-*QceN*hlaW8F<;}VfHS}>PZIoCV_icf^g~eCv?0M_fubF@o`?#imUT}-~{po z$B^GYeYN(yAK>tP<%NR(F`L)MJlJN5=e-vgKW)BYC8Cm>Z`ragd*8`TYj0YK_|5CK zuC8_83G&a<@r_~VAe~o(j!8DI>)8a%l3qTR;AhJ7Wx+GjJSpH&uGb5`!1eFvagRwp z|4$)*A4iw{9>92S3fCDqZ?!`R5f|sJc1-ZF`Eh@Q&mW<@_>wK}M~C4X;84!F%C}=G zAMredWvl$#=J*Kz`=H;lPl^-PDLpt>sd6Lv=jG>jYvj*)JLEqVeD1e#`w^Gc0L;L`T1zzDFMHHR6iqU?~;6q$=8gp z&-Z?m2{I`lF5TAQe!Kd$^8HQC_j8sn<(KpEtF2%u=i^r!|JW|WYDJrwk6&JuPde>7 z%YWKZiD~5)fDG`$l~Ca^bu3A(Q^C^8H~; zUd1Q0E6D#-GNnJ*VF3AciLk=@jZn7!Bs-4Bydl>q2BG)G0pDVY$Gi&Y_*vfL(kA78 zDc$3e$PvD{UP1lK^$PAU>+|)BxNhL<6|R4~`)qExXG%%94}6DJ{wwPTT!(1)?>4{6 z^@Abn96J;&&No9YQLm`y|B{ibTKBlT9dhOUdMFv5&}nuG?VO+fx&ySdwBP(aE@uA3 z^@ia4)x*^$_vv(7cM0)c7h9KMy8}P^=hR-8c-PI)80OGE=gLpcYr;PH!`ycf1LdzY zqZ;cHZ&5@=xt{`VqlV$Dk+^uT zHSN@Rugm5BeVY2NzLNB6PnWsZWu8D35*t!7lTRJO$WgWxp$DhT$ zFMWTVS;ttDTgPDklj&F9qwvG5W3c`*_rA2`_;-OYU^sak&Al(Xa_bgfk}(C^(}7(4 z--{j!XRI-PpMAahJXg`=O_S?eZNC4U>r9;@4v+)%XLZfez8j}&dW7pG+;*YWAKztZ zn{SF3-?2|=#;mgye&#IogpOb{{KkGdS$9!FL!Z5~luoyI*5n+W!b`uRemtMqchJLjj#t)M z9Bz-t+xuc!JB9Nd*f$OSz=5v+TSb@gtB`!p?8PDUoZ<1Lr{rPMGtI-KV}geBbky3X z{qYPP#k#LJo;*L=J)Ze@c$oQ<9}7L6bhK!A2p#>#2dp!rJ)b!x*JD#x1~KkKd6 z!{pmG53{~D49S0+)JJyz-3HJ9{USGLzm38Nd7n+`C|h$9zC7Z5xkuvxH(uc4`=fom zPWM!W!*|tZ_rKfHZCRr}&xLWn>2UgGrrUYxqZPe^`l;w0+n|%qHN2t6^$0pqaS?x= z={xRQ$G8rz)8or^sfN?JmOkCM)XL|28v6h1xqb#HAV%KP+|j~i4hcd-$>mc&l;`0Tx1 z8#TU9fByX5C43{lv-LI{f4?iTIePxuIBX>T-Xi?vyC@Yt7++or{5Ucuew<$3t`&Yz zZlHf~E{t}F+|Qu(zFvd?nB?4!hU0q`XGx^E!6(dzynR&UE&Fb)o^$K-?}BHK)_-Dn zGVyQMc6e&Y`TN}(e^TRd&g~&Vl`d-6^sGPVL)@IlcNDQr5aFk+_@alUp^EF2{N zJR7*hcXH=hy;R=SHTejfpE2`4;r_*|RvSF)y;x`cLb`04$w6PW+;e`7{bOjpOcBU? z_69%KCN8r24E+P&Vb9vTTVDY=*{$h{b?-KL*m#EFdEDAXTyNmJ?VPXW`-z;d<@NC>tgmE9{|Y{Eo<9s2 zzo2j4SjC5%3=ik2f9B<$pyi0`e!RElewKThe`mQ%HOn3R-Rm%5^^0;}TvhI?e$L9> zk}G$+hgt50xpH^ZEO*vU#yBQBuHks%!&T$0TdVot@16$A{q&QTFXevv2@g|Fj}M_E zZ%^dMH7(MwpgumU^r4-F{1zTxXy4cPI84^Z;M$u1Qsd(}{`2g6xM7WcAMH2~`P7L_ zRK|6mRJ`eVh6nT8aAn+kGfB$l;X?9SjUQaAUc&Q$VZo$2#H7m(8r=(QgMGT}fQNse z3DRYIEPTN~Sa{KH3-3Lm{){D-5&AnIGSV*JJjFs$%gOo*d#!z? zK93w0uhH+k51Z#;+a@D-k$>N35!;Q=QmQ#o3hmxs`~6v6jjn&Bd?-oL?d5NU&Gd$Rrwp`%z^XP+H>^s*@Ag704U!86h(rtD) z=_la09+*w{5lIKRm8@jEbX}wiVa~5YuHeF6w}bYXoxuAynl7{NZAGN2)WcQtEuQUH z<)s<@CH4I(%`e@!$^6H*S^gpym>=mBaYDqL3C%h`$Mc=xapPapU4}p0V1Dd}JYUKu z%a>m#3adeXV`(biT!M`Mpqlw=TWH+r#5awOriS zf_eq!tevz;>I-!4l6cf7?(U}^=Ke~~KO!FKdB01%fA1QrD9$TUuSNTs`z&b3a-Rk5 zT#n-6h7l#nIGv->6GJLQmkPS5@%&?M>X z!{%=}q@TZdhD!^(F6=Ej@0^rTZwV9-Bp*}Qr! z;@>6!JP!ywC5g-C_hIB`;!^YeF2EzdnUWvxDdfEkJg?33`P`Qm?Rd^#+%HJPasC2y z2s)($Sp)xG&>{1SaK(wgtUmg6<&VJU`SgB)&!1E0d^_+>lng?*@gqIMeC|Vt=g=ow z!C4O+_i3L36CFDJ{22ZDIuYIxOc{G)X&L!Ne9&kIY#?Mta#(w zuoxpQ95|wJq%*EJxSWA*vfPAGrjcC!=09Tq?2y)nT9 z%K`cn_%IFtoqR{7kodlo0|%APET6tAaO_uUxAVO*(ECg0OZj-7p7)1we;V&k?E zUYU`bxIgUyMWf$$i`<}}f(w?N*on5V5H5E5HyqIKUE8c93>OPxz;8I{@n@<}y&o33 z-S$?~*Ijo(JBYr;#J)K0ui!fc;jM=eoy@qx#>ZGcQ2i|SB=j@r0|f8)9+H0j9{DQi z$A8Dy}&BpCwPyE4x$&3ywc)9R{XJs?8&PU)A`BE&rTIon9c|A=! zWc4-4Iyj8()LVVb|Sm7q2(Ih69#Q*WD)Xk-yH@ zaKPy4y36fi=UWkiFRqZBP`a9~(V*DdRxjx_>otP*cY4iA4_kew*K}IgnsbuQze>Z9 zEA-px{B0Ue7vCJ?@gtqztKpvcTh&kJ-{bx+_jjw0^Z!PP27CF=mg>cGLATM5@XI8; z*6k3z_eYahy0qB>Q7;NS9O&Ew-^5MuS-HxE2E`1$Rm$8$gm&`B|5fyaq=Vei?khLc zCC4-NLE38d!25>MR^u=46B-oRf#W?+VY!9FiX>;=*#HCp>QHyH;8I2+RHZ z<0l>7nrP&Tf~Jz4g>APPVn_boTA(qJ5awT-iV@08<0VtNSpz~Mq-c-Wq}z61U& z%EbbWVmE>^5FLLgVjBF;1TIvMXP?rL^@>hsjRZ@8=3_)WPC z4|uy8u=uX|rcaA?pI7=q|8n#Dma0GaAWCI&c^m9DYlhO#n>>~4yr1hcILHCyTh39a zTtS}SBVj_ipn29K4@*T+(rTwN>z3A}ED_CjZv#)DEi?huq-*vd6Q0eCL7h zRmeK4mSg34M&F>zdDChOc6pHtF2HpdDV1m+BoN&F@zfG`koJ2aochJN8 z%=Zf}rO)|8J-=0|3;BSKvN+HIf8_%FlJ?r6UhEeDcY!#>!Y3ShJxJ!tc6^ccVqy^W zGrl8=@xaq0PGQ9-9>iDGr_%vCabs1}4M-vR%r>8Th46(_9 zKj`U^4~ok9h_Tie<(2hDd5-gELKj@VKEnZP7ns*VIalsbFqwA~g&;0nc1qhG_=Otd zyI<+`)-FhY7)YGoJJb*REX?-7`8l4qTQ8X{6nvj>pZ&-?&YEiJyhSKyN8^hM}iz2m%M2&%yj%={&8zh3r?7Sq1i>@f>Rn! z7acPH`h(^#J79k69`)Id=17+>S*X1SpZ)uKkvB+Q?w+-Vw&wdJR^Wvua7y-189r|> zu$wjB@6(5!GUu4sEd!=+ipfq*ANCplFpdUaIj@j%evSLhdA^nV&3WG>@AI>6CC+!I zt-Gz9lXj^e{2uUh`aK#h7OvO)SGqjrmFTr=z zFXK_KReG!0(jmS|Z+SfERCyF%0E%ykU98quO<)m0RQ`5`22`vZp`FKf;^iK)1;p1L4IE3M-f1%GI4`r_Vc>dY( z=sofs?dRKHg57U&8TPnbd;2LQe$K_mabaMB$$7nGAg<`=l;qrW95;OWi)e3FzR-V6 z<9QC7=R;B-pNBrHkJQKS@qRdAK1vq$dioW{PwGwHo0z)3E@B^x(qsHUxjrg%Jgl*( z2iGGmzxe+0HmTpK1`tZ_U*`4d{mo0^p3SANl(wzhg3Q`^Q^?cqO^{%7(3`X5XVpEM zx$mv=9ejb@Aic8y2zm<%zG7X)?7NZf+5B;CU&938};rKk#J)6ff=UI^cE_mv;5;1qfvY4;J2mQc5fY>aJ#W3#>>#8gKau|{&c!|D0w50bW}MtV z?s`(<@rmp2)PvNY#lp*s{*t90y6$jTKg?*i3?lv47<|uS%oIreQjTnW#dUs(S2{;I zFHSz{@=J@9+x36jV#}X&BR^N!$iBDJ;mQTmb!|%wp5sgIPs-DopO+tm@gZ(d*3RHK z9|(TqCR5s-sFyE(LF_J5gR)*^tr~F18E}9H+-0(4i1^DE|LWyyeVq;U)p_|Ft`v}D z>57#rxZVc1lEh{6`4sZ8>Kt)heSzc?=V`ewp8H;T9)SDvM_5lsR@wajSn|)WkK;Sw zP`){mFYi%GS4pHelw&FU4BV3_hYjoGeKHYhe3$VX_ylJNZc2K{7VvFi|_A|{9mwj`9|bY$%prQq`sep>)W6s z&;R?B4qFeAd1log;eJJzDd)n7AL-L69U6}Jj&t3AjqwZlKw@c+(eBEw`)hp&5bNVX z)ZY$8tMyuvpRh0a{oVNPbg|yE-+xQKNBtv-Sk9$_2le?CxsW>U3vfM~wNnp^J;Hqf zoL@t_dnKOytK45>cE+AIYs*!=-hFVKRHWq&|f>q&%C+_3utHtXk~{{Da-&-V`V z!M7GM$m0G0?yKVd0Pd^e{s8W)>N7cH`-%Glrj!(M9Oq1F@vx7>rnGz5$76o~{gC@> zTz<0q{vaoycS%NmIF8TigNGnLotjPTr=Rb>F|qqj0%MQUpI%|*N-rvDM%jLQp2R>7 z;UNF&OZ(`RAE5tp|FycGe^TPJaEBf4(>b_qX)Cqhj^yCxNuIUfp3cENGYs6va&S)y z?X~cIBnQ_mW6oM|f0%=tCtY(bxDVyvo*4%2139=Sr7x+4Z(k0sTl(l)aC>ra^F+{U z!M!I3_slSGkL2K<6j`W+@BSQI_oUiz@65r?lOabfe0S&Io*4#iXAbU3QSi0!{YDP1 zTbBH5!EMXI&69IMwcvVkaL)__cT*1TNm*^Gh4023T(`8XT5vby;P51KEx46AxMzle zyFLf^q|8;+!dK40b<3PfEx6@5xOp-KRSWJ{a&XTK19wFZ?#W_pe3#|mx}~qJmG5hF zaP!3At_62-4(^#@;O6Jxo|FMYEqrY`xNccfuLakVgPS+KHr%Wn+%v<#y)p;)N@0mArWOLj`uOAj~&zTW%`)g>5rNH&i%!4 z{bPY9lYJ8P`h)8bai7KlF_OfEeTJuNh3{`#aE8ZQK3ywLX?|R{2p8BaQrLG~mF_G_U;bT7v%LtKwy5QYG0tQ)1-?GZ}4@RbdkXaU#CfZy(owc0LOR7 zP;VAKVf^v^VcGHN5sXg<3=Y#lCnTJFYJ5ljCddhX@0`iubhp{~l2Eao(}A|Ihfrc}MPZ!#*7;LNfCWmM*>5>M8Ue z(EPZ56%H8vT+iv6EXJoe$YGtx8Q+5nz8)6#I{ycZFI@|MQ8~+f_uzAfq;H$|7eJ%) z3mxJYl20Qj>t&XIetoXV#752oIGuebluquW%hn6_MWPG1l#Mf7~LgzrhO)57u3))Qy+(KCokZ@Fvx1W^T(=zr)5Pr;`BJ*x zWZ}Vw%-<^Xp}wZcn6GM`dy9RiJQE&pRr_%qZmZyjW?LC7U6bSIIZ1q%tP;ipPt`iN z<3W5?eG4pqi`cu8-#TfvgJPwovCp7AKC^!6rx)W27{{=L`yuHgFj zN)cRf;a>B*!YP#pv{#LPz3bn*&F{L>_sO{&4!YbFC;q0UE68hfNLOr_q3Jo_j`@4k z59=@Px4c}zlED?`pSHdT-)ZoWtE&3C((#D=YJE9;lf(0zDe?iUn;af^s_H8fUsYco zPdVK>-s+8VjD7cTT$gB)?{F*UnxAgpru+%}jZf?4J5sFdvG3&uAzU2zg_>&WKuFrT z@23g-b~~SMSD*SM>@~dX2Ts2)Zh_Hpzu||TscOgbES~#y(0`%+_6xD_SLVJ$e`z}t zx~tmR62tRzzc0@DkmsM=TdaKIJ`$c|8NAWTh5crqm7nP%>%Zc8y>qM`1)kSC=cMtQ z=k?C<{%D8cNzXZ6RiCs6py&4p2>O>_l_N&M`$^<`SmH6ST!Qa%)YjwgETS^R0nd~0 z1rHqG>BD|9fE1Ds{{->qzYmMgdGvDqFGTwh_9EB0InP=gX95j9@oWk9q!{%`KTEx2 zz2t{1c|R`jpDE#Dy$p85MLU!G=zTv9-+85-70=bq)@1Syr}@R<&g)<9dT!5A*=l6a~-!+Q^>mKCZaSNwcpRe(t z6LJ~(W#nr+(DopD3a+51f05ttb0U+5`3* znS9tiE|G7Jr`*tBe1mcj2Y!7`{6aEE!>8rX*$Ni-wTBK1hj>oyH2mpsd5!1SoIf2d zukrj^jz827!7ZLe$x!+r33B|#`-XWAkM>v7I)iV!_WQYdw0C$rW`u1bvyOG+@ z;}_RzKjUKi(fNpi*^8hb?Y5A}WUIK-%0p4XUifp1hBk@k7FqA>L_vuga{s)v(t=sjZ>+>1y-rl9%AfIU0&vJS8{SIC2F3+=Eo_!y4 zmpo1iytD&C$?^Mn$F4e)=W>HA?ubj5?N)g1dkJr}d8rWJ&oFz3`vcIwEfN)7Yro-5 zepdqUqP?T0Ij@CzncvEN7xCBr5_TrO`_ey>_p;o@Mw7dd-p}|}Za)|_Fz6&+S3!!G z_eZc^;`# zjC;;_uXk>a?;e@Q2A-B&{QFzbE-inaD-Hw3uNm{r9s*v_Rjjv;3V51x^k9Z{SUGfI zFC@QbeCK}Xfdd-un*Wa;Z*md_tUkNuTlrb8;O#mL9I|xt>|KP)JrWiMeI|igF4-q& z4H=(5Ppa;M5 z@7SfgjX^9w@9UvnP4B9FM~&q7-NpK7(QM-W`@Ei`oPb}LU&r_pn17b{!0ZI0wzu$27lkA^{i|?fR_?h$6gY)#Wws&;> zwd|#Jo1qcRL65)^F5q!FanRSjx$oynJlX^LTYPs`0KAvyGdNFJoG>2oN%!RQ)vxpi zrf-n`hq?aB?6p@aeBeFc4;z1m*hSZXyP)@e^?80Bx#CMY(fRsd$-Zw^Pv(ow;0X^| zZ`6II#iwsFc-#wv8f3i@Pj`o>7wq=k#!GV=F4VgrppUfu%{Mso_Z_)@{Vnnh<-Rn> zR}rf!zMAG%Jh7joTp}Mh+E2iDT_zvi8v;1c$#a3Uud;ehjxEZ4AiK2x1)f!1m7IAG zMHU|GNW7N<@P}Siksq$#A^uM?^(yzJD1JGQt$r3C?8)qXCcP4deDw-LIB(5;uw38b zK48jWalDP+7@yt0DWoW0UncFk;L=)@d2`qXg~aTpu%skV!ly;s%nn&%a*_JFX;@-% zkuKWc->n^|i&j~=TyBDyn^pHuqCDC4zg1qocJZTq*WasnW;9#J4f_;y(~bl`E47kD zdEh-L(EEb!S>F3rNdDO9^nNz<-=pEKW*c|K@kYPZdsnmF{}jh-91r&I_H;IGm-iqH zuiwKI_->oq>4SGG8{@fAwjazFgQG+3bh9&y^`^&Yr?VcjcKYX!R^~HDbDrnZ3U78Q z@8vA|eIwxKv|Rg}tNebqmQTN1QSi67kXSt19rPp|+V{3hyMsKwLR1~n@5;r`mJ9|L zTDr$aqsa~`ng5Wi*RO>0Lw z=kYG&XX01WZY&87-LN&dSLZ zhFrAUYS{}1a(2N45g6b(oQr=Y?B8zGp7@j9AI1FN*K+bG!;RtP(S^Q{{13D1erEH3 z@6-H8vX5XoWbK8mQV%29M}UWXer>k(!#7E|$M2y9zt_Ee-Fk^iR&TlS=5(q6iqrVb zt1%^(+`M_^4Ylp4n>USQN8JFrMqx*NM(BDm?5GvU|EFk2T_O36WJh72`wL-5p+0{W zcGUk6{r2-leks{e-;;@)IPdoou%pHce@C*Tt`j-__hd&sP^@i7&D&CKN4-kg zUDl3zOYVEVe|fHU)T6n6G;g21RP3m6$}cgx#r;RJqkiWzW3!`vTj48q)DLs@f06R> zOyy%OJL*`@UizxoOX$B(=HmYu{p@qFqduOa=VGB`SiAn`&5m+<(nUwquFCHxO}8Jj z`0X|?!2NpZcB_wc`w9IHekNz^`%6^93Hi_Y-R%7BZs{Mh^S6f)9zY7)hjU!Q;M-wQ zD_no)y`J&@qrO8%&y-U4oo1HBW8Zah z&ovtnf|1Ad5$?m~e#@ui8}PO99WD6{7v|c0&7jX4V80pQxPFXzWjKW4xQ@GWsfEM& zW@mE$f72Zn-}VMi=c>0n!NP>&c$fKxb8R45oM47#r=&xQVnG-oj^jh_7kx^|*6+T5 z5n;Gak1tP<$w_g%4^H0swfb0ekNN&iA@?Jv+r7TFAGGh=t$v2ei|tp)%8SZJzhrZ&P0tr?DQRX-{cc}Z_IhNf?{a;1${&(WUneK%o_)f?rj>vB*p@?yz!pNSOz z**1^od7ZMKZwO}_-Z01fvE#7;eK_0b<$C1U@%VlUQ_o2_*9yS(1Ga}QzH8_00sPYP zc|7;E@qIGx>*$nr1xAxD)R$C=oXe`_7x~L}UXjn;Uf(Xq+f9!3+M%pIyRTQ_^7Wt9 zUyx7K|L;rIl*i|~&uOUMHF-+!Gda!Dah>Fw)vIV1S-K88T}9z3*Efdh)osRyDJ89- z(6`CMCWoPKgNI$N`c`?^xgVJMhJEddC-7eSMiY=uNe@iLaTrYqeL^?F)FWXaF~0b{ z#FRg#E00^K=?V!(E0WIB%X_Js4)aEEsq?Fl1PgCJ^%MNa^O@_9yhoOF5O13bhUJ6w z+#hmUzAgu}oWE)}f6)2Jb6ey~+pRgiDmNq_hmU-H!0;4xw!5<5tvGJBehB-le0hFS z-{$#9K9|R}YWjlio)x)t{-@V3SAM43mzv+&VgB@m>W6to56`m(Txuh(TxWt9e7|YA zP6SUJ_svxFmapUY>1R7BmF$4@t! z-b|^tHXhM(4b^jpHU7EuM@yvWmHx=eoo=xDur;VbNy1Rw^jb#_zphG zzxWPcv;!`-d`4O?Ju2^OL*Kqy={&0Kzg%~vCJZ}0{~q(%?z?jPLXp3S;j?WX&wKOB zbtb=Ir(_^5^q4<(JZ9*Ioi1lRUT&cot*IS5?kJ@(vB>`wzK4LFH=f{q%85XU*pH{dA{hpR9Db z4+s3co7*kE$?LmqA*9CoQ}|_U7!o(=aIrt}e8WDwN4tm(_ToBS?uz3~&yBV}DUUP4 z((BK#_Pc0;`K>BOGXApusUkP72S#a!W$RJpreDcbzMYbGUdzt9L_nhaOetxCC_iz& z!SrHfzTvRa&3mgW^m>2c`{$mwoNqSzA$PO0bWT6ya(vYIH_CGrS^nhZd=KQ@>mT{O zvr5iQJ__>sB`AvedV0HuSx+q<-r#hVJiN-o(>!eH(#UU1$NNWlFM5UFmM(vfFwYB6 z-(y_{^&V!MzUFwcJkAVPe24zvc)47-KpBkq4vjbYE*IJ>@hI;c4eI!_Lw*(uW)E?H z=~jsYhpsZSCdtO53Dt_1CR8&X6x4#3DybP?%Ea?t1C%~{4xaY=R%x!FbEP%gWac^v zh?^$i>^%mgf8{FuoNSeHp&Tn6p7$~jdU^33alW zdy@498}E>g|K#rr%U|rf><~e~cwXC!zi(OgcM%bUAD)+8~%7M^bQO2UZ}x+7KR=Fy;8F4ui|SbB6-dibs9didQ{e0}nV| z3%-2cTFQIB6CfZ}GZQcvH>s?y`GPglOdzC(|q94k94+~nW6t^huu zJWUQiC-g7V!;|7_uZ@)c*VcBf+tI-pwapc`;Fea z5h1Qqe9+7MKqu<2kerXtNvH4|`IjeNVeQMyO?a$FDV+kNbmFoOaiP!o8|_Bt^YjDC z?>H|U&*Rdb+il+mjLta?m)3(mIsdEW7Q0aWnYOGT=eRd%81oGs0yop=4+mFCKpf;z zKE?BD>xD?=(_DNNIh<;K)L+wk4Ic_6F7^3(kt3xmz0c+3KBKo&kb^$%zoK4(Tq2*i z-iSefxZ8sHVYT^uSB>{-L7yleFFUIFAq-bYKBHltM{n9;@w~V1HsTwwc+`v5|Lqn| zT~DJOXnirChwT29@PzTT*!VV0ke92b^9)aReE|3=SCj|z-zGoCz{+~xPf)BRU2J#= zkNHcbN20adK29to*DE0Tbo14gj`s(!9r67U$gQSd;^#^%d*Pei7ZfhG^nAAh^8)$x zwtcd7mFuOU#Sn0vDQz|VKIrsc>fp{Gy@0Wf3pK_WV7oEU3@d2ZU_Yv{l zF7$tJ7$3dqLXoRJ3xh4kB%Pcy*Zk5}lhZJ8((>P8aM|?#Bts7wck1_<=h*iwz9XPx zr^i3&ehoZJb95Y1I*vFUCO2W==&*ErL+LnVbXd1sRjvcT_k{D8d~vz0#g~h6^z26Z z0|ykpoEJAbSgso7`K-_r?V2eqT5!G_k@`LTvX4gbRK|BZ1*^xQmI(aSpeA^jf9HT{%^ zd7h5v=i+_}=QG+L?0`A-59a*AiY(9K0qu6K%P>YRM>kD}6rT@wEQ}KIw!UmIr zY`t73`LW%{cK49wo7EqmTu`llmP)#?|2pM&7+9q~&ppzvwYQ(d4Pw(lh_IY2Q%%2*VYUuW2}2FD*67 z75183XUlbolq;KVTFrFUKC2#hm$N#X`+G{r5Y)QxWT%{aV z-o;jaz6Zwoz&bD(nSXaT>?>)yCeitDVebUPbFGE*_i=>BO)rK6*3R?ybA)|o7(O4r zg*~UVzBo?fJy+Zh6Xj!Hi>6DjcYWNt$-;hqm*a`hpBR2`-(ojtSoFHJ3#or;va*h0 z9ep+)F*x6uw@dcQRAA^^9@B$SOi#IkTn(50kfhJ+Bk&tNANBTD3Cq7YK4ClHIW_PB z_=}C7RUh`7h|ja>#tlXnDo-5i|0xR!tQoK~*H}b)zWG=6nh(1MEs68{Z7V>9j638% zP?q&pMdWP)kkzBGKPgAF)2P>*z+`c>3qcR?MZJ!X;=&WVl>R2~ufqct-*(=2wBC>& zu8@3N!^QFa8fL#i{Ve)W@lcOZZsPdVs$1|(()1~qFOKuDkq1ek$d40B>|2M`b`z+Nk&P%9XnQp`8Z)oq{~Noe}y?E^)sUTp_8m zcIf+j*xuzmcdfVE?ohC}-;Vp>ZoA#%A3v<=2ff_4?euuxM|#^13l}HY{Mp?uH=W{N zDxbpsW19ZvyAAHDE6u<8P7k|&ySdlHCG&4Kwj@`1e@=Ut`_{P6h3z}+v;JP*-PUrY z%dG;#KI?B&-jlfRgwm7pKE-{kQe~CQ? zw^e*d&dT)~OlEE}KYxEV>0f!Pecvh`%CXYnVc&H~nEEI?FM)C~9<@}po*{I>A--xJ z%;Ui?)X+?)uUxqD$AD*l%Kc>+=eB3|m2$rs^%K_*v-fv^uj%w{Di2vZ1Ns*0&iVf7 z8zrGr^2x{F-&Q-mSK=wJBlTPVI1Ju?Ne}u_e@G8L*6KI^R?_qSD;f7_dkCC=G4W3L z{$;xHq<+tq7a36BL2tpu{?PR##_8kdi~Mf}*Mx7aCx+m`e!P*=KPzJ-c^|{m9~+Xs zbei=4s_7vq$lvXmJ`L)AruNgM|9hn0VDx9pe;DQ8X?P<2HO2|wmh{;onzt>5tb+zu);%V?6)GT>6u>((g6C&^|%E9YwkQ{qErVezN80t%0Z3IO7w! z{CCw#SEJmAbLsZfN>`)2AIYUVSR-9{+~uG~zE4(_+vFx&?v5JxYL)vyj*jIu(uKWN z?^(Y0<nben3Wt5MzuQQrOus?WlJ+5bcK$%q~il@i-e+|MfUaNGyW z`@X|~jZ5M@6tbg#>aW6(0@ef=5RI{z>sJaX1OXh5iza;ymM|Rqk*2#X3_C~RHamXrmt|hg>SLC{ zHTgJ|^^q>J_Q3m95f8`vl1P7~%jJ*bm{|WTkKdQicafS(imt*ho)3!o?6&eZ8=TA^ z8@*osrp=yS^fQvsK4&`|oNC|6he7X8n|xds>BJ+R;y8Y~%G+h=JEi%d-&XvOI-Hl2 z^Z8eK{e=EKMxV=J)1#I@`PoMP8=8do*z?_00dYPFLt(s_;YTA-_fb0UGOT!j)~^h!6~4~%kuV(h4aV`T zpKFWq#PW;$RORRLbd~E7-m5zJh~bUxc(sR{&5!N)MyJE+=6p@8=PCZ3^?awLzsl=1 z&L>{v_@_92fk(==wbki)j)dcXjx(FOEgj#JX?m06VSP__JYL^2PBs3zyfy81c&`uI zJ#F`UIxk<_PAdr8FZUIS{!;s`?DzYY8_!gPT0dX6m`CZn6A3o!+~h z&MuDA`Vyr#U0hPYFkt#5op1d(*MrjemS5`kYoHv+C-rvD@mrYHu6RT*Dc&&47!ltM zn`Qa%J+3gz?M%KKHtRZ1?|NiFgZiET#^Yi3D*t|1@q-SCT0AGZ=#cS0lkTLX&b7#~_qpJnGijv_tZA>;jj{gOx=@a>ZCpaZHMc3_?SCqHDJ!T8|( zOj|d2*!kJI%EQ)RWZvGxieBin_8Oe;X{)t+-lrn#76$M4mK7&ntqIpl`$bi=Jwfl3 zBpeP{`hwoVi}r2h{fbm(2fp`P{$G)DA>1|D?(N}u)e4-^U4gTPM)?kX#?K18mAe9O za)R}rlH~IV_up}`7+2!5_TzH!-`|T%H=fe+rW;SFpM^s`a6O9W)L;&LEOXw!)_KW; zk}hB0D2M#h`uv1U!Bp=5wsKVVV|)Ga{O@$*UvNE{PPh8QI(bR(VLj=%dixLvN zzF$Ai>)!5i;qRJ-J6tY&UrE@Z$z(ijdWP@0rHl3$U;G|=?pqK2mOuH<`j|2Ku)?F9 zLe8^o&9)x^VPTob&i@&Pzb7r95$XZ;#r*}ekBah6102@tAeW(UY0Mw_q1}Awe@XlH zbrYV$A^kHZOG0t%4>|A2^Es5;uE~C%zn{|y{Z>C+lTB`@UqjSyR-aw7>|ROO!Jwze zeh`_8V|$MGXS7&6*XfHBY#ugTX7yF9o2&p~vDIg}-o6*do4(_|3yz0T{#&H{MZHB4 zJSu8>(#v(>O8L(={FgZ$x4ZA-JK(!Z@tJ{9NM5D@+z($X|8)Xi*3|^jkGC4#*?Xu> zF02EgeLdJ~`9}CgD}RM<`yTn{{qP{|oCWrs@RYwc+B;~3;yPU}%ME@$WwpiAeq8VU zYulBeQuN1__I-{_BKHc(L>GV#80n3-e+hfBNaC5i4FpTaUrvI~@2eMOS{q zXZhz!x^pF59A^ZC)yChs0)%+ptIK@_pdXm2_fN-X@a>(W+O2ZH zaU0t``OJQYd`er7ncPf2YU%o%Os7>3)u9nEV+Hc7Z$y(FE7S{FJ+E|B5d<}Hrldih0r5%2pT zT#KIH0X=@MFbr7zP_L)WZ&7@Kc5Cwr4?8{0zv^N2w1<-WxG9Y6Oa-tYM1zQkQF7e~zB?)|XnC-6b#3BD9sJna{M|9@8BPlkAH zZ}J26GDGB<_noAZOfSLixuW4Fxxq^0>|>uk$a`CX9~4rrW%;^C_?fkXpud2Bp}+&* z$G6(9md}3nNAFFiECp(ffB(mSsJ|!O{e|`Eln(t3_}{twz7q&s{SVi*avv1p=YMqk zClLSRZ(aIJ+$Tl7$n!C*SL(gcXL4NGFLlEB7tcFcxhv;$TyIZlK~|OidyWQ^C?~C! zF3L%(;SKY_CE*j>kB@WMuEBpmq^(Dljx67F-LTc_7jPwovwF_L9TWb>eO3Yo$x2%f zDn6`>3})?Cf47%dO1Z;q`6w>pHrBW%#@$h zYaIVZedpyzJ|%$z{zk~lYD>@lG@lRe=ioYBQ@g z=ErjJ{R8rGxqWB3vh=BZIDIqS&IvA$bH+_Dx|%J0Ek2W;FM%G{_v9z*k@F(yI>Uqh zKbtS@1@2!%`TpgXDsmC!m-8caCWn-7z@IDnhx^aD-<5g)eN+;N zOEdF$h{@7F3Hkbb7ROzrFZKJq(|Z+7RX_7pv@4+(^kvuW5Dt5--Gkn5pCw_w4-)l} z?Gvu(E8Fi;(Pv+E{bl)f_t{%n>80h?{TB6mrj*p@K1q~M%bAhe@lsCKH_8WA z6ApA(F4(sQ3wt@j)b3jyFmulVs^IbUa* z(Tr9o{1mw|Ja$hN_`Nb$-np8O$!Cb~G+4ja)%>qo)_6a(+l_p`0r=tg&Jx#=x|*#& zPJJFP9I$Z6y{E?eS+G9}a?bexgrOpMZer+rYBmm@D|`kV*O9XN|NS2y+dB=r72fCs zpIk)=_M|uTA5uOP>y5AYesPYE;9yoRoi&=C_E5H6;{)U!4&$+Hnf(~R^95-L$Y)nB z{(ETuR$WmKKu;h);FF(9#|=`RTKXQ}0S9>G^OeK^0e_Bh%p?Gw;-M8fD_ zQ4X%t78{{;1pj>pRnEX~4g2_n?`~s07(FTELeMCzc;nlqT%hoLp9blHpZBy-A7=Si z68@d`9^W;JHlAbpslA6FvIoa~HQ?JnP8NQ#fA;&cL!bB0es6Z@^Zt1%N)LGNpS@n# zKle;6`FFGD)2SvGaejCzqzLJcDv#5t3pHG9w7v@a>roHn>!9-~tAAc0{AGKu^&Y}X zk;_zJz|F22`9xvc1!Ju2tGcU>l*?EXL1f79== z_TcZMX7!wogEQ}UoqD>GbFKM59bZqkaXRuH?)|PWX2;LZ;r*@&=zsQGJEwl+{1@#8 z%He3_YCN;Q9jO_PaT~ zGe;U5-|52ltR2aCwmbC4aIpUlWZE6%@exsANPjpN|GVfntUF40u7UG_z$@Kkm7O+A z=pV@r1U&M4mk^5dTh_tulL&FhfAyA|b=0!5XUppBI}|sqy=f(2Zr-$R^GNSdoQ3>H zVJH5Emd_JxHs*)@1#pUm(X21G+Vb*?q z&zZ0%Z9E<2jrOb0n@0PU@<#jB; zBem?>lR5k9JVBX#hhoasYP(jBAynC4)po6wdo=HP{c0}XmKG&@YCdWK<#>(y9G}K{E3Q|bU9#^Fs3+&*Y<;dcPA)|dH=6gnAZPy(-}Cx!(tdaj zFYGk=E!O+Hr#qB=aefZ+w^ZTH&OpCAn5%#6s2b@#uVXno;lIl~5%8SM#qX7-6Xy%j ziHHjWX8+HaZ}N%$3=&eTm&GS>!1JygJ@X|VbaVVxC66xjFNOEK&{9}ll;_8PHnP10 zc)+cfIR)@#%P{Yq(mukwrz6=%e~S^2^s}&!eqP=?-79>4DcMI`rNUlH_R#~v-`L;!zEQ3(LO*;%bl|@y`)G!kXtnI4 zZ{_TtynXbtZ7+^}v?Q1BOVvJt>iv&sAGJ#R8H;^1Tj4AA(I4jO|8(}zg!fnE5bblL z%%h@Zjs~Mel*H zm=^+0%pZ+p|NI5;j>7)AMDRZEbwnXj%O&)+d-+KB$Aperb_w86uJa}T(XJ<^y^=s& z+N)mjVyq*6GCL3B=RJQG>xhpE-%e|1tT8^Yo_}idFl#;EJIvR1ujfE~9_A&mj(Df= zcO*OG3&l#ieJSk+I!orDYS|NyAbF=B|?i^n+b(ZBT z@Vk3St|PuIvrdyfhjql;q@BHx>xh%2U1!%3C&--3f373Gbk-3+H;DSPiIdT+BmN(S zAATM2%3S+vmBz;PLfBJ2#f~N@dhOl1-ir1Omt99(n6qbJE(wu-c`kky>?y80__nq=3J&7J~Fh1n>XM?Vie9x{cd{FqndH3LU0p^`GKMWql@tj>) zWB4oIJwM-v7xP5yNG{LJR}zHEL|IutORxzK!{|BU#XHCpnQ&Pg24>sG!qf76Dw z%J*)`FE~9oC#2=_PN`gH>3MFd^4;;)VYXcQ^AXB(yWpoiKzqHr8KoA!)fwHW@4w2) zlMHLB^tnN5GLa`68|CG!*dPoQ*L^N}1I!0oIKTf5>HaB~ABsF&|4~TH{v!RMS z%`ey65AwfUZ~a}EY<`*Vt{ORVudB6=a-Ho99a4_WzRgLYG7d!jA?qxiCdo?2(0)$f zrKrNKf7JF@q2KZkHeH^S$4O@)j_;e6$I0e$4LkkgWHC@2?*XjPZ~b?9oZS}}*ZJov zc*1iJp!aX37!~`;@D3?AIu&ud7{hjA20g7wUqNLeSde$yZ?GL^i5mb^-!|NXRX$&(@nM_EC-?mhS&#JlHwq%raL|jWpOo+J-CIJm_H);W5Bj7CbiHj4xO`7+ zNpK}dxZB!ec7GbqSMmG}`Ul_vomU9_YQN{8ST{q@DW=;i`{5=s7m?TQ{x4~LvmUWO z{r3cKaiaAnIG=p2zytpZ_#zi5(NKC#AE5tMc$Y`e^PP`n=R-`708aiXKEMNRs^}Wv zy)#!&NP+L@ck=nN-N$nU1HLbAzwK|2_Z?g$2*kzrq}jfFe@yJRynY^5XyJRG@eSX> zU(SbvAEf&h(Pil*TSw;pS@22uAaE!r=zm=Jzn$n?-)ScbW2;FCX$TiBdG8x4sbd)`py2bxP``*f2{0ssDLy>nV+udX!EE z`F34~zZOFFIriud}@Ev@GAMaC;FkGj{mm5s0qkMqldyFh^r^i!H zz|Vs@zWuVu7~*&5;(y8Gdvo!ak1IC#{$9Q#Zry6U2W{+po-C9~xgvk<9Cf*&Ny}9p z_lnrA^7Kpm%~b%)9uGVR@Ws7uM_Z;otaS_%}}C2en5NUp2n8KYu@O@M8QQ6|K9}GX)ueR}?FYgy==ai+r!SQ_)o+A(I zHM;8D-g0RN)Ys{v7Hw~!>yLyk+RyAqVV?tYd_UTicG_%s%M-1gQ!j=aO}~W)?K{RZ zNX~%P=)B(S%XFFT`%X6= zxA;Ylf57TFik^4UNRIegE z(lyDsVBU|z`*r3D(sKor`;Do80sr)$0)B&*vsf>LDBLCuXW>33aQXb=IcR?$hV2q~ z`=u;H>EEMxXGHtV>%YHU;|qFP6Lc=saPaul$7!LjL*sehSm;~mVI{jJKfeFtvGJp? ziXYaG$^H47PnI9=6}W=Vih|yQ!_a#mN3WGXwQeEaKQO1EkU({!U%f{B?ELDHTtD@{ zrHksGW#ui(JPMjxxk0aVka%l&ybq^)7NiN^F=`V0QYUcTv#g#6FSKy6VB2eZh3hWKA#`zJ z&TRF&=eDWO`-zJ3Ab`S4u~4$L&H7p2xwjH&xHoM!`+@cW`|a+zHt$~)LmPO?<4x~& z&$a!B2$JIt`_ zS#9+6)LA*xtG%C?XZ5)DG6k3WUyR2 zfAuWOm-pCqN;U!_^BUHX!R{+bnD-I!y%yj(A)-+5(HX)|oQp7O&-COMB>Y0;^N+cF zuAfz%&s`FJA@cdAsJyE3-55hQ4m6 zkK?K`?L+5p&vYAajznkO_6k3s*GcE)IXb_cqq8GN=My;nj*m81>x1t|IG-QivxWT+$x9cR ze$2ktuICgNwVVWB(LN9Dsnlb-s6EENBF)B6i}Aaw;Lp%_vDejyJi%f~r`x#%gfVnU zrf6dTY8K3Ufeu?cZoqpKDXf)!|?MJLX-F`^@ ztetyE=)gD>4)z~$Ysa>mB|s^SQpt`|zM|Gp=_=3#fv;OjAGfoBrcCI^Ar7m^vNQ>%2ebbvtc7gnUCk zwJKNNKahSO^@w(Z@7r?UCuHOyi)U93u0i0k-w))ze_!&6;~l@3kN1J5+kG6g@tEoF z`>Z`?J8lcie;JKm>Uxg$Ss{^0d2wYR5?4t6*7|emLr;f%&m2|0)<37HTyfd_G;GC=?T~!@ zk9Z&Thov2{9~$XC>Z^tK?0;VT`>0)CT&dNk=glD*Z5Nw-upOX3LJaz&4}RbS9{{v? z^{2a!dcCBRBvtoPw@bLzz0`>3eXHz;Ah(e3TKCduf4^5gii3W{{0!vwo>B67_GtJ# z%kbA4pFfrz5Ba$R%+-LM> z_21MCe&|zZ1oTg{Gx6YKPevcc{fB;ES9TmnzQ1F-)N7;7>!;gKnO^C${Go0Dmz@{G zI27`fA7`E^suTALLf+V4lpD?NteLOCvw!EjMezE__q(U-Z`aAZyO5NuYiQQ|iw&^S zg^nnPUapMX<;T;@bNvfQf&G)MZ}9i!!mmQ|TE)Lk_GO~}iVd$xq$=Y6G1#B)$>Eb^ z0=q!d6&oh&ykoK9)oK@vX1xEGIeh4`)IQ6|7wG#pssDoR(ga;owExb^(XBZ=C}%-; z=~dJJ;T)U{RRvCrPjO@6>wnAPL6-x%uglT>b*0D(U&y8!Kn_H0~lhj5E4H#C?&7ohf> zT|RE6)XQnocM2ZhFE`l!(9y%&{*#O!vEPA;s0ce@E;x{{D;N}|HK&K2gV5hzA?f-I!5^W#|ZyhV}!qRjPSRO5k8C& zzGsZ^?-(Qe`Z2<<9wYpl#|U2@Bm7lkgnz>r;g=|Ue%+qyOtfoZhhdzLzCpa(+UDCh zK06NpeV_83C*MCE+L1!y{T?FrbC?%en(J3SB~#epOz9BC4WBQ1b@_fqN&PIoRXKcz zM}^P&>2RjudrGjP9Gp)?7%rR7wp>1cAo+klX4xuuZ#dWHF9HS;;?OTw@oxpJ9I+2( zsi*HJ0FwK>Qh%q4{6znt;|AYff&375*PG6>Uy;rs^f+ICPU+$P$vi(W?y>l>+)*^J)8x`q%8(*aDC45iPlEuI7Lc_!J zCKwlF=}wy!T=rX<4?7bnxQ{Ab?d6qpSvh7p&ZC}o{>FwK$;=g&58G3mx3uW^{s-(& zIG%suyID=wSUkrQz z9yPEfzH7^KDRZS>+9e)(OXfA={6^~U%Ct?sJ*ERb(g(T`2HhW&10^5|9mhx9_d8dxg1$k?503Aq^4-Ut$*mgC{c=5%jbXeG zuxGLenYefl;pCEr`R-`CMhYM_x!D$Y~$c~ui@lhRsgQQu^fZGjuHlr zX*tji!I1ETm5c8=%lT;K6Zhjq`&eS}A&yrRD!U*4TuXPq(bwd9v0IuET(Rzw1R1kl zF^?es;y8%!RK|CQT|O)0nR66>TM?sh!Q*lYeS9+4FMLs0Hf+6M;Zq#%Ct-U;{cHK^ ztY8SkaXmCp&Zk;D_OAev?IN6K^^|Yt!{OyQ65`>0=Tb4-x&M~u6nQUw zXf7#1T;JzAvV141tM~z}@4-F=({cTA==JMu-H7kGp&T2qZ+py`5SvnJw1ncjmOuFwh!{z&VO`lFWquK;I z4OZ5R7h9O`WbvF3^-p)RfA@VKJSUWImy|Q;>5?SL>dkSYH*4MJ0yxOaW|^Rjcy~yo zIKB%J^}gRf3cQkq^qa=mv;=5L@99t-_|gVNFOL0Ar>85||DMKkT?>2!Jn^>e7%rW5 z8FF4~m)1wRakt5@>ruYTKKP)bPnz5a2p3s+?Hcn@o)*s!`J(*CpOXF%N=#g>_n-g| zxL>QqC%-o(U0*_S-3JXnohk{$v0SO^t8}hL$9)YdzLG&~pWa^Ea_@mx`1ejl!2Ro# z6O;>_YxDJU)?25?m+S7abj_AO@rJ-%u*cd#xnZZ_asFaH9(-gu z+vW*E55up}{hJ8NdbsW5;l5_tbvzG4yDs?qJ#6Rcvcp>6Wj}X~{NPfabGXm&hW>-r zUfnK;c%`3I_>J~`@DU3)c|6-4+d237kiP*!Ag-d1O<;!TV?VzEd3owb(8H!TLQW6o z?GEbY)7u}$hl>4S<)QtN?kp*t=>ulHrkhO;L!Y-Bzu$oSC-U}1c--36P`lz^egedU zZV#}X$9g|&xO!i#^pL+z^EDXnN9OrO#N5>Uzm%()Z>@u;$Q3W8*=fNczkaz{vwaxor}M~;}7QI+dckB zF22pOw1^wC~dG)=sIHQ{N|ve0Sei;ZOFR5&0zPUA}9)Gjb62pT9E_)|gy})g?(K zd>K$r=S6x24)i@NI-7d6SZ{gqo^U#QC-hN`m-fmY_tJK zvC(#t6q1<|3%CaDKZ}jN&e(XS_REDt7QglTKD{Bn*!VHs-wb?^F7h{9F7Oq6{Gj$b z#<%SF4;m5aQIa-4Z-p=%`>AqWv+n4Z`>Hj5H2J+x^5Z;3#JAt_=lLi0>+jh1X0T7^ z2at7koR2W*|GhuLIRA|bm;H`(Y|kA3vmVzqHY?x5-eX$t>l!yI|BH>EM>666ALxl& zzVkju?uS7JdH$gtfWM&QkspdYIzIN#VZX^=()4EAVg_4{QBwH#o}k%x?Q0 zuCRV-PFN=@}cPR^-f#IZv1t< z8D}hfxMh@lK>lOr!?&Il(uMB7_Ig{U@}53sc22SJoplrCJLhjIbn~9KlUm+veN4*G z!S~KNUdz`{L3Vb*MSI8f4f)%1zkZJ6)zs^?-1v&5B%g-V?^5fBxL<8f-2vtIka|AP z@YbsDD}~?Of5v(*B)@~^5&OX=ulH`(Z=|EE+s{??8GhJls&6-2n0g*^wJ2M!E?=yN zVq;0$N3n5=%5x#PSL>(P_#uzKSza=idO_uh%gXiX+SRL&ug^oUeA)Vg{v53w&8d5z z%g-ZD-&~!41zn)8kZkt&J$fS&>766j{Np=77nuBJ?R@lK!`b=e`qw@yqzk|7+}F@{ zdU`$k9kH9Jukvz%^&IGxtbAa|Hk^ELK7QEtyh9R;gB}3gY|q8U=K7L^2QQO9;$r(U zcEonx)otf&hqUKh`x?B`@SvT2TFMvCvBY*}`!1dfzn&g}U*yx;RhEBV-%(Fg`lA*F zM1P2;JW{z(4=B$cWb+?ozeGL{&A)xj`45aCfAW`d#D0?WJa>8d|LaOZ`HLYhcaI=1 z>=#+CiatAiJNxx4-$u~C(2Hze+5QtvAumraeE%r|CN3<7w8?w-byI4~p#i`+gq%=QUY=#(d0<8Dghp^bSf!`5V$6o-n)Tbvvy5L)v4m+}zKO zcG*#*+<7{(@b8mzJ56^RpN6G}_U{!76>uzi;?*_ylcy(dzYa^s6&+*F|CM9NpX~{H zVyWcx!s&@O)|CX`i=ij#QQunxxwwj)Klgs+)W40bA4Y#cJuy@-;H&UaI&1W^`Fiik zvq-)SG5GZrDc=jpug{IZFZRQ*-!Oh5pR)QC9OaBH?cMz9yK--Ys>m**k<+Z8A^`t{Ml4))4~F~~Xf(hF~|bi8Qw_&r%@9Q;Tw>#hutb9rZr2}SudUZq z;_Z7_!gS_W@H&29m`~3sX@c~8ql4=};R1sjvd%2!fn3COW-nj4;Y^J$B=7n$5C+bN zH7ojr`))Xon%^%`Zus(#fVYs`S)G2er}y>mn4at0G5-mk|8Hpe?0#Nw0QRKP?eEqi z42S#lE#F%$U9-i3$wmh1o2(`~SHeTI+o&A<;i!npJu zBec^(GU2r8*VT~ULh^>1>9`)udG)ORehb=DuHP;sAG3W8Ir>M!b8SsL3w55E_Av8D z`Ja}G<2qG-9TfAv!`)lbl36#${`aMsFz|jx3@_|+g!;`k|*7AjG{N3o*T{f>?ca3Hc`j6Om-{&+}GCNnk zkPqBPlX89D_tVW4n0EQkbK8)gW3rA}(A7JM??0t@XUx4^^BKHVJ=Nce-_%0FbzbfV zFUYPYK!$Y&A0BGaA7QbDXUwVBAK)MO!uNzY?-bS<;;uRV4!55t2efOmJ2pM?_Sfac*^xQ-V2XtFbTd-80TswnU+Dk+uCM> zO3=Ab{jl5U3p3w_jdm;IZ z`guB8e(opcx;5{k!TvDK|5_z02@g5l&d*9Z;~Vz_ROqvHm2`!b6ZTFOoqk_ux{>pI zA2B+)-^uS23w^sap7)KhU2wm4l-~jGhkV`(?FRYrehKcMNaq_pp^y3EmJ7(M&EK*A z$UPP+U9(?NBwZM zx9{njJnZdz`UZpZc20f3b)dA>^Y?uz#kv-ym-ZX)?V%o>D{_VQ3Ob(_2Vn6U+9t2U zPigAV!5a*|`>vAy=viw%?-B2L(BRS@li#$#@TAu|-o0LLOAHV6 z8~A|srhdJ(^K$)nto-|(51TEYaBGVuMyzh5JsaM*uVTX@gA=^qQ!T#TE_~y= zgT;vp6h8DD9@xX}NayR_Y`@_5F9`ptGdoSSJJg?^N?-0*f=;k@*BgoCBGz_7|U<$05Y~OA|yn* zlCEW8>v<*1hg$|2%(KmeR{~|42HOOav`Of6Iwt+nipiHwXs4ahM_Q&&+G!_gn zavYo<)AFA?r2eM{#3$a=pBeOAI?A1LNP=U*sfPrv;QEQ(&k0Uyyq&{jIX{jpLHI72 z_RD498~JDVLCl^b`~c`|@7RX#@0?V6Md1zu&ibdFb1}Wma=r+jg!V*-;4Sk11;pEX zN%8j=%w7!NVLsup`>VXCT0?tMk!IkF}_?RcrGg?etGYQ5N{Q8=QM^sbk3 zQ&ql4yi!$bW!#Fwu^y}+I=J`Bx^Aj!wVYRp!s+<|?O&;?tK@`9s;XXID2l?7FG)}D z`9$N{f3xm84ymeZq(0L(N$+Y2N8x!sE$JNFw8NwQm-@>3Z;gs=*l$O{93NS`Q9S(?m!7M1{kL*1+K7Hvxm5da;Nlz7Y4)2gi|E*lC{kO7J`!C*2p`5WCsdq@Z@N!@<#XGO1-4BK9!F}lAm}h=OqZd zwVk=jt5hFU?oxe#cV#H&%&&5!=P8+=Vds#4pZrW!Zd3hGxlHr}^QoLEH1jd+9P+(i zex@ontG>W@ZrD$lPvwx*QaUs29P(|KpL~ag`YcshtLHnBPvunQiuI}F3pny^a`_rn zpH#j_^hv6+NqS&>ea7xHch)JjlHPjB&vMBZ=TPoy`N??2Sb~UmdI|C5lXHl_R(=wm z^mPvLPA?&zdZZZd^b+DppJIHGpK8%a3d-^22g_Fr&f=Y3LVU#Gy?hN(p6H70)zO~i zP#-&o@{9abN=pjjl}m)Do_7xMly>DKx}G`2D|ZOb@xnR8E4>*{{ubj^dKj-WK1l&Q zBH$RWnpEShU1lG!-}Lp5_I1uuEEvx89Y$E-y#ftNxI(u^6tuf}_MWHB|BOxrcYl}l zmYhF#_i2iDzS=E?6|afC6g~g`2J}^7h1!EG=jDfroVR&?6#gZFe`7*9FC$*`1Q-%L zuk#_ZUt7^mw%FG1I4_j<5)p}?ILEB*O(m2I;X8?kRBD5JNfqQDRR1IJn2+;ZRC_MC zSLqhoY4>S)22Z@!Q*UQAKj|Xl1nOr_rmsf2@oBtS;llIruJeTNecq*XEZn7Zv-g|H zPZB0SU+4VM_oQC|o;F|QeI4Q*o}bt5>y4{e2tG_tIk{!I_y@b&@d<(U$U0JCzT@v* zUsrqBy-Uh`Y+rZ};-t2V_BZh%zXu}akPbT`@HWpaxbyhoJA3wh)swE>;dy>nzw7Mb z{pTxusW7DLBCX%(N;w!ql(jE@p1l=#;vd#U7fZ&VOT3cvZIoYI2eomX?HGiqW98cV zQ+SVzg~Aof*Kx(x0hoUU^4oWrqVtGb-wQc(O2_7HQMqEd%uiFx?ju?{9`%Brca31f5rqZdLNY-F`aNs_o)Z6x-JoP=z2u(gzED` zz50QB&$LjF3~<2{PXE=bZ{M>j)JwA7Pe(wSp`PH23gd7%rt{LL#|Sj#OmYX=M5 z_nr#P8zud4{Z{b{GanL~Q;yIC+y{A7`_V?7m)d%2;jWh@-sWw#eq!&5*?ivKZL#~n z_B~X)M?`)^+*rZx1)bJduD3l6K1cP(7{b9x<%j6|4;3vL()$_gJBIzNQOmLS=dHil z`UCk%ibeT(Y!ZGFj^+O!R2Lj~{F~+Ycihp}-75~M?t}~Kazj5d`U36 z7oKtSe#Wf>47z%39U@#;cv0yet~p?$Y|={UxL` zDv$S<8z-T!^_O2HeIJX__pum#A9M0;^nJ{=dlLEvA^pik^3U?S{t@!e^^cIh+CL~K zRKSO4ZI%4SFUD6O#_p}zeK*QA};1CLFh%Jw-i0NBy@PKIw55q8PQGkS(;IXg?5t zTbGO4hf;o|oZTqL*5#t(=#?-4i3f`E3J>59T&TYs-jp*+W`6#gB0n*`*=+X|3HLN6 zKDG`SJ#TC8u9nANxnTBzz0Xzr&XlD)yQ)sm6g|dY<=XWY-t8bh_=oqa72ev_jYuC+ zqt4IaRut!7xx`$VXXFwqWZsoa+$8xq-qWQLPdeYelc*h^>8vN|Ju2xroX6q}E0=hW z*tfaFwK~sCd{F1rup2P!1O8!wXMPPoB;j1*5*ZhAiAK5Em`b#JFn8n~uv+*@?_Pmp zJ`Eq2a4vDBSgN_i`(#FyO5}7tjQVswTnO`N_%4@kv9|=jQowJK_&0lt#ZO3l(i;{y z)_O z$v0bnB|aL?gz!u8D+PWo6F(vSH0kY?biyyke_Mx5CAN7!!Ysc|{Dkl|>Fq%XeH;H5 z;=irKrV^V)Z>JJ#b^QeRR7pBVCxzGWHVLN^*XepvqF&dN0I%ywy8g=YHN45eH|jbN z@Mn)=dpWPReNe({xY5C{k(oUAqvkkx_7hv@AiRbf1fF582-tcD!<37pm&RYkf3AeD z3Bx!BhIBP_2~W983Uw8pbWV!YX_)=M)?o?Hu5Iu5GRzr--3MlvbhmX_hAH2+ugWm_ znG`Iwe6F9^IxN#Er*~UH`C~e^T!s^7{`mkKfZbVE4uupX>6EFh1AUJ#60#V0@-8cZ~4^xlY9M zmzK+Z$ofbh)^{cFiq2yg&ve2u{XyVUXlJbEC!Pgo??&xf%AbvIT*rGYHsAUk;AHf% z`9ru~QD&Xl2VMqGeCXoqNtVv}!1&#N@)Lx4t@#t@>>W$Se=B(FCB}cY`!_xo&u{kX zcsC(19|n`jnH(k{Ko{RrQu!Fns$AK+n5}=Os(sNLX1Ce6M!GGey}&}?Oil)~NPufa zJO^|uCj)&>{my(ZG2bAeu)YI0Stmj}NP_enN-^t6xgEPt_Gr~uCPTUg)e2)(9)`;U?K|H!`5 z{f4A>m(VkZbu;#>AK6zwvahJglU}39MJn+l`|5|;z7l;5PMRL&xI_)wib0a&YOZ3Y z&MWa=F8Q9TaP!KFoieU-zxuYwdqt^=F9HfS58_9DOl>}fS+3f7VnU!CH@~cyuk*`_ z2AyA4JR|lu^MB*+()?;izR2Q`|6-kARxH-}WyMEje#v}y{)f_hYEOUK<(nt-iCo1R zonKZwspmD2?;j)hCcRS*zFGBNMONpPz;7{9SkJG<>QOuXr(C|P#12hWoDj>L@C#$` zIMf)DAZcK4$pJo&`(X;HEK_s8g^^O{ElK2?$Se1YF9^9jN~ z5yMCEKjGl7(RpaaO}d{9_?IGlPI~t^xD`78thiC;pDg#*Sh=)klV0H9m+SnnVvpD* zgg;bL?y!Sfrt`vz8)RNcxRtT?vE4RLPgN|@c_HMI77*cIi_tG7@P}Oai*#OCv0dm& z_%FuFSN?Q3_$4yWN>$t;GdsfnL#&-j?+*z))9;U^>pb?hF#XL~eX3Uvgz49n&A&HH ze<0RAm5&?4^r~3-+Ws5D^k0hQ*LmOeF#W@^{5sEU4%0s!!%xe9Uzq-W}?usz}%2<08U$#o+<=0~LP<|~8(@(_mvn5H< zI-1}6V5#1r9ERtlfcIBp`R7VHd!6RLDAq1b*Le!t{kJjvR38#c&Hr>PUFDI~)AUWT z`n7%2!t`H`)E}NhK)v6H#8VG(-L25T*1_K*UO0Z(d+m14*WPvIJbWHNIsai!jyr2U zJN10|Bw+Jn)N-h2z}43-LFwx#GORaHGKS||IWGjNSih{*`Da4q%jTDy4{{xMvUT3V z{N-=3CeHs~e6GmX!u)!PXL}fH-~S@qd+&+Dx$lS}es4*B{f-#V8&H20?$muvmIqR! zJl)^4{e9cl=lpwKqVbU=dvgrP&r#K^`qT(KAZOfP-M98K>9`LMEVhL-lK`*wKakl^IaUR z5BY#*SP%0*8sRVT*ci#r@Gl^2=T43Oop=5^%_}cT?>N0?M(YK#KtpFhKXQ*^(J=LBpWlIcGNJwZDAJbdb$y;~a1hwV}5)u)B- z`F0K>IH~*n_MZM!@1L@tu|40#kYeY& zXa%k?${|!EE>UC*QgsUbsuoFBI-7Pj}Ws{u_PC zhgT!=9OVN<4R9*Q6Z!hOw43>-UyBaloShqs!aphedO3vmk45F?C5ey1D}UX&j49r) zHU9k)_-FTfiv3OLQLevjF4NyA2fvLr(Em;N_MKejr^R94xyU8lJUHRz!HH{R{KzG4 z5V@s(eC75cJyMC6kz4i23lVu|xu^>A^hHUZxX-#;=fR0j=sXzZt{f|t%lx#zlHPxD z`E8$diOz!)Pv|@t`F=8%Pwlw7e>~Q<6 zKc(|w@GUX?b-rtM-BP!2x?AVR#6JeF^zV1&FLwK; zyL5gG_*crxWsiejsPp5*4xJwZ{^A(^x?a#3mcJCgQi%`i{21`lkC)1=;&X?`Fp17Z5Bk^W@++055pHs8K5->MiLR8DRV(|;zGu6XSU(~rl}6|bFP`Zr?f%Ezr? z`d4H4sa)uOHt~P6Z2HD9|If$LmGA4r^bN6eT{l=ArWa!AivP+my*Y-zmha}t!0)A4 zy7qr^SLOI4v2?9}VVM3_EM4^ucfB?LnYTd_f~MaTOINzGmumXQWA$n} zTc_zCj-@N#2&L)28B15Xsa-|By^+E7LJ03!z`Vz`Z7R`K0u7?KL{4DmZ$geAlm237%>} z*uCeFap@slktTGl=BYH$Z%{m$_cNY+eDO1wce?nLSUK#AIu2Ml##e@!FMN+-i!0B` zy}d8Ra$hbfm${gaS{`&g3II4O&&m(aIVrfDcjM0#FW$fzp47LDXFX4s)T8fy*g2!Z zH05ux9a2oF$1(}6h~*5Sf6=G2@2i=<5^m1xJ@C34f1~5%ctnr>{xpmOG7A75-8&^Fm*M*);XN$XuXf%oCT$3l0_xO1n@_P(f(wC_`Fih9E zQNJ52VXz>5S)IgBRSr3tcHgT?%4`;RX*M}u#_^5A;~4z>{I^67EqxU6)F4cMUDCDw zQM3)@7Wt9<2#+y+#lJ{?j;lo3&W#4{o@S6mB-|ZyK|nZLKezigqz~;qd$-5#m4)A@ zsFQ5+9uiOr^_rbuHN0DqSlTYd%kbU>d|yWX{0c`;;EO2WtM>Nf<+%Hb4iC~t=rEza z@^qXEFWT$uk3hvEes9F^BgKM4AQ1hmcwX&s^*0SVeeL3ne$oE%J6PW==GXpP z`)S4UV)#bj7ZSfin!oU%)=xb~XLz#Q$Iz~Lyrr9O>(h46pZWd>|Ly)oMph$8f1kzU z{i@~aFV=oaJNpmsRbDWG{giU`5&S3r>>dc;pJ9tadUPNF$NK&!{@Z!Y@iB=G+rjcz z|5>|CX8-tC|GFZ2e)Ss<{O_v?{qV0m-4GuagF2r{Ua)W?zJDCUmorAg*XS};{;$XK zb3Po;Z|nSL6U2gcp4ws5uWv^5rmc4qj-cT?ZSFpAbUfg`hONu8oMX?bPSNrw+Tj2% zyC2GPw50p5ql~HWoc?@~uip=zP2VS;q>ZiPcB9EY8q6&V`njr^x^7nWQG{sUe^$;l z(azU(4(>jw{XoGyT*p`!hF_?ov}*W6ebk35P#ep=NY95k&vl%J})U)I%9RgcL{kjdI7^AyyT-f=H0jLiR}7?gL9#K*^1`+hU| z{63ggHczqfC*`Sv<329+pA>>WhDV#5kCM);$>#0$E(z^{H>3I1X$fsR)E|ZDkR&bp zHEecWevi(B4)6J%)X8#a=O`5FDN_zW!g35h;>Gw@{NakwxQ{<@qxerf(~AERzYIU% zMxlQ=uD8OMcvTDDD1bpCuNA(;FC{eKG00JJTH!N(9_+r?BI~QPf1=@<9CHB<@FnpN zxOkLb60hX<_&xiQc!cTZ;eSbdGh=}dh)R6yJC>BYyAjT>P#O+CPAGt}UGVJN2Y(4ZHiW!5S=b}H{+iJ&$ z2!FriyP$Rt6aF57=eSj(Z=5_Df7;X>^~fi-)acyOAlW_Ys}jA#P6`**S7b%Y7Ks*n zgun7`{pIjRr*}|JiQXww4k=~j7=9){W#aT{Vh$vZ}_#GauAV%`*6+Z{Ul*fiL<7zUUj|6MZvB8GROES}v6TS6_gTImdoJ zhOsF+pZOjdWbg5w1w3~o^Q%z+9K#?jmHrCkNxbhu8-`(&X7iq#-G*|H5=I}*W@ZF~AJ*OSM?!01f z+TrW?$@US$e1pT+*?URP*~d;EsD~EcSEM7`c@6TBo={ied&9v}A0g0X*>d%VIWi+X zE1v8Fw?xZ9{j@U*dmfT8&*lZi`P2s`qrK-LUZJu%(z;IkgdKP#X zea!A8U5|s88SgrQvvlIKFh)PH2H!U=rC-KdEBONV?nNr0jI?*5qvd_@g34Q=c)TE< zyw^(l**cxg$0<+s03rT-YToLxc17PG&!K&GemHna%MIMS9D!Tciq7XAWqm^mM>!#X zDJOLHUas(K%k!e82jBDKU$~=A+imY1GrjkY|M7dwbM4;0>0D4p>JbmlO0_)37~X`x zF#5odg$wddFE~7{oH&0bo4=df*}ez+-3?Lsp0DeYv`2{NVcTbN^W`YLUIV?zhZFc7 zCiMj23wvIXeiq$tdINBUJugc<;phtXjwSpcbb#PkC$-8mO_CtfqbsVyN!0{(EpN%(mj`cx}#ye^E z$uHX{pxwjr3y*01nZ!Mko<5otKkVo3{Hxg$woY2>@L@kf0r}>=S!1M6ch~fO1 z_PU+pu3ao(Sf^ns^phKcZT?=fSJMa8!`Zp(WjyEh9Pv#bP&+Z~H*VaoMxyk4cfKKg zIZtV!eEerWuW830xTIKPiW~9fJ~4fxOZxH@!MtFa(q;UR1m)hqOgM(VICho4p!69#w>Q=`9Z{9Q(x>97FWmT)xx{ zcfQ;0iwExg=>^-amW;LA#Pe+adwx3Cl+f9}!;r?uuvn6fYZkv?uA{H<&+)b9fYv*> zUHw$mwDXj!f@Jx|cW6GdLuy<5Fljm zn9m@-x1r?*+f^`4Zmd1QX~jF^-cb(oJ?`?keH`1Dis#RG`?VgcFP4w^_QmieepXI! zTH9~-tl^cBudo9DHHx>{+a?DFXYXVty{&>r@p~e!UrmA6NDMF1(eSeJGw$BI@iBkb zxbQ7_+?V=-Cmi3Nlc0)jQPtFO=9-YL66de@*M#^Q!tAUvc$ozn^F?sr_L5HF=;N`)62a z$jB*Z^uv>`eW;gq)?|ETzCVzBTonTU-1ij1`OPU;4us%klw*81zGh@JWCVjzt%VY ztW+;HvgnhUPM_TF^vRxQrC*2d*zQq19-L7A*!U8RIsJ0Kwm*Bq>6eF^q=LM&?>qOr zA?2MV-vNnCZp+m|BSs+!^r8IyLF}9X>oa;;f46;|Y=_fB$S3`@;_p>{CaZ6*pb_q` zu)eW~yjwpF>l<|NfTJTlr2Q3tDfQ$NO3z94j^jsYUqOtOzB&OK>{9(4bW8DGcF^e` zosP-AinJ@KgtBw^sf4a`1%tk142B(DU4J$^kL`=h#ZD&c6A6_0KcT?s593aJ$+) zKXARHWblmc`H!m{=O567xZbgTV|Gaf_kV?sP7eijzJ|-#4acYGpJwlb?@lk3V!YsC z^_R?;CMW%9S9YIJUB?vKQCGp|zS0{Jdy{tFe%v3X9O5QLKmY#y1B>v^UORt==YBV< zI>fu^YQL>=;aaCh?Y?r2vx}*hWu4|39ghl6OL0;^E}HxAZFcl~3hNE5 zukeW4tM>jyBiPowwZ?l2KGSUyrA3T7h1cF&+2@NAEr zQ|@-_9JUV9NF@L4yUA|&K7Nim|0(!V4t${pSXpmBcc9p;z zpMYmcyjus6?=CgpCCcF2w}T#TeA(>uxwW%qK+6xFS9%BTd+#+zHNHnZ?=ad=*Yl|Q z!KYPT4o~Cm8sf*q^MV(YFNfWC^FltaM+4w`7AoA~rC3onkO}Uc&)CVjw2skD3=cYsd#;m z(~|NVT|Rh}f3|~o)v%p#?B87PwfU`tN_$^w_ z1UX)$@ja{6KfF|ZlRM*AH~8fvY1H}Dhopac-+Lqj`N?pUpA3imoTKHoA|LtH2%r42 zbulheGHm*g^pW$%otm%4jo<9oXqwEcqV$gIw;Esb^#GpkuxqE$iStnIkk>f9)3e3F zU8Wg~UPeE|n|2WKOfS-T{rTDvuWthVFVlM2UY=(#d)3w@!+vVxM4iHy!;33D=|#HU zGofBID@re0@3neE`E%tTR`U__LhS&A1A6BSZWriRkNWIfl#Nf}__|o}w0$6Z*Msy! zR@qOYZ}5dZhr~WEY-|_b#t*KKLX5BvsCtlL?o_1fcJjxB^%z_KwsjoVN5}r3zjFQ8 z`TOs_^Yq7TouA|Plb4j1!~L*w`G5PxJ%7mj*Dt9#8qNO`vHUWB>ma1g+v!hL|A|@|Gf3o_24Eb%I8JvDW>9>}(7Uer|>)p0aVEiXNNm90lzI{L0+GpPbHhV0+O&Aca z|JZ&(@TsSz-Wrrc7o664`uG72o4z&vr+2t=9Dl}lJGiUWPe19>)eFCeRind*SL5V! z6moezvRJ>N+_ML_=rWa@Pi1=e?()N0KJ&5bQJYZfqW(_x$!Bo;DF6P`A2p2_9VutKgim8ir{jb`zEkQ~1CQTgd8b_%bm92G ze4l}0u=_$SSxvY7(2SecMe$-eHBMjHyIR(dOutV2{4?YA!?*rg z4g9`Ad{0WUR}H#xzyB=SQSabeVS>;;BYnr6T{H@FgLH2>r1^*!Rj~DMp7V{;fBXT3 z+bk4CIg$DXSbvA6Peu26LiYis`(p}!A#^W*?ndYIgTEvB*=|%@((d1mwYv%J-i3CP z4*5HOOY)f>N_xL0VH@{or$pDUNoSBGq%-M{Dm^<7&ajVg!OatG|J~M~xNb>T{I1%| zQeV*IYy3v-mzn8$4>efNkrV4P)4IgB@A%q27C)zlf5JzKLg4xX#{&e%p{*M5b`7@p}J~-~u??WV9lpoZeglD@m?t2lu7el%bACO4wC-FgN&XZEpZbU5b zdqO;{H+@^`O+rc~e#3G}NX^(KKFeXtm!Ta^@WXb`VX2^>`%b*=>(Smtmax5cuEEZ+ z*mr@be}qn_UXprE&jxi;tQRa|DO0s~yQD|kOZ-^Bwfhyecg9MAv-{ksN&vu3;6uk` zd{6^5dtBSw>COWTy6+d+_hPcg)j+cKQ2QQIaG#c&s?_y5_Io<6^N~;XefN+aryV`m z`Qd`KN{37psipI4=Ck+Q?cH>i%XnKqweuxv2LS#)o!6u)f8!ic2=3MSPpVSa6>WS= zdcP|CpuQLW7(R=TiTOf&zASK-FK~8o()({JM<=!a*nQgcAhBb6l@6T0N%kT->lj6v5lWWtNV_f!7aY8aS&*>{4q z{Mli;>D5+8|4hYJf-qFT~C_HN{?mu$X1o@o(pTWDz5}vnQ{kgvQ;eEGz zB^Z9kaojbKdhydp;yBE9XQVZx$5P1<@E|u_`k3*9r-dOm&%7#pE`sriFty6(LH=u;;#K`+2EvP{m5thse>dfa&zjzRgYl`*Y(c*X%Mo@0^`eF9?PD1$Z!~vrgj& z=dcv;(~S#8&wN(ngE_vYJAMaODk64X()KGtdy#`1T<|HCPxAjJgfrf6OFK+1IZg>Z z;FBJ9zdm@vXLjg&)x(rKVp<$GRo^9HMG`JG<5IDUlinE#2T#spbeMlK{%5corEn_8 zfg9KIb*|l~G~MV#y3i%PFGxOCp3vCDO0n#sp*N~WxVakWigAC8~yoXBaOL`ATc*1u}mE4~7 zPmYs5${lrnO4i?Jd%+3Dg1*iUH26?on0<|WbT)6~KA(l@XEKmD{A{gHlMO` zlb)`}1u1kCxIjJ6*5_IdNHoq%>iAZesrtzJpS{16N^&-c{BB*<>It4xIk9zc8`pxA z@->yzag6=!b>ufYJ1plAO9fqatG}R0Ctx;zF@6n>X}aOv4L!%6^cCmxp04om0fl2e zQqJJhe59A*NIF#s>HG%%r<;||(R7Z-rk8D?Vc&u z{{&Cx=aLnIC-vy-08Ot`_*B(w`4OB}dZxFbqr)v&tNV}PzFx|g_+YjQo}F*8b+;nl z4=bFV|J^1*Z*YzJ-Dtev<$U7vG6>kaPUXwyNp{Y$xR2y(x$*kzv|KwcXzeKC^8w9w zrMAn)dt09mUQm6I9&&uTLd&y$Zt_iie=!(i{nf@nv-4797@Vy`*?D3+mr>I!$#UB^n>+Kl=yk%_YC7 z<0*vkBr&VZZ$Z9vmEW zcGuu3EjK#PB0Vz+?f9fWT~b!LlWn5_1>#K>EC!E{BrpS-yrZ!%FoVb^}B9RKetzW({F^o=12U+ zdvh^8IOq!l{x$gfPxxN;dHZ98zuy)33+L~*1pYSVe`mY;U5C`q4Tv9=hra^+zZ?C$ z%k?*9fAQS)B;%sZ*UX+Y`N&V3B?T4Sc`O?@Y2R|ZO1b$O!*uzjv&EnAPTs8&?b&;( z;W;ZeUko0L$?0RhWC-!F@%b6mI}`AjPHZra8XhZj{Iz?t(Q%vaK-suq^E~Q{ZPXX1 z+`QeLug@fC)xf@}6R$XLP3S&JaU9q44h$2MOocWqcwX_%;C>3@2TwY?z@0NUJE+{d zU{jr6HG4ms)p>0z3a4F@Nqkr4@l%yMjN5l zDd5MV$oVqcKQz7?AJ1p!*?q7$KUn^K(erpP&dITGeo!Z92p-=ob5D$Xe-r2QCbY}! znMPj<2pUvA!|!Rt){`iA3jtZ!_?pm*?c+S*tecNyyst_**51^#Uy-ocBQ;qGVqN$p z$!~Cl`7cO#{C-Vmd31$4pOyH+BTtK;l#}q7w?FRE-Ti924{Pg*c3(0$sr4*K{j6jd z+^hL)KEnN(CPaP3`MJarU4OVm;n?lbWZ?U%)IWF&BH$>8o8jAetX2@Amf_V*)qQo& zfLqke^_D=Y{uE*Ro;T}d`rrYUj`ASXEH8XV#;xaY9hmgB`{%aq%XJfMvxeu+TDBqp z$9d@+@Y7XbI9yoIK9+)dP@U9MCqa+v2Skzh^Mt3ppJV5kxE_rnvF@www{th4+(qa_ z${3yO`!P{{^cT=a;rcS_<4&2K&!v3cfCoN;)32N(CW9ycLi|+X%jd=<{7sEN^)2yh z9bMTTMx-jn&$0gCIhCXIljJJmv7SVi-U&ds1zpp{Pr7#^Lb==`(cyPLNVlk5Q0`@2 zWlmZEQ!|vF>5ngw@PZ!JPX1mkzRjcUUajqG*nQh@zcsdQYWgDMsQ`>0lj6MWN!`co zbmxEqM<3f?p*)ff)W2i!Ij^Q~=MV?axOclw>N?`4L8XtKW3~0U{GFP9wytCEcqF~Q6TTU|o$oSz7pHeF@%yr_V(>QqwE45`pYS~* z{|x>(M>)D1yc%@v96Y7%-c+aSdM3ZN&tmThk-wCbbeVT_z8-r==U4Cm|2w5iM!tdq zx;cB$_{MviW2isr={keLk^q72JjWX?cW~D*D+3y^_}Q9lo}Imw&|c%B&01D_JQfY=`NAjEu6ZU(e@H?B{B) z8eQZ4Jgo<%#C~Qwcpf$0zjt3q|Bm68krX+=QuxK^-MHg-PKQ?<6kau${R<0wohU2qQvVu+7W+BHS^KXLK*NWIh>e;Szw$N7;;_@Wt8Sa%TnCB~0T zVnFR38~?kd*l>S1o&S=nPxoDI9%_1)@`!(t?*<%{0_^^%*)R6~Nt{oqYTtVuIa1X( zdw&bx?0b%rwFtMuCjzXO^SkuJtd079i}-Q*;&>wSlDWGaoSQdfB`D{TS%S5r_`QP` z0l09yFO_*^s`_&8JIG0S<_>Ese8D$H=OxwKl+Gc3I`AaDW~sLoNO7Eqwjv!SmJgyj=%vi?&n|4J!8=^Z5*gzkJyD&Ecv z`14K1qkfeOqswe&n*g7~`T0-A@Q%_YooDMPf7+08_zQiscv)`{I~m#N7x7Ox{68%C z+qv@meVQ@BdusW%e8TY@N^62G8X;Y&0}hh zN-(_dXx|^QdtEj@WTwxMb_dUCJvCc2U#dd&_xN57x5ATlIQ(mDeAHynO%n!(H6nh_ zCN4KQ+&do!U93Nt{!LBOby338CB45A{?)WgP|him^t7{)9p9nDKnphtkEEz!=Y-?E zPrhTz-}IB-ZvONJcZnzWTSaa_Hjx{`BPmzy&MP8JXXh1zQz!~7Rq|2)a~PU|PV zeUxy=933C+{6@_!ijSScv3WA(l>D&sOtmV`;XXyd?KjwYT+WNmiN;mGtx?Mz1zkuv zdq=y*(TDd|^yItxt6V(cDFP;+m+RXtld$&@nt|1Zr6U+?c_DIGf^K<_tFi&EAk%g z@2{h+A-xqH&(5_by`L3$Ygejbh2SxGxp;CPoh`KUZZhviIP8CHES&Wlvp1}Nr7C<$ zPkOrlQS*QV!~LLEkd;MrJD>JawBH5o3O}yhRQjp(8$^rT!xy=CdZtaq+_U?n)C=T4 z&!39^fn3oQ-_23`jrB(5$JT?}+`8>3@`xP8$`^d4=Q}xa>4dMx|C(m}ftxU&-P0s= zFKnz2=}&Ew_EjK+9|#X#p@ny&9l{6a=cZkx`VVEZfWa%I7`gXT3O6l;FJXB<(T9r z9Q#dS{zF=>`yPCJ-fr*4M&tLQeRhwF<(GT6)xM7t&DSjXUX}Wy@l6u{vc$8z()ET{ z9DXlN$XAbiwoj8@gp6>uE?3w%An@hru{^R)T$o>^2U+>f>7nhKNRQVX{@RXcyOkc3 zwR>WF2ZT;9PN2gW;!W>S{{F{zaBi@Yy%u*A7pXwm`jY9ZK}kh_fG_>V`IO6-QC_dR ze^R7pyVlFj4QKZPP3|}O3pIY5c;2{!W9?WTEv^fod~zyXul%z4d&cUcP{Kv?6LM3t z_8~jpXL^d`-)#|ovi+~3!muCjl;XngfSNs8dh*RdTQ*1m4&Z7JXRxr(Jy z@1Q#$Nqb>4N;W>Uf_}9OyLMA=;h)#CRH~2cZ@yO|SU%GSufPwuRs>jHONRtQyMyhN zeN0zQi?frdzuo|78uJ;tu)NvY?p82Q%5&oh?OWN$6h31gbE{c#pcoDIfoB~nB@$1Y1rmX6ZB-BQ~}>^RXBSubHNf_x1=7s2TAPT%6 zzl8fY`%np7Ik>j~XZ*DNF6v1i@T1Nr9T`q9T86(Wm&BX#q(gd<@^`BIY-xJYY-V?S zsX&;e(8g4K&+|z?^`MTWj4!Ov{zSd@C4d%IXn$k)%aZQ;&!-5D_91@kfnt8*cP-<= zr`IC!b{>rI|MQU|e6+rFtUh1D6YKl%!^QlpZ@H`QbCG!J`2hW-6=`g-%{y$|s0M!j z{f2)zM|tv*&iO8V;|t4WKJHU+JY@SB4&VPkn9klAss`ML8&Dp6AMspAp-;PkH;6bt zmHfBzuZHQHT|)UiH5>Wi`-mTPKF8BjkYVB>`MHn@$1q(qf9>Vv^K-p7ZTZkHI-Vb5 z6NoRPTGlaE`xk4lak$0Fai-$;L@$`UQIGw367|9N0UwpyKS!DAMLI4-;eX*9|6CTH zO-V0u?@Q1afMfZ4|EN6t-anoRc)jm#<+lR69{(MDv-imO1uV^CNFP?kv zOBJ&2?s?xjM{@bVW7PSk4=KNIB=p-?#4|ld@9&!2B)y+2p_ADcHhxmSyj1diP^A;$ z=xkiG@tN_|cQ!6Z=L@f*9hCDH>L*9Ha`nCPF4wo^JoUZyF4wo_JoSD3U9NA*dFt~% z?K+?E<@@K~T+~N>LpPXGpM2!b9rXrS41KNpc;nA&I`zdE_;2qD<)?8}K)*pbh&~rx zZ>B(+Jwm;NB5}@;QiXUo-qOyeo)~XuEZ%{^|9bpSXZ8FS>!U8U^rWZf`l9>2V@M~y z$C>YP)`Bql%NTr)gRjNjpZrUF=g0Ct7|OqnKT$cCb~-z*8gMMn-Uk~06asK&@3B4> z#ri%GJ=Y6#kR<11eh%kEY&`g3pnD_R*N#7M?*`rf>ko+T_m-i1&;Jj3hrs_^l=laJ zKy=^#Lq_*^Ti$Q`0nvSN8Mh{D0?i|AyEt6ZO2F?=U@W z`uFYV`Q<+#`hN%KlHz*)hk^d3^8fwQ|4rN*GXDK*j5MJe3U=h^P>PF9PM=4x0z{rV20}p zbSZq_M(lnP7p@iS24c^7S{F6isVgE|dQ9DlVQ+mEq+J`s*ooLbL(75D8e|AWvcbfF$OsbMJ>46^UL zTRo&aPVi;=XmhH*v?%V?nXY6gy-E$-d{hY={*mqf4F;* z{OHDC!nfi-->2s}my&!4)Ah8A=N+zIB0sne&-e0|BymdS_3tJbUWSwb6>ZlB5aznD+-!>2Tz`wW8L4h`=DUiJ8y zclAWa&1gGJUO8@(R`xxNP~KhnX7_U3W|K`0MgK+Q*7U2Le>S-%+^2wWTo3o?HdMHe zLbzwkz-1%n?R?b#6||pzuBuY$umu6Q2ZoiI6oCuW9K-ulYS-C&HAc^m09Q1nxDU0M*kk;Ny{l{OAinzil6@ z8--E=DUXb|bJuomvR1pNXXnq-OO$X)c|{ug8;;L*{>AQPaestE9Q*NYKY_xXt68c) zvIo_to?!j-efUv3mf=wsKU%Hn3y$IkoYlj99%5wsQ+CcJeN^pAJ0BX_l@4Fazoep3 z>viq4eJ$=kmf)>sfY9&vQD?T9Sa960lKkvv_=kJvY&Fuii6``7*W$QL-}*zQ;wk|! ze9s^o#Nk|x!y{FxirV@a^}-)T>F7vm0e#2x?gs7m z_D-dJpT+Ja)KGZf1~cmC52(-bSWfR{dOpSQG5XuR7wQAbSJKma@gY52dnq?U52rtu zfj&YHC-+OL779H!bu5;OjgI6S&ja7RR?k@&zHA@sGkn-T@-50lWbVj#ev6f%h=IVhQF(3EA z#0OniZ!YnF<(&y@&-v&}{OHarH_Za(jho!;((nYiL3t%|bBmLkwQhYctNHBSi1DFj zhsK9;bBl(p|MqTEdM3TU(0=!}`^nr%`bnugv{D{WHTSQh=h9a>xeoi8q8Hkm){hEQ zEO24}!EY3WeMr4ues_bLKXefa{m;om-tpJ?-FunJe^R6b_9pmEyyE?20-u*lvEjHg zihR;vmT3Gkv`_lWIt?#LE>XH@P|k6Xf0PG0dk?|%wC&g1y(N36F|^|#|H6O#5g+3s z+i!ff^Mf|82_BYeykJ;;J5ROXNqxVw8+Z`^;00Z`xA*wZD!6y|IwfFIM)9J)x_g}( zJa%5dzJoJb(eYsXIfbK~%J;DD)v)QY z?6V5jjc6bLzv6uBm(~uRwje{tI zbXrgc0fjSuPJp-fRRE*#N?o(R4UhBjGnIIs)MM>8`_tsHWwj)S=gFB)5hxcA(JC9y z{#6d1tS%StN8-(nqaBigLz4K$hf!3@mfJn5+O32De-Xb1j=$-LeAvbq$PXI9cC1r+nKyptc?XIBRb= z8b#F?aC5~8H_i3iQ_@hy}HfftsmI?<#zto>_XP(BP072{R#61h@-?i{bTb8 zd;f2hlkZg&I5@kXHj3Xq!#bRJtMpiI@!r=U65;F~W%^1W;KKCnoUxrB9dBeT+WVCH zb}!cM*V?^HyBAx?E1b29_uza)wXj3M*}2(Omuq;{66d$8Uo+c# z4%vN=HYZmmx14uV4{$%|9R8c#&U>~J4`1T%*ZO5%1H+m2Tm}5}Ez2aq zHoS1>*UvG9^F^;5Gac-8^(xp(*bt_Ew!t-!uZ_#cziujrb72f)zy<2Yd zZ0po?>u1I9x4%~sh#ygl>p2@2sF#?|`APzYOYkXeR}Kc~TZprDlT!Y?aZgK1>$US3 zN%<-Y^fmNQkzW}1A(wP*P7vbdq*Ka3yYJNI`hR+s64dU8m*$)9wK#of`Ru#oY)3^& zJHCF7?KoSh?d;8KMviwyJRw(Z9PS1z<)meqppEwHj|ugIKXgg=UGfDf!&mJtI~VWN zpaOeu-jhl!m*1u3y>XA(XSGhBE~!jNzqI%woqfr_Af!*NmM4Gh59&$k`=>27KWf)B z<0t9Fe$V#$@NK=y#{Cv$i`PG_`8d7`ob&H-3 zi~iy8<~YWB?A~7}XAygV7?q2sN-Y=95v*K1MW|f7ABjKX&|_L++(UbN515B7*!w_ZYh z%c@%8Q~vr7;13+jiT20zto_PQw7(hYsS3xZMTADX5sxh4IoKA*pQxS_K9z|VK9z|V zK9z|VK9!00QNPuD#^FD~j*ifS__nd}aJ9Fn&v{Y_pPXK^c@p`+cC>;0A z`~*Hl;!F7C;?0h>cWJqfB>aId{Bix7_vp#Tq~{A?!u7b5+71jCavy=?7McXR&)vIn z_lF8gNnO|nP9J?tLJ03^*7E8#qOjry2^Z$qYy3Wqr#(;l*>`GN7HhnHCzt8M?=tbi z?=tbi?=tbiZ;NN4aAEyaP;fRr*t^wX{mV2xikDA#D{n~C317u{gO9`uUM`;cGTQIY zAQStE=_l(4;k<=|7o5!-m@e&cc6~J72V9Gbr`(pv;ZBwUxpQ_J=Yb@J%>!#(`Wete z@NwfrIA3(_t=%QXgx_svI{tZvWySE&Q_h zF{Yw?N9Q;lsVAC|&iO0pnn@}mMxWq$*WcTb2*>h?Zwuh*Y@Lnq#H(Dq(6J1>(6LOs z(6LOsA1VI~(uI!gh=en|+I$ImwU$3l&kjwu{%`%o?8*54l=XXyH#uFk)0KmINVjVE z&G0w%5PQ(&aq(on;o~REp&vY_avGdeeYphhYive5^fdk={gk8kZV6%>B~?-0(;D8q zT>Vd8t^Qf{WZjQH?R_(@AJCmecJk{i^aXwEC&6=CkKqx17aVYO=_H{wKm7%JHEi>k zR#zU&r^{4cD}L=>@xt{Bt2bW0t?$@<*rc~uGETNHNIJCv!r)9UZC}^cUsBT?y<9ss zcPc)gY*)Vmk&&G=dSl!k5o@fGMTA5qbH@fbQ}@M$N%PX6sZ zG};*_P-pP`nBbL~ruatl4>12J*FR1>{M|Uk{6EJ0FAPZj&4(19X#RHOw|+;tqqBX3 zvovzxSk7_2w{cR#w1f7t-ZM_$x^aD~`~mU@rw$1o!O6W!XD1)FFLV~=;-8n^%D_;Z1)Z5A)o8)wlEm~Q$Z{e&wI@pJ}nc4yN2oZ!uNwKF|SD9Gdd=}M2wHQbH- zKI8YylK8K; z59^1U=DPjNlbVn1dY1L*a2$>c!ShZ|I~C6CEY?HY%Iw?V&mV8@k~1 zO9B^;TfRhx`g+v$FZ*ss%OaP)T79OoD2oqHy{6@k?Kz;!Es)7jO1{MqX`zJUaalZ$~-02)!L%QF!804*uUWeQh2PoL2o<f5@C z*+=2{f1azz2Bv4v{&ousa>ZL)et(UxpU*L-M0Rk^u8o`MfLBagfqJz{e>=g zp;_n`(l@8^qK~nzPJ4hD1g9PqxFVgL9bw-sb#w|&Kdt<5bTqw~#`-p0XkXN8*w%Mi zk)JUcXNM%cH3DDLE}m!SzJoJbui1t6?y2Ev^iA(_^=Wp`-Zu=+IQ<@I0_B$UvwSvB zNjab^mXxyZx4HVvUbB8&>*PP_jY|2! z^Ge6@LxPT1F3e$=qY(hvRR2pY)z>3UB8g z$Y;9A_DL)AL!GOR^mMEo%5U$pDMoY#`S;oa^$h z^}G9r{n?3%IaSkUHPj!>_8dX%aKC@B=g1L%^icnioZm5W@L+Cex90U*28Mh3`~0EY zz)&uq>l@B8xY1wfA2^gluA%&}-w_Pw{N54%%K7y^>TcMYJ#wUfxHmU+_(-m8s2`g` zH}?>ugtI?&Ub>uMPp?#Z_aax8V|D3DmrV)8!w65aM(w(CfL=ix5rYl#O?ZqU=$ zl{?lUHROl0hjVSCxt{Jr!}yKF{fueVh-hkGXJ6aFT&}HqsDEUjEt-t_sA8jkb7$YqTy7V$XpveI`LVmNvwvs^RP{Tu`5fponjPx$0Xdu-^7n4vqWJn9 z{m1-lXD3=d?002{vt&!IcVIXm+m2>OjtsZ;59Ef(v9_+UBMSphW-8^)D8u-3rYZ_%t6|m%#uM$*nD4hAb+TT zxI{K~2+b|Yzq#KxlpzcKz9mGF4vWsUNx^LfR6?WDI@u>DDT8F{l&&QCBWNpb`$`0N zib9lb%JZQTTu{YC6;VQlu)kSbiajulvHm=1-YZ72iU3o zV&_s#=a>xIX@7N!tRIS+%AhwpjQ)}z85rmv;_$)>e1Vw+6D>9Dh5K^sWS!7?kP+A6 zs4))?_4oQRu80iiuV|6zw9JU^#!lLXJRrB~#uAk~DWzgHPtf3kLHQcd!Q+gU-mQ@~ z1e}*wB^a2^?8_l(L26oei8E%E<>;f?`uroKway!=|!AY{XH1T4}qtPhy{_Nis>|0mc3% zs-Y5){kdVy2!c*Xxj8o~Ei1_p>P~0tiTWym$cSm7@-#8Em|t}K{%~X}Yjv4&E(>On z(kTpbduYp*Ahj>2;!YBjBoy(XF3}N3Eu~4oZtm=x6iPA{%PFO(B!;<5(=dZ@hRkHS zOCiEyq|g#fi+}(!swl>j)d@hMVL2#BNJM%72}u#Vyrj}?Fwky=eO3~QtT)L-WFO`s z@f7sDt=bJsQk+6nodRpST%t9~_+JjDNX9bEbd^Lzbd$QRJ9h29+26loTV~(x10Ps< z;I;$)tqm^C>&y1$ygfa`{mb(HO*?Ld${FxOVpDIi*Y8z>*h7E46LXZdk)b2rq2b|y z{Ki$Q?nK^|eYxS${-MLGJQzO1+0Nm-m+Qr3aN}?;+uIz4@CN#i^mGPc?rzw|BONO{ z`+KFJ^g&J?WYVHxsL}M0nE0qXhl%76438t(zV4B1cMi4mdHMd4q0St%cUSJ6lg~zrQQ!7lUr(;952h<7b!fDoAINs*wsdt3 zVGg|ID5%Wx>l;?DS-Wn1W7CFgM`u^=U{}x49_EIvQB6bdHt#k=F<69LnCPV z&?@2Yz>(}Q%<=AIcE3g@#$Icm>m7c#{lUaYN zw0!O6W;@tmJaHB~MctFPcT#jqRu-lxx*UCUj$=wJj`;-6<~Uu8+Mbi-pR5&=fk)Ry zOtbpu!|J?%6A3?gmdP-lBFBXxJvr6$&g{thomijg?>iE339ZLw&Tdtra|B8h6DurG z3}ArM$#g#KYe)LCUFi1cpw#?oN1d0AebG7ZQcu?IbtPFLDD!SHz)0;DU#$CvO_PbM!iKG!Jo{vADwAXC=9SAZv8838CU3O$ z*YmVoDRds-$=W$twy7ICX|9Pao(!~Hv&q8|%w55^-F?HkZY*>3AI%LNJkmd^_BEHt zXw*J$)8-4qiLMw6Oo?)IT_Lu0i(!HNG*rt7Pm8M%k+M}ryEn({qol)Pd0wU zfVse#53qLJYipbaV6!2P%QA3VLTo^aMVAcWQ)ROGO{fDvG;OqdhLXC7Vf`L5u zEQb5LyN_^dP-0vM+uPrjX;{y{>(|Qf)lKlWa_)|NIjo0wF-s?=*DyEQ_vP|fyWO~P zq;GU6JFxufcFZBT)7!pdXs8{sa%3d8Rd(|a^!M-YMW)^TZeKq$e6VQ;i-WRuQLH5v zwe3)LC>C`f+ifx1xplAG(p&lhEN<+#jiCM52)q#|5r+5Y21jyzow=K^$Z$jUXzs>b zU-$5#Z3y-EvMuk&HZ*o{yY~0@=KS|<^0DQeyScwF2m99-6o-cThs4&vJ}yXxun#nT~~v;5B1!6xVNu=U=Rl^Mvjgi3+_?@ z7#<1+`gH(iL{IUQfNSr-NXOw^fIvp}V)SeWR;{xB(H`02b3%y?fi7&I=W*PlcL3WL zxh|H+c_#8=kZ_Ax*qY~RR%diI?chir>sz{j7+o@p0A)L5Ly$et#pb%KLhMnX01FW$ zYl~u5XcszD$B_1+o`IZOtF!f++%YUu=lf+O|P0t z)O)0-@32p}t1)b1Pt_X5Qv5^NzOLfpVo%=y4qyyqadshx1vguBqyih}IT@bMp!&qls3tG9u!c3MK!ESJ*A4g1b$2!5zT$facbwB?|b_kYQP8VLm@O+Rvmg^kR z4Sa!OT<1`KKA-N&9qsAFDG)XS?Sw!OHx$yF?F%G2kJ3hZxgc=_e1L$Vg8shlB0nIW z=!_!+*bxS*rV~brr);>2ic=@784cvm-dr!()eoJHLm2GiBYoHv&UN<>$&mtyYRUIP zUa_FMg8-^SB*9%tR4LaTSkUk4&EeU(L%AvL5}=%PPDrpT9CVRSPoYei=$vGJ8?t{+sr-v z>GcO28oO4n?&!!itgEkYIJgRi%^@hO(Y|#3%7(Qo*GR6R?5G=r1lI}m+Jd{_ZaLbH z<~pH$vpjW!qZK-l;u^f)(>rh^$3Cac9Lf!k3~|8$W4#Fe#NrP2Qjwz&>N$=AP`G-q z$(kNX%a3#nWAO;5yLj@c3_vf;jGh53PfiAqKh$#&3L%F-B_M`axLOIk9AU0rxpFJk zVERXeLi+Qjkzom$sz(en5>KUy`r|TP8z0c{GgdbdR8%R4*Bi0D4aPoknQ97 zGEi>Vp9xi{$IJ9E97YSAb1LAFq+ghM?aaIVyO%GYdF>r#n}9VtkhlI=Jw%{>b^V(9 zwe{=j*Vi}JH`Q-wsBdU!SlzIuVQs^@hV>1N4NVOjR@bj?SiO4nn$>GpuUox-b>r%$ z)f?8-uW4Ab8keZou35Kc{hG!#O=~u+tzX-)cJ|5)*RXE& zx;5+8u3NWm{kq0=P3tzSuV3G=e)ak_>({Pdw|@Ql#`R6>H#F8aHZ-noT+_I=ab4s3 z#>U2`#tlvNO$|+}o7Oa~ZCcl~zNxXPscFLoptu3mZ$Qx-kZl8^vgpf0kuJ2+U%_Q( z|9Zb+!@=wkB+C95MzkS!I?}gJrYSr=$IZ1I)j2vo%sdxX;z~{o$HO?+1FB;PkN~e- z{2*O%1Mi*ws+iJM##|L$q^b|DT)Fa&4(OXcn!g+vm$%D#TBVxJfwDe!C5QB!CidyB z^QjZKe3$R(%K2B5=IxAd!>OLRmla@YZHqTcUB#)_Wts3`2C7MNW7l>vx=`(eZAtF{u@|MVvd zKmNqWPyOd#eBkG%RnJ(z>H1s0{pxR3T|RGpIe+1j>oY`3d;Y@vk3-I}ObeaU_Q z>EOskV_$yc#?DJl)X%!&qo28V*U2y3yPQro&u*qj zJoV=E6Ti1&#;LCzUsIDBoQK;i|*QODbnn&w9Ll*Nox!jeT!se)>Ra=Utay zeEG$#Gp-za@c6FE`?uDlPVB9nHf`*y*Cem6OAMqd=T}u6Z?2uaF_}32>WX{+&)7d) zePh+ks*3w&Z@+QV*q7cnEm4(8zGrR4@k_6*>bm&WnPZ=8T5!p=RWqt9E}b^^%zdv_ z&91zpaw0y zNogx7LurJ9K}JXmjPW9{Q5=FiOdUqU1tEIpYOXR6$Dsth7lJRMj1nqn6dwUcCW63S z|Kz_;_WIo(JD<-DAG-5hYyJQGk)6Hw$;p48v4A-~7KqWxMXaS~?LBvFAuY>#YbKQr zhw|T9K62xbVfur?p&?&r$O;Y58d)$pcWmAad*;}9!1f<;#Jh%Pn{xtTbFP10Xui3? zw|KVWU+G^xI$=fwYkkL?U-UiT`|p7Vtv7sc2H*0%?SE(ZOSx-qzUA}L^HVo}(p~VA znd9$2)xie$ddr#lKBY)#p9)0ZbA3WE2^@f{nzW)o4J^sX#&;0oLMDd^h&w;0( zIsL4&&);;>N+T-6m@Z@u|X3yEY<@Nt~`{@&nLeBgMaJ1`iCp7ylu}_e}BxxnlC=`wa1@)=D8O> zYsBv?MsHjE-S0pB&RJ()usLLn+sCf??e8w%z2TI1|JV1rx1V##RrRMD&o+Pb3;H_) z&g9a4F9i0z$C@7)nzi?#nfV8Ui--5l_s_M=z?#6iK*(=~hC;JOlH*5&&I|bi3r2?h zmOte8(Q$Mv5cH1@nKO?Ho)wxOIzQwanlqLRoa(2cL4jFA<6|cU7A~K3b_Fh7p05Y@ zRs8dY_Pyob6q-F89#(tYr9&e_^M*EsRt8TSSrwq2!@qWPRbbxGs6YP@rPi!H)1SZJ zI@v$&KRFb$RtERIGb?PZnYG%#WPHhZ{*!@ycg`I>s&GeeO>iUaYT@DhV@vmp<$pMD zuN}<4F#6hk{td%>w;Y+TTKVq;N5UKZBSSIkG;3^V&*&om1%XY&`5VFuMrIG65y=0y zp$G3Dn-f^OKd|@5$Ard$!TcBOy?+Rq&Wa(*-V(?^=AZ8$pE2$6Ss$k-)z67^i2i@- zcF+ezS@k)Q=MqnSzl2T|`r9RR2BL8#Hv}Bx_F&q$@LeV2h&j&UF(jsf1zQupU$p;> z_`0ph&ZYZ5aQ5ycAH4U`{U0=**?P{7XZN3D{CLT^M(f4%9zMAB{9nJcWK*NPf0JYU zc2mc^{sJRwgpAd6`uEX)W@0pYlx^;ym(}Mp14o;S=5HQ7X?WNS2h3qQ1qE07PqtQs zO=kl&2w3#;g+_b}&68B~fJG%EzImq4cOspu0zTSZ%tb!GIjS6_0`o}UY&w%sHT7+o zA^(VPk-3rDj#0}9bx*zegR~=se51Wy)gsYMeaZ`bC(`oqTo#%MGeDmTnWkla!1RU2 ztcy+G@M!3C-+b!NG&jsNX^z2BbJ?)@p@2C=3+0>Z3;6B840<|bj+?aY{R@4I=)btn z3|Xdcbl9Yw*u2WO)V$gs@C}fi7s{iltt4P0xkA3u)PbHu-v`V5(;_?HDu>%W*|zIDTN5H~0NYE#li zG4D0~)=|AJYnrponIV7hTb5dxIclrYYo+Q;-z&6iLo}W5JI_*)OV#$E><&M@s6k`c z^!aGh0 z)iZNw+@N>1VZJ*+w|?^(6TUe#AecV;vn*d|QQ!{0u_180WzIBb2hDNnbyly(;C8b_ zZB7Z$76|PM8R>jyaCNuj0=^rcVJx8VBAxQa9f}>R<14BDX#HGG&LZn!oxP~Hof^Km zrWZ8~Z+tDqcs?l_LmSpLYHTq-oV)s$pws0OI?r`6w?D`(c1pj8l%95r{eC!})bR^x zx%+n$qBSvsY=^wq3JPQJ~PqJ=cwPs{inTu?*wP4e@ExV_w)2vSC!{D zPgQv~)L)P3=3iUg?^Lzd!}g~7QJG4x-(Ka@{Zf(7p4ktoynBdzlw?YC`n(CGj5u0KD&ZvWPK#+nO*`tDYSF^8Hg(epl^;%a~9$Ccz644UT2 zyx%3rdbbd972BZOcbZ@eu^(s#q?Ny5#PPDe~)u+>y4j8bqjmdhH(zn>rH8R=j)BfsFq)o>XxA4=(cKd z9kuV@g}fgukVifF?~}c+!{O?!$-(QnoBHX$tLZ)IiEpF0e!GbHg%s!4-&=n^#d({1 zm5!i-t2KCR|@H~q1e7*4~#ras_jjMM0@L*)AA6^CuA9nlPPW8O~)$7aieV*dp zQc=^w#dfw)$?r{D8E!FGc)V;gda{u1A>Ys+h_Z~l@dVbwi z;BfuFMfH(>)81bZOQ^kB*jkE@#PoG{o!!S`5vn0 z^R#*$c{#mtbsXU3^v2b3OdaFY<9pN(zkk)RaDMH)@zW{J$4PHojXgh9TeYV8@14CA=jBxAb?)b0iq9hR<6-h#22H)~)PBbM+LNC7 zsCDMo%^O$!@ciGVdVLBu{*vPSzVXJ@dh_->b9-=Vo%T|^eC>|0bEQBbAqC2k)B)$vl6IbAQ7h9$a4?^weXV_p3*# zp3fT&)$@LJfa1K(C~W+a?A@<^N9JXDHZWK&jPr7R`-))iI#Knd-bdUP<4aWmJ(_eq z>hzu&#zQpadW`dN;MP5Z>mOB1FKzE}7sdI#rUGqxf7$(1@82fn54HXWDehhW;8k+{ zSCIL-_9p6ymk;B-{w=EK`3!sZQ;c){o~sA1`!-Mi80Y=&Z$2{EzG+WA#(De3a>3r; zXrfZ%Po8>=^YVP_n!)-$Pxb2BPCXhFKcfHC_&7~Z)6c!}+bH&)L&UG6*vUG6kow_m zxSnGCUM$kjPyBx1scMqezbYwFp3gZvRr$In&#$*PAJspXm1)ZR3Qtx0q;7A0bgvJ;2d7o4JFz@%?_-2Z8jW@2|pBx|l+rf3u zPI^K4EbEP{{pe8fqbbhIH#LuAk9NMPp;ax8kKh%8BoF=aROas?`8me<9MIWw=>3~NSDP-6pTAZAp}!|NSM}E; zuBZI@8^>chUj2yrII{oXsZxUC>QSR9tyb^BQ-$~^`}sQM5B0w6>h=|yP8|#T-Sqyg zU)^u7^89}0sVeAD-a9{Tdi3>!?W$rEWN+Q6dj~&%sQe`PV)cX`e@*}MF~Ydx(97-K z*I%yK=laver~A_b)Q|V)m*+h5QO5>;|8{k~nxA*MqBKACIA-7Adh8lHfjPY>eMUra zb$!o|3&=|tG`$vl51HTVs_b<#@2|?%4TI}u?{cV|dMu^9H;zu}^;fmtI-}HIkJOgI z^_Z&JLjCdlMZHSW<8sP-&(}%n|4{o)<}3aFb6@LEE6?_)`=~viYj;wAd_3^RU!@uI z>+m>j53c_Z9Yp!F7jJx$;!Ar=XBdSW2g|QQpCxlnxd7LRn17V&z1QvECG&AXwSVcR z!FfjcS9D#_Uo$0ozd`@6QN4FN{+`Uo5LN#+`55&?Kic&3oA>>*?C<3D>EZfYLG|9{ z$ND(sd3y$F!B*+{Zl?Hh{akHhb*-iz-ncr(t?WhV=b4)apW7rU#_vaOd<(^SS&pW^ z^USZE`uvl(fj9mkit~Hn6sqTQHUCV*+nL|Lk%#(w3Gc5_bt0z+pR@UR^~O8z>{jtN zbO%EU^r$`1Z_mrZ`!Vl(@dx|Q_}Kn(zuD9}Y5u1tYCkxF_J(k$zehB6OZ79gio37$ zTUd1Dd`fd(GsY+#b3o#buM1?Y?Bv z=zVd0igcF?`cmzVN&1ERo+~GNVfsqxw(K=i&*>}0Q_r{U%J!erJSOS>L)$0yckStZ zLA~$V_d2*Tv-S8B*7u&$_r2*m?vuSiFWV>8jTn02OVquM)HjDG=?m#}N5ZL~`gXoI zM$58`zO32%=qIK36Fy$#!F6yKZ0iF#w{L z-T}A4G5v!%*Qdc{a1-1CSM`aK`?K_ih3qIe2QGta;KI0ky_#S?81VcO`ryFMfn9J) zpIErQ3~qqK`ozQa32+)5(I+OZ&w)$eHaL8wT>mJz3r-v*>$Bi0xDDnL9WQTPpUl}| zeeuSQgA3p)xB)JPCAZ6Q*d_7{|Jh%d`g9}TH-h=yZg84$7 z^9g<7&dz~daPnx`e+gU%+skBq9Gn7&m&^JL*abJimb!4G2d|Fe9=ts>;4-)kjzpn7xD0NA zV{2vmEVuwpo*?U8a06`XAHsP3l3*9y0NWen{E}c7+yL7#Xb*P54X}M8v0NW=+d$0>`fUQ%Y{rjYA;0`$cSF*kcu7iy;WPJ);0GG~`^;K{K z96n3dN5NTe0o*!Uwzrbf4mb_YfeYuz_Qtu=32+YVf}4LW+ecH<;my+gc>}*cJ0FsH z=Mw2OxR{ao3b+n#fxF<;PT5~AE8PUgJ}mRutE5X;OV>Xt-2%t2m3e!gbOc=bgv?jL ztvhADd$)As9_jGC(n)aoi;#avy8EznFF}=UKAnM;Ql(`=5aD#RKK|M)`Ok3GM@lfz`6CZ-crBN z^Yotv*VHfSJo(Ifr1Rh+xB@obC)?K(($4A9jkBe@U^^-Ej{f^-ynP$$7w(?rxAX^M zoOd_Nd>LE=yBErOTm6F8(|`UF>D+GVCOC4X%vZo&aAc3HkArj9%Df9MgN^HCeeQbc z61WWx>kkxpdq%-2{dameZ+%?$?|{R%$b13Z02gnS^%byvyUZ8CEwKG5Sswv6!0u;c zeHmN>H;b}9^I7S{ebVjEN#{$_?tba8{sV%%y`$g+I1SE$U9h2kLGIb#lm8-J1m~ZX z`ObHxYu}U3eP7!6fpiHRQC~RpEKll3(rs|$$1P29CF7z5;fB33;&dE18e#-&^timjjo^ePVqx^QAlM zr0o-=GhlbU%r`bjJ2B}Pxbl9PkDeo4Jy$w$k#q)J1Ggt-ePpY24(x)Pcgy-NIC8Jd zyZ1?#!G#Ls!N!+mKKd2u?EjFift%pcKgs&|52Z8U3b+OCqMwufRrN2Zc>imF>pzk8 zZE*ajG9P+y0Sj=N9iiKfqqBUhv|32-s4LOoHu2@4t9Mq z-vrx1nNNW`<1*i{r8|qHZAUt~RyqkTgGSM#`wlp{P3EK9r4!&HxCOR%$o3I%{SukCGtv=oahJ?j z!S>}cZ)c_B;1sw7Ze1g3I6< zxD7TQko`r#ov+G#`J2-91JZ49;z^m0)ur=KNjJc4u=}*EuYm1mWWEEo8Zw^(m%t5h z8(jFF?5_cCgEK#p^#yPX{k*KV|4ljq&VmaEWqlcJ{7mMJm!+fN1lR>Pe3^3lf*auU>$2YJN*DepU3){i z3GRUNZ_0Y(Ptsv<(d>W0hRoMf)47dcg^q6_BkAo}VHaMrp5O8}JTn2Z+ zwjM*k?UUdVxC*A*0EzRt1CD|nTh_L zew@r_j+f4Zi{J{l4sL*Vsd;2OAovaGLtP`U|j zgX7y|eE}TZA@ezK;u4upgQGiT-n~@XzDzo{OS-W~I{Pu{0=Nz~u9NjKa0Xli*TG$I zdI2RFg7J7j$pTmu_-Lp`_vE`m#+lkJlwX&2l8hxg0+9JmH9+%M~k;0m}7 zZh^aKJph7_2i5~}ehxSWPJ%OF`$5@X>LKY8xC2gpN!Ay@EpYTz}2I;0ieRn5<7dE^XDM zyWq?>Wj^sQ(p_-#TQZ;jS83}B=_EJNV-&ze~qnmrnkNbOjvk%DfA<|0wfWaQjV}?+gXS=PB8cbOY>IGM@*xz~Nz8pB#}k zW=N;OMQ|P51-rKFuL^E~^Rr}q4cr8Gz|kXQ`vkaiq|B#}l5T+Gvt_;lZqJeVR9HGa zSGoai&Xf7XeCgx@=^D7PQ0C){q?3!KYiK=Sk0v4KA;NJlKxPeDyZz2DlCGf-P6JcffoPTYi0#kWYcL z;2by)E`VKd5nKY7!4+^F+yb}3bYpLE{IkGea0DC&C%|cN2Al^Mz$I`QTm#p^EpQub zduHg0(Zgor{(%az;SR2oCO!aC2$qo0Jp(iuyu!A z-Y_@4G&l!#!DVm_+yr;P)@NY(!4Yr_oCK%AS#Ta)1ed{8a2?zNcfr=*!}@_E z;5ax1&VX~^0=NXOg6rTWxD7V$g!KVO!3l60oCW8>F1QS?ft%nC*wQyV;PaOQj)9Zl z3^)%ig3I74xB+g1jk{obgQMUCI1SE$U2qx9_etUH)qs2(Ynf;1sA|2 za24DDx538Ua(TnxC^!L5gL7aPTn5*`4R8zG1>5(?<&S{l;1oCm&VgNU8C(N5!5y%5 zuUuXS90kY0DR2gy1H0fdxCU;5J7DWRSblH}oCIgUd2kV20oTDTa2ITRPA+d4?0{q7 zI5-7PgL7aPTn5*`O>hg`1=}T9KX4Qr11G^La2A{cyWk?Y0RmE zH~~(Bb6^)-2G_t%a0hICUM{Z#j)9Zl3^)%if-B%UxCQQl?SGKV8v)0`32+*m1sA|2 zFyFIs>ii4&Cb$Fcf{pv-`dDBa90oh!2sjE(fRo@1I14U-U2qv(0oTDTa0hJsquf3= zI0BA?6W}B`11g&VX~^BDf5$f$QJ~xC!on ztuM;;bHFih0-OS8z&UUMTm+ZFRd5~L1h>ImuvM1p8wN+fF>nH$0%ySma0y%mH^6PM z@qk?Zs{Vbux=ouJNtfvl-|5{iUj61G*8lazl&zuO$9Ge1hNR2G(m8!UH`TxDDltRm z!{gFzTe_<6PsZ&FN6LI^wsa&c-JL64pD*n$luqgUOY!`iB{JV$DqUG7ozwS6;`Z@~ z%(wOZJUHLc_w!&U^!+^8Eqy-@c6O)iFRAa3!TGAb9|k+6?{~rO>ibo&tNQ*E?3}*; z1UsYeKf$*3{Ug{ZeZL5HTi?%tozwScU>m=d%U{*^SKz#(@29|y9MvD+$u2t5wz_dS zJviUd_b*^)R>^#Qt#ni0Z-DE=@00nWz8?VR+vmx=qsQ}ezO2XRvm<(ZKD&5{Z13vv z_?+*6i+X%L=firuJiCyW{bludcg{!jcyxANj~{2Z!5uw5obyIW_Fn{-_4seDFY58u zY)g-?W;emE9&gS0upVE{u7cxwyfx>udVDoI-jd5(1;_OGXs)k-(|UX~=eyvn9xu)L zlpZh5j_UEz>?$~_$45`)!BIUvn)4lSMvsr?d<&e^z&vs`qTYm}+*bCe-u)-$R{6)&IS1OjDWhQZ&%;3?l%ce?hgDWR5~+uQo*9-ivU*s1m7 z`RgA)jOEl;y`K8Vp{e`u^ZoeqdV8pOsHfas|M=0{x7573J>RFFr!P~^`_SD@g%;2= qwZCzD{yPFZ<@@^atHAT;*G^0C5iRX?xor`JfOxbOGzF0Y6(tk{fqZMN z|5|%*?Q@PLzsQUIOFUY8X4cHC*Q}XYd(ItKy!Fb;ii*IV1;GykhHAfrcg>n{}*BAEuCUx<2^E&<5J&*FNIAwQWO*@$wM2C;fvV_yyWlT4?3 zQO{DVS4pqAy~&lRmrM_#T3~6F!i972NA-d=@<+A~aC=2nz&H=nK)CFhwT+U&@B;d) zeZhr68x>eY|8u^-2pBoPhTv2HL=5xe(?d^lT0`)W1re=%db_;ma@?Lz4+!kjgOV0< zT#MSF{IRE>e%hz^g?NTnBai>}>G7iSN2UCcu>6sSlvh&zQ25?>13Y8$-e-;$;p--R zg*=zSxVIC0C-LopJA=UGx~06!_esj{Df?-oSFh6f0aO<{T&=RfuMX4TUrlKZ5qC01 zAI9@*`+~sm71jX(mm5x$d-4OZa;L!8Yl%$1;A;?co?*T&pC?}*ohM&OMUJblTdBbF zfvG4nMx<%-Z3P&Xcd3 z=E+x{M~}zXDq{TeL0<<>QC}Z9!+hN`Prg>oldtTD9gna3o+oyA=PB~_x6UwMSI?8L z|L}V;dwm?giCnSc^7T=2e4h{cy6Y7Adhr?N>$mTU@#{GA>%H^j>s)@*_UDPdo~qw; zZ1e$IrKIAJh8)jPElXqe1`cN%#*JV&6BSS=iux1=gB(f&Qs*; zOU^J~|AzKG=ChCZuG7TVBhM3kJypJbcGsEpoA%F>uZ<=73I{sc_g@sS-*n-EhG0=l zZ{z#UGl_lY3uAbB-OJCjdB6Wu{iO%z!Os+(fbhfgV1K23!Ghp;{^p6C4^%A#ZpP30 z+#2V9IKLM2mFL$J;p-Fci|oa7fv=}JufF3X`FcFRqAHBf%T6eO1U0r^C zcq)ATJnfH!^YC%>weke@_15PYUr+Vi^&30qo>xoR;p3f`oC;q*JWsyzKH`bk;nzRM z_#6YdH|NP$c70BSuRq%#*@5T6{CcYUhkYl>*W>NKo(f; ztd-=qu0*j3Jq0btaVp>CxDR&v=sCPF2$iIjO2>z*~9g` z_LuwDb3+ikYc0mj@g!T^tv7Pn{@Pn^G&=D6U;FJ_Yx#Z8%{SM2?T@SHH#BaznR4zi z&ErP5g2UvZa=+HBBOd>Xow4y#J$v|1ul?rnGb#K!<@-ADJ){ret)_e)e{;g$-uSx|^P@Rs2oN4I*vN6M{JStt9@#((DB#l_``g_#s}7Wev)?D z@>xGGzvGYoiOXENwyoJKK7Rkiji1=V@9%uYvfoVBFVu4m**Epi-CDLT#7EVG>dM6; zA?=kgY=_A>v1A=a_X5Tr_iw8!E|mI(9!WyG$@2Gm11xW}*r2CcV^veZj}cF(bWGy6 zdB^m#dcnC;|3t6?=5Kn}mR_6sZUu5dZh^J4wrH=%vM z9EgkT`%2W~c9kTUx3VvmjAVa&7Uc^N=b&KlZoKF5e4F&t-$j4WoG zyu|gc8i?pn0GT7cZUH{m1AJY}mF17CH;MN+_RLcR6{$Zub?QQot> z@RJIDMN)qo%7uRBIlz+&u9o~HKSKFn@g9&O!oN^Hs`0L{8R>aCE|dSm^qR-DW;6dw z@*@9|J(jmbU#L^)H|<5S;Cg*uBsV+XAQ&8mC-Jk2Qu^h00xD?Ae1C}KrLYU{Q^CvS zk9Ky@=t%`%5&bGV?z!}0lb{LC>qlyI4fSFZ+9}eDwJ2})WiP}nxL)7$JPOXJpSS## zQX%PQ*!){idujZQ$6<5DQ~>sD`_k!A~n@C?ce+?@(J!U5^Bza*`V11HQ^_BS)dls(gM?IU53Mv;jQ8`v2o6oF2 zK0PDjOi9!}*^wK7?IuFQ<$d~?b(cvYN=irsgIZ7E@%t{9*CndU5_RB~OMwo6Zn_dSiTu^CugBuYRSw zggQ^pA`&|0yV>n(U8kAdb{9yoz`q@7JAJLe<(cm^43RfZ-G%gm9@i#`tl0$bS-@Yd z(sh_%emo01J?&>zNKPvFfyk4q#YEsPs+IigYRE^n;Z=ao&q&%|r|C|-@6V!~@2Qo1 zUjaORGx}xgT7S7@hkm{FhwU}rIgpzJO(mp@^JfWQWzlH8yUZ@LwFpS*f`>LkL#H{u%1)VprG=xh%yCF zGrgq8T?BjQRs;5GAAK+E9M_jVQ9Z`PeWPwZUAPkE+k+npjeQU2QoHI0dG$ZS`QTEN zFF*`JKd*gZm)KQz-I5tD;Wk%EYUh-0bD%K3Cvdln3!&Q zIoMhDlXyI-piS`j1A|h}IVwnxy9X)TTOKFBqvVb?%fP=>@CAM)-#zggf3DWMua;j2 z?q0@D^v+2l<#-<8eq8aRr=KqF$6KUeI3FyV64+-BpGugkwy~yYN7akY)_a*NtMK|cPKd=wh&HzeMvtuQ1w7?z8sfk?%&-zZjiH`70rpZWMlj zzmhsgkAEFogCOX=Xg0oba+C`GOYk|}k7Yf1?Oh*#8RPiOt#`k#s}BCVv;9lT|G+g* ztmpi1uKwwjU3FiS`b-yo+&c7&?bo5l&iNs~ySnGoo4eNila#Y{O)3EFa*#V)2ZihH z5$MaRdZjb9%S}{Qf{x;6C;0Sw$~Qj6{qW#?qKo=psX*zu40LiM(8I3!?}HAu-!uY! z?5dx^_^5q;+)}0c$WeLUwH*95KR6WtF4D(csL%cT+;5kX+gd457(zMs2>2S>5nI>l zIwXmo@&5i(rR)*bHSS@^kFEE){NG(21lLk?Y!%sGc$&J93f4#m+&=W9R6BpYo#+tS zUzPLF|J8nvKu+B#=;u{l{p(at^*h#xFLlyjvAiq3gDUsefd1v=Jrz7Cd@07G^-oZ5 zQh}&K;115FpRIS@$dI(>Mh7Kjx}HOGb1_{ZK&&S`f>Z!`Grcjr?>!58>+S*l+3vI4 zdn_-h-g~4SmhY*=e)(m0MDic*YrwpR^RwH0Y=$wrt7eeCeHv-o6w;1KN&WtbpGvuo z35Aa;d{p6ag~t^>qVN%g4=H>|;W35B6dqQ1Sm7ashZG)Ecu?Ve3hz^RK;Z#}w=29| z;XZ}?6yB=vR)xD2?pC-%;SPn{6mC}zs65bn-^7ya4 zqEtVARSG!WhjqG+DewPZC-M{8*>eanJ+8MF=@!g4$^LQsTg3lQ>H!u~F4rge;m+*_ z9_>fy$6GtMHP6bA{G(Rs7t@RVPH*Ms(XZ7%VLu?*ugj$!e_&kn$hl#(JLmf74bl(0 zZ^36Ek0HJ0v0h@iW_o?6B)$Fz`eF21_9)=xe<$gx`b#8@`*W{6pZJr9J+S@RRB)4& zV|vZ@$M*;yB7Yq7XXKCHD*65*^T%&i7>2{m)a4a# zeFAU2;$1G)Xg+^Z+Uv=qT{og~Gddve-9a7a6_7(Wx*h$y0&?mvQGfqJ#aj)$Cih8s zO9kIJBkiSvCeUHo*Ch3sBapMDUjm#y1X$%Q?3c-pn?U_JXPz@g+ZvV zD=EmD9=CLlq;&7War<4$E?j>*)erR#=OH2fJox8El)t0pAKO=vkQo0$i-_T5dvi|=qk=Etl*Yv0V4gN|F zeocS+>k7lK=}&)6VfZ!u=_!TbC-tWvR~UX&fBN4QhM(1+{)WQv!}`G#bBmauluh8pJ=r_yfzrCTks#v&XJ^lRz4fr6VU6Oxoyxtod%brWj9cj6{|Az?KhW?hH8S+x zr-$WzoQ{7$_-NlbEWiC>jDhjcy;nu$CrLi&-Ya=;bf9~)!urQz>usOz2+MIVe7aj; zpYD@1-j99KkF7=Tn|Zu3dF>#W>GDzX-(0R)%Da4z-N)1^C= z;vvez^?v!i(Y^rZwqj6V%|cFGTv|7?DVly|uXtv9IkY88flxm=CH1Ck&2?}_D6+!V3*={wLzeqXKU z^_e-v_K#B@?lH;=YE1zkQarc95q|ZhtrO!j?N0-|5xV#Lgod{^Z*Xk9;OO1f8zG;KB zV{ubP2XFDG7I$jnk)!}f#uy6Co2dU8JS zhjRRRsqfQAB#q?#tNMOi-m=ZVD)*fe0-OExuUjGb{gqhv`fJQiz5{&E zD;+b3fG4l^B{QZl=t1Yd0{7(6PH*NTXh-LB_eitAso(+{Imyw`ptM^=7cvS=mpFY6 z6Mek;jV{+Acx-{U9U#{`8V+Y{$|N{mrF8dfBX)tlYA%ED<0w% z8|NhOK5!Ix)&F2WROm(ja9Yg_p(nI2_l^tfGg$v*@9k6=>!0ks*C~wkPxjspg(rW? zf4F~$$D*?Ax zzD>ShNd<=me*wb`!Z=Z*gUZbsje9mZwEc1B6F(M@?Cu=|oRdWO#QNYfkPFiTmjCat z{6C=bpOCXqPN^UK*ggiw%W#XSU>N*6WqHTBpSw@xeXTS%r+h{74!%Y54!O(TZ}JZL za}OR8#O{8TbKS>_JF0 zT&8b%xgQfbjN~4270Es1G?IJhNhJ5s6L+u4J@mwrEbtS_{SD~n3Cg=)JOn-J97fs% zdlSjM$xnyUds5nU_fAQgeP9~t;J1+8_k^VVO*^D~|67%ctAWUJmr?Ph;KXa-dg#`YoCddiAGU6b8L~x>;eRS9*iO%I9>W!aCkG{Alu* zMUH&BR`aKYF8%4W!ZQk=tMD;}YZRUY9n;&Kke56 z4!?6L`@N3VBYP+H>Hs_ScPuW~Y5YgzUsXv#ms9%ZmF}mxA9J_g4f^L*?tb3wk3sI@ z{AGT+Jminp--YVzhkwKtI-xoBYW{Lc>h4NAfk9d zdFGlQ!+9n2e;yM&UhRe(JPLf=NqXkO=k6Pqa=rz=Cj_+Kqr9u*KOvu&Ml6Katm2Q%sb+!D#=iv5mKkWR}_FdT?vS`}((qVit z_n#hj;SB6kt$=}NB=q>VACvc7pY1u=lSPv`=a^B_KBDu-0zg^MxOckXzqF7nVpB?{D|9sH_{>Yh3UA7 z_Ke&J^wsJ7%=~9T3%~y+fZ-zhulnRhq5tg_w@JBF@JhkolmCdwMQ`TMV9)aRLtZi; zQ~3P~k1D)J;lw_H?q8lUze4vRp6&eX$qfmeA~{E#XPnRReRt;X?{16sucCUtMBHTk zQ2VTYRCVPcW^>@i)Q;=<6Z;Fi^yydmuSe06F#=$4zk}avo^=VQxg}Fsk%Eh<^Dw<75A$3?8i?#>WD0 z`8eBmgFYLD%;NX+3x5G%p1&AQ>V12-ACi-K+U4MHm^_5`qi()+H@B z=vB_>+NLn*8m+T2UqtI{&^cOX8=afAzR`ID+HGyr_s3+wKIBJ@*vH7vH9yw;T==n( zpNn}f@^d@1KI{V75yh{1)uei=`vmk|BjzoYv!s7(d@=vT{7+lQRtK*Ue#HGv@Qb}1 z_&LA69VxGapZ$I+97&T)o(EcD{V(UY@;)yW4ea}DwjXTyZNJprIs<Uz9KXr)qKjzvWiTkNc2_Aozz7sjq z`y$VFza{#;FSQT#;Ggm7RQHA2E59mwmF%G)G$2Ql9l%>%R-&W{^u zM7hxhNtsT|FU5H!&rcRt|CP}hD}^vTBWG=8M$JJj{xl5)JR&ZE|6XClX}J8S6vjrM$=d_yRv3@Q^FsG=`sbSl zWIXZyo!=GjpY5~#htS==XX1Ur!4h~~9`to1pi`W#kG7P;I|aOZ(Q)?gW=h~SddA}m z-`-ma-xq*ym*OM68X(b$`++P6%!f-#*8fMT@Aq#Px^TQ>ZHf98H^lwnhw+_L65prL zpB&2ZIfk|0Mz)0FqgBpWiy1yDFvSxi9f6hp5(YK?$XYA+^Fiy z!66whkK;ctFGbgn%1HeVl(T-)_ZCtw?pO0TEI!9Q3_8ZY*Bht&qJACZS~Px>pHavq z_vgB|mFmxjg|6=Q3DnC{L3+x6H+Gri+c}b7iSn+$R^Gc?V3?Er;`Y1$i}$yTPo@uh zK#%=>GJc~6m;c0DIbIxo7~9v!zX%=WhB%Ic$Sd-~ep!4yxa@qQo5tU!f}Mij>?ikI z{5b3PDCnN-Z%9AYKkmV@|WJK`IsMl z_?^cLDw=`gMg^o!^xaGVbl{M|wx z^>`en@5^bQ)a6uvc$1YLifaeG6n1&gd)2bDB;UqYU9ntX9?yp+XUo$oC?^%X2>OsS ze4vlZgP&c?5U=UNd?FA%E)V{9EyH{r&c}Jhb3XKfzMqqRcGZ7J+KJ|4z*djq`X!(H zIT-U}yspQ&9P`h9uiEaJiPKN!Y5kpR#xwgn0LWFm{Fr!)iPB1 z$EQO*5WX2dO7XFc_4>XB?;oB}y_=Gh=q+ioKQ0gXQN%Dp`Gr*;&Q3r{nC%n56f%-9Y)j-Yia~M zs(x5&1K@+|hjrEpZ2nvMxY|#nUS9cV@p(Gm`^+(nL+S4`GXnRhzt@|=cWk}-JsjPS zN)+btEB(%u;m|!wGMsSTQ7(Se?x~348r~%pLwrm5SL0Y;JJ2t+Q#Oy5!|O)0KkElj zzqLW?XV<3{ZdCYOg*Pb78cXf9Hlw}uOB8Mad~p@vrZ&JM=vNet{W&kfbe`{UV@Cuv`T`_J~p{7;t~Mn6j2m%#SJ_HF%s(98UIHv+oE1(vreSc$!gE^;E1dV*gEpD*Qo$j?7si|KL_xlnnCqJzw0J3o`LTTgrYAoZ z|Kv} z7vk6P(f4DZf2gO1e}4kM>CcJkC*&^DAMiW7-t?zI+K=?75&aMKX9M6+e^j4F(2ruh z@1Nc8<-*?Jd+(C@rwGD`z_{I3XJlkgaqx#fl`V)X2ZKgla zC)Z~Bvjp^DL7>NX)(Bi2Z-3S7ewWi9jn@nHN0*Ae4&&qB<=}==K@0GW>3DVxN&D{3 zVM()hk0Bk<`F|hg|Ni}md+cBQ7OCHV0C;?7v%p^C8ZGXC?5p-0bc)WmL8mBw9CV7# zw?U`qd>eG4@9q_!(#hgD!}D#GqtGuI1U)rw+~sNoj{FkHYvh+eUL(I`gVr~{q*-D3 zyR>dn*!+_Yh0Q-{QFvVZmAG9gwH_;sfKR;7$?Gk)SG>Mtdvlmv*&bb|wO71V~!Z)Vh=ibfh(Qbil z|C8m01^7ww&XY}5g{MC?~#Qwnxoo_w4 zY2imC&mU5G)_LCc+vhxAxQNvx^oLI5eBp!tS?YXYJNlvfKrAPH^d>H6%{jK^0zKu=OP0(YPYueu-L+EB?ZUoryg#RI5%$;G3Hv*YdUN*oXM)exbKdaGqrd(1!*s>xJ6*H-%k=%;M&2iZK9}ov z^`cKpKazBb@4Nop4bi@9BzJ3PwP)YERaZU+Iz6I(i?08e@4OW;pEzyf%fHT)8s{f~ zU(XBO=swt|%cVPk+sL~^Brp1VI_^#2yE`~0<$Oo21(9ziO|(_h3N-EXCH57^gHrT;NDN5E$z5_ znJPHnV?uj~gV3Mha2+OGh<}$n1i8#%y>R05r>)}Xs;s{ZjfKSm0biMI z*Uk6oy7|MpZq~T|{w7^F+xLNs%dDF_ZQYD@ay zzwPJSI@$K~H)y`?=Qk@pTQ|2T3_Ucz(atqPdl&BO+qxR-X0~T+fg^t5`=R(ci?U<< zsuVu4J^bBUU>A8kZ1tbL>ohXt^w@a0|F_5bUvAx2E)Ly|3<%v%H4feAM)#-bdaX^; zxL?5Qu=%XlrW5f5a*i<~!g8Qbr`dMp+6iyipI zx=iDg*1`Xa&(myAd>!O=?tQW|y-(FF{G#81g#CWb>_?qk9QyOYuJH`Tc5UGc!mgQr zJ}vVR&)dJ7i2LI83e$u2Dn75TIG_CLFpluOKW2?PZT|f|j4ig$aW3Fg&?oKKI?m1y zIi7nw76-=teJ9;R67P3aB0lf`iO=6O?wLgW2NU^|iTwLw`Rxm@M|?x3MfBY6fpyp7 z_YB;1xQEzXgL{eX_i=f@rICgfpRf6QYoBQoJofumypE(U%6LB6#q0VR$&c3cR|$bE z&Vcc@QH3O4rpHLh`mIvmZ-c!m+$CVJh{6NM>CYsd!WQInzxZLiyG!DIqvZPojiAep z4U)RMYk4?9VXtHd>?d_IoKu)`<-7BH0b5+JofC6=o!6G4gRbk#;n@v*aXi0qO({Is zFYx;tw7=9KJ@$K9<@d7&rChi_WcS$LS1a#J{caqOlgIPqfAGFZlCK+4&%QUWX#;!@ z`V+2ywVfpW81EqMr$^tf>%NNNEA#!jJJF*27b?-duA%CyYTPNM4^%z5J4{(pJ z-4Ai9_ov!-2h~+rXZZcK;7?BZbZ(!tJ2#)MN4YVqt4@YrEhYJ--%S{Om(`-3{~<~E zd)2GfrR7B!FHm`bz;XL}_WA5jDj$}ieni(NG4Aktf2{A5d~khX?{jufHpRRhn-_d^OdwHV_q^o{P%A5Umy(*VWHz0rQS0&%} zM^XV`zrP0Krr2tJlJf5Iu)k(6M6Z5@I?sm;?{6Z73)W@qQOCGsDQ=aW9$!~M3n znEsu3YjR8XK4QEX=#lM9#Q8G7>&86l@w{aA?lRgZrgM7mQ{$;D{>tUM<$ZB{It`E> z9{)=zH955V=u^Q%(jWVsc8aeTJYCCvsP9pqez!*7<9AlfpKu#h4()zn{hdFaXZfM{ zLEy=4kop(W`?D$TJ0;Nov(J}bM*3^_>{DE$l=nNTB(;0oFRb{{(@Us6#bqkK9||0& zpV4b(Td8?!z4Xhez42*{BRbD+}{$wW7G;MNwAci%LO zcAA<2XT|{Ay?%Mvv+7EY=ALb@38Z~$t+AJ^zA5jm+%Ac{PjWm5yqXCix0PUCbXSnLbv(1gYw1l zYVqNQAO78m+YQvuu73jk3-$P03PV1#>!%fle7f~h3PV2K#gl+TJyy94>0{@279Vc? zZbrQmwnx*DzaspfQ9r`)o4tdc&Y$jDKeTtym&o2hU$X1X-a%io>&@OlUtDPKpf4`8 zcZVPcBj{H#fB#ov9I$uE`Eu^~al5blV}j4($H{+?_Rp&m@`wJeeZMr{IP$puivDc@ zKGQq)C$252Z;+29KkWBLY+keexV*}f`Zwl3w3DC61|<~?Cg^4Q202N#Q+|FZCs(-d zFq|I}e$V{zC-n_-kX>*3c0~9R=^OMf(zm0Kn^4~-RF2BoiFYUH(ku)LzxzGqd9x>h z-}G$~{hBxaq`nO(9n8Pjr?BbUpu(naLufD5H`TvT-^P$Xf_@e28|Jx)zh(4o-tp#_ zfi8N#h})?C#1-mKG^;k?iE#^cVXZ{&5? zfVAIUu}S03wY}$U+&2+|9*bLx){pEh^7^40{5WIxN@3k!uKiSi@1@O;yovb8E3+Cl z{`&9;%nm>Eap!+1Ju4T#Fn8QJ{N+=RI|rU;^8V+$V)_)Ft61Fm3tO*}{*&#OxRDmI zW9&aI+{*fQOzd3J|MTX*k+iYE74MgPTK%J&1>bY!zP)EYzTo)wu=sxG_lgyY^ONV5 z_!ba=NDp>OJ>swM$LwEqWffUC;(PdeU2U2##U=ig`8}WbVcX~2^zBmTU|tY9 z{k;j=cb9hFsD8H3)Sz9v2RdH`xDNfWeQBrnxrF-;btJg-(7GM-MqQ12uR(>aU;7lc`iBzvM-uhN0WYEveCi_d~x& zZ)}~&`^$F_%=Ed5=+nPTp?rh-?= zd{KPqR>EI*qBEw-Qg1(7>C$B@(=RJ(qa zu6NsXz58}u?_RCz-F98?cItY!RoA;s+oeIjxDRR3de_#U9ZE0oEBc)zZR`OCUCUg#d|I~MFSGHo9slE4|I4kn%Eya?9;Y5J4m>`y4|4kq++Y5s zUY`F0$muWkDL{vMF-ze+^QV4)`GCydso*`5{$f3)aR#RxFW#+kt9Jdg^9}bJME4g! zU!T2pPTVXy*ErdD@#nr>;~9$W`(-Z(yY>U|LsG%_B(>kkw)Kt0fA&_uzqfq_SEu%k z#Dyis;>qZIy=*-9hD3bYAJIK*OlOW4D!d-};`ftt#Ho95Km_v4-A|779?$FCUMhfJ z`OM!>UMqG!x}RM4Lx#GAQ1Sg9{*KFM;1AmU%!T`iQwtwb{1o3lNPm{?H|aD#ar|GurWF2{NjdU!;iqpyoPW6gVfO^a#x+hgirPIW zXab`ft#^g~EccJTJ>Kq%XWi#czxyTaM)B1e2Nu3{d=VYg$L0O z_D5?;){FY7^pAg+@bPP-{<(+HpGa=#aGjpQy^5EN=AjaJd7K}N;fd<^NJW1D>m0rh zzpX@l!yo@Hp|2!9dPz@mT-^R2&^dq$<(?X%$L`NP`TNqZ0-i^X3O|$W+IJf!_ij}6 z_aO9|`}G!Dx)$qWi{SM;P|m)?^l7a>iuUNfFKL(Mk^ZCoMExFROzBDbJHzky>-Q-+ zK`U`;@$Wv}!)iB_|Ma_ep?psMQmOuB(7*k<9zHOk^9J<9>AsS!$BXEq?f9mns5ct5 z_rN;l%cQg)#zo~O6mFK(_(tnwg~v6&QPSwXaqUkSN87FCXS95W!qW=3Nm_KSw+wXS z_a_(My;2YupDg}^`TwnL(fk+QAAAwwL0w+;{UXGLy1eT9MG1Y^-wm|;9_)LH;_tRV zM>p?#D1IP*ak+J^#l@V=J?CwS_;Hp;rpIkhaQwK+J%$}b@#Em{s%6+eFnZee6H$CP z~|Z=DqRtggWRCiB14{ps9q_Fotu zO>NYU+HIAaRPYb8+N1kmL02k25c7xV-tDj)<2jx8oHww45Z?D2sJufScwEQ7=iK(a z#-(0dpWE1P%QJOxF9*jvFg+@Tk8Ls@iu;$;=oGJSd_P;xX@NhQAIKQc6Y0C|XQ%f` zKO(!R``+n6eQ);T&vcwact6&r`Q7>+bV&Bcez%zW`+t8UvVZLrzjXY2&MD?!`m218 z;u!G0deQsjk9d3gh%ampK0+$Z?R|}WlVtq+n!fJ>ukBYaZ6J=Ze2j~H#_=)zT^@4F z^QGZEcmvZBS+ij(fjRA(r4LG!RwY!NSa+SiM0Al zNSAzBQhy)---~`n6LbmBfA#*9=$=u~CAw!6bcya61zjS02)cy#pYNCuIWc{S?mwRt z`W4@Q-YxI@!~4&-0uJv#?*km(f4&`f!u!t$0EhRV!!A`<-XL^f`y6{>=O0ayL%$ac zd46PC@VE!ri=OQVi=Cl$5!-{fz2VwpvUB`P)x3uGWK&3h5^Bk0ml=?)OUAtrNHB*ODb zVOlqHzWML-jZ=4~m-iOyCC}GBV|<2vVLxDCmi|EBl5)s?5&pjb<2!Bo0{&*#o4%O-n7$0bzO6TXQT++^WeoWv<@DwDM8Bagr#`>kJR9!{ z)tfhiFTCF(azp=cI=9|*sGgYKm>!wlm>!wlK#wB5*$z5{dILT3x1vAo;dcw|v-^G0 zdSi0-tH7t<(Yduc&##-{VF%euCXqJkJb&?*1@3Q3+;iTE^KqYP7JPOd?swoEJi6x` zbc()j1D&FHZ_p`<_XeG!_yy1@jN=M_|EUV?hW>}jQ+Ut$q|i6=KOnb}{{gv;{Es0i zANe1{T7OFY49IQhe{3`V*`J>lI`V{S7Se&`)v);#S-^)k8yG_KQr+on}*y(Zg*l&vR0ms+vG@S7J z`nltkbshB##bw0#5Z|AOf2Vip7S=!5+4wklg79@hmdNiEFAaVu=d@Q$sh{ITf1-Qz z_NYGD{pN_9aN+&tw`#uKZ$5jU!1kBi3zTm?Iqa82^868DM7U0>nE9Q+?<>RpE}N2m(0w#Q z=XZv`?>H^|Eq9N4+}`m1a(q9xj+!VwZ^KG2Y8Uply7C+upBq#At^2yXugZVU=lciS z-+rnjfBr%g#2thjUjKj$H{zH6ZmL^Xqjb=9+&bu+&kTyLIgNj^_&N9$7Y2R6A^t>_;da(b%^G4F2wf!j8=jqp!il;vy^yPIhKdE3?{^0!!rfBgw&5-2x z-8Qsr}x9pd7oDqy`|rK zmiF8&+-mH8^wogF{hFeA)7EYGL!We?^~C32!}C7a-FwjP$>Z6j{W(+XIa^m9gIsIe zeYBpl^_$(lZRi9*3qHa9WQ&8Yt`s!KiGTmR5SqSEmDKhh zi`LzZy6%2`nRWL8y(fL~9P94zo^?I2ClYINm z+K2a~!ygR4>&i`Pxf!MNY35V7?uI>%_Wey>Uy5 zbsp9uHeT-c=VJXYx9;P(F)A7qc#@=t?YGct1oMI0GXuWgiF|*RrW=pRd#=y++18D= z4r*V3e52dxxF3Bh`3K4K>vHo<{5$=hbg^Hh^X3b5eQ@UYOMYAQJQci4(qF9WbbWEk z@$QY#zcaSawL$r|9(MXUv!69by!-RmuJJ;L?R)nN!md3oencvmlC;=QSG%rpa!%t_ z=KAh6x~K7M`MsiIyr<^>rvk+F@_fusa{baMa$DD;$< zoxICbO}l7%fgG6-KCs@q(W~pJ z@cWCh;~ba%n0)X#H9vfh{-Ic$A@gNmYvhO4kUSkr>L*Xff+Y|=9DK1;!Z2p+OUn2I-r?)FyrEs6Zfx=q_W_h!RCEem0YKZ@{ z;;Snlz_zby=k#13oJPFunsXz&$fzwogU9&>iYw)m*IUVU{XOV~&ooHC`dDq+*KUweiNd^8jKY7f=^;sz?VA1I_BYFSK9f?ey`mO;Qv5ED_8qztIFpCi zI+o?g*GYGS>KCD4yuD!>C(GX8psQ+tHA%f(lIjxJb9&e zW>8_!h4z;Prubjr&+I{(9*{H@)YHgGF7FTz^%<@huP68A z9TaqdKd?mFF@Cxc&?_Ey*iU%fz>rix6cO19YKIAGcERM<=0{I_;-|f`Oq}8FVaa!7 zd-<6YpXzxOUx@E0++O8(c7AxWd;VMs`qpDV;yl829bD_J^zxqWCMd!9| z*SNqwb42iFcXcW}t}qb!%u$6qG=D;1x2sLzNrhV!o)Xxry=)KTojxV~q_|3yzk5Q; zJDmsSqJ#bZgkL-c{O3w_qQg}hH+V$whjD|7m;GCz18E1R^Nt(bH7E@v^?}aAz;Bg* zwlh0e?(bB&-;93QctUxne(>X~k+u^*xId{N2mTEVOU8-ojo}#rp4}?vYtfGNqn!Rl zat=9*JF0T&8b%xrZD@au2!8?z#^B4)wz19(obUJ@mruGP#Fd`1{e1NbdFfi4&A} zzjz38-Z_l43HG-*zW+L<_oOuJc1=l|y>lArz_*a@enQgzrY+(R_Al;1+Sv{NFw=oF z-G=l)3)0qRNnK9$F1R4Dy(Re9^g+!bC*ei;g+Jt%`p7n~SOh4vs zAM)o$RsMe7?LVUaPEN;7#)SFG>tORQpWyvET_5v#3EP|G?_w8p9!%cPN#7$wu5^77 z*^7Vu8Mo*LnmGx|%?IXxh4Txw&rjq}*}U)=@a;T`{1ddtZudCqH9=pqyE_$zK4o`b zr!e#>yIbvL6ZF&VRzIN$`ssGJXnE)-eeW!EDE=Pmf1}^K)lZn`eC8Lco_4DJD4L)C z6nJ;3z1%Y;{VB4S`(Q8WJ89UBCbLUsr_3&y9W%QGy7Z^bE`cunX|qdax6CeqE_BWc zdB}k-kzF#oV|K~x;*^%J1^v`6v0u-c*S{Zj+5Dyc#jwl1^APN0C+sr)ehbFe3cKtx zdIFOo#N|wdQs(uk#BxgD!7`}ASkFjK6}qsrJLk`waXvn&Qo1}gV5K` zLklr`#&ovz&#A}Jf{s6r`P#pB4ue&I?K}+kYyW4^)rZE4J&p;FV&h zqILi=WACmHgF~A)6A>+?0Oq~|_ z-U--^G)Q|)m)&F%EN<8K`+1yWWJ@@I=`Gl2fc-dE5Re>8{b+w5@*C~%LweElllq;eImGogRxPq~NT z2UG{b-lDj`H5H#1c?;tKcTNf%uEz(a1FXW{z$nADH_T*!Om z=YpTn`Wt?4u|rp__9RXPw>UgXLSCI_(Va;^d$Y&HF~bac51RG zI-h#>`>SMN=`nfa{(m;s|8o9ox%+PmKo^|q{kQacg77y^;ojQ;+8>F>0hZc7v3T?N zKFW_fPNQEMv(w_2s$G1x`!)2th|vGM+J$-%lN0yL%Z?v$GpXS7&xk+jY=7j8eV_Nt z`lV-{f5XDJRPYf=pZ#{OLIn6^@4tO6+cjH0h4j_?~l4**=GHO?J+@@TC%$;_jM}I*#T>ei+YLZJ!$8zyGFo!@F0leKG$Tea~6} zzn}et*ME@~F#2#mUP*sjd)z-lMf)M>D8mwGr0wfAZ{YOzX1Lwg-t}<_2KmgbcfYS| z?f0-g>!AVA!}%Y$<_XzF|K{qSUfH$wJCbj7a5rebsNA8)&iNtNxw_}mo4eK{-pKZS zQvqPVW9r|zjJsC9=eiExak<^y{5r6BrbRTwMuv0pTF%plzCm#nnMtYdnBu3ZqeqY)1RuSAU*|7@y!#6wFLwT&3O+3TcO$@;Y~S`3eABq_$L>=u{El=( z^zt6sk}j6ZZNza}XKyJtRc^pNPz_Jm(jatH0NNV(|yX*6i(=>8mx z$Mx0%&lc!uvj5DtkAIo>ag2T@M{h$tqvKNCgG2i<=%4nB+v9$P?cLpsdiS7x_V+oT zUs4kL-n&=Ih5bEO`+Hxlym$AO@9$f_QmVgiLcNESPdx$({r{HtfixH}KK>bs%QHP- zI((bTIqi2iF6&ng<6ecI4=4M&3GL+qkte#ZQ~5X}sZS399tA(+?Xv#S{fo#SnUIv* zf8)h0w~$j>-$G7vkYDg9; zz7AypwS5?VKP&E%;-L89{Tb%3#Wga&XT@FCWuzR>7xZ5kU&Z5nS$8yV(Y_n{qk85> zl@4!)9Jo=X!0i1p7x`=IUM)GP z;4aAJ-f<}u*K4s4XF;FicH+0lPDJ=$37`%-}ShWF~B-? zu>JWc;dNnL6!6YpKeQjZ?kpR>zH>r4Xm)J=_+iJ2_3!(bXLiFr&NnVTyGP@hS|NYg zJvfK*ty_gI**(+fXRGSlo+*X5OTODPsj$jlIBttG!+O^38J2S9$INACisO2}i1FR2 z_GPo6n^SMyfZCVEjTp~>+Ly%}6juAP81kJRID+vkZo&8l)V?f+e7ga)FN-@Q-`1^> zefcZ&bEn#u*gQz07#ybmISu1Hq&1S`I{eWd{Fi*elL{UcJYn2=5AqL_9ta-N7EUd` ziuHmYJ_q54`@>Jc{KsA6fOC=v-B>StM)|0E!16ybOa7rRN%^#WChiYEE{}5N7q_2f z{2x|&n7l(ylK9HcH|6B&0mTb>KT$o(YTUDL0$;PcRqn%iNabGVA-8)<#_5}&7jC!8 zeUs|N$ROmQNd5=V&%MJ^f4=i#@w}z?T;k9oj+sV%7HHZY0QIkpN5RH zKaF|Nf zTg!T6ektn{PvJcOe*Nl5pQ3XWi!W4twsp7iBmBN%9f%u#M^O)bi1JlGVtxq?C;WbP z?zo0$RLcYQ{Tm8C);^KL;n4gl#ljvk#GAW66cZD@44>#Oa)toF1EjN;`YV-+n*x-_LhjxjM%RR z%yefs^pmAkzE|7ot9cuaag0Z})dneYWG~Kbt=o-QNj3r{n(4 zX7+27^VbOa!|f*hUZx1sVfWkFZ&P`Bf%>=fUB@?>QfF@6_>bb(rh-rZ!oMXtn;!U6 zzQ6NK^X)<9+kKKg`|TVYmO0~fpT~BMXDGI7(`1XE|8{MY%I6y;<@t@DD~_R`I{x;G#Qld#=k`jRd;3<%*BkFg`-MKFw4D+RGTxBh$0Wn{Vcc$% zTeTeP4eM`p1q_PQ`*{3;KJ*8l;m<=(zAT7OMo!-F#Yj#f`FIKXt^8!XE%e6p%8j6Z zEC=6wGwl-uO;VoeJ&}mlJCAa>9Lp1rmrNA#2Xybrk7Ar7%GXiIi^&PaiAw&Q-$9p= z+f?vF>6h(`x!q{DYxxA)t)q_7<8~wMTK*66p8F~9FNA)%-AKFM{&$kkbmAu!0H)uo zm|@uJh4v%IZ^>R!ak2LY0f*lQ#q7nK=4IsS^|Tbwhmx41^n z$S0(qz52UBZsgAd_WFB2ZscPMgC1@KeDLc89gXWV@`t-s;|eYZeahRZ4@mh?{#Gbm zH`U5}`rTt`&uuJkuYxa}njC&eZ`ZL(Cq zE&$%V>iIn#0#iI0$-h0(6Nhpi3I`&PbLVV%$JU&3Z&)_gog?Z?lJtv|osz~h{f zb|Srcx!_}cWeTT)I)UeFk5W{FpW^b`|AT7B=C}{S?r}&3n3#(3DW33N2<1zedm$pb z`jm_}y656afunmazOAs@`TXMw!`@iGTz*jA+x!*k_c9qi#a%*P`90frZ}OWT(t3&f zWq?xwes759i|K@)UH===d?E8mBLBQZ{*gpJ-)m>{4c8w|a!%g|0Um{%)BTfx zA)bL-4!W<}M24Oo_TOoUB#ruA)C#lbz|*z$7)==DN-Jy*i3V0ev<%%rIV`R{OHKR$=hPZN~4{ z*?l!`Gk(7=yzgc+)+h8GfZ~HcVD>G(4jiO*;`E%L{@ZtEOjqV7)AjSRewYw^k=-I< z(-W>QZxt{o^*yEFy%+huX`kTnt%H)r*OxUHG5?b7yk066uPev#J_o$4_wja2p1A+H zHfn}7uqkgQ^%QSd0ae>Uy*q>NuTr3 zZf;WO>1_Q}#vUfe%XlYc|Jm@KKO0|O^?t0IsNuB^s@Wp?S6IlFxpAd(dH+n z=R>WfHw?OxYmEJn9W$Wa%#3d0a$z}jgvVCrMOx91vNBh2# ztrN+mwKod=a~tJ#WXJ2l_b?u{GWvcDCFvZ7ibU&H*lC{E!iV}A<^Nw9XEA?td00yT5z-|Qc`^NjwPxCA6cU658<|~SS6#UWe ztBxtG-{b4P4Ba0-!MOOL>O*v3b1r zEZC7$0KArOeB=Grpb9=U*W>m+7W31X{xymGf4MI5^TPSQfos!v!u{AQan5_4(kGn9 z8-B*+qItYVVVlQm6}EYN3E=)lt~P7_p5osRR5|iZjZ&_l^KyKieKk#4v<{K{Xg*&L ze6f8^Zo%DBynoB>#OKBT@3K<(GQg+tJIvp1dJ~TyGx<9Zc%tz;bw(FKlOZWiTOlyZ7k8k#;*|ok{0t@hM^fW(oxtT;1kI0Tc=r7Hl(ZMkpHB)LTTdw*uBQ|( zJ%74;)V}30uC8U@g1+5D1g6L3G5)S)urH=B_T9W20lm8Fzb^Gyp7==xfL(u|z-}$@ zmG_HYBo#wDtajC{tCjlImB@GNVW+Z}%WH}Yx-wWwe{#QgBx~wkEO^+?W@!r|72F^N zeN&atf&Ei1NBd*~7ZeZ6(*csFcHtJ+>y>&gzeLJK_ec&a47!ouuCNp*c^(8jT8mWi zJDW!~DP7-$anSE+NIBo6@|H0~8*E_xA3;C({AG~gQH5#T zalNAZ7zGcHJ4Y&WEft&nJ7#Z{ig7<9ZeN`0BiFzB06B00r&by0A{mQRX82DSESJ9o7ph4=f1g&u~9qN>dxB zwoc<}WRIjjtd~a?M0TIzyQF`Nhg+ugu)y~Fzj3-So$dFU8P71bz8F2#8zO_&K8^_1{OK~|6K4BYQ#`{xZuAPt2_q0;q=n$@R zs6D2$&6B3DtdC4D`+cZXusx*LmV_N+i8j34e|uJufiu6*>F>X%0-Rr%J+XG9`FWRA zxBfBT$LWpDYcfx31Ke+451?QDW}k`p$6Yc07$4*G>tH>}qdxCjGyQlRw~;^XvNMw5 zmZ`pG_R092?jPCtqQ&D{{Jec%U~%;J{ZoWT{hJUE_!8lPzZBsaM!WZDKhm%pc8`dS z&+NI4)9z(8d8XenP&rmVEVL)sM{v1X&4<4crxWBH_9sp+mgl(L5qTU{dXfmo?GDp* z|1Z;gd`m=crf~e;UBR~#`CR@Y+Q%(CqUGsOTkzil*yS6gzg(`4%jL8jF}<1lrR{M3 zD$ajc^T}vPztlf5KZE_@Mk>PP(>Klb8dr?1LuA0iZ9PHg{YL_|W^003Ol}r1* zwRrz{9Nf-ps9$s+i}06zw+?*P^GuVUcskYU6 zrxI`8Cv=SbL99RG?V4VEgw98}T^^_DZ7P7j(UX6_;EDW`J&>nxzwJE=n}2ev!mxFQ zuT;CgNA1!*AX47uX?1ydE(VH9=0o!v3Wq`pfgsF z@`n@opX`qE%lrS$TDMN790WeaC~DorSb80C*+KVB7ExI#Qtu#)Q|WCL+?tg z2L#V9s!UIKKisW=Pz8$4eAxG2gdO}2RuxFZYRDTxS0Iu;(nfAvR+(2?`SDi z??S0Z`USlR_e(y3_oJ8(Egrpizl1gw>G7=_K=(wynZG+FJ|Rh0^w;VByW1&f0^2uP zwX8v2nEq8)tP?ms@6FyXxgjh++n;}r^efVrTLq5%`I{9seYrv5+4>UghaumMD7}n6 z2Vqy{z84(7569yoQIF~O+obUf_X+e5nv!tck>yEWY_aA{D zCZB2Ox%mz8d1R2}(fkMQ_rBHA1lrT{CfkpW*K6VVCy%o151U+niR>hO=MDPjJ0xX! zr{YEa0QqGoSB}ql<#V03W8+aerULvl=UZ=* z{K8I2WFNGV{+!P~nY63^U9x=_+b{hI?Lii|dkNazt?iPr@1{TPcQBLVi_h^+g6aknJm*90)8_oA5l=Lv&*dBSiPxHQ7d2jobNqJ=X8~hy4*Gc~CJUIsbm#Vk6 zjpuW#$J}LD<>jQ$O=&?uPw~0gw2c3R&oy-)H`EKGOWE_zzW|->yf>$GU$IsCWp>Kt zVGo&ppR6ghe@FsP5A%bv$+phE&)L>_(S8s72-b^33H$vg*UZ+Np+x?NWBK9wtH{2; zRAy?5cb1NsKhOJ&i>`W*tX~j(>$5Evtg4I7t*I-tZ_mA_F%Ymdq0gn`KDZXf&H8KO zH2>WEU2gZK-&y`eF8lV5f4s4)PW`W)L{NG-zhV2kFXzAi`7i$Zmae+l^fUjL*Q>X^ z_)l_N?$)-a-`lkgauVr<%FVWY;OlM87=I7fKs&SU1Fz#=qyIdT>WY;D^Z4g;AGpOE zN8d|NpZ*O?*FQe=dD4>kLHa*>8bevyl>e^<%{rd zk!EQ>bF0*k&#&!GrSRTagtz;+c)N@6Hq*XaaesFd;T=3K-i{)??{6-J_qU7ij+er_ zi(b-0_fZw$ZIka!lJvbn@X|ekQogL86x+Ap6U-v|o8J_+-y-Ixg{@f_!gvWd38%wqOI&By9;c~CJ zlJxK)*pqf!|IEdYoUmE^ThXrs{oP10{ofa(e|yF2#S$m!$o*;lb)I)2C+!uuSJ90O z2|ttNU4AR%M)l5(svS58`@s07&nrcTbxMa})VF&q9zr`j&+%jXuy#LV+zt#-nPh)# zf7a|xoF8u|m%3Q~=%O%s4%0G||M2_u958myeuy$`J!o<=XS@0xMd1d<4*Rx^T$GG{+@zl!;=?G>fke;n=KHi5K<3ev;#2K#;dHzl$A z-}ec2|%fTr`|dlKtZLmw$%+!leCq3iWc}JDmfG@8C0i(zaXHA^ARYOyM?# zXB5`;er6i*(q`nRrvU4C!hI3zZw~nA_qI_k2Yfy~p)l};>sF03592_ch)a0ye0h5P zkJu+d*S4=PCSEJyK&-11E)roRrpzq1>?Py89{fp4qDc^j$S?I>! zLo!D~zjh4v!Onx+$bgjN@xAYI(yPTCN^h-Cy_NbpkHpL0N#zTKpC8*dVLW@TAh;Z# zWBxkR$JQ~2u`CD<<5-69p&!*1SbtSlVLe|BHj}@7G1}eU4SMdRhUqEK_g|87A-!u* zp6)4@dggDI(vzo3^8J&jcRT!{x!3RY=%?D#upjeS$D90pswCh47X2tQAH?G$#);nc z{SxzOY*i`y^Y_5_&@klS5y+{HkNYirvwkbx!}m(pBpu`Tr2i(#N3kAlLAwuYyJmlE zzft$il6ZL>m&yLdp!7HLQ-^?0^(?%veW|>r{Y?1T_WR4xdAGK&_wV_Pj$iB9{ZIaY z`q|rbei{K@iA!NR+x)*%+tYd3#q9q%M1R`4BYM(0gyj0^T3$y!C>7k!o00Cqf!3;J zB0iK)lLkFWI>f&>S{I{ld&LOOYmJ{LK-XU@N!L;I%kbR!e!zS7Al>~Qq&i;a&ku;y z#p8Sk<$5F$zlld^`$V>`E?#e;UXOm)K=+ml-=p6V((fDUajxLcnrTq%eiM3y09JuyNzssO=5$R8l{{FN1 zdrrSsaR;&kIJ zHxAp63+DXzPTe-rD^Tsel%L^hD!QIT%$tdr<9f zDA$;$eKQ;l-w3(vtyTR+cDR2Y@@J0uDHS|YhHu*bN$VFTs%U&VuF+GKcZ_!~`nodq z)Z(+4K4NFF?M3s+KA{8ee>J=)CA{H(*lwS9_3VAgC5il(C-RRa_AOT?@~0B{%Mys*p?1(s|EbOf6mp^aqfi`ulhf$>MErm(~ZY%cEeRbp4eZ! zz3#JjaG|FY6BV*6vupZ; zFY8)|{myp&IFTNAHT;C?g}*Hwavks=EI!xdHhZTO5BfVhgfBOy8Ul8SZ%n;j;xwyM z_(9|9)VpOo1k3xB%&@$l*(><{fd*N~nY?6o@#yKk%nbNlU0Es2uC5H^Mqi58lyS2h zOe~D#z|Mt@e)PK{QqI2Ti~Aw$Uz&f!c&2Ba#~zh>@$&nA%H_xL{$c4}z~|jt*?-C_ zOrsfKSl8VwmxZh7{}}u*`6;R3V@xL5kLN#nRW4G&n}r@Xrv88S-UYs@>$(#@0$+n^ zQeosPgA&4(0fUt&kzX;;JcJz_!elD2tw3n0kg)^CNzCEehV9cCBmBai%vVtCn7V1h z6%Wa6W+45wpGn(q#I&7;b}|{#W?GVICnQZt^NkNH$-mc%tCf#T!VW~W z@`a{XsekFDSBszH{kP{s@Xy42xMdt4u2DYRG>#AVN_ZvtPl|W;)J$zxwduQrSFrt~ z%LRFHSZ9O2VTkGcTqM>5pjbLjvnBNvr2FX2noZsT<$g@>q@N}IB{6y=jIjL98DG_m zzW*8yu>7#p`+*;*NB!sr@EQ8g-}Ux=`syX8h<69e&DKu+9x(8JbNS=v&PV4XR@x=Z zhK%V|3KrxC_fdK$Js$an{QrAn@wIorLtIV1!U8v6aURM`o{ldI;Yzm-0s#A|?K=0Js(G_C% zT(9{tF4_flxzo9HSnEkswrit6n5ohSFq)ENqu>j<(Nk40NmKR{(g}+sX&tSR_!jk& z)j|)fKYMN`oTeC!QjVmriJw03!vMA1h2qC@x3GeZ5~lU1tw^T%w0_ACmp*uf`Wr=9 zY5iMRanE`UmUu3#5wGQJr~b525^Fh|EL}jXlrQLOIS*3b?D+`eS4)0v$KwWzr!cc6 z8>O8!3EO6V-d;#`;@u^Fx*eqpEZ!pVdw_(vH06X6>AjWrPB+RhR6L1x;&LxdnK9D+ z0V-UPeyJcLE|wP{{nc z?d(~k(FyvmCjUTx_}k3y5g#s1g`vdBcQbWdq$wlh{5|X4@*0g#>#wDszK;I3+3Mfh z@pH=G*;9|JFYm@_zSYt!?VmwSmaLX)0EZFI8$FLPe4}&^@J9QUE;jF@J!oeS8Y2$F zv)JEZKk@T)axXyf@3~f!%lD=JAH^rOU+S+()Hd^b#D}}L?NLp)p<{>o>HdGud{%$j z_>jV0Zo2=e7!SPDegBTI{8s!8-}KOv3>R{0f0-SlX9H>#dawP6#>ep~^}($cI-s4P z$3lVAgKS5(c0}ol@sF;JeAXb{>ur>a<#FKj=`lH+HlEOWdsd+~^!t;lheGatQRHsg z_?X5=erQ<#IzJ3OrtxWmbQ60%`K0lP<=aB|`JI+-z`{3KI?`F*b1?X4E&jN{W;ba& zL3rQKSUJyGIvWoSes5Ubk+6JRhl##P_~5gak8~Ej*3vy=@saT2=D)}egb)3c#>@NSN>A6Pd|n76>owhKk#9%{IS2j`d!dGJ6#6UwJdIwF z@L|$twTus>56_c+E+2cI)ntjrOM;$v5)KDWvL4K%5(N3B<7gh^>rcH`@r(0Y#}0!> zG+(`A6X6lkXSHP3a>Q4Bhe*dL7k3&S!oP~=2LHV7%m0x@{@Mqg^$FW&CS9Z9Lz* z;wQA;Z1X+VkCcP;j-W8)1+u!_+bF*QyZp=6rk0QE9$9Zl%fN584r+P^U_pPU{C2*c zB68}b*Bx(5B#Gwdy5rx)@(}Lhn5vDzDK3=legE`sXlLI zZES~sA6o1f+Max!c$eZC^9>MhFE0!LOTlv|^)1=YC0|&%z~|3IzE=`mjtZURQjoZO zU3Q`U%6DS%of=?({Os42T3#i&QSHxpx%)7@G#$Fc?F+CU9C=AQ1m$_ro^VX?FRuH> z-aGxl*&hx|Q;PP7XJucmlH8>2L%U>1$@=*^!qQ7l2L#AD0qtkd`+I_Kzu9G`$e>0( zc`ri8x%Z>5gEC)F`1-=uZ`DcvRrvt)a)$uFSE=_%cQ6LwetvA!^nYL1X2$>ECnOIC zh=-5k&YM{Q+J|_5uO4y{c#qy7f5gq1M<44dxECZ6IXn3bsKN;ERjZTaTMkHMsURn! zO&##;T*Q4<)Q9}A`=mm5_(HZ9ef>Iay=Ci0fQ;doX?l(z?BO3pb~LWAF}b~^F{g?74^6-o6q#=BT|7l*T2#M zThF+DYIdXak=ZdFa-@$_zsCHf^MuV;=_Ay?y*%&F^wHO7{vPX}^buRf2$8kCouzdQ z*)~ZZvGt{=d&1V2LPo8Z?4G1MY<(%Upv%)oC}(=miGX)h)yKSo`kb%RCvCkb$+bLY zOVR;bFG@dYc{^;qNOI-tRf<_TCqQ|+TGH#hSL%<|GR`#oE=5Diuj!Atp4vG_VP7Y? z9FFBhJ(%)O=VQ#byEU7XCmkU!miHJ&gT%AhqQ}!dlMa;D)lb;ETIjhD>B&w>`WRX* zbQ>VQy4@t@^UxAbMs+!o4oCx${z($t*IUpRYZC7EDDWfTZRYpfEPyzlw_N{;>tmGZF(}vqiC|A>l+e>U7YS3OH@4Q%kx0{?X*zG1JyN=sWPFTF#Pq@xa8*V>2YVow6$h$EHyB&q=_O#)4lwB6@c9b0k(~ctR zA%jhxx2-4qJneJoFzs^bA==;4eYDHDyiX6({+8N%f$4tQz0x7GQ|@Y~+<%JmTo@s} z>rdG_+Uy_or`#U0TkEeswVCVX5%ypGse27RV)5@c_?W>TP`KXlLHbMXqrZadv%0On z>(jO}d~==tL-*5v?g91Xd>zZ{c+g<3&*eP0g>SR)9R_bVm~?h~fV_8S@ef)2h{2h` z?8kb?Pa1sO;{T1owBo^)d#4rj;`_Y7Fv2L^x;64!CWWE{da@8z6b}p4W_;j4lFmA`m)>`F_`*t zIIzZGu9xIKfx*xQg3Z>jT zHkb-~IB>#XXt${Uq`_Qgg#)Jy<~l1JIBoE2Ed3dSUu*DLgJ&B|`BPuYbzeQ5W8pRB zC)g2A2|rH}J_NsH9UKpl|E7_jMiD6vdU#FzZRVeE9E71n@#&1`Pk&WQ0>7i2&ZQ6O z2#D*=0b37BJbEPEY3oQyrTRa+LH1c~KD70suUpf{$xm*FO&=khV*I0|qpxS9zHfHj zbf>K!r9Q2{u>REgfwZ`EC;K1kBGi};5V3qcPr9c&$yaeb{|NaC_-J`^wK!R~ksq;6 z(eieZUwdxzc=j*i6|X1QzgXXBIZzbErH=vPdI#zJ+C7T0{3iZy46A_T}NL>Sl-7|{wwcK z$@^s*p0Kcp>` zTiFthqjaVeBrcs!d6l+Mex{}#zV-=_@w$@geK@@m~{AKT#v?mI_saPhaS;#^ZmMg2K#>9Zi8J9-DL2E>Y>&TQ4giOOQRl2d6+g_ z52d_J`M+|m7bg#j#hwd`i_0Sy#yB@mU zVAn&}80>oJN`t9~W^Kz2ramh7atx+kD(|ZrO#M{s^#)Tv&Ds_c9@)iyeBSibL#C%T zc%CT?QSPss@m;mA7wzIW4+VSXxBi>xJ^pTzeV?R~d{fgyo?~QTKEb|6y^Z^Tt}n}X zn^k|>&~dx^{X>l3(6Lfs(KFST{S@;#;rF}JVd4e*`6o;71VHYAp29XSV!v7QnVrw?nT>9cMB?Q9n)36g z+3}F?M%f6fBr~kPC?D^x%+-ChYUOwJ+-0@$l_t!qjt}tog+HXMN4e0$F%>%=ZpSaz z@Aur1?hCTvRY@jmy}rM4cjbLeD79Mol5T{>{RP`s2#1W`!|V^>@$C{lFw;36*+cf; z&Su^tb^CI>w|Y<#i*tPCJzk|x*k7Y>^g?-W`3&&=bL+=Do1j@$EB~tr8dt>A_lXSO zXm1}OzG-8(@kdR4S#KLW)8Ija*$-*s!v<5n$a>!3#L_=w@L9^K#zzc3V=(Cz&kHs7 zTKp-C|7n9y8vLBWCk+0m!N(2$8H0}*{IdohHTdTYK4LJ(L2hr@XYu> zZSa1BeO@?V@D7VVXz+G}4;j40V2-=oUNK_v>n;9ygVz}R^9HXp_!kVedF1(zE1Vws z5A=usqx#^(&y>akRxmjqo;@R=?3Z*|dC4~cXPeJxc=p_Bi)TGz?=hHoh`q;P;wkrX z4Q4y!UarBL6mPL_8B99Jc^-pFCpnj6FzG1$V=(C~=g|$`ZRPDXc(1{`4c=$)E`vu5 z-eK?&gSQ)e)Zi@!A2WEp!N(2WWbg@t*BE@#;FSiSGI+Vcrw#5l_>94e3_fdcm%$uI zGS3^#aVF=h4dyr$J=I{2Q`r|XnB&;*pJz`c8t(TGLt~-#_e;r@gDO|$}F*X{NWQ?XMkM&egLCi@vVq z*uGNQ4oLKSuWw;#ea2|1ue2VXXX{~^WVAe6zohn_ek^YR5svFal1a+5b*tlFvvn(* zpYJzpQS|ct(pw0J2I;w4^6PqTfZ@?TW%41$_j|nem#yo({sp$KGyL@YnrNy$?`6KU zo$IgIZ}T{3_gK5-yIVRQ(s{NHmFSveP22PRQeB5GwesfMI&L1n*w{!N{ z3Xa2cp)AD2rMw<7;rdSO9LoO_uJfdrnv@gwM~&}7gZTS;4C_6l!+H?@HuHPPzG}Ba zS7BYJ{l8oQaiKwcolZT*XQ9FNA-qe{#eGoYXRI?(-h}(0kE3+C4^!R;eQxYNXufY@ zbc^dn>z}w@T+aTA`xe~>`+9K^;RANQ^Lefl(?i^+$oD5GNAvv&%GrEfc-+eMb>T6C zeSgCEG44+swRm3_9x>S0g(C+0x^SPtzAoHru&)bu8$8jvaJ$9(x^RoZzAoHku&)c( z8|>@CH3s{-a3$dp+pjpt^So^{S4YW$NF`A zJ3m|K*CO9th5AbEb-udLcKZ6a*v^N)27F!}+u8m5)Fps1_^O6_WnP85&~}D{><6U3 zUC(hgP1xSc3v?~Ecb?SuchTPO%DbJF`q6nXcmqeX}2gLzJ7~r#~*VyF7lK&iObik3+6wGP8da$MbO=&&KZ!v{LMk zQs0q+9Gz%lJ@LC+hbGLgN_oxA9y?XdE|~=QBd%gu!-BqjAz;JEvjmhkBcx z&#-ksy^Z$r{)XAv+h_;xZ$Ci- zSZ6W5g&$ac{2ui&gU_)3_`T}W1~b3d-xMz5A!@F+>%ZSDcHc|6uAU?)L`60<)?7fs7_Ke}@?|_7@XIY=gpD8l_kv_BiUEJS*3-%p_JRYN5!fAHUdor4@ zU4!L21>az~_I}nBm0MD7JM&eN|E=+QFM(O~I~?J90yEK!9sCUp{QI1Kzrf|)99w@o z-Cq!LprxI+u)TocoL}R81CwJBn|T|DwpweirGv;~zSWC?EzZk% zg58+(K?xNHe8#l1pN5Gto>@QBck(&dWONx}$7}Rf_P>z{JDvHj(K z_V$}y$oC!RSbxR#dp|F=_Ip2j`~6;t(J}V()x>Xn`$@nup5A_MFZ3Qbw0Gv(@$HT0 z!HAZ~5sqV&yRz_Zj^9rLUm~f$qayb2apV0M#$)kamIaD}{_%G(i}GQMrl))-k^B+xWu^yNt*Q9-5Tf>HGzHn zx_m-;f4v6sii`>PEvo^88l>+gfxUjO59$6=@_D%XkQX1g@2(FbZ~xZ&?(*?>*!UIk z^}Fv6h`;l$yYJ6BDEANFj1Z9z;6rg@ht&3q-Ozl>=d9y6;kBeIg@rjM&`29B1`?d)_*GoRw8)1p{`z(iV6hNHo zhe-D(z$-Oa&%0bMKYOG;xNv}U#d$^Okz(gm`ndf!43WM*-km<8mub4uE47%U*?8aW z5Y4wSeALGEVH^LWcQYLJP~h)!X`}qsb{L-i?g-M=Bo6v7e18{Wv?_o&#CM4g`zxB{ z_u>zyh?VjkznctxfWGrzv*F|S?Y2ohua_h!4^_BcJ$i@bGk)}W4(XM?w=y2<0;R9< zO~hj^R&v?|Q^f*U-W9VG2ubcU| ziC2?<;7|BHP7idpxaMy4l4fZ)++nMCw9sy)kM$qQQTp7)^hG+>Bq;Kk)A?fQ10srR zmUhD(wtCOUUy;tsm=Aodbi9Qy@KQS7PPokf-ai;eAWe_;lgryUj)YD%#M}66ZWln@ zVWX$7bGJ!4?H`HLaZp1lN$2gmg0Ra4$eEf1`MXJxzh^MrVMK~M+^t@mC!GG!H^R2J767f}C_ zQhumN7nA?GQhul|7n8rElwZ2&qViu=%0J~I^S71qW4(1T_|GWihsEV$@=q(}*Ylpf zPP~Brn^ek=6G#_R|6jd(EdS!9&c)>aRw@6hFEanvO8I~IBJ+Q_l)w2R^M9d~UxxHW zwf}cZ`JrcD4E=tmlpiYZ#pM6>QhunS7nA>2O8KFhUrhdAEaiv3e=+%gzLXzJri;lx zRLT#Le=+%=E9Hk>|6=kFmhxji{$lb!RmzX^6Bm`QQIB|F}?Y~pQhD-@TIbvBfd3fY+2yJm;RhRnB*Q%FfRl-iM@ZN5Xs9v^CeS`zU zIfDwuans4q%_XDk#`xW*7nm%WKjoKohrsVGqdU=_&MVofirx;A@iV{-%?wWN!Q?7# z6rC&%_Bc@LzNHE#J^l2hiMns?>(xuL3gOP{entbr9SjevqyxldYspt`hihKOc;9c>ChdaV5%ocz>RHP8 z!>h)pgPl*_spk3xlb1N0KStAfmMbWBde#@-!32kme&LXXzmI&@EIb1@3WA7h_VttQ zu;k~RU9JGXnxyO5gVbW5dxE~d7l8MOYZ4Ug#*bON;e~i`82S;E>3Nj#VTA2DZ1_0- z+5Mzngs&jpF2|dVKH;Fz7s`w{Z*S&wzKh|Yv)U%_NS_co1?dp6n+0S}vs^L`0y4DXdJ&-*{TN0TMZkJESl+$MC@{+D93 z|E*uWorec3y^Y&w@3r=Vp4$J$cg1~^nq~^u6>Cb2>>z zcN5+wUYGpy?M;pGM!|DfG93G(yxJ)T~|;hOvD z`}mLjC&Ai(-Hdm8w2zl5CZ|T9wfxqip2uN{R#ro+PHYhVXJ70$9+0QQs z9pR!}yR3F@xi*Ups3cgO3EjLs?FuHN#`n$EfA=i4aOp?53zl=ciEp|5X5*i9z{(Tv zrQ}_9_aaT``?_c!Q0X^5tt88pZq17|S&}`|tv{Y2n9EsfU(9cGuO$Cd^ZWN8Uua@R z?AM|Na_>XqeZ0-NhVi3M)4xg5p-Dc^W*wa0X3u?tCcwV@|CMnGK)i=L_Xq=sL-)4a zTA2Tg?|dFZ{D?f~ z()ssNk$y%2AGvQrx(;aacn&An_o$%XAb<5T^q#cezSDX4Wq%>f#JQoB=+p$z{|c^_zu!W|%k>oO zYas*5fnB9@#uCkcll(m zLin)Zg?9FelPL9z`=G$n@3;6lBIx_qi$i+&j(^{E2oe27GNB!P*D9DaTf6<;WyGUB z9u7IuEDovR_gQ*`%l#VpOaDy;`ayDn?x5?#bCE*mZ2b_PXFtk%Li1+Rc4@!*IE6l; z{Dtx*pWoZTxPf!L!V`q6lP=dEz~^Cc0N{fTMf{u}gX=#lncvTG3|ELR^a#}7cO&C{ zA38HVVu}z4{fhY%?W;~AVwL1O+794}_|82Z{y()K*B8NWi1%|I-foP~tEHE{oz+W! zN6U}BU+E28}jp0#qyz#`8lkaQrQl1Kmxv(Ur}vE_(8%kg~|DQE!UQZ@%#($ zMR|SwC_(6N!fIZR)_&oTYP_tpFEV!GJxOaF(~>vZ*gX}0#HEleNjY~kml^RV$7&IO>| z<#MZ&LxPIaL6#ruG5w_O^$!Z+kj~y2sxKy7Z@2J4NC(8zuP-*g-NFY^JL2ir7n|R1 z;e(JKh^Jp)Y<|0i50XF97W(zY=G#5mbdUgB^Rs8(Chs%@UODI?R=2>|+)AxA^&pj3AgTC7sKc|!)>5B7!r^9@Vp6oMu z)@xft1o%G$#vlk@Qv$Q!x!xuLpN`ils`#O z=(~k@c>6}@65b~9K;6SJ|BRuVmoMRC=w{)-6LhO2_bFfK{3)=!SE+E_()oQGv|Hu! zovgokEB*R)l*?g|aMn)0lC0o(u9s9+M~&nV0h&9E+C&b7!NsjM*0ck2=!rH{?;wh&ixz@VdRXaSN(!n@OQ_$nOry_ z9Q}+y@#b{4K&umZc$0c+2M2z>Zo)cRP>$>L=Iu-ed5!uzpTysM-d%0sZU;quW7bjj zZh2A9hF!DRo{ytFBPZDI9a?nFd@g8C5Q;}iMTh-`5A>3Tn;t5g^9@%lRL9>q2( z2XtN_KGM2AnYFFddP8G9{mUg%oa-lYzemHXO;{mIz0dDvx@Q$khNUpni}3{g1)=c! ztiQ+WM>|UOY**OVsXov8dG>IS_51oZn|4&w`*$jXeJ8tb0r3e3k7)c9k*~lj?Bn>{ zCefH`U7ow1S{Hi2xgPH0Htc6Tu9v&KtxrPlOZqPL)P9fl0)js*{f>G!iVu31>kIZB zUY|$YZX)+774P_ts`tYTlq~D%$`k5mYuUbDEPTa9$EBAa!3m;dtKr+B{z6 zv*}+L*JI>!A$*K{w(xC2Y@OF0VR=4|{e4>RkFbA>(!uq{{Qd#?6634#SHjQ2Kj=@C z20xodKJxRUVW07b@lUXKoc+A0>yyFo>${zJ_1gDNUA{OU_E|Z8?%L;JznA3srwCoN zKPR7Me;WM|4!#=}I>7Ip;lNY-v7LDJ0cmmNapd0v-6+VYeqw%E4!t1#2={^c4=KIE&4hhiS0`;F z9j(4dM=4L~Xyec2<$38?N62p>a73wJtJk{@Hc zO}=L~PX)Vg<9dMHi)Fg~E7fovXar-lb+YC>NpqJQLnwO_#Z?+KwCxZcUYCIV`oD?{| zVs?g|j(&Zy`S#9XI>>WkX$$@OV)N}i!E})O#Ayrt`eO6jEqsvYywVo>^~L75TlgT? z)oBa;`eO6#-Mw^>c7xRH2ld5->+Kdks9PR_KmGb*^V=sG(628xzum$IuQdGW*B6^_?^&jU)SJ>4`t`-;w_Et&EXz;7zS#VB3m?QO zPw)r*`eO6jEqw4b7Eiyv*nE3;GabY!P2{IvUu?d;ZF^-Y<@fam$Eazx?s17cIKU0T)uxbk8t1p^nHDE!FFbwXUEx@t-L8B zzc3>I#o3u3F*=(*bbdSY8(&0@VEOv$%wL`SdJT6wGU$nUA+CR|e2c@OAsW0(FT#p9In-*~(9Zt^9@n~_2#)+ACK)|METjt}@5_`iI+ z^j@t$w@d#Dsu8=ig+HrcZkHB3q3xu0O7wytqPiz@e zzq~81aBiR8rEo>R1^{|+of7M{{p)C--oH=NZxgyK797iVuDGv6wL{M}sD3Q%f{S)( z+gBPE{s6tP?pOZ3+0t45VEe5uCpSvHNayDZ+@2RqZ^*8r{0OE;W!G`s2dA6wXF1)h z9@r&7Z`2Oo_xr0APK*=UuNNSP@QpAD_~iWdYtq--{j%-Xjbyh1jpZDdT^<{hEB1+po!|Zg&m`IIdr@U;o3}uOT1dJ}{T^$L-ay zKVcjp4t}5UYjsjx<%jdh6dV7(PWE&j54)E9KYl;wT9hpJB&=Ld-zPF14*HY)|My_8 zey_I2_ak$AHTl!^gO_iwzCz1)`@wj7^>nL$uljS^wfRZ$JtN2^*sE9RcVnh$IzMLs z`zoA|U&z6!(;#8#vJSQb>Cg{;ZYt}b94o%FbWG%U*1>U$a@PNE+~Gvx;%%u z9?xOUqdClVb`Eo0ox@yD=P=jLIm~r(4s%@`;mhZc-qz2ng)hJ;FUUV|QLmkg1Pu2K zJ-PRDmi#0<1Lx;7{GOEGH-emjzT*52xwS)J@Y_39iawE=-msN)4|Vo`u=Q{Dk(#8I zbat;Jd(Slr*B@V~es-&RN%m&u%dTcQSu4wR`)$@rM#pyeCnby9Rf~8Xkv%Ekb*=sdq=e0C>NaQ;}iLQMc%jWP00U<2mKI_bclarH+|?`h`(>Ue8+D(@rK;K zv;Y0?m!!#k_xIf?=NEKclSw|fbPxNOMBaBb zm<%W1l{1**Ox}kzm<^Zr)eYu2m3Pz)<~Ww`%^1vaF7K%uOgSL$q#8^)A@8XhOgSR= zbCu5N9?BWH_h_*3Q`>2SDW~MUQ-dkTW^8TU0l#}wkA;QlJW1wF@ z0xl7MuX?(lQInrVzPeBNYLw_Dy_b#WFQf;5efjMO@=Iutf9kWyPoY8ns?Q?7g+{lf zCqIV9a)ZgQp|R3n@^fgcF_`@B`bOQpXX@+A`Yeu@VBaOG&*FIUb0hUx9B-kq!^-1$ z^z+sASsbr^zNbEmac>KhhdfbVx*0tVeponxJVH9efAsA+ zzl6q!;^p!t+kBSr<}>uComL<9)Fi*Z`|J0fvgc03c=WWt`zidc;h}L<`M}q~Mg8R{ z^pO3MNgVb);ru)p@VrCpuwjVhRP8>ol*jrcy|;Isv^NZ$)bKo=;Z;aCN7J!gc{*!f zAzhoKi}Qf(`}=u=qP+?Fm)j5hTn_MF1UWCm0Q3%rh)4BO@&)95!ASiLEj9mI z_CLx2XCpt#bNgxUB*qu%@I}yJkooiPZ?K=xuAftMBVA$Q8u+Ii=82(|GxAyymS=UP{>P zS-#)k_HVooj2Xr6y@vN`@}$WB#{>cA5C47w=&)VFLqF5I9Gh+Xqka!kzALH;(Ei^n zwg2@JQ>3RfMdS(jYXhgHsIU4v6Fwf+wSSxa?R2wov#y=v-rvKBd};D?UHjLW?-1pp z-xsb$kQ>tM7kj6go-oAvs*^bHFAzRKI_JOQxVu-zU3{m)4)r12@5_Mh>jg%6oj*!FLcpY6;;U>0`OVjPS;q}*A=^8V_bG)3EqemVBP#>yvXTIkR1rT+TSrDOYRj?mM`Yv&AbiAz9JYVvQJ`PF2N{1QGYl^gm$ ztM!LFe_Qe;^{> z^~9{z>NUMEYi0fDFVvGg{&~d*>;EsIJS*SFcd^~qqTL6{cVX3kmfZQc@%hU6K*x2v zly{pZQ~d}zR$INjQ~yNyJTp2#UvHB5aGT2k(xrHA)#brYf&UE;|87&nSITG;I{Ldy zvToyewEdQFfO31?4T2o%i}6Z^_>RiD8@SFu`^Tia!uZMTF6)*Nu1?18Lrx$6zCt{Q ze#3jwyd+$|O8uT&015qsPahJ$I=M&T>ZD(Pv0IjK<@}hZuaTVL2G*CYAfJaDh_}C| z5N_Z=^mh~d{JXz<9NWufT{8O?&I4#C5UV7AN_uS|pST^=&ky?fGu&fC`Y31YzNPm= zxSM?E?FjphXu19_xYMQcMkPml$A9+ignb{+`DnG!2{GR9y_0^H{A2u_z1iwBe)D~x z&~E%KnUUYy8Tr-v7yW>gtBnr5llN%(mE;%H2Oh5#zVdgRdN1X7Ae|n5z9sh40pq)y zt-t0P-`s5dHP`s>EQIP>;|gK-{{l!C|Y#53Gsc=uX+tY5RHFEX9o z>&Tk^K;dlFr|2&orGG7I7P-@YSp9p`KT|))|Gz5?{J$!{&V~Oz$hZ9_hpvUyGM7V0 z4+6yg6&Mb+HnmItT72)MUed9J`Ez>QMtZP$kxpU215rB}s47Wyx~UF~T9n_g*?*Gh)7ex-?%y|HXX0 zyfk00>CWft(I+(fxc6s5_mh_+2KPHaFP{%O-LB2_8l!iY`guQ5Zn=Hl?doEG zwe;)FUu5~b-&R`qLiNl22mF9q5kHtLa{Lx3-Z`BT+0q<9IRNibczW)owH_`mnCb#oWwQ_$=`$hImtiPsP_&$SY z8a!ff&EO*n7xCE+z8bRng>HL|uYOVL@3nPTE^keqNbL`&y6KyS1Kd zCgm{nW9Wl^AEIcdS})0dE)xgxOG@bKg#DJ)&o4-uadRqzzr;CL_J*xsQqMsF!&Iz zIG_I~X=m8O@Ms_VuplTdJS{{Q=j+tW-X-$=a^E-8_1RZpUtsz$BUOeD`$v@i>#j39 z{fuDYM=bnGE#L8yb(X~+W&E||v))OZFnoRC^Z{L;lpKDKrP!|Dl6GyB(rfZ>oB95( zD%MFyC9D^Un>f(#Dxsghk6Nxr)|rOaKE-SHb$mw)cnb=WkMCY#o^3rfc3lk1H2PbH zk$xu-dSG0S4N0Upps`QleP06YSTEtoH-8KtQVzV-`g?t8KL3fg=k@#dP~6@g>|Cnv zFNSvZyYDaN_kFsx-Ju=Wf{zy|?D~rDcZQ{m52lxfTa1tFUCC?(xy((jPU&_bBUmihPG~`Cr;W{scYW zROmP7>tcPUP=294^q1F%{*o@#{yOz088<0LyZBo_OTYek`uBfHeYewOOHL3k|IXMs z&XXKNJ^h+3zVD0neOB7%>sQ|ot2R+VlJAykxxNpxQIJCn@I3>*Y=YN#q`v9C z`?Kk?fES1GySLo80lzo()j#;ke!UP*+$;O_GM0qDe1Gkw_Upecsvh`FhRutw?AKqe z{PQn)zy7)j$|r{k{PKg`uZP@o`?aqN|2F&eKcV~U$HvI@bM4o^QQCVU`}Nmo zx-+HnPAgoTC+0~y*H`?WzUvP~yy+J+;(k58*Zgw!>+dY~%SG(he@8B={A1p) zKPW2tKc)TpuLwQ8|DiWw-1xo$Tx{_?Yj33SZ}ia>J%}miyObc{wfWJ z-qKajAEEbLEiwL`Tf~2?6hBYmeVzh-Ac>FX4O~XXb;5=3&)=f>T=@R{O`6{CoBSa6 z=O2=BANsZUxZgM1-YLX-QAi^6fs~})x1~fMQKKcE`wQdu-+A68(k1S{TYpRWaO3yi zDeuBR!2WwCepTgjCHW-fG>9k;{00Ym?(Wk1@5ozo zJ_8?)l;Xc!7{Ac}TSfm5t+Z>0E4hxDy>X$I@B52xr>$ZEE)KBz@~5f2xKAg5xXjl> zv$!7e_uZQpX>?qd#P{Yf7{sj>A2sw&rrgK|Y&|uT>nT4E(tL|1*L@(Q_x-KTMSui9 zTat%aUs5>Mzed5Nc{%;gCrL1XT9@t>+!;79}Fd0YmEy6>jS7=9W!Mp8`EZtOt|HNQvn8yFP!NzZGf2nX$&sqO% zp&zryaR>TU=o)BKdIdMe^aGq{ldbgv&M?KW;XD+)RGV z`Hp;;Z63>?VVHas8sv{~Xr=OroJUvxUORsz_Iri>{FLiyn0_$yisJ?2r#N2z4aUoZ z@0V1<=T^_Xt(y%tIof)s!ChKU*1A^V`s44laKpcq<0bou6e>>6y&GQ34K}&k+HJ7W zrS*P;jXteEX|Rpk)<+E{eX`aMDO_yl(-Kl_r^=y%+*vOuLEb(sG#Qf)@~2;mW`%n^_ied*E5iHlzi+F&2dVY@x%&Jb5ADYJ zJ)Ye<-{kjrb{UM*YQh)s-p*?b-fr>SugLH1%(8get@C?4v|p!tuC(|ymY)ax+^(MP zsapJUi@(C)ZiBhc;db?O4-Yow_jW)%;JeV$lV16~9j;IEdpp#t^Lsn5w)|5qKi4h! zy&a!N&njKhJ<}}x8G|n~__V=O4L)Ts74H1r4(+-5y&c-6WuCV5RLEqWHW-YI_8m1? z7)oL852nw8yI%S}&tJ+XPA;B@?UN@^!;A)Ykd~^H^1jY{?70DkpD&AT74WJ!QR2l@AYsz<@b8F zSbEzpY;Zi*XK{Sy_k1{h^LsviuZQD3zt^+Z%9}&}>8J1euxbD6G(6t#>t{LfeqVn{ zPV|@LM1M(6^q1s>-Ajn~`}#|AqQ4|3`b%=6za%I6OLC&WBq#bya-zQ^C;CfrqQ4|3 z`b%=6za%I6OLC&WBq#bya-zQ^C;CfrqQ4|3`b%=6za%I6OLD^Q_sG7Kj{#r}&ptU3qT1)bvwImN(InI55z1~VWfOQZ!6nc#M1wH2V!YAk2 z&qO-tF&~qqE_~=Qp+P;#^%4KhZ1xcCH<{h@%Ql}-d4&3EQqH*her?A!-uDZ#=Z?no z=n>z4^?hq;kMe!ouO5;_;`}^8@f;7rWq(7{1MfS&g?)k%E#LQvr5&tK*c&4N>E;O? z{Cs7e&hRRvo1^JYmddmC7Sgq8I?^RC@ASBOtCH>+AT2KLJCKdWtgp|t?=X4?^8x>1 zIX@KlABKsKe}5PHX2DJEOgZl(-)+VI_5Fvy)Gu{k@GF0Q0s9YIS&r)~e`ogv>#Qdn zV1J=q&s+;Wu=|2>+}VA>CXPstv$Oq!-50d^ zG~O39`(Hfg!tvxJAO{Ed=*UeJ>KHTf(3ZhFS~?+^Z{roZt0iJMveOWYsC%;e{`F7*CjI7m8! z9`{3j_iHtUeG1OE`91~bKhSkfiLTd4)*@Y@8H7U|Z~q+c5#A}|@nZHdeq7U2-edgE zEYPQTkMQdy{<{7gbbYCNgvUzz8he)H{epfwS&ILSg4_r#@7IC(g48KV<37S{yZ0LR z4_dBZIuTz;7vm|;lF;&Ns+jqGq!@4dW@x#K@gn!ek)Qw1 z)>6)oFPGt?8(B|ixkK~CawFaT8sc8R1CDlQn>l{Qm&fqXazN9^a*V#&<~4@jyG!LA z;XL%v=?U7;_|Wnl?TPW_Qf~+?AJus8-)uAG$ap*%9$MbP^zwI1e`+ZD`|FDN3xch- z`>A~s;K%UM_c$G&)_mDhdo{i9XN||h;+J{8x3Inm_$#!0MSH~gDqFF80(=-AT0W%d zvlY9Dhv{A8@nC#txq;=&Uv2N&9n5z(@f)9y@uB4pm9ulYZ)d&-C(6h8(DEqrO+f$b zp>8cF?6>~ie~NI+8_tt&k*3R@+G6QHrz6Sdi^xCL&)IX!t)7#X{wJB<`aPyst`+`Y zvB}b(we<6teu93>)~>hoXXsz?X;oxl2UotEhrcquv=of5l}u;%8AJ7crk7T0`uUb# zsUUpX`dY?Yzs+KwD1pXKoGQat-1kKbF0-&cZ3r#$^gDgHzWK2n0$l;EQ!nEjTQf2P+wF!O-v3tArmOyk8TB^!@73 zZe6K9;<3Ll|I!~#y5)C%-kW%Ys0R~IznAal`=Mt|6MZedXDemQl=6C;-XXt)zXjQv z_?>1~ay=UQF~V^^;!H^o_)x(fyg;Pm8cFB%`uF7hyhivi2WWVj3>uz+HY4rWNBpy< zUjw-WCjME|FDaa@`Z)ci2k2k>QTpxJt&#CK!0`h-6>k5E#s8uCpELi{>W7r{#r_up z+`e`Gdy8{e{*~50L&P)eH$H$eE3S8v*;np=RAIkQ8}>7Ov!(O--{nl_JVh_cnegC; znEx*IN2lViP;DNwToEFgO)%LiY*SQEN$CBLn+ z@3F>vU01GB1e}k<)}K@ub`qtZmG9pxb~%~XOFqvQFkdD4Lmek^Jlv$kC4LVG<#tO@ z@f?8PAN-J*=I&{5-1asJ^N9;X9H-^b@o*6XP80$&pK@U+WRqChw;@6j+d;1 z<>%jJX8XfVmN(n_3-zKue4QHxY`htL*3I}H@g1=7XZ*Wv2Kxv3;lfTEm&VuY=JH$^ z-cdOw@x4vFztj9IQcL=}*&DYCamAJU0d&8&K=V3#&H3y~O)#F%wp)Fqzwevme72*6&z5oc%tZY|R{tc~_qF=hDZOWE`f8Kg zzZbH+p_=*_7wG8?90o~tweg$Tu`0=DHC>V3P1;}M!!ObB&uIElVS6~A*C1!R1rA#m zAvp2-`ki;mgdr~6zg>NQkG=CQK}?*>Lmxk$FI?Bf^bPfNJV8Db$N6k&Kja$tBkzZE z^QYU*{CzO=A9eul*++iNT=KDEspe>XY?Zb zB%L=Hz2+Oe7O>vV8;xGegwW!=9H)OsNw4`$Hz(6259T}g@xA1ipyPLh7CHTJiV860 zQ`UB&PWPG|cD~NYB^u=p0wxx ztmJokxLt5uzCq#z`R4h4NAiV{mD=7gZ1f#sI_OWpGwSb!e7_<2dgTEJv=H*?hpz%Z zZPIe)H~lLeZ&~E4T^bI#h4dBq4=&Hgewi=p2aHqSZ_HXbuKgZme!tSop}g|*4D*|A zN}45q*4m}z`#IKXlj$$*WE_7#IoNluGUxAh^0D)G@N==ok5i-=ZLj=+U!97-#>epG zUsJoBm$vxcrVd2Q`%K1fd(1zuM(N@DeCL44;}4UcoL@R0F?nqF&PSit;^Ox)!c}Z% zv7gKH*D~QP;5Bysx>5@&=kt2~JU96AM(KZ_XMFzYZQ{Q5h2-x9pG$3H=dUSRznAO$ z0R7-l$*w=f{w%q`XD%0fz6;j~qT<4C`Y1=X8%D3^I1`EP1@1l9Ipufe2HIgwt7%bF<<2>p^M28f5!sxQ-3J$ zFW}b@7V!2Uexmn+_7?gX>2JF!&xiJbPhn?SUrKjYR4*^z-*feGgnV6+5B!4sNcTN? zPhynARpq?|k2$A-^hozX=_fDO-&aO?pcKlJpBn#7Deqk8Igk$d?=F;s{P%1W{a`x# z74U}pKKQXw` zNBOzaBNk6V;paipJ?v;d50aldBJ+R-!#69=QpRb9idTEy43Bxgm`{)I_VJ4Z`!$Ox2M;qla6v;-SYc- zdbz>uhj@N-I{PV}-<UNnfVkdNpH4oC=QpQw0*dE1r;`uk zJMGi2P&&o)TGOir@3ZpB=W?FOV2%TSx3WGRvljF@&J*?N97l3~*V6lWGLF0YbU#nV zaao_vaq8>F`gA`}cFOX<&dNV+@EnC9Cu;I5yKjun!6TYL)VHdxfH~J{c^K)3B&o@QIiRYtCPKSn_ zkFxW5@jZ2WuO&3>e3Z*+QEE`~#hi1xN$BJJo3$=d#GJoF2;}EWd1_>@w}DgcYQW+cu#!a zdnalYy4yRe@xJ#?^eV#Xuba#90KU@jM{JyrdzYQ%`MEOm>nG;Px>2^cFLTa7eW-VV_$Uv226{mf`FA&~mr@R=d#*w9_>N@p9ribt%DYPQn>?}k z_5A0k-mT@I?;KTi@={IrQun*134*`L|Bq<$G4}6c(oWxhbNeRRpGtV>XL>&$`cm(& z50={fddXU(BWA#V&gZ6nRqkQA{_gvUFLnR>mo+`*ANUNM?(`|1n|fb~uHf7?l zRIRiQ_*!Wlu&5OO+Jau==c2F=sN+HEh8sOZANSFAd{6EbQ!PufTe~$PyOr`SyS2+; z3Xtqpj_2%FVK8yotsKYkdsOeKX*fpNa{1SreCx#>GCadP%eRz%m}mKy(hu{joTc;w z2A#O9oqqk=n*MOPvZ0+38`?!^h{OD{Q2gRL5$(eI4)ufHTW&7c2as5tzyE^%g9e52 z<)^0alm0!|dIafE-tS$P_ao%c-FNkUK%$a+K6rmUGkp-@_iydrfbe@IK{k#0jh{nL z2dJ00TuuilXZ##_I^gRQ>QU){uTMCyrvtt|;e4D9n4ZwKTj#fQz}F|c47Psodvya; zXyW=|fO=b8KMYW>^Y=E>0qT8m{V>3Z!}q_^0qTu${lMpCh0e>Ze)f0f-@_X4^~oZO z_w`Aa!PINx`eEQzAf=R7vv^;h%ruzuMqED(hytSNr&_%6nST#!fb&889+uhL;`guy ze0^f~>IQ_Fw7gS)13!I|>w@%gVI06ufP2K>rQSK$C09(qS2f83zS>pbtNIR>Q{TdJ z>+4xheK+fk_toNhgm~8DdW81_^YzGC#k(HYBZ<yd6tPriuv)#7?&rNxu4^7Rh+&h5MPxE|SL>3u!2#bEMv zzTP3<=j$DlOO0KY-q$0$4fgfOUW0u-vd>^&kBk`X>yaY{oBrVX-Sj!yv5-48`6c|P z>4fj-7rr|MN|x`T_&Uk$6h*mr0{XyyExzzwspDEM`0KK-x>pPCN9^YhO_O*fu@x-5Jmd=@c&96sHwciHDl_-tbM zdFivo>Mhe}yM<4L&kn|q!)F)kUG}LGK1UdSUVM&Py=8okS@=Zw9B2GEd`__5Wxrg) z=Pbj|i;r%TCFA>Rsw$=vk9Qi<$B%dHNMU?Yamo3)o8jlhXSvl|rq4sjxzJtg|=V)%LSp@Kf1K6}rD&%O!p88LhwF5z?H0`WO%^_J;#%EBk2&uPYwqt6-E zyG)n71^F{m7f9zH?=?*~o<0jLd?I|h7(Wi5MXYz(kCo`Np5f>1uT55OnLb-Ad?I|d zGkzRCusVRBX^74Bj}^pkzR&;EOTv7ge{Z#Tpa0%ru+Kj?8|?DG+hCW!Z!_5C-&+iJ z`Lo2}6E^-A8SLZl%?3OFi!n)DnD6{Y1wPF8{_nDQr~exb_V&|G`+|mweZ};(YUK@T zm+^C)&F^7?Y#P@g*)*@7p!~2`g5bTbA;ut zd&2sY%l{<2uUpFtBdmYj<8~fd@I?>#JBpQr>!0S^GGJ_+)o-y_HR3J=txiwA~rIUF}mSVMDtX7x$NDk{lZN>Z@QcWcNuMGN)>Sz#qpnOksM1JS% zMQgX6Pxo`_eja_a%62yU`e7yEaENqSx8CTz(&%sRhOS$0_J@@ge~5Hlx1M$k+*5-K z@jF+y*w*{)p>e<70pS)bG7HU7fGpjx3xUaYzc7P$K!*Id58T50P1Yv#lnYWAGRGN!^ z&o)ohb`|q)lJssDn$RMX!fWWNIm?*d_q(zx z``gd=WL5G<*u(O&D#u^g!+cql{1%25X}O>$>gkkUIIHC-59u6}jW zS2cdLrk*olgP;t&D zes09yQFA+Q*1>l8Iqn6LZ@J_P&yx?DY5t1eovAhv5%3+Vi1OCx<>xThwVS;4eX^ll z+O~D=q_^{J7^EBw`*$<`I?CH>)9)$l=eYd*!sv(5D)Q|o|5W(e$FIF#)!Re?Q{-Rp zzwFPey%YTYUa@<5{ccZ={j{I`ASQ&*^(Ol71P!4yBaxU+!&w7du%4}vbEis?y&K#j}y$&FaFKnXq&|Qn`X;D zEX*S4zSw8^f!7Xkr~|%-_g26~($99z1mz(fVetE`JePOjz)Zy>>}NZwlW({Fryk&V zVcenq(UmAk-gJ?FijUA8&gK6$=_U{h_4bNG9Q;128OfY(gyiY_9wod@JaAy&4#Le+ z4xGzr#3RVVQGT;Hq=E0{BYaGMS-rsPN09a|P4%KK7pwdrbe`f|6za^Yq3Caa>7x+J6H~*KF}V??Ucr|5-TX z%9wFg2p==9EZoPH+?!LGwOR_3BI3K;pbO}adGP;A!+?c&5Bt3=r<>o;nuE;Zs+G^E zpLJM2J*Z$ZEb{{B1HBjYL@4|yZ?`iY&g-Mz-b*==gvS}~`8~a-!+d~(tC#+j;+QQV z{}jImk9z(7#jw;5xNnf)% zDruL5kKu!w2J83T-Yh>?%fC~Cik~B!W90|a55n9t93S&e)4%Ez>22Rr_xDl40mIY3 z&((HP(}6z=?vl=>tN`=>r)6mhAM|kk#yEwZ<#fk76-88=xX=e zuRy;b?D-w9aFFSH6X{TK&^u6W84o6%>8? zC}DfIDw{Toa|D+<3T*=_iGeIFT1H|N(;vQ^S- z>#2BO&)(sR=T+@oUc66f=l4Rx@Ur|^Uq}<>eg^r-zmF&*Oxl4xw3Q~^Ow5v zegXe)D#ib4K`-&|*~E8CU~~{Sy1;x44snpTm?|;8;2>9bh>vk}^sUmalao% z^V>^(rxcnN@=KF6UzhaLxz1f89m+k>Ngv}F@?z=D?_RdS&aZ*KtCzj&osu+Jen;OO zuY8}>`YnE!z|UK9Ud-=<&D8!+<9)E{27~I-?`=~J_VbpB!L+BlojBcN>+CkpkEz>J z+wA=09xS9#o}HiELwz&ye}q#ELnG5%Q&fOsEluSTEq+~uEq zS@cq(m%P;XN!JwkD}I0A9&o4J3k$4I^lz=dK8yI|-x(mja<9YU*&fmF4L1F{!S>f@ zkshL-S-g$Y2I*IyMTAAavv|@w#P1CFIZXCXeHQyG-cy@p`ydVWZ++GeYdc&os?Q=n z#CvLf@43;X{ac?!eJ|cqn?;4l?T7VQRN&&d&G_Acm6pG1^{p|O0xX`}oOPweZ?brk zTa7ISbKw!sZO-y@m^&=~HI{#u!LK!Vx52Xw-fQsd4Blt(9EGue1Ik05s9%srKPP;1 zE_sA>kVi8L{1Wv>4m;UbIji+#o6pcMoa?Mf{_?rb?70(~4tSs^{d~H=H!k|C@`0Zh zFYfOg6?r)R+~*N34|x8L*v8My>sby)eoR=xE@+bD2HCo{`3}6=uj{82dsOKNx7q{G01vm z(v;*AB7*SV>m^qSd))eo^3cSaJGc<-S5fz`>RDcu|?$DOqouLb|TkHv*$QpRFe5x@Fv z-<(w}Kik884nupjUbowX=h;5Luf5Lf7~s<;;ZwKwgt#9&Lwf?_u_i@#wtq@10KEWL zlK-M`X77i%-OlY#zCWBjXZYAVR(@ZgI1hmi7;kSAIWWp*3qB_m|K27tT)F>!JrMS9 z(samwgY+-(h4c3mf~`~CKI;3j;2YHI@6~J*V(R|wlI>dF=#4A~_JA>VKMTit3NBm1 z`P1JyiRUfW?$&bp7BXG)0DZql>-N~gw=>-N7UOBm{;k=UhNV71_r<=sOpkQfw*Umc z&+KHqlT9Doe^l$AD4pA}o&VgvRc(5Y&hwSz2CZncOFcc0fchQJXtx!9fE(qoNw!IM zp$ky&nEi{YCQIhrM&Hx-JwSMyl!K!Bwi0f(^8CFU#7mbY9**+I*lDeNgpcVj3kP1h zfBP8Acl)^Sd&lu~Gvgi4u*cFHKEd#h?_COBXm6Cqt8Cm3(vR<5t}=N%NI$-JiAIYH zt4to-d$jASl*fHWf4ApWCq1hDT1kFg{pw`uw=M@DC(hYV#z#I*F-rmOar?O@M_jI5 zXg@h0xqL(U+Fyl!c{%;%`%>UnxMu6WKI@Nc?G~kj+if9#(5`$uSbx|%6QB=5Ts}0D z>`69_<2cy&bHYK6-%9cX%iquSfYUh~WWDA20rfAE@;_xhHKejVn zwW&+f`ThjtH~Mo}%7gFogvVoCNR;l6ju~$^v%EQ0uAc{p<4rOFwZ4?;nysIH(EV3Q zTD3iXPHl7(>%%_LH|3|lj~j+LKNa7dK+2-ss$T~1`RrD%-(+jfvfedk%r|{v%_+jm zkI`Rroc_WS^k<$_AN#pAsULVAgD+wgboTkPbI;c_ozFMu=TAty&kGej^(8V;_Gyu? zk{s4`S=1+Qkw9_1lYS`bld3;nLIIC?`Nf*lH$*(ElSBy?=k{luUy%P3f5#c+UM_HT z(gGxt`r6fZeL?IeY~MdNeUsBnGEU#Th4^}ZRubx;IX!6SI6plqN(Ud;bFNiT?W36g z(O*Jp)%QnF$^5p1@z2oH?^)m{Tru-_p4BQj%@VmzMz6n4@65B5%se?KftVM70o zSO7zb%j`QAK_P*skuL+gFPXgsqm%WLf6T5^WBxEqeqJ}l^!*y^3B%;~byLXyE;qvh zn_os4zHU3`7uac!Nj<%Zy{FudS_Pk_w5w#-f~$lNXON%#ozrZU@z<(rG#>SUU!87Y zMne$>eSyE{^YM`_A^u?oKS-#0XfYj|DfRX%Awo+7(@ESvjOA&l;JbqDmh{@;zRwuv zkC={p6|VS_w4CGh-1t#0NWJLVXn*#7Ntf$c2k9yFQN?SbdP7UUwg~(@rarQ#;BWmu zqk-tWowhF+_eob!9ti$O7SAO_dO#?M6MXa+`bF!r{Z!|NY}!7R2U5=1bd$7t<#&{m zP20tEGulb7iOO3``6=a*4imL=?Iy}0Kd(Qd9cC=uA030I(Irgh2R9>K9lzM0D-FMf zYQQm_wwiQA`BKgmT933s>+y25RnpGQ+r1n$i28YL-@o+p;PLx3)A`}en66yjiV-XC zOKL!!2(N6#7N(ot&;cCNd3n~(Fr!^HIKR&j@lx*8_US;yd^)Cm+8b!!UdM;@b~-qn zA|2Liy3D_C*4j?|g?MAi1u@0>x>@>NH3*CgO?N5&3jHa@`D zd0o?cd6s^ojAEb|4w3IRNmiB1= zd6sVYB*QKL=zA^v;86{a<(ZsEJzCz#F6I;bw7l(JFUL9RRea_$yyp?~mzjT$r5`@_ z|Fics0CpGEz5l!6CV{7KA#fqt8grLT$P!)Mgs+em2_P8bN5aQtV>C^cWlaEu+}uF& z=if>;e9EhB7EmJ3r`b&)LEDP06|CA##cKSj*k@~O{fMu%Vr!MwYNMig=XZYR%$@!3 z-OVn97AgbTxifR-%$YN1&YYS5e1!59ug#y4U-51*xIG6#dBB_QK1lj*H+*yHgFYxe zoV$nMEwcd+^DQ*xGMoRVgDZ$n%0ann1*w+TDgkln;O3C7^rLOGq1NB<#;6C?V)_ox2$aV?xqhpQ9ek$l@Gm^ zzL>s$??Amt{}$qN`c->@59~jI{(*-`zwv3=c~+j_QU02p;_vvZm1IP92~)7kVHubs z&gV5gFYJ>1m`}{Ibp>As=n`10pKX$Spc8lyZtesY!6W(LfCuF_TlvU``5(jr^K;D8 z#VPIhvD)A^$!~Ogw-7@j>M<0G^w7x5N_XNUf3Uvevi-_?gyG!2 z_H;j|RG+`_+{F~<*OkoY{KER%pUcI`-u{kfroH1^FNfuP(_B{vKX_`Cav?$nTrRl;5L*{OWwN$nTq=6ZUyAd6x5kRC(n)7OcE6$DSM2ZBg!Kf|bK(6uQEtR*^RBJX-lBhx*!_kW1tPo5-?a_z4N%Yf zzIODle))vtcAxJb({H3eH#7I_uVa0 zIG^W3|Dav6`Ii6qvZj|N$wxbJqurC3K0rFK&k{NPJ5OrkbNqc_ukgk1OH6mM9Z;{A zJ}KX8?7M`2Uk~?lws0Kw?+m`a=T_EhUwfoo72M?-VDfzzYJuDB)1dupiS4J4) zBG#eMZk40OuoCCrQ%X(zzBpIfA8_da@nGBp9?-w@o7fLmLN<9{Nj+IF;yOUuM>+rx z{sR90DEt#jAKLrmoE)7daK(g%3FHXQ$4Rv3mfSwJufC)%AHcUgmwxoTdb;=Hv`V~w z9yYag*qIB25mp|GE;X?O1Mk!vo#0GfF6;9CZ(2Y9E}8D69l6TR8@ha^JK4Ug7W^y6 z0pT;8pSw-%d&{c%|HAgP`CBnz82mxI)XU|1i+}$FeP2GSK5qDZhv^(GCh>RF{QQ*9 zi#pCg?dx_?m#6~7RE{$FuzM8!yG`@?3Le%Me1EIkRh6UcT@I-qj!a(T?|LCDS^eC2 z)(WYrxA!%z8VuuJqwn;c2JiBCmdMw5?uUPWd%gX9zYO?^p&0T8y1>5~< zxVK3^c{cBddwDDDq5sfvIeQN5hr3qz>GluCCA4F_A8udNPGO$2knNv82ax)DT_3Cu z;lttPIU7(K;OK{Y-2I%uX!p3+48Pq&vT8o<%1RUQ_;+2%*FgcT&to{hg5CDo?}))+ z4&ypzs>S3L8iVw;x%87I#pM-z!aSy!aK2D7`?%VdpTDpS;QO1Loyd7pr7)0N(#=!WSV=$Cl^Rwu$HXK0VBC7kv#Uply4c{a24OpWmQMcl5ZD zR3g~Tp!?~BHH!ff7}fT#SpA=i9+ zN5EtMq3dImJKFnIe;vu!SnpSD1OEn58=2n;KJXXyUn%`O-OYJxYV*Q$a0lrz{yLw= z^M2Lef1UIFn4ff2~O z22wyb;6cYh>7X&4*RI}p6-w#3rgx*ihXeXCpF;e`)z|i9X19Ruvcv%&@B+^bC|B$i zxUtUNd@m~3&lzSjz-5uQZcr!v+|Q@*bGwA8*V1lHmN5NBbo7Fb$xN@<_se4P1Gc}( zv!8EE`>Z^F|FMtdgm+x}tUUXUgz}b16zUB~)VoW{i|4C|M>^n;{`siB*_uv?m2|;V z7C$?7vHXS42F{Lcw{V%h-3tSFP@3620hyhH%hoHH;zO4m^_?Q22hR1b>pg$hcQiXR z-go^$(JN2oe8zoI{U>6NzEt@7Y_>eRDhde<}V|(<5sGWj6Dr;GA zLln#Ku9dGlK9|2`qMf47}{m+f{GX*WE8v{NrDD|8<2w{JJ~!(sVq>bG9P&*k;^KmNOkt-t?)!qfhMy!|ZK?(>zOqp!bD&)E;(6+v-1 z0lv;$`V28(Jl*^IQ(1rCm!orm(CK(xKI`k?|F!GyW7$VA6=FN3TiRnR`v~x;=U3(o zukW=IU$b=w@V)mfy=x^Z>E3wNb-qrd@f*6aB%NHhVfB@b?WpV4k7Y-_4)l$~j@ld1 z_e|JPzX3WXqb8y^{}b$}mq~eJ*-?X0eV>jUh4%fgw4>ht6z!;LRR4+CQImwP&t^O7 z!!Ler*-=0G+p+Da*G290-_edr#o%dVM@|3ka63vq6A!baF36?l$CIaON4@PZ_Sv)5 zjsmxSmF=iIr5{aXAHyvQU$>+FU#{KHRDOI15=Q@<1i}CX?;Ae8|{a*pD$APcqfWpV? z4BIg;7z6~@OLd;G)56ke4@f%r^u8j-2hM{^cE78i6UDEU!!A z0fBv(aK3)&`=A_;-yi3A>iLM+~kq;$z`2KvO)9-5@ ziO=brq6n~_jTkTA`G@Zna4sKR0*;u<6j=ln7w#jsbN$&;&KLc>x#!Dne8lAGA@jNZ zI#Q0JPfbzFRdcp5! z$!?@Pdw&4jj%a)f@AVBRT0N%*I`N%hbA%7_{lZ8Or90T~+`jX6%Wk6_WtS6QHktIg z+@>ek`sp0{4+|LTvFbwnm3rx4@ugo08HYM7-p?afCfR*DClGHs$M_-nCWeO}Cess0 zZ#u{N+l2A(dtyp;o}?$U0%#vN=gX?|t$rrYp?((c`>niRdH?N_Y!Ec(6Xu8Vf6~G0 z5(2-RzkXkQejUu|8SPxA%S)Jl+d0rxt+%#21rqE1a)K;yX%Q zzMqr#`}0G&-p*d`&BkxrA6ze&`+Hrl#(EIt2mNRA*JXOPQndbK@u{6-si$+G^?lsq z$IJb(kbk)Jmi#zyhQ=3@bJX|m*(1;8f_nBN_`c3Rl1{>f@ygo6`yak%!#O?m`oa2# z9$l#I6V8n-TEg(s1kz^Emfc*X@G+tWU5vIX;d@ zf2{yrkv!}`II;^MFOZ8%MW$1GU!+u^9p(4X)#YS8=?HSN&cdcQ>|GlFZs__cGke;{ z5w|ys$uBhjXyZ!E@5g>=_v$E}yl=2lxJcVQ-D=~2?b~tvw#x3iMHmkBNOceSK4HHX zy;9&gl60%pqeg$?cxXtara%22(uw0?zi@q?TK}CW9{;}Ig?gj@(rwUle0(qJZQu2N zXIt2A1K_I6TLeGkD3+@&2ybON?aHGloQ0rg?fJN90B{k#G7 zXDknSxz6jkpUN0mm**}eq?mAA7&$)g|0Mw2E;{`9%=;_T!2_DF%fJL2uNLDB2He7XFrx7SnCUt_h`4#>yN_3|Da z)?Tfe5cE$=r^$=yA3Nvl?#pC8k{s?U$7Yr5axJ>hZA z?n$q=8(JUjcBIBRJMY$VP~|S0r`=lCDWrje)sdA zm7~toa*z%^6km?wcT4$FjYFt?d>@kEqrXYaMbNpLW@9p|R|4XuOBm;v z@f*I=zj_T~q<(@I^;m82zCJP}@o-2-eLNobY7M*Z%J_u%VfT|o{Da1tMX?w8mqVW7 zoQ~)Ddt~{GeT0ie5OD8|_Lno*Un=&#BjUB6*t_zTq9|K&h}V8%@6eZu?}u1C-%dxm zA3zC+!?{PaQ^$7nT8sL!shggf53Tk)i) z8@+zN%IQ|V-xqVb(Z6(u>i_g~;>~P6maSYzyX!K>uN9>ORt(+G@D!nQyU^2oA;YkL z7t7CVX*2t?FnfCx@R~z5&yMz3{bG`a#7dVWk;5YXF!yybOcACOro`4j2uaxj_`VkGy9U3Z=Txpt{?b^i4el*@Vx=&z9xnZ&+tA{%D<&(|cm7;=u?Ca$RQf~4Cx1^7@~rt^ZRg8byx{qv z)+;@W`6}q3;xHeHzZ+ql0{fG&j-CHb^fztw@5bq=9Bxwft<>lrFP zFSoGEX*}O~9P^vIn9tXN{r+%_mvETxTt8prYJg#|XTRi22On4baxbX*>CPwAZ)$XwN?X9WK5ZoDj|g7>ZZGd;Yq@KsJd_j5q1s)oiY|fP5)fHuAf0tOI$8e# z>U+6gjP)=+_&FU}m(h5CZx(z(17!2aw~i~cIl_8=m!($<-I@S+biob=<#(^f`+H1Y z-&h}NJ&g|NLGV+~&5_%O_7~=o{I%Yhs_z{LHCjY46nBMjV^rpKOD!!QB zbwY2PZ+gRgi$~<^&gGlhFyE39`Bvug={j1Duj#JkBl2CUe4VcK^zV^F=d=FJF4w{w z@CWlp<Xe_8W4Q>?{an@YZ2id3<)E}##)Nb~3JUrFyjmicY|mF_sGbo%_t z-yipV6t3^Y9@l&_zoT#GS~Gi(ATzr^9ZZPd>f`rn%epW5A?+<)Prg^+FXX!(4}H+c zKKO01Nia@J^YHxyshI1f4qp2zc&Nu9Pov@ay&N9Ext2LpzpAHg#sp*v< zJKUq4pC_dE-@$q+6lmdhw^{2Lk=;sv**5x^55v;I_fqw}-4{rNxGwpJcJ%sI^mazz zJq^i8xlaAZ?{$btz<-OshDfcTtI!Hb5-{axy^L{YL!donzW?yoh%Z>4WsE6r(f1|&<;r45K{{hx>$K&)L z;5g~`zNUN1THkD8SrKKMX>VrhSYS4lglE<-vgz%FYwx7LWTE=+)W0G(0=`&&rzU4U zFD?~dq7eD@{IFiNMw1ht|6zSdnl4$^ARcnlC?1qHibqWv#h0V_-SQi9hVKc()~m#h zfZHTK`o-$C8cb%br=Ne9$mw5w1JgH&hkC3wc-KST?~soAcs#g2Y`se8fkXVT^(u=8 zpU?uXM=QmPe*ySlcebq8cGr1lv=7nmh4OJs>|=%VeI@yMYSyw@(_?!A^YBLd8~?Sn zJ}v>z-tVG@bkHl3+;BWQn9H{%m+wWRkluX%bNdwh63pmG}J z`&NV>{DoYB9^`HW|IZP;-vcB2r>W;t$s{giH%o@B-^;s$njW_Y3UWK#*V!A{v2$dI zOzplHx$kxa9_#}gEB{Q*k6wj&&HfSj%ZJHd()^HUB~a(^KCQhPr%olgG8_1Mw?kFQt7c)pV>e_Nw`4eI^PT)rKR@-?XMS9AFuYLG8| zz~rDoxnCMqZ^}(v@5K%9HLCZgb95|kkT2cEc8~dmwTigBH4XClcpK+?B$sb}qkIkO z`yteK(Cm|)oR^H)uOs?IR7>bTZjVFH!1+F0e`hA$$@x*(=P}#hY=5%97v=8>h4Eu0 z^-RYlD2?L^e7_gp?N5K#DBM#iFv!u-YWa{!oZo9U)XT8H3+Cl@T%Ipyzm7Xz4%Rmm z?*#^D<8wOLPPngllKMz-N&WxG$7tk z%j;x0Vcucob@;m}p`7i*_$2Gi%y0E?-eB~Ken%GH@96!|?>lim4B2?vEOTDCKxdT& zA`G|8`lYl#siX_sN;c{`aN@q2uS-bK94-jBO2+)6+6V|(}8 z`?0SZg?2vC==65JmHC%hyQVwWu%2d*h4o&Slb+%EWgUk6oJKt1oI=&Ywm#YX2BXv4 zdz#^~_LlVymTU6XyxrifJ=|{V=(l{=@^R_8`wv_rNPTwZ! zpaZHM_U9yNBIk#cqwSX(pHkb0lG^^K^fcp(pT}Fd#md=C-_|Sr9&MqI_)Jda{yT>4 zey-Bd4+|B17Y0oYJqvkAcN4Cd+=pc8*V`5U*ClZ=!uAf#*M%PlubMU>e*s56)!|5A z9qv5}=W?CeInO%$9V{o{r2<$h{3=V%&zS$#GpLATI}Y}kf2Tsf0KD`I^2OdUj^WV` zey%lJxnJR64*V#Z7dAQv_>km_+w(DNPa#d)^Rv>X)qyWrxzrQYfxooy8uhCKU$pQF z3-kMY*0S8fpSScS7UuVQ+3#TC&szFI3-7aVr-gYxpuekB9r(1Rmo5D-EIi%9yf-jw zIo86DTKZHA^PSVIC9&|ITRQFhs-3eD{;Pad2Zk)2`k^}TXBMV@st$a@!ut*W;}(9* z!XLBnJ`4Y;h4)(cqZWR|!jD+^AqzjO;p*T==$H%c+SThdT)VSM|CkN*SJvn+-AI4Y_4Mc6K)+>^`ZE40JvCczk#o-$PA#4FskPi_ zVb-_Sl3AGisI~l-g~_j4%Wqql{H(RS&BEk&t>q>QKVjuy9VYYXz zrcgx2s=kvG-bSAA8N#qx`#Ct>x_ozu4gKwD1xO z|1S$KxA5;+c!h=EVc|6v{#^^Nv+(b!kNqpCf#WI)%F2q zi*_g;*Yke9#K&#dYp%a$PFtz)&~H-gp^jG4gRrO;`JVA+O&_U;o?t!!g4~Uzx1i^W zvKvRhL=SJ*_AE7VqJZ^u=yB1j8t>&|oVo0;WIWutL*xB>gV$r`G|4E=?MJ`wGqru3 zuJ6M;%Kq+$%{Rh3%BZBcG}r@oD4na?cn>i~8-SGDZUnG6AOA)>PgR;~=ZWn*N~ypB zEj^$1LZyl6r73J*-`C{pN2q^`)W2l!Rmpp}T5fg&^9MW1_VxSz*7Q6DOZ>ctuL}U* zOB5dsvZ8&jDna1HH7frYg(rjjoQ;?7=fms#M=MHun7uc&MT1FzpKNxO@jHM1!0$nT z{Q?lj8_w}qzAo|cvr?F=1*F}?>+`I&Qa;i<<}!oNL+8kc97r7KQ{T$Ldg!Mfee_Wv z6K_&Sjrn{X!_RZh6}XcHrlPw(h~&x7FLHP2omupI2G?yh_0mv=3^6_JQyFA$ChWK;I_u z&z1Pm`A_*vdx&pTzT5k;u|8|WcW-y_<7V*3-tRe*{4Yj)j`@Fl6z}gihI=d)ZKvGK zWj(!pygmGVA%x-lJ^;U8BChu%QZJWt)bsaHPrDbu*JE7n{r;1%U(@8k&r7D)Qx4Ph zf=FDtiQ|&5XIyhjS>rPwuk3t%cA3eqlf;8@;+CT1^V$&iTQkge7INJ&Rb^}vcBmp zQjoapLi*_~R=*34KerH1b|HQJE-!6$&4p!+$k%hU`uYB~q0Ph-*YonIo_;U(i2AX6 zCf>e&UqQNy@|9j-c7>gjNH4H@`8axk)yv1*3rL5bpU`m}^_rir*F$W-fk)J@zi-9% z4DVm{6JKik4E;X+^d6&Y>0`{FyEn+?-`~ad`%-jV5_+^>z&HH}x#;-v@OuCyS<|6F zkvJF9-KlH-P-t3>&q+QikLFMs*T_hct7(R_%1@27sQzmK{7Z`NifE)MbUyYSA( z5xDr@R?N#zM5*#!P~aDQe9C9=`xoCi_odm1o%k8%$3DLJ{MXN&%Y1?DVD}dUyJ(JP z7rV;*PH;o;ok<4O*9EM8S6jX8J#+c4r+Cp$$eGP!d&hj^zKVSTuB>pBqZn>K+HD@? z4fr5I**wyXaq{2e`SZLztWSe=kUfG2axMRdb1#sK_n#2igQrL$(odrA<6n9O>pgTl z<7Zj_n=`4H%mREj)Z5tmX}>d zA9@GnW~UgPQYW}srU%o_Wev|cYH}h&^e}qSKA`t!*pF=Xn9~{ReY$2(nhjrOdJyGm z`&zla-z(kJ&hiSyQOo;2%IhIsFDJhbCy(Fd)A!Ya?o;JRs>cU8qq(j97#wvu1pIC> z7+l|0ut5yz(tl77moLDzM0x}JxUdusIQ6C8@>BWte}E5}#Z{)(@gW`f01te}#`q-h zs)H4|&IvCN`O((EoSJqK^!mrXN0|bHz!$V|G2w?cq<=M+4%Os*0H4qmipeth zEA!xSypMBU4&RG4KMfv#|G?jo!TbyS25#|g^6zKG0aF8oMubCcSMsiY` z^a}MyyGI=Na3P=bqtUxGd(qw-6phyVCJEQyq2YKk)AqCa`h?H7!M}TQ^_{Eb;jhSj zAN!x%m8)9$o>bsNxNpnW8>F7h$Bvv1avWUMYU8A>|EGI6UgqBcb$bu%fhgJQjW96! z`K*!efO0?VT#*mJ`S)kk*Za@O(tpA{*Z4KoJ18$wc+w3y0^W3x<8Y~|C?ScH__yTv z*Dmp~{DCEY?|0mPkstC52RV8?+FyqJ%f%{lJSZo5AoL}QBH+(h_h5rV zJ|4JznPKn~=k4xxoZDBiU9J7t>`l=37t)V0?xH@v@1r!C>uo9zidp|m^>Y)*H!Z4{ z--|eO6@k|ZK4{Z;oN<2b5`I0K@4c>w?HR`5=fHb*_Xt0|-63D#v)dVHryodD!k1=# zPL9_v7M0s*yk7TD!^dk#Z2a!omcJREUY2y{1Ng-8dXdDAZ@m6lPXCHpl=3e|Tjotok z8se|*xpp;5zixf+hU}O`%lGxdYTqm*h4sQ}A7)Jmlke3&U1F3l?fz;X=jpy5q}m6q z4S3EMs(r#F4L_#kRQoWBAbp>O(JBaYepBtELF((L)js>)X!lxF`$`7C$MSQ5$k%VH zeO%b`d-AG%;1=+2GkEUn@^cn+{Rt+lMPuuOIp{~8N( z{*tw=4eG3AiKWAu0DX%sOvB#qkFEC6@bq_0t9_h1g#9Uf9E1HnylNkH zrQe5F?W1B2`%`X%6v;lD+tt(cm9qSdHEEE(<#5co?V+x`S$hf+H}gle>bU3XaDf~^lH=DfBYWJ+I03WzmKmr zo&C?h8`h??f2IQmj6U|?bl`Cdvw!<~e{DMZzkd&?O`oOwulCdT?+VraoSx{<>52ZF zp6Ji%iT<3P=+Ehi{+yoZ&*_Q&oSx{<>52ZFp6Ji%iT<3P=+Ehiei3+aelD!qpVJfl zIX%&z(-ZwUJ<*@j6a6_o(Vx>3{W(3+pVJflIX%&z(-ZwUJ<*@j6a6_o@h;O7_iD8p z>WQy@FcFH0mJ#t}#=#)Z2@K4SX? z@kj?8(x*h@N4Bj~VkKShl*P}ET`Yg$FEqd1{4)J5)CX=i<=1mk-*3%st9T)4ex4|s z#suGIolz_&yuRc22BAKmlJ7uIWu51*r3rl;oya=RTH)`rd7Wpvi2ieFou^Cq`A(HC z$|2-6Ug!Bx)K1~M%vsFf_Pnp#hI_oLl+%>mJek=>L34Y{*TuvA;m4y?*%#jSbM3$3 ze)8ifudCV)YCQ78r8gV?zh~1svEbIP^802#_`<~AH+x*+pHMkPyZ?Kx-RCPmIW9t;;9UMO zomi5am!B*{oXZLDt;wZhYl5eHe-HbbIXug`(H%<9%muU`yuX&3*pY$vlQ}vU2wjdB zbV5Gsa{piaPVCv>TH|#GXcn&@=zov2M?7wWkP`!pb!2VuQS{x@)6rBm_a~(mWYpW<7cvj;^W8DS!ciq{%lxh`1Dg;XV@3< zVIu1cD}}$$=5>buA*R%GX`NxI@YClV(07p6c%5Na)K32$*BKs@CEP~q3@vzA(;WJ5 zew|@)F8xTYGi=VSU$hGit}lEZC+9_VJbgB;GYA`hrPmq0{ke&)GyI*x*Vh@omuvUK ztut)Sts|78bp*_Vx8>4b70p|`zdx0AhKF->KC9Om(0Ro9I;iWn*q+-a?J(B+X zze$lE1b?w#0l2ZRA(J%-xJLHkEx^xZ8+lKIIt6g@zuE<5@qzz8WJ$^U`P1IFDnx^B zl+$VDcs(`+zvqKxvA5Ht$ z!}qT}IlD7nF9-g;x%7XJ^uCV^jrOfxA}YlDkK1$A)^;t|->nD!Y5y{O-Uidm^8r70 z==ggi2%|owNvuHiWb{%AH!N9>9cRJHnMVqAdJ+IP4J?Qtl zRhn!ckL?5T@8!-91rQ2J|Mhb z^gBPk6~E;MvIkdPz<1M1O}0PF+C8T42iO1JH^)QY^z&DJ9C-iXEnocGM)ZB8<;m9} z{pOFpaPA2DOzu(cFF)~9?d@1g=zYBwp)D85BD&*J&p6J$Pc-FkNDkoKVzwIyuN#o-N*d| zzz>d8cYRH1EFDF|4CiA1Q1F$eU=>2>5Zm@L_yT+>eC1h8ALSDLRnrA8>V@(T$blHY z#}w&^N4bDQI<_Et|4Q%YKEd?Cy_92nx1)RD!y4`$d`SK5a@Ie47x|r?iC&5J5W?V} zqMn>HX1ZLXr0w8*c75*qo{G~VnqN~`~i7}@XnN=Pm%W$l(d(yWbnpaRT=d zJj~=|=6PlPbuPsy$h-3^+#e_9!Br-mr1c()uJZpBVKfJN;`QlsPiF$IpOC8QIs|-w zUj}@Fs}!ajww$TCa_)`lkNN@CIkU;TqV_5&&*HtGJ0H3%-sSG8@c;Ix{KNB|`)Eh- z-S`stZcSe~>bS%5BhUBiBYM1^l}Rs9Je8yVhxWIz+A+`HYa)CuU*qNHoJo^)Cm-5Z z`?1L<WrZ+}%b2$QDuG;y!w~YWbz=r?Y1>+}5Ff$@E4^?&kr$ z&==58J!DH5uXb6;(Hnc+Fc_|1DCs%Oq1Cod3)7CneJXI(lc`AD zjw($Np%GV1exUWO-Sppv|eWElvWANZ?+5n#One-1 z`_;qfua}A`>hcuFZ;`1&KE5h81zbh+0-WFL4ts3V56N@HV;n*V_}?d8)8D^?JplRk z^F;Celek|i#&=xo<&ow2JzKap23^Ya%+O9?7kgCV_5QF$?N1;K{kb=)N4BUl z#NQp&J6kk8#P1je&wC5yT&bo*KVt?c_fLl~>`yr_s9}E>FXrz}Q8{QYz+=90M--3x z-gJcd``Pkv{vMs&I_N<@Z-lqKy^+Zc#c23mwS9a$knx5TBeH+{F4~bkspTeP@ z&=jcG4T31O`>fqA9$KqliN9mv^O-pRS(blStL#U*9U-B|?0%mo;2toPlkTKG^ZSJT zylP!en2vfu!|DBu;)nhs?ScAMjygIii|_Zg`FrI4-j(lTo<*@&dr83cJxQFK_i@$V zq4#(6&e;m=DDMIF)B8zJ^Mx8rs>|v7yMF$@q~AkaO_`2+N+9>m5F&9y+vtNksMnBn z*}38)JnIciuiZ-@^CD1!_sWIbLnV8Mv6yh2@^`>W$v3o}9pB8YJg0O{JJ+@SeZ5dF z&GBTe_)3SBJIm+}?R|TW9;poI>A0Bp)E5)Zi_e*xDWKm6)7-^;c)t_yH7l=F{I#Sk zVZR^A;nD|gl}4%=LfC?&Wp4fP4cJ^bP!$D=UtEt4S&KJYZJP%@Lc{JdHt(w*D2eM(dLUBdg#(3OPu z`}nHoF+8X6+T?8cIdq1lm*`h7-l9KhQYIYw6Y4dy9e=?m+B4`k=-M5&&)V;i+9dIS z^!u%wuRt#10sSujRqHR+IZUr%aw@KNK7Eu6c)t(K=eus_7ZbZ*Yd7^ztas!(suAz3 zgMUA65xp=#JE9`@4Z`_3;>_luSrxrW^h&pSK_7MxaK74O=jyu$IIrz#;d=;GJMUX6 z{8;C=exFy(&h^Xtccv$4hxmP^+0~?{+s+gAv{2r=2Z*<)h3Ac8c|!fhlBeWY)(th# zaXSiw55-8H{9f5}HayJ!-N$-koLruh!+V8)E{ERUfO{wh_swYi19FJ|54d$XxUWTU zF4r;MEjjrA8^OnL+jDSRBRZdsJ@WHekarm*B@~)LsR9vaRb&qr{*ZR_RRIq;T&d29>2rTmX_~Ga5LcMP$K8FV%FNc1# z{ssAf#SOYq1EgOP@nt-Gd~FPmMNx5%ANal^_46>wD@%G@-YcX0v7PqWSYHc$SiuLr z(}iGfHy@Al=M#^_y8tH%T}Sd=fOf^lb{x;U08s^3Ofmti^gZ?x6 z{q&rl#qR?kpU=B6Zh~`u-^)ba2lykAW4~wONZwD6tG*%Ef_~Wh0Ov;W{@w3a`F((` z&k+9$5ufAt0h*)uu%2MwQ++%^yPZ8F(nm5z!ohBWULDVTc+0T)ZGArchtYgAo(~@w z20wB>3{BwkU!NB@ng_op!Vi6nFD{q|KN!WMe(yc0Ucc1tA5SF?F1Gs~m-YdCi&Wgt zKW8@{q+K&Wc-WDEi|;qX`T*>ca#Szi`Ks7S`T0?0vVB+1;RF23N)esMu{>Y5>7DdK z)f>H&%Br7Awk`{Ls1J2P!aUXIuY8TH0f;KW7$f) zSTC9#@fUm-b4g(5DnMz{OudJ?B$Sf7y3=9FkA&gyy$A7_ zhb)ij7iMFn6#Z#oZr<@~Apqq`cRO@A{9RUVWfIpR#t;85+TX_y&+nGwhvz(S{P2G_ zL3;jrg791?89zOIpKtu|d~a|3@O(dS{P3K2jvxL*6XeeaCJ2B31mWK|LHKu15dQ87 z!t?#P@$>Wc3F7DbeB;N@_X5WcfBgjMxo(2+S4|MUXM*snCJ5g(LHLU&2>+T1!e2N+ z_;VFL%Y7eBj}t{Nð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 diff --git a/integration_tests/tests/fixtures/test_builder.rs b/integration_tests/tests/fixtures/test_builder.rs index d5f9917c..8785b083 100644 --- a/integration_tests/tests/fixtures/test_builder.rs +++ b/integration_tests/tests/fixtures/test_builder.rs @@ -145,24 +145,6 @@ impl TestBuilder { self.context.banks_client.get_account(*address).await } - // pub async fn airdrop(&mut self, to: &Pubkey, lamports: u64) -> Result<(), BanksClientError> { - // let transaction = Transaction::new_signed_with_payer( - // &[system_instruction::transfer( - // &self.whale.pubkey(), - // to, - // lamports, - // )], - // Some(&self.whale.pubkey()), - // &[&self.whale], - // self.context.last_blockhash, - // ); - - // self.context - // .banks_client - // .process_transaction(transaction) - // .await - // } - pub async fn warp_slot_incremental( &mut self, incremental_slots: u64, diff --git a/integration_tests/tests/fixtures/tip_router_client.rs b/integration_tests/tests/fixtures/tip_router_client.rs index 665c44df..bf987d05 100644 --- a/integration_tests/tests/fixtures/tip_router_client.rs +++ b/integration_tests/tests/fixtures/tip_router_client.rs @@ -43,6 +43,7 @@ use solana_program_test::{BanksClient, ProgramTestBanksClientExt}; use solana_sdk::{ clock::Clock, commitment_config::CommitmentLevel, + compute_budget::ComputeBudgetInstruction, signature::{Keypair, Signer}, system_program, transaction::{Transaction, TransactionError}, @@ -1404,7 +1405,11 @@ impl TipRouterClient { let blockhash = self.banks_client.get_latest_blockhash().await?; self.process_transaction(&Transaction::new_signed_with_payer( - &[ix], + &[ + // TODO: should make this instruction much more efficient + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + ix, + ], Some(&self.payer.pubkey()), &[&self.payer], blockhash, diff --git a/integration_tests/tests/tip_router/bpf/set_merkle_root.rs b/integration_tests/tests/tip_router/bpf/set_merkle_root.rs index dbb5ed51..78235f63 100644 --- a/integration_tests/tests/tip_router/bpf/set_merkle_root.rs +++ b/integration_tests/tests/tip_router/bpf/set_merkle_root.rs @@ -431,7 +431,7 @@ mod set_merkle_root { // Initialize ballot box tip_router_client - .do_initialize_ballot_box(ncn, ncn_epoch) + .do_full_initialize_ballot_box(ncn, ncn_epoch) .await?; // Try setting merkle root before consensus diff --git a/program/src/cast_vote.rs b/program/src/cast_vote.rs index 6e4b60c4..8a186d8b 100644 --- a/program/src/cast_vote.rs +++ b/program/src/cast_vote.rs @@ -28,16 +28,11 @@ pub fn process_cast_vote( load_signer(operator_admin, false)?; NcnConfig::load(program_id, ncn.key, ncn_config, false)?; - msg!("Loaded NcnConfig"); Ncn::load(restaking_program.key, ncn, false)?; - msg!("Loaded Ncn"); Operator::load(restaking_program.key, operator, false)?; - msg!("Loaded Operator"); BallotBox::load(program_id, ncn.key, epoch, ballot_box, true)?; - msg!("Loaded BallotBox"); EpochSnapshot::load(program_id, ncn.key, epoch, epoch_snapshot, false)?; - msg!("Loaded EpochSnapshot"); OperatorSnapshot::load( program_id, operator.key, @@ -46,7 +41,6 @@ pub fn process_cast_vote( operator_snapshot, false, )?; - msg!("Loaded OperatorSnapshot"); let operator_data = operator.data.borrow(); let operator_account = Operator::try_from_slice_unchecked(&operator_data)?; diff --git a/program/src/initialize_ncn_reward_router.rs b/program/src/initialize_ncn_reward_router.rs index 8b58f69f..58e398b2 100644 --- a/program/src/initialize_ncn_reward_router.rs +++ b/program/src/initialize_ncn_reward_router.rs @@ -63,7 +63,6 @@ pub fn process_initialize_ncn_reward_router( ncn.key, epoch ); - msg!("Payer lamports: {}", payer.lamports()); create_account( payer, ncn_reward_router, @@ -75,7 +74,6 @@ pub fn process_initialize_ncn_reward_router( .unwrap(), &ncn_reward_router_seeds, )?; - msg!("Payer lamports post: {}", payer.lamports()); let mut ncn_reward_router_data = ncn_reward_router.try_borrow_mut_data()?; ncn_reward_router_data[0] = NcnRewardRouter::DISCRIMINATOR; diff --git a/program/src/initialize_tracked_mints.rs b/program/src/initialize_tracked_mints.rs index 84bcd3b7..bf30127f 100644 --- a/program/src/initialize_tracked_mints.rs +++ b/program/src/initialize_tracked_mints.rs @@ -47,7 +47,7 @@ pub fn process_initialize_tracked_mints( tracked_mints_data[0] = TrackedMints::DISCRIMINATOR; let tracked_mints_account = TrackedMints::try_from_slice_unchecked_mut(&mut tracked_mints_data)?; - *tracked_mints_account = TrackedMints::new(*ncn_account.key, tracked_mints_bump); + tracked_mints_account.initialize(*ncn_account.key, tracked_mints_bump); Ok(()) } diff --git a/program/src/realloc_ballot_box.rs b/program/src/realloc_ballot_box.rs index 067edfcb..919eaf1c 100644 --- a/program/src/realloc_ballot_box.rs +++ b/program/src/realloc_ballot_box.rs @@ -42,9 +42,10 @@ pub fn process_realloc_ballot_box( realloc(ballot_box, new_size, payer, &Rent::get()?)?; } - if ballot_box.data_len() >= BallotBox::SIZE - && ballot_box.try_borrow_data()?[0] != BallotBox::DISCRIMINATOR - { + let should_initialize = ballot_box.data_len() >= BallotBox::SIZE + && ballot_box.try_borrow_data()?[0] != BallotBox::DISCRIMINATOR; + + if should_initialize { let mut ballot_box_data = ballot_box.try_borrow_mut_data()?; ballot_box_data[0] = BallotBox::DISCRIMINATOR; let ballot_box_account = BallotBox::try_from_slice_unchecked_mut(&mut ballot_box_data)?; diff --git a/program/src/realloc_base_reward_router.rs b/program/src/realloc_base_reward_router.rs index a9f7e050..04adbf7c 100644 --- a/program/src/realloc_base_reward_router.rs +++ b/program/src/realloc_base_reward_router.rs @@ -42,16 +42,21 @@ pub fn process_realloc_base_reward_router( realloc(base_reward_router, new_size, payer, &Rent::get()?)?; } - if base_reward_router.data_len() >= BaseRewardRouter::SIZE - && base_reward_router.try_borrow_data()?[0] != BaseRewardRouter::DISCRIMINATOR - { + let should_initialize = base_reward_router.data_len() >= BaseRewardRouter::SIZE + && base_reward_router.try_borrow_data()?[0] != BaseRewardRouter::DISCRIMINATOR; + + if should_initialize { let mut base_reward_router_data = base_reward_router.try_borrow_mut_data()?; base_reward_router_data[0] = BaseRewardRouter::DISCRIMINATOR; let base_reward_router_account = BaseRewardRouter::try_from_slice_unchecked_mut(&mut base_reward_router_data)?; - *base_reward_router_account = - BaseRewardRouter::new(*ncn.key, epoch, base_reward_router_bump, Clock::get()?.slot); + base_reward_router_account.initialize( + *ncn.key, + epoch, + base_reward_router_bump, + Clock::get()?.slot, + ); } Ok(()) diff --git a/program/src/realloc_operator_snapshot.rs b/program/src/realloc_operator_snapshot.rs index 0a729901..63077e64 100644 --- a/program/src/realloc_operator_snapshot.rs +++ b/program/src/realloc_operator_snapshot.rs @@ -68,9 +68,10 @@ pub fn process_realloc_operator_snapshot( realloc(operator_snapshot, new_size, payer, &Rent::get()?)?; } - if operator_snapshot.data_len() >= OperatorSnapshot::SIZE - && operator_snapshot.try_borrow_data()?[0] != OperatorSnapshot::DISCRIMINATOR - { + let should_initialize = operator_snapshot.data_len() >= OperatorSnapshot::SIZE + && operator_snapshot.try_borrow_data()?[0] != OperatorSnapshot::DISCRIMINATOR; + + if should_initialize { let current_slot = Clock::get()?.slot; let (_, ncn_epoch_length) = load_ncn_epoch(restaking_config, current_slot, None)?; @@ -114,29 +115,18 @@ pub fn process_realloc_operator_snapshot( 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, - epoch, - operator_snapshot_bump, - current_slot, - ncn_operator_index, - operator_index, - operator_fee_bps, - vault_count, - )? - } else { - OperatorSnapshot::new_inactive( - *operator.key, - *ncn.key, - epoch, - operator_snapshot_bump, - current_slot, - ncn_operator_index, - operator_index, - )? - }; + operator_snapshot_account.initialize( + *operator.key, + *ncn.key, + epoch, + operator_snapshot_bump, + current_slot, + is_active, + ncn_operator_index, + operator_index, + operator_fee_bps, + vault_count, + )?; // Increment operator registration for an inactive operator if !is_active { diff --git a/program/src/realloc_weight_table.rs b/program/src/realloc_weight_table.rs index 21e80224..8c59aca4 100644 --- a/program/src/realloc_weight_table.rs +++ b/program/src/realloc_weight_table.rs @@ -42,14 +42,12 @@ pub fn process_realloc_weight_table( new_size ); realloc(weight_table, new_size, payer, &Rent::get()?)?; - msg!("New size: {}", weight_table.data_len()); - msg!("Data is empty? {}", weight_table.data_is_empty()); } - if weight_table.data_len() >= WeightTable::SIZE - && weight_table.try_borrow_data()?[0] != WeightTable::DISCRIMINATOR - { - msg!("actually Initializing weight table"); + let should_initialize = weight_table.data_len() >= WeightTable::SIZE + && weight_table.try_borrow_data()?[0] != WeightTable::DISCRIMINATOR; + + if should_initialize { let unique_mints = { let tracked_mints_data = tracked_mints.data.borrow(); let tracked_mints = TrackedMints::try_from_slice_unchecked(&tracked_mints_data)?; @@ -61,11 +59,13 @@ pub fn process_realloc_weight_table( let weight_table_account = WeightTable::try_from_slice_unchecked_mut(&mut weight_table_data)?; - *weight_table_account = - WeightTable::new(*ncn.key, epoch, Clock::get()?.slot, weight_table_bump); - - weight_table_account.initalize_weight_table(&unique_mints)?; - msg!("INTIIALIZZING"); + weight_table_account.initialize( + *ncn.key, + epoch, + Clock::get()?.slot, + weight_table_bump, + &unique_mints, + )?; } Ok(()) diff --git a/program/src/route_ncn_rewards.rs b/program/src/route_ncn_rewards.rs index 4f73656e..b4bdfbc6 100644 --- a/program/src/route_ncn_rewards.rs +++ b/program/src/route_ncn_rewards.rs @@ -5,8 +5,8 @@ use jito_tip_router_core::{ ncn_reward_router::NcnRewardRouter, }; use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, - pubkey::Pubkey, rent::Rent, sysvar::Sysvar, + account_info::AccountInfo, entrypoint::ProgramResult, log::sol_log_compute_units, msg, + program_error::ProgramError, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, }; /// Can be backfilled for previous epochs @@ -63,8 +63,12 @@ pub fn process_route_ncn_rewards( let rent_cost = ncn_reward_router_account.rent_cost(&Rent::get()?)?; + msg!("A"); + sol_log_compute_units(); ncn_reward_router_account.route_incoming_rewards(rent_cost, account_balance)?; + msg!("B"); + sol_log_compute_units(); ncn_reward_router_account.route_reward_pool(operator_snapshot_account)?; Ok(()) From c6f8f1eba6743a325f640045fb39462af35dbbc8 Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Mon, 16 Dec 2024 20:18:53 -0500 Subject: [PATCH 08/10] Ignored test now working --- .gitignore | 3 +- .../tests/fixtures/jito_tip_router_program.so | Bin 395744 -> 0 bytes .../tests/tip_router/bpf/set_merkle_root.rs | 29 +++++++++--------- 3 files changed, 17 insertions(+), 15 deletions(-) delete mode 100755 integration_tests/tests/fixtures/jito_tip_router_program.so diff --git a/.gitignore b/.gitignore index 3b003a6c..ade25985 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ node_modules # Debugging program_errors.json test_errors.output -tests.output \ No newline at end of file +tests.output +integration_tests/tests/fixtures/jito_tip_router_program.so diff --git a/integration_tests/tests/fixtures/jito_tip_router_program.so b/integration_tests/tests/fixtures/jito_tip_router_program.so deleted file mode 100755 index d64f75fbddb5feba116cc9e1918c76754659656d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 395744 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 qwZCzD{yPFZ<@@^atHAT; TestResult<()> { + async fn test_set_merkle_root_no_fixture() -> TestResult<()> { let mut fixture = TestBuilder::new().await; let mut tip_router_client = fixture.tip_router_client(); let mut tip_distribution_client = fixture.tip_distribution_client(); @@ -298,15 +296,12 @@ mod set_merkle_root { fixture.snapshot_test_ncn(&test_ncn).await?; let clock = fixture.clock().await; - let slot = clock.slot; - let restaking_config_account = tip_router_client.get_restaking_config().await?; - let ncn_epoch = slot / restaking_config_account.epoch_length(); + let epoch = clock.epoch; let ncn = test_ncn.ncn_root.ncn_pubkey; let ncn_config_address = NcnConfig::find_program_address(&jito_tip_router_program::id(), &ncn).0; - let epoch: u64 = 0; tip_distribution_client .do_initialize(ncn_config_address) .await?; @@ -314,12 +309,12 @@ mod set_merkle_root { let vote_account = vote_keypair.pubkey(); tip_distribution_client - .do_initialize_tip_distribution_account( - ncn_config_address, - vote_keypair, - ncn_epoch, - 100, - ) + .do_initialize_tip_distribution_account(ncn_config_address, vote_keypair, epoch, 100) + .await?; + + // Initialize ballot box + tip_router_client + .do_full_initialize_ballot_box(ncn, epoch) .await?; let meta_merkle_tree_fixture = @@ -330,7 +325,7 @@ mod set_merkle_root { let operator_admin = &test_ncn.operators[0].operator_admin; tip_router_client - .do_cast_vote(ncn, operator, operator_admin, winning_root, ncn_epoch) + .do_cast_vote(ncn, operator, operator_admin, winning_root, epoch) .await?; let tip_distribution_address = derive_tip_distribution_account_address( &jito_tip_distribution::ID, @@ -357,6 +352,12 @@ mod set_merkle_root { ) .unwrap(); + // Wait 1 slot before set merkle root + let epoch_schedule: EpochSchedule = fixture.epoch_schedule().await; + fixture + .warp_slot_incremental(epoch_schedule.get_slots_in_epoch(epoch)) + .await?; + // Invoke set_merkle_root tip_router_client .do_set_merkle_root( From 34d9f81ae7d3bd8647bb7852752d644cc6d90ac7 Mon Sep 17 00:00:00 2001 From: Evan Batsell Date: Mon, 16 Dec 2024 20:21:35 -0500 Subject: [PATCH 09/10] Up CU --- integration_tests/tests/fixtures/tip_router_client.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration_tests/tests/fixtures/tip_router_client.rs b/integration_tests/tests/fixtures/tip_router_client.rs index bf987d05..e6e3e375 100644 --- a/integration_tests/tests/fixtures/tip_router_client.rs +++ b/integration_tests/tests/fixtures/tip_router_client.rs @@ -1338,7 +1338,10 @@ impl TipRouterClient { let blockhash = self.banks_client.get_latest_blockhash().await?; self.process_transaction(&Transaction::new_signed_with_payer( - &[ix], + &[ + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + ix, + ], Some(&self.payer.pubkey()), &[&self.payer], blockhash, From 4394b1d0c7969cd87984b904b863ef194e28d254 Mon Sep 17 00:00:00 2001 From: Evan B Date: Tue, 17 Dec 2024 15:20:33 -0500 Subject: [PATCH 10/10] Update route_ncn_rewards.rs --- program/src/route_ncn_rewards.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/program/src/route_ncn_rewards.rs b/program/src/route_ncn_rewards.rs index b4bdfbc6..3b6278f1 100644 --- a/program/src/route_ncn_rewards.rs +++ b/program/src/route_ncn_rewards.rs @@ -63,12 +63,8 @@ pub fn process_route_ncn_rewards( let rent_cost = ncn_reward_router_account.rent_cost(&Rent::get()?)?; - msg!("A"); - sol_log_compute_units(); ncn_reward_router_account.route_incoming_rewards(rent_cost, account_balance)?; - msg!("B"); - sol_log_compute_units(); ncn_reward_router_account.route_reward_pool(operator_snapshot_account)?; Ok(())