From 5959b11467c2b10eace2880982db981b63cb500e Mon Sep 17 00:00:00 2001 From: ariessa Date: Thu, 20 Aug 2026 19:02:18 +0800 Subject: [PATCH 1/3] chore(solana): resync devnet IDLs and regenerate clients --- .../instructions/claimEscrowRefund.ts | 70 +++++- .../instructions/closeIntent.ts | 200 +++++++++++++++++- .../instructions/initialize.ts | 6 +- .../instructions/initialize.ts | 6 +- .../instructions/cleanupProposedTerms.ts | 77 +++++-- .../instructions/initialize.ts | 6 +- .../types/proposedTermsCleanedUp.ts | 14 ++ .../errors/subscriptionState.ts | 4 + .../instructions/activateSubscription.ts | 98 ++++++++- src/core/solana/idl/fund_transfer_hook.json | 117 +++++++--- src/core/solana/idl/multi_hook_router.json | 58 ++--- src/core/solana/idl/subscription_hook.json | 75 ++++--- src/core/solana/idl/subscription_state.json | 36 +++- src/core/solana/jobStateRetryGuard.ts | 2 +- 14 files changed, 615 insertions(+), 154 deletions(-) diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/claimEscrowRefund.ts b/src/core/solana/generated/fund-transfer-hook/instructions/claimEscrowRefund.ts index a13d54b..5ab54d0 100644 --- a/src/core/solana/generated/fund-transfer-hook/instructions/claimEscrowRefund.ts +++ b/src/core/solana/generated/fund-transfer-hook/instructions/claimEscrowRefund.ts @@ -66,6 +66,8 @@ export type ClaimEscrowRefundInstruction< TAccountEscrowAuthority extends string | AccountMeta = string, TAccountTokenProgram extends string | AccountMeta = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + TAccountAcpState extends string | AccountMeta = string, + TAccountPlatformTreasury extends string | AccountMeta = string, TRemainingAccounts extends readonly AccountMeta[] = [], > = Instruction & InstructionWithData & @@ -99,6 +101,12 @@ export type ClaimEscrowRefundInstruction< TAccountTokenProgram extends string ? ReadonlyAccount : TAccountTokenProgram, + TAccountAcpState extends string + ? ReadonlyAccount + : TAccountAcpState, + TAccountPlatformTreasury extends string + ? WritableAccount + : TAccountPlatformTreasury, ...TRemainingAccounts, ] >; @@ -147,6 +155,8 @@ export type ClaimEscrowRefundAsyncInput< TAccountProviderTokenAccount extends string = string, TAccountEscrowAuthority extends string = string, TAccountTokenProgram extends string = string, + TAccountAcpState extends string = string, + TAccountPlatformTreasury extends string = string, > = { caller: TransactionSigner; hookState?: Address; @@ -163,6 +173,10 @@ export type ClaimEscrowRefundAsyncInput< providerTokenAccount: Address; escrowAuthority?: Address; tokenProgram?: Address; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: Address; + /** equal `acp_state.platform_treasury`. */ + platformTreasury: Address; jobId: ClaimEscrowRefundInstructionDataArgs["jobId"]; }; @@ -176,6 +190,8 @@ export async function getClaimEscrowRefundInstructionAsync< TAccountProviderTokenAccount extends string, TAccountEscrowAuthority extends string, TAccountTokenProgram extends string, + TAccountAcpState extends string, + TAccountPlatformTreasury extends string, TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, >( input: ClaimEscrowRefundAsyncInput< @@ -187,7 +203,9 @@ export async function getClaimEscrowRefundInstructionAsync< TAccountEscrowVault, TAccountProviderTokenAccount, TAccountEscrowAuthority, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAcpState, + TAccountPlatformTreasury >, config?: { programAddress?: TProgramAddress }, ): Promise< @@ -201,7 +219,9 @@ export async function getClaimEscrowRefundInstructionAsync< TAccountEscrowVault, TAccountProviderTokenAccount, TAccountEscrowAuthority, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAcpState, + TAccountPlatformTreasury > > { // Program address. @@ -228,6 +248,11 @@ export async function getClaimEscrowRefundInstructionAsync< isWritable: false, }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: false }, + platformTreasury: { + value: input.platformTreasury ?? null, + isWritable: true, + }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -268,6 +293,8 @@ export async function getClaimEscrowRefundInstructionAsync< getAccountMeta(accounts.providerTokenAccount), getAccountMeta(accounts.escrowAuthority), getAccountMeta(accounts.tokenProgram), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.platformTreasury), ], data: getClaimEscrowRefundInstructionDataEncoder().encode( args as ClaimEscrowRefundInstructionDataArgs, @@ -283,7 +310,9 @@ export async function getClaimEscrowRefundInstructionAsync< TAccountEscrowVault, TAccountProviderTokenAccount, TAccountEscrowAuthority, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAcpState, + TAccountPlatformTreasury >); } @@ -297,6 +326,8 @@ export type ClaimEscrowRefundInput< TAccountProviderTokenAccount extends string = string, TAccountEscrowAuthority extends string = string, TAccountTokenProgram extends string = string, + TAccountAcpState extends string = string, + TAccountPlatformTreasury extends string = string, > = { caller: TransactionSigner; hookState: Address; @@ -313,6 +344,10 @@ export type ClaimEscrowRefundInput< providerTokenAccount: Address; escrowAuthority: Address; tokenProgram?: Address; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: Address; + /** equal `acp_state.platform_treasury`. */ + platformTreasury: Address; jobId: ClaimEscrowRefundInstructionDataArgs["jobId"]; }; @@ -326,6 +361,8 @@ export function getClaimEscrowRefundInstruction< TAccountProviderTokenAccount extends string, TAccountEscrowAuthority extends string, TAccountTokenProgram extends string, + TAccountAcpState extends string, + TAccountPlatformTreasury extends string, TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, >( input: ClaimEscrowRefundInput< @@ -337,7 +374,9 @@ export function getClaimEscrowRefundInstruction< TAccountEscrowVault, TAccountProviderTokenAccount, TAccountEscrowAuthority, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAcpState, + TAccountPlatformTreasury >, config?: { programAddress?: TProgramAddress }, ): ClaimEscrowRefundInstruction< @@ -350,7 +389,9 @@ export function getClaimEscrowRefundInstruction< TAccountEscrowVault, TAccountProviderTokenAccount, TAccountEscrowAuthority, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAcpState, + TAccountPlatformTreasury > { // Program address. const programAddress = @@ -376,6 +417,11 @@ export function getClaimEscrowRefundInstruction< isWritable: false, }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: false }, + platformTreasury: { + value: input.platformTreasury ?? null, + isWritable: true, + }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -403,6 +449,8 @@ export function getClaimEscrowRefundInstruction< getAccountMeta(accounts.providerTokenAccount), getAccountMeta(accounts.escrowAuthority), getAccountMeta(accounts.tokenProgram), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.platformTreasury), ], data: getClaimEscrowRefundInstructionDataEncoder().encode( args as ClaimEscrowRefundInstructionDataArgs, @@ -418,7 +466,9 @@ export function getClaimEscrowRefundInstruction< TAccountEscrowVault, TAccountProviderTokenAccount, TAccountEscrowAuthority, - TAccountTokenProgram + TAccountTokenProgram, + TAccountAcpState, + TAccountPlatformTreasury >); } @@ -443,6 +493,10 @@ export type ParsedClaimEscrowRefundInstruction< providerTokenAccount: TAccountMetas[6]; escrowAuthority: TAccountMetas[7]; tokenProgram: TAccountMetas[8]; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: TAccountMetas[9]; + /** equal `acp_state.platform_treasury`. */ + platformTreasury: TAccountMetas[10]; }; data: ClaimEscrowRefundInstructionData; }; @@ -455,7 +509,7 @@ export function parseClaimEscrowRefundInstruction< InstructionWithAccounts & InstructionWithData, ): ParsedClaimEscrowRefundInstruction { - if (instruction.accounts.length < 9) { + if (instruction.accounts.length < 11) { // TODO: Coded error. throw new Error("Not enough accounts"); } @@ -477,6 +531,8 @@ export function parseClaimEscrowRefundInstruction< providerTokenAccount: getNextAccount(), escrowAuthority: getNextAccount(), tokenProgram: getNextAccount(), + acpState: getNextAccount(), + platformTreasury: getNextAccount(), }, data: getClaimEscrowRefundInstructionDataDecoder().decode(instruction.data), }; diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/closeIntent.ts b/src/core/solana/generated/fund-transfer-hook/instructions/closeIntent.ts index da313a3..e0966ae 100644 --- a/src/core/solana/generated/fund-transfer-hook/instructions/closeIntent.ts +++ b/src/core/solana/generated/fund-transfer-hook/instructions/closeIntent.ts @@ -32,6 +32,7 @@ import { type WritableAccount, type WritableSignerAccount, } from "@solana/kit"; +import { findHookStatePda } from "../pdas/index.js"; import { FUND_TRANSFER_HOOK_PROGRAM_ADDRESS } from "../programs/index.js"; import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; @@ -50,8 +51,11 @@ export type CloseIntentInstruction< TAccountActor extends string | AccountMeta = string, TAccountIntent extends string | AccountMeta = string, TAccountFundRequestMap extends string | AccountMeta = string, + TAccountHookState extends string | AccountMeta = string, TAccountSystemProgram extends string | AccountMeta = "11111111111111111111111111111111", + TAccountAcpState extends string | AccountMeta = string, + TAccountPlatformTreasury extends string | AccountMeta = string, TRemainingAccounts extends readonly AccountMeta[] = [], > = Instruction & InstructionWithData & @@ -67,9 +71,18 @@ export type CloseIntentInstruction< TAccountFundRequestMap extends string ? ReadonlyAccount : TAccountFundRequestMap, + TAccountHookState extends string + ? ReadonlyAccount + : TAccountHookState, TAccountSystemProgram extends string ? ReadonlyAccount : TAccountSystemProgram, + TAccountAcpState extends string + ? ReadonlyAccount + : TAccountAcpState, + TAccountPlatformTreasury extends string + ? WritableAccount + : TAccountPlatformTreasury, ...TRemainingAccounts, ] >; @@ -108,15 +121,147 @@ export function getCloseIntentInstructionDataCodec(): FixedSizeCodec< ); } +export type CloseIntentAsyncInput< + TAccountActor extends string = string, + TAccountIntent extends string = string, + TAccountFundRequestMap extends string = string, + TAccountHookState extends string = string, + TAccountSystemProgram extends string = string, + TAccountAcpState extends string = string, + TAccountPlatformTreasury extends string = string, +> = { + /** + * The original actor who proposed the intent (provider for fund requests). + * Still required to authorize the close even though the rent now goes to + * the treasury: the intent PDA is reused in place by the next proposal, so + * letting a stranger deallocate it would force the sponsor to pay its rent + * again for no gain to anyone. + */ + actor: TransactionSigner; + /** + * The intent PDA to close; must be unsigned and non-escrow. The address + * does not encode the intent id, so `intent.id == intent_id` is asserted explicitly. + */ + intent: Address; + /** Per-job fund request intent map; prevents closing the currently active intent. */ + fundRequestMap: Address; + hookState?: Address; + systemProgram?: Address; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: Address; + /** `acp_state.platform_treasury`. */ + platformTreasury: Address; + intentId: CloseIntentInstructionDataArgs["intentId"]; +}; + +export async function getCloseIntentInstructionAsync< + TAccountActor extends string, + TAccountIntent extends string, + TAccountFundRequestMap extends string, + TAccountHookState extends string, + TAccountSystemProgram extends string, + TAccountAcpState extends string, + TAccountPlatformTreasury extends string, + TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, +>( + input: CloseIntentAsyncInput< + TAccountActor, + TAccountIntent, + TAccountFundRequestMap, + TAccountHookState, + TAccountSystemProgram, + TAccountAcpState, + TAccountPlatformTreasury + >, + config?: { programAddress?: TProgramAddress }, +): Promise< + CloseIntentInstruction< + TProgramAddress, + TAccountActor, + TAccountIntent, + TAccountFundRequestMap, + TAccountHookState, + TAccountSystemProgram, + TAccountAcpState, + TAccountPlatformTreasury + > +> { + // Program address. + const programAddress = + config?.programAddress ?? FUND_TRANSFER_HOOK_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + actor: { value: input.actor ?? null, isWritable: true }, + intent: { value: input.intent ?? null, isWritable: true }, + fundRequestMap: { value: input.fundRequestMap ?? null, isWritable: false }, + hookState: { value: input.hookState ?? null, isWritable: false }, + systemProgram: { value: input.systemProgram ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: false }, + platformTreasury: { + value: input.platformTreasury ?? null, + isWritable: true, + }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + // Resolve default values. + if (!accounts.hookState.value) { + accounts.hookState.value = await findHookStatePda(); + } + if (!accounts.systemProgram.value) { + accounts.systemProgram.value = + "11111111111111111111111111111111" as Address<"11111111111111111111111111111111">; + } + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.actor), + getAccountMeta(accounts.intent), + getAccountMeta(accounts.fundRequestMap), + getAccountMeta(accounts.hookState), + getAccountMeta(accounts.systemProgram), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.platformTreasury), + ], + data: getCloseIntentInstructionDataEncoder().encode( + args as CloseIntentInstructionDataArgs, + ), + programAddress, + } as CloseIntentInstruction< + TProgramAddress, + TAccountActor, + TAccountIntent, + TAccountFundRequestMap, + TAccountHookState, + TAccountSystemProgram, + TAccountAcpState, + TAccountPlatformTreasury + >); +} + export type CloseIntentInput< TAccountActor extends string = string, TAccountIntent extends string = string, TAccountFundRequestMap extends string = string, + TAccountHookState extends string = string, TAccountSystemProgram extends string = string, + TAccountAcpState extends string = string, + TAccountPlatformTreasury extends string = string, > = { /** - * The original actor who paid for the intent (provider for fund requests). - * Receives the reclaimed rent lamports. + * The original actor who proposed the intent (provider for fund requests). + * Still required to authorize the close even though the rent now goes to + * the treasury: the intent PDA is reused in place by the next proposal, so + * letting a stranger deallocate it would force the sponsor to pay its rent + * again for no gain to anyone. */ actor: TransactionSigner; /** @@ -126,7 +271,12 @@ export type CloseIntentInput< intent: Address; /** Per-job fund request intent map; prevents closing the currently active intent. */ fundRequestMap: Address; + hookState: Address; systemProgram?: Address; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: Address; + /** `acp_state.platform_treasury`. */ + platformTreasury: Address; intentId: CloseIntentInstructionDataArgs["intentId"]; }; @@ -134,14 +284,20 @@ export function getCloseIntentInstruction< TAccountActor extends string, TAccountIntent extends string, TAccountFundRequestMap extends string, + TAccountHookState extends string, TAccountSystemProgram extends string, + TAccountAcpState extends string, + TAccountPlatformTreasury extends string, TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, >( input: CloseIntentInput< TAccountActor, TAccountIntent, TAccountFundRequestMap, - TAccountSystemProgram + TAccountHookState, + TAccountSystemProgram, + TAccountAcpState, + TAccountPlatformTreasury >, config?: { programAddress?: TProgramAddress }, ): CloseIntentInstruction< @@ -149,7 +305,10 @@ export function getCloseIntentInstruction< TAccountActor, TAccountIntent, TAccountFundRequestMap, - TAccountSystemProgram + TAccountHookState, + TAccountSystemProgram, + TAccountAcpState, + TAccountPlatformTreasury > { // Program address. const programAddress = @@ -160,7 +319,13 @@ export function getCloseIntentInstruction< actor: { value: input.actor ?? null, isWritable: true }, intent: { value: input.intent ?? null, isWritable: true }, fundRequestMap: { value: input.fundRequestMap ?? null, isWritable: false }, + hookState: { value: input.hookState ?? null, isWritable: false }, systemProgram: { value: input.systemProgram ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: false }, + platformTreasury: { + value: input.platformTreasury ?? null, + isWritable: true, + }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -182,7 +347,10 @@ export function getCloseIntentInstruction< getAccountMeta(accounts.actor), getAccountMeta(accounts.intent), getAccountMeta(accounts.fundRequestMap), + getAccountMeta(accounts.hookState), getAccountMeta(accounts.systemProgram), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.platformTreasury), ], data: getCloseIntentInstructionDataEncoder().encode( args as CloseIntentInstructionDataArgs, @@ -193,7 +361,10 @@ export function getCloseIntentInstruction< TAccountActor, TAccountIntent, TAccountFundRequestMap, - TAccountSystemProgram + TAccountHookState, + TAccountSystemProgram, + TAccountAcpState, + TAccountPlatformTreasury >); } @@ -204,8 +375,11 @@ export type ParsedCloseIntentInstruction< programAddress: Address; accounts: { /** - * The original actor who paid for the intent (provider for fund requests). - * Receives the reclaimed rent lamports. + * The original actor who proposed the intent (provider for fund requests). + * Still required to authorize the close even though the rent now goes to + * the treasury: the intent PDA is reused in place by the next proposal, so + * letting a stranger deallocate it would force the sponsor to pay its rent + * again for no gain to anyone. */ actor: TAccountMetas[0]; /** @@ -215,7 +389,12 @@ export type ParsedCloseIntentInstruction< intent: TAccountMetas[1]; /** Per-job fund request intent map; prevents closing the currently active intent. */ fundRequestMap: TAccountMetas[2]; - systemProgram: TAccountMetas[3]; + hookState: TAccountMetas[3]; + systemProgram: TAccountMetas[4]; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: TAccountMetas[5]; + /** `acp_state.platform_treasury`. */ + platformTreasury: TAccountMetas[6]; }; data: CloseIntentInstructionData; }; @@ -228,7 +407,7 @@ export function parseCloseIntentInstruction< InstructionWithAccounts & InstructionWithData, ): ParsedCloseIntentInstruction { - if (instruction.accounts.length < 4) { + if (instruction.accounts.length < 7) { // TODO: Coded error. throw new Error("Not enough accounts"); } @@ -244,7 +423,10 @@ export function parseCloseIntentInstruction< actor: getNextAccount(), intent: getNextAccount(), fundRequestMap: getNextAccount(), + hookState: getNextAccount(), systemProgram: getNextAccount(), + acpState: getNextAccount(), + platformTreasury: getNextAccount(), }, data: getCloseIntentInstructionDataDecoder().decode(instruction.data), }; diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/initialize.ts b/src/core/solana/generated/fund-transfer-hook/instructions/initialize.ts index fc19eef..428d694 100644 --- a/src/core/solana/generated/fund-transfer-hook/instructions/initialize.ts +++ b/src/core/solana/generated/fund-transfer-hook/instructions/initialize.ts @@ -188,9 +188,9 @@ export async function getInitializeInstructionAsync< seeds: [ getBytesEncoder().encode( new Uint8Array([ - 99, 80, 179, 118, 113, 157, 172, 156, 120, 235, 172, 99, 23, 155, - 108, 227, 80, 135, 184, 199, 34, 228, 170, 203, 0, 150, 10, 8, 205, - 195, 225, 17, + 246, 71, 52, 188, 141, 72, 100, 245, 18, 10, 17, 229, 45, 152, 144, + 5, 191, 169, 137, 86, 3, 180, 252, 183, 196, 60, 27, 104, 33, 19, + 36, 51, ]), ), ], diff --git a/src/core/solana/generated/multi-hook-router/instructions/initialize.ts b/src/core/solana/generated/multi-hook-router/instructions/initialize.ts index d66839b..1ddac22 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/initialize.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/initialize.ts @@ -198,9 +198,9 @@ export async function getInitializeInstructionAsync< seeds: [ getBytesEncoder().encode( new Uint8Array([ - 39, 92, 81, 151, 240, 84, 54, 168, 218, 152, 52, 47, 154, 197, 170, - 241, 51, 103, 195, 132, 43, 55, 221, 33, 38, 56, 190, 33, 142, 233, - 87, 232, + 203, 9, 225, 30, 61, 29, 149, 241, 219, 111, 190, 59, 76, 171, 208, + 45, 19, 245, 181, 160, 87, 233, 72, 170, 14, 181, 126, 111, 15, 157, + 236, 230, ]), ), ], diff --git a/src/core/solana/generated/subscription-hook/instructions/cleanupProposedTerms.ts b/src/core/solana/generated/subscription-hook/instructions/cleanupProposedTerms.ts index a3b38c0..d660a06 100644 --- a/src/core/solana/generated/subscription-hook/instructions/cleanupProposedTerms.ts +++ b/src/core/solana/generated/subscription-hook/instructions/cleanupProposedTerms.ts @@ -52,7 +52,8 @@ export type CleanupProposedTermsInstruction< TAccountHookState extends string | AccountMeta = string, TAccountJobAccount extends string | AccountMeta = string, TAccountProposedTerms extends string | AccountMeta = string, - TAccountProvider extends string | AccountMeta = string, + TAccountAcpState extends string | AccountMeta = string, + TAccountPlatformTreasury extends string | AccountMeta = string, TRemainingAccounts extends readonly AccountMeta[] = [], > = Instruction & InstructionWithData & @@ -71,9 +72,12 @@ export type CleanupProposedTermsInstruction< TAccountProposedTerms extends string ? WritableAccount : TAccountProposedTerms, - TAccountProvider extends string - ? WritableAccount - : TAccountProvider, + TAccountAcpState extends string + ? ReadonlyAccount + : TAccountAcpState, + TAccountPlatformTreasury extends string + ? WritableAccount + : TAccountPlatformTreasury, ...TRemainingAccounts, ] >; @@ -122,7 +126,8 @@ export type CleanupProposedTermsAsyncInput< TAccountHookState extends string = string, TAccountJobAccount extends string = string, TAccountProposedTerms extends string = string, - TAccountProvider extends string = string, + TAccountAcpState extends string = string, + TAccountPlatformTreasury extends string = string, > = { /** Permissionless trigger; the only authorization is that the job has expired. */ caller: TransactionSigner; @@ -131,7 +136,9 @@ export type CleanupProposedTermsAsyncInput< jobAccount: Address; /** Manually closed after the provider check. */ proposedTerms: Address; - provider: Address; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: Address; + platformTreasury: Address; jobId: CleanupProposedTermsInstructionDataArgs["jobId"]; }; @@ -140,7 +147,8 @@ export async function getCleanupProposedTermsInstructionAsync< TAccountHookState extends string, TAccountJobAccount extends string, TAccountProposedTerms extends string, - TAccountProvider extends string, + TAccountAcpState extends string, + TAccountPlatformTreasury extends string, TProgramAddress extends Address = typeof SUBSCRIPTION_HOOK_PROGRAM_ADDRESS, >( input: CleanupProposedTermsAsyncInput< @@ -148,7 +156,8 @@ export async function getCleanupProposedTermsInstructionAsync< TAccountHookState, TAccountJobAccount, TAccountProposedTerms, - TAccountProvider + TAccountAcpState, + TAccountPlatformTreasury >, config?: { programAddress?: TProgramAddress }, ): Promise< @@ -158,7 +167,8 @@ export async function getCleanupProposedTermsInstructionAsync< TAccountHookState, TAccountJobAccount, TAccountProposedTerms, - TAccountProvider + TAccountAcpState, + TAccountPlatformTreasury > > { // Program address. @@ -171,7 +181,11 @@ export async function getCleanupProposedTermsInstructionAsync< hookState: { value: input.hookState ?? null, isWritable: false }, jobAccount: { value: input.jobAccount ?? null, isWritable: false }, proposedTerms: { value: input.proposedTerms ?? null, isWritable: true }, - provider: { value: input.provider ?? null, isWritable: true }, + acpState: { value: input.acpState ?? null, isWritable: false }, + platformTreasury: { + value: input.platformTreasury ?? null, + isWritable: true, + }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -193,7 +207,8 @@ export async function getCleanupProposedTermsInstructionAsync< getAccountMeta(accounts.hookState), getAccountMeta(accounts.jobAccount), getAccountMeta(accounts.proposedTerms), - getAccountMeta(accounts.provider), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.platformTreasury), ], data: getCleanupProposedTermsInstructionDataEncoder().encode( args as CleanupProposedTermsInstructionDataArgs, @@ -205,7 +220,8 @@ export async function getCleanupProposedTermsInstructionAsync< TAccountHookState, TAccountJobAccount, TAccountProposedTerms, - TAccountProvider + TAccountAcpState, + TAccountPlatformTreasury >); } @@ -214,7 +230,8 @@ export type CleanupProposedTermsInput< TAccountHookState extends string = string, TAccountJobAccount extends string = string, TAccountProposedTerms extends string = string, - TAccountProvider extends string = string, + TAccountAcpState extends string = string, + TAccountPlatformTreasury extends string = string, > = { /** Permissionless trigger; the only authorization is that the job has expired. */ caller: TransactionSigner; @@ -223,7 +240,9 @@ export type CleanupProposedTermsInput< jobAccount: Address; /** Manually closed after the provider check. */ proposedTerms: Address; - provider: Address; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: Address; + platformTreasury: Address; jobId: CleanupProposedTermsInstructionDataArgs["jobId"]; }; @@ -232,7 +251,8 @@ export function getCleanupProposedTermsInstruction< TAccountHookState extends string, TAccountJobAccount extends string, TAccountProposedTerms extends string, - TAccountProvider extends string, + TAccountAcpState extends string, + TAccountPlatformTreasury extends string, TProgramAddress extends Address = typeof SUBSCRIPTION_HOOK_PROGRAM_ADDRESS, >( input: CleanupProposedTermsInput< @@ -240,7 +260,8 @@ export function getCleanupProposedTermsInstruction< TAccountHookState, TAccountJobAccount, TAccountProposedTerms, - TAccountProvider + TAccountAcpState, + TAccountPlatformTreasury >, config?: { programAddress?: TProgramAddress }, ): CleanupProposedTermsInstruction< @@ -249,7 +270,8 @@ export function getCleanupProposedTermsInstruction< TAccountHookState, TAccountJobAccount, TAccountProposedTerms, - TAccountProvider + TAccountAcpState, + TAccountPlatformTreasury > { // Program address. const programAddress = @@ -261,7 +283,11 @@ export function getCleanupProposedTermsInstruction< hookState: { value: input.hookState ?? null, isWritable: false }, jobAccount: { value: input.jobAccount ?? null, isWritable: false }, proposedTerms: { value: input.proposedTerms ?? null, isWritable: true }, - provider: { value: input.provider ?? null, isWritable: true }, + acpState: { value: input.acpState ?? null, isWritable: false }, + platformTreasury: { + value: input.platformTreasury ?? null, + isWritable: true, + }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -278,7 +304,8 @@ export function getCleanupProposedTermsInstruction< getAccountMeta(accounts.hookState), getAccountMeta(accounts.jobAccount), getAccountMeta(accounts.proposedTerms), - getAccountMeta(accounts.provider), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.platformTreasury), ], data: getCleanupProposedTermsInstructionDataEncoder().encode( args as CleanupProposedTermsInstructionDataArgs, @@ -290,7 +317,8 @@ export function getCleanupProposedTermsInstruction< TAccountHookState, TAccountJobAccount, TAccountProposedTerms, - TAccountProvider + TAccountAcpState, + TAccountPlatformTreasury >); } @@ -307,7 +335,9 @@ export type ParsedCleanupProposedTermsInstruction< jobAccount: TAccountMetas[2]; /** Manually closed after the provider check. */ proposedTerms: TAccountMetas[3]; - provider: TAccountMetas[4]; + /** against `hook_state.acp_program`. Read only to source the treasury. */ + acpState: TAccountMetas[4]; + platformTreasury: TAccountMetas[5]; }; data: CleanupProposedTermsInstructionData; }; @@ -320,7 +350,7 @@ export function parseCleanupProposedTermsInstruction< InstructionWithAccounts & InstructionWithData, ): ParsedCleanupProposedTermsInstruction { - if (instruction.accounts.length < 5) { + if (instruction.accounts.length < 6) { // TODO: Coded error. throw new Error("Not enough accounts"); } @@ -337,7 +367,8 @@ export function parseCleanupProposedTermsInstruction< hookState: getNextAccount(), jobAccount: getNextAccount(), proposedTerms: getNextAccount(), - provider: getNextAccount(), + acpState: getNextAccount(), + platformTreasury: getNextAccount(), }, data: getCleanupProposedTermsInstructionDataDecoder().decode( instruction.data, diff --git a/src/core/solana/generated/subscription-hook/instructions/initialize.ts b/src/core/solana/generated/subscription-hook/instructions/initialize.ts index 52e21ee..3aaacd9 100644 --- a/src/core/solana/generated/subscription-hook/instructions/initialize.ts +++ b/src/core/solana/generated/subscription-hook/instructions/initialize.ts @@ -195,9 +195,9 @@ export async function getInitializeInstructionAsync< seeds: [ getBytesEncoder().encode( new Uint8Array([ - 20, 0, 166, 80, 93, 113, 128, 167, 240, 179, 33, 165, 80, 28, 221, - 130, 51, 63, 58, 69, 102, 183, 31, 211, 194, 2, 150, 90, 229, 86, - 114, 131, + 82, 35, 22, 92, 78, 34, 36, 77, 223, 4, 88, 68, 137, 148, 42, 102, + 63, 194, 165, 181, 196, 243, 93, 127, 58, 161, 228, 131, 1, 90, 114, + 14, ]), ), ], diff --git a/src/core/solana/generated/subscription-hook/types/proposedTermsCleanedUp.ts b/src/core/solana/generated/subscription-hook/types/proposedTermsCleanedUp.ts index ecd8d91..a7723ce 100644 --- a/src/core/solana/generated/subscription-hook/types/proposedTermsCleanedUp.ts +++ b/src/core/solana/generated/subscription-hook/types/proposedTermsCleanedUp.ts @@ -22,14 +22,26 @@ import { export type ProposedTermsCleanedUp = { jobId: bigint; + /** + * Provider recorded on the terms. Informational only — it is no longer the + * rent destination; see `rent_destination`. + */ provider: Address; lamportsRefunded: bigint; + /** Where the reclaimed rent actually went (the platform treasury). */ + rentDestination: Address; }; export type ProposedTermsCleanedUpArgs = { jobId: number | bigint; + /** + * Provider recorded on the terms. Informational only — it is no longer the + * rent destination; see `rent_destination`. + */ provider: Address; lamportsRefunded: number | bigint; + /** Where the reclaimed rent actually went (the platform treasury). */ + rentDestination: Address; }; export function getProposedTermsCleanedUpEncoder(): FixedSizeEncoder { @@ -37,6 +49,7 @@ export function getProposedTermsCleanedUpEncoder(): FixedSizeEncoder & - AccountSignerMeta + ? WritableAccount : TAccountPayer, TAccountWriterRegistry extends string ? ReadonlyAccount @@ -147,7 +145,28 @@ export type ActivateSubscriptionAsyncInput< TAccountSubscriptionExpiry extends string = string, TAccountSystemProgram extends string = string, > = { - payer: TransactionSigner; + /** + * The job's provider. Named, never debited: this handler does not allocate + * (see below), so nothing is spent here. It stays in the account list + * because the calling hook credits it the `proposed_terms` rent refund + * immediately after this CPI returns. + * + * Deliberately NOT `Signer`. `sub_expiry` is allocated ahead of time by + * `pre_create_sub_expiry`, so by the time activation runs there is nothing + * to fund and therefore nothing for a signature to authorize. Requiring one + * anyway forced the provider to co-sign the evaluator's `complete` — the + * only place in the protocol where two independent parties had to sign a + * single transaction. + * + * Authorization does not rest on this account: the caller binds it to the + * job's provider (subscription-hook `after_action.rs` requires + * `payer_info.key() == terms.provider`), and the provider's consent to + * these terms was already recorded — and signed for — when it funded + * `proposed_terms` at `set_budget`. + * + * refund the hook credits after this CPI returns. + */ + payer: Address; /** * The calling program must be a registered writer. * PDA seeded by the writer's program ID. Writer identity validated in handler. @@ -159,8 +178,13 @@ export type ActivateSubscriptionAsyncInput< */ writerSigner: TransactionSigner; /** - * init_if_needed is safe: the monotonic expiry check prevents backward - * reinitialization and no close path exists for this PDA. + * Must already exist — allocated by `pre_create_sub_expiry`. Untyped here + * only so its absence surfaces as `SubExpiryNotPreCreated` rather than an + * opaque Anchor deserialization failure; the handler does the typed read. + * The monotonic expiry check prevents backward reinitialization and no + * close path exists for this PDA. + * + * deserialize in the handler before any field is trusted. */ subscriptionExpiry?: Address; systemProgram?: Address; @@ -262,7 +286,28 @@ export type ActivateSubscriptionInput< TAccountSubscriptionExpiry extends string = string, TAccountSystemProgram extends string = string, > = { - payer: TransactionSigner; + /** + * The job's provider. Named, never debited: this handler does not allocate + * (see below), so nothing is spent here. It stays in the account list + * because the calling hook credits it the `proposed_terms` rent refund + * immediately after this CPI returns. + * + * Deliberately NOT `Signer`. `sub_expiry` is allocated ahead of time by + * `pre_create_sub_expiry`, so by the time activation runs there is nothing + * to fund and therefore nothing for a signature to authorize. Requiring one + * anyway forced the provider to co-sign the evaluator's `complete` — the + * only place in the protocol where two independent parties had to sign a + * single transaction. + * + * Authorization does not rest on this account: the caller binds it to the + * job's provider (subscription-hook `after_action.rs` requires + * `payer_info.key() == terms.provider`), and the provider's consent to + * these terms was already recorded — and signed for — when it funded + * `proposed_terms` at `set_budget`. + * + * refund the hook credits after this CPI returns. + */ + payer: Address; /** * The calling program must be a registered writer. * PDA seeded by the writer's program ID. Writer identity validated in handler. @@ -274,8 +319,13 @@ export type ActivateSubscriptionInput< */ writerSigner: TransactionSigner; /** - * init_if_needed is safe: the monotonic expiry check prevents backward - * reinitialization and no close path exists for this PDA. + * Must already exist — allocated by `pre_create_sub_expiry`. Untyped here + * only so its absence surfaces as `SubExpiryNotPreCreated` rather than an + * opaque Anchor deserialization failure; the handler does the typed read. + * The monotonic expiry check prevents backward reinitialization and no + * close path exists for this PDA. + * + * deserialize in the handler before any field is trusted. */ subscriptionExpiry: Address; systemProgram?: Address; @@ -367,6 +417,27 @@ export type ParsedActivateSubscriptionInstruction< > = { programAddress: Address; accounts: { + /** + * The job's provider. Named, never debited: this handler does not allocate + * (see below), so nothing is spent here. It stays in the account list + * because the calling hook credits it the `proposed_terms` rent refund + * immediately after this CPI returns. + * + * Deliberately NOT `Signer`. `sub_expiry` is allocated ahead of time by + * `pre_create_sub_expiry`, so by the time activation runs there is nothing + * to fund and therefore nothing for a signature to authorize. Requiring one + * anyway forced the provider to co-sign the evaluator's `complete` — the + * only place in the protocol where two independent parties had to sign a + * single transaction. + * + * Authorization does not rest on this account: the caller binds it to the + * job's provider (subscription-hook `after_action.rs` requires + * `payer_info.key() == terms.provider`), and the provider's consent to + * these terms was already recorded — and signed for — when it funded + * `proposed_terms` at `set_budget`. + * + * refund the hook credits after this CPI returns. + */ payer: TAccountMetas[0]; /** * The calling program must be a registered writer. @@ -379,8 +450,13 @@ export type ParsedActivateSubscriptionInstruction< */ writerSigner: TAccountMetas[2]; /** - * init_if_needed is safe: the monotonic expiry check prevents backward - * reinitialization and no close path exists for this PDA. + * Must already exist — allocated by `pre_create_sub_expiry`. Untyped here + * only so its absence surfaces as `SubExpiryNotPreCreated` rather than an + * opaque Anchor deserialization failure; the handler does the typed read. + * The monotonic expiry check prevents backward reinitialization and no + * close path exists for this PDA. + * + * deserialize in the handler before any field is trusted. */ subscriptionExpiry: TAccountMetas[3]; systemProgram: TAccountMetas[4]; diff --git a/src/core/solana/idl/fund_transfer_hook.json b/src/core/solana/idl/fund_transfer_hook.json index 8109869..ca59bf5 100644 --- a/src/core/solana/idl/fund_transfer_hook.json +++ b/src/core/solana/idl/fund_transfer_hook.json @@ -401,6 +401,19 @@ { "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "name": "token_program" + }, + { + "docs": [ + "against `hook_state.acp_program`. Read only to source the treasury." + ], + "name": "acp_state" + }, + { + "docs": [ + "equal `acp_state.platform_treasury`." + ], + "name": "platform_treasury", + "writable": true } ], "args": [ @@ -428,8 +441,11 @@ "accounts": [ { "docs": [ - "The original actor who paid for the intent (provider for fund requests).", - "Receives the reclaimed rent lamports." + "The original actor who proposed the intent (provider for fund requests).", + "Still required to authorize the close even though the rent now goes to", + "the treasury: the intent PDA is reused in place by the next proposal, so", + "letting a stranger deallocate it would force the sponsor to pay its rent", + "again for no gain to anyone." ], "name": "actor", "signer": true, @@ -485,9 +501,44 @@ ] } }, + { + "name": "hook_state", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 104, + 111, + 111, + 107, + 95, + 115, + 116, + 97, + 116, + 101 + ] + } + ] + } + }, { "address": "11111111111111111111111111111111", "name": "system_program" + }, + { + "docs": [ + "against `hook_state.acp_program`. Read only to source the treasury." + ], + "name": "acp_state" + }, + { + "docs": [ + "`acp_state.platform_treasury`." + ], + "name": "platform_treasury", + "writable": true } ], "args": [ @@ -614,38 +665,38 @@ { "kind": "const", "value": [ - 99, - 80, - 179, - 118, - 113, - 157, - 172, - 156, - 120, - 235, - 172, - 99, - 23, - 155, - 108, - 227, - 80, - 135, - 184, - 199, - 34, - 228, - 170, - 203, - 0, - 150, + 246, + 71, + 52, + 188, + 141, + 72, + 100, + 245, + 18, 10, - 8, - 205, - 195, - 225, - 17 + 17, + 229, + 45, + 152, + 144, + 5, + 191, + 169, + 137, + 86, + 3, + 180, + 252, + 183, + 196, + 60, + 27, + 104, + 33, + 19, + 36, + 51 ] } ] diff --git a/src/core/solana/idl/multi_hook_router.json b/src/core/solana/idl/multi_hook_router.json index 2658f6a..bf293d8 100644 --- a/src/core/solana/idl/multi_hook_router.json +++ b/src/core/solana/idl/multi_hook_router.json @@ -871,38 +871,38 @@ { "kind": "const", "value": [ - 39, - 92, - 81, - 151, - 240, - 84, - 54, - 168, - 218, - 152, - 52, - 47, - 154, - 197, - 170, + 203, + 9, + 225, + 30, + 61, + 29, + 149, 241, - 51, - 103, - 195, - 132, - 43, - 55, - 221, - 33, - 38, - 56, + 219, + 111, 190, - 33, - 142, - 233, + 59, + 76, + 171, + 208, + 45, + 19, + 245, + 181, + 160, 87, - 232 + 233, + 72, + 170, + 14, + 181, + 126, + 111, + 15, + 157, + 236, + 230 ] } ] diff --git a/src/core/solana/idl/subscription_hook.json b/src/core/solana/idl/subscription_hook.json index 0cf0059..006ba0e 100644 --- a/src/core/solana/idl/subscription_hook.json +++ b/src/core/solana/idl/subscription_hook.json @@ -252,7 +252,13 @@ "writable": true }, { - "name": "provider", + "docs": [ + "against `hook_state.acp_program`. Read only to source the treasury." + ], + "name": "acp_state" + }, + { + "name": "platform_treasury", "writable": true } ], @@ -381,38 +387,38 @@ { "kind": "const", "value": [ - 20, - 0, - 166, - 80, - 93, - 113, - 128, - 167, - 240, - 179, - 33, - 165, - 80, - 28, - 221, - 130, - 51, - 63, - 58, - 69, + 82, + 35, + 22, + 92, + 78, + 34, + 36, + 77, + 223, + 4, + 88, + 68, + 137, + 148, + 42, 102, - 183, - 31, - 211, + 63, 194, - 2, - 150, + 165, + 181, + 196, + 243, + 93, + 127, + 58, + 161, + 228, + 131, + 1, 90, - 229, - 86, 114, - 131 + 14 ] } ] @@ -585,12 +591,23 @@ "type": "u64" }, { + "docs": [ + "Provider recorded on the terms. Informational only — it is no longer the", + "rent destination; see `rent_destination`." + ], "name": "provider", "type": "pubkey" }, { "name": "lamports_refunded", "type": "u64" + }, + { + "docs": [ + "Where the reclaimed rent actually went (the platform treasury)." + ], + "name": "rent_destination", + "type": "pubkey" } ], "kind": "struct" diff --git a/src/core/solana/idl/subscription_state.json b/src/core/solana/idl/subscription_state.json index 39fc628..b0189a3 100644 --- a/src/core/solana/idl/subscription_state.json +++ b/src/core/solana/idl/subscription_state.json @@ -65,6 +65,11 @@ "code": 6004, "msg": "Writer has been deactivated", "name": "WriterDeactivated" + }, + { + "code": 6005, + "msg": "sub_expiry must be pre-created; the provider calls pre_create_sub_expiry", + "name": "SubExpiryNotPreCreated" } ], "events": [ @@ -186,8 +191,28 @@ { "accounts": [ { + "docs": [ + "The job's provider. Named, never debited: this handler does not allocate", + "(see below), so nothing is spent here. It stays in the account list", + "because the calling hook credits it the `proposed_terms` rent refund", + "immediately after this CPI returns.", + "", + "Deliberately NOT `Signer`. `sub_expiry` is allocated ahead of time by", + "`pre_create_sub_expiry`, so by the time activation runs there is nothing", + "to fund and therefore nothing for a signature to authorize. Requiring one", + "anyway forced the provider to co-sign the evaluator's `complete` — the", + "only place in the protocol where two independent parties had to sign a", + "single transaction.", + "", + "Authorization does not rest on this account: the caller binds it to the", + "job's provider (subscription-hook `after_action.rs` requires", + "`payer_info.key() == terms.provider`), and the provider's consent to", + "these terms was already recorded — and signed for — when it funded", + "`proposed_terms` at `set_budget`.", + "", + "refund the hook credits after this CPI returns." + ], "name": "payer", - "signer": true, "writable": true }, { @@ -227,8 +252,13 @@ }, { "docs": [ - "init_if_needed is safe: the monotonic expiry check prevents backward", - "reinitialization and no close path exists for this PDA." + "Must already exist — allocated by `pre_create_sub_expiry`. Untyped here", + "only so its absence surfaces as `SubExpiryNotPreCreated` rather than an", + "opaque Anchor deserialization failure; the handler does the typed read.", + "The monotonic expiry check prevents backward reinitialization and no", + "close path exists for this PDA.", + "", + "deserialize in the handler before any field is trusted." ], "name": "subscription_expiry", "pda": { diff --git a/src/core/solana/jobStateRetryGuard.ts b/src/core/solana/jobStateRetryGuard.ts index 5113816..c670d1e 100644 --- a/src/core/solana/jobStateRetryGuard.ts +++ b/src/core/solana/jobStateRetryGuard.ts @@ -136,7 +136,7 @@ const ROUTER_STATE_GATES: StateGate[] = [ // abandoned job's ProposedTerms PDA requires the job to have reached Expired // (cleanup_proposed_terms.rs:47 — state only, no clock check), which happens // on claim_refund. Its job account is at index 2: -// caller, hook_state, job_account, proposed_terms, provider. +// caller, hook_state, job_account, proposed_terms, acp_state, platform_treasury. // No expiry gate: Expired IS the terminal state the instruction wants, so // job.expiredAt being in the past is the precondition, not a disqualifier. const SUB_HOOK_STATE_GATES: StateGate[] = [ From b72bce48509a515f1984fc1774f0c58d4db3695a Mon Sep 17 00:00:00 2001 From: ariessa Date: Tue, 25 Aug 2026 02:22:58 +0800 Subject: [PATCH 2/3] chore(solana): resync IDLs and regenerate clients --- src/acpAgent.ts | 6 +- src/clients/solanaAcpClient.ts | 608 +-- .../solana/generated/acp/accounts/acpState.ts | 38 +- src/core/solana/generated/acp/accounts/job.ts | 68 +- .../generated/acp/errors/agenticCommerceV3.ts | 46 +- .../generated/acp/instructions/claimRefund.ts | 66 +- .../generated/acp/instructions/complete.ts | 66 +- .../generated/acp/instructions/createJob.ts | 31 +- .../solana/generated/acp/instructions/fund.ts | 37 +- .../generated/acp/instructions/index.ts | 2 + .../generated/acp/instructions/initialize.ts | 12 +- .../acp/instructions/migrateState.ts | 31 +- .../generated/acp/instructions/reject.ts | 66 +- .../generated/acp/instructions/setBudget.ts | 30 +- .../acp/instructions/setPaymentToken.ts | 282 ++ .../generated/acp/instructions/setSponsor.ts | 244 ++ .../generated/acp/instructions/submit.ts | 66 +- .../solana/generated/acp/pdas/acpState.ts | 4 +- .../generated/acp/pdas/hookWhitelist.ts | 4 +- src/core/solana/generated/acp/pdas/index.ts | 2 + src/core/solana/generated/acp/pdas/job.ts | 38 + src/core/solana/generated/acp/pdas/vault.ts | 35 + .../acp/programs/agenticCommerceV3.ts | 55 +- .../solana/generated/acp/types/budgetSet.ts | 11 +- .../generated/acp/types/evaluatorFeePaid.ts | 8 +- .../generated/acp/types/hookDetached.ts | 10 +- src/core/solana/generated/acp/types/index.ts | 2 + .../generated/acp/types/jobCompleted.ts | 14 +- .../solana/generated/acp/types/jobCreated.ts | 35 +- .../solana/generated/acp/types/jobExpired.ts | 13 +- .../solana/generated/acp/types/jobFunded.ts | 8 +- .../solana/generated/acp/types/jobRejected.ts | 14 +- .../generated/acp/types/jobSubmitted.ts | 14 +- .../generated/acp/types/paymentReleased.ts | 8 +- .../acp/types/paymentTokenUpdated.ts | 47 + .../solana/generated/acp/types/providerSet.ts | 10 +- .../solana/generated/acp/types/refunded.ts | 8 +- .../generated/acp/types/sponsorUpdated.ts | 38 + .../accounts/fundRequestIntentId.ts | 171 - .../fund-transfer-hook/accounts/index.ts | 1 - .../fund-transfer-hook/accounts/intent.ts | 10 +- .../accounts/providerEscrowIntentId.ts | 12 +- .../instructions/afterAction.ts | 14 +- .../instructions/beforeAction.ts | 14 +- .../instructions/claimEscrowRefund.ts | 76 +- .../instructions/closeIntent.ts | 433 --- .../instructions/closeJobHookAccounts.ts | 496 +++ .../fund-transfer-hook/instructions/index.ts | 2 +- .../instructions/preCreateIntent.ts | 16 +- .../pdas/escrowAuthority.ts | 10 +- .../fund-transfer-hook/pdas/hookMetadata.ts | 4 +- .../fund-transfer-hook/pdas/hookState.ts | 4 +- .../pdas/providerEscrowIntentId.ts | 10 +- .../programs/fundTransferHook.ts | 39 +- .../fund-transfer-hook/types/index.ts | 1 + .../types/jobHookAccountsClosed.ts | 59 + .../fund-transfer-hook/types/newIntent.ts | 8 +- .../types/payableFundsEscrowed.ts | 8 +- .../types/payableFundsRefunded.ts | 8 +- .../types/payableTransferExecuted.ts | 8 +- .../multi-hook-router/accounts/hookRouter.ts | 10 +- .../errors/multiHookRouter.ts | 16 + .../multi-hook-router/instructions/addHook.ts | 83 +- .../instructions/afterAction.ts | 16 +- .../instructions/batchConfigureHooks.ts | 28 +- .../instructions/beforeAction.ts | 16 +- .../instructions/closeHookRouter.ts | 383 ++ .../instructions/configureHooks.ts | 37 +- .../multi-hook-router/instructions/index.ts | 1 + .../instructions/removeHook.ts | 16 +- .../instructions/reorderHooks.ts | 16 +- .../multi-hook-router/pdas/hookMetadata.ts | 4 +- .../multi-hook-router/pdas/hookRouter.ts | 10 +- .../multi-hook-router/pdas/routerState.ts | 4 +- .../programs/multiHookRouter.ts | 31 +- .../types/dewhitelistedHookSkipped.ts | 14 +- .../multi-hook-router/types/hookAdded.ts | 15 +- .../multi-hook-router/types/hookRemoved.ts | 14 +- .../types/hookRouterClosed.ts | 59 + .../types/hooksConfigured.ts | 14 +- .../multi-hook-router/types/hooksReordered.ts | 14 +- .../multi-hook-router/types/index.ts | 1 + .../instructions/afterAction.ts | 14 +- .../instructions/beforeAction.ts | 14 +- .../instructions/cleanupProposedTerms.ts | 74 +- .../instructions/preCreateProposedTerms.ts | 18 +- .../subscription-hook/pdas/hookMetadata.ts | 4 +- .../subscription-hook/pdas/hookState.ts | 4 +- .../programs/subscriptionHook.ts | 7 +- .../types/proposedTermsCleanedUp.ts | 8 +- .../types/subscriptionActivated.ts | 8 +- .../types/subscriptionTermsProposed.ts | 11 +- .../types/subscriptionTermsSkipped.ts | 11 +- .../subscription-state/pdas/stateConfig.ts | 4 +- .../pdas/subscriptionExpiry.ts | 4 +- .../subscription-state/pdas/writerRegistry.ts | 4 +- .../programs/subscriptionState.ts | 7 +- src/core/solana/idl/agentic_commerce_v3.json | 3305 +++++++++-------- src/core/solana/idl/fund_transfer_hook.json | 855 +++-- src/core/solana/idl/multi_hook_router.json | 1266 ++++--- src/core/solana/idl/subscription_hook.json | 510 +-- src/core/solana/idl/subscription_state.json | 748 ++-- src/core/solana/multiHook.ts | 132 +- src/core/solana/preCreate.ts | 13 +- src/core/solana/routerLayout.ts | 101 +- 105 files changed, 6707 insertions(+), 4698 deletions(-) create mode 100644 src/core/solana/generated/acp/instructions/setPaymentToken.ts create mode 100644 src/core/solana/generated/acp/instructions/setSponsor.ts create mode 100644 src/core/solana/generated/acp/pdas/job.ts create mode 100644 src/core/solana/generated/acp/pdas/vault.ts create mode 100644 src/core/solana/generated/acp/types/paymentTokenUpdated.ts create mode 100644 src/core/solana/generated/acp/types/sponsorUpdated.ts delete mode 100644 src/core/solana/generated/fund-transfer-hook/accounts/fundRequestIntentId.ts delete mode 100644 src/core/solana/generated/fund-transfer-hook/instructions/closeIntent.ts create mode 100644 src/core/solana/generated/fund-transfer-hook/instructions/closeJobHookAccounts.ts create mode 100644 src/core/solana/generated/fund-transfer-hook/types/jobHookAccountsClosed.ts create mode 100644 src/core/solana/generated/multi-hook-router/instructions/closeHookRouter.ts create mode 100644 src/core/solana/generated/multi-hook-router/types/hookRouterClosed.ts diff --git a/src/acpAgent.ts b/src/acpAgent.ts index 2481a0d..08d0ee2 100644 --- a/src/acpAgent.ts +++ b/src/acpAgent.ts @@ -966,10 +966,11 @@ export class AcpAgent { chainId, "SubscriptionHook", ); + const jobPda = await acpClient.resolveJobPda(chainId, jobId); const terms = await fetchProposedTerms( acpClient.getProvider().getRpc(chainId), subHook as SolanaAddress, - jobId, + jobPda, ACP_COMMITMENT, ); // Absent PDA mirrors the EVM zero-struct read for "nothing proposed". @@ -1381,7 +1382,8 @@ export class AcpAgent { chainId, "MultiHookRouter", ); - const pda = await hookRouterPda(routerAddress as SolanaAddress, jobId); + const jobPda = await client.resolveJobPda(chainId, jobId); + const pda = await hookRouterPda(routerAddress as SolanaAddress, jobPda); const maybe = await fetchMaybeHookRouter( client.getProvider().getRpc(chainId), pda, diff --git a/src/clients/solanaAcpClient.ts b/src/clients/solanaAcpClient.ts index bd974a0..3061448 100644 --- a/src/clients/solanaAcpClient.ts +++ b/src/clients/solanaAcpClient.ts @@ -3,7 +3,9 @@ import { type Signature, AccountRole, getProgramDerivedAddress, + getAddressDecoder, getAddressEncoder, + getU64Decoder, getU64Encoder, getUtf8Encoder, } from "@solana/kit"; @@ -22,6 +24,7 @@ import { encodeSubParams, decodeSubParams, fetchProposedTerms, + fetchMaybeFundRequestIntentId, hookRouterPda, routerStatePda, subExpiryPda, @@ -104,7 +107,6 @@ import { getCompleteInstructionAsync } from "../core/solana/generated/acp/instru import { getRejectInstructionAsync } from "../core/solana/generated/acp/instructions/reject.js"; import { getBatchConfigureHooksInstructionAsync } from "../core/solana/generated/multi-hook-router/instructions/batchConfigureHooks.js"; import { getJobCreatedDecoder } from "../core/solana/generated/acp/types/jobCreated.js"; -import { fetchMaybeFundRequestIntentId } from "../core/solana/generated/fund-transfer-hook/accounts/fundRequestIntentId.js"; import { fetchMaybeProviderEscrowIntentId } from "../core/solana/generated/fund-transfer-hook/accounts/providerEscrowIntentId.js"; import { fetchIntent } from "../core/solana/generated/fund-transfer-hook/accounts/intent.js"; @@ -119,19 +121,20 @@ const EMPTY_OPT_PARAMS = new Uint8Array(0); // No generated errors module exists for the hook, hence the local constant. const HOOK_INVALID_JOB_CODE = 6000; -// Anchor framework ConstraintSeeds (2006). On CreateJob this is the job-counter -// race: the SDK derives the job PDA from acp_state.job_counter + 1 read at -// prepare time, while the program re-derives from its live counter — a -// back-to-back createJob can advance the counter in between. The code is -// framework-generic (every Anchor account constraint emits it), so a 2006 is -// treated as stale ONLY when the logs also carry the CreateJob marker. +// Anchor framework ConstraintSeeds (2006). On CreateJob this means the job PDA +// the SDK derived is already taken — a collision on the client-chosen random +// u64 seed. Retrying re-runs createJob, which draws a fresh seed, so the retry +// is the correct recovery. The code is framework-generic (every Anchor account +// constraint emits it), so a 2006 is treated as stale ONLY when the logs also +// carry the CreateJob marker. const ACP_CONSTRAINT_SEEDS_CODE = 2006; -// Marker strings (lowercased) identifying the createJob counter race in +// Marker strings (lowercased) identifying a CreateJob seed collision in // sponsor-simulation error text. All three must be present: the sponsor prefix // scopes it to a simulation failure (tx never broadcast, retry is safe), the // instruction marker scopes it to CreateJob (other instructions derive the job -// PDA from the job_id param — a 2006 there is a genuine bug, never retried). +// PDA from a known client+seed pair — a 2006 there is a genuine bug, never +// retried). const CREATE_JOB_SEEDS_RACE_MARKERS = [ "alchemy_requestfeepayer failed", "instruction: createjob", @@ -180,8 +183,9 @@ const DEFAULT_PUBKEY = SOLANA_NO_EVALUATOR_ADDRESS as Address; export class SolanaAcpClient extends BaseAcpClient { private readonly provider: ISolanaProviderAdapter; - // Job PDAs are per-cluster: the same job id exists independently on devnet - // and mainnet, so cache entries are keyed `${chainId}:${jobId}`. + // Job PDAs are per-cluster (the same job id exists independently on devnet + // and mainnet) AND per-client (jobId is a caller-chosen seed, unique only + // within one client, not globally) — see jobPdaCacheKey. private jobPdaCache: Map = new Map(); private constructor( @@ -286,10 +290,10 @@ export class SolanaAcpClient extends BaseAcpClient { chainId: number, err: unknown ): Promise { - // Sponsor-simulation form of the createJob counter race: a plain Error + // Sponsor-simulation form of a CreateJob seed collision: a plain Error // whose text (cause chain included) carries the requestFeePayer prefix // plus the CreateJob + ConstraintSeeds markers. Never broadcast, so a - // re-prepare cannot double-create. + // re-prepare cannot double-create; it also draws a fresh seed. const text = collectErrorText(err).toLowerCase(); if (CREATE_JOB_SEEDS_RACE_MARKERS.every((m) => text.includes(m))) { return true; @@ -378,16 +382,22 @@ export class SolanaAcpClient extends BaseAcpClient { chainId: number, params: CreateJobParams ): Promise { - const rpc = this.provider.getRpc(chainId); const signer = this.provider.getSigner(); - const acpStatePda = await this.deriveAcpStatePda(chainId); - const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT }); - // On-chain job_counter stores the LAST issued ID (EVM jobCounter - // parity); the job we are about to create receives counter+1. - const jobCounter = acpState.data.jobCounter + 1n; + // The job's identity is its own account address, derived from + // ["job", client, seed] — `seed` is a caller-chosen u64 uniquifier, not a + // re-usable identifier, and need only be unique per client (the program's + // `init` rejects a reuse by the same client). A random value is therefore + // both sufficient and race-free: unlike the old acp_state.job_counter + 1 + // scheme, two concurrent createJob calls (even from the same client) no + // longer contend on one shared counter and cannot collide with each other + // by racing a read. + const seedBytes = new Uint8Array(8); + crypto.getRandomValues(seedBytes); + const seed = getU64Decoder().decode(seedBytes); - const jobPda = await this.deriveJobPda(chainId, signer.address, jobCounter); + const acpStatePda = await this.deriveAcpStatePda(chainId); + const jobPda = await this.deriveJobPda(chainId, signer.address, seed); let hookWhitelist: Address | undefined; if (params.hookAddress) { @@ -410,6 +420,7 @@ export class SolanaAcpClient extends BaseAcpClient { client: signer, job: jobPda, acpState: acpStatePda, + seed, provider: params.providerAddress as Address, evaluator, description: params.description, @@ -427,7 +438,7 @@ export class SolanaAcpClient extends BaseAcpClient { // The router has no hook_state PDA; it validates through its per-job // hook_router PDA, router_state, and the instructions sysvar. extraAccounts.push( - ...(await routerPrefixAccounts(routerContext(chainId), jobCounter)) + ...(await routerPrefixAccounts(routerContext(chainId), jobPda)) ); } else if (params.hookAddress) { const hookStatePda = await this.deriveHookStatePda( @@ -436,7 +447,7 @@ export class SolanaAcpClient extends BaseAcpClient { extraAccounts.push({ address: hookStatePda, role: AccountRole.READONLY }); } - this.jobPdaCache.set(`${chainId}:${jobCounter}`, jobPda); + this.jobPdaCache.set(this.jobPdaCacheKey(chainId, signer.address, seed), jobPda); return this.wrapMany(chainId, [ { @@ -456,14 +467,13 @@ export class SolanaAcpClient extends BaseAcpClient { const jobPda = await this.resolveJobPda(chainId, params.jobId, params.clientAddress); const job = await fetchJob(rpc, jobPda, { commitment: ACP_COMMITMENT }); - let mintAddress: Address; - if (job.data.budgetMint.__option === "Some") { - mintAddress = job.data.budgetMint.value; - } else { - const acpStatePda = await this.deriveAcpStatePda(chainId); - const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT }); - mintAddress = acpState.data.paymentToken; - } + // No vault exists yet at setBudget time (fund() creates it), and + // budget_mint no longer exists on Job — the payment mint is the single + // global acp_state.payment_token, checked against a caller's choice only + // at fund, not stored per-job. + const acpStatePda = await this.deriveAcpStatePda(chainId); + const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT }); + const mintAddress = acpState.data.paymentToken; const hookAddress = job.data.hookAddress.__option === "Some" @@ -484,7 +494,6 @@ export class SolanaAcpClient extends BaseAcpClient { return this.setBudgetViaRouter(chainId, params, { signer, jobPda, - jobId: job.data.jobId, clientAddress: job.data.client, providerAddress: job.data.provider, mintAddress, @@ -495,7 +504,6 @@ export class SolanaAcpClient extends BaseAcpClient { return this.setBudgetViaSubscriptionHook(chainId, params, { signer, jobPda, - jobId: job.data.jobId, clientAddress: job.data.client, providerAddress: job.data.provider, mintAddress, @@ -507,7 +515,6 @@ export class SolanaAcpClient extends BaseAcpClient { { caller: signer, job: jobPda, - budgetMint: mintAddress, acpState: await this.deriveAcpStatePda(chainId), amount: params.amount, ...(hookAddress ? { hookProgram: hookAddress } : {}), @@ -543,12 +550,12 @@ export class SolanaAcpClient extends BaseAcpClient { // renegotiation appendix (the hook overwrites the intent in place). const intentPda = await this.deriveIntentPda( hookAddress, - job.data.jobId, + jobPda, INTENT_KIND_FUND_REQUEST ); const fundRequestIntentIdPda = await this.deriveFundRequestIntentIdPda( hookAddress, - job.data.jobId + jobPda ); extraAccounts.push( @@ -591,16 +598,15 @@ export class SolanaAcpClient extends BaseAcpClient { const vaultAuthorityPda = await this.deriveVaultAuthorityPda(chainId, jobPda); - let mintAddress: Address; - if (job.data.budgetMint.__option === "Some") { - mintAddress = job.data.budgetMint.value; - } else { - const acpStatePda = await this.deriveAcpStatePda(chainId); - const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT }); - mintAddress = acpState.data.paymentToken; - } + // No vault exists yet — fund() is what creates it (program `init`, not a + // client-side ATA). budget_mint no longer exists on Job, so the mint is + // simply the single global acp_state.payment_token; fund is also where + // the allowlist check against it now happens on-chain. + const acpStatePda = await this.deriveAcpStatePda(chainId); + const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT }); + const mintAddress = acpState.data.paymentToken; - const vaultAta = await this.deriveAta(vaultAuthorityPda, mintAddress); + const vaultPda = await this.deriveVaultPda(chainId, jobPda); const clientAta = await this.deriveAta(signer.address, mintAddress); const createClientAtaIx = this.buildCreateAtaIdempotentIx( @@ -610,13 +616,6 @@ export class SolanaAcpClient extends BaseAcpClient { mintAddress ); - const createVaultAtaIx = this.buildCreateAtaIdempotentIx( - signer.address, - vaultAta, - vaultAuthorityPda, - mintAddress - ); - const hookAddress = job.data.hookAddress.__option === "Some" ? job.data.hookAddress.value @@ -628,11 +627,10 @@ export class SolanaAcpClient extends BaseAcpClient { jobPda, job, mintAddress, - vaultAta, + vaultPda, clientAta, vaultAuthorityPda, createClientAtaIx, - createVaultAtaIx, }); } if (hookAddress && isSubscriptionHook(chainId, hookAddress)) { @@ -641,11 +639,10 @@ export class SolanaAcpClient extends BaseAcpClient { jobPda, job, mintAddress, - vaultAta, + vaultPda, clientAta, vaultAuthorityPda, createClientAtaIx, - createVaultAtaIx, }); } @@ -677,7 +674,7 @@ export class SolanaAcpClient extends BaseAcpClient { "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address; const hookStatePda = await this.deriveHookStatePda(hookAddress); const fundRequestIntentIdPda = - await this.deriveFundRequestIntentIdPda(hookAddress, job.data.jobId); + await this.deriveFundRequestIntentIdPda(hookAddress, jobPda); // A hooked job may have no fund-request at all (nothing proposed, // or proposal cancelled — intent_id 0 sentinel). Even then, post_fund // still requires the fund-request map PDA at remaining[1] @@ -700,7 +697,7 @@ export class SolanaAcpClient extends BaseAcpClient { } else { const intentPda = await this.deriveIntentPda( hookAddress, - job.data.jobId, + jobPda, INTENT_KIND_FUND_REQUEST ); const intent = await fetchIntent(rpc, intentPda, { @@ -777,7 +774,7 @@ export class SolanaAcpClient extends BaseAcpClient { acpState: await this.deriveAcpStatePda(chainId), job: jobPda, clientTokenAccount: clientAta, - vault: vaultAta, + vault: vaultPda, vaultAuthority: vaultAuthorityPda, mint: mintAddress, ...(hookAddress ? { hookProgram: hookAddress } : {}), @@ -795,7 +792,6 @@ export class SolanaAcpClient extends BaseAcpClient { return this.wrapMany(chainId, [ createClientAtaIx, - createVaultAtaIx, ...hookPreIxs, { programAddress: ix.programAddress, @@ -861,14 +857,17 @@ export class SolanaAcpClient extends BaseAcpClient { const acpStatePda = await this.deriveAcpStatePda(chainId); const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT }); - let mintAddress: Address; - if (job.data.budgetMint.__option === "Some") { - mintAddress = job.data.budgetMint.value; - } else { - mintAddress = acpState.data.paymentToken; + // budget_mint no longer exists on Job; the vault is a program-derived + // account created at fund() time, so — since isFunded guarantees fund() + // already ran — its own mint is authoritative and always fetchable here. + const vaultPda = await this.deriveVaultPda(chainId, jobPda); + const mintAddress = await this.fetchVaultMint(chainId, vaultPda); + if (!mintAddress) { + throw new Error( + `submit: vault ${vaultPda} not found for a funded job (budget > 0); ` + + `fund() should have created it.` + ); } - - const vaultAta = await this.deriveAta(vaultAuthorityPda, mintAddress); const providerAta = await this.deriveAta(signer.address, mintAddress); const treasuryAta = await this.deriveAta( acpState.data.platformTreasury, @@ -891,11 +890,13 @@ export class SolanaAcpClient extends BaseAcpClient { ); vaultAccounts = { - vault: vaultAta, + vault: vaultPda, vaultAuthority: vaultAuthorityPda, providerTokenAccount: providerAta, treasuryTokenAccount: treasuryAta, - platformTreasury: acpState.data.platformTreasury, + // Native rent destination on close; distinct from platformTreasury, + // which only ever receives the SPL fee via treasuryTokenAccount. + sponsor: acpState.data.sponsor, }; if (hookAddress) { @@ -931,17 +932,17 @@ export class SolanaAcpClient extends BaseAcpClient { // concurrent intent-creating transactions on the same hook. const escrowIntentPda = await this.deriveIntentPda( hookAddress, - job.data.jobId, + jobPda, INTENT_KIND_ESCROW ); const provEscrowIntentIdPda = await this.deriveProviderEscrowIntentIdPda( hookAddress, - job.data.jobId + jobPda ); const escrowAuthorityPda = await this.deriveEscrowAuthorityPda( hookAddress, - job.data.jobId + jobPda ); const escrowVault = await this.deriveAta( escrowAuthorityPda, @@ -1175,14 +1176,14 @@ export class SolanaAcpClient extends BaseAcpClient { const acpStatePda = await this.deriveAcpStatePda(chainId); const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT }); - let mintAddress: Address; - if (job.data.budgetMint.__option === "Some") { - mintAddress = job.data.budgetMint.value; - } else { - mintAddress = acpState.data.paymentToken; - } - - const vaultAta = await this.deriveAta(vaultAuthorityPda, mintAddress); + // complete() runs on zero-budget jobs too, where no vault was ever + // created — fetchVaultMint then returns null and acp_state.payment_token + // (the same fallback budgetMint.None used) stands in. It is never actually + // read from for a zero-budget job; the derived accounts below just need a + // valid mint to compute ATAs against. + const vaultPda = await this.deriveVaultPda(chainId, jobPda); + const mintAddress = + (await this.fetchVaultMint(chainId, vaultPda)) ?? acpState.data.paymentToken; const providerAta = await this.deriveAta(job.data.provider, mintAddress); const treasuryAta = await this.deriveAta( acpState.data.platformTreasury, @@ -1234,7 +1235,7 @@ export class SolanaAcpClient extends BaseAcpClient { const provEscrowIntentIdPda = await this.deriveProviderEscrowIntentIdPda( hookAddress, - job.data.jobId + jobPda ); // The escrow-map PDA is mandatory at the hook's remaining[1] even with // no escrow: auto_sign_escrow fails IncompleteHookAccountSet (6019) on @@ -1258,7 +1259,7 @@ export class SolanaAcpClient extends BaseAcpClient { if (maybePeii.exists && maybePeii.data.intentId !== 0n) { const escrowIntentPda = await this.deriveIntentPda( hookAddress, - job.data.jobId, + jobPda, INTENT_KIND_ESCROW ); const intent = await fetchIntent(rpc, escrowIntentPda, { @@ -1266,7 +1267,7 @@ export class SolanaAcpClient extends BaseAcpClient { }); const escrowAuthorityPda = await this.deriveEscrowAuthorityPda( hookAddress, - job.data.jobId + jobPda ); const escrowVault = await this.deriveAta( escrowAuthorityPda, @@ -1301,12 +1302,12 @@ export class SolanaAcpClient extends BaseAcpClient { evaluator: signer, job: jobPda, acpState: acpStatePda, - vault: vaultAta, + vault: vaultPda, vaultAuthority: vaultAuthorityPda, providerTokenAccount: providerAta, treasuryTokenAccount: treasuryAta, ...(evaluatorAta ? { evaluatorTokenAccount: evaluatorAta } : {}), - platformTreasury: acpState.data.platformTreasury, + sponsor: acpState.data.sponsor, ...(hookAddress ? { hookProgram: hookAddress } : {}), ...(hookAddress ? { hookWhitelist: await this.deriveHookWhitelistPda(chainId, hookAddress) } @@ -1355,13 +1356,14 @@ export class SolanaAcpClient extends BaseAcpClient { if (isFunded && job.data.budgetAmount > 0n) { vaultAuthority = await this.deriveVaultAuthorityPda(chainId, jobPda); - let mintAddress: Address; - if (job.data.budgetMint.__option === "Some") { - mintAddress = job.data.budgetMint.value; - } else { - mintAddress = acpState.data.paymentToken; + vault = await this.deriveVaultPda(chainId, jobPda); + const mintAddress = await this.fetchVaultMint(chainId, vault); + if (!mintAddress) { + throw new Error( + `reject: vault ${vault} not found for a funded job (budget > 0); ` + + `fund() should have created it.` + ); } - vault = await this.deriveAta(vaultAuthority, mintAddress); clientTokenAccount = await this.deriveAta(job.data.client, mintAddress); } @@ -1379,7 +1381,7 @@ export class SolanaAcpClient extends BaseAcpClient { vault, vaultAuthority, clientTokenAccount, - platformTreasury: acpState.data.platformTreasury, + sponsor: acpState.data.sponsor, }); } if (hookAddress && isSubscriptionHook(chainId, hookAddress)) { @@ -1391,7 +1393,7 @@ export class SolanaAcpClient extends BaseAcpClient { vault, vaultAuthority, clientTokenAccount, - platformTreasury: acpState.data.platformTreasury, + sponsor: acpState.data.sponsor, }); } @@ -1406,7 +1408,7 @@ export class SolanaAcpClient extends BaseAcpClient { const provEscrowIntentIdPda = await this.deriveProviderEscrowIntentIdPda( hookAddress, - job.data.jobId + jobPda ); // The escrow-map PDA is mandatory at the hook's remaining[1] even with // no escrow: auto_sign_escrow fails IncompleteHookAccountSet (6019) on @@ -1430,7 +1432,7 @@ export class SolanaAcpClient extends BaseAcpClient { if (maybePeii.exists && maybePeii.data.intentId !== 0n) { const escrowIntentPda = await this.deriveIntentPda( hookAddress, - job.data.jobId, + jobPda, INTENT_KIND_ESCROW ); const intent = await fetchIntent(rpc, escrowIntentPda, { @@ -1438,7 +1440,7 @@ export class SolanaAcpClient extends BaseAcpClient { }); const escrowAuthorityPda = await this.deriveEscrowAuthorityPda( hookAddress, - job.data.jobId + jobPda ); const escrowVault = await this.deriveAta( escrowAuthorityPda, @@ -1467,7 +1469,7 @@ export class SolanaAcpClient extends BaseAcpClient { ...(vault ? { vault } : {}), ...(vaultAuthority ? { vaultAuthority } : {}), ...(clientTokenAccount ? { clientTokenAccount } : {}), - platformTreasury: acpState.data.platformTreasury, + sponsor: acpState.data.sponsor, ...(hookAddress ? { hookProgram: hookAddress } : {}), ...(hookAddress ? { hookWhitelist: await this.deriveHookWhitelistPda(chainId, hookAddress) } @@ -1489,40 +1491,66 @@ export class SolanaAcpClient extends BaseAcpClient { ]); } - override async getJobIdFromTxHash( + /** + * Decodes the JobCreated event out of a confirmed transaction's logs, or + * null if the transaction is missing or carries no such event. Shared by + * getJobIdFromTxHash (needs `.job`) and getJob's description recovery + * (needs `.description`) so the log-scanning logic lives in one place. + */ + private async decodeJobCreatedFromTx( chainId: number, txHash: string - ): Promise { + ): Promise { const rpc = this.provider.getRpc(chainId); - const tx = await rpc .getTransaction(txHash as Signature, { encoding: "json", maxSupportedTransactionVersion: 0, }) .send(); - if (!tx) return null; - const logs = tx.meta?.logMessages ?? []; const decoder = getJobCreatedDecoder(); - - for (const log of logs) { + for (const log of tx.meta?.logMessages ?? []) { if (!log.startsWith("Program data: ")) continue; const data = Uint8Array.from( atob(log.slice("Program data: ".length)), (c) => c.charCodeAt(0) ); - if (data.length < 8) continue; if (!JOB_CREATED_EVENT_DISC.every((b, i) => data[i] === b)) continue; - - return decoder.decode(data.slice(8)).jobId; + return decoder.decode(data.slice(8)); } - return null; } + /** + * Recovers the chain-agnostic bigint jobId (the seed passed to createJob) + * from a confirmed createJob transaction. + * + * The job's identity is now its own account address, and JobCreated only + * carries that address (`job`), not the seed — the seed is a write-once + * input, not part of the job's on-chain identity, so the event does not + * echo it. This decodes the event to find the address, then fetches the + * job account itself and reads `seed` back off it (the account still + * stores it, purely so the PDA can be re-derived). One extra RPC round + * trip versus the old counter-value-in-the-event scheme, in exchange for + * keeping this method's external bigint contract unchanged. + */ + override async getJobIdFromTxHash( + chainId: number, + txHash: string + ): Promise { + const event = await this.decodeJobCreatedFromTx(chainId, txHash); + if (!event) return null; + + const rpc = this.provider.getRpc(chainId); + const jobAccount = await fetchJob(rpc, event.job, { + commitment: ACP_COMMITMENT, + }); + return jobAccount.data.seed; + } + /** * Signature of the transaction that created the given job, recovered from * the job PDA's history (`getSignaturesForAddress`, oldest entry). Keyed by @@ -1562,12 +1590,30 @@ export class SolanaAcpClient extends BaseAcpClient { }); const job = jobAccount.data; + // description moved to event-only (JobCreated.description) — it is no + // longer stored on the Job account at all. Recover it from the job's + // creation transaction, the only place it still exists on-chain. Best + // effort: a job created before an RPC's log-retention window, or one + // whose creation tx cannot be located, degrades to "" rather than + // failing the whole read. + let description = ""; + const createSig = await rpc + .getSignaturesForAddress(jobPda, { commitment: "confirmed" }) + .send(); + if (createSig.length > 0) { + const event = await this.decodeJobCreatedFromTx( + chainId, + createSig[createSig.length - 1]!.signature + ); + if (event) description = event.description; + } + return { - id: job.jobId, + id: job.seed, client: job.client, provider: job.provider, evaluator: job.evaluator, - description: job.description, + description, budget: job.budgetAmount, expiredAt: job.expiredAt, status: job.state as number, @@ -1667,10 +1713,10 @@ export class SolanaAcpClient extends BaseAcpClient { { client: signer, job: jobPda, - hookRouter: await hookRouterPda(ctx.router, params.jobId), + hookRouter: await hookRouterPda(ctx.router, jobPda), routerState: await routerStatePda(ctx.router), systemProgram: "11111111111111111111111111111111" as Address, - jobId: params.jobId, + jobKey: jobPda, ...lists, }, { programAddress: ctx.router } @@ -1702,22 +1748,26 @@ export class SolanaAcpClient extends BaseAcpClient { /** * Complete a subscription-activating job — a multi-hook (router) job or a * standalone subscription-hook job. EAGER — sends immediately and returns - * the signature, deviating from the prepared pattern because subscription - * activation requires the provider's co-signature (sub-state's - * ActivateSubscription declares the rent payer a Signer), and a runtime - * signer cannot ride in prepared data. + * the signature, deviating from the prepared pattern because it needs a + * runtime lookup-table/retry-guard setup that doesn't ride in prepared + * data. + * + * `providerSigner` is accepted for backward compatibility but is no longer + * required (F-118): the provider used to have to co-sign because + * ActivateSubscription's on-chain payer was declared a Signer, allocating + * sub_expiry at activation. sub_expiry is now pre-created at set_budget, so + * activation allocates nothing and the evaluator completes alone. * * Router jobs additionally need an address lookup table (the fan-out * account set exceeds the legacy tx size) — they compress against the * persistent complete ALT (MULTI_HOOK_COMPLETE_ALT_ADDRESSES). Standalone * subscription jobs fit a legacy transaction and need no table. Both kinds - * send through the sponsored multi-signer path (sponsorLookupTables), so - * fees and rents — including first-activation sub_expiry — are prefunded - * and no wallet needs SOL. + * send through the sponsored path, so fees and rents — including + * first-activation sub_expiry — are prefunded and no wallet needs SOL. */ async completeSubscriptionJob( chainId: number, - params: CompleteParams & { providerSigner: SolanaSigner } + params: CompleteParams & { providerSigner?: SolanaSigner } ): Promise { const rpc = this.provider.getRpc(chainId); const signer = this.provider.getSigner(); @@ -1742,12 +1792,15 @@ export class SolanaAcpClient extends BaseAcpClient { "submitPrepared for other jobs." ); } - if (params.providerSigner.address !== job.data.provider) { + // providerSigner is no longer required (F-118) — only validate it when a + // caller supplies one, so a stale/wrong signer isn't silently ignored. + if ( + params.providerSigner && + params.providerSigner.address !== job.data.provider + ) { throw new Error( `completeSubscriptionJob: providerSigner ${params.providerSigner.address} ` + - `is not the job's provider ${job.data.provider} — the provider must ` + - `co-sign (it pays the subscription rent and receives the ` + - `proposed_terms refund).` + `is not the job's provider ${job.data.provider}.` ); } @@ -1778,12 +1831,10 @@ export class SolanaAcpClient extends BaseAcpClient { const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT, }); - const mintAddress = - job.data.budgetMint.__option === "Some" - ? job.data.budgetMint.value - : acpState.data.paymentToken; const vaultAuthorityPda = await this.deriveVaultAuthorityPda(chainId, jobPda); - const vaultAta = await this.deriveAta(vaultAuthorityPda, mintAddress); + const vaultPda = await this.deriveVaultPda(chainId, jobPda); + const mintAddress = + (await this.fetchVaultMint(chainId, vaultPda)) ?? acpState.data.paymentToken; const providerAta = await this.deriveAta(job.data.provider, mintAddress); const treasuryAta = await this.deriveAta( acpState.data.platformTreasury, @@ -1812,7 +1863,7 @@ export class SolanaAcpClient extends BaseAcpClient { const terms = await fetchProposedTerms( rpc, ctx.subHook, - job.data.jobId, + jobPda, ACP_COMMITMENT ); @@ -1823,7 +1874,7 @@ export class SolanaAcpClient extends BaseAcpClient { } | null = null; const provEscrowIntentIdPda = await this.deriveProviderEscrowIntentIdPda( ctx.fundHook, - job.data.jobId + jobPda ); const maybePeii = await fetchMaybeProviderEscrowIntentId( rpc, @@ -1835,7 +1886,7 @@ export class SolanaAcpClient extends BaseAcpClient { if (maybePeii.exists && maybePeii.data.intentId !== 0n) { const escrowIntentPda = await this.deriveIntentPda( ctx.fundHook, - job.data.jobId, + jobPda, INTENT_KIND_ESCROW ); const intent = await fetchIntent(rpc, escrowIntentPda, { @@ -1843,7 +1894,7 @@ export class SolanaAcpClient extends BaseAcpClient { }); const escrowAuthorityPda = await this.deriveEscrowAuthorityPda( ctx.fundHook, - job.data.jobId + jobPda ); const escrowVault = await this.deriveAta( escrowAuthorityPda, @@ -1869,7 +1920,6 @@ export class SolanaAcpClient extends BaseAcpClient { } const fanOut = await buildCompleteFanOut(ctx, { - jobId: job.data.jobId, jobPda, provider: job.data.provider, clientAddress: job.data.client, @@ -1882,12 +1932,12 @@ export class SolanaAcpClient extends BaseAcpClient { evaluator: signer, job: jobPda, acpState: acpStatePda, - vault: vaultAta, + vault: vaultPda, vaultAuthority: vaultAuthorityPda, providerTokenAccount: providerAta, treasuryTokenAccount: treasuryAta, ...(evaluatorAta ? { evaluatorTokenAccount: evaluatorAta } : {}), - platformTreasury: acpState.data.platformTreasury, + sponsor: acpState.data.sponsor, hookProgram: ctx.router, hookWhitelist: await this.deriveHookWhitelistPda(chainId, ctx.router), reason: encodeReasonBytes(params.reason), @@ -1948,7 +1998,10 @@ export class SolanaAcpClient extends BaseAcpClient { hookRentPreCreated = existing.value !== null; } const result = await this.provider.sendInstructions(chainId, instructions, { - extraSigners: fanOut.requiredExtraSigner ? [params.providerSigner] : [], + extraSigners: + fanOut.requiredExtraSigner && params.providerSigner + ? [params.providerSigner] + : [], lookupTables: { [lut]: addresses }, retryGuard: guard, sponsorLookupTables: true, @@ -2039,7 +2092,6 @@ export class SolanaAcpClient extends BaseAcpClient { s: { signer: SolanaSigner; jobPda: Address; - jobId: bigint; clientAddress: Address; providerAddress: Address; mintAddress: Address; @@ -2072,7 +2124,6 @@ export class SolanaAcpClient extends BaseAcpClient { { signer: s.signer, jobPda: s.jobPda, - jobId: s.jobId, clientAddress: s.clientAddress, providerAddress: s.providerAddress, fundRequestIntent: false, @@ -2083,7 +2134,6 @@ export class SolanaAcpClient extends BaseAcpClient { ); const extraAccounts = await buildSubSetBudgetAccounts(sctx, { - jobId: s.jobId, jobPda: s.jobPda, seller: s.signer.address, clientAddress: s.clientAddress, @@ -2094,7 +2144,6 @@ export class SolanaAcpClient extends BaseAcpClient { { caller: s.signer, job: s.jobPda, - budgetMint: s.mintAddress, acpState: await this.deriveAcpStatePda(chainId), amount: params.amount, hookProgram: sctx.subHook, @@ -2125,11 +2174,10 @@ export class SolanaAcpClient extends BaseAcpClient { jobPda: Address; job: Awaited>; mintAddress: Address; - vaultAta: Address; + vaultPda: Address; clientAta: Address; vaultAuthorityPda: Address; createClientAtaIx: SolanaInstructionLike; - createVaultAtaIx: SolanaInstructionLike; } ): Promise { const sctx = subscriptionContext(chainId); @@ -2140,7 +2188,7 @@ export class SolanaAcpClient extends BaseAcpClient { const terms = await fetchProposedTerms( rpc, sctx.subHook, - s.job.data.jobId, + s.jobPda, ACP_COMMITMENT ); const optParams = terms @@ -2154,7 +2202,7 @@ export class SolanaAcpClient extends BaseAcpClient { acpState: await this.deriveAcpStatePda(chainId), job: s.jobPda, clientTokenAccount: s.clientAta, - vault: s.vaultAta, + vault: s.vaultPda, vaultAuthority: s.vaultAuthorityPda, mint: s.mintAddress, hookProgram: sctx.subHook, @@ -2167,12 +2215,11 @@ export class SolanaAcpClient extends BaseAcpClient { return this.wrapMany(chainId, [ s.createClientAtaIx, - s.createVaultAtaIx, { programAddress: ix.programAddress, accounts: [ ...ix.accounts, - ...(await buildSubFundAccounts(sctx, s.job.data.jobId)), + ...(await buildSubFundAccounts(sctx, s.jobPda)), ], data: ix.data as Uint8Array, }, @@ -2210,15 +2257,18 @@ export class SolanaAcpClient extends BaseAcpClient { const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT, }); - const mintAddress = - job.data.budgetMint.__option === "Some" - ? job.data.budgetMint.value - : acpState.data.paymentToken; const vaultAuthorityPda = await this.deriveVaultAuthorityPda( chainId, s.jobPda ); - const vaultAta = await this.deriveAta(vaultAuthorityPda, mintAddress); + const vaultPda = await this.deriveVaultPda(chainId, s.jobPda); + const mintAddress = await this.fetchVaultMint(chainId, vaultPda); + if (!mintAddress) { + throw new Error( + `submit: vault ${vaultPda} not found for a funded job (budget > 0); ` + + `fund() should have created it.` + ); + } const providerAta = await this.deriveAta(s.signer.address, mintAddress); const treasuryAta = await this.deriveAta( acpState.data.platformTreasury, @@ -2239,11 +2289,11 @@ export class SolanaAcpClient extends BaseAcpClient { ) ); vaultAccounts = { - vault: vaultAta, + vault: vaultPda, vaultAuthority: vaultAuthorityPda, providerTokenAccount: providerAta, treasuryTokenAccount: treasuryAta, - platformTreasury: acpState.data.platformTreasury, + sponsor: acpState.data.sponsor, }; } @@ -2255,7 +2305,7 @@ export class SolanaAcpClient extends BaseAcpClient { const terms = await fetchProposedTerms( rpc, sctx.subHook, - job.data.jobId, + s.jobPda, ACP_COMMITMENT ); // No-evaluator submit auto-completes: sub_expiry activation fires inside @@ -2281,7 +2331,6 @@ export class SolanaAcpClient extends BaseAcpClient { { signer: s.signer, jobPda: s.jobPda, - jobId: job.data.jobId, clientAddress: job.data.client, providerAddress: job.data.provider, fundRequestIntent: false, @@ -2293,7 +2342,6 @@ export class SolanaAcpClient extends BaseAcpClient { } } const completeSlice = await buildSubCompleteAccounts(sctx, { - jobId: job.data.jobId, jobPda: s.jobPda, provider: s.signer.address, clientAddress: job.data.client, @@ -2349,12 +2397,12 @@ export class SolanaAcpClient extends BaseAcpClient { vault: Address | undefined; vaultAuthority: Address | undefined; clientTokenAccount: Address | undefined; - platformTreasury: Address; + sponsor: Address; } ): Promise { const sctx = subscriptionContext(chainId); const extraAccounts = await buildSubRejectAccounts(sctx, { - jobId: s.job.data.jobId, + jobPda: s.jobPda, provider: s.job.data.provider, }); @@ -2368,7 +2416,7 @@ export class SolanaAcpClient extends BaseAcpClient { ...(s.clientTokenAccount ? { clientTokenAccount: s.clientTokenAccount } : {}), - platformTreasury: s.platformTreasury, + sponsor: s.sponsor, hookProgram: sctx.subHook, hookWhitelist: await this.deriveHookWhitelistPda(chainId, sctx.subHook), reason: s.reasonBytes, @@ -2391,13 +2439,12 @@ export class SolanaAcpClient extends BaseAcpClient { /** * Standalone subscription complete: same activation semantics as the * router path but no fan-out header, no lookup table (the account set fits - * a legacy transaction). Always sponsored: with terms, the provider's - * co-signature rides the sponsored multi-signer path; with no terms the - * send is a plain sponsored single-signer tx. + * a legacy transaction). Always sponsored, single-signer — post-F-118 no + * co-signature is required (see completeSubscriptionJob). */ private async completeSubscriptionStandalone( chainId: number, - params: CompleteParams & { providerSigner: SolanaSigner }, + params: CompleteParams & { providerSigner?: SolanaSigner }, s: { signer: SolanaSigner; jobPda: Address; @@ -2412,15 +2459,13 @@ export class SolanaAcpClient extends BaseAcpClient { const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT, }); - const mintAddress = - job.data.budgetMint.__option === "Some" - ? job.data.budgetMint.value - : acpState.data.paymentToken; const vaultAuthorityPda = await this.deriveVaultAuthorityPda( chainId, s.jobPda ); - const vaultAta = await this.deriveAta(vaultAuthorityPda, mintAddress); + const vaultPda = await this.deriveVaultPda(chainId, s.jobPda); + const mintAddress = + (await this.fetchVaultMint(chainId, vaultPda)) ?? acpState.data.paymentToken; const providerAta = await this.deriveAta(job.data.provider, mintAddress); const treasuryAta = await this.deriveAta( acpState.data.platformTreasury, @@ -2447,11 +2492,10 @@ export class SolanaAcpClient extends BaseAcpClient { const terms = await fetchProposedTerms( rpc, sctx.subHook, - job.data.jobId, + s.jobPda, ACP_COMMITMENT ); const completeAccounts = await buildSubCompleteAccounts(sctx, { - jobId: job.data.jobId, jobPda: s.jobPda, provider: job.data.provider, clientAddress: job.data.client, @@ -2463,12 +2507,12 @@ export class SolanaAcpClient extends BaseAcpClient { evaluator: s.signer, job: s.jobPda, acpState: acpStatePda, - vault: vaultAta, + vault: vaultPda, vaultAuthority: vaultAuthorityPda, providerTokenAccount: providerAta, treasuryTokenAccount: treasuryAta, ...(evaluatorAta ? { evaluatorTokenAccount: evaluatorAta } : {}), - platformTreasury: acpState.data.platformTreasury, + sponsor: acpState.data.sponsor, hookProgram: sctx.subHook, hookWhitelist: await this.deriveHookWhitelistPda(chainId, sctx.subHook), reason: encodeReasonBytes(params.reason), @@ -2498,16 +2542,15 @@ export class SolanaAcpClient extends BaseAcpClient { chainId, standaloneIxs, { - extraSigners: completeAccounts.requiredExtraSigner - ? [params.providerSigner] - : [], + // requiredExtraSigner is always null post-F-118 (see + // buildSubCompleteAccounts); this stays defensive rather than + // hardcoded to [] so a future signer requirement needs no call-site + // shape change. + extraSigners: + completeAccounts.requiredExtraSigner && params.providerSigner + ? [params.providerSigner] + : [], retryGuard: standaloneGuard, - // With terms, the provider co-signature would force self-pay by - // default — and on FIRST activation this tx creates the sub_expiry - // PDA, so the wallet would really pay that rent, not just a fee. - // Sponsor via the multi-signer path instead (no lookup table needed; - // Alchemy sponsors two-signer txs), so prefundRent covers the - // sub_expiry rent and the wallet stays flat. ...(completeAccounts.requiredExtraSigner ? { sponsorLookupTables: true } : {}), @@ -2524,7 +2567,6 @@ export class SolanaAcpClient extends BaseAcpClient { s: { signer: SolanaSigner; jobPda: Address; - jobId: bigint; clientAddress: Address; providerAddress: Address; mintAddress: Address; @@ -2548,7 +2590,6 @@ export class SolanaAcpClient extends BaseAcpClient { { signer: s.signer, jobPda: s.jobPda, - jobId: s.jobId, clientAddress: s.clientAddress, providerAddress: s.providerAddress, fundRequestIntent: s.fundRequestParams.length > 0, @@ -2559,7 +2600,6 @@ export class SolanaAcpClient extends BaseAcpClient { ); const fanOut = await buildSetBudgetFanOut(ctx, { - jobId: s.jobId, jobPda: s.jobPda, seller: s.signer.address, clientAddress: s.clientAddress, @@ -2577,7 +2617,6 @@ export class SolanaAcpClient extends BaseAcpClient { { caller: s.signer, job: s.jobPda, - budgetMint: s.mintAddress, acpState: await this.deriveAcpStatePda(chainId), amount: params.amount, hookProgram: ctx.router, @@ -2609,11 +2648,10 @@ export class SolanaAcpClient extends BaseAcpClient { jobPda: Address; job: Awaited>; mintAddress: Address; - vaultAta: Address; + vaultPda: Address; clientAta: Address; vaultAuthorityPda: Address; createClientAtaIx: SolanaInstructionLike; - createVaultAtaIx: SolanaInstructionLike; } ): Promise { const ctx = routerContext(chainId); @@ -2623,7 +2661,7 @@ export class SolanaAcpClient extends BaseAcpClient { const terms = await fetchProposedTerms( rpc, ctx.subHook, - job.data.jobId, + s.jobPda, ACP_COMMITMENT ); @@ -2634,20 +2672,10 @@ export class SolanaAcpClient extends BaseAcpClient { // ~1068 bytes and sponsors cleanly; the ATA-only tx sponsors on its own // (ATA program is sponsorable). Net: router fund is fully sponsored, buyer // pays no SOL. - const ataCreateIxs: SolanaInstructionLike[] = [ - s.createClientAtaIx, - s.createVaultAtaIx, - ]; + const ataCreateIxs: SolanaInstructionLike[] = [s.createClientAtaIx]; const hookPreIxs: SolanaInstructionLike[] = []; const hookPostIxs: SolanaInstructionLike[] = []; let passHookDelegate = true; - // A fund request the core's delegate approval does not cover — a - // foreign mint (upfront token ≠ payment mint) OR a same-mint request ABOVE - // the budget (R-H6's Y=2.5X) — adds an Approve/Revoke bracket, pushing - // the sponsored fund tx over 1232 bytes. Compress its static accounts - // against the persistent complete ALT at the return (same treatment as the - // bracketed submit). - let bracketed = false; let fundIntent: { token: Address; amount: bigint; @@ -2658,7 +2686,7 @@ export class SolanaAcpClient extends BaseAcpClient { const fundRequestIntentIdPda = await this.deriveFundRequestIntentIdPda( ctx.fundHook, - job.data.jobId + s.jobPda ); const maybeFriid = await fetchMaybeFundRequestIntentId( rpc, @@ -2668,7 +2696,7 @@ export class SolanaAcpClient extends BaseAcpClient { if (maybeFriid.exists && maybeFriid.data.intentId !== 0n) { const intentPda = await this.deriveIntentPda( ctx.fundHook, - job.data.jobId, + s.jobPda, INTENT_KIND_FUND_REQUEST ); const intent = await fetchIntent(rpc, intentPda, { @@ -2706,11 +2734,6 @@ export class SolanaAcpClient extends BaseAcpClient { ) ); hookPostIxs.push(this.buildRevokeIx(fromAta, s.signer.address)); - // The bracket + router fan-out overflow the sponsored fund past 1232 — - // regardless of mint — so it must ride the ALT compression at the return - // (R-H6 was the same-mint over-budget case that got the bracket but not - // the compression, and failed alchemy_requestFeePayer at ~1224 bytes). - bracketed = true; if (intent.data.token === s.mintAddress) { passHookDelegate = false; } @@ -2726,7 +2749,6 @@ export class SolanaAcpClient extends BaseAcpClient { } const fanOut = await buildFundFanOut(ctx, { - jobId: job.data.jobId, jobPda: s.jobPda, proposedTerms: terms ? { duration: terms.duration, packageId: terms.packageId } @@ -2740,7 +2762,7 @@ export class SolanaAcpClient extends BaseAcpClient { acpState: await this.deriveAcpStatePda(chainId), job: s.jobPda, clientTokenAccount: s.clientAta, - vault: s.vaultAta, + vault: s.vaultPda, vaultAuthority: s.vaultAuthorityPda, mint: s.mintAddress, hookProgram: ctx.router, @@ -2791,10 +2813,14 @@ export class SolanaAcpClient extends BaseAcpClient { prepared.sendOptions = { ...prepared.sendOptions, hookRentPreCreated: true }; const persistentAlt = MULTI_HOOK_COMPLETE_ALT_ADDRESSES[chainId]; - if (bracketed && persistentAlt) { - // Sponsored ALT compression: the ATA creates are already split out - // (above), so compressing the static accounts fits the bracketed fund - // (foreign mint or same-mint over-budget) under 1232. + if (persistentAlt) { + // Sponsored ALT compression applies to EVERY router fund, not just a + // bracketed one: the ATA creates are already split out (above), and an + // uncompressed router fund still measures close enough to 1232 bytes + // that Alchemy's own rent-prefund augmentation can push it over, which + // fails the tx with no on-chain trace (never broadcast) and leaves the + // acting wallet's ATA-create tx unsponsored. Compressing the static + // accounts reclaims headroom regardless of whether a bracket is present. prepared.sendOptions = { ...prepared.sendOptions, lookupTables: { @@ -2839,15 +2865,16 @@ export class SolanaAcpClient extends BaseAcpClient { const acpState = await fetchAcpState(rpc, acpStatePda, { commitment: ACP_COMMITMENT, }); - const mintAddress = - job.data.budgetMint.__option === "Some" - ? job.data.budgetMint.value - : acpState.data.paymentToken; const vaultAuthorityPda = await this.deriveVaultAuthorityPda( chainId, s.jobPda ); - const vaultAta = await this.deriveAta(vaultAuthorityPda, mintAddress); + const vaultPda = await this.deriveVaultPda(chainId, s.jobPda); + // A zero-budget router job (see above) never had a vault created — + // fetchVaultMint then returns null and acp_state.payment_token stands in, + // matching the old budgetMint.None fallback. + const mintAddress = + (await this.fetchVaultMint(chainId, vaultPda)) ?? acpState.data.paymentToken; const providerAta = await this.deriveAta(s.signer.address, mintAddress); const treasuryAta = await this.deriveAta( acpState.data.platformTreasury, @@ -2914,7 +2941,7 @@ export class SolanaAcpClient extends BaseAcpClient { const escrowToken = escrowProposal.token as Address; const escrowAuthorityPda = await this.deriveEscrowAuthorityPda( ctx.fundHook, - job.data.jobId + s.jobPda ); const escrowVault = await this.deriveAta(escrowAuthorityPda, escrowToken); const providerEscrowAta = await this.deriveAta( @@ -2981,7 +3008,7 @@ export class SolanaAcpClient extends BaseAcpClient { if (escrow !== null) { const escrowIntentPda = await this.deriveIntentPda( ctx.fundHook, - job.data.jobId, + s.jobPda, 1 ); const existing = await this.provider @@ -2997,7 +3024,6 @@ export class SolanaAcpClient extends BaseAcpClient { { signer: s.signer, jobPda: s.jobPda, - jobId: job.data.jobId, clientAddress: job.data.client, providerAddress: job.data.provider, fundRequestIntent: false, @@ -3010,7 +3036,6 @@ export class SolanaAcpClient extends BaseAcpClient { } const fanOut = await buildSubmitFanOut(ctx, { - jobId: job.data.jobId, jobPda: s.jobPda, seller: s.signer.address, escrow, @@ -3021,11 +3046,11 @@ export class SolanaAcpClient extends BaseAcpClient { provider: s.signer, job: s.jobPda, acpState: acpStatePda, - vault: vaultAta, + vault: vaultPda, vaultAuthority: vaultAuthorityPda, providerTokenAccount: providerAta, treasuryTokenAccount: treasuryAta, - platformTreasury: acpState.data.platformTreasury, + sponsor: acpState.data.sponsor, deliverable: s.deliverableBytes, hookProgram: ctx.router, hookWhitelist: await this.deriveHookWhitelistPda(chainId, ctx.router), @@ -3073,13 +3098,16 @@ export class SolanaAcpClient extends BaseAcpClient { ...postIxs, ]); - if (splitAtas) { - // Sponsored ALT compression: with the ATA-create prefund gone, the - // remaining Alchemy augmentation (CPI rent for the intent PDAs) is small - // enough that compressing the static accounts fits under 1232. + if (persistentAlt) { + // Sponsored ALT compression applies to EVERY router submit, not just a + // bracketed one: an uncompressed router submit measures close enough to + // 1232 bytes that Alchemy's own rent-prefund augmentation can push it + // over, which fails the tx with no on-chain trace (never broadcast) and + // leaves the inline ATA creates unsponsored. Compressing the static + // accounts reclaims headroom regardless of whether a bracket is present. prepared.sendOptions = { lookupTables: { - [persistentAlt as string]: await this.completeStaticAltAccounts(chainId), + [persistentAlt]: await this.completeStaticAltAccounts(chainId), }, sponsorLookupTables: true, }; @@ -3101,7 +3129,7 @@ export class SolanaAcpClient extends BaseAcpClient { vault: Address | undefined; vaultAuthority: Address | undefined; clientTokenAccount: Address | undefined; - platformTreasury: Address; + sponsor: Address; } ): Promise { const ctx = routerContext(chainId); @@ -3115,7 +3143,7 @@ export class SolanaAcpClient extends BaseAcpClient { } | null = null; const provEscrowIntentIdPda = await this.deriveProviderEscrowIntentIdPda( ctx.fundHook, - job.data.jobId + s.jobPda ); const maybePeii = await fetchMaybeProviderEscrowIntentId( rpc, @@ -3127,7 +3155,7 @@ export class SolanaAcpClient extends BaseAcpClient { if (maybePeii.exists && maybePeii.data.intentId !== 0n) { const escrowIntentPda = await this.deriveIntentPda( ctx.fundHook, - job.data.jobId, + s.jobPda, INTENT_KIND_ESCROW ); const intent = await fetchIntent(rpc, escrowIntentPda, { @@ -3135,7 +3163,7 @@ export class SolanaAcpClient extends BaseAcpClient { }); const escrowAuthorityPda = await this.deriveEscrowAuthorityPda( ctx.fundHook, - job.data.jobId + s.jobPda ); escrow = { providerAta: await this.deriveAta(intent.data.from, intent.data.token), @@ -3145,7 +3173,6 @@ export class SolanaAcpClient extends BaseAcpClient { } const fanOut = await buildRejectFanOut(ctx, { - jobId: job.data.jobId, jobPda: s.jobPda, provider: job.data.provider, escrow, @@ -3161,7 +3188,7 @@ export class SolanaAcpClient extends BaseAcpClient { ...(s.clientTokenAccount ? { clientTokenAccount: s.clientTokenAccount } : {}), - platformTreasury: s.platformTreasury, + sponsor: s.sponsor, hookProgram: ctx.router, hookWhitelist: await this.deriveHookWhitelistPda(chainId, ctx.router), reason: s.reasonBytes, @@ -3226,7 +3253,6 @@ export class SolanaAcpClient extends BaseAcpClient { args: { signer: SolanaSigner; jobPda: Address; - jobId: bigint; clientAddress: Address; providerAddress: Address; fundRequestIntent: boolean; @@ -3274,11 +3300,11 @@ export class SolanaAcpClient extends BaseAcpClient { throw new Error("fundHook address is required to pre-create intent PDAs"); } if (fundRequestIntent) { - const already = await exists(await intentPda(programs.fundHook, args.jobId, INTENT_KIND_FUND_REQUEST)); + const already = await exists(await intentPda(programs.fundHook, args.jobPda, INTENT_KIND_FUND_REQUEST)); if (already) { fundRequestIntent = false; anyPreexisting = true; } } if (escrowIntent) { - const already = await exists(await intentPda(programs.fundHook, args.jobId, INTENT_KIND_ESCROW)); + const already = await exists(await intentPda(programs.fundHook, args.jobPda, INTENT_KIND_ESCROW)); if (already) { escrowIntent = false; anyPreexisting = true; } } } @@ -3313,7 +3339,7 @@ export class SolanaAcpClient extends BaseAcpClient { if (subscriptionAlreadyActive) { proposedTerms = false; } else { - const already = await exists(await proposedTermsPda(programs.subHook, args.jobId)); + const already = await exists(await proposedTermsPda(programs.subHook, args.jobPda)); if (already) { proposedTerms = false; anyPreexisting = true; } } } @@ -3322,7 +3348,6 @@ export class SolanaAcpClient extends BaseAcpClient { ...programs, payer: args.signer, jobPda: args.jobPda, - jobId: args.jobId, clientAddress: args.clientAddress, providerAddress: args.providerAddress, fundRequestIntent, @@ -3365,14 +3390,14 @@ export class SolanaAcpClient extends BaseAcpClient { private async deriveJobPda( chainId: number, client: Address, - jobCounter: bigint + seed: bigint ): Promise
{ const [pda] = await getProgramDerivedAddress({ programAddress: this.programAddress(chainId), seeds: [ getUtf8Encoder().encode("job"), getAddressEncoder().encode(client), - getU64Encoder().encode(jobCounter), + getU64Encoder().encode(seed), ], }); return pda; @@ -3409,21 +3434,22 @@ export class SolanaAcpClient extends BaseAcpClient { } /** - * Intent PDAs are job-scoped — ["intent", job_id, kind] with kind - * 0 = fund-request, 1 = escrow. Derived from the job id alone, so prepare - * never predicts the hook's global intent counter (predicting it raced - * whenever two intent-creating transactions were in flight). + * Intent PDAs are job-scoped — ["intent", job_key, kind] with kind + * 0 = fund-request, 1 = escrow, where job_key is the job account's own + * address. Derived from the job's address alone, so prepare never predicts + * the hook's global intent counter (predicting it raced whenever two + * intent-creating transactions were in flight). */ private async deriveIntentPda( hookProgram: Address, - jobId: bigint, + job: Address, kind: typeof INTENT_KIND_FUND_REQUEST | typeof INTENT_KIND_ESCROW ): Promise
{ const [pda] = await getProgramDerivedAddress({ programAddress: hookProgram, seeds: [ getUtf8Encoder().encode("intent"), - getU64Encoder().encode(jobId), + getAddressEncoder().encode(job), new Uint8Array([kind]), ], }); @@ -3432,13 +3458,13 @@ export class SolanaAcpClient extends BaseAcpClient { private async deriveFundRequestIntentIdPda( hookProgram: Address, - jobId: bigint + job: Address ): Promise
{ const [pda] = await getProgramDerivedAddress({ programAddress: hookProgram, seeds: [ getUtf8Encoder().encode("fund_request_intent_id"), - getU64Encoder().encode(jobId), + getAddressEncoder().encode(job), ], }); return pda; @@ -3446,13 +3472,13 @@ export class SolanaAcpClient extends BaseAcpClient { private async deriveProviderEscrowIntentIdPda( hookProgram: Address, - jobId: bigint + job: Address ): Promise
{ const [pda] = await getProgramDerivedAddress({ programAddress: hookProgram, seeds: [ getUtf8Encoder().encode("provider_escrow_intent_id"), - getU64Encoder().encode(jobId), + getAddressEncoder().encode(job), ], }); return pda; @@ -3460,13 +3486,13 @@ export class SolanaAcpClient extends BaseAcpClient { private async deriveEscrowAuthorityPda( hookProgram: Address, - jobId: bigint + job: Address ): Promise
{ const [pda] = await getProgramDerivedAddress({ programAddress: hookProgram, seeds: [ getUtf8Encoder().encode("escrow_authority"), - getU64Encoder().encode(jobId), + getAddressEncoder().encode(job), ], }); return pda; @@ -3486,6 +3512,53 @@ export class SolanaAcpClient extends BaseAcpClient { return pda; } + /** + * The per-job escrow token account, program-derived so its ADDRESS is + * unique to the job. `vault.mint` is authoritative — there is exactly one + * vault per job and a token account's mint is immutable once initialized — + * which is what makes reading the mint back from the fetched vault safe on + * every exit path (submit/complete/reject/claimRefund), instead of trusting + * a caller-supplied or separately-stored mint. + */ + private async deriveVaultPda(chainId: number, jobPda: Address): Promise
{ + const [pda] = await getProgramDerivedAddress({ + programAddress: this.programAddress(chainId), + seeds: [ + getUtf8Encoder().encode("vault"), + getAddressEncoder().encode(jobPda), + ], + }); + return pda; + } + + /** + * Reads the mint straight off the vault's own bytes rather than decoding the + * full SPL token account: no generated fetcher exists for it here (this SDK + * has no @solana-program/token dependency), and the mint is the first 32 + * bytes of the standard Token Program account layout — the same raw-decode + * convention getTokenDecimals already uses for a mint account. + * + * Returns null when the vault does not exist (a zero-budget or not-yet- + * funded job), which every caller must treat as "no vault, no mint" rather + * than an error. + */ + private async fetchVaultMint( + chainId: number, + vaultPda: Address + ): Promise
{ + const rpc = this.provider.getRpc(chainId); + const accountInfo = await rpc + .getAccountInfo(vaultPda, { encoding: "base64", commitment: ACP_COMMITMENT }) + .send(); + if (!accountInfo.value) return null; + + const data = Uint8Array.from( + atob(accountInfo.value.data[0] as string), + (c) => c.charCodeAt(0) + ); + return getAddressDecoder().decode(data.subarray(0, 32)); + } + private async deriveAta(owner: Address, mint: Address): Promise
{ const TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address; @@ -3503,17 +3576,38 @@ export class SolanaAcpClient extends BaseAcpClient { return pda; } - private async resolveJobPda( + /** + * Cache key for a job PDA. Includes the client: `jobId` (the seed passed at + * creation) is a caller-chosen u64 that need only be unique PER CLIENT, not + * globally — unlike the old acp_state.job_counter-derived id, two different + * clients may legitimately pick the same value. A key without the client + * would let one client's cache entry silently answer another client's + * lookup. + */ + private jobPdaCacheKey(chainId: number, client: Address, jobId: bigint): string { + return `${chainId}:${client}:${jobId}`; + } + + /** + * Resolves the chain-agnostic bigint jobId (the seed passed to createJob) + * to the job's actual Solana address. Public because the job's identity IS + * that address — anything doing address-level work on a Solana job (PDA + * derivation for a hook/router account, a raw account read) needs this, not + * just internal lifecycle methods. `clientAddress` defaults to the current + * signer, matching every other jobId-keyed method on this class: without an + * explicit client, a job is assumed to belong to the caller's own wallet. + */ + async resolveJobPda( chainId: number, jobId: bigint, clientAddress?: string ): Promise
{ - const cacheKey = `${chainId}:${jobId}`; + const client = (clientAddress ?? + this.provider.getSigner().address) as Address; + const cacheKey = this.jobPdaCacheKey(chainId, client, jobId); const cached = this.jobPdaCache.get(cacheKey); if (cached) return cached; - const client = (clientAddress ?? - this.provider.getSigner().address) as Address; const pda = await this.deriveJobPda(chainId, client, jobId); this.jobPdaCache.set(cacheKey, pda); return pda; diff --git a/src/core/solana/generated/acp/accounts/acpState.ts b/src/core/solana/generated/acp/accounts/acpState.ts index ae03124..696f51a 100644 --- a/src/core/solana/generated/acp/accounts/acpState.ts +++ b/src/core/solana/generated/acp/accounts/acpState.ts @@ -61,17 +61,12 @@ export type AcpState = { pendingAuthority: Option
; /** SPL mint accepted for job budgets. */ paymentToken: Address; - /** Wallet that receives platform fees and reclaimed vault rent. */ + /** Wallet that receives platform fees. Rent goes to `sponsor` instead. */ platformTreasury: Address; /** Platform fee in basis points (0-10000). Applied to vault balance on completion. */ platformFeeBp: bigint; /** Evaluator fee in basis points (0-10000). Applied on evaluator-confirmed completions. */ evaluatorFeeBp: bigint; - /** - * Monotonically increasing counter storing the LAST issued job ID - * The next job receives `job_counter + 1`; 0 means no jobs issued yet. - */ - jobCounter: bigint; /** PDA bump seed. */ bump: number; /** @@ -79,6 +74,16 @@ export type AcpState = { * Appended after `bump` so the existing on-chain layout only grows by one byte. */ paused: boolean; + /** + * Receives reclaimed native rent from every account close. Separate from + * `platform_treasury`, which receives SPL fees: under gas sponsorship the + * rent was never the user's money nor protocol revenue, it is the sponsor's + * float coming back. Appended after `paused` so the layout only grows at + * the tail; `migrate_state` backfills it from an explicit argument when + * zero, since it must differ from `platform_treasury` and so cannot be + * inferred from existing state. + */ + sponsor: Address; }; export type AcpStateArgs = { @@ -88,17 +93,12 @@ export type AcpStateArgs = { pendingAuthority: OptionOrNullable
; /** SPL mint accepted for job budgets. */ paymentToken: Address; - /** Wallet that receives platform fees and reclaimed vault rent. */ + /** Wallet that receives platform fees. Rent goes to `sponsor` instead. */ platformTreasury: Address; /** Platform fee in basis points (0-10000). Applied to vault balance on completion. */ platformFeeBp: number | bigint; /** Evaluator fee in basis points (0-10000). Applied on evaluator-confirmed completions. */ evaluatorFeeBp: number | bigint; - /** - * Monotonically increasing counter storing the LAST issued job ID - * The next job receives `job_counter + 1`; 0 means no jobs issued yet. - */ - jobCounter: number | bigint; /** PDA bump seed. */ bump: number; /** @@ -106,6 +106,16 @@ export type AcpStateArgs = { * Appended after `bump` so the existing on-chain layout only grows by one byte. */ paused: boolean; + /** + * Receives reclaimed native rent from every account close. Separate from + * `platform_treasury`, which receives SPL fees: under gas sponsorship the + * rent was never the user's money nor protocol revenue, it is the sponsor's + * float coming back. Appended after `paused` so the layout only grows at + * the tail; `migrate_state` backfills it from an explicit argument when + * zero, since it must differ from `platform_treasury` and so cannot be + * inferred from existing state. + */ + sponsor: Address; }; /** Gets the encoder for {@link AcpStateArgs} account data. */ @@ -119,9 +129,9 @@ export function getAcpStateEncoder(): Encoder { ["platformTreasury", getAddressEncoder()], ["platformFeeBp", getU64Encoder()], ["evaluatorFeeBp", getU64Encoder()], - ["jobCounter", getU64Encoder()], ["bump", getU8Encoder()], ["paused", getBooleanEncoder()], + ["sponsor", getAddressEncoder()], ]), (value) => ({ ...value, discriminator: ACP_STATE_DISCRIMINATOR }), ); @@ -137,9 +147,9 @@ export function getAcpStateDecoder(): Decoder { ["platformTreasury", getAddressDecoder()], ["platformFeeBp", getU64Decoder()], ["evaluatorFeeBp", getU64Decoder()], - ["jobCounter", getU64Decoder()], ["bump", getU8Decoder()], ["paused", getBooleanDecoder()], + ["sponsor", getAddressDecoder()], ]); } diff --git a/src/core/solana/generated/acp/accounts/job.ts b/src/core/solana/generated/acp/accounts/job.ts index 3997a33..9beb246 100644 --- a/src/core/solana/generated/acp/accounts/job.ts +++ b/src/core/solana/generated/acp/accounts/job.ts @@ -7,8 +7,6 @@ */ import { - addDecoderSizePrefix, - addEncoderSizePrefix, assertAccountExists, assertAccountsExist, combineCodec, @@ -27,14 +25,10 @@ import { getOptionEncoder, getStructDecoder, getStructEncoder, - getU32Decoder, - getU32Encoder, getU64Decoder, getU64Encoder, getU8Decoder, getU8Encoder, - getUtf8Decoder, - getUtf8Encoder, transformEncoder, type Account, type Address, @@ -67,8 +61,18 @@ export function getJobDiscriminatorBytes() { export type Job = { discriminator: ReadonlyUint8Array; - /** Unique identifier: `AcpState.job_counter` after its increment at creation. */ - jobId: bigint; + /** + * Client-supplied uniquifier, kept only so the PDA can be re-derived + * (`seeds = [b"job", client, seed]`). It is NOT the job's identity and + * carries no ordering: two clients may pick the same value, and `init` + * rejects a reuse by the same client because the address already exists. + * + * **The job's identity is its own address**, `job.key()`. That is what the + * hooks seed their per-job PDAs from and what the CPI payload carries. + * Deriving the address from a shared counter instead made every creation + * contend on one account, capping the protocol at one job per slot. + */ + seed: bigint; /** The party that created and funds the job. */ client: Address; /** The agent fulfilling the job. `Pubkey::default()` until set via `set_provider`. */ @@ -79,20 +83,10 @@ export type Job = { state: JobState; /** Token amount escrowed on fund. Zero for zero-budget jobs. */ budgetAmount: bigint; - /** SPL mint for the budget. `None` until `set_budget` is called. */ - budgetMint: Option
; - /** Human-readable description (max 256 bytes). */ - description: string; /** Unix timestamp after which the job may be claimed as expired. */ expiredAt: bigint; - /** Unix timestamp of job creation. */ - createdAt: bigint; /** Optional hook program invoked on before/after action callbacks. */ hookAddress: Option
; - /** 32-byte hash/identifier of the work product, set on submit. */ - deliverable: ReadonlyUint8Array; - /** 32-byte completion or rejection reason, set on complete/reject. */ - completionReason: ReadonlyUint8Array; /** PDA bump for the per-job vault authority. Set during fund. */ vaultBump: number; /** PDA bump seed for this job account. */ @@ -100,8 +94,18 @@ export type Job = { }; export type JobArgs = { - /** Unique identifier: `AcpState.job_counter` after its increment at creation. */ - jobId: number | bigint; + /** + * Client-supplied uniquifier, kept only so the PDA can be re-derived + * (`seeds = [b"job", client, seed]`). It is NOT the job's identity and + * carries no ordering: two clients may pick the same value, and `init` + * rejects a reuse by the same client because the address already exists. + * + * **The job's identity is its own address**, `job.key()`. That is what the + * hooks seed their per-job PDAs from and what the CPI payload carries. + * Deriving the address from a shared counter instead made every creation + * contend on one account, capping the protocol at one job per slot. + */ + seed: number | bigint; /** The party that created and funds the job. */ client: Address; /** The agent fulfilling the job. `Pubkey::default()` until set via `set_provider`. */ @@ -112,20 +116,10 @@ export type JobArgs = { state: JobStateArgs; /** Token amount escrowed on fund. Zero for zero-budget jobs. */ budgetAmount: number | bigint; - /** SPL mint for the budget. `None` until `set_budget` is called. */ - budgetMint: OptionOrNullable
; - /** Human-readable description (max 256 bytes). */ - description: string; /** Unix timestamp after which the job may be claimed as expired. */ expiredAt: number | bigint; - /** Unix timestamp of job creation. */ - createdAt: number | bigint; /** Optional hook program invoked on before/after action callbacks. */ hookAddress: OptionOrNullable
; - /** 32-byte hash/identifier of the work product, set on submit. */ - deliverable: ReadonlyUint8Array; - /** 32-byte completion or rejection reason, set on complete/reject. */ - completionReason: ReadonlyUint8Array; /** PDA bump for the per-job vault authority. Set during fund. */ vaultBump: number; /** PDA bump seed for this job account. */ @@ -137,19 +131,14 @@ export function getJobEncoder(): Encoder { return transformEncoder( getStructEncoder([ ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], - ["jobId", getU64Encoder()], + ["seed", getU64Encoder()], ["client", getAddressEncoder()], ["provider", getAddressEncoder()], ["evaluator", getAddressEncoder()], ["state", getJobStateEncoder()], ["budgetAmount", getU64Encoder()], - ["budgetMint", getOptionEncoder(getAddressEncoder())], - ["description", addEncoderSizePrefix(getUtf8Encoder(), getU32Encoder())], ["expiredAt", getI64Encoder()], - ["createdAt", getI64Encoder()], ["hookAddress", getOptionEncoder(getAddressEncoder())], - ["deliverable", fixEncoderSize(getBytesEncoder(), 32)], - ["completionReason", fixEncoderSize(getBytesEncoder(), 32)], ["vaultBump", getU8Encoder()], ["bump", getU8Encoder()], ]), @@ -161,19 +150,14 @@ export function getJobEncoder(): Encoder { export function getJobDecoder(): Decoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["seed", getU64Decoder()], ["client", getAddressDecoder()], ["provider", getAddressDecoder()], ["evaluator", getAddressDecoder()], ["state", getJobStateDecoder()], ["budgetAmount", getU64Decoder()], - ["budgetMint", getOptionDecoder(getAddressDecoder())], - ["description", addDecoderSizePrefix(getUtf8Decoder(), getU32Decoder())], ["expiredAt", getI64Decoder()], - ["createdAt", getI64Decoder()], ["hookAddress", getOptionDecoder(getAddressDecoder())], - ["deliverable", fixDecoderSize(getBytesDecoder(), 32)], - ["completionReason", fixDecoderSize(getBytesDecoder(), 32)], ["vaultBump", getU8Decoder()], ["bump", getU8Decoder()], ]); diff --git a/src/core/solana/generated/acp/errors/agenticCommerceV3.ts b/src/core/solana/generated/acp/errors/agenticCommerceV3.ts index da11fc5..1ebe263 100644 --- a/src/core/solana/generated/acp/errors/agenticCommerceV3.ts +++ b/src/core/solana/generated/acp/errors/agenticCommerceV3.ts @@ -56,38 +56,44 @@ export const AGENTIC_COMMERCE_V3_ERROR__BUDGET_NOT_SET = 0x1782; // 6018 export const AGENTIC_COMMERCE_V3_ERROR__BUDGET_MISMATCH = 0x1783; // 6019 /** ProviderNotSet: Provider not set */ export const AGENTIC_COMMERCE_V3_ERROR__PROVIDER_NOT_SET = 0x1784; // 6020 +/** ZeroBudgetVaultNotAllowed: Vault not allowed on a zero-budget job */ +export const AGENTIC_COMMERCE_V3_ERROR__ZERO_BUDGET_VAULT_NOT_ALLOWED = 0x1785; // 6021 /** ExpiryTooShort: Expiry too short */ -export const AGENTIC_COMMERCE_V3_ERROR__EXPIRY_TOO_SHORT = 0x1785; // 6021 +export const AGENTIC_COMMERCE_V3_ERROR__EXPIRY_TOO_SHORT = 0x1786; // 6022 +/** ExpiryTooLong: Expiry too long */ +export const AGENTIC_COMMERCE_V3_ERROR__EXPIRY_TOO_LONG = 0x1787; // 6023 /** ExpiredAtMustBeInTheFuture: ExpiredAt must be in the future */ -export const AGENTIC_COMMERCE_V3_ERROR__EXPIRED_AT_MUST_BE_IN_THE_FUTURE = 0x1786; // 6022 +export const AGENTIC_COMMERCE_V3_ERROR__EXPIRED_AT_MUST_BE_IN_THE_FUTURE = 0x1788; // 6024 /** PlatformFeeTooHigh: Platform fee too high */ -export const AGENTIC_COMMERCE_V3_ERROR__PLATFORM_FEE_TOO_HIGH = 0x1787; // 6023 +export const AGENTIC_COMMERCE_V3_ERROR__PLATFORM_FEE_TOO_HIGH = 0x1789; // 6025 /** FeesTooHigh: Combined fees too high */ -export const AGENTIC_COMMERCE_V3_ERROR__FEES_TOO_HIGH = 0x1788; // 6024 +export const AGENTIC_COMMERCE_V3_ERROR__FEES_TOO_HIGH = 0x178a; // 6026 +/** SponsorCannotBeTreasury: Sponsor and platform treasury must be different addresses */ +export const AGENTIC_COMMERCE_V3_ERROR__SPONSOR_CANNOT_BE_TREASURY = 0x178b; // 6027 /** InvalidDestination: Invalid destination account */ -export const AGENTIC_COMMERCE_V3_ERROR__INVALID_DESTINATION = 0x1789; // 6025 +export const AGENTIC_COMMERCE_V3_ERROR__INVALID_DESTINATION = 0x178c; // 6028 /** InvalidVaultAuthority: Invalid vault authority */ -export const AGENTIC_COMMERCE_V3_ERROR__INVALID_VAULT_AUTHORITY = 0x178a; // 6026 +export const AGENTIC_COMMERCE_V3_ERROR__INVALID_VAULT_AUTHORITY = 0x178d; // 6029 /** InvalidVaultOwner: Invalid vault owner */ -export const AGENTIC_COMMERCE_V3_ERROR__INVALID_VAULT_OWNER = 0x178b; // 6027 +export const AGENTIC_COMMERCE_V3_ERROR__INVALID_VAULT_OWNER = 0x178e; // 6030 /** InvalidVaultMint: Invalid vault mint */ -export const AGENTIC_COMMERCE_V3_ERROR__INVALID_VAULT_MINT = 0x178c; // 6028 +export const AGENTIC_COMMERCE_V3_ERROR__INVALID_VAULT_MINT = 0x178f; // 6031 /** BudgetMintNotSet: Budget mint must be set when budget amount is nonzero */ -export const AGENTIC_COMMERCE_V3_ERROR__BUDGET_MINT_NOT_SET = 0x178d; // 6029 +export const AGENTIC_COMMERCE_V3_ERROR__BUDGET_MINT_NOT_SET = 0x1790; // 6032 /** InsufficientVaultBalance: Insufficient vault balance */ -export const AGENTIC_COMMERCE_V3_ERROR__INSUFFICIENT_VAULT_BALANCE = 0x178e; // 6030 +export const AGENTIC_COMMERCE_V3_ERROR__INSUFFICIENT_VAULT_BALANCE = 0x1791; // 6033 /** MissingRequiredAccount: Missing required account for funded job */ -export const AGENTIC_COMMERCE_V3_ERROR__MISSING_REQUIRED_ACCOUNT = 0x178f; // 6031 +export const AGENTIC_COMMERCE_V3_ERROR__MISSING_REQUIRED_ACCOUNT = 0x1792; // 6034 /** ArithmeticOverflow: Arithmetic overflow */ -export const AGENTIC_COMMERCE_V3_ERROR__ARITHMETIC_OVERFLOW = 0x1790; // 6032 +export const AGENTIC_COMMERCE_V3_ERROR__ARITHMETIC_OVERFLOW = 0x1793; // 6035 /** UntrustedHookUpgradeAuthority: Hook program upgrade authority must be None or the ACP authority */ -export const AGENTIC_COMMERCE_V3_ERROR__UNTRUSTED_HOOK_UPGRADE_AUTHORITY = 0x1791; // 6033 +export const AGENTIC_COMMERCE_V3_ERROR__UNTRUSTED_HOOK_UPGRADE_AUTHORITY = 0x1794; // 6036 /** MissingHookProgram: Missing hook program account: job declares a hook but hook_program was not supplied */ -export const AGENTIC_COMMERCE_V3_ERROR__MISSING_HOOK_PROGRAM = 0x1792; // 6034 +export const AGENTIC_COMMERCE_V3_ERROR__MISSING_HOOK_PROGRAM = 0x1795; // 6037 /** InvalidAutoCompleteParams: Invalid complete_opt_params: must be empty (legacy mode) or [u16 submit_count][...] with submit_count <= remaining_accounts.len() */ -export const AGENTIC_COMMERCE_V3_ERROR__INVALID_AUTO_COMPLETE_PARAMS = 0x1793; // 6035 +export const AGENTIC_COMMERCE_V3_ERROR__INVALID_AUTO_COMPLETE_PARAMS = 0x1796; // 6038 /** ProtocolPaused: Protocol is paused */ -export const AGENTIC_COMMERCE_V3_ERROR__PROTOCOL_PAUSED = 0x1794; // 6036 +export const AGENTIC_COMMERCE_V3_ERROR__PROTOCOL_PAUSED = 0x1797; // 6039 export type AgenticCommerceV3Error = | typeof AGENTIC_COMMERCE_V3_ERROR__AMOUNT_MUST_BE_GREATER_THAN_ZERO @@ -99,6 +105,7 @@ export type AgenticCommerceV3Error = | typeof AGENTIC_COMMERCE_V3_ERROR__EVALUATOR_CANNOT_BE_CLIENT | typeof AGENTIC_COMMERCE_V3_ERROR__EVALUATOR_CANNOT_BE_PROVIDER | typeof AGENTIC_COMMERCE_V3_ERROR__EXPIRED_AT_MUST_BE_IN_THE_FUTURE + | typeof AGENTIC_COMMERCE_V3_ERROR__EXPIRY_TOO_LONG | typeof AGENTIC_COMMERCE_V3_ERROR__EXPIRY_TOO_SHORT | typeof AGENTIC_COMMERCE_V3_ERROR__FEES_TOO_HIGH | typeof AGENTIC_COMMERCE_V3_ERROR__HOOK_NOT_WHITELISTED @@ -121,12 +128,14 @@ export type AgenticCommerceV3Error = | typeof AGENTIC_COMMERCE_V3_ERROR__PROTOCOL_PAUSED | typeof AGENTIC_COMMERCE_V3_ERROR__PROVIDER_CANNOT_BE_CLIENT | typeof AGENTIC_COMMERCE_V3_ERROR__PROVIDER_NOT_SET + | typeof AGENTIC_COMMERCE_V3_ERROR__SPONSOR_CANNOT_BE_TREASURY | typeof AGENTIC_COMMERCE_V3_ERROR__UNAUTHORIZED | typeof AGENTIC_COMMERCE_V3_ERROR__UNTRUSTED_HOOK_UPGRADE_AUTHORITY | typeof AGENTIC_COMMERCE_V3_ERROR__WRONG_STATUS | typeof AGENTIC_COMMERCE_V3_ERROR__ZERO_ADDRESS | typeof AGENTIC_COMMERCE_V3_ERROR__ZERO_ADDRESS_EVALUATOR - | typeof AGENTIC_COMMERCE_V3_ERROR__ZERO_ADDRESS_PROVIDER; + | typeof AGENTIC_COMMERCE_V3_ERROR__ZERO_ADDRESS_PROVIDER + | typeof AGENTIC_COMMERCE_V3_ERROR__ZERO_BUDGET_VAULT_NOT_ALLOWED; let agenticCommerceV3ErrorMessages: | Record @@ -142,6 +151,7 @@ if (process.env.NODE_ENV !== "production") { [AGENTIC_COMMERCE_V3_ERROR__EVALUATOR_CANNOT_BE_CLIENT]: `Evaluator cannot be the client`, [AGENTIC_COMMERCE_V3_ERROR__EVALUATOR_CANNOT_BE_PROVIDER]: `Evaluator cannot be the provider`, [AGENTIC_COMMERCE_V3_ERROR__EXPIRED_AT_MUST_BE_IN_THE_FUTURE]: `ExpiredAt must be in the future`, + [AGENTIC_COMMERCE_V3_ERROR__EXPIRY_TOO_LONG]: `Expiry too long`, [AGENTIC_COMMERCE_V3_ERROR__EXPIRY_TOO_SHORT]: `Expiry too short`, [AGENTIC_COMMERCE_V3_ERROR__FEES_TOO_HIGH]: `Combined fees too high`, [AGENTIC_COMMERCE_V3_ERROR__HOOK_NOT_WHITELISTED]: `Hook not whitelisted`, @@ -164,12 +174,14 @@ if (process.env.NODE_ENV !== "production") { [AGENTIC_COMMERCE_V3_ERROR__PROTOCOL_PAUSED]: `Protocol is paused`, [AGENTIC_COMMERCE_V3_ERROR__PROVIDER_CANNOT_BE_CLIENT]: `Provider cannot be the client`, [AGENTIC_COMMERCE_V3_ERROR__PROVIDER_NOT_SET]: `Provider not set`, + [AGENTIC_COMMERCE_V3_ERROR__SPONSOR_CANNOT_BE_TREASURY]: `Sponsor and platform treasury must be different addresses`, [AGENTIC_COMMERCE_V3_ERROR__UNAUTHORIZED]: `Unauthorized`, [AGENTIC_COMMERCE_V3_ERROR__UNTRUSTED_HOOK_UPGRADE_AUTHORITY]: `Hook program upgrade authority must be None or the ACP authority`, [AGENTIC_COMMERCE_V3_ERROR__WRONG_STATUS]: `Wrong job status`, [AGENTIC_COMMERCE_V3_ERROR__ZERO_ADDRESS]: `ZeroAddress`, [AGENTIC_COMMERCE_V3_ERROR__ZERO_ADDRESS_EVALUATOR]: `ZeroAddressEvaluator`, [AGENTIC_COMMERCE_V3_ERROR__ZERO_ADDRESS_PROVIDER]: `ZeroAddressProvider`, + [AGENTIC_COMMERCE_V3_ERROR__ZERO_BUDGET_VAULT_NOT_ALLOWED]: `Vault not allowed on a zero-budget job`, }; } diff --git a/src/core/solana/generated/acp/instructions/claimRefund.ts b/src/core/solana/generated/acp/instructions/claimRefund.ts index b39bcc8..6ae47d6 100644 --- a/src/core/solana/generated/acp/instructions/claimRefund.ts +++ b/src/core/solana/generated/acp/instructions/claimRefund.ts @@ -30,9 +30,13 @@ import { type TransactionSigner, type WritableAccount, } from "@solana/kit"; -import { findAcpStatePda } from "../pdas/index.js"; +import { findAcpStatePda, findVaultPda } from "../pdas/index.js"; import { AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS } from "../programs/index.js"; -import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; +import { + expectAddress, + getAccountMetaFactory, + type ResolvedAccount, +} from "../shared/index.js"; export const CLAIM_REFUND_DISCRIMINATOR = new Uint8Array([ 15, 16, 30, 161, 255, 228, 97, 60, @@ -52,7 +56,7 @@ export type ClaimRefundInstruction< TAccountVault extends string | AccountMeta = string, TAccountVaultAuthority extends string | AccountMeta = string, TAccountClientTokenAccount extends string | AccountMeta = string, - TAccountPlatformTreasury extends string | AccountMeta = string, + TAccountSponsor extends string | AccountMeta = string, TAccountTokenProgram extends string | AccountMeta = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", TRemainingAccounts extends readonly AccountMeta[] = [], @@ -77,9 +81,9 @@ export type ClaimRefundInstruction< TAccountClientTokenAccount extends string ? WritableAccount : TAccountClientTokenAccount, - TAccountPlatformTreasury extends string - ? WritableAccount - : TAccountPlatformTreasury, + TAccountSponsor extends string + ? WritableAccount + : TAccountSponsor, TAccountTokenProgram extends string ? ReadonlyAccount : TAccountTokenProgram, @@ -121,7 +125,7 @@ export type ClaimRefundAsyncInput< TAccountVault extends string = string, TAccountVaultAuthority extends string = string, TAccountClientTokenAccount extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, TAccountTokenProgram extends string = string, > = { /** Anyone can call — tokens always go to job.client */ @@ -133,7 +137,8 @@ export type ClaimRefundAsyncInput< vaultAuthority?: Address; /** Optional: required when the job was Funded/Submitted with budget > 0 */ clientTokenAccount?: Address; - platformTreasury?: Address; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: Address; tokenProgram?: Address; }; @@ -144,7 +149,7 @@ export async function getClaimRefundInstructionAsync< TAccountVault extends string, TAccountVaultAuthority extends string, TAccountClientTokenAccount extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TAccountTokenProgram extends string, TProgramAddress extends Address = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, >( @@ -155,7 +160,7 @@ export async function getClaimRefundInstructionAsync< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram >, config?: { programAddress?: TProgramAddress }, @@ -168,7 +173,7 @@ export async function getClaimRefundInstructionAsync< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram > > { @@ -187,10 +192,7 @@ export async function getClaimRefundInstructionAsync< value: input.clientTokenAccount ?? null, isWritable: true, }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, }; const accounts = originalAccounts as Record< @@ -202,6 +204,11 @@ export async function getClaimRefundInstructionAsync< if (!accounts.acpState.value) { accounts.acpState.value = await findAcpStatePda(); } + if (!accounts.vault.value) { + accounts.vault.value = await findVaultPda({ + job: expectAddress(accounts.job.value), + }); + } if (!accounts.tokenProgram.value) { accounts.tokenProgram.value = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">; @@ -216,7 +223,7 @@ export async function getClaimRefundInstructionAsync< getAccountMeta(accounts.vault), getAccountMeta(accounts.vaultAuthority), getAccountMeta(accounts.clientTokenAccount), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), getAccountMeta(accounts.tokenProgram), ], data: getClaimRefundInstructionDataEncoder().encode({}), @@ -229,7 +236,7 @@ export async function getClaimRefundInstructionAsync< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram >); } @@ -241,7 +248,7 @@ export type ClaimRefundInput< TAccountVault extends string = string, TAccountVaultAuthority extends string = string, TAccountClientTokenAccount extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, TAccountTokenProgram extends string = string, > = { /** Anyone can call — tokens always go to job.client */ @@ -253,7 +260,8 @@ export type ClaimRefundInput< vaultAuthority?: Address; /** Optional: required when the job was Funded/Submitted with budget > 0 */ clientTokenAccount?: Address; - platformTreasury?: Address; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: Address; tokenProgram?: Address; }; @@ -264,7 +272,7 @@ export function getClaimRefundInstruction< TAccountVault extends string, TAccountVaultAuthority extends string, TAccountClientTokenAccount extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TAccountTokenProgram extends string, TProgramAddress extends Address = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, >( @@ -275,7 +283,7 @@ export function getClaimRefundInstruction< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram >, config?: { programAddress?: TProgramAddress }, @@ -287,7 +295,7 @@ export function getClaimRefundInstruction< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram > { // Program address. @@ -305,10 +313,7 @@ export function getClaimRefundInstruction< value: input.clientTokenAccount ?? null, isWritable: true, }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, }; const accounts = originalAccounts as Record< @@ -331,7 +336,7 @@ export function getClaimRefundInstruction< getAccountMeta(accounts.vault), getAccountMeta(accounts.vaultAuthority), getAccountMeta(accounts.clientTokenAccount), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), getAccountMeta(accounts.tokenProgram), ], data: getClaimRefundInstructionDataEncoder().encode({}), @@ -344,7 +349,7 @@ export function getClaimRefundInstruction< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram >); } @@ -364,7 +369,8 @@ export type ParsedClaimRefundInstruction< vaultAuthority?: TAccountMetas[4] | undefined; /** Optional: required when the job was Funded/Submitted with budget > 0 */ clientTokenAccount?: TAccountMetas[5] | undefined; - platformTreasury?: TAccountMetas[6] | undefined; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: TAccountMetas[6] | undefined; tokenProgram?: TAccountMetas[7] | undefined; }; data: ClaimRefundInstructionData; @@ -403,7 +409,7 @@ export function parseClaimRefundInstruction< vault: getNextOptionalAccount(), vaultAuthority: getNextOptionalAccount(), clientTokenAccount: getNextOptionalAccount(), - platformTreasury: getNextOptionalAccount(), + sponsor: getNextOptionalAccount(), tokenProgram: getNextOptionalAccount(), }, data: getClaimRefundInstructionDataDecoder().decode(instruction.data), diff --git a/src/core/solana/generated/acp/instructions/complete.ts b/src/core/solana/generated/acp/instructions/complete.ts index 9610475..5460539 100644 --- a/src/core/solana/generated/acp/instructions/complete.ts +++ b/src/core/solana/generated/acp/instructions/complete.ts @@ -34,9 +34,13 @@ import { type TransactionSigner, type WritableAccount, } from "@solana/kit"; -import { findAcpStatePda } from "../pdas/index.js"; +import { findAcpStatePda, findVaultPda } from "../pdas/index.js"; import { AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS } from "../programs/index.js"; -import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; +import { + expectAddress, + getAccountMetaFactory, + type ResolvedAccount, +} from "../shared/index.js"; export const COMPLETE_DISCRIMINATOR = new Uint8Array([ 0, 77, 224, 147, 136, 25, 88, 76, @@ -56,7 +60,7 @@ export type CompleteInstruction< TAccountProviderTokenAccount extends string | AccountMeta = string, TAccountTreasuryTokenAccount extends string | AccountMeta = string, TAccountEvaluatorTokenAccount extends string | AccountMeta = string, - TAccountPlatformTreasury extends string | AccountMeta = string, + TAccountSponsor extends string | AccountMeta = string, TAccountTokenProgram extends string | AccountMeta = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", TAccountHookProgram extends string | AccountMeta = string, @@ -89,9 +93,9 @@ export type CompleteInstruction< TAccountEvaluatorTokenAccount extends string ? WritableAccount : TAccountEvaluatorTokenAccount, - TAccountPlatformTreasury extends string - ? WritableAccount - : TAccountPlatformTreasury, + TAccountSponsor extends string + ? WritableAccount + : TAccountSponsor, TAccountTokenProgram extends string ? ReadonlyAccount : TAccountTokenProgram, @@ -154,7 +158,7 @@ export type CompleteAsyncInput< TAccountProviderTokenAccount extends string = string, TAccountTreasuryTokenAccount extends string = string, TAccountEvaluatorTokenAccount extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, TAccountTokenProgram extends string = string, TAccountHookProgram extends string = string, TAccountHookWhitelist extends string = string, @@ -171,7 +175,8 @@ export type CompleteAsyncInput< treasuryTokenAccount?: Address; /** Optional: required when evaluator_fee_bp > 0 and budget > 0 */ evaluatorTokenAccount?: Address; - platformTreasury?: Address; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: Address; tokenProgram?: Address; hookProgram?: Address; hookWhitelist?: Address; @@ -188,7 +193,7 @@ export async function getCompleteInstructionAsync< TAccountProviderTokenAccount extends string, TAccountTreasuryTokenAccount extends string, TAccountEvaluatorTokenAccount extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TAccountTokenProgram extends string, TAccountHookProgram extends string, TAccountHookWhitelist extends string, @@ -203,7 +208,7 @@ export async function getCompleteInstructionAsync< TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, TAccountEvaluatorTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -220,7 +225,7 @@ export async function getCompleteInstructionAsync< TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, TAccountEvaluatorTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -249,10 +254,7 @@ export async function getCompleteInstructionAsync< value: input.evaluatorTokenAccount ?? null, isWritable: true, }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, hookProgram: { value: input.hookProgram ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, @@ -269,6 +271,11 @@ export async function getCompleteInstructionAsync< if (!accounts.acpState.value) { accounts.acpState.value = await findAcpStatePda(); } + if (!accounts.vault.value) { + accounts.vault.value = await findVaultPda({ + job: expectAddress(accounts.job.value), + }); + } if (!accounts.tokenProgram.value) { accounts.tokenProgram.value = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">; @@ -285,7 +292,7 @@ export async function getCompleteInstructionAsync< getAccountMeta(accounts.providerTokenAccount), getAccountMeta(accounts.treasuryTokenAccount), getAccountMeta(accounts.evaluatorTokenAccount), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), getAccountMeta(accounts.tokenProgram), getAccountMeta(accounts.hookProgram), getAccountMeta(accounts.hookWhitelist), @@ -304,7 +311,7 @@ export async function getCompleteInstructionAsync< TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, TAccountEvaluatorTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -320,7 +327,7 @@ export type CompleteInput< TAccountProviderTokenAccount extends string = string, TAccountTreasuryTokenAccount extends string = string, TAccountEvaluatorTokenAccount extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, TAccountTokenProgram extends string = string, TAccountHookProgram extends string = string, TAccountHookWhitelist extends string = string, @@ -337,7 +344,8 @@ export type CompleteInput< treasuryTokenAccount?: Address; /** Optional: required when evaluator_fee_bp > 0 and budget > 0 */ evaluatorTokenAccount?: Address; - platformTreasury?: Address; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: Address; tokenProgram?: Address; hookProgram?: Address; hookWhitelist?: Address; @@ -354,7 +362,7 @@ export function getCompleteInstruction< TAccountProviderTokenAccount extends string, TAccountTreasuryTokenAccount extends string, TAccountEvaluatorTokenAccount extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TAccountTokenProgram extends string, TAccountHookProgram extends string, TAccountHookWhitelist extends string, @@ -369,7 +377,7 @@ export function getCompleteInstruction< TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, TAccountEvaluatorTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -385,7 +393,7 @@ export function getCompleteInstruction< TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, TAccountEvaluatorTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -413,10 +421,7 @@ export function getCompleteInstruction< value: input.evaluatorTokenAccount ?? null, isWritable: true, }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, hookProgram: { value: input.hookProgram ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, @@ -446,7 +451,7 @@ export function getCompleteInstruction< getAccountMeta(accounts.providerTokenAccount), getAccountMeta(accounts.treasuryTokenAccount), getAccountMeta(accounts.evaluatorTokenAccount), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), getAccountMeta(accounts.tokenProgram), getAccountMeta(accounts.hookProgram), getAccountMeta(accounts.hookWhitelist), @@ -465,7 +470,7 @@ export function getCompleteInstruction< TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, TAccountEvaluatorTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -490,7 +495,8 @@ export type ParsedCompleteInstruction< treasuryTokenAccount?: TAccountMetas[6] | undefined; /** Optional: required when evaluator_fee_bp > 0 and budget > 0 */ evaluatorTokenAccount?: TAccountMetas[7] | undefined; - platformTreasury?: TAccountMetas[8] | undefined; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: TAccountMetas[8] | undefined; tokenProgram?: TAccountMetas[9] | undefined; hookProgram?: TAccountMetas[10] | undefined; hookWhitelist?: TAccountMetas[11] | undefined; @@ -533,7 +539,7 @@ export function parseCompleteInstruction< providerTokenAccount: getNextOptionalAccount(), treasuryTokenAccount: getNextOptionalAccount(), evaluatorTokenAccount: getNextOptionalAccount(), - platformTreasury: getNextOptionalAccount(), + sponsor: getNextOptionalAccount(), tokenProgram: getNextOptionalAccount(), hookProgram: getNextOptionalAccount(), hookWhitelist: getNextOptionalAccount(), diff --git a/src/core/solana/generated/acp/instructions/createJob.ts b/src/core/solana/generated/acp/instructions/createJob.ts index 0e9f666..e6a96f7 100644 --- a/src/core/solana/generated/acp/instructions/createJob.ts +++ b/src/core/solana/generated/acp/instructions/createJob.ts @@ -24,6 +24,8 @@ import { getStructEncoder, getU32Decoder, getU32Encoder, + getU64Decoder, + getU64Encoder, getUtf8Decoder, getUtf8Encoder, transformEncoder, @@ -44,9 +46,14 @@ import { type WritableAccount, type WritableSignerAccount, } from "@solana/kit"; -import { findAcpStatePda } from "../pdas/index.js"; +import { findAcpStatePda, findJobPda } from "../pdas/index.js"; import { AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS } from "../programs/index.js"; -import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; +import { + expectAddress, + expectSome, + getAccountMetaFactory, + type ResolvedAccount, +} from "../shared/index.js"; export const CREATE_JOB_DISCRIMINATOR = new Uint8Array([ 178, 130, 217, 110, 100, 27, 82, 119, @@ -74,7 +81,7 @@ export type CreateJobInstruction< AccountSignerMeta : TAccountClient, TAccountAcpState extends string - ? WritableAccount + ? ReadonlyAccount : TAccountAcpState, TAccountJob extends string ? WritableAccount : TAccountJob, TAccountHookWhitelist extends string @@ -89,6 +96,7 @@ export type CreateJobInstruction< export type CreateJobInstructionData = { discriminator: ReadonlyUint8Array; + seed: bigint; provider: Address; evaluator: Address; description: string; @@ -97,6 +105,7 @@ export type CreateJobInstructionData = { }; export type CreateJobInstructionDataArgs = { + seed: number | bigint; provider: Address; evaluator: Address; description: string; @@ -108,6 +117,7 @@ export function getCreateJobInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], + ["seed", getU64Decoder()], ["provider", getAddressDecoder()], ["evaluator", getAddressDecoder()], ["description", addDecoderSizePrefix(getUtf8Decoder(), getU32Decoder())], @@ -148,10 +159,11 @@ export type CreateJobAsyncInput< > = { client: TransactionSigner; acpState?: Address; - job: Address; + job?: Address; /** Optional: must be provided if hook_address is Some */ hookWhitelist?: Address; systemProgram?: Address; + seed: CreateJobInstructionDataArgs["seed"]; provider: CreateJobInstructionDataArgs["provider"]; evaluator: CreateJobInstructionDataArgs["evaluator"]; description: CreateJobInstructionDataArgs["description"]; @@ -192,7 +204,7 @@ export async function getCreateJobInstructionAsync< // Original accounts. const originalAccounts = { client: { value: input.client ?? null, isWritable: true }, - acpState: { value: input.acpState ?? null, isWritable: true }, + acpState: { value: input.acpState ?? null, isWritable: false }, job: { value: input.job ?? null, isWritable: true }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, systemProgram: { value: input.systemProgram ?? null, isWritable: false }, @@ -209,6 +221,12 @@ export async function getCreateJobInstructionAsync< if (!accounts.acpState.value) { accounts.acpState.value = await findAcpStatePda(); } + if (!accounts.job.value) { + accounts.job.value = await findJobPda({ + client: expectAddress(accounts.client.value), + seed: expectSome(args.seed), + }); + } if (!accounts.systemProgram.value) { accounts.systemProgram.value = "11111111111111111111111111111111" as Address<"11111111111111111111111111111111">; @@ -250,6 +268,7 @@ export type CreateJobInput< /** Optional: must be provided if hook_address is Some */ hookWhitelist?: Address; systemProgram?: Address; + seed: CreateJobInstructionDataArgs["seed"]; provider: CreateJobInstructionDataArgs["provider"]; evaluator: CreateJobInstructionDataArgs["evaluator"]; description: CreateJobInstructionDataArgs["description"]; @@ -288,7 +307,7 @@ export function getCreateJobInstruction< // Original accounts. const originalAccounts = { client: { value: input.client ?? null, isWritable: true }, - acpState: { value: input.acpState ?? null, isWritable: true }, + acpState: { value: input.acpState ?? null, isWritable: false }, job: { value: input.job ?? null, isWritable: true }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, systemProgram: { value: input.systemProgram ?? null, isWritable: false }, diff --git a/src/core/solana/generated/acp/instructions/fund.ts b/src/core/solana/generated/acp/instructions/fund.ts index d5969a1..bb3fc23 100644 --- a/src/core/solana/generated/acp/instructions/fund.ts +++ b/src/core/solana/generated/acp/instructions/fund.ts @@ -36,9 +36,13 @@ import { type WritableAccount, type WritableSignerAccount, } from "@solana/kit"; -import { findAcpStatePda } from "../pdas/index.js"; +import { findAcpStatePda, findVaultPda } from "../pdas/index.js"; import { AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS } from "../programs/index.js"; -import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; +import { + expectAddress, + getAccountMetaFactory, + type ResolvedAccount, +} from "../shared/index.js"; export const FUND_DISCRIMINATOR = new Uint8Array([ 218, 188, 111, 221, 152, 113, 174, 7, @@ -172,7 +176,13 @@ export type FundAsyncInput< acpState?: Address; /** Optional: required when budget > 0 */ clientTokenAccount?: Address; - /** Optional: required when budget > 0 (init vault for escrow) */ + /** + * Per-job escrow vault, program-derived so its ADDRESS is unique to the job. + * That, not a stored mint, is what makes `vault.mint` authoritative on every + * exit path: there is exactly one vault per job and a token account's mint is + * immutable once initialized, so no substitute can be passed in its place. + * Required when budget > 0. + */ vault?: Address; vaultAuthority?: Address; mint?: Address; @@ -276,6 +286,11 @@ export async function getFundInstructionAsync< if (!accounts.acpState.value) { accounts.acpState.value = await findAcpStatePda(); } + if (!accounts.vault.value) { + accounts.vault.value = await findVaultPda({ + job: expectAddress(accounts.job.value), + }); + } if (!accounts.tokenProgram.value) { accounts.tokenProgram.value = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">; @@ -344,7 +359,13 @@ export type FundInput< acpState: Address; /** Optional: required when budget > 0 */ clientTokenAccount?: Address; - /** Optional: required when budget > 0 (init vault for escrow) */ + /** + * Per-job escrow vault, program-derived so its ADDRESS is unique to the job. + * That, not a stored mint, is what makes `vault.mint` authoritative on every + * exit path: there is exactly one vault per job and a token account's mint is + * immutable once initialized, so no substitute can be passed in its place. + * Required when budget > 0. + */ vault?: Address; vaultAuthority?: Address; mint?: Address; @@ -502,7 +523,13 @@ export type ParsedFundInstruction< acpState: TAccountMetas[2]; /** Optional: required when budget > 0 */ clientTokenAccount?: TAccountMetas[3] | undefined; - /** Optional: required when budget > 0 (init vault for escrow) */ + /** + * Per-job escrow vault, program-derived so its ADDRESS is unique to the job. + * That, not a stored mint, is what makes `vault.mint` authoritative on every + * exit path: there is exactly one vault per job and a token account's mint is + * immutable once initialized, so no substitute can be passed in its place. + * Required when budget > 0. + */ vault?: TAccountMetas[4] | undefined; vaultAuthority?: TAccountMetas[5] | undefined; mint?: TAccountMetas[6] | undefined; diff --git a/src/core/solana/generated/acp/instructions/index.ts b/src/core/solana/generated/acp/instructions/index.ts index 97400fe..1b876dc 100644 --- a/src/core/solana/generated/acp/instructions/index.ts +++ b/src/core/solana/generated/acp/instructions/index.ts @@ -21,6 +21,8 @@ export * from "./removeHookWhitelist.js"; export * from "./setBudget.js"; export * from "./setEvaluatorFee.js"; export * from "./setPause.js"; +export * from "./setPaymentToken.js"; export * from "./setPlatformFee.js"; export * from "./setProvider.js"; +export * from "./setSponsor.js"; export * from "./submit.js"; diff --git a/src/core/solana/generated/acp/instructions/initialize.ts b/src/core/solana/generated/acp/instructions/initialize.ts index 2e819e2..c6e8627 100644 --- a/src/core/solana/generated/acp/instructions/initialize.ts +++ b/src/core/solana/generated/acp/instructions/initialize.ts @@ -83,16 +83,16 @@ export type InitializeInstruction< export type InitializeInstructionData = { discriminator: ReadonlyUint8Array; platformTreasury: Address; + sponsor: Address; platformFeeBp: bigint; evaluatorFeeBp: bigint; - jobCounter: bigint; }; export type InitializeInstructionDataArgs = { platformTreasury: Address; + sponsor: Address; platformFeeBp: number | bigint; evaluatorFeeBp: number | bigint; - jobCounter: number | bigint; }; export function getInitializeInstructionDataEncoder(): FixedSizeEncoder { @@ -100,9 +100,9 @@ export function getInitializeInstructionDataEncoder(): FixedSizeEncoder ({ ...value, discriminator: INITIALIZE_DISCRIMINATOR }), ); @@ -112,9 +112,9 @@ export function getInitializeInstructionDataDecoder(): FixedSizeDecoder; systemProgram?: Address; platformTreasury: InitializeInstructionDataArgs["platformTreasury"]; + sponsor: InitializeInstructionDataArgs["sponsor"]; platformFeeBp: InitializeInstructionDataArgs["platformFeeBp"]; evaluatorFeeBp: InitializeInstructionDataArgs["evaluatorFeeBp"]; - jobCounter: InitializeInstructionDataArgs["jobCounter"]; }; export async function getInitializeInstructionAsync< @@ -252,9 +252,9 @@ export type InitializeInput< paymentToken: Address; systemProgram?: Address; platformTreasury: InitializeInstructionDataArgs["platformTreasury"]; + sponsor: InitializeInstructionDataArgs["sponsor"]; platformFeeBp: InitializeInstructionDataArgs["platformFeeBp"]; evaluatorFeeBp: InitializeInstructionDataArgs["evaluatorFeeBp"]; - jobCounter: InitializeInstructionDataArgs["jobCounter"]; }; export function getInitializeInstruction< diff --git a/src/core/solana/generated/acp/instructions/migrateState.ts b/src/core/solana/generated/acp/instructions/migrateState.ts index 95975e1..aad7302 100644 --- a/src/core/solana/generated/acp/instructions/migrateState.ts +++ b/src/core/solana/generated/acp/instructions/migrateState.ts @@ -10,6 +10,8 @@ import { combineCodec, fixDecoderSize, fixEncoderSize, + getAddressDecoder, + getAddressEncoder, getBytesDecoder, getBytesEncoder, getStructDecoder, @@ -69,13 +71,19 @@ export type MigrateStateInstruction< ] >; -export type MigrateStateInstructionData = { discriminator: ReadonlyUint8Array }; +export type MigrateStateInstructionData = { + discriminator: ReadonlyUint8Array; + sponsor: Address; +}; -export type MigrateStateInstructionDataArgs = {}; +export type MigrateStateInstructionDataArgs = { sponsor: Address }; export function getMigrateStateInstructionDataEncoder(): FixedSizeEncoder { return transformEncoder( - getStructEncoder([["discriminator", fixEncoderSize(getBytesEncoder(), 8)]]), + getStructEncoder([ + ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], + ["sponsor", getAddressEncoder()], + ]), (value) => ({ ...value, discriminator: MIGRATE_STATE_DISCRIMINATOR }), ); } @@ -83,6 +91,7 @@ export function getMigrateStateInstructionDataEncoder(): FixedSizeEncoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], + ["sponsor", getAddressDecoder()], ]); } @@ -105,6 +114,7 @@ export type MigrateStateAsyncInput< /** field (offset [8..40]) against the signer in the handler. */ acpState?: Address; systemProgram?: Address; + sponsor: MigrateStateInstructionDataArgs["sponsor"]; }; export async function getMigrateStateInstructionAsync< @@ -142,6 +152,9 @@ export async function getMigrateStateInstructionAsync< ResolvedAccount >; + // Original args. + const args = { ...input }; + // Resolve default values. if (!accounts.acpState.value) { accounts.acpState.value = await findAcpStatePda(); @@ -158,7 +171,9 @@ export async function getMigrateStateInstructionAsync< getAccountMeta(accounts.acpState), getAccountMeta(accounts.systemProgram), ], - data: getMigrateStateInstructionDataEncoder().encode({}), + data: getMigrateStateInstructionDataEncoder().encode( + args as MigrateStateInstructionDataArgs, + ), programAddress, } as MigrateStateInstruction< TProgramAddress, @@ -177,6 +192,7 @@ export type MigrateStateInput< /** field (offset [8..40]) against the signer in the handler. */ acpState: Address; systemProgram?: Address; + sponsor: MigrateStateInstructionDataArgs["sponsor"]; }; export function getMigrateStateInstruction< @@ -212,6 +228,9 @@ export function getMigrateStateInstruction< ResolvedAccount >; + // Original args. + const args = { ...input }; + // Resolve default values. if (!accounts.systemProgram.value) { accounts.systemProgram.value = @@ -225,7 +244,9 @@ export function getMigrateStateInstruction< getAccountMeta(accounts.acpState), getAccountMeta(accounts.systemProgram), ], - data: getMigrateStateInstructionDataEncoder().encode({}), + data: getMigrateStateInstructionDataEncoder().encode( + args as MigrateStateInstructionDataArgs, + ), programAddress, } as MigrateStateInstruction< TProgramAddress, diff --git a/src/core/solana/generated/acp/instructions/reject.ts b/src/core/solana/generated/acp/instructions/reject.ts index a5669d2..981267c 100644 --- a/src/core/solana/generated/acp/instructions/reject.ts +++ b/src/core/solana/generated/acp/instructions/reject.ts @@ -34,9 +34,13 @@ import { type TransactionSigner, type WritableAccount, } from "@solana/kit"; -import { findAcpStatePda } from "../pdas/index.js"; +import { findAcpStatePda, findVaultPda } from "../pdas/index.js"; import { AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS } from "../programs/index.js"; -import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; +import { + expectAddress, + getAccountMetaFactory, + type ResolvedAccount, +} from "../shared/index.js"; export const REJECT_DISCRIMINATOR = new Uint8Array([ 135, 7, 63, 85, 131, 114, 111, 224, @@ -54,7 +58,7 @@ export type RejectInstruction< TAccountVault extends string | AccountMeta = string, TAccountVaultAuthority extends string | AccountMeta = string, TAccountClientTokenAccount extends string | AccountMeta = string, - TAccountPlatformTreasury extends string | AccountMeta = string, + TAccountSponsor extends string | AccountMeta = string, TAccountTokenProgram extends string | AccountMeta = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", TAccountHookProgram extends string | AccountMeta = string, @@ -81,9 +85,9 @@ export type RejectInstruction< TAccountClientTokenAccount extends string ? WritableAccount : TAccountClientTokenAccount, - TAccountPlatformTreasury extends string - ? WritableAccount - : TAccountPlatformTreasury, + TAccountSponsor extends string + ? WritableAccount + : TAccountSponsor, TAccountTokenProgram extends string ? ReadonlyAccount : TAccountTokenProgram, @@ -144,7 +148,7 @@ export type RejectAsyncInput< TAccountVault extends string = string, TAccountVaultAuthority extends string = string, TAccountClientTokenAccount extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, TAccountTokenProgram extends string = string, TAccountHookProgram extends string = string, TAccountHookWhitelist extends string = string, @@ -155,7 +159,8 @@ export type RejectAsyncInput< vault?: Address; vaultAuthority?: Address; clientTokenAccount?: Address; - platformTreasury?: Address; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: Address; tokenProgram?: Address; hookProgram?: Address; hookWhitelist?: Address; @@ -170,7 +175,7 @@ export async function getRejectInstructionAsync< TAccountVault extends string, TAccountVaultAuthority extends string, TAccountClientTokenAccount extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TAccountTokenProgram extends string, TAccountHookProgram extends string, TAccountHookWhitelist extends string, @@ -183,7 +188,7 @@ export async function getRejectInstructionAsync< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -198,7 +203,7 @@ export async function getRejectInstructionAsync< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -219,10 +224,7 @@ export async function getRejectInstructionAsync< value: input.clientTokenAccount ?? null, isWritable: true, }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, hookProgram: { value: input.hookProgram ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, @@ -239,6 +241,11 @@ export async function getRejectInstructionAsync< if (!accounts.acpState.value) { accounts.acpState.value = await findAcpStatePda(); } + if (!accounts.vault.value) { + accounts.vault.value = await findVaultPda({ + job: expectAddress(accounts.job.value), + }); + } if (!accounts.tokenProgram.value) { accounts.tokenProgram.value = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">; @@ -253,7 +260,7 @@ export async function getRejectInstructionAsync< getAccountMeta(accounts.vault), getAccountMeta(accounts.vaultAuthority), getAccountMeta(accounts.clientTokenAccount), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), getAccountMeta(accounts.tokenProgram), getAccountMeta(accounts.hookProgram), getAccountMeta(accounts.hookWhitelist), @@ -270,7 +277,7 @@ export async function getRejectInstructionAsync< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -284,7 +291,7 @@ export type RejectInput< TAccountVault extends string = string, TAccountVaultAuthority extends string = string, TAccountClientTokenAccount extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, TAccountTokenProgram extends string = string, TAccountHookProgram extends string = string, TAccountHookWhitelist extends string = string, @@ -295,7 +302,8 @@ export type RejectInput< vault?: Address; vaultAuthority?: Address; clientTokenAccount?: Address; - platformTreasury?: Address; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: Address; tokenProgram?: Address; hookProgram?: Address; hookWhitelist?: Address; @@ -310,7 +318,7 @@ export function getRejectInstruction< TAccountVault extends string, TAccountVaultAuthority extends string, TAccountClientTokenAccount extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TAccountTokenProgram extends string, TAccountHookProgram extends string, TAccountHookWhitelist extends string, @@ -323,7 +331,7 @@ export function getRejectInstruction< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -337,7 +345,7 @@ export function getRejectInstruction< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -357,10 +365,7 @@ export function getRejectInstruction< value: input.clientTokenAccount ?? null, isWritable: true, }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, hookProgram: { value: input.hookProgram ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, @@ -388,7 +393,7 @@ export function getRejectInstruction< getAccountMeta(accounts.vault), getAccountMeta(accounts.vaultAuthority), getAccountMeta(accounts.clientTokenAccount), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), getAccountMeta(accounts.tokenProgram), getAccountMeta(accounts.hookProgram), getAccountMeta(accounts.hookWhitelist), @@ -405,7 +410,7 @@ export function getRejectInstruction< TAccountVault, TAccountVaultAuthority, TAccountClientTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist @@ -424,7 +429,8 @@ export type ParsedRejectInstruction< vault?: TAccountMetas[3] | undefined; vaultAuthority?: TAccountMetas[4] | undefined; clientTokenAccount?: TAccountMetas[5] | undefined; - platformTreasury?: TAccountMetas[6] | undefined; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: TAccountMetas[6] | undefined; tokenProgram?: TAccountMetas[7] | undefined; hookProgram?: TAccountMetas[8] | undefined; hookWhitelist?: TAccountMetas[9] | undefined; @@ -465,7 +471,7 @@ export function parseRejectInstruction< vault: getNextOptionalAccount(), vaultAuthority: getNextOptionalAccount(), clientTokenAccount: getNextOptionalAccount(), - platformTreasury: getNextOptionalAccount(), + sponsor: getNextOptionalAccount(), tokenProgram: getNextOptionalAccount(), hookProgram: getNextOptionalAccount(), hookWhitelist: getNextOptionalAccount(), diff --git a/src/core/solana/generated/acp/instructions/setBudget.ts b/src/core/solana/generated/acp/instructions/setBudget.ts index 22906de..554f574 100644 --- a/src/core/solana/generated/acp/instructions/setBudget.ts +++ b/src/core/solana/generated/acp/instructions/setBudget.ts @@ -52,7 +52,6 @@ export type SetBudgetInstruction< TProgram extends string = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, TAccountCaller extends string | AccountMeta = string, TAccountJob extends string | AccountMeta = string, - TAccountBudgetMint extends string | AccountMeta = string, TAccountAcpState extends string | AccountMeta = string, TAccountHookProgram extends string | AccountMeta = string, TAccountHookWhitelist extends string | AccountMeta = string, @@ -66,9 +65,6 @@ export type SetBudgetInstruction< AccountSignerMeta : TAccountCaller, TAccountJob extends string ? WritableAccount : TAccountJob, - TAccountBudgetMint extends string - ? ReadonlyAccount - : TAccountBudgetMint, TAccountAcpState extends string ? ReadonlyAccount : TAccountAcpState, @@ -125,14 +121,12 @@ export function getSetBudgetInstructionDataCodec(): Codec< export type SetBudgetAsyncInput< TAccountCaller extends string = string, TAccountJob extends string = string, - TAccountBudgetMint extends string = string, TAccountAcpState extends string = string, TAccountHookProgram extends string = string, TAccountHookWhitelist extends string = string, > = { caller: TransactionSigner; job: Address; - budgetMint: Address; acpState?: Address; hookProgram?: Address; hookWhitelist?: Address; @@ -143,7 +137,6 @@ export type SetBudgetAsyncInput< export async function getSetBudgetInstructionAsync< TAccountCaller extends string, TAccountJob extends string, - TAccountBudgetMint extends string, TAccountAcpState extends string, TAccountHookProgram extends string, TAccountHookWhitelist extends string, @@ -152,7 +145,6 @@ export async function getSetBudgetInstructionAsync< input: SetBudgetAsyncInput< TAccountCaller, TAccountJob, - TAccountBudgetMint, TAccountAcpState, TAccountHookProgram, TAccountHookWhitelist @@ -163,7 +155,6 @@ export async function getSetBudgetInstructionAsync< TProgramAddress, TAccountCaller, TAccountJob, - TAccountBudgetMint, TAccountAcpState, TAccountHookProgram, TAccountHookWhitelist @@ -177,7 +168,6 @@ export async function getSetBudgetInstructionAsync< const originalAccounts = { caller: { value: input.caller ?? null, isWritable: false }, job: { value: input.job ?? null, isWritable: true }, - budgetMint: { value: input.budgetMint ?? null, isWritable: false }, acpState: { value: input.acpState ?? null, isWritable: false }, hookProgram: { value: input.hookProgram ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, @@ -200,7 +190,6 @@ export async function getSetBudgetInstructionAsync< accounts: [ getAccountMeta(accounts.caller), getAccountMeta(accounts.job), - getAccountMeta(accounts.budgetMint), getAccountMeta(accounts.acpState), getAccountMeta(accounts.hookProgram), getAccountMeta(accounts.hookWhitelist), @@ -213,7 +202,6 @@ export async function getSetBudgetInstructionAsync< TProgramAddress, TAccountCaller, TAccountJob, - TAccountBudgetMint, TAccountAcpState, TAccountHookProgram, TAccountHookWhitelist @@ -223,14 +211,12 @@ export async function getSetBudgetInstructionAsync< export type SetBudgetInput< TAccountCaller extends string = string, TAccountJob extends string = string, - TAccountBudgetMint extends string = string, TAccountAcpState extends string = string, TAccountHookProgram extends string = string, TAccountHookWhitelist extends string = string, > = { caller: TransactionSigner; job: Address; - budgetMint: Address; acpState: Address; hookProgram?: Address; hookWhitelist?: Address; @@ -241,7 +227,6 @@ export type SetBudgetInput< export function getSetBudgetInstruction< TAccountCaller extends string, TAccountJob extends string, - TAccountBudgetMint extends string, TAccountAcpState extends string, TAccountHookProgram extends string, TAccountHookWhitelist extends string, @@ -250,7 +235,6 @@ export function getSetBudgetInstruction< input: SetBudgetInput< TAccountCaller, TAccountJob, - TAccountBudgetMint, TAccountAcpState, TAccountHookProgram, TAccountHookWhitelist @@ -260,7 +244,6 @@ export function getSetBudgetInstruction< TProgramAddress, TAccountCaller, TAccountJob, - TAccountBudgetMint, TAccountAcpState, TAccountHookProgram, TAccountHookWhitelist @@ -273,7 +256,6 @@ export function getSetBudgetInstruction< const originalAccounts = { caller: { value: input.caller ?? null, isWritable: false }, job: { value: input.job ?? null, isWritable: true }, - budgetMint: { value: input.budgetMint ?? null, isWritable: false }, acpState: { value: input.acpState ?? null, isWritable: false }, hookProgram: { value: input.hookProgram ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, @@ -291,7 +273,6 @@ export function getSetBudgetInstruction< accounts: [ getAccountMeta(accounts.caller), getAccountMeta(accounts.job), - getAccountMeta(accounts.budgetMint), getAccountMeta(accounts.acpState), getAccountMeta(accounts.hookProgram), getAccountMeta(accounts.hookWhitelist), @@ -304,7 +285,6 @@ export function getSetBudgetInstruction< TProgramAddress, TAccountCaller, TAccountJob, - TAccountBudgetMint, TAccountAcpState, TAccountHookProgram, TAccountHookWhitelist @@ -319,10 +299,9 @@ export type ParsedSetBudgetInstruction< accounts: { caller: TAccountMetas[0]; job: TAccountMetas[1]; - budgetMint: TAccountMetas[2]; - acpState: TAccountMetas[3]; - hookProgram?: TAccountMetas[4] | undefined; - hookWhitelist?: TAccountMetas[5] | undefined; + acpState: TAccountMetas[2]; + hookProgram?: TAccountMetas[3] | undefined; + hookWhitelist?: TAccountMetas[4] | undefined; }; data: SetBudgetInstructionData; }; @@ -335,7 +314,7 @@ export function parseSetBudgetInstruction< InstructionWithAccounts & InstructionWithData, ): ParsedSetBudgetInstruction { - if (instruction.accounts.length < 6) { + if (instruction.accounts.length < 5) { // TODO: Coded error. throw new Error("Not enough accounts"); } @@ -356,7 +335,6 @@ export function parseSetBudgetInstruction< accounts: { caller: getNextAccount(), job: getNextAccount(), - budgetMint: getNextAccount(), acpState: getNextAccount(), hookProgram: getNextOptionalAccount(), hookWhitelist: getNextOptionalAccount(), diff --git a/src/core/solana/generated/acp/instructions/setPaymentToken.ts b/src/core/solana/generated/acp/instructions/setPaymentToken.ts new file mode 100644 index 0000000..647db99 --- /dev/null +++ b/src/core/solana/generated/acp/instructions/setPaymentToken.ts @@ -0,0 +1,282 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + combineCodec, + fixDecoderSize, + fixEncoderSize, + getBytesDecoder, + getBytesEncoder, + getStructDecoder, + getStructEncoder, + transformEncoder, + type AccountMeta, + type AccountSignerMeta, + type Address, + type FixedSizeCodec, + type FixedSizeDecoder, + type FixedSizeEncoder, + type Instruction, + type InstructionWithAccounts, + type InstructionWithData, + type ReadonlyAccount, + type ReadonlySignerAccount, + type ReadonlyUint8Array, + type TransactionSigner, + type WritableAccount, +} from "@solana/kit"; +import { findAcpStatePda } from "../pdas/index.js"; +import { AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS } from "../programs/index.js"; +import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; + +export const SET_PAYMENT_TOKEN_DISCRIMINATOR = new Uint8Array([ + 155, 213, 140, 249, 53, 59, 20, 5, +]); + +export function getSetPaymentTokenDiscriminatorBytes() { + return fixEncoderSize(getBytesEncoder(), 8).encode( + SET_PAYMENT_TOKEN_DISCRIMINATOR, + ); +} + +export type SetPaymentTokenInstruction< + TProgram extends string = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, + TAccountAuthority extends string | AccountMeta = string, + TAccountAcpState extends string | AccountMeta = string, + TAccountPaymentToken extends string | AccountMeta = string, + TRemainingAccounts extends readonly AccountMeta[] = [], +> = Instruction & + InstructionWithData & + InstructionWithAccounts< + [ + TAccountAuthority extends string + ? ReadonlySignerAccount & + AccountSignerMeta + : TAccountAuthority, + TAccountAcpState extends string + ? WritableAccount + : TAccountAcpState, + TAccountPaymentToken extends string + ? ReadonlyAccount + : TAccountPaymentToken, + ...TRemainingAccounts, + ] + >; + +export type SetPaymentTokenInstructionData = { + discriminator: ReadonlyUint8Array; +}; + +export type SetPaymentTokenInstructionDataArgs = {}; + +export function getSetPaymentTokenInstructionDataEncoder(): FixedSizeEncoder { + return transformEncoder( + getStructEncoder([["discriminator", fixEncoderSize(getBytesEncoder(), 8)]]), + (value) => ({ ...value, discriminator: SET_PAYMENT_TOKEN_DISCRIMINATOR }), + ); +} + +export function getSetPaymentTokenInstructionDataDecoder(): FixedSizeDecoder { + return getStructDecoder([ + ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], + ]); +} + +export function getSetPaymentTokenInstructionDataCodec(): FixedSizeCodec< + SetPaymentTokenInstructionDataArgs, + SetPaymentTokenInstructionData +> { + return combineCodec( + getSetPaymentTokenInstructionDataEncoder(), + getSetPaymentTokenInstructionDataDecoder(), + ); +} + +export type SetPaymentTokenAsyncInput< + TAccountAuthority extends string = string, + TAccountAcpState extends string = string, + TAccountPaymentToken extends string = string, +> = { + authority: TransactionSigner; + acpState?: Address; + /** + * Typed as `Mint` so the new value must be a real SPL mint that exists, + * the same guarantee `initialize` gives. This also makes a zero-address + * value unrepresentable, so no explicit check is needed. + */ + paymentToken: Address; +}; + +export async function getSetPaymentTokenInstructionAsync< + TAccountAuthority extends string, + TAccountAcpState extends string, + TAccountPaymentToken extends string, + TProgramAddress extends Address = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, +>( + input: SetPaymentTokenAsyncInput< + TAccountAuthority, + TAccountAcpState, + TAccountPaymentToken + >, + config?: { programAddress?: TProgramAddress }, +): Promise< + SetPaymentTokenInstruction< + TProgramAddress, + TAccountAuthority, + TAccountAcpState, + TAccountPaymentToken + > +> { + // Program address. + const programAddress = + config?.programAddress ?? AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + authority: { value: input.authority ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: true }, + paymentToken: { value: input.paymentToken ?? null, isWritable: false }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Resolve default values. + if (!accounts.acpState.value) { + accounts.acpState.value = await findAcpStatePda(); + } + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.authority), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.paymentToken), + ], + data: getSetPaymentTokenInstructionDataEncoder().encode({}), + programAddress, + } as SetPaymentTokenInstruction< + TProgramAddress, + TAccountAuthority, + TAccountAcpState, + TAccountPaymentToken + >); +} + +export type SetPaymentTokenInput< + TAccountAuthority extends string = string, + TAccountAcpState extends string = string, + TAccountPaymentToken extends string = string, +> = { + authority: TransactionSigner; + acpState: Address; + /** + * Typed as `Mint` so the new value must be a real SPL mint that exists, + * the same guarantee `initialize` gives. This also makes a zero-address + * value unrepresentable, so no explicit check is needed. + */ + paymentToken: Address; +}; + +export function getSetPaymentTokenInstruction< + TAccountAuthority extends string, + TAccountAcpState extends string, + TAccountPaymentToken extends string, + TProgramAddress extends Address = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, +>( + input: SetPaymentTokenInput< + TAccountAuthority, + TAccountAcpState, + TAccountPaymentToken + >, + config?: { programAddress?: TProgramAddress }, +): SetPaymentTokenInstruction< + TProgramAddress, + TAccountAuthority, + TAccountAcpState, + TAccountPaymentToken +> { + // Program address. + const programAddress = + config?.programAddress ?? AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + authority: { value: input.authority ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: true }, + paymentToken: { value: input.paymentToken ?? null, isWritable: false }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.authority), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.paymentToken), + ], + data: getSetPaymentTokenInstructionDataEncoder().encode({}), + programAddress, + } as SetPaymentTokenInstruction< + TProgramAddress, + TAccountAuthority, + TAccountAcpState, + TAccountPaymentToken + >); +} + +export type ParsedSetPaymentTokenInstruction< + TProgram extends string = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, + TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[], +> = { + programAddress: Address; + accounts: { + authority: TAccountMetas[0]; + acpState: TAccountMetas[1]; + /** + * Typed as `Mint` so the new value must be a real SPL mint that exists, + * the same guarantee `initialize` gives. This also makes a zero-address + * value unrepresentable, so no explicit check is needed. + */ + paymentToken: TAccountMetas[2]; + }; + data: SetPaymentTokenInstructionData; +}; + +export function parseSetPaymentTokenInstruction< + TProgram extends string, + TAccountMetas extends readonly AccountMeta[], +>( + instruction: Instruction & + InstructionWithAccounts & + InstructionWithData, +): ParsedSetPaymentTokenInstruction { + if (instruction.accounts.length < 3) { + // TODO: Coded error. + throw new Error("Not enough accounts"); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { + authority: getNextAccount(), + acpState: getNextAccount(), + paymentToken: getNextAccount(), + }, + data: getSetPaymentTokenInstructionDataDecoder().decode(instruction.data), + }; +} diff --git a/src/core/solana/generated/acp/instructions/setSponsor.ts b/src/core/solana/generated/acp/instructions/setSponsor.ts new file mode 100644 index 0000000..ef262d3 --- /dev/null +++ b/src/core/solana/generated/acp/instructions/setSponsor.ts @@ -0,0 +1,244 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + combineCodec, + fixDecoderSize, + fixEncoderSize, + getAddressDecoder, + getAddressEncoder, + getBytesDecoder, + getBytesEncoder, + getStructDecoder, + getStructEncoder, + transformEncoder, + type AccountMeta, + type AccountSignerMeta, + type Address, + type FixedSizeCodec, + type FixedSizeDecoder, + type FixedSizeEncoder, + type Instruction, + type InstructionWithAccounts, + type InstructionWithData, + type ReadonlySignerAccount, + type ReadonlyUint8Array, + type TransactionSigner, + type WritableAccount, +} from "@solana/kit"; +import { findAcpStatePda } from "../pdas/index.js"; +import { AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS } from "../programs/index.js"; +import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; + +export const SET_SPONSOR_DISCRIMINATOR = new Uint8Array([ + 217, 184, 101, 32, 234, 70, 199, 138, +]); + +export function getSetSponsorDiscriminatorBytes() { + return fixEncoderSize(getBytesEncoder(), 8).encode(SET_SPONSOR_DISCRIMINATOR); +} + +export type SetSponsorInstruction< + TProgram extends string = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, + TAccountAuthority extends string | AccountMeta = string, + TAccountAcpState extends string | AccountMeta = string, + TRemainingAccounts extends readonly AccountMeta[] = [], +> = Instruction & + InstructionWithData & + InstructionWithAccounts< + [ + TAccountAuthority extends string + ? ReadonlySignerAccount & + AccountSignerMeta + : TAccountAuthority, + TAccountAcpState extends string + ? WritableAccount + : TAccountAcpState, + ...TRemainingAccounts, + ] + >; + +export type SetSponsorInstructionData = { + discriminator: ReadonlyUint8Array; + sponsor: Address; +}; + +export type SetSponsorInstructionDataArgs = { sponsor: Address }; + +export function getSetSponsorInstructionDataEncoder(): FixedSizeEncoder { + return transformEncoder( + getStructEncoder([ + ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], + ["sponsor", getAddressEncoder()], + ]), + (value) => ({ ...value, discriminator: SET_SPONSOR_DISCRIMINATOR }), + ); +} + +export function getSetSponsorInstructionDataDecoder(): FixedSizeDecoder { + return getStructDecoder([ + ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], + ["sponsor", getAddressDecoder()], + ]); +} + +export function getSetSponsorInstructionDataCodec(): FixedSizeCodec< + SetSponsorInstructionDataArgs, + SetSponsorInstructionData +> { + return combineCodec( + getSetSponsorInstructionDataEncoder(), + getSetSponsorInstructionDataDecoder(), + ); +} + +export type SetSponsorAsyncInput< + TAccountAuthority extends string = string, + TAccountAcpState extends string = string, +> = { + authority: TransactionSigner; + acpState?: Address; + sponsor: SetSponsorInstructionDataArgs["sponsor"]; +}; + +export async function getSetSponsorInstructionAsync< + TAccountAuthority extends string, + TAccountAcpState extends string, + TProgramAddress extends Address = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, +>( + input: SetSponsorAsyncInput, + config?: { programAddress?: TProgramAddress }, +): Promise< + SetSponsorInstruction +> { + // Program address. + const programAddress = + config?.programAddress ?? AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + authority: { value: input.authority ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: true }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + // Resolve default values. + if (!accounts.acpState.value) { + accounts.acpState.value = await findAcpStatePda(); + } + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.authority), + getAccountMeta(accounts.acpState), + ], + data: getSetSponsorInstructionDataEncoder().encode( + args as SetSponsorInstructionDataArgs, + ), + programAddress, + } as SetSponsorInstruction< + TProgramAddress, + TAccountAuthority, + TAccountAcpState + >); +} + +export type SetSponsorInput< + TAccountAuthority extends string = string, + TAccountAcpState extends string = string, +> = { + authority: TransactionSigner; + acpState: Address; + sponsor: SetSponsorInstructionDataArgs["sponsor"]; +}; + +export function getSetSponsorInstruction< + TAccountAuthority extends string, + TAccountAcpState extends string, + TProgramAddress extends Address = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, +>( + input: SetSponsorInput, + config?: { programAddress?: TProgramAddress }, +): SetSponsorInstruction { + // Program address. + const programAddress = + config?.programAddress ?? AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + authority: { value: input.authority ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: true }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.authority), + getAccountMeta(accounts.acpState), + ], + data: getSetSponsorInstructionDataEncoder().encode( + args as SetSponsorInstructionDataArgs, + ), + programAddress, + } as SetSponsorInstruction< + TProgramAddress, + TAccountAuthority, + TAccountAcpState + >); +} + +export type ParsedSetSponsorInstruction< + TProgram extends string = typeof AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS, + TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[], +> = { + programAddress: Address; + accounts: { + authority: TAccountMetas[0]; + acpState: TAccountMetas[1]; + }; + data: SetSponsorInstructionData; +}; + +export function parseSetSponsorInstruction< + TProgram extends string, + TAccountMetas extends readonly AccountMeta[], +>( + instruction: Instruction & + InstructionWithAccounts & + InstructionWithData, +): ParsedSetSponsorInstruction { + if (instruction.accounts.length < 2) { + // TODO: Coded error. + throw new Error("Not enough accounts"); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { authority: getNextAccount(), acpState: getNextAccount() }, + data: getSetSponsorInstructionDataDecoder().decode(instruction.data), + }; +} diff --git a/src/core/solana/generated/acp/instructions/submit.ts b/src/core/solana/generated/acp/instructions/submit.ts index a8af26f..7a3fb1c 100644 --- a/src/core/solana/generated/acp/instructions/submit.ts +++ b/src/core/solana/generated/acp/instructions/submit.ts @@ -34,9 +34,13 @@ import { type TransactionSigner, type WritableAccount, } from "@solana/kit"; -import { findAcpStatePda } from "../pdas/index.js"; +import { findAcpStatePda, findVaultPda } from "../pdas/index.js"; import { AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS } from "../programs/index.js"; -import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; +import { + expectAddress, + getAccountMetaFactory, + type ResolvedAccount, +} from "../shared/index.js"; export const SUBMIT_DISCRIMINATOR = new Uint8Array([ 88, 166, 102, 181, 162, 127, 170, 48, @@ -55,7 +59,7 @@ export type SubmitInstruction< TAccountVaultAuthority extends string | AccountMeta = string, TAccountProviderTokenAccount extends string | AccountMeta = string, TAccountTreasuryTokenAccount extends string | AccountMeta = string, - TAccountPlatformTreasury extends string | AccountMeta = string, + TAccountSponsor extends string | AccountMeta = string, TAccountTokenProgram extends string | AccountMeta = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", TAccountHookProgram extends string | AccountMeta = string, @@ -89,9 +93,9 @@ export type SubmitInstruction< TAccountTreasuryTokenAccount extends string ? WritableAccount : TAccountTreasuryTokenAccount, - TAccountPlatformTreasury extends string - ? WritableAccount - : TAccountPlatformTreasury, + TAccountSponsor extends string + ? WritableAccount + : TAccountSponsor, TAccountTokenProgram extends string ? ReadonlyAccount : TAccountTokenProgram, @@ -172,7 +176,7 @@ export type SubmitAsyncInput< TAccountVaultAuthority extends string = string, TAccountProviderTokenAccount extends string = string, TAccountTreasuryTokenAccount extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, TAccountTokenProgram extends string = string, TAccountHookProgram extends string = string, TAccountHookWhitelist extends string = string, @@ -190,7 +194,8 @@ export type SubmitAsyncInput< providerTokenAccount?: Address; /** Optional: required for auto-complete when budget > 0 */ treasuryTokenAccount?: Address; - platformTreasury?: Address; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: Address; tokenProgram?: Address; hookProgram?: Address; hookWhitelist?: Address; @@ -219,7 +224,7 @@ export async function getSubmitInstructionAsync< TAccountVaultAuthority extends string, TAccountProviderTokenAccount extends string, TAccountTreasuryTokenAccount extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TAccountTokenProgram extends string, TAccountHookProgram extends string, TAccountHookWhitelist extends string, @@ -236,7 +241,7 @@ export async function getSubmitInstructionAsync< TAccountVaultAuthority, TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist, @@ -255,7 +260,7 @@ export async function getSubmitInstructionAsync< TAccountVaultAuthority, TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist, @@ -283,10 +288,7 @@ export async function getSubmitInstructionAsync< value: input.treasuryTokenAccount ?? null, isWritable: true, }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, hookProgram: { value: input.hookProgram ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, @@ -312,6 +314,11 @@ export async function getSubmitInstructionAsync< if (!accounts.acpState.value) { accounts.acpState.value = await findAcpStatePda(); } + if (!accounts.vault.value) { + accounts.vault.value = await findVaultPda({ + job: expectAddress(accounts.job.value), + }); + } if (!accounts.tokenProgram.value) { accounts.tokenProgram.value = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">; @@ -327,7 +334,7 @@ export async function getSubmitInstructionAsync< getAccountMeta(accounts.vaultAuthority), getAccountMeta(accounts.providerTokenAccount), getAccountMeta(accounts.treasuryTokenAccount), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), getAccountMeta(accounts.tokenProgram), getAccountMeta(accounts.hookProgram), getAccountMeta(accounts.hookWhitelist), @@ -348,7 +355,7 @@ export async function getSubmitInstructionAsync< TAccountVaultAuthority, TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist, @@ -366,7 +373,7 @@ export type SubmitInput< TAccountVaultAuthority extends string = string, TAccountProviderTokenAccount extends string = string, TAccountTreasuryTokenAccount extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, TAccountTokenProgram extends string = string, TAccountHookProgram extends string = string, TAccountHookWhitelist extends string = string, @@ -384,7 +391,8 @@ export type SubmitInput< providerTokenAccount?: Address; /** Optional: required for auto-complete when budget > 0 */ treasuryTokenAccount?: Address; - platformTreasury?: Address; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: Address; tokenProgram?: Address; hookProgram?: Address; hookWhitelist?: Address; @@ -413,7 +421,7 @@ export function getSubmitInstruction< TAccountVaultAuthority extends string, TAccountProviderTokenAccount extends string, TAccountTreasuryTokenAccount extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TAccountTokenProgram extends string, TAccountHookProgram extends string, TAccountHookWhitelist extends string, @@ -430,7 +438,7 @@ export function getSubmitInstruction< TAccountVaultAuthority, TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist, @@ -448,7 +456,7 @@ export function getSubmitInstruction< TAccountVaultAuthority, TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist, @@ -475,10 +483,7 @@ export function getSubmitInstruction< value: input.treasuryTokenAccount ?? null, isWritable: true, }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, hookProgram: { value: input.hookProgram ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, @@ -516,7 +521,7 @@ export function getSubmitInstruction< getAccountMeta(accounts.vaultAuthority), getAccountMeta(accounts.providerTokenAccount), getAccountMeta(accounts.treasuryTokenAccount), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), getAccountMeta(accounts.tokenProgram), getAccountMeta(accounts.hookProgram), getAccountMeta(accounts.hookWhitelist), @@ -537,7 +542,7 @@ export function getSubmitInstruction< TAccountVaultAuthority, TAccountProviderTokenAccount, TAccountTreasuryTokenAccount, - TAccountPlatformTreasury, + TAccountSponsor, TAccountTokenProgram, TAccountHookProgram, TAccountHookWhitelist, @@ -563,7 +568,8 @@ export type ParsedSubmitInstruction< providerTokenAccount?: TAccountMetas[5] | undefined; /** Optional: required for auto-complete when budget > 0 */ treasuryTokenAccount?: TAccountMetas[6] | undefined; - platformTreasury?: TAccountMetas[7] | undefined; + /** acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees. */ + sponsor?: TAccountMetas[7] | undefined; tokenProgram?: TAccountMetas[8] | undefined; hookProgram?: TAccountMetas[9] | undefined; hookWhitelist?: TAccountMetas[10] | undefined; @@ -617,7 +623,7 @@ export function parseSubmitInstruction< vaultAuthority: getNextOptionalAccount(), providerTokenAccount: getNextOptionalAccount(), treasuryTokenAccount: getNextOptionalAccount(), - platformTreasury: getNextOptionalAccount(), + sponsor: getNextOptionalAccount(), tokenProgram: getNextOptionalAccount(), hookProgram: getNextOptionalAccount(), hookWhitelist: getNextOptionalAccount(), diff --git a/src/core/solana/generated/acp/pdas/acpState.ts b/src/core/solana/generated/acp/pdas/acpState.ts index 446f8a6..cacd8ce 100644 --- a/src/core/solana/generated/acp/pdas/acpState.ts +++ b/src/core/solana/generated/acp/pdas/acpState.ts @@ -16,7 +16,9 @@ import { export async function findAcpStatePda( config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y" as Address<"FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/acp/pdas/hookWhitelist.ts b/src/core/solana/generated/acp/pdas/hookWhitelist.ts index c333ece..7fd307b 100644 --- a/src/core/solana/generated/acp/pdas/hookWhitelist.ts +++ b/src/core/solana/generated/acp/pdas/hookWhitelist.ts @@ -22,7 +22,9 @@ export async function findHookWhitelistPda( seeds: HookWhitelistSeeds, config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y" as Address<"FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/acp/pdas/index.ts b/src/core/solana/generated/acp/pdas/index.ts index 1a134e2..87aae86 100644 --- a/src/core/solana/generated/acp/pdas/index.ts +++ b/src/core/solana/generated/acp/pdas/index.ts @@ -8,3 +8,5 @@ export * from "./acpState.js"; export * from "./hookWhitelist.js"; +export * from "./job.js"; +export * from "./vault.js"; diff --git a/src/core/solana/generated/acp/pdas/job.ts b/src/core/solana/generated/acp/pdas/job.ts new file mode 100644 index 0000000..8dcaf31 --- /dev/null +++ b/src/core/solana/generated/acp/pdas/job.ts @@ -0,0 +1,38 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + getAddressEncoder, + getBytesEncoder, + getProgramDerivedAddress, + getU64Encoder, + type Address, + type ProgramDerivedAddress, +} from "@solana/kit"; + +export type JobSeeds = { + client: Address; + seed: number | bigint; +}; + +export async function findJobPda( + seeds: JobSeeds, + config: { programAddress?: Address | undefined } = {}, +): Promise { + const { + programAddress = "FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y" as Address<"FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y">, + } = config; + return await getProgramDerivedAddress({ + programAddress, + seeds: [ + getBytesEncoder().encode(new Uint8Array([106, 111, 98])), + getAddressEncoder().encode(seeds.client), + getU64Encoder().encode(seeds.seed), + ], + }); +} diff --git a/src/core/solana/generated/acp/pdas/vault.ts b/src/core/solana/generated/acp/pdas/vault.ts new file mode 100644 index 0000000..78712b0 --- /dev/null +++ b/src/core/solana/generated/acp/pdas/vault.ts @@ -0,0 +1,35 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + getAddressEncoder, + getBytesEncoder, + getProgramDerivedAddress, + type Address, + type ProgramDerivedAddress, +} from "@solana/kit"; + +export type VaultSeeds = { + job: Address; +}; + +export async function findVaultPda( + seeds: VaultSeeds, + config: { programAddress?: Address | undefined } = {}, +): Promise { + const { + programAddress = "FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y" as Address<"FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y">, + } = config; + return await getProgramDerivedAddress({ + programAddress, + seeds: [ + getBytesEncoder().encode(new Uint8Array([118, 97, 117, 108, 116])), + getAddressEncoder().encode(seeds.job), + ], + }); +} diff --git a/src/core/solana/generated/acp/programs/agenticCommerceV3.ts b/src/core/solana/generated/acp/programs/agenticCommerceV3.ts index 972f8a0..8871843 100644 --- a/src/core/solana/generated/acp/programs/agenticCommerceV3.ts +++ b/src/core/solana/generated/acp/programs/agenticCommerceV3.ts @@ -32,8 +32,10 @@ import { parseSetBudgetInstruction, parseSetEvaluatorFeeInstruction, parseSetPauseInstruction, + parseSetPaymentTokenInstruction, parseSetPlatformFeeInstruction, parseSetProviderInstruction, + parseSetSponsorInstruction, parseSubmitInstruction, type ParsedAcceptAuthorityInstruction, type ParsedAddHookWhitelistInstruction, @@ -50,12 +52,15 @@ import { type ParsedSetBudgetInstruction, type ParsedSetEvaluatorFeeInstruction, type ParsedSetPauseInstruction, + type ParsedSetPaymentTokenInstruction, type ParsedSetPlatformFeeInstruction, type ParsedSetProviderInstruction, + type ParsedSetSponsorInstruction, type ParsedSubmitInstruction, } from "../instructions/index.js"; -export const AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS = "" as Address<"">; +export const AGENTIC_COMMERCE_V3_PROGRAM_ADDRESS = + "FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y" as Address<"FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y">; export enum AgenticCommerceV3Account { AcpState, @@ -121,8 +126,10 @@ export enum AgenticCommerceV3Instruction { SetBudget, SetEvaluatorFee, SetPause, + SetPaymentToken, SetPlatformFee, SetProvider, + SetSponsor, Submit, } @@ -295,6 +302,17 @@ export function identifyAgenticCommerceV3Instruction( ) { return AgenticCommerceV3Instruction.SetPause; } + if ( + containsBytes( + data, + fixEncoderSize(getBytesEncoder(), 8).encode( + new Uint8Array([155, 213, 140, 249, 53, 59, 20, 5]), + ), + 0, + ) + ) { + return AgenticCommerceV3Instruction.SetPaymentToken; + } if ( containsBytes( data, @@ -317,6 +335,17 @@ export function identifyAgenticCommerceV3Instruction( ) { return AgenticCommerceV3Instruction.SetProvider; } + if ( + containsBytes( + data, + fixEncoderSize(getBytesEncoder(), 8).encode( + new Uint8Array([217, 184, 101, 32, 234, 70, 199, 138]), + ), + 0, + ) + ) { + return AgenticCommerceV3Instruction.SetSponsor; + } if ( containsBytes( data, @@ -333,7 +362,9 @@ export function identifyAgenticCommerceV3Instruction( ); } -export type ParsedAgenticCommerceV3Instruction = +export type ParsedAgenticCommerceV3Instruction< + TProgram extends string = "FVd3tKVfUWH7DDPrUodQqv6uJT2efd6Bw8mYuiUWFf8Y", +> = | ({ instructionType: AgenticCommerceV3Instruction.AcceptAuthority; } & ParsedAcceptAuthorityInstruction) @@ -379,12 +410,18 @@ export type ParsedAgenticCommerceV3Instruction = | ({ instructionType: AgenticCommerceV3Instruction.SetPause; } & ParsedSetPauseInstruction) + | ({ + instructionType: AgenticCommerceV3Instruction.SetPaymentToken; + } & ParsedSetPaymentTokenInstruction) | ({ instructionType: AgenticCommerceV3Instruction.SetPlatformFee; } & ParsedSetPlatformFeeInstruction) | ({ instructionType: AgenticCommerceV3Instruction.SetProvider; } & ParsedSetProviderInstruction) + | ({ + instructionType: AgenticCommerceV3Instruction.SetSponsor; + } & ParsedSetSponsorInstruction) | ({ instructionType: AgenticCommerceV3Instruction.Submit; } & ParsedSubmitInstruction); @@ -499,6 +536,13 @@ export function parseAgenticCommerceV3Instruction( ...parseSetPauseInstruction(instruction), }; } + case AgenticCommerceV3Instruction.SetPaymentToken: { + assertIsInstructionWithAccounts(instruction); + return { + instructionType: AgenticCommerceV3Instruction.SetPaymentToken, + ...parseSetPaymentTokenInstruction(instruction), + }; + } case AgenticCommerceV3Instruction.SetPlatformFee: { assertIsInstructionWithAccounts(instruction); return { @@ -513,6 +557,13 @@ export function parseAgenticCommerceV3Instruction( ...parseSetProviderInstruction(instruction), }; } + case AgenticCommerceV3Instruction.SetSponsor: { + assertIsInstructionWithAccounts(instruction); + return { + instructionType: AgenticCommerceV3Instruction.SetSponsor, + ...parseSetSponsorInstruction(instruction), + }; + } case AgenticCommerceV3Instruction.Submit: { assertIsInstructionWithAccounts(instruction); return { diff --git a/src/core/solana/generated/acp/types/budgetSet.ts b/src/core/solana/generated/acp/types/budgetSet.ts index a4456b8..5f60971 100644 --- a/src/core/solana/generated/acp/types/budgetSet.ts +++ b/src/core/solana/generated/acp/types/budgetSet.ts @@ -8,29 +8,32 @@ import { combineCodec, + getAddressDecoder, + getAddressEncoder, getStructDecoder, getStructEncoder, getU64Decoder, getU64Encoder, + type Address, type FixedSizeCodec, type FixedSizeDecoder, type FixedSizeEncoder, } from "@solana/kit"; -export type BudgetSet = { jobId: bigint; amount: bigint }; +export type BudgetSet = { job: Address; amount: bigint }; -export type BudgetSetArgs = { jobId: number | bigint; amount: number | bigint }; +export type BudgetSetArgs = { job: Address; amount: number | bigint }; export function getBudgetSetEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["amount", getU64Encoder()], ]); } export function getBudgetSetDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["amount", getU64Decoder()], ]); } diff --git a/src/core/solana/generated/acp/types/evaluatorFeePaid.ts b/src/core/solana/generated/acp/types/evaluatorFeePaid.ts index 8817d26..075b2cb 100644 --- a/src/core/solana/generated/acp/types/evaluatorFeePaid.ts +++ b/src/core/solana/generated/acp/types/evaluatorFeePaid.ts @@ -21,20 +21,20 @@ import { } from "@solana/kit"; export type EvaluatorFeePaid = { - jobId: bigint; + job: Address; evaluator: Address; amount: bigint; }; export type EvaluatorFeePaidArgs = { - jobId: number | bigint; + job: Address; evaluator: Address; amount: number | bigint; }; export function getEvaluatorFeePaidEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["evaluator", getAddressEncoder()], ["amount", getU64Encoder()], ]); @@ -42,7 +42,7 @@ export function getEvaluatorFeePaidEncoder(): FixedSizeEncoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["evaluator", getAddressDecoder()], ["amount", getU64Decoder()], ]); diff --git a/src/core/solana/generated/acp/types/hookDetached.ts b/src/core/solana/generated/acp/types/hookDetached.ts index dcd46ff..1337397 100644 --- a/src/core/solana/generated/acp/types/hookDetached.ts +++ b/src/core/solana/generated/acp/types/hookDetached.ts @@ -14,8 +14,6 @@ import { getOptionEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, type Address, type Codec, type Decoder, @@ -25,27 +23,27 @@ import { } from "@solana/kit"; export type HookDetached = { - jobId: bigint; + job: Address; /** The hook address that was detached, captured before it is cleared to None. */ hookAddress: Option
; }; export type HookDetachedArgs = { - jobId: number | bigint; + job: Address; /** The hook address that was detached, captured before it is cleared to None. */ hookAddress: OptionOrNullable
; }; export function getHookDetachedEncoder(): Encoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["hookAddress", getOptionEncoder(getAddressEncoder())], ]); } export function getHookDetachedDecoder(): Decoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["hookAddress", getOptionDecoder(getAddressDecoder())], ]); } diff --git a/src/core/solana/generated/acp/types/index.ts b/src/core/solana/generated/acp/types/index.ts index 3c63cb8..f530592 100644 --- a/src/core/solana/generated/acp/types/index.ts +++ b/src/core/solana/generated/acp/types/index.ts @@ -22,6 +22,8 @@ export * from "./jobState.js"; export * from "./jobSubmitted.js"; export * from "./pauseSet.js"; export * from "./paymentReleased.js"; +export * from "./paymentTokenUpdated.js"; export * from "./platformFeeUpdated.js"; export * from "./providerSet.js"; export * from "./refunded.js"; +export * from "./sponsorUpdated.js"; diff --git a/src/core/solana/generated/acp/types/jobCompleted.ts b/src/core/solana/generated/acp/types/jobCompleted.ts index 84d12d4..58167ef 100644 --- a/src/core/solana/generated/acp/types/jobCompleted.ts +++ b/src/core/solana/generated/acp/types/jobCompleted.ts @@ -16,8 +16,6 @@ import { getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, type Address, type FixedSizeCodec, type FixedSizeDecoder, @@ -26,20 +24,16 @@ import { } from "@solana/kit"; export type JobCompleted = { - jobId: bigint; + job: Address; evaluator: Address; reason: ReadonlyUint8Array; }; -export type JobCompletedArgs = { - jobId: number | bigint; - evaluator: Address; - reason: ReadonlyUint8Array; -}; +export type JobCompletedArgs = JobCompleted; export function getJobCompletedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["evaluator", getAddressEncoder()], ["reason", fixEncoderSize(getBytesEncoder(), 32)], ]); @@ -47,7 +41,7 @@ export function getJobCompletedEncoder(): FixedSizeEncoder { export function getJobCompletedDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["evaluator", getAddressDecoder()], ["reason", fixDecoderSize(getBytesDecoder(), 32)], ]); diff --git a/src/core/solana/generated/acp/types/jobCreated.ts b/src/core/solana/generated/acp/types/jobCreated.ts index 0dd0b4d..aac598f 100644 --- a/src/core/solana/generated/acp/types/jobCreated.ts +++ b/src/core/solana/generated/acp/types/jobCreated.ts @@ -7,6 +7,8 @@ */ import { + addDecoderSizePrefix, + addEncoderSizePrefix, combineCodec, getAddressDecoder, getAddressEncoder, @@ -14,57 +16,60 @@ import { getI64Encoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, + getU32Decoder, + getU32Encoder, + getUtf8Decoder, + getUtf8Encoder, type Address, - type FixedSizeCodec, - type FixedSizeDecoder, - type FixedSizeEncoder, + type Codec, + type Decoder, + type Encoder, } from "@solana/kit"; export type JobCreated = { - jobId: bigint; + job: Address; client: Address; provider: Address; evaluator: Address; expiredAt: bigint; hook: Address; + description: string; }; export type JobCreatedArgs = { - jobId: number | bigint; + job: Address; client: Address; provider: Address; evaluator: Address; expiredAt: number | bigint; hook: Address; + description: string; }; -export function getJobCreatedEncoder(): FixedSizeEncoder { +export function getJobCreatedEncoder(): Encoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["client", getAddressEncoder()], ["provider", getAddressEncoder()], ["evaluator", getAddressEncoder()], ["expiredAt", getI64Encoder()], ["hook", getAddressEncoder()], + ["description", addEncoderSizePrefix(getUtf8Encoder(), getU32Encoder())], ]); } -export function getJobCreatedDecoder(): FixedSizeDecoder { +export function getJobCreatedDecoder(): Decoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["client", getAddressDecoder()], ["provider", getAddressDecoder()], ["evaluator", getAddressDecoder()], ["expiredAt", getI64Decoder()], ["hook", getAddressDecoder()], + ["description", addDecoderSizePrefix(getUtf8Decoder(), getU32Decoder())], ]); } -export function getJobCreatedCodec(): FixedSizeCodec< - JobCreatedArgs, - JobCreated -> { +export function getJobCreatedCodec(): Codec { return combineCodec(getJobCreatedEncoder(), getJobCreatedDecoder()); } diff --git a/src/core/solana/generated/acp/types/jobExpired.ts b/src/core/solana/generated/acp/types/jobExpired.ts index ff0cefb..bac0047 100644 --- a/src/core/solana/generated/acp/types/jobExpired.ts +++ b/src/core/solana/generated/acp/types/jobExpired.ts @@ -8,25 +8,26 @@ import { combineCodec, + getAddressDecoder, + getAddressEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, + type Address, type FixedSizeCodec, type FixedSizeDecoder, type FixedSizeEncoder, } from "@solana/kit"; -export type JobExpired = { jobId: bigint }; +export type JobExpired = { job: Address }; -export type JobExpiredArgs = { jobId: number | bigint }; +export type JobExpiredArgs = JobExpired; export function getJobExpiredEncoder(): FixedSizeEncoder { - return getStructEncoder([["jobId", getU64Encoder()]]); + return getStructEncoder([["job", getAddressEncoder()]]); } export function getJobExpiredDecoder(): FixedSizeDecoder { - return getStructDecoder([["jobId", getU64Decoder()]]); + return getStructDecoder([["job", getAddressDecoder()]]); } export function getJobExpiredCodec(): FixedSizeCodec< diff --git a/src/core/solana/generated/acp/types/jobFunded.ts b/src/core/solana/generated/acp/types/jobFunded.ts index e3e9127..de63350 100644 --- a/src/core/solana/generated/acp/types/jobFunded.ts +++ b/src/core/solana/generated/acp/types/jobFunded.ts @@ -20,17 +20,17 @@ import { type FixedSizeEncoder, } from "@solana/kit"; -export type JobFunded = { jobId: bigint; client: Address; amount: bigint }; +export type JobFunded = { job: Address; client: Address; amount: bigint }; export type JobFundedArgs = { - jobId: number | bigint; + job: Address; client: Address; amount: number | bigint; }; export function getJobFundedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["client", getAddressEncoder()], ["amount", getU64Encoder()], ]); @@ -38,7 +38,7 @@ export function getJobFundedEncoder(): FixedSizeEncoder { export function getJobFundedDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["client", getAddressDecoder()], ["amount", getU64Decoder()], ]); diff --git a/src/core/solana/generated/acp/types/jobRejected.ts b/src/core/solana/generated/acp/types/jobRejected.ts index 1d60b6c..e72aa34 100644 --- a/src/core/solana/generated/acp/types/jobRejected.ts +++ b/src/core/solana/generated/acp/types/jobRejected.ts @@ -16,8 +16,6 @@ import { getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, type Address, type FixedSizeCodec, type FixedSizeDecoder, @@ -26,20 +24,16 @@ import { } from "@solana/kit"; export type JobRejected = { - jobId: bigint; + job: Address; rejector: Address; reason: ReadonlyUint8Array; }; -export type JobRejectedArgs = { - jobId: number | bigint; - rejector: Address; - reason: ReadonlyUint8Array; -}; +export type JobRejectedArgs = JobRejected; export function getJobRejectedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["rejector", getAddressEncoder()], ["reason", fixEncoderSize(getBytesEncoder(), 32)], ]); @@ -47,7 +41,7 @@ export function getJobRejectedEncoder(): FixedSizeEncoder { export function getJobRejectedDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["rejector", getAddressDecoder()], ["reason", fixDecoderSize(getBytesDecoder(), 32)], ]); diff --git a/src/core/solana/generated/acp/types/jobSubmitted.ts b/src/core/solana/generated/acp/types/jobSubmitted.ts index f4b79b1..f017491 100644 --- a/src/core/solana/generated/acp/types/jobSubmitted.ts +++ b/src/core/solana/generated/acp/types/jobSubmitted.ts @@ -16,8 +16,6 @@ import { getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, type Address, type FixedSizeCodec, type FixedSizeDecoder, @@ -26,20 +24,16 @@ import { } from "@solana/kit"; export type JobSubmitted = { - jobId: bigint; + job: Address; provider: Address; deliverable: ReadonlyUint8Array; }; -export type JobSubmittedArgs = { - jobId: number | bigint; - provider: Address; - deliverable: ReadonlyUint8Array; -}; +export type JobSubmittedArgs = JobSubmitted; export function getJobSubmittedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["provider", getAddressEncoder()], ["deliverable", fixEncoderSize(getBytesEncoder(), 32)], ]); @@ -47,7 +41,7 @@ export function getJobSubmittedEncoder(): FixedSizeEncoder { export function getJobSubmittedDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["provider", getAddressDecoder()], ["deliverable", fixDecoderSize(getBytesDecoder(), 32)], ]); diff --git a/src/core/solana/generated/acp/types/paymentReleased.ts b/src/core/solana/generated/acp/types/paymentReleased.ts index 9ff892a..e50e94d 100644 --- a/src/core/solana/generated/acp/types/paymentReleased.ts +++ b/src/core/solana/generated/acp/types/paymentReleased.ts @@ -21,20 +21,20 @@ import { } from "@solana/kit"; export type PaymentReleased = { - jobId: bigint; + job: Address; provider: Address; amount: bigint; }; export type PaymentReleasedArgs = { - jobId: number | bigint; + job: Address; provider: Address; amount: number | bigint; }; export function getPaymentReleasedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["provider", getAddressEncoder()], ["amount", getU64Encoder()], ]); @@ -42,7 +42,7 @@ export function getPaymentReleasedEncoder(): FixedSizeEncoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["provider", getAddressDecoder()], ["amount", getU64Decoder()], ]); diff --git a/src/core/solana/generated/acp/types/paymentTokenUpdated.ts b/src/core/solana/generated/acp/types/paymentTokenUpdated.ts new file mode 100644 index 0000000..aa2d631 --- /dev/null +++ b/src/core/solana/generated/acp/types/paymentTokenUpdated.ts @@ -0,0 +1,47 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + combineCodec, + getAddressDecoder, + getAddressEncoder, + getStructDecoder, + getStructEncoder, + type Address, + type FixedSizeCodec, + type FixedSizeDecoder, + type FixedSizeEncoder, +} from "@solana/kit"; + +export type PaymentTokenUpdated = { paymentToken: Address; previous: Address }; + +export type PaymentTokenUpdatedArgs = PaymentTokenUpdated; + +export function getPaymentTokenUpdatedEncoder(): FixedSizeEncoder { + return getStructEncoder([ + ["paymentToken", getAddressEncoder()], + ["previous", getAddressEncoder()], + ]); +} + +export function getPaymentTokenUpdatedDecoder(): FixedSizeDecoder { + return getStructDecoder([ + ["paymentToken", getAddressDecoder()], + ["previous", getAddressDecoder()], + ]); +} + +export function getPaymentTokenUpdatedCodec(): FixedSizeCodec< + PaymentTokenUpdatedArgs, + PaymentTokenUpdated +> { + return combineCodec( + getPaymentTokenUpdatedEncoder(), + getPaymentTokenUpdatedDecoder(), + ); +} diff --git a/src/core/solana/generated/acp/types/providerSet.ts b/src/core/solana/generated/acp/types/providerSet.ts index 2cdde4c..5b3546a 100644 --- a/src/core/solana/generated/acp/types/providerSet.ts +++ b/src/core/solana/generated/acp/types/providerSet.ts @@ -12,28 +12,26 @@ import { getAddressEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, type Address, type FixedSizeCodec, type FixedSizeDecoder, type FixedSizeEncoder, } from "@solana/kit"; -export type ProviderSet = { jobId: bigint; provider: Address }; +export type ProviderSet = { job: Address; provider: Address }; -export type ProviderSetArgs = { jobId: number | bigint; provider: Address }; +export type ProviderSetArgs = ProviderSet; export function getProviderSetEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["provider", getAddressEncoder()], ]); } export function getProviderSetDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["provider", getAddressDecoder()], ]); } diff --git a/src/core/solana/generated/acp/types/refunded.ts b/src/core/solana/generated/acp/types/refunded.ts index 3533869..4b2638c 100644 --- a/src/core/solana/generated/acp/types/refunded.ts +++ b/src/core/solana/generated/acp/types/refunded.ts @@ -20,17 +20,17 @@ import { type FixedSizeEncoder, } from "@solana/kit"; -export type Refunded = { jobId: bigint; client: Address; amount: bigint }; +export type Refunded = { job: Address; client: Address; amount: bigint }; export type RefundedArgs = { - jobId: number | bigint; + job: Address; client: Address; amount: number | bigint; }; export function getRefundedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["job", getAddressEncoder()], ["client", getAddressEncoder()], ["amount", getU64Encoder()], ]); @@ -38,7 +38,7 @@ export function getRefundedEncoder(): FixedSizeEncoder { export function getRefundedDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["job", getAddressDecoder()], ["client", getAddressDecoder()], ["amount", getU64Decoder()], ]); diff --git a/src/core/solana/generated/acp/types/sponsorUpdated.ts b/src/core/solana/generated/acp/types/sponsorUpdated.ts new file mode 100644 index 0000000..1921557 --- /dev/null +++ b/src/core/solana/generated/acp/types/sponsorUpdated.ts @@ -0,0 +1,38 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + combineCodec, + getAddressDecoder, + getAddressEncoder, + getStructDecoder, + getStructEncoder, + type Address, + type FixedSizeCodec, + type FixedSizeDecoder, + type FixedSizeEncoder, +} from "@solana/kit"; + +export type SponsorUpdated = { sponsor: Address }; + +export type SponsorUpdatedArgs = SponsorUpdated; + +export function getSponsorUpdatedEncoder(): FixedSizeEncoder { + return getStructEncoder([["sponsor", getAddressEncoder()]]); +} + +export function getSponsorUpdatedDecoder(): FixedSizeDecoder { + return getStructDecoder([["sponsor", getAddressDecoder()]]); +} + +export function getSponsorUpdatedCodec(): FixedSizeCodec< + SponsorUpdatedArgs, + SponsorUpdated +> { + return combineCodec(getSponsorUpdatedEncoder(), getSponsorUpdatedDecoder()); +} diff --git a/src/core/solana/generated/fund-transfer-hook/accounts/fundRequestIntentId.ts b/src/core/solana/generated/fund-transfer-hook/accounts/fundRequestIntentId.ts deleted file mode 100644 index 0ee86f4..0000000 --- a/src/core/solana/generated/fund-transfer-hook/accounts/fundRequestIntentId.ts +++ /dev/null @@ -1,171 +0,0 @@ -/** - * This code was AUTOGENERATED using the Codama library. - * Please DO NOT EDIT THIS FILE, instead use visitors - * to add features, then rerun Codama to update it. - * - * @see https://github.com/codama-idl/codama - */ - -import { - assertAccountExists, - assertAccountsExist, - combineCodec, - decodeAccount, - fetchEncodedAccount, - fetchEncodedAccounts, - fixDecoderSize, - fixEncoderSize, - getBytesDecoder, - getBytesEncoder, - getStructDecoder, - getStructEncoder, - getU64Decoder, - getU64Encoder, - getU8Decoder, - getU8Encoder, - transformEncoder, - type Account, - type Address, - type EncodedAccount, - type FetchAccountConfig, - type FetchAccountsConfig, - type FixedSizeCodec, - type FixedSizeDecoder, - type FixedSizeEncoder, - type MaybeAccount, - type MaybeEncodedAccount, - type ReadonlyUint8Array, -} from "@solana/kit"; - -export const FUND_REQUEST_INTENT_ID_DISCRIMINATOR = new Uint8Array([ - 115, 10, 133, 59, 77, 84, 106, 104, -]); - -export function getFundRequestIntentIdDiscriminatorBytes() { - return fixEncoderSize(getBytesEncoder(), 8).encode( - FUND_REQUEST_INTENT_ID_DISCRIMINATOR, - ); -} - -export type FundRequestIntentId = { - discriminator: ReadonlyUint8Array; - jobId: bigint; - intentId: bigint; - bump: number; -}; - -export type FundRequestIntentIdArgs = { - jobId: number | bigint; - intentId: number | bigint; - bump: number; -}; - -/** Gets the encoder for {@link FundRequestIntentIdArgs} account data. */ -export function getFundRequestIntentIdEncoder(): FixedSizeEncoder { - return transformEncoder( - getStructEncoder([ - ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], - ["jobId", getU64Encoder()], - ["intentId", getU64Encoder()], - ["bump", getU8Encoder()], - ]), - (value) => ({ - ...value, - discriminator: FUND_REQUEST_INTENT_ID_DISCRIMINATOR, - }), - ); -} - -/** Gets the decoder for {@link FundRequestIntentId} account data. */ -export function getFundRequestIntentIdDecoder(): FixedSizeDecoder { - return getStructDecoder([ - ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], - ["intentId", getU64Decoder()], - ["bump", getU8Decoder()], - ]); -} - -/** Gets the codec for {@link FundRequestIntentId} account data. */ -export function getFundRequestIntentIdCodec(): FixedSizeCodec< - FundRequestIntentIdArgs, - FundRequestIntentId -> { - return combineCodec( - getFundRequestIntentIdEncoder(), - getFundRequestIntentIdDecoder(), - ); -} - -export function decodeFundRequestIntentId( - encodedAccount: EncodedAccount, -): Account; -export function decodeFundRequestIntentId( - encodedAccount: MaybeEncodedAccount, -): MaybeAccount; -export function decodeFundRequestIntentId( - encodedAccount: EncodedAccount | MaybeEncodedAccount, -): - | Account - | MaybeAccount { - return decodeAccount( - encodedAccount as MaybeEncodedAccount, - getFundRequestIntentIdDecoder(), - ); -} - -export async function fetchFundRequestIntentId< - TAddress extends string = string, ->( - rpc: Parameters[0], - address: Address, - config?: FetchAccountConfig, -): Promise> { - const maybeAccount = await fetchMaybeFundRequestIntentId( - rpc, - address, - config, - ); - assertAccountExists(maybeAccount); - return maybeAccount; -} - -export async function fetchMaybeFundRequestIntentId< - TAddress extends string = string, ->( - rpc: Parameters[0], - address: Address, - config?: FetchAccountConfig, -): Promise> { - const maybeAccount = await fetchEncodedAccount(rpc, address, config); - return decodeFundRequestIntentId(maybeAccount); -} - -export async function fetchAllFundRequestIntentId( - rpc: Parameters[0], - addresses: Array
, - config?: FetchAccountsConfig, -): Promise[]> { - const maybeAccounts = await fetchAllMaybeFundRequestIntentId( - rpc, - addresses, - config, - ); - assertAccountsExist(maybeAccounts); - return maybeAccounts; -} - -export async function fetchAllMaybeFundRequestIntentId( - rpc: Parameters[0], - addresses: Array
, - config?: FetchAccountsConfig, -): Promise[]> { - const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config); - return maybeAccounts.map((maybeAccount) => - decodeFundRequestIntentId(maybeAccount), - ); -} - -export function getFundRequestIntentIdSize(): number { - return 25; -} diff --git a/src/core/solana/generated/fund-transfer-hook/accounts/index.ts b/src/core/solana/generated/fund-transfer-hook/accounts/index.ts index 19ab62a..a56792a 100644 --- a/src/core/solana/generated/fund-transfer-hook/accounts/index.ts +++ b/src/core/solana/generated/fund-transfer-hook/accounts/index.ts @@ -6,7 +6,6 @@ * @see https://github.com/codama-idl/codama */ -export * from "./fundRequestIntentId.js"; export * from "./hookMetadata.js"; export * from "./hookState.js"; export * from "./intent.js"; diff --git a/src/core/solana/generated/fund-transfer-hook/accounts/intent.ts b/src/core/solana/generated/fund-transfer-hook/accounts/intent.ts index f7a4eb6..76ccf66 100644 --- a/src/core/solana/generated/fund-transfer-hook/accounts/intent.ts +++ b/src/core/solana/generated/fund-transfer-hook/accounts/intent.ts @@ -52,7 +52,7 @@ export function getIntentDiscriminatorBytes() { export type Intent = { discriminator: ReadonlyUint8Array; id: bigint; - jobId: bigint; + jobKey: Address; actor: Address; isEscrow: boolean; isSigned: boolean; @@ -67,7 +67,7 @@ export type Intent = { export type IntentArgs = { id: number | bigint; - jobId: number | bigint; + jobKey: Address; actor: Address; isEscrow: boolean; isSigned: boolean; @@ -86,7 +86,7 @@ export function getIntentEncoder(): FixedSizeEncoder { getStructEncoder([ ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], ["id", getU64Encoder()], - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["actor", getAddressEncoder()], ["isEscrow", getBooleanEncoder()], ["isSigned", getBooleanEncoder()], @@ -106,7 +106,7 @@ export function getIntentDecoder(): FixedSizeDecoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], ["id", getU64Decoder()], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["actor", getAddressDecoder()], ["isEscrow", getBooleanDecoder()], ["isSigned", getBooleanDecoder()], @@ -178,5 +178,5 @@ export async function fetchAllMaybeIntent( } export function getIntentSize(): number { - return 164; + return 188; } diff --git a/src/core/solana/generated/fund-transfer-hook/accounts/providerEscrowIntentId.ts b/src/core/solana/generated/fund-transfer-hook/accounts/providerEscrowIntentId.ts index 57c6768..442e018 100644 --- a/src/core/solana/generated/fund-transfer-hook/accounts/providerEscrowIntentId.ts +++ b/src/core/solana/generated/fund-transfer-hook/accounts/providerEscrowIntentId.ts @@ -15,6 +15,8 @@ import { fetchEncodedAccounts, fixDecoderSize, fixEncoderSize, + getAddressDecoder, + getAddressEncoder, getBytesDecoder, getBytesEncoder, getStructDecoder, @@ -49,13 +51,13 @@ export function getProviderEscrowIntentIdDiscriminatorBytes() { export type ProviderEscrowIntentId = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; intentId: bigint; bump: number; }; export type ProviderEscrowIntentIdArgs = { - jobId: number | bigint; + jobKey: Address; intentId: number | bigint; bump: number; }; @@ -65,7 +67,7 @@ export function getProviderEscrowIntentIdEncoder(): FixedSizeEncoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["intentId", getU64Decoder()], ["bump", getU8Decoder()], ]); @@ -167,5 +169,5 @@ export async function fetchAllMaybeProviderEscrowIntentId( } export function getProviderEscrowIntentIdSize(): number { - return 25; + return 49; } diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/afterAction.ts b/src/core/solana/generated/fund-transfer-hook/instructions/afterAction.ts index e8328b5..063500e 100644 --- a/src/core/solana/generated/fund-transfer-hook/instructions/afterAction.ts +++ b/src/core/solana/generated/fund-transfer-hook/instructions/afterAction.ts @@ -20,8 +20,6 @@ import { getStructEncoder, getU32Decoder, getU32Encoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -67,7 +65,7 @@ export type AfterActionInstruction< export type AfterActionInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -75,7 +73,7 @@ export type AfterActionInstructionData = { }; export type AfterActionInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -86,7 +84,7 @@ export function getAfterActionInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["optParams", addDecoderSizePrefix(getBytesDecoder(), getU32Decoder())], ["caller", getAddressDecoder()], @@ -119,7 +117,7 @@ export function getAfterActionInstructionDataCodec(): Codec< export type AfterActionAsyncInput = { hookState?: Address; - jobId: AfterActionInstructionDataArgs["jobId"]; + jobKey: AfterActionInstructionDataArgs["jobKey"]; action: AfterActionInstructionDataArgs["action"]; optParams: AfterActionInstructionDataArgs["optParams"]; caller: AfterActionInstructionDataArgs["caller"]; @@ -166,7 +164,7 @@ export async function getAfterActionInstructionAsync< export type AfterActionInput = { hookState: Address; - jobId: AfterActionInstructionDataArgs["jobId"]; + jobKey: AfterActionInstructionDataArgs["jobKey"]; action: AfterActionInstructionDataArgs["action"]; optParams: AfterActionInstructionDataArgs["optParams"]; caller: AfterActionInstructionDataArgs["caller"]; diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/beforeAction.ts b/src/core/solana/generated/fund-transfer-hook/instructions/beforeAction.ts index 4e535d4..209934d 100644 --- a/src/core/solana/generated/fund-transfer-hook/instructions/beforeAction.ts +++ b/src/core/solana/generated/fund-transfer-hook/instructions/beforeAction.ts @@ -20,8 +20,6 @@ import { getStructEncoder, getU32Decoder, getU32Encoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -67,7 +65,7 @@ export type BeforeActionInstruction< export type BeforeActionInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -75,7 +73,7 @@ export type BeforeActionInstructionData = { }; export type BeforeActionInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -86,7 +84,7 @@ export function getBeforeActionInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["optParams", addDecoderSizePrefix(getBytesDecoder(), getU32Decoder())], ["caller", getAddressDecoder()], @@ -120,7 +118,7 @@ export function getBeforeActionInstructionDataCodec(): Codec< export type BeforeActionAsyncInput = { hookState?: Address; - jobId: BeforeActionInstructionDataArgs["jobId"]; + jobKey: BeforeActionInstructionDataArgs["jobKey"]; action: BeforeActionInstructionDataArgs["action"]; optParams: BeforeActionInstructionDataArgs["optParams"]; caller: BeforeActionInstructionDataArgs["caller"]; @@ -167,7 +165,7 @@ export async function getBeforeActionInstructionAsync< export type BeforeActionInput = { hookState: Address; - jobId: BeforeActionInstructionDataArgs["jobId"]; + jobKey: BeforeActionInstructionDataArgs["jobKey"]; action: BeforeActionInstructionDataArgs["action"]; optParams: BeforeActionInstructionDataArgs["optParams"]; caller: BeforeActionInstructionDataArgs["caller"]; diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/claimEscrowRefund.ts b/src/core/solana/generated/fund-transfer-hook/instructions/claimEscrowRefund.ts index 5ab54d0..456ff1c 100644 --- a/src/core/solana/generated/fund-transfer-hook/instructions/claimEscrowRefund.ts +++ b/src/core/solana/generated/fund-transfer-hook/instructions/claimEscrowRefund.ts @@ -10,12 +10,12 @@ import { combineCodec, fixDecoderSize, fixEncoderSize, + getAddressDecoder, + getAddressEncoder, getBytesDecoder, getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, transformEncoder, type AccountMeta, type AccountSignerMeta, @@ -67,7 +67,7 @@ export type ClaimEscrowRefundInstruction< TAccountTokenProgram extends string | AccountMeta = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", TAccountAcpState extends string | AccountMeta = string, - TAccountPlatformTreasury extends string | AccountMeta = string, + TAccountSponsor extends string | AccountMeta = string, TRemainingAccounts extends readonly AccountMeta[] = [], > = Instruction & InstructionWithData & @@ -104,25 +104,25 @@ export type ClaimEscrowRefundInstruction< TAccountAcpState extends string ? ReadonlyAccount : TAccountAcpState, - TAccountPlatformTreasury extends string - ? WritableAccount - : TAccountPlatformTreasury, + TAccountSponsor extends string + ? WritableAccount + : TAccountSponsor, ...TRemainingAccounts, ] >; export type ClaimEscrowRefundInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; }; -export type ClaimEscrowRefundInstructionDataArgs = { jobId: number | bigint }; +export type ClaimEscrowRefundInstructionDataArgs = { jobKey: Address }; export function getClaimEscrowRefundInstructionDataEncoder(): FixedSizeEncoder { return transformEncoder( getStructEncoder([ ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ]), (value) => ({ ...value, discriminator: CLAIM_ESCROW_REFUND_DISCRIMINATOR }), ); @@ -131,7 +131,7 @@ export function getClaimEscrowRefundInstructionDataEncoder(): FixedSizeEncoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ]); } @@ -156,7 +156,7 @@ export type ClaimEscrowRefundAsyncInput< TAccountEscrowAuthority extends string = string, TAccountTokenProgram extends string = string, TAccountAcpState extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, > = { caller: TransactionSigner; hookState?: Address; @@ -175,9 +175,9 @@ export type ClaimEscrowRefundAsyncInput< tokenProgram?: Address; /** against `hook_state.acp_program`. Read only to source the treasury. */ acpState: Address; - /** equal `acp_state.platform_treasury`. */ - platformTreasury: Address; - jobId: ClaimEscrowRefundInstructionDataArgs["jobId"]; + /** equal `acp_state.sponsor`. */ + sponsor: Address; + jobKey: ClaimEscrowRefundInstructionDataArgs["jobKey"]; }; export async function getClaimEscrowRefundInstructionAsync< @@ -191,7 +191,7 @@ export async function getClaimEscrowRefundInstructionAsync< TAccountEscrowAuthority extends string, TAccountTokenProgram extends string, TAccountAcpState extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, >( input: ClaimEscrowRefundAsyncInput< @@ -205,7 +205,7 @@ export async function getClaimEscrowRefundInstructionAsync< TAccountEscrowAuthority, TAccountTokenProgram, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor >, config?: { programAddress?: TProgramAddress }, ): Promise< @@ -221,7 +221,7 @@ export async function getClaimEscrowRefundInstructionAsync< TAccountEscrowAuthority, TAccountTokenProgram, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor > > { // Program address. @@ -249,10 +249,7 @@ export async function getClaimEscrowRefundInstructionAsync< }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, acpState: { value: input.acpState ?? null, isWritable: false }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -268,12 +265,12 @@ export async function getClaimEscrowRefundInstructionAsync< } if (!accounts.providerEscrowIntentId.value) { accounts.providerEscrowIntentId.value = await findProviderEscrowIntentIdPda( - { jobId: expectSome(args.jobId) }, + { jobKey: expectSome(args.jobKey) }, ); } if (!accounts.escrowAuthority.value) { accounts.escrowAuthority.value = await findEscrowAuthorityPda({ - jobId: expectSome(args.jobId), + jobKey: expectSome(args.jobKey), }); } if (!accounts.tokenProgram.value) { @@ -294,7 +291,7 @@ export async function getClaimEscrowRefundInstructionAsync< getAccountMeta(accounts.escrowAuthority), getAccountMeta(accounts.tokenProgram), getAccountMeta(accounts.acpState), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), ], data: getClaimEscrowRefundInstructionDataEncoder().encode( args as ClaimEscrowRefundInstructionDataArgs, @@ -312,7 +309,7 @@ export async function getClaimEscrowRefundInstructionAsync< TAccountEscrowAuthority, TAccountTokenProgram, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor >); } @@ -327,7 +324,7 @@ export type ClaimEscrowRefundInput< TAccountEscrowAuthority extends string = string, TAccountTokenProgram extends string = string, TAccountAcpState extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, > = { caller: TransactionSigner; hookState: Address; @@ -346,9 +343,9 @@ export type ClaimEscrowRefundInput< tokenProgram?: Address; /** against `hook_state.acp_program`. Read only to source the treasury. */ acpState: Address; - /** equal `acp_state.platform_treasury`. */ - platformTreasury: Address; - jobId: ClaimEscrowRefundInstructionDataArgs["jobId"]; + /** equal `acp_state.sponsor`. */ + sponsor: Address; + jobKey: ClaimEscrowRefundInstructionDataArgs["jobKey"]; }; export function getClaimEscrowRefundInstruction< @@ -362,7 +359,7 @@ export function getClaimEscrowRefundInstruction< TAccountEscrowAuthority extends string, TAccountTokenProgram extends string, TAccountAcpState extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, >( input: ClaimEscrowRefundInput< @@ -376,7 +373,7 @@ export function getClaimEscrowRefundInstruction< TAccountEscrowAuthority, TAccountTokenProgram, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor >, config?: { programAddress?: TProgramAddress }, ): ClaimEscrowRefundInstruction< @@ -391,7 +388,7 @@ export function getClaimEscrowRefundInstruction< TAccountEscrowAuthority, TAccountTokenProgram, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor > { // Program address. const programAddress = @@ -418,10 +415,7 @@ export function getClaimEscrowRefundInstruction< }, tokenProgram: { value: input.tokenProgram ?? null, isWritable: false }, acpState: { value: input.acpState ?? null, isWritable: false }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -450,7 +444,7 @@ export function getClaimEscrowRefundInstruction< getAccountMeta(accounts.escrowAuthority), getAccountMeta(accounts.tokenProgram), getAccountMeta(accounts.acpState), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), ], data: getClaimEscrowRefundInstructionDataEncoder().encode( args as ClaimEscrowRefundInstructionDataArgs, @@ -468,7 +462,7 @@ export function getClaimEscrowRefundInstruction< TAccountEscrowAuthority, TAccountTokenProgram, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor >); } @@ -495,8 +489,8 @@ export type ParsedClaimEscrowRefundInstruction< tokenProgram: TAccountMetas[8]; /** against `hook_state.acp_program`. Read only to source the treasury. */ acpState: TAccountMetas[9]; - /** equal `acp_state.platform_treasury`. */ - platformTreasury: TAccountMetas[10]; + /** equal `acp_state.sponsor`. */ + sponsor: TAccountMetas[10]; }; data: ClaimEscrowRefundInstructionData; }; @@ -532,7 +526,7 @@ export function parseClaimEscrowRefundInstruction< escrowAuthority: getNextAccount(), tokenProgram: getNextAccount(), acpState: getNextAccount(), - platformTreasury: getNextAccount(), + sponsor: getNextAccount(), }, data: getClaimEscrowRefundInstructionDataDecoder().decode(instruction.data), }; diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/closeIntent.ts b/src/core/solana/generated/fund-transfer-hook/instructions/closeIntent.ts deleted file mode 100644 index e0966ae..0000000 --- a/src/core/solana/generated/fund-transfer-hook/instructions/closeIntent.ts +++ /dev/null @@ -1,433 +0,0 @@ -/** - * This code was AUTOGENERATED using the Codama library. - * Please DO NOT EDIT THIS FILE, instead use visitors - * to add features, then rerun Codama to update it. - * - * @see https://github.com/codama-idl/codama - */ - -import { - combineCodec, - fixDecoderSize, - fixEncoderSize, - getBytesDecoder, - getBytesEncoder, - getStructDecoder, - getStructEncoder, - getU64Decoder, - getU64Encoder, - transformEncoder, - type AccountMeta, - type AccountSignerMeta, - type Address, - type FixedSizeCodec, - type FixedSizeDecoder, - type FixedSizeEncoder, - type Instruction, - type InstructionWithAccounts, - type InstructionWithData, - type ReadonlyAccount, - type ReadonlyUint8Array, - type TransactionSigner, - type WritableAccount, - type WritableSignerAccount, -} from "@solana/kit"; -import { findHookStatePda } from "../pdas/index.js"; -import { FUND_TRANSFER_HOOK_PROGRAM_ADDRESS } from "../programs/index.js"; -import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; - -export const CLOSE_INTENT_DISCRIMINATOR = new Uint8Array([ - 112, 245, 154, 249, 57, 126, 54, 122, -]); - -export function getCloseIntentDiscriminatorBytes() { - return fixEncoderSize(getBytesEncoder(), 8).encode( - CLOSE_INTENT_DISCRIMINATOR, - ); -} - -export type CloseIntentInstruction< - TProgram extends string = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, - TAccountActor extends string | AccountMeta = string, - TAccountIntent extends string | AccountMeta = string, - TAccountFundRequestMap extends string | AccountMeta = string, - TAccountHookState extends string | AccountMeta = string, - TAccountSystemProgram extends string | AccountMeta = - "11111111111111111111111111111111", - TAccountAcpState extends string | AccountMeta = string, - TAccountPlatformTreasury extends string | AccountMeta = string, - TRemainingAccounts extends readonly AccountMeta[] = [], -> = Instruction & - InstructionWithData & - InstructionWithAccounts< - [ - TAccountActor extends string - ? WritableSignerAccount & - AccountSignerMeta - : TAccountActor, - TAccountIntent extends string - ? WritableAccount - : TAccountIntent, - TAccountFundRequestMap extends string - ? ReadonlyAccount - : TAccountFundRequestMap, - TAccountHookState extends string - ? ReadonlyAccount - : TAccountHookState, - TAccountSystemProgram extends string - ? ReadonlyAccount - : TAccountSystemProgram, - TAccountAcpState extends string - ? ReadonlyAccount - : TAccountAcpState, - TAccountPlatformTreasury extends string - ? WritableAccount - : TAccountPlatformTreasury, - ...TRemainingAccounts, - ] - >; - -export type CloseIntentInstructionData = { - discriminator: ReadonlyUint8Array; - intentId: bigint; -}; - -export type CloseIntentInstructionDataArgs = { intentId: number | bigint }; - -export function getCloseIntentInstructionDataEncoder(): FixedSizeEncoder { - return transformEncoder( - getStructEncoder([ - ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], - ["intentId", getU64Encoder()], - ]), - (value) => ({ ...value, discriminator: CLOSE_INTENT_DISCRIMINATOR }), - ); -} - -export function getCloseIntentInstructionDataDecoder(): FixedSizeDecoder { - return getStructDecoder([ - ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["intentId", getU64Decoder()], - ]); -} - -export function getCloseIntentInstructionDataCodec(): FixedSizeCodec< - CloseIntentInstructionDataArgs, - CloseIntentInstructionData -> { - return combineCodec( - getCloseIntentInstructionDataEncoder(), - getCloseIntentInstructionDataDecoder(), - ); -} - -export type CloseIntentAsyncInput< - TAccountActor extends string = string, - TAccountIntent extends string = string, - TAccountFundRequestMap extends string = string, - TAccountHookState extends string = string, - TAccountSystemProgram extends string = string, - TAccountAcpState extends string = string, - TAccountPlatformTreasury extends string = string, -> = { - /** - * The original actor who proposed the intent (provider for fund requests). - * Still required to authorize the close even though the rent now goes to - * the treasury: the intent PDA is reused in place by the next proposal, so - * letting a stranger deallocate it would force the sponsor to pay its rent - * again for no gain to anyone. - */ - actor: TransactionSigner; - /** - * The intent PDA to close; must be unsigned and non-escrow. The address - * does not encode the intent id, so `intent.id == intent_id` is asserted explicitly. - */ - intent: Address; - /** Per-job fund request intent map; prevents closing the currently active intent. */ - fundRequestMap: Address; - hookState?: Address; - systemProgram?: Address; - /** against `hook_state.acp_program`. Read only to source the treasury. */ - acpState: Address; - /** `acp_state.platform_treasury`. */ - platformTreasury: Address; - intentId: CloseIntentInstructionDataArgs["intentId"]; -}; - -export async function getCloseIntentInstructionAsync< - TAccountActor extends string, - TAccountIntent extends string, - TAccountFundRequestMap extends string, - TAccountHookState extends string, - TAccountSystemProgram extends string, - TAccountAcpState extends string, - TAccountPlatformTreasury extends string, - TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, ->( - input: CloseIntentAsyncInput< - TAccountActor, - TAccountIntent, - TAccountFundRequestMap, - TAccountHookState, - TAccountSystemProgram, - TAccountAcpState, - TAccountPlatformTreasury - >, - config?: { programAddress?: TProgramAddress }, -): Promise< - CloseIntentInstruction< - TProgramAddress, - TAccountActor, - TAccountIntent, - TAccountFundRequestMap, - TAccountHookState, - TAccountSystemProgram, - TAccountAcpState, - TAccountPlatformTreasury - > -> { - // Program address. - const programAddress = - config?.programAddress ?? FUND_TRANSFER_HOOK_PROGRAM_ADDRESS; - - // Original accounts. - const originalAccounts = { - actor: { value: input.actor ?? null, isWritable: true }, - intent: { value: input.intent ?? null, isWritable: true }, - fundRequestMap: { value: input.fundRequestMap ?? null, isWritable: false }, - hookState: { value: input.hookState ?? null, isWritable: false }, - systemProgram: { value: input.systemProgram ?? null, isWritable: false }, - acpState: { value: input.acpState ?? null, isWritable: false }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, - }; - const accounts = originalAccounts as Record< - keyof typeof originalAccounts, - ResolvedAccount - >; - - // Original args. - const args = { ...input }; - - // Resolve default values. - if (!accounts.hookState.value) { - accounts.hookState.value = await findHookStatePda(); - } - if (!accounts.systemProgram.value) { - accounts.systemProgram.value = - "11111111111111111111111111111111" as Address<"11111111111111111111111111111111">; - } - - const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); - return Object.freeze({ - accounts: [ - getAccountMeta(accounts.actor), - getAccountMeta(accounts.intent), - getAccountMeta(accounts.fundRequestMap), - getAccountMeta(accounts.hookState), - getAccountMeta(accounts.systemProgram), - getAccountMeta(accounts.acpState), - getAccountMeta(accounts.platformTreasury), - ], - data: getCloseIntentInstructionDataEncoder().encode( - args as CloseIntentInstructionDataArgs, - ), - programAddress, - } as CloseIntentInstruction< - TProgramAddress, - TAccountActor, - TAccountIntent, - TAccountFundRequestMap, - TAccountHookState, - TAccountSystemProgram, - TAccountAcpState, - TAccountPlatformTreasury - >); -} - -export type CloseIntentInput< - TAccountActor extends string = string, - TAccountIntent extends string = string, - TAccountFundRequestMap extends string = string, - TAccountHookState extends string = string, - TAccountSystemProgram extends string = string, - TAccountAcpState extends string = string, - TAccountPlatformTreasury extends string = string, -> = { - /** - * The original actor who proposed the intent (provider for fund requests). - * Still required to authorize the close even though the rent now goes to - * the treasury: the intent PDA is reused in place by the next proposal, so - * letting a stranger deallocate it would force the sponsor to pay its rent - * again for no gain to anyone. - */ - actor: TransactionSigner; - /** - * The intent PDA to close; must be unsigned and non-escrow. The address - * does not encode the intent id, so `intent.id == intent_id` is asserted explicitly. - */ - intent: Address; - /** Per-job fund request intent map; prevents closing the currently active intent. */ - fundRequestMap: Address; - hookState: Address; - systemProgram?: Address; - /** against `hook_state.acp_program`. Read only to source the treasury. */ - acpState: Address; - /** `acp_state.platform_treasury`. */ - platformTreasury: Address; - intentId: CloseIntentInstructionDataArgs["intentId"]; -}; - -export function getCloseIntentInstruction< - TAccountActor extends string, - TAccountIntent extends string, - TAccountFundRequestMap extends string, - TAccountHookState extends string, - TAccountSystemProgram extends string, - TAccountAcpState extends string, - TAccountPlatformTreasury extends string, - TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, ->( - input: CloseIntentInput< - TAccountActor, - TAccountIntent, - TAccountFundRequestMap, - TAccountHookState, - TAccountSystemProgram, - TAccountAcpState, - TAccountPlatformTreasury - >, - config?: { programAddress?: TProgramAddress }, -): CloseIntentInstruction< - TProgramAddress, - TAccountActor, - TAccountIntent, - TAccountFundRequestMap, - TAccountHookState, - TAccountSystemProgram, - TAccountAcpState, - TAccountPlatformTreasury -> { - // Program address. - const programAddress = - config?.programAddress ?? FUND_TRANSFER_HOOK_PROGRAM_ADDRESS; - - // Original accounts. - const originalAccounts = { - actor: { value: input.actor ?? null, isWritable: true }, - intent: { value: input.intent ?? null, isWritable: true }, - fundRequestMap: { value: input.fundRequestMap ?? null, isWritable: false }, - hookState: { value: input.hookState ?? null, isWritable: false }, - systemProgram: { value: input.systemProgram ?? null, isWritable: false }, - acpState: { value: input.acpState ?? null, isWritable: false }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, - }; - 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"); - return Object.freeze({ - accounts: [ - getAccountMeta(accounts.actor), - getAccountMeta(accounts.intent), - getAccountMeta(accounts.fundRequestMap), - getAccountMeta(accounts.hookState), - getAccountMeta(accounts.systemProgram), - getAccountMeta(accounts.acpState), - getAccountMeta(accounts.platformTreasury), - ], - data: getCloseIntentInstructionDataEncoder().encode( - args as CloseIntentInstructionDataArgs, - ), - programAddress, - } as CloseIntentInstruction< - TProgramAddress, - TAccountActor, - TAccountIntent, - TAccountFundRequestMap, - TAccountHookState, - TAccountSystemProgram, - TAccountAcpState, - TAccountPlatformTreasury - >); -} - -export type ParsedCloseIntentInstruction< - TProgram extends string = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, - TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[], -> = { - programAddress: Address; - accounts: { - /** - * The original actor who proposed the intent (provider for fund requests). - * Still required to authorize the close even though the rent now goes to - * the treasury: the intent PDA is reused in place by the next proposal, so - * letting a stranger deallocate it would force the sponsor to pay its rent - * again for no gain to anyone. - */ - actor: TAccountMetas[0]; - /** - * The intent PDA to close; must be unsigned and non-escrow. The address - * does not encode the intent id, so `intent.id == intent_id` is asserted explicitly. - */ - intent: TAccountMetas[1]; - /** Per-job fund request intent map; prevents closing the currently active intent. */ - fundRequestMap: TAccountMetas[2]; - hookState: TAccountMetas[3]; - systemProgram: TAccountMetas[4]; - /** against `hook_state.acp_program`. Read only to source the treasury. */ - acpState: TAccountMetas[5]; - /** `acp_state.platform_treasury`. */ - platformTreasury: TAccountMetas[6]; - }; - data: CloseIntentInstructionData; -}; - -export function parseCloseIntentInstruction< - TProgram extends string, - TAccountMetas extends readonly AccountMeta[], ->( - instruction: Instruction & - InstructionWithAccounts & - InstructionWithData, -): ParsedCloseIntentInstruction { - if (instruction.accounts.length < 7) { - // TODO: Coded error. - throw new Error("Not enough accounts"); - } - let accountIndex = 0; - const getNextAccount = () => { - const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!; - accountIndex += 1; - return accountMeta; - }; - return { - programAddress: instruction.programAddress, - accounts: { - actor: getNextAccount(), - intent: getNextAccount(), - fundRequestMap: getNextAccount(), - hookState: getNextAccount(), - systemProgram: getNextAccount(), - acpState: getNextAccount(), - platformTreasury: getNextAccount(), - }, - data: getCloseIntentInstructionDataDecoder().decode(instruction.data), - }; -} diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/closeJobHookAccounts.ts b/src/core/solana/generated/fund-transfer-hook/instructions/closeJobHookAccounts.ts new file mode 100644 index 0000000..a81147f --- /dev/null +++ b/src/core/solana/generated/fund-transfer-hook/instructions/closeJobHookAccounts.ts @@ -0,0 +1,496 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + combineCodec, + fixDecoderSize, + fixEncoderSize, + getAddressDecoder, + getAddressEncoder, + getBytesDecoder, + getBytesEncoder, + getStructDecoder, + getStructEncoder, + transformEncoder, + type AccountMeta, + type AccountSignerMeta, + type Address, + type FixedSizeCodec, + type FixedSizeDecoder, + type FixedSizeEncoder, + type Instruction, + type InstructionWithAccounts, + type InstructionWithData, + type ReadonlyAccount, + type ReadonlySignerAccount, + type ReadonlyUint8Array, + type TransactionSigner, + type WritableAccount, +} from "@solana/kit"; +import { findHookStatePda } from "../pdas/index.js"; +import { FUND_TRANSFER_HOOK_PROGRAM_ADDRESS } from "../programs/index.js"; +import { getAccountMetaFactory, type ResolvedAccount } from "../shared/index.js"; + +export const CLOSE_JOB_HOOK_ACCOUNTS_DISCRIMINATOR = new Uint8Array([ + 77, 224, 55, 159, 116, 59, 13, 125, +]); + +export function getCloseJobHookAccountsDiscriminatorBytes() { + return fixEncoderSize(getBytesEncoder(), 8).encode( + CLOSE_JOB_HOOK_ACCOUNTS_DISCRIMINATOR, + ); +} + +export type CloseJobHookAccountsInstruction< + TProgram extends string = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, + TAccountCaller extends string | AccountMeta = string, + TAccountJob extends string | AccountMeta = string, + TAccountFundRequestIntent extends string | AccountMeta = string, + TAccountEscrowIntent extends string | AccountMeta = string, + TAccountFundRequestMap extends string | AccountMeta = string, + TAccountEscrowMap extends string | AccountMeta = string, + TAccountHookState extends string | AccountMeta = string, + TAccountAcpState extends string | AccountMeta = string, + TAccountSponsor extends string | AccountMeta = string, + TRemainingAccounts extends readonly AccountMeta[] = [], +> = Instruction & + InstructionWithData & + InstructionWithAccounts< + [ + TAccountCaller extends string + ? ReadonlySignerAccount & + AccountSignerMeta + : TAccountCaller, + TAccountJob extends string ? ReadonlyAccount : TAccountJob, + TAccountFundRequestIntent extends string + ? WritableAccount + : TAccountFundRequestIntent, + TAccountEscrowIntent extends string + ? WritableAccount + : TAccountEscrowIntent, + TAccountFundRequestMap extends string + ? WritableAccount + : TAccountFundRequestMap, + TAccountEscrowMap extends string + ? WritableAccount + : TAccountEscrowMap, + TAccountHookState extends string + ? ReadonlyAccount + : TAccountHookState, + TAccountAcpState extends string + ? ReadonlyAccount + : TAccountAcpState, + TAccountSponsor extends string + ? WritableAccount + : TAccountSponsor, + ...TRemainingAccounts, + ] + >; + +export type CloseJobHookAccountsInstructionData = { + discriminator: ReadonlyUint8Array; + jobKey: Address; +}; + +export type CloseJobHookAccountsInstructionDataArgs = { jobKey: Address }; + +export function getCloseJobHookAccountsInstructionDataEncoder(): FixedSizeEncoder { + return transformEncoder( + getStructEncoder([ + ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], + ["jobKey", getAddressEncoder()], + ]), + (value) => ({ + ...value, + discriminator: CLOSE_JOB_HOOK_ACCOUNTS_DISCRIMINATOR, + }), + ); +} + +export function getCloseJobHookAccountsInstructionDataDecoder(): FixedSizeDecoder { + return getStructDecoder([ + ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], + ["jobKey", getAddressDecoder()], + ]); +} + +export function getCloseJobHookAccountsInstructionDataCodec(): FixedSizeCodec< + CloseJobHookAccountsInstructionDataArgs, + CloseJobHookAccountsInstructionData +> { + return combineCodec( + getCloseJobHookAccountsInstructionDataEncoder(), + getCloseJobHookAccountsInstructionDataDecoder(), + ); +} + +export type CloseJobHookAccountsAsyncInput< + TAccountCaller extends string = string, + TAccountJob extends string = string, + TAccountFundRequestIntent extends string = string, + TAccountEscrowIntent extends string = string, + TAccountFundRequestMap extends string = string, + TAccountEscrowMap extends string = string, + TAccountHookState extends string = string, + TAccountAcpState extends string = string, + TAccountSponsor extends string = string, +> = { + /** + * Permissionless. Rent goes to `acp_state.sponsor`, so the caller gains + * nothing and there is no race incentive. Doubles as a sweep for jobs whose + * terminal-transaction bundle failed to land. + */ + caller: TransactionSigner; + /** `deserialize_job` against `hook_state.acp_program`. */ + job: Address; + /** + * All four are optional and individually skipped when already empty: a + * zero-budget job may have no escrow leg, and a job that never had a fund + * request has no kind-0 intent. Each is verified against its canonical + * seeds before anything is drained. + */ + fundRequestIntent?: Address; + escrowIntent?: Address; + /** + * `Account`: a typed account is deserialized at entry, + * so a second call on an already-closed map would fail + * `AccountNotInitialized` and break the blind-sweep idempotence the bundled + * close depends on. The cost is that `FundRequestIntentId` no longer + * appears in the IDL — `close_intent` was its last typed reference — so + * off-chain readers must decode it manually. Its layout is + * `job_key: Pubkey, intent_id: u64, bump: u8` after the 8-byte discriminator. + */ + fundRequestMap?: Address; + escrowMap?: Address; + hookState?: Address; + acpState: Address; + sponsor: Address; + jobKey: CloseJobHookAccountsInstructionDataArgs["jobKey"]; +}; + +export async function getCloseJobHookAccountsInstructionAsync< + TAccountCaller extends string, + TAccountJob extends string, + TAccountFundRequestIntent extends string, + TAccountEscrowIntent extends string, + TAccountFundRequestMap extends string, + TAccountEscrowMap extends string, + TAccountHookState extends string, + TAccountAcpState extends string, + TAccountSponsor extends string, + TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, +>( + input: CloseJobHookAccountsAsyncInput< + TAccountCaller, + TAccountJob, + TAccountFundRequestIntent, + TAccountEscrowIntent, + TAccountFundRequestMap, + TAccountEscrowMap, + TAccountHookState, + TAccountAcpState, + TAccountSponsor + >, + config?: { programAddress?: TProgramAddress }, +): Promise< + CloseJobHookAccountsInstruction< + TProgramAddress, + TAccountCaller, + TAccountJob, + TAccountFundRequestIntent, + TAccountEscrowIntent, + TAccountFundRequestMap, + TAccountEscrowMap, + TAccountHookState, + TAccountAcpState, + TAccountSponsor + > +> { + // Program address. + const programAddress = + config?.programAddress ?? FUND_TRANSFER_HOOK_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + caller: { value: input.caller ?? null, isWritable: false }, + job: { value: input.job ?? null, isWritable: false }, + fundRequestIntent: { + value: input.fundRequestIntent ?? null, + isWritable: true, + }, + escrowIntent: { value: input.escrowIntent ?? null, isWritable: true }, + fundRequestMap: { value: input.fundRequestMap ?? null, isWritable: true }, + escrowMap: { value: input.escrowMap ?? null, isWritable: true }, + hookState: { value: input.hookState ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: false }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + // Resolve default values. + if (!accounts.hookState.value) { + accounts.hookState.value = await findHookStatePda(); + } + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.caller), + getAccountMeta(accounts.job), + getAccountMeta(accounts.fundRequestIntent), + getAccountMeta(accounts.escrowIntent), + getAccountMeta(accounts.fundRequestMap), + getAccountMeta(accounts.escrowMap), + getAccountMeta(accounts.hookState), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.sponsor), + ], + data: getCloseJobHookAccountsInstructionDataEncoder().encode( + args as CloseJobHookAccountsInstructionDataArgs, + ), + programAddress, + } as CloseJobHookAccountsInstruction< + TProgramAddress, + TAccountCaller, + TAccountJob, + TAccountFundRequestIntent, + TAccountEscrowIntent, + TAccountFundRequestMap, + TAccountEscrowMap, + TAccountHookState, + TAccountAcpState, + TAccountSponsor + >); +} + +export type CloseJobHookAccountsInput< + TAccountCaller extends string = string, + TAccountJob extends string = string, + TAccountFundRequestIntent extends string = string, + TAccountEscrowIntent extends string = string, + TAccountFundRequestMap extends string = string, + TAccountEscrowMap extends string = string, + TAccountHookState extends string = string, + TAccountAcpState extends string = string, + TAccountSponsor extends string = string, +> = { + /** + * Permissionless. Rent goes to `acp_state.sponsor`, so the caller gains + * nothing and there is no race incentive. Doubles as a sweep for jobs whose + * terminal-transaction bundle failed to land. + */ + caller: TransactionSigner; + /** `deserialize_job` against `hook_state.acp_program`. */ + job: Address; + /** + * All four are optional and individually skipped when already empty: a + * zero-budget job may have no escrow leg, and a job that never had a fund + * request has no kind-0 intent. Each is verified against its canonical + * seeds before anything is drained. + */ + fundRequestIntent?: Address; + escrowIntent?: Address; + /** + * `Account`: a typed account is deserialized at entry, + * so a second call on an already-closed map would fail + * `AccountNotInitialized` and break the blind-sweep idempotence the bundled + * close depends on. The cost is that `FundRequestIntentId` no longer + * appears in the IDL — `close_intent` was its last typed reference — so + * off-chain readers must decode it manually. Its layout is + * `job_key: Pubkey, intent_id: u64, bump: u8` after the 8-byte discriminator. + */ + fundRequestMap?: Address; + escrowMap?: Address; + hookState: Address; + acpState: Address; + sponsor: Address; + jobKey: CloseJobHookAccountsInstructionDataArgs["jobKey"]; +}; + +export function getCloseJobHookAccountsInstruction< + TAccountCaller extends string, + TAccountJob extends string, + TAccountFundRequestIntent extends string, + TAccountEscrowIntent extends string, + TAccountFundRequestMap extends string, + TAccountEscrowMap extends string, + TAccountHookState extends string, + TAccountAcpState extends string, + TAccountSponsor extends string, + TProgramAddress extends Address = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, +>( + input: CloseJobHookAccountsInput< + TAccountCaller, + TAccountJob, + TAccountFundRequestIntent, + TAccountEscrowIntent, + TAccountFundRequestMap, + TAccountEscrowMap, + TAccountHookState, + TAccountAcpState, + TAccountSponsor + >, + config?: { programAddress?: TProgramAddress }, +): CloseJobHookAccountsInstruction< + TProgramAddress, + TAccountCaller, + TAccountJob, + TAccountFundRequestIntent, + TAccountEscrowIntent, + TAccountFundRequestMap, + TAccountEscrowMap, + TAccountHookState, + TAccountAcpState, + TAccountSponsor +> { + // Program address. + const programAddress = + config?.programAddress ?? FUND_TRANSFER_HOOK_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + caller: { value: input.caller ?? null, isWritable: false }, + job: { value: input.job ?? null, isWritable: false }, + fundRequestIntent: { + value: input.fundRequestIntent ?? null, + isWritable: true, + }, + escrowIntent: { value: input.escrowIntent ?? null, isWritable: true }, + fundRequestMap: { value: input.fundRequestMap ?? null, isWritable: true }, + escrowMap: { value: input.escrowMap ?? null, isWritable: true }, + hookState: { value: input.hookState ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: false }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.caller), + getAccountMeta(accounts.job), + getAccountMeta(accounts.fundRequestIntent), + getAccountMeta(accounts.escrowIntent), + getAccountMeta(accounts.fundRequestMap), + getAccountMeta(accounts.escrowMap), + getAccountMeta(accounts.hookState), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.sponsor), + ], + data: getCloseJobHookAccountsInstructionDataEncoder().encode( + args as CloseJobHookAccountsInstructionDataArgs, + ), + programAddress, + } as CloseJobHookAccountsInstruction< + TProgramAddress, + TAccountCaller, + TAccountJob, + TAccountFundRequestIntent, + TAccountEscrowIntent, + TAccountFundRequestMap, + TAccountEscrowMap, + TAccountHookState, + TAccountAcpState, + TAccountSponsor + >); +} + +export type ParsedCloseJobHookAccountsInstruction< + TProgram extends string = typeof FUND_TRANSFER_HOOK_PROGRAM_ADDRESS, + TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[], +> = { + programAddress: Address; + accounts: { + /** + * Permissionless. Rent goes to `acp_state.sponsor`, so the caller gains + * nothing and there is no race incentive. Doubles as a sweep for jobs whose + * terminal-transaction bundle failed to land. + */ + caller: TAccountMetas[0]; + /** `deserialize_job` against `hook_state.acp_program`. */ + job: TAccountMetas[1]; + /** + * All four are optional and individually skipped when already empty: a + * zero-budget job may have no escrow leg, and a job that never had a fund + * request has no kind-0 intent. Each is verified against its canonical + * seeds before anything is drained. + */ + fundRequestIntent?: TAccountMetas[2] | undefined; + escrowIntent?: TAccountMetas[3] | undefined; + /** + * `Account`: a typed account is deserialized at entry, + * so a second call on an already-closed map would fail + * `AccountNotInitialized` and break the blind-sweep idempotence the bundled + * close depends on. The cost is that `FundRequestIntentId` no longer + * appears in the IDL — `close_intent` was its last typed reference — so + * off-chain readers must decode it manually. Its layout is + * `job_key: Pubkey, intent_id: u64, bump: u8` after the 8-byte discriminator. + */ + fundRequestMap?: TAccountMetas[4] | undefined; + escrowMap?: TAccountMetas[5] | undefined; + hookState: TAccountMetas[6]; + acpState: TAccountMetas[7]; + sponsor: TAccountMetas[8]; + }; + data: CloseJobHookAccountsInstructionData; +}; + +export function parseCloseJobHookAccountsInstruction< + TProgram extends string, + TAccountMetas extends readonly AccountMeta[], +>( + instruction: Instruction & + InstructionWithAccounts & + InstructionWithData, +): ParsedCloseJobHookAccountsInstruction { + if (instruction.accounts.length < 9) { + // TODO: Coded error. + throw new Error("Not enough accounts"); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + const getNextOptionalAccount = () => { + const accountMeta = getNextAccount(); + return accountMeta.address === FUND_TRANSFER_HOOK_PROGRAM_ADDRESS + ? undefined + : accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { + caller: getNextAccount(), + job: getNextAccount(), + fundRequestIntent: getNextOptionalAccount(), + escrowIntent: getNextOptionalAccount(), + fundRequestMap: getNextOptionalAccount(), + escrowMap: getNextOptionalAccount(), + hookState: getNextAccount(), + acpState: getNextAccount(), + sponsor: getNextAccount(), + }, + data: getCloseJobHookAccountsInstructionDataDecoder().decode( + instruction.data, + ), + }; +} diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/index.ts b/src/core/solana/generated/fund-transfer-hook/instructions/index.ts index dc7cb6b..710205f 100644 --- a/src/core/solana/generated/fund-transfer-hook/instructions/index.ts +++ b/src/core/solana/generated/fund-transfer-hook/instructions/index.ts @@ -9,6 +9,6 @@ export * from "./afterAction.js"; export * from "./beforeAction.js"; export * from "./claimEscrowRefund.js"; -export * from "./closeIntent.js"; +export * from "./closeJobHookAccounts.js"; export * from "./initialize.js"; export * from "./preCreateIntent.js"; diff --git a/src/core/solana/generated/fund-transfer-hook/instructions/preCreateIntent.ts b/src/core/solana/generated/fund-transfer-hook/instructions/preCreateIntent.ts index b7c8ed0..d01ad70 100644 --- a/src/core/solana/generated/fund-transfer-hook/instructions/preCreateIntent.ts +++ b/src/core/solana/generated/fund-transfer-hook/instructions/preCreateIntent.ts @@ -10,12 +10,12 @@ import { combineCodec, fixDecoderSize, fixEncoderSize, + getAddressDecoder, + getAddressEncoder, getBytesDecoder, getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -85,12 +85,12 @@ export type PreCreateIntentInstruction< export type PreCreateIntentInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; kind: number; }; export type PreCreateIntentInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; kind: number; }; @@ -98,7 +98,7 @@ export function getPreCreateIntentInstructionDataEncoder(): FixedSizeEncoder
 ({ ...value, discriminator: PRE_CREATE_INTENT_DISCRIMINATOR }),
@@ -108,7 +108,7 @@ export function getPreCreateIntentInstructionDataEncoder(): FixedSizeEncoder
 {
   return getStructDecoder([
     ["discriminator", fixDecoderSize(getBytesDecoder(), 8)],
-    ["jobId", getU64Decoder()],
+    ["jobKey", getAddressDecoder()],
     ["kind", getU8Decoder()],
   ]);
 }
@@ -143,7 +143,7 @@ export type PreCreateIntentAsyncInput<
   /** (seed prefix depends on `kind`). */
   intentMap: Address;
   systemProgram?: Address;
-  jobId: PreCreateIntentInstructionDataArgs["jobId"];
+  jobKey: PreCreateIntentInstructionDataArgs["jobKey"];
   kind: PreCreateIntentInstructionDataArgs["kind"];
 };
 
@@ -251,7 +251,7 @@ export type PreCreateIntentInput<
   /** (seed prefix depends on `kind`). */
   intentMap: Address;
   systemProgram?: Address;
-  jobId: PreCreateIntentInstructionDataArgs["jobId"];
+  jobKey: PreCreateIntentInstructionDataArgs["jobKey"];
   kind: PreCreateIntentInstructionDataArgs["kind"];
 };
 
diff --git a/src/core/solana/generated/fund-transfer-hook/pdas/escrowAuthority.ts b/src/core/solana/generated/fund-transfer-hook/pdas/escrowAuthority.ts
index 8eb9977..2c5b7eb 100644
--- a/src/core/solana/generated/fund-transfer-hook/pdas/escrowAuthority.ts
+++ b/src/core/solana/generated/fund-transfer-hook/pdas/escrowAuthority.ts
@@ -7,22 +7,24 @@
  */
 
 import {
+  getAddressEncoder,
   getBytesEncoder,
   getProgramDerivedAddress,
-  getU64Encoder,
   type Address,
   type ProgramDerivedAddress,
 } from "@solana/kit";
 
 export type EscrowAuthoritySeeds = {
-  jobId: number | bigint;
+  jobKey: Address;
 };
 
 export async function findEscrowAuthorityPda(
   seeds: EscrowAuthoritySeeds,
   config: { programAddress?: Address | undefined } = {},
 ): Promise {
-  const { programAddress = "" as Address<""> } = config;
+  const {
+    programAddress = "HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q" as Address<"HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q">,
+  } = config;
   return await getProgramDerivedAddress({
     programAddress,
     seeds: [
@@ -32,7 +34,7 @@ export async function findEscrowAuthorityPda(
           116, 121,
         ]),
       ),
-      getU64Encoder().encode(seeds.jobId),
+      getAddressEncoder().encode(seeds.jobKey),
     ],
   });
 }
diff --git a/src/core/solana/generated/fund-transfer-hook/pdas/hookMetadata.ts b/src/core/solana/generated/fund-transfer-hook/pdas/hookMetadata.ts
index 4c76c62..a54842c 100644
--- a/src/core/solana/generated/fund-transfer-hook/pdas/hookMetadata.ts
+++ b/src/core/solana/generated/fund-transfer-hook/pdas/hookMetadata.ts
@@ -16,7 +16,9 @@ import {
 export async function findHookMetadataPda(
   config: { programAddress?: Address | undefined } = {},
 ): Promise {
-  const { programAddress = "" as Address<""> } = config;
+  const {
+    programAddress = "HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q" as Address<"HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q">,
+  } = config;
   return await getProgramDerivedAddress({
     programAddress,
     seeds: [
diff --git a/src/core/solana/generated/fund-transfer-hook/pdas/hookState.ts b/src/core/solana/generated/fund-transfer-hook/pdas/hookState.ts
index e55228b..523c5bd 100644
--- a/src/core/solana/generated/fund-transfer-hook/pdas/hookState.ts
+++ b/src/core/solana/generated/fund-transfer-hook/pdas/hookState.ts
@@ -16,7 +16,9 @@ import {
 export async function findHookStatePda(
   config: { programAddress?: Address | undefined } = {},
 ): Promise {
-  const { programAddress = "" as Address<""> } = config;
+  const {
+    programAddress = "HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q" as Address<"HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q">,
+  } = config;
   return await getProgramDerivedAddress({
     programAddress,
     seeds: [
diff --git a/src/core/solana/generated/fund-transfer-hook/pdas/providerEscrowIntentId.ts b/src/core/solana/generated/fund-transfer-hook/pdas/providerEscrowIntentId.ts
index dbae67c..94a1903 100644
--- a/src/core/solana/generated/fund-transfer-hook/pdas/providerEscrowIntentId.ts
+++ b/src/core/solana/generated/fund-transfer-hook/pdas/providerEscrowIntentId.ts
@@ -7,22 +7,24 @@
  */
 
 import {
+  getAddressEncoder,
   getBytesEncoder,
   getProgramDerivedAddress,
-  getU64Encoder,
   type Address,
   type ProgramDerivedAddress,
 } from "@solana/kit";
 
 export type ProviderEscrowIntentIdSeeds = {
-  jobId: number | bigint;
+  jobKey: Address;
 };
 
 export async function findProviderEscrowIntentIdPda(
   seeds: ProviderEscrowIntentIdSeeds,
   config: { programAddress?: Address | undefined } = {},
 ): Promise {
-  const { programAddress = "" as Address<""> } = config;
+  const {
+    programAddress = "HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q" as Address<"HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q">,
+  } = config;
   return await getProgramDerivedAddress({
     programAddress,
     seeds: [
@@ -32,7 +34,7 @@ export async function findProviderEscrowIntentIdPda(
           119, 95, 105, 110, 116, 101, 110, 116, 95, 105, 100,
         ]),
       ),
-      getU64Encoder().encode(seeds.jobId),
+      getAddressEncoder().encode(seeds.jobKey),
     ],
   });
 }
diff --git a/src/core/solana/generated/fund-transfer-hook/programs/fundTransferHook.ts b/src/core/solana/generated/fund-transfer-hook/programs/fundTransferHook.ts
index f99e2fb..ceb0b7d 100644
--- a/src/core/solana/generated/fund-transfer-hook/programs/fundTransferHook.ts
+++ b/src/core/solana/generated/fund-transfer-hook/programs/fundTransferHook.ts
@@ -20,21 +20,21 @@ import {
   parseAfterActionInstruction,
   parseBeforeActionInstruction,
   parseClaimEscrowRefundInstruction,
-  parseCloseIntentInstruction,
+  parseCloseJobHookAccountsInstruction,
   parseInitializeInstruction,
   parsePreCreateIntentInstruction,
   type ParsedAfterActionInstruction,
   type ParsedBeforeActionInstruction,
   type ParsedClaimEscrowRefundInstruction,
-  type ParsedCloseIntentInstruction,
+  type ParsedCloseJobHookAccountsInstruction,
   type ParsedInitializeInstruction,
   type ParsedPreCreateIntentInstruction,
 } from "../instructions/index.js";
 
-export const FUND_TRANSFER_HOOK_PROGRAM_ADDRESS = "" as Address<"">;
+export const FUND_TRANSFER_HOOK_PROGRAM_ADDRESS =
+  "HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q" as Address<"HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q">;
 
 export enum FundTransferHookAccount {
-  FundRequestIntentId,
   HookMetadata,
   HookState,
   Intent,
@@ -45,17 +45,6 @@ export function identifyFundTransferHookAccount(
   account: { data: ReadonlyUint8Array } | ReadonlyUint8Array,
 ): FundTransferHookAccount {
   const data = "data" in account ? account.data : account;
-  if (
-    containsBytes(
-      data,
-      fixEncoderSize(getBytesEncoder(), 8).encode(
-        new Uint8Array([115, 10, 133, 59, 77, 84, 106, 104]),
-      ),
-      0,
-    )
-  ) {
-    return FundTransferHookAccount.FundRequestIntentId;
-  }
   if (
     containsBytes(
       data,
@@ -109,7 +98,7 @@ export enum FundTransferHookInstruction {
   AfterAction,
   BeforeAction,
   ClaimEscrowRefund,
-  CloseIntent,
+  CloseJobHookAccounts,
   Initialize,
   PreCreateIntent,
 }
@@ -155,12 +144,12 @@ export function identifyFundTransferHookInstruction(
     containsBytes(
       data,
       fixEncoderSize(getBytesEncoder(), 8).encode(
-        new Uint8Array([112, 245, 154, 249, 57, 126, 54, 122]),
+        new Uint8Array([77, 224, 55, 159, 116, 59, 13, 125]),
       ),
       0,
     )
   ) {
-    return FundTransferHookInstruction.CloseIntent;
+    return FundTransferHookInstruction.CloseJobHookAccounts;
   }
   if (
     containsBytes(
@@ -189,7 +178,9 @@ export function identifyFundTransferHookInstruction(
   );
 }
 
-export type ParsedFundTransferHookInstruction =
+export type ParsedFundTransferHookInstruction<
+  TProgram extends string = "HaNGaZnXPBkZBU75BB3XJ8oah3yRuqDHqBfeeHL7f41Q",
+> =
   | ({
       instructionType: FundTransferHookInstruction.AfterAction;
     } & ParsedAfterActionInstruction)
@@ -200,8 +191,8 @@ export type ParsedFundTransferHookInstruction =
       instructionType: FundTransferHookInstruction.ClaimEscrowRefund;
     } & ParsedClaimEscrowRefundInstruction)
   | ({
-      instructionType: FundTransferHookInstruction.CloseIntent;
-    } & ParsedCloseIntentInstruction)
+      instructionType: FundTransferHookInstruction.CloseJobHookAccounts;
+    } & ParsedCloseJobHookAccountsInstruction)
   | ({
       instructionType: FundTransferHookInstruction.Initialize;
     } & ParsedInitializeInstruction)
@@ -235,11 +226,11 @@ export function parseFundTransferHookInstruction(
         ...parseClaimEscrowRefundInstruction(instruction),
       };
     }
-    case FundTransferHookInstruction.CloseIntent: {
+    case FundTransferHookInstruction.CloseJobHookAccounts: {
       assertIsInstructionWithAccounts(instruction);
       return {
-        instructionType: FundTransferHookInstruction.CloseIntent,
-        ...parseCloseIntentInstruction(instruction),
+        instructionType: FundTransferHookInstruction.CloseJobHookAccounts,
+        ...parseCloseJobHookAccountsInstruction(instruction),
       };
     }
     case FundTransferHookInstruction.Initialize: {
diff --git a/src/core/solana/generated/fund-transfer-hook/types/index.ts b/src/core/solana/generated/fund-transfer-hook/types/index.ts
index 5d9ad87..53ffc8e 100644
--- a/src/core/solana/generated/fund-transfer-hook/types/index.ts
+++ b/src/core/solana/generated/fund-transfer-hook/types/index.ts
@@ -8,6 +8,7 @@
 
 export * from "./intentClosed.js";
 export * from "./intentSigned.js";
+export * from "./jobHookAccountsClosed.js";
 export * from "./newIntent.js";
 export * from "./payableFundsEscrowed.js";
 export * from "./payableFundsRefunded.js";
diff --git a/src/core/solana/generated/fund-transfer-hook/types/jobHookAccountsClosed.ts b/src/core/solana/generated/fund-transfer-hook/types/jobHookAccountsClosed.ts
new file mode 100644
index 0000000..432bad0
--- /dev/null
+++ b/src/core/solana/generated/fund-transfer-hook/types/jobHookAccountsClosed.ts
@@ -0,0 +1,59 @@
+/**
+ * This code was AUTOGENERATED using the Codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun Codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getAddressDecoder,
+  getAddressEncoder,
+  getStructDecoder,
+  getStructEncoder,
+  getU64Decoder,
+  getU64Encoder,
+  type Address,
+  type FixedSizeCodec,
+  type FixedSizeDecoder,
+  type FixedSizeEncoder,
+} from "@solana/kit";
+
+export type JobHookAccountsClosed = {
+  jobKey: Address;
+  rentDestination: Address;
+  lamports: bigint;
+};
+
+export type JobHookAccountsClosedArgs = {
+  jobKey: Address;
+  rentDestination: Address;
+  lamports: number | bigint;
+};
+
+export function getJobHookAccountsClosedEncoder(): FixedSizeEncoder {
+  return getStructEncoder([
+    ["jobKey", getAddressEncoder()],
+    ["rentDestination", getAddressEncoder()],
+    ["lamports", getU64Encoder()],
+  ]);
+}
+
+export function getJobHookAccountsClosedDecoder(): FixedSizeDecoder {
+  return getStructDecoder([
+    ["jobKey", getAddressDecoder()],
+    ["rentDestination", getAddressDecoder()],
+    ["lamports", getU64Decoder()],
+  ]);
+}
+
+export function getJobHookAccountsClosedCodec(): FixedSizeCodec<
+  JobHookAccountsClosedArgs,
+  JobHookAccountsClosed
+> {
+  return combineCodec(
+    getJobHookAccountsClosedEncoder(),
+    getJobHookAccountsClosedDecoder(),
+  );
+}
diff --git a/src/core/solana/generated/fund-transfer-hook/types/newIntent.ts b/src/core/solana/generated/fund-transfer-hook/types/newIntent.ts
index d754560..5c218a4 100644
--- a/src/core/solana/generated/fund-transfer-hook/types/newIntent.ts
+++ b/src/core/solana/generated/fund-transfer-hook/types/newIntent.ts
@@ -20,17 +20,17 @@ import {
   type FixedSizeEncoder,
 } from "@solana/kit";
 
-export type NewIntent = { jobId: bigint; actor: Address; intentId: bigint };
+export type NewIntent = { jobKey: Address; actor: Address; intentId: bigint };
 
 export type NewIntentArgs = {
-  jobId: number | bigint;
+  jobKey: Address;
   actor: Address;
   intentId: number | bigint;
 };
 
 export function getNewIntentEncoder(): FixedSizeEncoder {
   return getStructEncoder([
-    ["jobId", getU64Encoder()],
+    ["jobKey", getAddressEncoder()],
     ["actor", getAddressEncoder()],
     ["intentId", getU64Encoder()],
   ]);
@@ -38,7 +38,7 @@ export function getNewIntentEncoder(): FixedSizeEncoder {
 
 export function getNewIntentDecoder(): FixedSizeDecoder {
   return getStructDecoder([
-    ["jobId", getU64Decoder()],
+    ["jobKey", getAddressDecoder()],
     ["actor", getAddressDecoder()],
     ["intentId", getU64Decoder()],
   ]);
diff --git a/src/core/solana/generated/fund-transfer-hook/types/payableFundsEscrowed.ts b/src/core/solana/generated/fund-transfer-hook/types/payableFundsEscrowed.ts
index b9ae675..9b260ea 100644
--- a/src/core/solana/generated/fund-transfer-hook/types/payableFundsEscrowed.ts
+++ b/src/core/solana/generated/fund-transfer-hook/types/payableFundsEscrowed.ts
@@ -21,7 +21,7 @@ import {
 } from "@solana/kit";
 
 export type PayableFundsEscrowed = {
-  jobId: bigint;
+  jobKey: Address;
   intentId: bigint;
   sender: Address;
   token: Address;
@@ -29,7 +29,7 @@ export type PayableFundsEscrowed = {
 };
 
 export type PayableFundsEscrowedArgs = {
-  jobId: number | bigint;
+  jobKey: Address;
   intentId: number | bigint;
   sender: Address;
   token: Address;
@@ -38,7 +38,7 @@ export type PayableFundsEscrowedArgs = {
 
 export function getPayableFundsEscrowedEncoder(): FixedSizeEncoder {
   return getStructEncoder([
-    ["jobId", getU64Encoder()],
+    ["jobKey", getAddressEncoder()],
     ["intentId", getU64Encoder()],
     ["sender", getAddressEncoder()],
     ["token", getAddressEncoder()],
@@ -48,7 +48,7 @@ export function getPayableFundsEscrowedEncoder(): FixedSizeEncoder {
   return getStructDecoder([
-    ["jobId", getU64Decoder()],
+    ["jobKey", getAddressDecoder()],
     ["intentId", getU64Decoder()],
     ["sender", getAddressDecoder()],
     ["token", getAddressDecoder()],
diff --git a/src/core/solana/generated/fund-transfer-hook/types/payableFundsRefunded.ts b/src/core/solana/generated/fund-transfer-hook/types/payableFundsRefunded.ts
index a92c9b4..5e95c2a 100644
--- a/src/core/solana/generated/fund-transfer-hook/types/payableFundsRefunded.ts
+++ b/src/core/solana/generated/fund-transfer-hook/types/payableFundsRefunded.ts
@@ -21,7 +21,7 @@ import {
 } from "@solana/kit";
 
 export type PayableFundsRefunded = {
-  jobId: bigint;
+  jobKey: Address;
   intentId: bigint;
   sender: Address;
   token: Address;
@@ -29,7 +29,7 @@ export type PayableFundsRefunded = {
 };
 
 export type PayableFundsRefundedArgs = {
-  jobId: number | bigint;
+  jobKey: Address;
   intentId: number | bigint;
   sender: Address;
   token: Address;
@@ -38,7 +38,7 @@ export type PayableFundsRefundedArgs = {
 
 export function getPayableFundsRefundedEncoder(): FixedSizeEncoder {
   return getStructEncoder([
-    ["jobId", getU64Encoder()],
+    ["jobKey", getAddressEncoder()],
     ["intentId", getU64Encoder()],
     ["sender", getAddressEncoder()],
     ["token", getAddressEncoder()],
@@ -48,7 +48,7 @@ export function getPayableFundsRefundedEncoder(): FixedSizeEncoder {
   return getStructDecoder([
-    ["jobId", getU64Decoder()],
+    ["jobKey", getAddressDecoder()],
     ["intentId", getU64Decoder()],
     ["sender", getAddressDecoder()],
     ["token", getAddressDecoder()],
diff --git a/src/core/solana/generated/fund-transfer-hook/types/payableTransferExecuted.ts b/src/core/solana/generated/fund-transfer-hook/types/payableTransferExecuted.ts
index 380cfae..56acf15 100644
--- a/src/core/solana/generated/fund-transfer-hook/types/payableTransferExecuted.ts
+++ b/src/core/solana/generated/fund-transfer-hook/types/payableTransferExecuted.ts
@@ -21,7 +21,7 @@ import {
 } from "@solana/kit";
 
 export type PayableTransferExecuted = {
-  jobId: bigint;
+  jobKey: Address;
   intentId: bigint;
   from: Address;
   to: Address;
@@ -30,7 +30,7 @@ export type PayableTransferExecuted = {
 };
 
 export type PayableTransferExecutedArgs = {
-  jobId: number | bigint;
+  jobKey: Address;
   intentId: number | bigint;
   from: Address;
   to: Address;
@@ -40,7 +40,7 @@ export type PayableTransferExecutedArgs = {
 
 export function getPayableTransferExecutedEncoder(): FixedSizeEncoder {
   return getStructEncoder([
-    ["jobId", getU64Encoder()],
+    ["jobKey", getAddressEncoder()],
     ["intentId", getU64Encoder()],
     ["from", getAddressEncoder()],
     ["to", getAddressEncoder()],
@@ -51,7 +51,7 @@ export function getPayableTransferExecutedEncoder(): FixedSizeEncoder {
   return getStructDecoder([
-    ["jobId", getU64Decoder()],
+    ["jobKey", getAddressDecoder()],
     ["intentId", getU64Decoder()],
     ["from", getAddressDecoder()],
     ["to", getAddressDecoder()],
diff --git a/src/core/solana/generated/multi-hook-router/accounts/hookRouter.ts b/src/core/solana/generated/multi-hook-router/accounts/hookRouter.ts
index c93fa0f..f699bb2 100644
--- a/src/core/solana/generated/multi-hook-router/accounts/hookRouter.ts
+++ b/src/core/solana/generated/multi-hook-router/accounts/hookRouter.ts
@@ -23,8 +23,6 @@ import {
   getBytesEncoder,
   getStructDecoder,
   getStructEncoder,
-  getU64Decoder,
-  getU64Encoder,
   getU8Decoder,
   getU8Encoder,
   transformEncoder,
@@ -57,7 +55,7 @@ export function getHookRouterDiscriminatorBytes() {
 
 export type HookRouter = {
   discriminator: ReadonlyUint8Array;
-  jobId: bigint;
+  jobKey: Address;
   setBudgetHooks: Array
; fundHooks: Array
; submitHooks: Array
; @@ -69,7 +67,7 @@ export type HookRouter = { }; export type HookRouterArgs = { - jobId: number | bigint; + jobKey: Address; setBudgetHooks: Array
; fundHooks: Array
; submitHooks: Array
; @@ -85,7 +83,7 @@ export function getHookRouterEncoder(): Encoder { return transformEncoder( getStructEncoder([ ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["setBudgetHooks", getArrayEncoder(getAddressEncoder())], ["fundHooks", getArrayEncoder(getAddressEncoder())], ["submitHooks", getArrayEncoder(getAddressEncoder())], @@ -102,7 +100,7 @@ export function getHookRouterEncoder(): Encoder { export function getHookRouterDecoder(): Decoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["setBudgetHooks", getArrayDecoder(getAddressDecoder())], ["fundHooks", getArrayDecoder(getAddressDecoder())], ["submitHooks", getArrayDecoder(getAddressDecoder())], diff --git a/src/core/solana/generated/multi-hook-router/errors/multiHookRouter.ts b/src/core/solana/generated/multi-hook-router/errors/multiHookRouter.ts index f22f832..eba25a4 100644 --- a/src/core/solana/generated/multi-hook-router/errors/multiHookRouter.ts +++ b/src/core/solana/generated/multi-hook-router/errors/multiHookRouter.ts @@ -62,6 +62,14 @@ export const MULTI_HOOK_ROUTER_ERROR__INVALID_HOOK_METADATA = 0x1785; // 6021 export const MULTI_HOOK_ROUTER_ERROR__INVALID_OPT_PARAMS_MODE = 0x1786; // 6022 /** HookNotExecutable: Hook program account is not executable */ export const MULTI_HOOK_ROUTER_ERROR__HOOK_NOT_EXECUTABLE = 0x1787; // 6023 +/** RentPayerMissing: Growing the router needs a rent payer and the system program */ +export const MULTI_HOOK_ROUTER_ERROR__RENT_PAYER_MISSING = 0x1788; // 6024 +/** InvalidRefundRecipient: Rent destination is not acp_state.sponsor */ +export const MULTI_HOOK_ROUTER_ERROR__INVALID_REFUND_RECIPIENT = 0x1789; // 6025 +/** WrongStatus: Job is not in a terminal state */ +export const MULTI_HOOK_ROUTER_ERROR__WRONG_STATUS = 0x178a; // 6026 +/** TooManyCachedHooks: Cached hook metadata exceeds 5 x max_hooks_per_job entries */ +export const MULTI_HOOK_ROUTER_ERROR__TOO_MANY_CACHED_HOOKS = 0x178b; // 6027 export type MultiHookRouterError = | typeof MULTI_HOOK_ROUTER_ERROR__ACCOUNT_SLICE_OUT_OF_BOUNDS @@ -78,15 +86,19 @@ export type MultiHookRouterError = | typeof MULTI_HOOK_ROUTER_ERROR__INVALID_JOB | typeof MULTI_HOOK_ROUTER_ERROR__INVALID_MAX_HOOKS | typeof MULTI_HOOK_ROUTER_ERROR__INVALID_OPT_PARAMS_MODE + | typeof MULTI_HOOK_ROUTER_ERROR__INVALID_REFUND_RECIPIENT | typeof MULTI_HOOK_ROUTER_ERROR__INVALID_SELECTOR | typeof MULTI_HOOK_ROUTER_ERROR__ONLY_A_C_P_CONTRACT | typeof MULTI_HOOK_ROUTER_ERROR__ONLY_JOB_CLIENT | typeof MULTI_HOOK_ROUTER_ERROR__OPT_PARAMS_TOO_SHORT + | typeof MULTI_HOOK_ROUTER_ERROR__RENT_PAYER_MISSING | typeof MULTI_HOOK_ROUTER_ERROR__SUB_HOOK_NOT_WHITELISTED + | typeof MULTI_HOOK_ROUTER_ERROR__TOO_MANY_CACHED_HOOKS | typeof MULTI_HOOK_ROUTER_ERROR__TOO_MANY_HOOKS | typeof MULTI_HOOK_ROUTER_ERROR__UNAUTHORIZED_CALLER | typeof MULTI_HOOK_ROUTER_ERROR__WRONG_HOOK_PROGRAM | typeof MULTI_HOOK_ROUTER_ERROR__WRONG_HOOK_WHITELIST + | typeof MULTI_HOOK_ROUTER_ERROR__WRONG_STATUS | typeof MULTI_HOOK_ROUTER_ERROR__ZERO_ADDRESS; let multiHookRouterErrorMessages: @@ -108,15 +120,19 @@ if (process.env.NODE_ENV !== "production") { [MULTI_HOOK_ROUTER_ERROR__INVALID_JOB]: `Invalid job account`, [MULTI_HOOK_ROUTER_ERROR__INVALID_MAX_HOOKS]: `Max hooks per job must be >= 1`, [MULTI_HOOK_ROUTER_ERROR__INVALID_OPT_PARAMS_MODE]: `Invalid opt_params mode byte: must be 0x00 (Broadcast) or 0x01 (PerHook)`, + [MULTI_HOOK_ROUTER_ERROR__INVALID_REFUND_RECIPIENT]: `Rent destination is not acp_state.sponsor`, [MULTI_HOOK_ROUTER_ERROR__INVALID_SELECTOR]: `Action is not a hookable selector (only SetBudget, Fund, Submit, Complete, Reject)`, [MULTI_HOOK_ROUTER_ERROR__ONLY_A_C_P_CONTRACT]: `Only the ACP contract can call this`, [MULTI_HOOK_ROUTER_ERROR__ONLY_JOB_CLIENT]: `Only the job client can configure hooks`, [MULTI_HOOK_ROUTER_ERROR__OPT_PARAMS_TOO_SHORT]: `opt_params too short to parse header`, + [MULTI_HOOK_ROUTER_ERROR__RENT_PAYER_MISSING]: `Growing the router needs a rent payer and the system program`, [MULTI_HOOK_ROUTER_ERROR__SUB_HOOK_NOT_WHITELISTED]: `Sub-hook is not whitelisted on the ACP core`, + [MULTI_HOOK_ROUTER_ERROR__TOO_MANY_CACHED_HOOKS]: `Cached hook metadata exceeds 5 x max_hooks_per_job entries`, [MULTI_HOOK_ROUTER_ERROR__TOO_MANY_HOOKS]: `Too many hooks for this job`, [MULTI_HOOK_ROUTER_ERROR__UNAUTHORIZED_CALLER]: `Unauthorized caller`, [MULTI_HOOK_ROUTER_ERROR__WRONG_HOOK_PROGRAM]: `Wrong hook program in remaining accounts`, [MULTI_HOOK_ROUTER_ERROR__WRONG_HOOK_WHITELIST]: `Wrong hook whitelist account: not the canonical PDA for this hook`, + [MULTI_HOOK_ROUTER_ERROR__WRONG_STATUS]: `Job is not in a terminal state`, [MULTI_HOOK_ROUTER_ERROR__ZERO_ADDRESS]: `Zero address not allowed`, }; } diff --git a/src/core/solana/generated/multi-hook-router/instructions/addHook.ts b/src/core/solana/generated/multi-hook-router/instructions/addHook.ts index 2fe2509..6772cc5 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/addHook.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/addHook.ts @@ -16,8 +16,6 @@ import { getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -31,10 +29,10 @@ import { type InstructionWithAccounts, type InstructionWithData, type ReadonlyAccount, - type ReadonlySignerAccount, type ReadonlyUint8Array, type TransactionSigner, type WritableAccount, + type WritableSignerAccount, } from "@solana/kit"; import { findHookRouterPda, findRouterStatePda } from "../pdas/index.js"; import { MULTI_HOOK_ROUTER_PROGRAM_ADDRESS } from "../programs/index.js"; @@ -60,13 +58,15 @@ export type AddHookInstruction< TAccountRouterState extends string | AccountMeta = string, TAccountHookWhitelist extends string | AccountMeta = string, TAccountHookMetadata extends string | AccountMeta = string, + TAccountSystemProgram extends string | AccountMeta = + "11111111111111111111111111111111", TRemainingAccounts extends readonly AccountMeta[] = [], > = Instruction & InstructionWithData & InstructionWithAccounts< [ TAccountClient extends string - ? ReadonlySignerAccount & + ? WritableSignerAccount & AccountSignerMeta : TAccountClient, TAccountJob extends string ? ReadonlyAccount : TAccountJob, @@ -82,19 +82,22 @@ export type AddHookInstruction< TAccountHookMetadata extends string ? ReadonlyAccount : TAccountHookMetadata, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, ...TRemainingAccounts, ] >; export type AddHookInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; hook: Address; }; export type AddHookInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; hook: Address; }; @@ -103,7 +106,7 @@ export function getAddHookInstructionDataEncoder(): FixedSizeEncoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hook", getAddressDecoder()], ]); @@ -137,14 +140,20 @@ export type AddHookAsyncInput< TAccountRouterState extends string = string, TAccountHookWhitelist extends string = string, TAccountHookMetadata extends string = string, + TAccountSystemProgram extends string = string, > = { + /** + * Writable and the rent payer: adding a hook grows the router account, and + * the top-up needs a system-owned source. + */ client: TransactionSigner; job: Address; hookRouter?: Address; routerState?: Address; hookWhitelist: Address; hookMetadata: Address; - jobId: AddHookInstructionDataArgs["jobId"]; + systemProgram?: Address; + jobKey: AddHookInstructionDataArgs["jobKey"]; action: AddHookInstructionDataArgs["action"]; hook: AddHookInstructionDataArgs["hook"]; }; @@ -156,6 +165,7 @@ export async function getAddHookInstructionAsync< TAccountRouterState extends string, TAccountHookWhitelist extends string, TAccountHookMetadata extends string, + TAccountSystemProgram extends string, TProgramAddress extends Address = typeof MULTI_HOOK_ROUTER_PROGRAM_ADDRESS, >( input: AddHookAsyncInput< @@ -164,7 +174,8 @@ export async function getAddHookInstructionAsync< TAccountHookRouter, TAccountRouterState, TAccountHookWhitelist, - TAccountHookMetadata + TAccountHookMetadata, + TAccountSystemProgram >, config?: { programAddress?: TProgramAddress }, ): Promise< @@ -175,7 +186,8 @@ export async function getAddHookInstructionAsync< TAccountHookRouter, TAccountRouterState, TAccountHookWhitelist, - TAccountHookMetadata + TAccountHookMetadata, + TAccountSystemProgram > > { // Program address. @@ -184,12 +196,13 @@ export async function getAddHookInstructionAsync< // Original accounts. const originalAccounts = { - client: { value: input.client ?? null, isWritable: false }, + client: { value: input.client ?? null, isWritable: true }, job: { value: input.job ?? null, isWritable: false }, hookRouter: { value: input.hookRouter ?? null, isWritable: true }, routerState: { value: input.routerState ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, hookMetadata: { value: input.hookMetadata ?? null, isWritable: false }, + systemProgram: { value: input.systemProgram ?? null, isWritable: false }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -202,12 +215,16 @@ export async function getAddHookInstructionAsync< // Resolve default values. if (!accounts.hookRouter.value) { accounts.hookRouter.value = await findHookRouterPda({ - jobId: expectSome(args.jobId), + jobKey: expectSome(args.jobKey), }); } if (!accounts.routerState.value) { accounts.routerState.value = await findRouterStatePda(); } + if (!accounts.systemProgram.value) { + accounts.systemProgram.value = + "11111111111111111111111111111111" as Address<"11111111111111111111111111111111">; + } const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); return Object.freeze({ @@ -218,6 +235,7 @@ export async function getAddHookInstructionAsync< getAccountMeta(accounts.routerState), getAccountMeta(accounts.hookWhitelist), getAccountMeta(accounts.hookMetadata), + getAccountMeta(accounts.systemProgram), ], data: getAddHookInstructionDataEncoder().encode( args as AddHookInstructionDataArgs, @@ -230,7 +248,8 @@ export async function getAddHookInstructionAsync< TAccountHookRouter, TAccountRouterState, TAccountHookWhitelist, - TAccountHookMetadata + TAccountHookMetadata, + TAccountSystemProgram >); } @@ -241,14 +260,20 @@ export type AddHookInput< TAccountRouterState extends string = string, TAccountHookWhitelist extends string = string, TAccountHookMetadata extends string = string, + TAccountSystemProgram extends string = string, > = { + /** + * Writable and the rent payer: adding a hook grows the router account, and + * the top-up needs a system-owned source. + */ client: TransactionSigner; job: Address; hookRouter: Address; routerState: Address; hookWhitelist: Address; hookMetadata: Address; - jobId: AddHookInstructionDataArgs["jobId"]; + systemProgram?: Address; + jobKey: AddHookInstructionDataArgs["jobKey"]; action: AddHookInstructionDataArgs["action"]; hook: AddHookInstructionDataArgs["hook"]; }; @@ -260,6 +285,7 @@ export function getAddHookInstruction< TAccountRouterState extends string, TAccountHookWhitelist extends string, TAccountHookMetadata extends string, + TAccountSystemProgram extends string, TProgramAddress extends Address = typeof MULTI_HOOK_ROUTER_PROGRAM_ADDRESS, >( input: AddHookInput< @@ -268,7 +294,8 @@ export function getAddHookInstruction< TAccountHookRouter, TAccountRouterState, TAccountHookWhitelist, - TAccountHookMetadata + TAccountHookMetadata, + TAccountSystemProgram >, config?: { programAddress?: TProgramAddress }, ): AddHookInstruction< @@ -278,7 +305,8 @@ export function getAddHookInstruction< TAccountHookRouter, TAccountRouterState, TAccountHookWhitelist, - TAccountHookMetadata + TAccountHookMetadata, + TAccountSystemProgram > { // Program address. const programAddress = @@ -286,12 +314,13 @@ export function getAddHookInstruction< // Original accounts. const originalAccounts = { - client: { value: input.client ?? null, isWritable: false }, + client: { value: input.client ?? null, isWritable: true }, job: { value: input.job ?? null, isWritable: false }, hookRouter: { value: input.hookRouter ?? null, isWritable: true }, routerState: { value: input.routerState ?? null, isWritable: false }, hookWhitelist: { value: input.hookWhitelist ?? null, isWritable: false }, hookMetadata: { value: input.hookMetadata ?? null, isWritable: false }, + systemProgram: { value: input.systemProgram ?? null, isWritable: false }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -301,6 +330,12 @@ export function getAddHookInstruction< // Original args. const args = { ...input }; + // Resolve default values. + if (!accounts.systemProgram.value) { + accounts.systemProgram.value = + "11111111111111111111111111111111" as Address<"11111111111111111111111111111111">; + } + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); return Object.freeze({ accounts: [ @@ -310,6 +345,7 @@ export function getAddHookInstruction< getAccountMeta(accounts.routerState), getAccountMeta(accounts.hookWhitelist), getAccountMeta(accounts.hookMetadata), + getAccountMeta(accounts.systemProgram), ], data: getAddHookInstructionDataEncoder().encode( args as AddHookInstructionDataArgs, @@ -322,7 +358,8 @@ export function getAddHookInstruction< TAccountHookRouter, TAccountRouterState, TAccountHookWhitelist, - TAccountHookMetadata + TAccountHookMetadata, + TAccountSystemProgram >); } @@ -332,12 +369,17 @@ export type ParsedAddHookInstruction< > = { programAddress: Address; accounts: { + /** + * Writable and the rent payer: adding a hook grows the router account, and + * the top-up needs a system-owned source. + */ client: TAccountMetas[0]; job: TAccountMetas[1]; hookRouter: TAccountMetas[2]; routerState: TAccountMetas[3]; hookWhitelist: TAccountMetas[4]; hookMetadata: TAccountMetas[5]; + systemProgram: TAccountMetas[6]; }; data: AddHookInstructionData; }; @@ -350,7 +392,7 @@ export function parseAddHookInstruction< InstructionWithAccounts & InstructionWithData, ): ParsedAddHookInstruction { - if (instruction.accounts.length < 6) { + if (instruction.accounts.length < 7) { // TODO: Coded error. throw new Error("Not enough accounts"); } @@ -369,6 +411,7 @@ export function parseAddHookInstruction< routerState: getNextAccount(), hookWhitelist: getNextAccount(), hookMetadata: getNextAccount(), + systemProgram: getNextAccount(), }, data: getAddHookInstructionDataDecoder().decode(instruction.data), }; diff --git a/src/core/solana/generated/multi-hook-router/instructions/afterAction.ts b/src/core/solana/generated/multi-hook-router/instructions/afterAction.ts index 1fa0472..cdb1e2b 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/afterAction.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/afterAction.ts @@ -20,8 +20,6 @@ import { getStructEncoder, getU32Decoder, getU32Encoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -80,7 +78,7 @@ export type AfterActionInstruction< export type AfterActionInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -88,7 +86,7 @@ export type AfterActionInstructionData = { }; export type AfterActionInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -99,7 +97,7 @@ export function getAfterActionInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["optParams", addDecoderSizePrefix(getBytesDecoder(), getU32Decoder())], ["caller", getAddressDecoder()], @@ -139,7 +137,7 @@ export type AfterActionAsyncInput< hookRouter?: Address; routerState?: Address; instructionsSysvar?: Address; - jobId: AfterActionInstructionDataArgs["jobId"]; + jobKey: AfterActionInstructionDataArgs["jobKey"]; action: AfterActionInstructionDataArgs["action"]; optParams: AfterActionInstructionDataArgs["optParams"]; caller: AfterActionInstructionDataArgs["caller"]; @@ -190,7 +188,7 @@ export async function getAfterActionInstructionAsync< // Resolve default values. if (!accounts.hookRouter.value) { accounts.hookRouter.value = await findHookRouterPda({ - jobId: expectSome(args.jobId), + jobKey: expectSome(args.jobKey), }); } if (!accounts.routerState.value) { @@ -229,7 +227,7 @@ export type AfterActionInput< hookRouter: Address; routerState: Address; instructionsSysvar?: Address; - jobId: AfterActionInstructionDataArgs["jobId"]; + jobKey: AfterActionInstructionDataArgs["jobKey"]; action: AfterActionInstructionDataArgs["action"]; optParams: AfterActionInstructionDataArgs["optParams"]; caller: AfterActionInstructionDataArgs["caller"]; diff --git a/src/core/solana/generated/multi-hook-router/instructions/batchConfigureHooks.ts b/src/core/solana/generated/multi-hook-router/instructions/batchConfigureHooks.ts index 2f9e743..e2dc12c 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/batchConfigureHooks.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/batchConfigureHooks.ts @@ -20,8 +20,6 @@ import { getOptionEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, transformEncoder, type AccountMeta, type AccountSignerMeta, @@ -91,7 +89,7 @@ export type BatchConfigureHooksInstruction< export type BatchConfigureHooksInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; setBudget: Option>; fund: Option>; submit: Option>; @@ -100,7 +98,7 @@ export type BatchConfigureHooksInstructionData = { }; export type BatchConfigureHooksInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; setBudget: OptionOrNullable>; fund: OptionOrNullable>; submit: OptionOrNullable>; @@ -112,7 +110,7 @@ export function getBatchConfigureHooksInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["setBudget", getOptionDecoder(getArrayDecoder(getAddressDecoder()))], ["fund", getOptionDecoder(getArrayDecoder(getAddressDecoder()))], ["submit", getOptionDecoder(getArrayDecoder(getAddressDecoder()))], @@ -157,10 +155,14 @@ export type BatchConfigureHooksAsyncInput< > = { client: TransactionSigner; job: Address; + /** + * `ConfigureHooks::hook_router` for why `init_if_needed` cannot be used on + * an exact-fit account. The seeds below still pin the address. + */ hookRouter?: Address; routerState?: Address; systemProgram?: Address; - jobId: BatchConfigureHooksInstructionDataArgs["jobId"]; + jobKey: BatchConfigureHooksInstructionDataArgs["jobKey"]; setBudget: BatchConfigureHooksInstructionDataArgs["setBudget"]; fund: BatchConfigureHooksInstructionDataArgs["fund"]; submit: BatchConfigureHooksInstructionDataArgs["submit"]; @@ -217,7 +219,7 @@ export async function getBatchConfigureHooksInstructionAsync< // Resolve default values. if (!accounts.hookRouter.value) { accounts.hookRouter.value = await findHookRouterPda({ - jobId: expectSome(args.jobId), + jobKey: expectSome(args.jobKey), }); } if (!accounts.routerState.value) { @@ -260,10 +262,14 @@ export type BatchConfigureHooksInput< > = { client: TransactionSigner; job: Address; + /** + * `ConfigureHooks::hook_router` for why `init_if_needed` cannot be used on + * an exact-fit account. The seeds below still pin the address. + */ hookRouter: Address; routerState: Address; systemProgram?: Address; - jobId: BatchConfigureHooksInstructionDataArgs["jobId"]; + jobKey: BatchConfigureHooksInstructionDataArgs["jobKey"]; setBudget: BatchConfigureHooksInstructionDataArgs["setBudget"]; fund: BatchConfigureHooksInstructionDataArgs["fund"]; submit: BatchConfigureHooksInstructionDataArgs["submit"]; @@ -352,6 +358,10 @@ export type ParsedBatchConfigureHooksInstruction< accounts: { client: TAccountMetas[0]; job: TAccountMetas[1]; + /** + * `ConfigureHooks::hook_router` for why `init_if_needed` cannot be used on + * an exact-fit account. The seeds below still pin the address. + */ hookRouter: TAccountMetas[2]; routerState: TAccountMetas[3]; systemProgram: TAccountMetas[4]; diff --git a/src/core/solana/generated/multi-hook-router/instructions/beforeAction.ts b/src/core/solana/generated/multi-hook-router/instructions/beforeAction.ts index 151bc8b..76ab572 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/beforeAction.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/beforeAction.ts @@ -20,8 +20,6 @@ import { getStructEncoder, getU32Decoder, getU32Encoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -80,7 +78,7 @@ export type BeforeActionInstruction< export type BeforeActionInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -88,7 +86,7 @@ export type BeforeActionInstructionData = { }; export type BeforeActionInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -99,7 +97,7 @@ export function getBeforeActionInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["optParams", addDecoderSizePrefix(getBytesDecoder(), getU32Decoder())], ["caller", getAddressDecoder()], @@ -139,7 +137,7 @@ export type BeforeActionAsyncInput< hookRouter?: Address; routerState?: Address; instructionsSysvar?: Address; - jobId: BeforeActionInstructionDataArgs["jobId"]; + jobKey: BeforeActionInstructionDataArgs["jobKey"]; action: BeforeActionInstructionDataArgs["action"]; optParams: BeforeActionInstructionDataArgs["optParams"]; caller: BeforeActionInstructionDataArgs["caller"]; @@ -190,7 +188,7 @@ export async function getBeforeActionInstructionAsync< // Resolve default values. if (!accounts.hookRouter.value) { accounts.hookRouter.value = await findHookRouterPda({ - jobId: expectSome(args.jobId), + jobKey: expectSome(args.jobKey), }); } if (!accounts.routerState.value) { @@ -229,7 +227,7 @@ export type BeforeActionInput< hookRouter: Address; routerState: Address; instructionsSysvar?: Address; - jobId: BeforeActionInstructionDataArgs["jobId"]; + jobKey: BeforeActionInstructionDataArgs["jobKey"]; action: BeforeActionInstructionDataArgs["action"]; optParams: BeforeActionInstructionDataArgs["optParams"]; caller: BeforeActionInstructionDataArgs["caller"]; diff --git a/src/core/solana/generated/multi-hook-router/instructions/closeHookRouter.ts b/src/core/solana/generated/multi-hook-router/instructions/closeHookRouter.ts new file mode 100644 index 0000000..ce3b547 --- /dev/null +++ b/src/core/solana/generated/multi-hook-router/instructions/closeHookRouter.ts @@ -0,0 +1,383 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + combineCodec, + fixDecoderSize, + fixEncoderSize, + getAddressDecoder, + getAddressEncoder, + getBytesDecoder, + getBytesEncoder, + getStructDecoder, + getStructEncoder, + transformEncoder, + type AccountMeta, + type AccountSignerMeta, + type Address, + type FixedSizeCodec, + type FixedSizeDecoder, + type FixedSizeEncoder, + type Instruction, + type InstructionWithAccounts, + type InstructionWithData, + type ReadonlyAccount, + type ReadonlySignerAccount, + type ReadonlyUint8Array, + type TransactionSigner, + type WritableAccount, +} from "@solana/kit"; +import { findHookRouterPda, findRouterStatePda } from "../pdas/index.js"; +import { MULTI_HOOK_ROUTER_PROGRAM_ADDRESS } from "../programs/index.js"; +import { + expectSome, + getAccountMetaFactory, + type ResolvedAccount, +} from "../shared/index.js"; + +export const CLOSE_HOOK_ROUTER_DISCRIMINATOR = new Uint8Array([ + 204, 155, 217, 12, 192, 12, 49, 65, +]); + +export function getCloseHookRouterDiscriminatorBytes() { + return fixEncoderSize(getBytesEncoder(), 8).encode( + CLOSE_HOOK_ROUTER_DISCRIMINATOR, + ); +} + +export type CloseHookRouterInstruction< + TProgram extends string = typeof MULTI_HOOK_ROUTER_PROGRAM_ADDRESS, + TAccountCaller extends string | AccountMeta = string, + TAccountJob extends string | AccountMeta = string, + TAccountHookRouter extends string | AccountMeta = string, + TAccountRouterState extends string | AccountMeta = string, + TAccountAcpState extends string | AccountMeta = string, + TAccountSponsor extends string | AccountMeta = string, + TRemainingAccounts extends readonly AccountMeta[] = [], +> = Instruction & + InstructionWithData & + InstructionWithAccounts< + [ + TAccountCaller extends string + ? ReadonlySignerAccount & + AccountSignerMeta + : TAccountCaller, + TAccountJob extends string ? ReadonlyAccount : TAccountJob, + TAccountHookRouter extends string + ? WritableAccount + : TAccountHookRouter, + TAccountRouterState extends string + ? ReadonlyAccount + : TAccountRouterState, + TAccountAcpState extends string + ? ReadonlyAccount + : TAccountAcpState, + TAccountSponsor extends string + ? WritableAccount + : TAccountSponsor, + ...TRemainingAccounts, + ] + >; + +export type CloseHookRouterInstructionData = { + discriminator: ReadonlyUint8Array; + jobKey: Address; +}; + +export type CloseHookRouterInstructionDataArgs = { jobKey: Address }; + +export function getCloseHookRouterInstructionDataEncoder(): FixedSizeEncoder { + return transformEncoder( + getStructEncoder([ + ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], + ["jobKey", getAddressEncoder()], + ]), + (value) => ({ ...value, discriminator: CLOSE_HOOK_ROUTER_DISCRIMINATOR }), + ); +} + +export function getCloseHookRouterInstructionDataDecoder(): FixedSizeDecoder { + return getStructDecoder([ + ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], + ["jobKey", getAddressDecoder()], + ]); +} + +export function getCloseHookRouterInstructionDataCodec(): FixedSizeCodec< + CloseHookRouterInstructionDataArgs, + CloseHookRouterInstructionData +> { + return combineCodec( + getCloseHookRouterInstructionDataEncoder(), + getCloseHookRouterInstructionDataDecoder(), + ); +} + +export type CloseHookRouterAsyncInput< + TAccountCaller extends string = string, + TAccountJob extends string = string, + TAccountHookRouter extends string = string, + TAccountRouterState extends string = string, + TAccountAcpState extends string = string, + TAccountSponsor extends string = string, +> = { + /** + * Permissionless. The caller receives nothing — every lamport goes to + * `acp_state.sponsor` — so there is no incentive to race and no + * rent-farming surface. That also lets this double as a sweep for any + * router whose terminal-transaction bundle failed to land. + */ + caller: TransactionSigner; + /** `deserialize_job` against `router_state.acp_program`. */ + job: Address; + /** so it cannot be `Account`. Seeds pin the address. */ + hookRouter?: Address; + routerState?: Address; + acpState: Address; + sponsor: Address; + jobKey: CloseHookRouterInstructionDataArgs["jobKey"]; +}; + +export async function getCloseHookRouterInstructionAsync< + TAccountCaller extends string, + TAccountJob extends string, + TAccountHookRouter extends string, + TAccountRouterState extends string, + TAccountAcpState extends string, + TAccountSponsor extends string, + TProgramAddress extends Address = typeof MULTI_HOOK_ROUTER_PROGRAM_ADDRESS, +>( + input: CloseHookRouterAsyncInput< + TAccountCaller, + TAccountJob, + TAccountHookRouter, + TAccountRouterState, + TAccountAcpState, + TAccountSponsor + >, + config?: { programAddress?: TProgramAddress }, +): Promise< + CloseHookRouterInstruction< + TProgramAddress, + TAccountCaller, + TAccountJob, + TAccountHookRouter, + TAccountRouterState, + TAccountAcpState, + TAccountSponsor + > +> { + // Program address. + const programAddress = + config?.programAddress ?? MULTI_HOOK_ROUTER_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + caller: { value: input.caller ?? null, isWritable: false }, + job: { value: input.job ?? null, isWritable: false }, + hookRouter: { value: input.hookRouter ?? null, isWritable: true }, + routerState: { value: input.routerState ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: false }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + // Resolve default values. + if (!accounts.hookRouter.value) { + accounts.hookRouter.value = await findHookRouterPda({ + jobKey: expectSome(args.jobKey), + }); + } + if (!accounts.routerState.value) { + accounts.routerState.value = await findRouterStatePda(); + } + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.caller), + getAccountMeta(accounts.job), + getAccountMeta(accounts.hookRouter), + getAccountMeta(accounts.routerState), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.sponsor), + ], + data: getCloseHookRouterInstructionDataEncoder().encode( + args as CloseHookRouterInstructionDataArgs, + ), + programAddress, + } as CloseHookRouterInstruction< + TProgramAddress, + TAccountCaller, + TAccountJob, + TAccountHookRouter, + TAccountRouterState, + TAccountAcpState, + TAccountSponsor + >); +} + +export type CloseHookRouterInput< + TAccountCaller extends string = string, + TAccountJob extends string = string, + TAccountHookRouter extends string = string, + TAccountRouterState extends string = string, + TAccountAcpState extends string = string, + TAccountSponsor extends string = string, +> = { + /** + * Permissionless. The caller receives nothing — every lamport goes to + * `acp_state.sponsor` — so there is no incentive to race and no + * rent-farming surface. That also lets this double as a sweep for any + * router whose terminal-transaction bundle failed to land. + */ + caller: TransactionSigner; + /** `deserialize_job` against `router_state.acp_program`. */ + job: Address; + /** so it cannot be `Account`. Seeds pin the address. */ + hookRouter: Address; + routerState: Address; + acpState: Address; + sponsor: Address; + jobKey: CloseHookRouterInstructionDataArgs["jobKey"]; +}; + +export function getCloseHookRouterInstruction< + TAccountCaller extends string, + TAccountJob extends string, + TAccountHookRouter extends string, + TAccountRouterState extends string, + TAccountAcpState extends string, + TAccountSponsor extends string, + TProgramAddress extends Address = typeof MULTI_HOOK_ROUTER_PROGRAM_ADDRESS, +>( + input: CloseHookRouterInput< + TAccountCaller, + TAccountJob, + TAccountHookRouter, + TAccountRouterState, + TAccountAcpState, + TAccountSponsor + >, + config?: { programAddress?: TProgramAddress }, +): CloseHookRouterInstruction< + TProgramAddress, + TAccountCaller, + TAccountJob, + TAccountHookRouter, + TAccountRouterState, + TAccountAcpState, + TAccountSponsor +> { + // Program address. + const programAddress = + config?.programAddress ?? MULTI_HOOK_ROUTER_PROGRAM_ADDRESS; + + // Original accounts. + const originalAccounts = { + caller: { value: input.caller ?? null, isWritable: false }, + job: { value: input.job ?? null, isWritable: false }, + hookRouter: { value: input.hookRouter ?? null, isWritable: true }, + routerState: { value: input.routerState ?? null, isWritable: false }, + acpState: { value: input.acpState ?? null, isWritable: false }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, + }; + const accounts = originalAccounts as Record< + keyof typeof originalAccounts, + ResolvedAccount + >; + + // Original args. + const args = { ...input }; + + const getAccountMeta = getAccountMetaFactory(programAddress, "programId"); + return Object.freeze({ + accounts: [ + getAccountMeta(accounts.caller), + getAccountMeta(accounts.job), + getAccountMeta(accounts.hookRouter), + getAccountMeta(accounts.routerState), + getAccountMeta(accounts.acpState), + getAccountMeta(accounts.sponsor), + ], + data: getCloseHookRouterInstructionDataEncoder().encode( + args as CloseHookRouterInstructionDataArgs, + ), + programAddress, + } as CloseHookRouterInstruction< + TProgramAddress, + TAccountCaller, + TAccountJob, + TAccountHookRouter, + TAccountRouterState, + TAccountAcpState, + TAccountSponsor + >); +} + +export type ParsedCloseHookRouterInstruction< + TProgram extends string = typeof MULTI_HOOK_ROUTER_PROGRAM_ADDRESS, + TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[], +> = { + programAddress: Address; + accounts: { + /** + * Permissionless. The caller receives nothing — every lamport goes to + * `acp_state.sponsor` — so there is no incentive to race and no + * rent-farming surface. That also lets this double as a sweep for any + * router whose terminal-transaction bundle failed to land. + */ + caller: TAccountMetas[0]; + /** `deserialize_job` against `router_state.acp_program`. */ + job: TAccountMetas[1]; + /** so it cannot be `Account`. Seeds pin the address. */ + hookRouter: TAccountMetas[2]; + routerState: TAccountMetas[3]; + acpState: TAccountMetas[4]; + sponsor: TAccountMetas[5]; + }; + data: CloseHookRouterInstructionData; +}; + +export function parseCloseHookRouterInstruction< + TProgram extends string, + TAccountMetas extends readonly AccountMeta[], +>( + instruction: Instruction & + InstructionWithAccounts & + InstructionWithData, +): ParsedCloseHookRouterInstruction { + if (instruction.accounts.length < 6) { + // TODO: Coded error. + throw new Error("Not enough accounts"); + } + let accountIndex = 0; + const getNextAccount = () => { + const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!; + accountIndex += 1; + return accountMeta; + }; + return { + programAddress: instruction.programAddress, + accounts: { + caller: getNextAccount(), + job: getNextAccount(), + hookRouter: getNextAccount(), + routerState: getNextAccount(), + acpState: getNextAccount(), + sponsor: getNextAccount(), + }, + data: getCloseHookRouterInstructionDataDecoder().decode(instruction.data), + }; +} diff --git a/src/core/solana/generated/multi-hook-router/instructions/configureHooks.ts b/src/core/solana/generated/multi-hook-router/instructions/configureHooks.ts index a2fa0fb..c4ac150 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/configureHooks.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/configureHooks.ts @@ -18,8 +18,6 @@ import { getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -89,13 +87,13 @@ export type ConfigureHooksInstruction< export type ConfigureHooksInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; hooks: Array
; }; export type ConfigureHooksInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; hooks: Array
; }; @@ -104,7 +102,7 @@ export function getConfigureHooksInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hooks", getArrayDecoder(getAddressDecoder())], ]); @@ -140,10 +138,17 @@ export type ConfigureHooksAsyncInput< > = { client: TransactionSigner; job: Address; + /** + * `Account` with `init_if_needed`: the account is exact-fit, + * so its length varies with its contents, and `init_if_needed`'s codegen + * post-checks the length against a fixed `space =` expression. Every call + * after the first would fail `ConstraintSpace`. The seeds below still pin + * the address. + */ hookRouter?: Address; routerState?: Address; systemProgram?: Address; - jobId: ConfigureHooksInstructionDataArgs["jobId"]; + jobKey: ConfigureHooksInstructionDataArgs["jobKey"]; action: ConfigureHooksInstructionDataArgs["action"]; hooks: ConfigureHooksInstructionDataArgs["hooks"]; }; @@ -197,7 +202,7 @@ export async function getConfigureHooksInstructionAsync< // Resolve default values. if (!accounts.hookRouter.value) { accounts.hookRouter.value = await findHookRouterPda({ - jobId: expectSome(args.jobId), + jobKey: expectSome(args.jobKey), }); } if (!accounts.routerState.value) { @@ -240,10 +245,17 @@ export type ConfigureHooksInput< > = { client: TransactionSigner; job: Address; + /** + * `Account` with `init_if_needed`: the account is exact-fit, + * so its length varies with its contents, and `init_if_needed`'s codegen + * post-checks the length against a fixed `space =` expression. Every call + * after the first would fail `ConstraintSpace`. The seeds below still pin + * the address. + */ hookRouter: Address; routerState: Address; systemProgram?: Address; - jobId: ConfigureHooksInstructionDataArgs["jobId"]; + jobKey: ConfigureHooksInstructionDataArgs["jobKey"]; action: ConfigureHooksInstructionDataArgs["action"]; hooks: ConfigureHooksInstructionDataArgs["hooks"]; }; @@ -329,6 +341,13 @@ export type ParsedConfigureHooksInstruction< accounts: { client: TAccountMetas[0]; job: TAccountMetas[1]; + /** + * `Account` with `init_if_needed`: the account is exact-fit, + * so its length varies with its contents, and `init_if_needed`'s codegen + * post-checks the length against a fixed `space =` expression. Every call + * after the first would fail `ConstraintSpace`. The seeds below still pin + * the address. + */ hookRouter: TAccountMetas[2]; routerState: TAccountMetas[3]; systemProgram: TAccountMetas[4]; diff --git a/src/core/solana/generated/multi-hook-router/instructions/index.ts b/src/core/solana/generated/multi-hook-router/instructions/index.ts index f4c4df8..8cd0ff8 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/index.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/index.ts @@ -10,6 +10,7 @@ export * from "./addHook.js"; export * from "./afterAction.js"; export * from "./batchConfigureHooks.js"; export * from "./beforeAction.js"; +export * from "./closeHookRouter.js"; export * from "./configureHooks.js"; export * from "./initialize.js"; export * from "./removeHook.js"; diff --git a/src/core/solana/generated/multi-hook-router/instructions/removeHook.ts b/src/core/solana/generated/multi-hook-router/instructions/removeHook.ts index 30a242a..e8107fe 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/removeHook.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/removeHook.ts @@ -16,8 +16,6 @@ import { getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -80,13 +78,13 @@ export type RemoveHookInstruction< export type RemoveHookInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; hook: Address; }; export type RemoveHookInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; hook: Address; }; @@ -95,7 +93,7 @@ export function getRemoveHookInstructionDataEncoder(): FixedSizeEncoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hook", getAddressDecoder()], ]); @@ -132,7 +130,7 @@ export type RemoveHookAsyncInput< job: Address; hookRouter?: Address; routerState?: Address; - jobId: RemoveHookInstructionDataArgs["jobId"]; + jobKey: RemoveHookInstructionDataArgs["jobKey"]; action: RemoveHookInstructionDataArgs["action"]; hook: RemoveHookInstructionDataArgs["hook"]; }; @@ -182,7 +180,7 @@ export async function getRemoveHookInstructionAsync< // Resolve default values. if (!accounts.hookRouter.value) { accounts.hookRouter.value = await findHookRouterPda({ - jobId: expectSome(args.jobId), + jobKey: expectSome(args.jobKey), }); } if (!accounts.routerState.value) { @@ -220,7 +218,7 @@ export type RemoveHookInput< job: Address; hookRouter: Address; routerState: Address; - jobId: RemoveHookInstructionDataArgs["jobId"]; + jobKey: RemoveHookInstructionDataArgs["jobKey"]; action: RemoveHookInstructionDataArgs["action"]; hook: RemoveHookInstructionDataArgs["hook"]; }; diff --git a/src/core/solana/generated/multi-hook-router/instructions/reorderHooks.ts b/src/core/solana/generated/multi-hook-router/instructions/reorderHooks.ts index ed0bcd4..7b903a5 100644 --- a/src/core/solana/generated/multi-hook-router/instructions/reorderHooks.ts +++ b/src/core/solana/generated/multi-hook-router/instructions/reorderHooks.ts @@ -18,8 +18,6 @@ import { getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -84,13 +82,13 @@ export type ReorderHooksInstruction< export type ReorderHooksInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; hooks: Array
; }; export type ReorderHooksInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; hooks: Array
; }; @@ -99,7 +97,7 @@ export function getReorderHooksInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hooks", getArrayDecoder(getAddressDecoder())], ]); @@ -136,7 +134,7 @@ export type ReorderHooksAsyncInput< job: Address; hookRouter?: Address; routerState?: Address; - jobId: ReorderHooksInstructionDataArgs["jobId"]; + jobKey: ReorderHooksInstructionDataArgs["jobKey"]; action: ReorderHooksInstructionDataArgs["action"]; hooks: ReorderHooksInstructionDataArgs["hooks"]; }; @@ -186,7 +184,7 @@ export async function getReorderHooksInstructionAsync< // Resolve default values. if (!accounts.hookRouter.value) { accounts.hookRouter.value = await findHookRouterPda({ - jobId: expectSome(args.jobId), + jobKey: expectSome(args.jobKey), }); } if (!accounts.routerState.value) { @@ -224,7 +222,7 @@ export type ReorderHooksInput< job: Address; hookRouter: Address; routerState: Address; - jobId: ReorderHooksInstructionDataArgs["jobId"]; + jobKey: ReorderHooksInstructionDataArgs["jobKey"]; action: ReorderHooksInstructionDataArgs["action"]; hooks: ReorderHooksInstructionDataArgs["hooks"]; }; diff --git a/src/core/solana/generated/multi-hook-router/pdas/hookMetadata.ts b/src/core/solana/generated/multi-hook-router/pdas/hookMetadata.ts index 4c76c62..c172d64 100644 --- a/src/core/solana/generated/multi-hook-router/pdas/hookMetadata.ts +++ b/src/core/solana/generated/multi-hook-router/pdas/hookMetadata.ts @@ -16,7 +16,9 @@ import { export async function findHookMetadataPda( config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5" as Address<"EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/multi-hook-router/pdas/hookRouter.ts b/src/core/solana/generated/multi-hook-router/pdas/hookRouter.ts index 957443f..ff2bef2 100644 --- a/src/core/solana/generated/multi-hook-router/pdas/hookRouter.ts +++ b/src/core/solana/generated/multi-hook-router/pdas/hookRouter.ts @@ -7,29 +7,31 @@ */ import { + getAddressEncoder, getBytesEncoder, getProgramDerivedAddress, - getU64Encoder, type Address, type ProgramDerivedAddress, } from "@solana/kit"; export type HookRouterSeeds = { - jobId: number | bigint; + jobKey: Address; }; export async function findHookRouterPda( seeds: HookRouterSeeds, config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5" as Address<"EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ getBytesEncoder().encode( new Uint8Array([104, 111, 111, 107, 95, 114, 111, 117, 116, 101, 114]), ), - getU64Encoder().encode(seeds.jobId), + getAddressEncoder().encode(seeds.jobKey), ], }); } diff --git a/src/core/solana/generated/multi-hook-router/pdas/routerState.ts b/src/core/solana/generated/multi-hook-router/pdas/routerState.ts index d32c62f..b9a7189 100644 --- a/src/core/solana/generated/multi-hook-router/pdas/routerState.ts +++ b/src/core/solana/generated/multi-hook-router/pdas/routerState.ts @@ -16,7 +16,9 @@ import { export async function findRouterStatePda( config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5" as Address<"EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/multi-hook-router/programs/multiHookRouter.ts b/src/core/solana/generated/multi-hook-router/programs/multiHookRouter.ts index 881b8a8..aeb07bb 100644 --- a/src/core/solana/generated/multi-hook-router/programs/multiHookRouter.ts +++ b/src/core/solana/generated/multi-hook-router/programs/multiHookRouter.ts @@ -21,6 +21,7 @@ import { parseAfterActionInstruction, parseBatchConfigureHooksInstruction, parseBeforeActionInstruction, + parseCloseHookRouterInstruction, parseConfigureHooksInstruction, parseInitializeInstruction, parseRemoveHookInstruction, @@ -30,6 +31,7 @@ import { type ParsedAfterActionInstruction, type ParsedBatchConfigureHooksInstruction, type ParsedBeforeActionInstruction, + type ParsedCloseHookRouterInstruction, type ParsedConfigureHooksInstruction, type ParsedInitializeInstruction, type ParsedRemoveHookInstruction, @@ -37,7 +39,8 @@ import { type ParsedSetMaxHooksPerJobInstruction, } from "../instructions/index.js"; -export const MULTI_HOOK_ROUTER_PROGRAM_ADDRESS = "" as Address<"">; +export const MULTI_HOOK_ROUTER_PROGRAM_ADDRESS = + "EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5" as Address<"EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5">; export enum MultiHookRouterAccount { HookMetadata, @@ -92,6 +95,7 @@ export enum MultiHookRouterInstruction { AfterAction, BatchConfigureHooks, BeforeAction, + CloseHookRouter, ConfigureHooks, Initialize, RemoveHook, @@ -147,6 +151,17 @@ export function identifyMultiHookRouterInstruction( ) { return MultiHookRouterInstruction.BeforeAction; } + if ( + containsBytes( + data, + fixEncoderSize(getBytesEncoder(), 8).encode( + new Uint8Array([204, 155, 217, 12, 192, 12, 49, 65]), + ), + 0, + ) + ) { + return MultiHookRouterInstruction.CloseHookRouter; + } if ( containsBytes( data, @@ -207,7 +222,9 @@ export function identifyMultiHookRouterInstruction( ); } -export type ParsedMultiHookRouterInstruction = +export type ParsedMultiHookRouterInstruction< + TProgram extends string = "EfaW12djNhjHhyw8oTmxBLABqN1uUXofGGpbbnvw6QU5", +> = | ({ instructionType: MultiHookRouterInstruction.AddHook; } & ParsedAddHookInstruction) @@ -220,6 +237,9 @@ export type ParsedMultiHookRouterInstruction = | ({ instructionType: MultiHookRouterInstruction.BeforeAction; } & ParsedBeforeActionInstruction) + | ({ + instructionType: MultiHookRouterInstruction.CloseHookRouter; + } & ParsedCloseHookRouterInstruction) | ({ instructionType: MultiHookRouterInstruction.ConfigureHooks; } & ParsedConfigureHooksInstruction) @@ -269,6 +289,13 @@ export function parseMultiHookRouterInstruction( ...parseBeforeActionInstruction(instruction), }; } + case MultiHookRouterInstruction.CloseHookRouter: { + assertIsInstructionWithAccounts(instruction); + return { + instructionType: MultiHookRouterInstruction.CloseHookRouter, + ...parseCloseHookRouterInstruction(instruction), + }; + } case MultiHookRouterInstruction.ConfigureHooks: { assertIsInstructionWithAccounts(instruction); return { diff --git a/src/core/solana/generated/multi-hook-router/types/dewhitelistedHookSkipped.ts b/src/core/solana/generated/multi-hook-router/types/dewhitelistedHookSkipped.ts index 0aa8577..9e3783b 100644 --- a/src/core/solana/generated/multi-hook-router/types/dewhitelistedHookSkipped.ts +++ b/src/core/solana/generated/multi-hook-router/types/dewhitelistedHookSkipped.ts @@ -12,8 +12,6 @@ import { getAddressEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, type Address, @@ -23,20 +21,16 @@ import { } from "@solana/kit"; export type DewhitelistedHookSkipped = { - jobId: bigint; + jobKey: Address; action: number; hook: Address; }; -export type DewhitelistedHookSkippedArgs = { - jobId: number | bigint; - action: number; - hook: Address; -}; +export type DewhitelistedHookSkippedArgs = DewhitelistedHookSkipped; export function getDewhitelistedHookSkippedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["action", getU8Encoder()], ["hook", getAddressEncoder()], ]); @@ -44,7 +38,7 @@ export function getDewhitelistedHookSkippedEncoder(): FixedSizeEncoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hook", getAddressDecoder()], ]); diff --git a/src/core/solana/generated/multi-hook-router/types/hookAdded.ts b/src/core/solana/generated/multi-hook-router/types/hookAdded.ts index 71d4f1b..1597caf 100644 --- a/src/core/solana/generated/multi-hook-router/types/hookAdded.ts +++ b/src/core/solana/generated/multi-hook-router/types/hookAdded.ts @@ -12,8 +12,6 @@ import { getAddressEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, type Address, @@ -23,22 +21,17 @@ import { } from "@solana/kit"; export type HookAdded = { - jobId: bigint; + jobKey: Address; action: number; hook: Address; position: number; }; -export type HookAddedArgs = { - jobId: number | bigint; - action: number; - hook: Address; - position: number; -}; +export type HookAddedArgs = HookAdded; export function getHookAddedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["action", getU8Encoder()], ["hook", getAddressEncoder()], ["position", getU8Encoder()], @@ -47,7 +40,7 @@ export function getHookAddedEncoder(): FixedSizeEncoder { export function getHookAddedDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hook", getAddressDecoder()], ["position", getU8Decoder()], diff --git a/src/core/solana/generated/multi-hook-router/types/hookRemoved.ts b/src/core/solana/generated/multi-hook-router/types/hookRemoved.ts index 44d1623..f686074 100644 --- a/src/core/solana/generated/multi-hook-router/types/hookRemoved.ts +++ b/src/core/solana/generated/multi-hook-router/types/hookRemoved.ts @@ -12,8 +12,6 @@ import { getAddressEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, type Address, @@ -22,17 +20,13 @@ import { type FixedSizeEncoder, } from "@solana/kit"; -export type HookRemoved = { jobId: bigint; action: number; hook: Address }; +export type HookRemoved = { jobKey: Address; action: number; hook: Address }; -export type HookRemovedArgs = { - jobId: number | bigint; - action: number; - hook: Address; -}; +export type HookRemovedArgs = HookRemoved; export function getHookRemovedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["action", getU8Encoder()], ["hook", getAddressEncoder()], ]); @@ -40,7 +34,7 @@ export function getHookRemovedEncoder(): FixedSizeEncoder { export function getHookRemovedDecoder(): FixedSizeDecoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hook", getAddressDecoder()], ]); diff --git a/src/core/solana/generated/multi-hook-router/types/hookRouterClosed.ts b/src/core/solana/generated/multi-hook-router/types/hookRouterClosed.ts new file mode 100644 index 0000000..d16b9ee --- /dev/null +++ b/src/core/solana/generated/multi-hook-router/types/hookRouterClosed.ts @@ -0,0 +1,59 @@ +/** + * This code was AUTOGENERATED using the Codama library. + * Please DO NOT EDIT THIS FILE, instead use visitors + * to add features, then rerun Codama to update it. + * + * @see https://github.com/codama-idl/codama + */ + +import { + combineCodec, + getAddressDecoder, + getAddressEncoder, + getStructDecoder, + getStructEncoder, + getU64Decoder, + getU64Encoder, + type Address, + type FixedSizeCodec, + type FixedSizeDecoder, + type FixedSizeEncoder, +} from "@solana/kit"; + +export type HookRouterClosed = { + jobKey: Address; + rentDestination: Address; + lamports: bigint; +}; + +export type HookRouterClosedArgs = { + jobKey: Address; + rentDestination: Address; + lamports: number | bigint; +}; + +export function getHookRouterClosedEncoder(): FixedSizeEncoder { + return getStructEncoder([ + ["jobKey", getAddressEncoder()], + ["rentDestination", getAddressEncoder()], + ["lamports", getU64Encoder()], + ]); +} + +export function getHookRouterClosedDecoder(): FixedSizeDecoder { + return getStructDecoder([ + ["jobKey", getAddressDecoder()], + ["rentDestination", getAddressDecoder()], + ["lamports", getU64Decoder()], + ]); +} + +export function getHookRouterClosedCodec(): FixedSizeCodec< + HookRouterClosedArgs, + HookRouterClosed +> { + return combineCodec( + getHookRouterClosedEncoder(), + getHookRouterClosedDecoder(), + ); +} diff --git a/src/core/solana/generated/multi-hook-router/types/hooksConfigured.ts b/src/core/solana/generated/multi-hook-router/types/hooksConfigured.ts index 0d06d93..6b7cd96 100644 --- a/src/core/solana/generated/multi-hook-router/types/hooksConfigured.ts +++ b/src/core/solana/generated/multi-hook-router/types/hooksConfigured.ts @@ -14,8 +14,6 @@ import { getArrayEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, type Address, @@ -25,20 +23,16 @@ import { } from "@solana/kit"; export type HooksConfigured = { - jobId: bigint; + jobKey: Address; action: number; hooks: Array
; }; -export type HooksConfiguredArgs = { - jobId: number | bigint; - action: number; - hooks: Array
; -}; +export type HooksConfiguredArgs = HooksConfigured; export function getHooksConfiguredEncoder(): Encoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["action", getU8Encoder()], ["hooks", getArrayEncoder(getAddressEncoder())], ]); @@ -46,7 +40,7 @@ export function getHooksConfiguredEncoder(): Encoder { export function getHooksConfiguredDecoder(): Decoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hooks", getArrayDecoder(getAddressDecoder())], ]); diff --git a/src/core/solana/generated/multi-hook-router/types/hooksReordered.ts b/src/core/solana/generated/multi-hook-router/types/hooksReordered.ts index 8c303e1..5cb2e8b 100644 --- a/src/core/solana/generated/multi-hook-router/types/hooksReordered.ts +++ b/src/core/solana/generated/multi-hook-router/types/hooksReordered.ts @@ -14,8 +14,6 @@ import { getArrayEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, type Address, @@ -25,20 +23,16 @@ import { } from "@solana/kit"; export type HooksReordered = { - jobId: bigint; + jobKey: Address; action: number; hooks: Array
; }; -export type HooksReorderedArgs = { - jobId: number | bigint; - action: number; - hooks: Array
; -}; +export type HooksReorderedArgs = HooksReordered; export function getHooksReorderedEncoder(): Encoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["action", getU8Encoder()], ["hooks", getArrayEncoder(getAddressEncoder())], ]); @@ -46,7 +40,7 @@ export function getHooksReorderedEncoder(): Encoder { export function getHooksReorderedDecoder(): Decoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["hooks", getArrayDecoder(getAddressDecoder())], ]); diff --git a/src/core/solana/generated/multi-hook-router/types/index.ts b/src/core/solana/generated/multi-hook-router/types/index.ts index 94f38f5..13f1693 100644 --- a/src/core/solana/generated/multi-hook-router/types/index.ts +++ b/src/core/solana/generated/multi-hook-router/types/index.ts @@ -10,6 +10,7 @@ export * from "./cachedHookMetadata.js"; export * from "./dewhitelistedHookSkipped.js"; export * from "./hookAdded.js"; export * from "./hookRemoved.js"; +export * from "./hookRouterClosed.js"; export * from "./hooksConfigured.js"; export * from "./hooksReordered.js"; export * from "./maxHooksPerJobUpdated.js"; diff --git a/src/core/solana/generated/subscription-hook/instructions/afterAction.ts b/src/core/solana/generated/subscription-hook/instructions/afterAction.ts index 23956cf..af3c8f0 100644 --- a/src/core/solana/generated/subscription-hook/instructions/afterAction.ts +++ b/src/core/solana/generated/subscription-hook/instructions/afterAction.ts @@ -20,8 +20,6 @@ import { getStructEncoder, getU32Decoder, getU32Encoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -67,7 +65,7 @@ export type AfterActionInstruction< export type AfterActionInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -75,7 +73,7 @@ export type AfterActionInstructionData = { }; export type AfterActionInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -86,7 +84,7 @@ export function getAfterActionInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["optParams", addDecoderSizePrefix(getBytesDecoder(), getU32Decoder())], ["caller", getAddressDecoder()], @@ -119,7 +117,7 @@ export function getAfterActionInstructionDataCodec(): Codec< export type AfterActionAsyncInput = { hookState?: Address; - jobId: AfterActionInstructionDataArgs["jobId"]; + jobKey: AfterActionInstructionDataArgs["jobKey"]; action: AfterActionInstructionDataArgs["action"]; optParams: AfterActionInstructionDataArgs["optParams"]; caller: AfterActionInstructionDataArgs["caller"]; @@ -166,7 +164,7 @@ export async function getAfterActionInstructionAsync< export type AfterActionInput = { hookState: Address; - jobId: AfterActionInstructionDataArgs["jobId"]; + jobKey: AfterActionInstructionDataArgs["jobKey"]; action: AfterActionInstructionDataArgs["action"]; optParams: AfterActionInstructionDataArgs["optParams"]; caller: AfterActionInstructionDataArgs["caller"]; diff --git a/src/core/solana/generated/subscription-hook/instructions/beforeAction.ts b/src/core/solana/generated/subscription-hook/instructions/beforeAction.ts index c1f49d0..12b218e 100644 --- a/src/core/solana/generated/subscription-hook/instructions/beforeAction.ts +++ b/src/core/solana/generated/subscription-hook/instructions/beforeAction.ts @@ -20,8 +20,6 @@ import { getStructEncoder, getU32Decoder, getU32Encoder, - getU64Decoder, - getU64Encoder, getU8Decoder, getU8Encoder, transformEncoder, @@ -67,7 +65,7 @@ export type BeforeActionInstruction< export type BeforeActionInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -75,7 +73,7 @@ export type BeforeActionInstructionData = { }; export type BeforeActionInstructionDataArgs = { - jobId: number | bigint; + jobKey: Address; action: number; optParams: ReadonlyUint8Array; caller: Address; @@ -86,7 +84,7 @@ export function getBeforeActionInstructionDataEncoder(): Encoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["action", getU8Decoder()], ["optParams", addDecoderSizePrefix(getBytesDecoder(), getU32Decoder())], ["caller", getAddressDecoder()], @@ -120,7 +118,7 @@ export function getBeforeActionInstructionDataCodec(): Codec< export type BeforeActionAsyncInput = { hookState?: Address; - jobId: BeforeActionInstructionDataArgs["jobId"]; + jobKey: BeforeActionInstructionDataArgs["jobKey"]; action: BeforeActionInstructionDataArgs["action"]; optParams: BeforeActionInstructionDataArgs["optParams"]; caller: BeforeActionInstructionDataArgs["caller"]; @@ -167,7 +165,7 @@ export async function getBeforeActionInstructionAsync< export type BeforeActionInput = { hookState: Address; - jobId: BeforeActionInstructionDataArgs["jobId"]; + jobKey: BeforeActionInstructionDataArgs["jobKey"]; action: BeforeActionInstructionDataArgs["action"]; optParams: BeforeActionInstructionDataArgs["optParams"]; caller: BeforeActionInstructionDataArgs["caller"]; diff --git a/src/core/solana/generated/subscription-hook/instructions/cleanupProposedTerms.ts b/src/core/solana/generated/subscription-hook/instructions/cleanupProposedTerms.ts index d660a06..4cac219 100644 --- a/src/core/solana/generated/subscription-hook/instructions/cleanupProposedTerms.ts +++ b/src/core/solana/generated/subscription-hook/instructions/cleanupProposedTerms.ts @@ -10,12 +10,12 @@ import { combineCodec, fixDecoderSize, fixEncoderSize, + getAddressDecoder, + getAddressEncoder, getBytesDecoder, getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, transformEncoder, type AccountMeta, type AccountSignerMeta, @@ -53,7 +53,7 @@ export type CleanupProposedTermsInstruction< TAccountJobAccount extends string | AccountMeta = string, TAccountProposedTerms extends string | AccountMeta = string, TAccountAcpState extends string | AccountMeta = string, - TAccountPlatformTreasury extends string | AccountMeta = string, + TAccountSponsor extends string | AccountMeta = string, TRemainingAccounts extends readonly AccountMeta[] = [], > = Instruction & InstructionWithData & @@ -75,27 +75,25 @@ export type CleanupProposedTermsInstruction< TAccountAcpState extends string ? ReadonlyAccount : TAccountAcpState, - TAccountPlatformTreasury extends string - ? WritableAccount - : TAccountPlatformTreasury, + TAccountSponsor extends string + ? WritableAccount + : TAccountSponsor, ...TRemainingAccounts, ] >; export type CleanupProposedTermsInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; }; -export type CleanupProposedTermsInstructionDataArgs = { - jobId: number | bigint; -}; +export type CleanupProposedTermsInstructionDataArgs = { jobKey: Address }; export function getCleanupProposedTermsInstructionDataEncoder(): FixedSizeEncoder { return transformEncoder( getStructEncoder([ ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ]), (value) => ({ ...value, @@ -107,7 +105,7 @@ export function getCleanupProposedTermsInstructionDataEncoder(): FixedSizeEncode export function getCleanupProposedTermsInstructionDataDecoder(): FixedSizeDecoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ]); } @@ -127,19 +125,19 @@ export type CleanupProposedTermsAsyncInput< TAccountJobAccount extends string = string, TAccountProposedTerms extends string = string, TAccountAcpState extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, > = { /** Permissionless trigger; the only authorization is that the job has expired. */ caller: TransactionSigner; hookState?: Address; - /** (Expired) and identity (job_id) validated in handler. */ + /** (Expired) and identity (job_key) validated in handler. */ jobAccount: Address; /** Manually closed after the provider check. */ proposedTerms: Address; /** against `hook_state.acp_program`. Read only to source the treasury. */ acpState: Address; - platformTreasury: Address; - jobId: CleanupProposedTermsInstructionDataArgs["jobId"]; + sponsor: Address; + jobKey: CleanupProposedTermsInstructionDataArgs["jobKey"]; }; export async function getCleanupProposedTermsInstructionAsync< @@ -148,7 +146,7 @@ export async function getCleanupProposedTermsInstructionAsync< TAccountJobAccount extends string, TAccountProposedTerms extends string, TAccountAcpState extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TProgramAddress extends Address = typeof SUBSCRIPTION_HOOK_PROGRAM_ADDRESS, >( input: CleanupProposedTermsAsyncInput< @@ -157,7 +155,7 @@ export async function getCleanupProposedTermsInstructionAsync< TAccountJobAccount, TAccountProposedTerms, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor >, config?: { programAddress?: TProgramAddress }, ): Promise< @@ -168,7 +166,7 @@ export async function getCleanupProposedTermsInstructionAsync< TAccountJobAccount, TAccountProposedTerms, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor > > { // Program address. @@ -182,10 +180,7 @@ export async function getCleanupProposedTermsInstructionAsync< jobAccount: { value: input.jobAccount ?? null, isWritable: false }, proposedTerms: { value: input.proposedTerms ?? null, isWritable: true }, acpState: { value: input.acpState ?? null, isWritable: false }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -208,7 +203,7 @@ export async function getCleanupProposedTermsInstructionAsync< getAccountMeta(accounts.jobAccount), getAccountMeta(accounts.proposedTerms), getAccountMeta(accounts.acpState), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), ], data: getCleanupProposedTermsInstructionDataEncoder().encode( args as CleanupProposedTermsInstructionDataArgs, @@ -221,7 +216,7 @@ export async function getCleanupProposedTermsInstructionAsync< TAccountJobAccount, TAccountProposedTerms, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor >); } @@ -231,19 +226,19 @@ export type CleanupProposedTermsInput< TAccountJobAccount extends string = string, TAccountProposedTerms extends string = string, TAccountAcpState extends string = string, - TAccountPlatformTreasury extends string = string, + TAccountSponsor extends string = string, > = { /** Permissionless trigger; the only authorization is that the job has expired. */ caller: TransactionSigner; hookState: Address; - /** (Expired) and identity (job_id) validated in handler. */ + /** (Expired) and identity (job_key) validated in handler. */ jobAccount: Address; /** Manually closed after the provider check. */ proposedTerms: Address; /** against `hook_state.acp_program`. Read only to source the treasury. */ acpState: Address; - platformTreasury: Address; - jobId: CleanupProposedTermsInstructionDataArgs["jobId"]; + sponsor: Address; + jobKey: CleanupProposedTermsInstructionDataArgs["jobKey"]; }; export function getCleanupProposedTermsInstruction< @@ -252,7 +247,7 @@ export function getCleanupProposedTermsInstruction< TAccountJobAccount extends string, TAccountProposedTerms extends string, TAccountAcpState extends string, - TAccountPlatformTreasury extends string, + TAccountSponsor extends string, TProgramAddress extends Address = typeof SUBSCRIPTION_HOOK_PROGRAM_ADDRESS, >( input: CleanupProposedTermsInput< @@ -261,7 +256,7 @@ export function getCleanupProposedTermsInstruction< TAccountJobAccount, TAccountProposedTerms, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor >, config?: { programAddress?: TProgramAddress }, ): CleanupProposedTermsInstruction< @@ -271,7 +266,7 @@ export function getCleanupProposedTermsInstruction< TAccountJobAccount, TAccountProposedTerms, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor > { // Program address. const programAddress = @@ -284,10 +279,7 @@ export function getCleanupProposedTermsInstruction< jobAccount: { value: input.jobAccount ?? null, isWritable: false }, proposedTerms: { value: input.proposedTerms ?? null, isWritable: true }, acpState: { value: input.acpState ?? null, isWritable: false }, - platformTreasury: { - value: input.platformTreasury ?? null, - isWritable: true, - }, + sponsor: { value: input.sponsor ?? null, isWritable: true }, }; const accounts = originalAccounts as Record< keyof typeof originalAccounts, @@ -305,7 +297,7 @@ export function getCleanupProposedTermsInstruction< getAccountMeta(accounts.jobAccount), getAccountMeta(accounts.proposedTerms), getAccountMeta(accounts.acpState), - getAccountMeta(accounts.platformTreasury), + getAccountMeta(accounts.sponsor), ], data: getCleanupProposedTermsInstructionDataEncoder().encode( args as CleanupProposedTermsInstructionDataArgs, @@ -318,7 +310,7 @@ export function getCleanupProposedTermsInstruction< TAccountJobAccount, TAccountProposedTerms, TAccountAcpState, - TAccountPlatformTreasury + TAccountSponsor >); } @@ -331,13 +323,13 @@ export type ParsedCleanupProposedTermsInstruction< /** Permissionless trigger; the only authorization is that the job has expired. */ caller: TAccountMetas[0]; hookState: TAccountMetas[1]; - /** (Expired) and identity (job_id) validated in handler. */ + /** (Expired) and identity (job_key) validated in handler. */ jobAccount: TAccountMetas[2]; /** Manually closed after the provider check. */ proposedTerms: TAccountMetas[3]; /** against `hook_state.acp_program`. Read only to source the treasury. */ acpState: TAccountMetas[4]; - platformTreasury: TAccountMetas[5]; + sponsor: TAccountMetas[5]; }; data: CleanupProposedTermsInstructionData; }; @@ -368,7 +360,7 @@ export function parseCleanupProposedTermsInstruction< jobAccount: getNextAccount(), proposedTerms: getNextAccount(), acpState: getNextAccount(), - platformTreasury: getNextAccount(), + sponsor: getNextAccount(), }, data: getCleanupProposedTermsInstructionDataDecoder().decode( instruction.data, diff --git a/src/core/solana/generated/subscription-hook/instructions/preCreateProposedTerms.ts b/src/core/solana/generated/subscription-hook/instructions/preCreateProposedTerms.ts index 576b41f..08fc147 100644 --- a/src/core/solana/generated/subscription-hook/instructions/preCreateProposedTerms.ts +++ b/src/core/solana/generated/subscription-hook/instructions/preCreateProposedTerms.ts @@ -10,12 +10,12 @@ import { combineCodec, fixDecoderSize, fixEncoderSize, + getAddressDecoder, + getAddressEncoder, getBytesDecoder, getBytesEncoder, getStructDecoder, getStructEncoder, - getU64Decoder, - getU64Encoder, transformEncoder, type AccountMeta, type AccountSignerMeta, @@ -79,18 +79,16 @@ export type PreCreateProposedTermsInstruction< export type PreCreateProposedTermsInstructionData = { discriminator: ReadonlyUint8Array; - jobId: bigint; + jobKey: Address; }; -export type PreCreateProposedTermsInstructionDataArgs = { - jobId: number | bigint; -}; +export type PreCreateProposedTermsInstructionDataArgs = { jobKey: Address }; export function getPreCreateProposedTermsInstructionDataEncoder(): FixedSizeEncoder { return transformEncoder( getStructEncoder([ ["discriminator", fixEncoderSize(getBytesEncoder(), 8)], - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ]), (value) => ({ ...value, @@ -102,7 +100,7 @@ export function getPreCreateProposedTermsInstructionDataEncoder(): FixedSizeEnco export function getPreCreateProposedTermsInstructionDataDecoder(): FixedSizeDecoder { return getStructDecoder([ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)], - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ]); } @@ -134,7 +132,7 @@ export type PreCreateProposedTermsAsyncInput< /** in handler. */ proposedTerms: Address; systemProgram?: Address; - jobId: PreCreateProposedTermsInstructionDataArgs["jobId"]; + jobKey: PreCreateProposedTermsInstructionDataArgs["jobKey"]; }; export async function getPreCreateProposedTermsInstructionAsync< @@ -233,7 +231,7 @@ export type PreCreateProposedTermsInput< /** in handler. */ proposedTerms: Address; systemProgram?: Address; - jobId: PreCreateProposedTermsInstructionDataArgs["jobId"]; + jobKey: PreCreateProposedTermsInstructionDataArgs["jobKey"]; }; export function getPreCreateProposedTermsInstruction< diff --git a/src/core/solana/generated/subscription-hook/pdas/hookMetadata.ts b/src/core/solana/generated/subscription-hook/pdas/hookMetadata.ts index 4c76c62..d009b0a 100644 --- a/src/core/solana/generated/subscription-hook/pdas/hookMetadata.ts +++ b/src/core/solana/generated/subscription-hook/pdas/hookMetadata.ts @@ -16,7 +16,9 @@ import { export async function findHookMetadataPda( config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "6XdTqLDQDXpd312sspR6MZ1LuDb16FAHPdDYegMXFATP" as Address<"6XdTqLDQDXpd312sspR6MZ1LuDb16FAHPdDYegMXFATP">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/subscription-hook/pdas/hookState.ts b/src/core/solana/generated/subscription-hook/pdas/hookState.ts index e55228b..c6d6d1c 100644 --- a/src/core/solana/generated/subscription-hook/pdas/hookState.ts +++ b/src/core/solana/generated/subscription-hook/pdas/hookState.ts @@ -16,7 +16,9 @@ import { export async function findHookStatePda( config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "6XdTqLDQDXpd312sspR6MZ1LuDb16FAHPdDYegMXFATP" as Address<"6XdTqLDQDXpd312sspR6MZ1LuDb16FAHPdDYegMXFATP">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/subscription-hook/programs/subscriptionHook.ts b/src/core/solana/generated/subscription-hook/programs/subscriptionHook.ts index fb3bfa8..3c5c42c 100644 --- a/src/core/solana/generated/subscription-hook/programs/subscriptionHook.ts +++ b/src/core/solana/generated/subscription-hook/programs/subscriptionHook.ts @@ -29,7 +29,8 @@ import { type ParsedPreCreateProposedTermsInstruction, } from "../instructions/index.js"; -export const SUBSCRIPTION_HOOK_PROGRAM_ADDRESS = "" as Address<"">; +export const SUBSCRIPTION_HOOK_PROGRAM_ADDRESS = + "6XdTqLDQDXpd312sspR6MZ1LuDb16FAHPdDYegMXFATP" as Address<"6XdTqLDQDXpd312sspR6MZ1LuDb16FAHPdDYegMXFATP">; export enum SubscriptionHookAccount { HookMetadata, @@ -139,7 +140,9 @@ export function identifySubscriptionHookInstruction( ); } -export type ParsedSubscriptionHookInstruction = +export type ParsedSubscriptionHookInstruction< + TProgram extends string = "6XdTqLDQDXpd312sspR6MZ1LuDb16FAHPdDYegMXFATP", +> = | ({ instructionType: SubscriptionHookInstruction.AfterAction; } & ParsedAfterActionInstruction) diff --git a/src/core/solana/generated/subscription-hook/types/proposedTermsCleanedUp.ts b/src/core/solana/generated/subscription-hook/types/proposedTermsCleanedUp.ts index a7723ce..d1b2fc6 100644 --- a/src/core/solana/generated/subscription-hook/types/proposedTermsCleanedUp.ts +++ b/src/core/solana/generated/subscription-hook/types/proposedTermsCleanedUp.ts @@ -21,7 +21,7 @@ import { } from "@solana/kit"; export type ProposedTermsCleanedUp = { - jobId: bigint; + jobKey: Address; /** * Provider recorded on the terms. Informational only — it is no longer the * rent destination; see `rent_destination`. @@ -33,7 +33,7 @@ export type ProposedTermsCleanedUp = { }; export type ProposedTermsCleanedUpArgs = { - jobId: number | bigint; + jobKey: Address; /** * Provider recorded on the terms. Informational only — it is no longer the * rent destination; see `rent_destination`. @@ -46,7 +46,7 @@ export type ProposedTermsCleanedUpArgs = { export function getProposedTermsCleanedUpEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["provider", getAddressEncoder()], ["lamportsRefunded", getU64Encoder()], ["rentDestination", getAddressEncoder()], @@ -55,7 +55,7 @@ export function getProposedTermsCleanedUpEncoder(): FixedSizeEncoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["provider", getAddressDecoder()], ["lamportsRefunded", getU64Decoder()], ["rentDestination", getAddressDecoder()], diff --git a/src/core/solana/generated/subscription-hook/types/subscriptionActivated.ts b/src/core/solana/generated/subscription-hook/types/subscriptionActivated.ts index 39d4da5..5d1ae50 100644 --- a/src/core/solana/generated/subscription-hook/types/subscriptionActivated.ts +++ b/src/core/solana/generated/subscription-hook/types/subscriptionActivated.ts @@ -23,7 +23,7 @@ import { } from "@solana/kit"; export type SubscriptionActivated = { - jobId: bigint; + jobKey: Address; packageId: bigint; client: Address; provider: Address; @@ -31,7 +31,7 @@ export type SubscriptionActivated = { }; export type SubscriptionActivatedArgs = { - jobId: number | bigint; + jobKey: Address; packageId: number | bigint; client: Address; provider: Address; @@ -40,7 +40,7 @@ export type SubscriptionActivatedArgs = { export function getSubscriptionActivatedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["packageId", getU64Encoder()], ["client", getAddressEncoder()], ["provider", getAddressEncoder()], @@ -50,7 +50,7 @@ export function getSubscriptionActivatedEncoder(): FixedSizeEncoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["packageId", getU64Decoder()], ["client", getAddressDecoder()], ["provider", getAddressDecoder()], diff --git a/src/core/solana/generated/subscription-hook/types/subscriptionTermsProposed.ts b/src/core/solana/generated/subscription-hook/types/subscriptionTermsProposed.ts index 92a1387..93252de 100644 --- a/src/core/solana/generated/subscription-hook/types/subscriptionTermsProposed.ts +++ b/src/core/solana/generated/subscription-hook/types/subscriptionTermsProposed.ts @@ -8,32 +8,35 @@ import { combineCodec, + getAddressDecoder, + getAddressEncoder, getI64Decoder, getI64Encoder, getStructDecoder, getStructEncoder, getU64Decoder, getU64Encoder, + type Address, type FixedSizeCodec, type FixedSizeDecoder, type FixedSizeEncoder, } from "@solana/kit"; export type SubscriptionTermsProposed = { - jobId: bigint; + jobKey: Address; packageId: bigint; duration: bigint; }; export type SubscriptionTermsProposedArgs = { - jobId: number | bigint; + jobKey: Address; packageId: number | bigint; duration: number | bigint; }; export function getSubscriptionTermsProposedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["packageId", getU64Encoder()], ["duration", getI64Encoder()], ]); @@ -41,7 +44,7 @@ export function getSubscriptionTermsProposedEncoder(): FixedSizeEncoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["packageId", getU64Decoder()], ["duration", getI64Decoder()], ]); diff --git a/src/core/solana/generated/subscription-hook/types/subscriptionTermsSkipped.ts b/src/core/solana/generated/subscription-hook/types/subscriptionTermsSkipped.ts index e32a357..4a74a72 100644 --- a/src/core/solana/generated/subscription-hook/types/subscriptionTermsSkipped.ts +++ b/src/core/solana/generated/subscription-hook/types/subscriptionTermsSkipped.ts @@ -8,32 +8,35 @@ import { combineCodec, + getAddressDecoder, + getAddressEncoder, getI64Decoder, getI64Encoder, getStructDecoder, getStructEncoder, getU64Decoder, getU64Encoder, + type Address, type FixedSizeCodec, type FixedSizeDecoder, type FixedSizeEncoder, } from "@solana/kit"; export type SubscriptionTermsSkipped = { - jobId: bigint; + jobKey: Address; packageId: bigint; currentExpiry: bigint; }; export type SubscriptionTermsSkippedArgs = { - jobId: number | bigint; + jobKey: Address; packageId: number | bigint; currentExpiry: number | bigint; }; export function getSubscriptionTermsSkippedEncoder(): FixedSizeEncoder { return getStructEncoder([ - ["jobId", getU64Encoder()], + ["jobKey", getAddressEncoder()], ["packageId", getU64Encoder()], ["currentExpiry", getI64Encoder()], ]); @@ -41,7 +44,7 @@ export function getSubscriptionTermsSkippedEncoder(): FixedSizeEncoder { return getStructDecoder([ - ["jobId", getU64Decoder()], + ["jobKey", getAddressDecoder()], ["packageId", getU64Decoder()], ["currentExpiry", getI64Decoder()], ]); diff --git a/src/core/solana/generated/subscription-state/pdas/stateConfig.ts b/src/core/solana/generated/subscription-state/pdas/stateConfig.ts index 65071b6..2046b8e 100644 --- a/src/core/solana/generated/subscription-state/pdas/stateConfig.ts +++ b/src/core/solana/generated/subscription-state/pdas/stateConfig.ts @@ -16,7 +16,9 @@ import { export async function findStateConfigPda( config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1" as Address<"5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/subscription-state/pdas/subscriptionExpiry.ts b/src/core/solana/generated/subscription-state/pdas/subscriptionExpiry.ts index b5e24f6..f9c6ec9 100644 --- a/src/core/solana/generated/subscription-state/pdas/subscriptionExpiry.ts +++ b/src/core/solana/generated/subscription-state/pdas/subscriptionExpiry.ts @@ -25,7 +25,9 @@ export async function findSubscriptionExpiryPda( seeds: SubscriptionExpirySeeds, config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1" as Address<"5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/subscription-state/pdas/writerRegistry.ts b/src/core/solana/generated/subscription-state/pdas/writerRegistry.ts index e427d3a..29b0084 100644 --- a/src/core/solana/generated/subscription-state/pdas/writerRegistry.ts +++ b/src/core/solana/generated/subscription-state/pdas/writerRegistry.ts @@ -22,7 +22,9 @@ export async function findWriterRegistryPda( seeds: WriterRegistrySeeds, config: { programAddress?: Address | undefined } = {}, ): Promise { - const { programAddress = "" as Address<""> } = config; + const { + programAddress = "5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1" as Address<"5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1">, + } = config; return await getProgramDerivedAddress({ programAddress, seeds: [ diff --git a/src/core/solana/generated/subscription-state/programs/subscriptionState.ts b/src/core/solana/generated/subscription-state/programs/subscriptionState.ts index ef0fa2d..5cad378 100644 --- a/src/core/solana/generated/subscription-state/programs/subscriptionState.ts +++ b/src/core/solana/generated/subscription-state/programs/subscriptionState.ts @@ -35,7 +35,8 @@ import { type ParsedRemoveWriterInstruction, } from "../instructions/index.js"; -export const SUBSCRIPTION_STATE_PROGRAM_ADDRESS = "" as Address<"">; +export const SUBSCRIPTION_STATE_PROGRAM_ADDRESS = + "5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1" as Address<"5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1">; export enum SubscriptionStateAccount { StateConfig, @@ -193,7 +194,9 @@ export function identifySubscriptionStateInstruction( ); } -export type ParsedSubscriptionStateInstruction = +export type ParsedSubscriptionStateInstruction< + TProgram extends string = "5L694HKw4DvqDCUXAQ5XJhXgkYH3N4RuogrcJDsuTTU1", +> = | ({ instructionType: SubscriptionStateInstruction.AcceptAuthority; } & ParsedAcceptAuthorityInstruction) diff --git a/src/core/solana/idl/agentic_commerce_v3.json b/src/core/solana/idl/agentic_commerce_v3.json index 4464654..f35ee36 100644 --- a/src/core/solana/idl/agentic_commerce_v3.json +++ b/src/core/solana/idl/agentic_commerce_v3.json @@ -1,473 +1,331 @@ { - "accounts": [ + "metadata": { + "name": "agentic_commerce_v3", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [ { + "name": "accept_authority", + "docs": [ + "Accepts a pending authority nomination, completing ownership transfer." + ], "discriminator": [ - 182, - 32, - 157, - 40, - 114, - 224, - 13, - 39 + 107, + 86, + 198, + 91, + 33, + 12, + 107, + 160 + ], + "accounts": [ + { + "name": "new_authority", + "signer": true + }, + { + "name": "acp_state", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 97, + 99, + 112, + 95, + 115, + 116, + 97, + 116, + 101 + ] + } + ] + } + } ], - "name": "AcpState" + "args": [] }, { + "name": "add_hook_whitelist", + "docs": [ + "Whitelists a hook program by creating its `HookWhitelist` PDA. Authority-only." + ], "discriminator": [ - 5, - 224, - 69, - 103, + 155, + 81, + 232, + 227, 41, - 89, - 183, - 3 + 160, + 231, + 189 + ], + "accounts": [ + { + "name": "authority", + "writable": true, + "signer": true + }, + { + "name": "acp_state", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 97, + 99, + 112, + 95, + 115, + 116, + 97, + 116, + 101 + ] + } + ] + } + }, + { + "name": "hook_whitelist", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 104, + 111, + 111, + 107, + 95, + 119, + 104, + 105, + 116, + 101, + 108, + 105, + 115, + 116 + ] + }, + { + "kind": "arg", + "path": "hook" + } + ] + } + }, + { + "name": "hook_program_data", + "pda": { + "seeds": [ + { + "kind": "arg", + "path": "hook" + } + ], + "program": { + "kind": "const", + "value": [ + 2, + 168, + 246, + 145, + 78, + 136, + 161, + 176, + 226, + 16, + 21, + 62, + 247, + 99, + 174, + 43, + 0, + 194, + 185, + 61, + 22, + 193, + 36, + 210, + 192, + 83, + 122, + 16, + 4, + 128, + 0, + 0 + ] + } + } + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" + } ], - "name": "HookWhitelist" + "args": [ + { + "name": "hook", + "type": "pubkey" + } + ] }, { + "name": "claim_refund", + "docs": [ + "Permissionless refund for expired jobs. Tokens always go to `job.client`." + ], "discriminator": [ - 75, - 124, - 80, - 203, + 15, + 16, + 30, 161, - 180, - 202, - 80 + 255, + 228, + 97, + 60 ], - "name": "Job" - } - ], - "errors": [ - { - "code": 6000, - "msg": "Unauthorized", - "name": "Unauthorized" - }, - { - "code": 6001, - "msg": "Only client or provider", - "name": "OnlyClientOrProvider" - }, - { - "code": 6002, - "msg": "Only evaluator", - "name": "OnlyEvaluator" - }, - { - "code": 6003, - "msg": "Only provider", - "name": "OnlyProvider" - }, - { - "code": 6004, - "msg": "Only client", - "name": "OnlyClient" - }, - { - "code": 6005, - "msg": "ZeroAddress", - "name": "ZeroAddress" - }, - { - "code": 6006, - "msg": "ZeroAddressEvaluator", - "name": "ZeroAddressEvaluator" - }, - { - "code": 6007, - "msg": "ZeroAddressProvider", - "name": "ZeroAddressProvider" - }, - { - "code": 6008, - "msg": "Description too long", - "name": "DescriptionTooLong" - }, - { - "code": 6009, - "msg": "Provider cannot be the client", - "name": "ProviderCannotBeClient" - }, - { - "code": 6010, - "msg": "Evaluator cannot be the provider", - "name": "EvaluatorCannotBeProvider" - }, - { - "code": 6011, - "msg": "Evaluator cannot be the client", - "name": "EvaluatorCannotBeClient" - }, - { - "code": 6012, - "msg": "Invalid hook program", - "name": "InvalidHookProgram" - }, - { - "code": 6013, - "msg": "Hook not whitelisted", - "name": "HookNotWhitelisted" - }, - { - "code": 6014, - "msg": "Invalid job", - "name": "InvalidJob" - }, - { - "code": 6015, - "msg": "Wrong job status", - "name": "WrongStatus" - }, - { - "code": 6016, - "msg": "Amount must be greater than zero", - "name": "AmountMustBeGreaterThanZero" - }, - { - "code": 6017, - "msg": "Invalid payment token", - "name": "InvalidPaymentToken" - }, - { - "code": 6018, - "msg": "Budget not set", - "name": "BudgetNotSet" - }, - { - "code": 6019, - "msg": "Budget mismatch", - "name": "BudgetMismatch" - }, - { - "code": 6020, - "msg": "Provider not set", - "name": "ProviderNotSet" - }, - { - "code": 6021, - "msg": "Expiry too short", - "name": "ExpiryTooShort" - }, - { - "code": 6022, - "msg": "ExpiredAt must be in the future", - "name": "ExpiredAtMustBeInTheFuture" - }, - { - "code": 6023, - "msg": "Platform fee too high", - "name": "PlatformFeeTooHigh" - }, - { - "code": 6024, - "msg": "Combined fees too high", - "name": "FeesTooHigh" - }, - { - "code": 6025, - "msg": "Invalid destination account", - "name": "InvalidDestination" - }, - { - "code": 6026, - "msg": "Invalid vault authority", - "name": "InvalidVaultAuthority" - }, - { - "code": 6027, - "msg": "Invalid vault owner", - "name": "InvalidVaultOwner" - }, - { - "code": 6028, - "msg": "Invalid vault mint", - "name": "InvalidVaultMint" - }, - { - "code": 6029, - "msg": "Budget mint must be set when budget amount is nonzero", - "name": "BudgetMintNotSet" - }, - { - "code": 6030, - "msg": "Insufficient vault balance", - "name": "InsufficientVaultBalance" - }, - { - "code": 6031, - "msg": "Missing required account for funded job", - "name": "MissingRequiredAccount" - }, - { - "code": 6032, - "msg": "Arithmetic overflow", - "name": "ArithmeticOverflow" - }, - { - "code": 6033, - "msg": "Hook program upgrade authority must be None or the ACP authority", - "name": "UntrustedHookUpgradeAuthority" - }, - { - "code": 6034, - "msg": "Missing hook program account: job declares a hook but hook_program was not supplied", - "name": "MissingHookProgram" - }, - { - "code": 6035, - "msg": "Invalid complete_opt_params: must be empty (legacy mode) or [u16 submit_count][...] with submit_count <= remaining_accounts.len()", - "name": "InvalidAutoCompleteParams" - }, - { - "code": 6036, - "msg": "Protocol is paused", - "name": "ProtocolPaused" - } - ], - "events": [ - { - "discriminator": [ - 83, - 253, - 94, - 222, - 179, - 41, - 95, - 127 - ], - "name": "AuthorityNominated" - }, - { - "discriminator": [ - 245, - 109, - 179, - 54, - 135, - 92, - 22, - 64 - ], - "name": "AuthorityTransferred" - }, - { - "discriminator": [ - 151, - 45, - 229, - 243, - 179, - 219, - 252, - 60 - ], - "name": "BudgetSet" - }, - { - "discriminator": [ - 178, - 236, - 127, - 234, - 245, - 207, - 235, - 25 + "accounts": [ + { + "name": "caller", + "docs": [ + "Anyone can call — tokens always go to job.client" + ], + "signer": true + }, + { + "name": "acp_state", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 97, + 99, + 112, + 95, + 115, + 116, + 97, + 116, + 101 + ] + } + ] + } + }, + { + "name": "job", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 106, + 111, + 98 + ] + }, + { + "kind": "account", + "path": "job.client", + "account": "Job" + }, + { + "kind": "account", + "path": "job.seed", + "account": "Job" + } + ] + } + }, + { + "name": "vault", + "docs": [ + "Optional: required when the job was Funded/Submitted with budget > 0" + ], + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "job" + } + ] + } + }, + { + "name": "vault_authority", + "optional": true + }, + { + "name": "client_token_account", + "docs": [ + "Optional: required when the job was Funded/Submitted with budget > 0" + ], + "writable": true, + "optional": true + }, + { + "name": "sponsor", + "docs": [ + "acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees." + ], + "writable": true, + "optional": true + }, + { + "name": "token_program", + "optional": true, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + } ], - "name": "EvaluatorFeePaid" + "args": [] }, { - "discriminator": [ - 104, - 212, - 170, - 200, - 39, - 244, - 192, - 182 + "name": "complete", + "docs": [ + "Evaluator approves a submitted job, distributing escrowed funds." ], - "name": "EvaluatorFeeUpdated" - }, - { "discriminator": [ - 44, - 210, - 252, - 233, - 231, + 0, + 77, + 224, + 147, + 136, + 25, 88, - 168, - 1 - ], - "name": "HookDetached" - }, - { - "discriminator": [ - 112, - 220, - 175, - 129, - 195, - 163, - 32, - 72 - ], - "name": "HookWhitelistUpdated" - }, - { - "discriminator": [ - 176, - 207, - 246, - 115, - 95, - 179, - 9, - 132 + 76 ], - "name": "JobCompleted" - }, - { - "discriminator": [ - 48, - 110, - 162, - 177, - 67, - 74, - 159, - 131 - ], - "name": "JobCreated" - }, - { - "discriminator": [ - 213, - 123, - 115, - 173, - 157, - 242, - 12, - 71 - ], - "name": "JobExpired" - }, - { - "discriminator": [ - 109, - 177, - 206, - 113, - 255, - 142, - 11, - 19 - ], - "name": "JobFunded" - }, - { - "discriminator": [ - 23, - 78, - 227, - 57, - 28, - 0, - 197, - 216 - ], - "name": "JobRejected" - }, - { - "discriminator": [ - 129, - 204, - 35, - 127, - 38, - 50, - 131, - 248 - ], - "name": "JobSubmitted" - }, - { - "discriminator": [ - 175, - 57, - 198, - 136, - 192, - 66, - 204, - 73 - ], - "name": "PauseSet" - }, - { - "discriminator": [ - 160, - 132, - 155, - 232, - 46, - 254, - 69, - 219 - ], - "name": "PaymentReleased" - }, - { - "discriminator": [ - 210, - 134, - 201, - 4, - 92, - 228, - 80, - 26 - ], - "name": "PlatformFeeUpdated" - }, - { - "discriminator": [ - 10, - 148, - 30, - 104, - 250, - 235, - 161, - 159 - ], - "name": "ProviderSet" - }, - { - "discriminator": [ - 35, - 103, - 149, - 246, - 196, - 123, - 221, - 99 - ], - "name": "Refunded" - } - ], - "instructions": [ - { "accounts": [ { - "name": "new_authority", + "name": "evaluator", "signer": true }, { @@ -489,32 +347,151 @@ ] } ] - }, - "writable": true + } + }, + { + "name": "job", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 106, + 111, + 98 + ] + }, + { + "kind": "account", + "path": "job.client", + "account": "Job" + }, + { + "kind": "account", + "path": "job.seed", + "account": "Job" + } + ] + } + }, + { + "name": "vault", + "docs": [ + "Optional: required when budget > 0" + ], + "writable": true, + "optional": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "job" + } + ] + } + }, + { + "name": "vault_authority", + "optional": true + }, + { + "name": "provider_token_account", + "docs": [ + "Optional: required when budget > 0" + ], + "writable": true, + "optional": true + }, + { + "name": "treasury_token_account", + "docs": [ + "Optional: required when budget > 0" + ], + "writable": true, + "optional": true + }, + { + "name": "evaluator_token_account", + "docs": [ + "Optional: required when evaluator_fee_bp > 0 and budget > 0" + ], + "writable": true, + "optional": true + }, + { + "name": "sponsor", + "docs": [ + "acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees." + ], + "writable": true, + "optional": true + }, + { + "name": "token_program", + "optional": true, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "hook_program", + "optional": true + }, + { + "name": "hook_whitelist", + "optional": true } ], - "args": [], - "discriminator": [ - 107, - 86, - 198, - 91, - 33, - 12, - 107, - 160 - ], - "docs": [ - "Accepts a pending authority nomination, completing ownership transfer." - ], - "name": "accept_authority" + "args": [ + { + "name": "reason", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "opt_params", + "type": "bytes" + } + ] }, { + "name": "create_job", + "docs": [ + "Creates a new job in the Open state. Caller becomes the client.", + "", + "`seed` is a client-chosen uniquifier, not a protocol sequence — the job's", + "identity is its own address. Any unused value works; `init` rejects a", + "reuse by the same client. Creations no longer contend on a shared counter,", + "so they are unbounded per slot." + ], + "discriminator": [ + 178, + 130, + 217, + 110, + 100, + 27, + 82, + 119 + ], "accounts": [ { - "name": "authority", - "signer": true, - "writable": true + "name": "client", + "writable": true, + "signer": true }, { "name": "acp_state", @@ -538,117 +515,88 @@ } }, { - "name": "hook_whitelist", + "name": "job", + "writable": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 104, - 111, + 106, 111, - 107, - 95, - 119, - 104, - 105, - 116, - 101, - 108, - 105, - 115, - 116 + 98 ] }, { - "kind": "arg", - "path": "hook" - } - ] - }, - "writable": true - }, - { - "name": "hook_program_data", - "pda": { - "program": { - "kind": "const", - "value": [ - 2, - 168, - 246, - 145, - 78, - 136, - 161, - 176, - 226, - 16, - 21, - 62, - 247, - 99, - 174, - 43, - 0, - 194, - 185, - 61, - 22, - 193, - 36, - 210, - 192, - 83, - 122, - 16, - 4, - 128, - 0, - 0 - ] - }, - "seeds": [ + "kind": "account", + "path": "client" + }, { "kind": "arg", - "path": "hook" + "path": "seed" } ] } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "hook_whitelist", + "docs": [ + "Optional: must be provided if hook_address is Some" + ], + "optional": true + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "hook", + "name": "seed", + "type": "u64" + }, + { + "name": "provider", + "type": "pubkey" + }, + { + "name": "evaluator", "type": "pubkey" + }, + { + "name": "description", + "type": "string" + }, + { + "name": "expired_at", + "type": "i64" + }, + { + "name": "hook_address", + "type": { + "option": "pubkey" + } } + ] + }, + { + "name": "detach_hook", + "docs": [ + "Detaches a misbehaving hook from an in-flight job so escrow keeps flowing. Authority-only." ], "discriminator": [ - 155, - 81, - 232, - 227, - 41, - 160, - 231, - 189 - ], - "docs": [ - "Whitelists a hook program by creating its `HookWhitelist` PDA. Authority-only." + 142, + 76, + 153, + 126, + 252, + 12, + 56, + 231 ], - "name": "add_hook_whitelist" - }, - { "accounts": [ { - "docs": [ - "Anyone can call — tokens always go to job.client" - ], - "name": "caller", + "name": "authority", "signer": true }, { @@ -674,6 +622,7 @@ }, { "name": "job", + "writable": true, "pda": { "seeds": [ { @@ -685,72 +634,68 @@ ] }, { - "account": "Job", "kind": "account", - "path": "job.client" + "path": "job.client", + "account": "Job" }, { - "account": "Job", "kind": "account", - "path": "job.job_id" + "path": "job.seed", + "account": "Job" } ] - }, - "writable": true - }, - { - "docs": [ - "Optional: required when the job was Funded/Submitted with budget > 0" - ], - "name": "vault", - "optional": true, - "writable": true - }, - { - "name": "vault_authority", - "optional": true - }, - { - "docs": [ - "Optional: required when the job was Funded/Submitted with budget > 0" - ], - "name": "client_token_account", - "optional": true, - "writable": true - }, - { - "name": "platform_treasury", - "optional": true, - "writable": true - }, - { - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "name": "token_program", - "optional": true + } } ], - "args": [], - "discriminator": [ - 15, - 16, - 30, - 161, - 255, - 228, - 97, - 60 - ], - "docs": [ - "Permissionless refund for expired jobs. Tokens always go to `job.client`." - ], - "name": "claim_refund" + "args": [] }, { + "name": "fund", + "docs": [ + "Escrows tokens from the client into the vault and transitions the job to Funded." + ], + "discriminator": [ + 218, + 188, + 111, + 221, + 152, + 113, + 174, + 7 + ], "accounts": [ { - "name": "evaluator", + "name": "client", + "writable": true, "signer": true }, + { + "name": "job", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 106, + 111, + 98 + ] + }, + { + "kind": "account", + "path": "job.client", + "account": "Job" + }, + { + "kind": "account", + "path": "job.seed", + "account": "Job" + } + ] + } + }, { "name": "acp_state", "pda": { @@ -773,103 +718,118 @@ } }, { - "name": "job", - "writable": true - }, - { + "name": "client_token_account", "docs": [ "Optional: required when budget > 0" ], + "writable": true, + "optional": true + }, + { "name": "vault", + "docs": [ + "Per-job escrow vault, program-derived so its ADDRESS is unique to the job.", + "That, not a stored mint, is what makes `vault.mint` authoritative on every", + "exit path: there is exactly one vault per job and a token account's mint is", + "immutable once initialized, so no substitute can be passed in its place.", + "Required when budget > 0." + ], + "writable": true, "optional": true, - "writable": true + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "job" + } + ] + } }, { "name": "vault_authority", "optional": true }, { - "docs": [ - "Optional: required when budget > 0" - ], - "name": "provider_token_account", - "optional": true, - "writable": true + "name": "mint", + "optional": true }, { - "docs": [ - "Optional: required when budget > 0" - ], - "name": "treasury_token_account", + "name": "token_program", "optional": true, - "writable": true + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }, { - "docs": [ - "Optional: required when evaluator_fee_bp > 0 and budget > 0" - ], - "name": "evaluator_token_account", - "optional": true, - "writable": true + "name": "system_program", + "address": "11111111111111111111111111111111" }, { - "name": "platform_treasury", - "optional": true, - "writable": true + "name": "hook_program", + "optional": true }, { - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "name": "token_program", + "name": "hook_whitelist", "optional": true }, { - "name": "hook_program", + "name": "hook_delegate", + "docs": [ + "Required when hook is present and budget > 0; approved on client_token_account for budget_amount." + ], "optional": true }, { - "name": "hook_whitelist", + "name": "delegate_whitelist", + "docs": [ + "is owned by a sub-hook (MultiHookRouter) rather than the job's hook program." + ], "optional": true } ], "args": [ { - "name": "reason", - "type": { - "array": [ - "u8", - 32 - ] - } + "name": "expected_budget", + "type": "u64" }, { "name": "opt_params", "type": "bytes" } + ] + }, + { + "name": "initialize", + "docs": [ + "Initializes the global `AcpState` PDA. Caller must be the program's upgrade authority." ], "discriminator": [ - 0, - 77, - 224, - 147, - 136, - 25, - 88, - 76 - ], - "docs": [ - "Evaluator approves a submitted job, distributing escrowed funds." + 175, + 175, + 109, + 31, + 13, + 152, + 155, + 237 ], - "name": "complete" - }, - { "accounts": [ { - "name": "client", - "signer": true, - "writable": true + "name": "authority", + "writable": true, + "signer": true }, { "name": "acp_state", + "writable": true, "pda": { "seeds": [ { @@ -887,93 +847,144 @@ ] } ] - }, - "writable": true + } }, { - "name": "job", + "name": "program_data", "pda": { "seeds": [ { "kind": "const", "value": [ - 106, - 111, - 98 + 215, + 88, + 164, + 37, + 37, + 167, + 35, + 155, + 134, + 215, + 69, + 245, + 181, + 35, + 244, + 68, + 27, + 66, + 252, + 90, + 229, + 167, + 231, + 185, + 212, + 144, + 134, + 15, + 14, + 60, + 203, + 45 ] - }, - { - "kind": "account", - "path": "client" - }, - { - "account": "AcpState", - "kind": "account", - "path": "acp_state.job_counter.wrapping_add(1)" } - ] - }, - "writable": true + ], + "program": { + "kind": "const", + "value": [ + 2, + 168, + 246, + 145, + 78, + 136, + 161, + 176, + 226, + 16, + 21, + 62, + 247, + 99, + 174, + 43, + 0, + 194, + 185, + 61, + 22, + 193, + 36, + 210, + 192, + 83, + 122, + 16, + 4, + 128, + 0, + 0 + ] + } + } }, { - "docs": [ - "Optional: must be provided if hook_address is Some" - ], - "name": "hook_whitelist", - "optional": true + "name": "payment_token" }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "provider", + "name": "platform_treasury", "type": "pubkey" }, { - "name": "evaluator", + "name": "sponsor", "type": "pubkey" }, { - "name": "description", - "type": "string" - }, - { - "name": "expired_at", - "type": "i64" + "name": "platform_fee_bp", + "type": "u64" }, { - "name": "hook_address", - "type": { - "option": "pubkey" - } + "name": "evaluator_fee_bp", + "type": "u64" } + ] + }, + { + "name": "migrate_state", + "docs": [ + "Reallocs the live `AcpState` account to the current layout and backfills", + "`sponsor` when unset. Idempotent. Authority-only." ], "discriminator": [ - 178, - 130, - 217, - 110, - 100, - 27, - 82, - 119 - ], - "docs": [ - "Creates a new job in the Open state. Caller becomes the client." + 34, + 189, + 226, + 222, + 218, + 156, + 19, + 213 ], - "name": "create_job" - }, - { "accounts": [ { "name": "authority", + "writable": true, "signer": true }, { "name": "acp_state", + "docs": [ + "field (offset [8..40]) against the signer in the handler." + ], + "writable": true, "pda": { "seeds": [ { @@ -994,58 +1005,86 @@ } }, { - "name": "job", + "name": "system_program", + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "sponsor", + "type": "pubkey" + } + ] + }, + { + "name": "nominate_authority", + "docs": [ + "Nominates a new authority for ownership transfer. Authority-only." + ], + "discriminator": [ + 148, + 182, + 144, + 91, + 186, + 12, + 118, + 18 + ], + "accounts": [ + { + "name": "authority", + "signer": true + }, + { + "name": "acp_state", + "writable": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 106, - 111, - 98 + 97, + 99, + 112, + 95, + 115, + 116, + 97, + 116, + 101 ] - }, - { - "account": "Job", - "kind": "account", - "path": "job.client" - }, - { - "account": "Job", - "kind": "account", - "path": "job.job_id" } ] - }, - "writable": true + } } ], - "args": [], - "discriminator": [ - 142, - 76, - 153, - 126, - 252, - 12, - 56, - 231 - ], - "docs": [ - "Detaches a misbehaving hook from an in-flight job so escrow keeps flowing. Authority-only." - ], - "name": "detach_hook" + "args": [ + { + "name": "new_authority", + "type": "pubkey" + } + ] }, { + "name": "reject", + "docs": [ + "Rejects a job, refunding any escrowed tokens to the client." + ], + "discriminator": [ + 135, + 7, + 63, + 85, + 131, + 114, + 111, + 224 + ], "accounts": [ { - "name": "client", - "signer": true, - "writable": true - }, - { - "name": "job", - "writable": true + "name": "caller", + "signer": true }, { "name": "acp_state", @@ -1069,37 +1108,75 @@ } }, { - "docs": [ - "Optional: required when budget > 0" - ], - "name": "client_token_account", - "optional": true, - "writable": true + "name": "job", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 106, + 111, + 98 + ] + }, + { + "kind": "account", + "path": "job.client", + "account": "Job" + }, + { + "kind": "account", + "path": "job.seed", + "account": "Job" + } + ] + } }, { - "docs": [ - "Optional: required when budget > 0 (init vault for escrow)" - ], "name": "vault", + "writable": true, "optional": true, - "writable": true + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 118, + 97, + 117, + 108, + 116 + ] + }, + { + "kind": "account", + "path": "job" + } + ] + } }, { "name": "vault_authority", "optional": true }, { - "name": "mint", + "name": "client_token_account", + "writable": true, "optional": true }, { - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "name": "token_program", + "name": "sponsor", + "docs": [ + "acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees." + ], + "writable": true, "optional": true }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "token_program", + "optional": true, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }, { "name": "hook_program", @@ -1108,53 +1185,44 @@ { "name": "hook_whitelist", "optional": true - }, - { - "docs": [ - "Required when hook is present and budget > 0; approved on client_token_account for budget_amount." - ], - "name": "hook_delegate", - "optional": true - }, - { - "docs": [ - "is owned by a sub-hook (MultiHookRouter) rather than the job's hook program." - ], - "name": "delegate_whitelist", - "optional": true } ], "args": [ { - "name": "expected_budget", - "type": "u64" + "name": "reason", + "type": { + "array": [ + "u8", + 32 + ] + } }, { "name": "opt_params", "type": "bytes" } + ] + }, + { + "name": "remove_hook_whitelist", + "docs": [ + "Removes a hook program from the whitelist by closing its PDA. Authority-only." ], "discriminator": [ - 218, - 188, - 111, - 221, - 152, - 113, - 174, - 7 - ], - "docs": [ - "Escrows tokens from the client into the vault and transitions the job to Funded." + 110, + 220, + 154, + 1, + 141, + 191, + 150, + 74 ], - "name": "fund" - }, - { "accounts": [ { "name": "authority", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "acp_state", @@ -1175,142 +1243,94 @@ ] } ] - }, - "writable": true + } }, { - "name": "program_data", + "name": "hook_whitelist", + "writable": true, "pda": { - "program": { - "kind": "const", - "value": [ - 2, - 168, - 246, - 145, - 78, - 136, - 161, - 176, - 226, - 16, - 21, - 62, - 247, - 99, - 174, - 43, - 0, - 194, - 185, - 61, - 22, - 193, - 36, - 210, - 192, - 83, - 122, - 16, - 4, - 128, - 0, - 0 - ] - }, "seeds": [ { "kind": "const", "value": [ - 215, - 88, - 164, - 37, - 37, - 167, - 35, - 155, - 134, - 215, - 69, - 245, - 181, - 35, - 244, - 68, - 27, - 66, - 252, - 90, - 229, - 167, - 231, - 185, - 212, - 144, - 134, - 15, - 14, - 60, - 203, - 45 + 104, + 111, + 111, + 107, + 95, + 119, + 104, + 105, + 116, + 101, + 108, + 105, + 115, + 116 ] + }, + { + "kind": "arg", + "path": "hook" } ] } - }, - { - "name": "payment_token" - }, - { - "address": "11111111111111111111111111111111", - "name": "system_program" } ], "args": [ { - "name": "platform_treasury", + "name": "hook", "type": "pubkey" - }, - { - "name": "platform_fee_bp", - "type": "u64" - }, - { - "name": "evaluator_fee_bp", - "type": "u64" - }, - { - "name": "job_counter", - "type": "u64" } + ] + }, + { + "name": "set_budget", + "docs": [ + "Sets the budget amount on an Open job. Provider-only." ], "discriminator": [ - 175, - 175, - 109, - 31, - 13, - 152, - 155, - 237 - ], - "docs": [ - "Initializes the global `AcpState` PDA. Caller must be the program's upgrade authority." + 148, + 121, + 226, + 12, + 183, + 120, + 26, + 227 ], - "name": "initialize" - }, - { "accounts": [ { - "name": "authority", - "signer": true, - "writable": true + "name": "caller", + "signer": true + }, + { + "name": "job", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 106, + 111, + 98 + ] + }, + { + "kind": "account", + "path": "job.client", + "account": "Job" + }, + { + "kind": "account", + "path": "job.seed", + "account": "Job" + } + ] + } }, { - "docs": [ - "field (offset [8..40]) against the signer in the handler." - ], "name": "acp_state", "pda": { "seeds": [ @@ -1329,31 +1349,43 @@ ] } ] - }, - "writable": true + } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "hook_program", + "optional": true + }, + { + "name": "hook_whitelist", + "optional": true } ], - "args": [], - "discriminator": [ - 34, - 189, - 226, - 222, - 218, - 156, - 19, - 213 - ], - "docs": [ - "One-time realloc of the live `AcpState` account to the current layout (adds `paused`). Authority-only." - ], - "name": "migrate_state" + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "opt_params", + "type": "bytes" + } + ] }, { + "name": "set_evaluator_fee", + "docs": [ + "Updates the evaluator fee rate. Authority-only." + ], + "discriminator": [ + 81, + 212, + 20, + 60, + 184, + 238, + 205, + 132 + ], "accounts": [ { "name": "authority", @@ -1361,6 +1393,7 @@ }, { "name": "acp_state", + "writable": true, "pda": { "seeds": [ { @@ -1378,39 +1411,39 @@ ] } ] - }, - "writable": true + } } ], "args": [ { - "name": "new_authority", - "type": "pubkey" + "name": "fee_bp", + "type": "u64" } + ] + }, + { + "name": "set_pause", + "docs": [ + "Toggles the global pause flag. When paused, all lifecycle instructions revert. Authority-only." ], "discriminator": [ - 148, - 182, - 144, - 91, - 186, - 12, - 118, - 18 - ], - "docs": [ - "Nominates a new authority for ownership transfer. Authority-only." + 63, + 32, + 154, + 2, + 56, + 103, + 79, + 45 ], - "name": "nominate_authority" - }, - { "accounts": [ { - "name": "caller", + "name": "authority", "signer": true }, { "name": "acp_state", + "writable": true, "pda": { "seeds": [ { @@ -1429,105 +1462,39 @@ } ] } - }, - { - "name": "job", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 106, - 111, - 98 - ] - }, - { - "account": "Job", - "kind": "account", - "path": "job.client" - }, - { - "account": "Job", - "kind": "account", - "path": "job.job_id" - } - ] - }, - "writable": true - }, - { - "name": "vault", - "optional": true, - "writable": true - }, - { - "name": "vault_authority", - "optional": true - }, - { - "name": "client_token_account", - "optional": true, - "writable": true - }, - { - "name": "platform_treasury", - "optional": true, - "writable": true - }, - { - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "name": "token_program", - "optional": true - }, - { - "name": "hook_program", - "optional": true - }, - { - "name": "hook_whitelist", - "optional": true } ], "args": [ { - "name": "reason", - "type": { - "array": [ - "u8", - 32 - ] - } - }, - { - "name": "opt_params", - "type": "bytes" + "name": "paused", + "type": "bool" } + ] + }, + { + "name": "set_payment_token", + "docs": [ + "Updates the mint accepted for job budgets. Authority-only. Applies to", + "jobs funded after it; already-funded jobs are unaffected." ], "discriminator": [ - 135, - 7, - 63, - 85, - 131, - 114, - 111, - 224 - ], - "docs": [ - "Rejects a job, refunding any escrowed tokens to the client." + 155, + 213, + 140, + 249, + 53, + 59, + 20, + 5 ], - "name": "reject" - }, - { "accounts": [ { "name": "authority", - "signer": true, - "writable": true + "signer": true }, { "name": "acp_state", + "writable": true, "pda": { "seeds": [ { @@ -1548,71 +1515,90 @@ } }, { - "name": "hook_whitelist", + "name": "payment_token", + "docs": [ + "Typed as `Mint` so the new value must be a real SPL mint that exists,", + "the same guarantee `initialize` gives. This also makes a zero-address", + "value unrepresentable, so no explicit check is needed." + ] + } + ], + "args": [] + }, + { + "name": "set_platform_fee", + "docs": [ + "Updates the platform fee rate and treasury address. Authority-only." + ], + "discriminator": [ + 19, + 70, + 111, + 182, + 156, + 58, + 208, + 203 + ], + "accounts": [ + { + "name": "authority", + "signer": true + }, + { + "name": "acp_state", + "writable": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 104, - 111, - 111, - 107, + 97, + 99, + 112, 95, - 119, - 104, - 105, - 116, - 101, - 108, - 105, 115, - 116 + 116, + 97, + 116, + 101 ] - }, - { - "kind": "arg", - "path": "hook" } ] - }, - "writable": true + } } ], "args": [ { - "name": "hook", + "name": "fee_bp", + "type": "u64" + }, + { + "name": "treasury", "type": "pubkey" } + ] + }, + { + "name": "set_provider", + "docs": [ + "Assigns a provider to an Open job whose provider is currently unset." ], "discriminator": [ - 110, - 220, - 154, - 1, - 141, + 42, + 159, + 3, 191, - 150, - 74 - ], - "docs": [ - "Removes a hook program from the whitelist by closing its PDA. Authority-only." + 52, + 175, + 112, + 88 ], - "name": "remove_hook_whitelist" - }, - { "accounts": [ { - "name": "caller", + "name": "client", "signer": true }, - { - "name": "job", - "writable": true - }, - { - "name": "budget_mint" - }, { "name": "acp_state", "pda": { @@ -1635,90 +1621,54 @@ } }, { - "name": "hook_program", - "optional": true - }, - { - "name": "hook_whitelist", - "optional": true - } - ], - "args": [ - { - "name": "amount", - "type": "u64" - }, - { - "name": "opt_params", - "type": "bytes" - } - ], - "discriminator": [ - 148, - 121, - 226, - 12, - 183, - 120, - 26, - 227 - ], - "docs": [ - "Sets the budget amount and payment mint on an Open job. Provider-only." - ], - "name": "set_budget" - }, - { - "accounts": [ - { - "name": "authority", - "signer": true - }, - { - "name": "acp_state", + "name": "job", + "writable": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 97, - 99, - 112, - 95, - 115, - 116, - 97, - 116, - 101 + 106, + 111, + 98 ] + }, + { + "kind": "account", + "path": "job.client", + "account": "Job" + }, + { + "kind": "account", + "path": "job.seed", + "account": "Job" } ] - }, - "writable": true + } } ], "args": [ { - "name": "fee_bp", - "type": "u64" + "name": "provider", + "type": "pubkey" } + ] + }, + { + "name": "set_sponsor", + "docs": [ + "Updates the wallet that receives reclaimed rent. Authority-only." ], "discriminator": [ - 81, - 212, - 20, - 60, + 217, 184, - 238, - 205, - 132 - ], - "docs": [ - "Updates the evaluator fee rate. Authority-only." + 101, + 32, + 234, + 70, + 199, + 138 ], - "name": "set_evaluator_fee" - }, - { "accounts": [ { "name": "authority", @@ -1726,6 +1676,7 @@ }, { "name": "acp_state", + "writable": true, "pda": { "seeds": [ { @@ -1743,35 +1694,34 @@ ] } ] - }, - "writable": true + } } ], "args": [ { - "name": "paused", - "type": "bool" + "name": "sponsor", + "type": "pubkey" } + ] + }, + { + "name": "submit", + "docs": [ + "Records the provider's deliverable. Auto-completes if no evaluator is set." ], "discriminator": [ - 63, - 32, - 154, - 2, - 56, - 103, - 79, - 45 - ], - "docs": [ - "Toggles the global pause flag. When paused, all lifecycle instructions revert. Authority-only." + 88, + 166, + 102, + 181, + 162, + 127, + 170, + 48 ], - "name": "set_pause" - }, - { "accounts": [ { - "name": "authority", + "name": "provider", "signer": true }, { @@ -1793,156 +1743,92 @@ ] } ] - }, - "writable": true - } - ], - "args": [ - { - "name": "fee_bp", - "type": "u64" - }, - { - "name": "treasury", - "type": "pubkey" - } - ], - "discriminator": [ - 19, - 70, - 111, - 182, - 156, - 58, - 208, - 203 - ], - "docs": [ - "Updates the platform fee rate and treasury address. Authority-only." - ], - "name": "set_platform_fee" - }, - { - "accounts": [ - { - "name": "client", - "signer": true + } }, { - "name": "acp_state", + "name": "job", + "writable": true, "pda": { "seeds": [ { "kind": "const", "value": [ - 97, - 99, - 112, - 95, - 115, - 116, - 97, - 116, - 101 + 106, + 111, + 98 ] + }, + { + "kind": "account", + "path": "job.client", + "account": "Job" + }, + { + "kind": "account", + "path": "job.seed", + "account": "Job" } ] } }, { - "name": "job", - "writable": true - } - ], - "args": [ - { - "name": "provider", - "type": "pubkey" - } - ], - "discriminator": [ - 42, - 159, - 3, - 191, - 52, - 175, - 112, - 88 - ], - "docs": [ - "Assigns a provider to an Open job whose provider is currently unset." - ], - "name": "set_provider" - }, - { - "accounts": [ - { - "name": "provider", - "signer": true - }, - { - "name": "acp_state", + "name": "vault", + "docs": [ + "Optional: required for auto-complete when budget > 0" + ], + "writable": true, + "optional": true, "pda": { "seeds": [ { "kind": "const", "value": [ + 118, 97, - 99, - 112, - 95, - 115, - 116, - 97, - 116, - 101 + 117, + 108, + 116 ] + }, + { + "kind": "account", + "path": "job" } ] } }, { - "name": "job", - "writable": true + "name": "vault_authority", + "optional": true }, { + "name": "provider_token_account", "docs": [ "Optional: required for auto-complete when budget > 0" ], - "name": "vault", - "optional": true, - "writable": true - }, - { - "name": "vault_authority", + "writable": true, "optional": true }, { + "name": "treasury_token_account", "docs": [ "Optional: required for auto-complete when budget > 0" ], - "name": "provider_token_account", - "optional": true, - "writable": true + "writable": true, + "optional": true }, { + "name": "sponsor", "docs": [ - "Optional: required for auto-complete when budget > 0" + "acp_state.sponsor. Distinct from platform_treasury, which receives SPL fees." ], - "name": "treasury_token_account", - "optional": true, - "writable": true - }, - { - "name": "platform_treasury", - "optional": true, - "writable": true + "writable": true, + "optional": true }, { - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "name": "token_program", - "optional": true + "optional": true, + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }, { "name": "hook_program", @@ -1953,27 +1839,27 @@ "optional": true }, { + "name": "hook_delegate", "docs": [ "sub-hook program. Required when hook is present and budget > 0; approved as", "delegate on provider_hook_token_account for budget_amount." ], - "name": "hook_delegate", "optional": true }, { + "name": "provider_hook_token_account", "docs": [ "Optional: provider's token account used by the hook for escrow transfer.", "Required when hook is present and budget > 0 and evaluator is set." ], - "name": "provider_hook_token_account", - "optional": true, - "writable": true + "writable": true, + "optional": true }, { + "name": "delegate_whitelist", "docs": [ "using MultiHookRouter -- the delegate is owned by a sub-hook, not the router." ], - "name": "delegate_whitelist", "optional": true } ], @@ -1995,114 +1881,605 @@ "name": "complete_opt_params", "type": "bytes" } - ], + ] + } + ], + "accounts": [ + { + "name": "AcpState", "discriminator": [ - 88, - 166, - 102, - 181, - 162, - 127, - 170, - 48 - ], - "docs": [ - "Records the provider's deliverable. Auto-completes if no evaluator is set." - ], - "name": "submit" + 182, + 32, + 157, + 40, + 114, + 224, + 13, + 39 + ] + }, + { + "name": "HookWhitelist", + "discriminator": [ + 5, + 224, + 69, + 103, + 41, + 89, + 183, + 3 + ] + }, + { + "name": "Job", + "discriminator": [ + 75, + 124, + 80, + 203, + 161, + 180, + 202, + 80 + ] + } + ], + "events": [ + { + "name": "AuthorityNominated", + "discriminator": [ + 83, + 253, + 94, + 222, + 179, + 41, + 95, + 127 + ] + }, + { + "name": "AuthorityTransferred", + "discriminator": [ + 245, + 109, + 179, + 54, + 135, + 92, + 22, + 64 + ] + }, + { + "name": "BudgetSet", + "discriminator": [ + 151, + 45, + 229, + 243, + 179, + 219, + 252, + 60 + ] + }, + { + "name": "EvaluatorFeePaid", + "discriminator": [ + 178, + 236, + 127, + 234, + 245, + 207, + 235, + 25 + ] + }, + { + "name": "EvaluatorFeeUpdated", + "discriminator": [ + 104, + 212, + 170, + 200, + 39, + 244, + 192, + 182 + ] + }, + { + "name": "HookDetached", + "discriminator": [ + 44, + 210, + 252, + 233, + 231, + 88, + 168, + 1 + ] + }, + { + "name": "HookWhitelistUpdated", + "discriminator": [ + 112, + 220, + 175, + 129, + 195, + 163, + 32, + 72 + ] + }, + { + "name": "JobCompleted", + "discriminator": [ + 176, + 207, + 246, + 115, + 95, + 179, + 9, + 132 + ] + }, + { + "name": "JobCreated", + "discriminator": [ + 48, + 110, + 162, + 177, + 67, + 74, + 159, + 131 + ] + }, + { + "name": "JobExpired", + "discriminator": [ + 213, + 123, + 115, + 173, + 157, + 242, + 12, + 71 + ] + }, + { + "name": "JobFunded", + "discriminator": [ + 109, + 177, + 206, + 113, + 255, + 142, + 11, + 19 + ] + }, + { + "name": "JobRejected", + "discriminator": [ + 23, + 78, + 227, + 57, + 28, + 0, + 197, + 216 + ] + }, + { + "name": "JobSubmitted", + "discriminator": [ + 129, + 204, + 35, + 127, + 38, + 50, + 131, + 248 + ] + }, + { + "name": "PauseSet", + "discriminator": [ + 175, + 57, + 198, + 136, + 192, + 66, + 204, + 73 + ] + }, + { + "name": "PaymentReleased", + "discriminator": [ + 160, + 132, + 155, + 232, + 46, + 254, + 69, + 219 + ] + }, + { + "name": "PaymentTokenUpdated", + "discriminator": [ + 215, + 110, + 229, + 224, + 123, + 151, + 223, + 230 + ] + }, + { + "name": "PlatformFeeUpdated", + "discriminator": [ + 210, + 134, + 201, + 4, + 92, + 228, + 80, + 26 + ] + }, + { + "name": "ProviderSet", + "discriminator": [ + 10, + 148, + 30, + 104, + 250, + 235, + 161, + 159 + ] + }, + { + "name": "Refunded", + "discriminator": [ + 35, + 103, + 149, + 246, + 196, + 123, + 221, + 99 + ] + }, + { + "name": "SponsorUpdated", + "discriminator": [ + 130, + 204, + 192, + 236, + 236, + 138, + 252, + 118 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "Unauthorized", + "msg": "Unauthorized" + }, + { + "code": 6001, + "name": "OnlyClientOrProvider", + "msg": "Only client or provider" + }, + { + "code": 6002, + "name": "OnlyEvaluator", + "msg": "Only evaluator" + }, + { + "code": 6003, + "name": "OnlyProvider", + "msg": "Only provider" + }, + { + "code": 6004, + "name": "OnlyClient", + "msg": "Only client" + }, + { + "code": 6005, + "name": "ZeroAddress", + "msg": "ZeroAddress" + }, + { + "code": 6006, + "name": "ZeroAddressEvaluator", + "msg": "ZeroAddressEvaluator" + }, + { + "code": 6007, + "name": "ZeroAddressProvider", + "msg": "ZeroAddressProvider" + }, + { + "code": 6008, + "name": "DescriptionTooLong", + "msg": "Description too long" + }, + { + "code": 6009, + "name": "ProviderCannotBeClient", + "msg": "Provider cannot be the client" + }, + { + "code": 6010, + "name": "EvaluatorCannotBeProvider", + "msg": "Evaluator cannot be the provider" + }, + { + "code": 6011, + "name": "EvaluatorCannotBeClient", + "msg": "Evaluator cannot be the client" + }, + { + "code": 6012, + "name": "InvalidHookProgram", + "msg": "Invalid hook program" + }, + { + "code": 6013, + "name": "HookNotWhitelisted", + "msg": "Hook not whitelisted" + }, + { + "code": 6014, + "name": "InvalidJob", + "msg": "Invalid job" + }, + { + "code": 6015, + "name": "WrongStatus", + "msg": "Wrong job status" + }, + { + "code": 6016, + "name": "AmountMustBeGreaterThanZero", + "msg": "Amount must be greater than zero" + }, + { + "code": 6017, + "name": "InvalidPaymentToken", + "msg": "Invalid payment token" + }, + { + "code": 6018, + "name": "BudgetNotSet", + "msg": "Budget not set" + }, + { + "code": 6019, + "name": "BudgetMismatch", + "msg": "Budget mismatch" + }, + { + "code": 6020, + "name": "ProviderNotSet", + "msg": "Provider not set" + }, + { + "code": 6021, + "name": "ZeroBudgetVaultNotAllowed", + "msg": "Vault not allowed on a zero-budget job" + }, + { + "code": 6022, + "name": "ExpiryTooShort", + "msg": "Expiry too short" + }, + { + "code": 6023, + "name": "ExpiryTooLong", + "msg": "Expiry too long" + }, + { + "code": 6024, + "name": "ExpiredAtMustBeInTheFuture", + "msg": "ExpiredAt must be in the future" + }, + { + "code": 6025, + "name": "PlatformFeeTooHigh", + "msg": "Platform fee too high" + }, + { + "code": 6026, + "name": "FeesTooHigh", + "msg": "Combined fees too high" + }, + { + "code": 6027, + "name": "SponsorCannotBeTreasury", + "msg": "Sponsor and platform treasury must be different addresses" + }, + { + "code": 6028, + "name": "InvalidDestination", + "msg": "Invalid destination account" + }, + { + "code": 6029, + "name": "InvalidVaultAuthority", + "msg": "Invalid vault authority" + }, + { + "code": 6030, + "name": "InvalidVaultOwner", + "msg": "Invalid vault owner" + }, + { + "code": 6031, + "name": "InvalidVaultMint", + "msg": "Invalid vault mint" + }, + { + "code": 6032, + "name": "BudgetMintNotSet", + "msg": "Budget mint must be set when budget amount is nonzero" + }, + { + "code": 6033, + "name": "InsufficientVaultBalance", + "msg": "Insufficient vault balance" + }, + { + "code": 6034, + "name": "MissingRequiredAccount", + "msg": "Missing required account for funded job" + }, + { + "code": 6035, + "name": "ArithmeticOverflow", + "msg": "Arithmetic overflow" + }, + { + "code": 6036, + "name": "UntrustedHookUpgradeAuthority", + "msg": "Hook program upgrade authority must be None or the ACP authority" + }, + { + "code": 6037, + "name": "MissingHookProgram", + "msg": "Missing hook program account: job declares a hook but hook_program was not supplied" + }, + { + "code": 6038, + "name": "InvalidAutoCompleteParams", + "msg": "Invalid complete_opt_params: must be empty (legacy mode) or [u16 submit_count][...] with submit_count <= remaining_accounts.len()" + }, + { + "code": 6039, + "name": "ProtocolPaused", + "msg": "Protocol is paused" } ], - "metadata": { - "description": "Created with Anchor", - "name": "agentic_commerce_v3", - "spec": "0.1.0", - "version": "0.1.0" - }, "types": [ { + "name": "AcpState", "docs": [ "Global program configuration. Singleton PDA seeded by `[\"acp_state\"]`." ], - "name": "AcpState", "type": { + "kind": "struct", "fields": [ { + "name": "authority", "docs": [ "Current program authority; can create/remove hook whitelists and update fees." ], - "name": "authority", "type": "pubkey" }, { + "name": "pending_authority", "docs": [ "Nominated successor (two-step transfer). `None` until `nominate_authority` is called." ], - "name": "pending_authority", "type": { "option": "pubkey" } }, { + "name": "payment_token", "docs": [ "SPL mint accepted for job budgets." ], - "name": "payment_token", "type": "pubkey" }, { + "name": "platform_treasury", "docs": [ - "Wallet that receives platform fees and reclaimed vault rent." + "Wallet that receives platform fees. Rent goes to `sponsor` instead." ], - "name": "platform_treasury", "type": "pubkey" }, { + "name": "platform_fee_bp", "docs": [ "Platform fee in basis points (0-10000). Applied to vault balance on completion." ], - "name": "platform_fee_bp", "type": "u64" }, { - "docs": [ - "Evaluator fee in basis points (0-10000). Applied on evaluator-confirmed completions." - ], "name": "evaluator_fee_bp", - "type": "u64" - }, - { "docs": [ - "Monotonically increasing counter storing the LAST issued job ID", - "The next job receives `job_counter + 1`; 0 means no jobs issued yet." + "Evaluator fee in basis points (0-10000). Applied on evaluator-confirmed completions." ], - "name": "job_counter", "type": "u64" }, { + "name": "bump", "docs": [ "PDA bump seed." ], - "name": "bump", "type": "u8" }, { + "name": "paused", "docs": [ "When true, all user-facing lifecycle instructions revert. Admin-toggled.", "Appended after `bump` so the existing on-chain layout only grows by one byte." ], - "name": "paused", "type": "bool" + }, + { + "name": "sponsor", + "docs": [ + "Receives reclaimed native rent from every account close. Separate from", + "`platform_treasury`, which receives SPL fees: under gas sponsorship the", + "rent was never the user's money nor protocol revenue, it is the sponsor's", + "float coming back. Appended after `paused` so the layout only grows at", + "the tail; `migrate_state` backfills it from an explicit argument when", + "zero, since it must differ from `platform_treasury` and so cannot be", + "inferred from existing state." + ], + "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "AuthorityNominated", "docs": [ "Emitted when a new authority is nominated (first step of two-step transfer)." ], - "name": "AuthorityNominated", "type": { + "kind": "struct", "fields": [ { "name": "current_authority", @@ -2112,16 +2489,16 @@ "name": "pending_authority", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "AuthorityTransferred", "docs": [ "Emitted when a pending authority accepts ownership (second step of two-step transfer)." ], - "name": "AuthorityTransferred", "type": { + "kind": "struct", "fields": [ { "name": "old_authority", @@ -2131,39 +2508,39 @@ "name": "new_authority", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "BudgetSet", "docs": [ "Emitted when the budget is set on a job via `set_budget`." ], - "name": "BudgetSet", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "amount", "type": "u64" } - ], - "kind": "struct" + ] } }, { + "name": "EvaluatorFeePaid", "docs": [ "Emitted when the evaluator fee is transferred on evaluator-confirmed completion." ], - "name": "EvaluatorFeePaid", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "evaluator", @@ -2173,80 +2550,80 @@ "name": "amount", "type": "u64" } - ], - "kind": "struct" + ] } }, { + "name": "EvaluatorFeeUpdated", "docs": [ "Emitted when the evaluator fee rate is updated." ], - "name": "EvaluatorFeeUpdated", "type": { + "kind": "struct", "fields": [ { "name": "fee_bp", "type": "u64" } - ], - "kind": "struct" + ] } }, { + "name": "HookDetached", "docs": [ "Emitted when an admin detaches a hook from an in-flight job via `detach_hook`." ], - "name": "HookDetached", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { + "name": "hook_address", "docs": [ "The hook address that was detached, captured before it is cleared to None." ], - "name": "hook_address", "type": { "option": "pubkey" } } - ], - "kind": "struct" + ] } }, { + "name": "HookWhitelist", "docs": [ "Whitelist entry for an approved hook program. PDA seeded by `[\"hook_whitelist\", hook]`." ], - "name": "HookWhitelist", "type": { + "kind": "struct", "fields": [ { + "name": "hook", "docs": [ "The whitelisted hook program ID." ], - "name": "hook", "type": "pubkey" }, { + "name": "bump", "docs": [ "PDA bump seed." ], - "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "HookWhitelistUpdated", "docs": [ "Emitted when a hook program is added to or removed from the whitelist." ], - "name": "HookWhitelistUpdated", "type": { + "kind": "struct", "fields": [ { "name": "hook", @@ -2256,50 +2633,71 @@ "name": "status", "type": "bool" } - ], - "kind": "struct" + ] } }, { + "name": "Job", "docs": [ - "A single job. PDA seeded by `[\"job\", client, job_id_le_bytes]`." + "A single job. PDA seeded by `[\"job\", client, job_id_le_bytes]`.", + "", + "Every field here is read on-chain. `description`, `deliverable`,", + "`completion_reason` and `created_at` used to be stored and are now", + "event-only: nothing on-chain ever read them, and between them they were 365", + "of the account's 529 bytes. `description` alone was a 260-byte ceiling", + "allocated in full whatever the actual string. They are carried by", + "`JobCreated.description`, `JobSubmitted.deliverable`, `JobCompleted.reason`", + "and `JobRejected.reason`, plus the transaction's own block time.", + "", + "The trade-off is deliberate and worth stating plainly: Solana `emit!` writes", + "to transaction logs, which are retrievable only for as long as an RPC keeps", + "history. Anything needing a durable audit trail of these values must index", + "the events off-chain." ], - "name": "Job", "type": { + "kind": "struct", "fields": [ { + "name": "seed", "docs": [ - "Unique identifier: `AcpState.job_counter` after its increment at creation." + "Client-supplied uniquifier, kept only so the PDA can be re-derived", + "(`seeds = [b\"job\", client, seed]`). It is NOT the job's identity and", + "carries no ordering: two clients may pick the same value, and `init`", + "rejects a reuse by the same client because the address already exists.", + "", + "**The job's identity is its own address**, `job.key()`. That is what the", + "hooks seed their per-job PDAs from and what the CPI payload carries.", + "Deriving the address from a shared counter instead made every creation", + "contend on one account, capping the protocol at one job per slot." ], - "name": "job_id", "type": "u64" }, { + "name": "client", "docs": [ "The party that created and funds the job." ], - "name": "client", "type": "pubkey" }, { + "name": "provider", "docs": [ "The agent fulfilling the job. `Pubkey::default()` until set via `set_provider`." ], - "name": "provider", "type": "pubkey" }, { + "name": "evaluator", "docs": [ "The adjudicator who completes or rejects. `Pubkey::default()` enables auto-complete on submit." ], - "name": "evaluator", "type": "pubkey" }, { + "name": "state", "docs": [ "Current lifecycle state (see `JobState`)." ], - "name": "state", "type": { "defined": { "name": "JobState" @@ -2307,103 +2705,56 @@ } }, { + "name": "budget_amount", "docs": [ "Token amount escrowed on fund. Zero for zero-budget jobs." ], - "name": "budget_amount", "type": "u64" }, { - "docs": [ - "SPL mint for the budget. `None` until `set_budget` is called." - ], - "name": "budget_mint", - "type": { - "option": "pubkey" - } - }, - { - "docs": [ - "Human-readable description (max 256 bytes)." - ], - "name": "description", - "type": "string" - }, - { - "docs": [ - "Unix timestamp after which the job may be claimed as expired." - ], "name": "expired_at", - "type": "i64" - }, - { "docs": [ - "Unix timestamp of job creation." + "Unix timestamp after which the job may be claimed as expired." ], - "name": "created_at", "type": "i64" }, { + "name": "hook_address", "docs": [ "Optional hook program invoked on before/after action callbacks." ], - "name": "hook_address", "type": { "option": "pubkey" } }, { - "docs": [ - "32-byte hash/identifier of the work product, set on submit." - ], - "name": "deliverable", - "type": { - "array": [ - "u8", - 32 - ] - } - }, - { - "docs": [ - "32-byte completion or rejection reason, set on complete/reject." - ], - "name": "completion_reason", - "type": { - "array": [ - "u8", - 32 - ] - } - }, - { + "name": "vault_bump", "docs": [ "PDA bump for the per-job vault authority. Set during fund." ], - "name": "vault_bump", "type": "u8" }, { + "name": "bump", "docs": [ "PDA bump seed for this job account." ], - "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "JobCompleted", "docs": [ "Emitted when a job is completed (evaluator approval or auto-complete)." ], - "name": "JobCompleted", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "evaluator", @@ -2418,20 +2769,25 @@ ] } } - ], - "kind": "struct" + ] } }, { + "name": "JobCreated", "docs": [ - "Emitted when a new job is created via `create_job`." + "Emitted when a new job is created via `create_job`.", + "", + "`description` is carried here because it is no longer stored on the job.", + "Nothing on-chain reads it, and as a 260-byte ceiling allocated in full it was", + "49% of the account; this is now its only record, so an indexer that wants it", + "must capture it from this event." ], - "name": "JobCreated", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "client", @@ -2452,36 +2808,40 @@ { "name": "hook", "type": "pubkey" + }, + { + "name": "description", + "type": "string" } - ], - "kind": "struct" + ] } }, { + "name": "JobExpired", "docs": [ "Emitted when an expired job is claimed via `claim_refund`." ], - "name": "JobExpired", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "JobFunded", "docs": [ "Emitted when a client funds a job, escrowing tokens in the vault." ], - "name": "JobFunded", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "client", @@ -2491,20 +2851,20 @@ "name": "amount", "type": "u64" } - ], - "kind": "struct" + ] } }, { + "name": "JobRejected", "docs": [ "Emitted when a job is rejected by an evaluator, client, or provider." ], - "name": "JobRejected", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "rejector", @@ -2519,15 +2879,14 @@ ] } } - ], - "kind": "struct" + ] } }, { + "name": "JobState", "docs": [ "Job lifecycle states" ], - "name": "JobState", "type": { "kind": "enum", "variants": [ @@ -2553,15 +2912,16 @@ } }, { + "name": "JobSubmitted", "docs": [ "Emitted when a provider submits a deliverable." ], - "name": "JobSubmitted", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "provider", @@ -2576,35 +2936,35 @@ ] } } - ], - "kind": "struct" + ] } }, { + "name": "PauseSet", "docs": [ "Emitted when the protocol pause flag is toggled via `set_pause`." ], - "name": "PauseSet", "type": { + "kind": "struct", "fields": [ { "name": "paused", "type": "bool" } - ], - "kind": "struct" + ] } }, { + "name": "PaymentReleased", "docs": [ "Emitted when the net payment is transferred to the provider on completion." ], - "name": "PaymentReleased", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "provider", @@ -2614,16 +2974,37 @@ "name": "amount", "type": "u64" } - ], - "kind": "struct" + ] } }, { + "name": "PaymentTokenUpdated", "docs": [ - "Emitted when the platform fee rate or treasury address is updated." + "Emitted when the mint accepted for job budgets is rotated. Carries the", + "previous value so an indexer can tell which jobs were funded under which", + "allowlist without replaying every prior update." ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "payment_token", + "type": "pubkey" + }, + { + "name": "previous", + "type": "pubkey" + } + ] + } + }, + { "name": "PlatformFeeUpdated", + "docs": [ + "Emitted when the platform fee rate or treasury address is updated." + ], "type": { + "kind": "struct", "fields": [ { "name": "fee_bp", @@ -2633,39 +3014,39 @@ "name": "treasury", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "ProviderSet", "docs": [ "Emitted when a provider is assigned to a job via `set_provider`." ], - "name": "ProviderSet", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "provider", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "Refunded", "docs": [ "Emitted when escrowed tokens are returned to the client (rejection or expiry)." ], - "name": "Refunded", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job", + "type": "pubkey" }, { "name": "client", @@ -2675,9 +3056,23 @@ "name": "amount", "type": "u64" } - ], - "kind": "struct" + ] + } + }, + { + "name": "SponsorUpdated", + "docs": [ + "Emitted when the rent sponsor is updated via `set_sponsor`." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "sponsor", + "type": "pubkey" + } + ] } } ] -} +} \ No newline at end of file diff --git a/src/core/solana/idl/fund_transfer_hook.json b/src/core/solana/idl/fund_transfer_hook.json index ca59bf5..b1c293a 100644 --- a/src/core/solana/idl/fund_transfer_hook.json +++ b/src/core/solana/idl/fund_transfer_hook.json @@ -1,156 +1,30 @@ { - "accounts": [ - { - "discriminator": [ - 115, - 10, - 133, - 59, - 77, - 84, - 106, - 104 - ], - "name": "FundRequestIntentId" - }, - { - "discriminator": [ - 119, - 227, - 48, - 236, - 101, - 194, - 218, - 85 - ], - "name": "HookMetadata" - }, + "metadata": { + "name": "fund_transfer_hook", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Fund Transfer Hook - fund requests and provider escrow for ACP" + }, + "instructions": [ { - "discriminator": [ - 10, - 13, - 71, - 116, - 92, - 105, - 237, - 231 + "name": "after_action", + "docs": [ + "Post-action hook callback." ], - "name": "HookState" - }, - { "discriminator": [ + 100, 247, - 162, - 35, - 165, - 254, - 111, - 129, - 109 - ], - "name": "Intent" - }, - { - "discriminator": [ - 132, - 9, - 212, - 245, - 34, - 170, - 187, - 104 - ], - "name": "ProviderEscrowIntentId" - } - ], - "events": [ - { - "discriminator": [ - 127, - 229, - 67, - 202, - 91, - 56, - 164, - 0 - ], - "name": "IntentClosed" - }, - { - "discriminator": [ - 162, - 221, - 142, - 204, - 198, - 234, - 97, - 188 - ], - "name": "IntentSigned" - }, - { - "discriminator": [ - 101, - 118, - 73, - 187, - 15, - 74, - 57, - 97 - ], - "name": "NewIntent" - }, - { - "discriminator": [ - 71, - 24, - 78, - 26, - 156, - 102, - 16, - 91 - ], - "name": "PayableFundsEscrowed" - }, - { - "discriminator": [ - 128, - 69, - 197, - 251, - 154, - 144, - 198, - 57 - ], - "name": "PayableFundsRefunded" - }, - { - "discriminator": [ - 164, - 216, - 121, - 127, - 187, - 55, - 131, - 55 + 61, + 195, + 161, + 140, + 250, + 73 ], - "name": "PayableTransferExecuted" - } - ], - "instructions": [ - { "accounts": [ { "name": "hook_state", + "writable": true, "pda": { "seeds": [ { @@ -169,14 +43,13 @@ ] } ] - }, - "writable": true + } } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -194,23 +67,23 @@ "name": "variant", "type": "bytes" } + ] + }, + { + "name": "before_action", + "docs": [ + "Pre-action hook callback." ], "discriminator": [ - 100, - 247, + 5, 61, - 195, - 161, - 140, - 250, - 73 - ], - "docs": [ - "Post-action hook callback." + 71, + 243, + 197, + 108, + 160, + 137 ], - "name": "after_action" - }, - { "accounts": [ { "name": "hook_state", @@ -237,8 +110,8 @@ ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -256,23 +129,23 @@ "name": "variant", "type": "bytes" } + ] + }, + { + "name": "claim_escrow_refund", + "docs": [ + "Refunds escrowed provider tokens after the core job expires. Permissionless." ], "discriminator": [ - 5, - 61, - 71, - 243, - 197, - 108, - 160, - 137 - ], - "docs": [ - "Pre-action hook callback." + 180, + 244, + 255, + 206, + 147, + 149, + 56, + 223 ], - "name": "before_action" - }, - { "accounts": [ { "name": "caller", @@ -339,31 +212,31 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] } }, { + "name": "intent", "docs": [ "The job-scoped escrow intent. The map is not part of the address", "derivation, so the id equality is asserted explicitly." ], - "name": "intent", "writable": true }, { + "name": "escrow_vault", "docs": [ "Escrow vault holding the escrowed tokens" ], - "name": "escrow_vault", "writable": true }, { + "name": "provider_token_account", "docs": [ "Provider's token account to receive refund" ], - "name": "provider_token_account", "writable": true }, { @@ -393,113 +266,105 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] } }, { - "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "name": "token_program" + "name": "token_program", + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" }, { + "name": "acp_state", "docs": [ "against `hook_state.acp_program`. Read only to source the treasury." - ], - "name": "acp_state" + ] }, { + "name": "sponsor", "docs": [ - "equal `acp_state.platform_treasury`." + "equal `acp_state.sponsor`." ], - "name": "platform_treasury", "writable": true } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" } + ] + }, + { + "name": "close_job_hook_accounts", + "docs": [ + "Reclaim a terminal job's intents and intent-id maps to", + "`acp_state.sponsor`. Permissionless — the caller receives nothing — and", + "idempotent per account, so it can be swept blindly over terminal jobs.", + "Blocks while the escrow is unsettled, so it cannot strand a provider's", + "stake by closing the accounts `claim_escrow_refund` needs." ], "discriminator": [ - 180, - 244, - 255, - 206, - 147, - 149, - 56, - 223 - ], - "docs": [ - "Refunds escrowed provider tokens after the core job expires. Permissionless." + 77, + 224, + 55, + 159, + 116, + 59, + 13, + 125 ], - "name": "claim_escrow_refund" - }, - { "accounts": [ { + "name": "caller", "docs": [ - "The original actor who proposed the intent (provider for fund requests).", - "Still required to authorize the close even though the rent now goes to", - "the treasury: the intent PDA is reused in place by the next proposal, so", - "letting a stranger deallocate it would force the sponsor to pay its rent", - "again for no gain to anyone." + "Permissionless. Rent goes to `acp_state.sponsor`, so the caller gains", + "nothing and there is no race incentive. Doubles as a sweep for jobs whose", + "terminal-transaction bundle failed to land." ], - "name": "actor", - "signer": true, - "writable": true + "signer": true }, { + "name": "job", "docs": [ - "The intent PDA to close; must be unsigned and non-escrow. The address", - "does not encode the intent id, so `intent.id == intent_id` is asserted explicitly." - ], - "name": "intent", - "writable": true + "`deserialize_job` against `hook_state.acp_program`." + ] }, { + "name": "fund_request_intent", "docs": [ - "Per-job fund request intent map; prevents closing the currently active intent." + "All four are optional and individually skipped when already empty: a", + "zero-budget job may have no escrow leg, and a job that never had a fund", + "request has no kind-0 intent. Each is verified against its canonical", + "seeds before anything is drained." ], + "writable": true, + "optional": true + }, + { + "name": "escrow_intent", + "writable": true, + "optional": true + }, + { "name": "fund_request_map", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 102, - 117, - 110, - 100, - 95, - 114, - 101, - 113, - 117, - 101, - 115, - 116, - 95, - 105, - 110, - 116, - 101, - 110, - 116, - 95, - 105, - 100 - ] - }, - { - "account": "Intent", - "kind": "account", - "path": "intent.job_id" - } - ] - } + "docs": [ + "`Account`: a typed account is deserialized at entry,", + "so a second call on an already-closed map would fail", + "`AccountNotInitialized` and break the blind-sweep idempotence the bundled", + "close depends on. The cost is that `FundRequestIntentId` no longer", + "appears in the IDL — `close_intent` was its last typed reference — so", + "off-chain readers must decode it manually. Its layout is", + "`job_key: Pubkey, intent_id: u64, bump: u8` after the 8-byte discriminator." + ], + "writable": true, + "optional": true + }, + { + "name": "escrow_map", + "writable": true, + "optional": true }, { "name": "hook_state", @@ -524,53 +389,44 @@ } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" - }, - { - "docs": [ - "against `hook_state.acp_program`. Read only to source the treasury." - ], "name": "acp_state" }, { - "docs": [ - "`acp_state.platform_treasury`." - ], - "name": "platform_treasury", + "name": "sponsor", "writable": true } ], "args": [ { - "name": "intent_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" } + ] + }, + { + "name": "initialize", + "docs": [ + "Initializes the fund-transfer-hook state. Caller must be the program's upgrade authority." ], "discriminator": [ - 112, - 245, - 154, - 249, - 57, - 126, - 54, - 122 - ], - "docs": [ - "Closes an orphaned unsigned non-escrow intent, reclaiming rent to the actor." + 175, + 175, + 109, + 31, + 13, + 152, + 155, + 237 ], - "name": "close_intent" - }, - { "accounts": [ { "name": "authority", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "hook_state", + "writable": true, "pda": { "seeds": [ { @@ -589,14 +445,14 @@ ] } ] - }, - "writable": true + } }, { + "name": "hook_metadata", "docs": [ "Self-declared metadata read by multi-hook-router for completeness checks." ], - "name": "hook_metadata", + "writable": true, "pda": { "seeds": [ { @@ -618,49 +474,11 @@ ] } ] - }, - "writable": true + } }, { "name": "program_data", "pda": { - "program": { - "kind": "const", - "value": [ - 2, - 168, - 246, - 145, - 78, - 136, - 161, - 176, - 226, - 16, - 21, - 62, - 247, - 99, - 174, - 43, - 0, - 194, - 185, - 61, - 22, - 193, - 36, - 210, - 192, - 83, - 122, - 16, - 4, - 128, - 0, - 0 - ] - }, "seeds": [ { "kind": "const", @@ -699,12 +517,49 @@ 51 ] } - ] + ], + "program": { + "kind": "const", + "value": [ + 2, + 168, + 246, + 145, + 78, + 136, + 161, + 176, + 226, + 16, + 21, + 62, + 247, + 99, + 174, + 43, + 0, + 194, + 185, + 61, + 22, + 193, + 36, + 210, + 192, + 83, + 122, + 16, + 4, + 128, + 0, + 0 + ] + } } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -712,32 +567,34 @@ "name": "acp_program", "type": "pubkey" } + ] + }, + { + "name": "pre_create_intent", + "docs": [ + "Pre-creates the intent + intent-id map PDAs for a job, zeroed, rent", + "paid by the job's provider at a CPI depth the paymaster can prefund.", + "Idempotent: no-op when the accounts already exist." ], "discriminator": [ - 175, - 175, - 109, - 31, - 13, - 152, - 155, - 237 - ], - "docs": [ - "Initializes the fund-transfer-hook state. Caller must be the program's upgrade authority." + 157, + 244, + 37, + 181, + 86, + 92, + 73, + 162 ], - "name": "initialize" - }, - { "accounts": [ { + "name": "payer", "docs": [ "Rent payer; must be the job's provider (the wallet the lifecycle", "handlers debit, and the wallet the paymaster prefunds)." ], - "name": "payer", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "hook_state", @@ -762,95 +619,197 @@ } }, { + "name": "job", "docs": [ "via `deserialize_job` against `hook_state.acp_program`." - ], - "name": "job" + ] }, { "name": "intent", "writable": true }, { + "name": "intent_map", "docs": [ "(seed prefix depends on `kind`)." ], - "name": "intent_map", "writable": true }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "kind", "type": "u8" } - ], + ] + } + ], + "accounts": [ + { + "name": "HookMetadata", "discriminator": [ - 157, - 244, - 37, - 181, - 86, + 119, + 227, + 48, + 236, + 101, + 194, + 218, + 85 + ] + }, + { + "name": "HookState", + "discriminator": [ + 10, + 13, + 71, + 116, 92, - 73, - 162 - ], - "docs": [ - "Pre-creates the intent + intent-id map PDAs for a job, zeroed, rent", - "paid by the job's provider at a CPI depth the paymaster can prefund.", - "Idempotent: no-op when the accounts already exist." - ], - "name": "pre_create_intent" + 105, + 237, + 231 + ] + }, + { + "name": "Intent", + "discriminator": [ + 247, + 162, + 35, + 165, + 254, + 111, + 129, + 109 + ] + }, + { + "name": "ProviderEscrowIntentId", + "discriminator": [ + 132, + 9, + 212, + 245, + 34, + 170, + 187, + 104 + ] } ], - "metadata": { - "description": "Fund Transfer Hook - fund requests and provider escrow for ACP", - "name": "fund_transfer_hook", - "spec": "0.1.0", - "version": "0.1.0" - }, - "types": [ + "events": [ { - "docs": [ - "Maps a job to a fund request intent ID.", - "Seeds: [\"fund_request_intent_id\", job_id]" - ], - "name": "FundRequestIntentId", - "type": { - "fields": [ - { - "name": "job_id", - "type": "u64" - }, - { - "name": "intent_id", - "type": "u64" - }, - { - "name": "bump", - "type": "u8" - } - ], - "kind": "struct" - } + "name": "IntentClosed", + "discriminator": [ + 127, + 229, + 67, + 202, + 91, + 56, + 164, + 0 + ] + }, + { + "name": "IntentSigned", + "discriminator": [ + 162, + 221, + 142, + 204, + 198, + 234, + 97, + 188 + ] + }, + { + "name": "JobHookAccountsClosed", + "discriminator": [ + 180, + 89, + 65, + 138, + 208, + 224, + 61, + 244 + ] + }, + { + "name": "NewIntent", + "discriminator": [ + 101, + 118, + 73, + 187, + 15, + 74, + 57, + 97 + ] + }, + { + "name": "PayableFundsEscrowed", + "discriminator": [ + 71, + 24, + 78, + 26, + 156, + 102, + 16, + 91 + ] }, { + "name": "PayableFundsRefunded", + "discriminator": [ + 128, + 69, + 197, + 251, + 154, + 144, + 198, + 57 + ] + }, + { + "name": "PayableTransferExecuted", + "discriminator": [ + 164, + 216, + 121, + 127, + 187, + 55, + 131, + 55 + ] + } + ], + "types": [ + { + "name": "HookMetadata", "docs": [ "Hook self-declaration of which selectors this hook implements; read by", "multi-hook-router. Seeds: [\"hook_metadata\"].", "", "Bitmask layout: bit 0 = SetBudget, 1 = Fund, 2 = Submit, 3 = Complete, 4 = Reject." ], - "name": "HookMetadata", "type": { + "kind": "struct", "fields": [ { "name": "required_selectors", @@ -860,16 +819,16 @@ "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "HookState", "docs": [ "Global hook state — stores intent counter and config." ], - "name": "HookState", "type": { + "kind": "struct", "fields": [ { "name": "acp_program", @@ -883,25 +842,25 @@ "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "Intent", "docs": [ "An intent record (fund request or provider escrow).", - "Seeds: [\"intent\", job_id, kind]" + "Seeds: [\"intent\", job_key, kind]" ], - "name": "Intent", "type": { + "kind": "struct", "fields": [ { "name": "id", "type": "u64" }, { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "actor", @@ -936,19 +895,19 @@ "type": "u8" }, { + "name": "escrow_bump", "docs": [ "Cached escrow authority PDA bump; 0 for non-escrow intents." ], - "name": "escrow_bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { "name": "IntentClosed", "type": { + "kind": "struct", "fields": [ { "name": "intent_id", @@ -958,13 +917,13 @@ "name": "actor", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { "name": "IntentSigned", "type": { + "kind": "struct", "fields": [ { "name": "intent_id", @@ -978,17 +937,42 @@ "name": "is_approved", "type": "bool" } - ], - "kind": "struct" + ] } }, { - "name": "NewIntent", + "name": "JobHookAccountsClosed", + "docs": [ + "Emitted when a terminal job's fund-transfer-hook PDAs are reclaimed.", + "`rent_destination` is recorded so indexers can see where the lamports went,", + "following the precedent set by `ProposedTermsCleanedUp`." + ], "type": { + "kind": "struct", "fields": [ { - "name": "job_id", + "name": "job_key", + "type": "pubkey" + }, + { + "name": "rent_destination", + "type": "pubkey" + }, + { + "name": "lamports", "type": "u64" + } + ] + } + }, + { + "name": "NewIntent", + "type": { + "kind": "struct", + "fields": [ + { + "name": "job_key", + "type": "pubkey" }, { "name": "actor", @@ -998,17 +982,17 @@ "name": "intent_id", "type": "u64" } - ], - "kind": "struct" + ] } }, { "name": "PayableFundsEscrowed", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "intent_id", @@ -1026,17 +1010,17 @@ "name": "amount", "type": "u64" } - ], - "kind": "struct" + ] } }, { "name": "PayableFundsRefunded", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "intent_id", @@ -1054,17 +1038,17 @@ "name": "amount", "type": "u64" } - ], - "kind": "struct" + ] } }, { "name": "PayableTransferExecuted", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "intent_id", @@ -1086,21 +1070,21 @@ "name": "amount", "type": "u64" } - ], - "kind": "struct" + ] } }, { + "name": "ProviderEscrowIntentId", "docs": [ "Maps a job to a provider escrow intent ID.", - "Seeds: [\"provider_escrow_intent_id\", job_id]" + "Seeds: [\"provider_escrow_intent_id\", job_key]" ], - "name": "ProviderEscrowIntentId", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "intent_id", @@ -1110,9 +1094,8 @@ "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } } ] -} +} \ No newline at end of file diff --git a/src/core/solana/idl/multi_hook_router.json b/src/core/solana/idl/multi_hook_router.json index bf293d8..d69e47f 100644 --- a/src/core/solana/idl/multi_hook_router.json +++ b/src/core/solana/idl/multi_hook_router.json @@ -1,252 +1,34 @@ { - "accounts": [ - { - "discriminator": [ - 119, - 227, - 48, - 236, - 101, - 194, - 218, - 85 - ], - "name": "HookMetadata" - }, - { - "discriminator": [ - 184, - 143, - 187, - 182, - 118, - 184, - 3, - 236 - ], - "name": "HookRouter" - }, - { - "discriminator": [ - 141, - 157, - 194, - 155, - 75, - 208, - 200, - 27 - ], - "name": "RouterState" - } - ], - "errors": [ - { - "code": 6000, - "msg": "Only the ACP contract can call this", - "name": "OnlyACPContract" - }, - { - "code": 6001, - "msg": "Only the job client can configure hooks", - "name": "OnlyJobClient" - }, - { - "code": 6002, - "msg": "Hooks are locked after job leaves Open status", - "name": "HooksLocked" - }, - { - "code": 6003, - "msg": "Too many hooks for this job", - "name": "TooManyHooks" - }, - { - "code": 6004, - "msg": "Hook does not implement the expected interface", - "name": "InvalidHook" - }, - { - "code": 6005, - "msg": "Duplicate hook in list", - "name": "DuplicateHook" - }, - { - "code": 6006, - "msg": "Hook not found in current list", - "name": "HookNotFound" - }, - { - "code": 6007, - "msg": "Zero address not allowed", - "name": "ZeroAddress" - }, - { - "code": 6008, - "msg": "Provided array is empty", - "name": "EmptyArray" - }, - { - "code": 6009, - "msg": "Reorder array does not match current hook set", - "name": "HookSetMismatch" - }, - { - "code": 6010, - "msg": "Sub-hook is not whitelisted on the ACP core", - "name": "SubHookNotWhitelisted" - }, - { - "code": 6011, - "msg": "Invalid job account", - "name": "InvalidJob" - }, - { - "code": 6012, - "msg": "Hook count in opt_params does not match router config", - "name": "HookCountMismatch" - }, - { - "code": 6013, - "msg": "opt_params too short to parse header", - "name": "OptParamsTooShort" - }, - { - "code": 6014, - "msg": "Wrong hook program in remaining accounts", - "name": "WrongHookProgram" - }, - { - "code": 6015, - "msg": "Wrong hook whitelist account: not the canonical PDA for this hook", - "name": "WrongHookWhitelist" - }, - { - "code": 6016, - "msg": "Account count exceeds remaining accounts", - "name": "AccountSliceOutOfBounds" - }, - { - "code": 6017, - "msg": "Unauthorized caller", - "name": "UnauthorizedCaller" - }, - { - "code": 6018, - "msg": "Max hooks per job must be >= 1", - "name": "InvalidMaxHooks" - }, - { - "code": 6019, - "msg": "Action is not a hookable selector (only SetBudget, Fund, Submit, Complete, Reject)", - "name": "InvalidSelector" - }, - { - "code": 6020, - "msg": "Sub-hook is missing from a selector list it requires", - "name": "HookMissingRequiredSelector" - }, - { - "code": 6021, - "msg": "Sub-hook does not declare HookMetadata PDA", - "name": "InvalidHookMetadata" - }, - { - "code": 6022, - "msg": "Invalid opt_params mode byte: must be 0x00 (Broadcast) or 0x01 (PerHook)", - "name": "InvalidOptParamsMode" - }, - { - "code": 6023, - "msg": "Hook program account is not executable", - "name": "HookNotExecutable" - } - ], - "events": [ + "metadata": { + "name": "multi_hook_router", + "version": "0.1.0", + "spec": "0.1.0", + "description": "MultiHookRouter - routes hook callbacks to an ordered list of sub-hooks per job" + }, + "instructions": [ { - "discriminator": [ - 107, - 213, - 140, - 132, - 185, - 14, - 143, - 172 + "name": "add_hook", + "docs": [ + "Append a hook to the end of one selector's list." ], - "name": "DewhitelistedHookSkipped" - }, - { "discriminator": [ - 144, - 225, - 235, - 30, 112, - 225, - 97, - 215 - ], - "name": "HookAdded" - }, - { - "discriminator": [ - 81, - 39, + 125, + 164, 213, - 15, - 171, - 37, - 28, - 78 - ], - "name": "HookRemoved" - }, - { - "discriminator": [ - 142, - 144, - 70, - 85, - 7, - 178, - 81, - 215 - ], - "name": "HooksConfigured" - }, - { - "discriminator": [ - 194, - 8, - 11, - 39, - 28, - 196, - 51, - 161 - ], - "name": "HooksReordered" - }, - { - "discriminator": [ - 55, - 55, - 208, - 10, - 60, - 174, - 186, - 173 + 241, + 243, + 17, + 98 ], - "name": "MaxHooksPerJobUpdated" - } - ], - "instructions": [ - { "accounts": [ { "name": "client", + "docs": [ + "Writable and the rent payer: adding a hook grows the router account, and", + "the top-up needs a system-owned source." + ], + "writable": true, "signer": true }, { @@ -254,6 +36,7 @@ }, { "name": "hook_router", + "writable": true, "pda": { "seeds": [ { @@ -274,11 +57,10 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] - }, - "writable": true + } }, { "name": "router_state", @@ -309,12 +91,16 @@ }, { "name": "hook_metadata" + }, + { + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -324,29 +110,29 @@ "name": "hook", "type": "pubkey" } + ] + }, + { + "name": "after_action", + "docs": [ + "Post-action hook callback; fans out to each sub-hook configured for `action`." ], "discriminator": [ - 112, - 125, - 164, - 213, - 241, - 243, - 17, - 98 - ], - "docs": [ - "Append a hook to the end of one selector's list." + 100, + 247, + 61, + 195, + 161, + 140, + 250, + 73 ], - "name": "add_hook" - }, - { "accounts": [ { + "name": "hook_router", "docs": [ "See comment on BeforeAction.hook_router." ], - "name": "hook_router", "pda": { "seeds": [ { @@ -367,7 +153,7 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] } @@ -397,14 +183,14 @@ } }, { - "address": "Sysvar1nstructions1111111111111111111111111", - "name": "instructions_sysvar" + "name": "instructions_sysvar", + "address": "Sysvar1nstructions1111111111111111111111111" } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -422,34 +208,39 @@ "name": "variant", "type": "bytes" } + ] + }, + { + "name": "batch_configure_hooks", + "docs": [ + "Atomically replace all 5 per-selector hook lists for a job." ], "discriminator": [ - 100, + 152, + 156, + 231, + 0, 247, - 61, - 195, - 161, - 140, - 250, - 73 - ], - "docs": [ - "Post-action hook callback; fans out to each sub-hook configured for `action`." + 197, + 31, + 156 ], - "name": "after_action" - }, - { "accounts": [ { "name": "client", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "job" }, { "name": "hook_router", + "docs": [ + "`ConfigureHooks::hook_router` for why `init_if_needed` cannot be used on", + "an exact-fit account. The seeds below still pin the address." + ], + "writable": true, "pda": { "seeds": [ { @@ -470,11 +261,10 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] - }, - "writable": true + } }, { "name": "router_state", @@ -501,14 +291,14 @@ } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "set_budget", @@ -550,29 +340,29 @@ } } } + ] + }, + { + "name": "before_action", + "docs": [ + "Pre-action hook callback; fans out to each sub-hook configured for `action`." ], "discriminator": [ - 152, - 156, - 231, - 0, - 247, + 5, + 61, + 71, + 243, 197, - 31, - 156 - ], - "docs": [ - "Atomically replace all 5 per-selector hook lists for a job." + 108, + 160, + 137 ], - "name": "batch_configure_hooks" - }, - { "accounts": [ { + "name": "hook_router", "docs": [ "May not exist yet; handler treats a missing HookRouter PDA as a no-op." ], - "name": "hook_router", "pda": { "seeds": [ { @@ -593,7 +383,7 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] } @@ -623,14 +413,14 @@ } }, { - "address": "Sysvar1nstructions1111111111111111111111111", - "name": "instructions_sysvar" + "name": "instructions_sysvar", + "address": "Sysvar1nstructions1111111111111111111111111" } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -648,34 +438,147 @@ "name": "variant", "type": "bytes" } + ] + }, + { + "name": "close_hook_router", + "docs": [ + "Reclaim a terminal job's HookRouter rent to `acp_state.sponsor`.", + "Permissionless — the caller receives nothing. A no-op if already closed,", + "so it can be swept blindly over a list of terminal jobs." ], "discriminator": [ - 5, - 61, - 71, - 243, - 197, - 108, - 160, - 137 + 204, + 155, + 217, + 12, + 192, + 12, + 49, + 65 ], - "docs": [ - "Pre-action hook callback; fans out to each sub-hook configured for `action`." + "accounts": [ + { + "name": "caller", + "docs": [ + "Permissionless. The caller receives nothing — every lamport goes to", + "`acp_state.sponsor` — so there is no incentive to race and no", + "rent-farming surface. That also lets this double as a sweep for any", + "router whose terminal-transaction bundle failed to land." + ], + "signer": true + }, + { + "name": "job", + "docs": [ + "`deserialize_job` against `router_state.acp_program`." + ] + }, + { + "name": "hook_router", + "docs": [ + "so it cannot be `Account`. Seeds pin the address." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 104, + 111, + 111, + 107, + 95, + 114, + 111, + 117, + 116, + 101, + 114 + ] + }, + { + "kind": "arg", + "path": "job_key" + } + ] + } + }, + { + "name": "router_state", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 114, + 111, + 117, + 116, + 101, + 114, + 95, + 115, + 116, + 97, + 116, + 101 + ] + } + ] + } + }, + { + "name": "acp_state" + }, + { + "name": "sponsor", + "writable": true + } ], - "name": "before_action" + "args": [ + { + "name": "job_key", + "type": "pubkey" + } + ] }, { + "name": "configure_hooks", + "docs": [ + "Replace the hook list for ONE selector. Only callable by the job", + "client while the job is in Open status." + ], + "discriminator": [ + 171, + 90, + 59, + 227, + 56, + 135, + 114, + 228 + ], "accounts": [ { "name": "client", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "job" }, { "name": "hook_router", + "docs": [ + "`Account` with `init_if_needed`: the account is exact-fit,", + "so its length varies with its contents, and `init_if_needed`'s codegen", + "post-checks the length against a fixed `space =` expression. Every call", + "after the first would fail `ConstraintSpace`. The seeds below still pin", + "the address." + ], + "writable": true, "pda": { "seeds": [ { @@ -696,11 +599,10 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] - }, - "writable": true + } }, { "name": "router_state", @@ -727,14 +629,14 @@ } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -746,32 +648,32 @@ "vec": "pubkey" } } + ] + }, + { + "name": "initialize", + "docs": [ + "Initialize the router with ACP program reference and max hooks config." ], "discriminator": [ - 171, - 90, - 59, - 227, - 56, - 135, - 114, - 228 - ], - "docs": [ - "Replace the hook list for ONE selector. Only callable by the job", - "client while the job is in Open status." + 175, + 175, + 109, + 31, + 13, + 152, + 155, + 237 ], - "name": "configure_hooks" - }, - { "accounts": [ { "name": "authority", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "router_state", + "writable": true, "pda": { "seeds": [ { @@ -792,14 +694,14 @@ ] } ] - }, - "writable": true + } }, { + "name": "hook_metadata", "docs": [ "Self-declared hook metadata, read when this router is nested as a sub-hook." ], - "name": "hook_metadata", + "writable": true, "pda": { "seeds": [ { @@ -821,8 +723,7 @@ ] } ] - }, - "writable": true + } }, { "name": "acp_program" @@ -830,43 +731,6 @@ { "name": "program_data", "pda": { - "program": { - "kind": "const", - "value": [ - 2, - 168, - 246, - 145, - 78, - 136, - 161, - 176, - 226, - 16, - 21, - 62, - 247, - 99, - 174, - 43, - 0, - 194, - 185, - 61, - 22, - 193, - 36, - 210, - 192, - 83, - 122, - 16, - 4, - 128, - 0, - 0 - ] - }, "seeds": [ { "kind": "const", @@ -905,12 +769,49 @@ 230 ] } - ] + ], + "program": { + "kind": "const", + "value": [ + 2, + 168, + 246, + 145, + 78, + 136, + 161, + 176, + 226, + 16, + 21, + 62, + 247, + 99, + 174, + 43, + 0, + 194, + 185, + 61, + 22, + 193, + 36, + 210, + 192, + 83, + 122, + 16, + 4, + 128, + 0, + 0 + ] + } } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -918,23 +819,23 @@ "name": "max_hooks_per_job", "type": "u8" } + ] + }, + { + "name": "remove_hook", + "docs": [ + "Remove a hook from one selector's list (swap-and-pop)." ], "discriminator": [ - 175, - 175, - 109, - 31, - 13, - 152, - 155, - 237 - ], - "docs": [ - "Initialize the router with ACP program reference and max hooks config." + 190, + 124, + 75, + 130, + 220, + 82, + 21, + 208 ], - "name": "initialize" - }, - { "accounts": [ { "name": "client", @@ -945,6 +846,7 @@ }, { "name": "hook_router", + "writable": true, "pda": { "seeds": [ { @@ -965,11 +867,10 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] - }, - "writable": true + } }, { "name": "router_state", @@ -998,8 +899,8 @@ ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -1009,23 +910,23 @@ "name": "hook", "type": "pubkey" } + ] + }, + { + "name": "reorder_hooks", + "docs": [ + "Replace one selector's hook list with a reordered version (must be a permutation)." ], "discriminator": [ - 190, - 124, - 75, - 130, - 220, - 82, - 21, - 208 - ], - "docs": [ - "Remove a hook from one selector's list (swap-and-pop)." + 206, + 139, + 158, + 238, + 151, + 152, + 40, + 246 ], - "name": "remove_hook" - }, - { "accounts": [ { "name": "client", @@ -1036,6 +937,7 @@ }, { "name": "hook_router", + "writable": true, "pda": { "seeds": [ { @@ -1056,11 +958,10 @@ }, { "kind": "arg", - "path": "job_id" + "path": "job_key" } ] - }, - "writable": true + } }, { "name": "router_state", @@ -1089,8 +990,8 @@ ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -1102,95 +1003,352 @@ "vec": "pubkey" } } + ] + }, + { + "name": "set_max_hooks_per_job", + "docs": [ + "Update the maximum sub-hooks allowed per job per selector.", + "Callable only by the ACP core authority (the router has no admin of its own)." ], "discriminator": [ - 206, - 139, - 158, - 238, - 151, - 152, - 40, - 246 + 211, + 27, + 171, + 166, + 105, + 24, + 236, + 200 ], - "docs": [ - "Replace one selector's hook list with a reordered version (must be a permutation)." + "accounts": [ + { + "name": "authority", + "docs": [ + "Must be the ACP core authority (`AcpState.authority`), verified in the", + "handler against `acp_state`. The router has no admin of its own." + ], + "signer": true + }, + { + "name": "router_state", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 114, + 111, + 117, + 116, + 101, + 114, + 95, + 115, + 116, + 97, + 116, + 101 + ] + } + ] + } + }, + { + "name": "acp_state", + "docs": [ + "PDA address, owner, and stored `authority` are validated in the handler." + ] + } ], - "name": "reorder_hooks" + "args": [ + { + "name": "new_max", + "type": "u8" + } + ] + } + ], + "accounts": [ + { + "name": "HookMetadata", + "discriminator": [ + 119, + 227, + 48, + 236, + 101, + 194, + 218, + 85 + ] + }, + { + "name": "HookRouter", + "discriminator": [ + 184, + 143, + 187, + 182, + 118, + 184, + 3, + 236 + ] + }, + { + "name": "RouterState", + "discriminator": [ + 141, + 157, + 194, + 155, + 75, + 208, + 200, + 27 + ] + } + ], + "events": [ + { + "name": "DewhitelistedHookSkipped", + "discriminator": [ + 107, + 213, + 140, + 132, + 185, + 14, + 143, + 172 + ] }, { - "accounts": [ - { - "docs": [ - "Must be the ACP core authority (`AcpState.authority`), verified in the", - "handler against `acp_state`. The router has no admin of its own." - ], - "name": "authority", - "signer": true - }, - { - "name": "router_state", - "pda": { - "seeds": [ - { - "kind": "const", - "value": [ - 114, - 111, - 117, - 116, - 101, - 114, - 95, - 115, - 116, - 97, - 116, - 101 - ] - } - ] - }, - "writable": true - }, - { - "docs": [ - "PDA address, owner, and stored `authority` are validated in the handler." - ], - "name": "acp_state" - } - ], - "args": [ - { - "name": "new_max", - "type": "u8" - } - ], + "name": "HookAdded", "discriminator": [ - 211, - 27, + 144, + 225, + 235, + 30, + 112, + 225, + 97, + 215 + ] + }, + { + "name": "HookRemoved", + "discriminator": [ + 81, + 39, + 213, + 15, 171, - 166, - 105, - 24, - 236, - 200 - ], - "docs": [ - "Update the maximum sub-hooks allowed per job per selector.", - "Callable only by the ACP core authority (the router has no admin of its own)." - ], - "name": "set_max_hooks_per_job" + 37, + 28, + 78 + ] + }, + { + "name": "HookRouterClosed", + "discriminator": [ + 196, + 106, + 82, + 197, + 52, + 92, + 135, + 55 + ] + }, + { + "name": "HooksConfigured", + "discriminator": [ + 142, + 144, + 70, + 85, + 7, + 178, + 81, + 215 + ] + }, + { + "name": "HooksReordered", + "discriminator": [ + 194, + 8, + 11, + 39, + 28, + 196, + 51, + 161 + ] + }, + { + "name": "MaxHooksPerJobUpdated", + "discriminator": [ + 55, + 55, + 208, + 10, + 60, + 174, + 186, + 173 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "OnlyACPContract", + "msg": "Only the ACP contract can call this" + }, + { + "code": 6001, + "name": "OnlyJobClient", + "msg": "Only the job client can configure hooks" + }, + { + "code": 6002, + "name": "HooksLocked", + "msg": "Hooks are locked after job leaves Open status" + }, + { + "code": 6003, + "name": "TooManyHooks", + "msg": "Too many hooks for this job" + }, + { + "code": 6004, + "name": "InvalidHook", + "msg": "Hook does not implement the expected interface" + }, + { + "code": 6005, + "name": "DuplicateHook", + "msg": "Duplicate hook in list" + }, + { + "code": 6006, + "name": "HookNotFound", + "msg": "Hook not found in current list" + }, + { + "code": 6007, + "name": "ZeroAddress", + "msg": "Zero address not allowed" + }, + { + "code": 6008, + "name": "EmptyArray", + "msg": "Provided array is empty" + }, + { + "code": 6009, + "name": "HookSetMismatch", + "msg": "Reorder array does not match current hook set" + }, + { + "code": 6010, + "name": "SubHookNotWhitelisted", + "msg": "Sub-hook is not whitelisted on the ACP core" + }, + { + "code": 6011, + "name": "InvalidJob", + "msg": "Invalid job account" + }, + { + "code": 6012, + "name": "HookCountMismatch", + "msg": "Hook count in opt_params does not match router config" + }, + { + "code": 6013, + "name": "OptParamsTooShort", + "msg": "opt_params too short to parse header" + }, + { + "code": 6014, + "name": "WrongHookProgram", + "msg": "Wrong hook program in remaining accounts" + }, + { + "code": 6015, + "name": "WrongHookWhitelist", + "msg": "Wrong hook whitelist account: not the canonical PDA for this hook" + }, + { + "code": 6016, + "name": "AccountSliceOutOfBounds", + "msg": "Account count exceeds remaining accounts" + }, + { + "code": 6017, + "name": "UnauthorizedCaller", + "msg": "Unauthorized caller" + }, + { + "code": 6018, + "name": "InvalidMaxHooks", + "msg": "Max hooks per job must be >= 1" + }, + { + "code": 6019, + "name": "InvalidSelector", + "msg": "Action is not a hookable selector (only SetBudget, Fund, Submit, Complete, Reject)" + }, + { + "code": 6020, + "name": "HookMissingRequiredSelector", + "msg": "Sub-hook is missing from a selector list it requires" + }, + { + "code": 6021, + "name": "InvalidHookMetadata", + "msg": "Sub-hook does not declare HookMetadata PDA" + }, + { + "code": 6022, + "name": "InvalidOptParamsMode", + "msg": "Invalid opt_params mode byte: must be 0x00 (Broadcast) or 0x01 (PerHook)" + }, + { + "code": 6023, + "name": "HookNotExecutable", + "msg": "Hook program account is not executable" + }, + { + "code": 6024, + "name": "RentPayerMissing", + "msg": "Growing the router needs a rent payer and the system program" + }, + { + "code": 6025, + "name": "InvalidRefundRecipient", + "msg": "Rent destination is not acp_state.sponsor" + }, + { + "code": 6026, + "name": "WrongStatus", + "msg": "Job is not in a terminal state" + }, + { + "code": 6027, + "name": "TooManyCachedHooks", + "msg": "Cached hook metadata exceeds 5 x max_hooks_per_job entries" } ], - "metadata": { - "description": "MultiHookRouter - routes hook callbacks to an ordered list of sub-hooks per job", - "name": "multi_hook_router", - "spec": "0.1.0", - "version": "0.1.0" - }, "types": [ { + "name": "CachedHookMetadata", "docs": [ "Cached required-selectors bitmask for one configured sub-hook.", "", @@ -1200,8 +1358,8 @@ "If `HookMetadata` ever becomes mutable, `validate_completeness` must re-read", "it live instead of trusting this cache." ], - "name": "CachedHookMetadata", "type": { + "kind": "struct", "fields": [ { "name": "hook", @@ -1211,21 +1369,21 @@ "name": "required_selectors", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "DewhitelistedHookSkipped", "docs": [ "Emitted when a configured sub-hook is no longer whitelisted at fan-out", "time; the router skips it and continues." ], - "name": "DewhitelistedHookSkipped", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -1235,17 +1393,17 @@ "name": "hook", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { "name": "HookAdded", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -1259,17 +1417,17 @@ "name": "position", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "HookMetadata", "docs": [ "Hook self-declaration, read when this router is nested as a sub-hook.", "PDA seeded by `[b\"hook_metadata\"]`." ], - "name": "HookMetadata", "type": { + "kind": "struct", "fields": [ { "name": "required_selectors", @@ -1279,17 +1437,17 @@ "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { "name": "HookRemoved", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -1299,21 +1457,21 @@ "name": "hook", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "HookRouter", "docs": [ "Per-job hook configuration. Five per-selector ordered lists, one per", "hookable lifecycle action. PDA seeded by `[\"hook_router\", job_id_le_bytes]`." ], - "name": "HookRouter", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "set_budget_hooks", @@ -1346,10 +1504,10 @@ } }, { + "name": "hook_metadata_cache", "docs": [ "Cached metadata for unique configured sub-hooks." ], - "name": "hook_metadata_cache", "type": { "vec": { "defined": { @@ -1362,17 +1520,42 @@ "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { - "name": "HooksConfigured", + "name": "HookRouterClosed", + "docs": [ + "Emitted when a terminal job's `HookRouter` PDA is closed and its rent", + "reclaimed. `rent_destination` is recorded so indexers can see where the", + "lamports went, following the precedent set by `ProposedTermsCleanedUp`." + ], "type": { + "kind": "struct", "fields": [ { - "name": "job_id", + "name": "job_key", + "type": "pubkey" + }, + { + "name": "rent_destination", + "type": "pubkey" + }, + { + "name": "lamports", "type": "u64" + } + ] + } + }, + { + "name": "HooksConfigured", + "type": { + "kind": "struct", + "fields": [ + { + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -1384,17 +1567,17 @@ "vec": "pubkey" } } - ], - "kind": "struct" + ] } }, { "name": "HooksReordered", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -1406,13 +1589,13 @@ "vec": "pubkey" } } - ], - "kind": "struct" + ] } }, { "name": "MaxHooksPerJobUpdated", "type": { + "kind": "struct", "fields": [ { "name": "old_max", @@ -1422,36 +1605,35 @@ "name": "new_max", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "RouterState", "docs": [ "Global router configuration. Singleton PDA seeded by `[\"router_state\"]`." ], - "name": "RouterState", "type": { + "kind": "struct", "fields": [ { "name": "acp_program", "type": "pubkey" }, { + "name": "max_hooks_per_job", "docs": [ "Max sub-hooks per job, per selector. Tunable only by the ACP core", "authority (`AcpState.authority`); the router has no admin of its own." ], - "name": "max_hooks_per_job", "type": "u8" }, { "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } } ] -} +} \ No newline at end of file diff --git a/src/core/solana/idl/subscription_hook.json b/src/core/solana/idl/subscription_hook.json index 006ba0e..c12b6b5 100644 --- a/src/core/solana/idl/subscription_hook.json +++ b/src/core/solana/idl/subscription_hook.json @@ -1,88 +1,27 @@ { - "accounts": [ - { - "discriminator": [ - 119, - 227, - 48, - 236, - 101, - 194, - 218, - 85 - ], - "name": "HookMetadata" - }, - { - "discriminator": [ - 10, - 13, - 71, - 116, - 92, - 105, - 237, - 231 - ], - "name": "HookState" - } - ], - "events": [ - { - "discriminator": [ - 81, - 50, - 178, - 8, - 93, - 221, - 142, - 71 - ], - "name": "ProposedTermsCleanedUp" - }, + "metadata": { + "name": "subscription_hook", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Subscription Hook - subscription management for ACP" + }, + "instructions": [ { - "discriminator": [ - 66, - 23, - 25, - 128, - 66, - 95, - 220, - 100 + "name": "after_action", + "docs": [ + "Post-action hook callback; validates terms on Fund, activates the", + "subscription on Complete, and cleans up proposed terms on Reject." ], - "name": "SubscriptionActivated" - }, - { "discriminator": [ - 168, - 155, + 100, + 247, + 61, + 195, + 161, 140, - 139, - 180, - 239, - 130, - 153 - ], - "name": "SubscriptionTermsProposed" - }, - { - "discriminator": [ - 224, - 53, - 137, - 103, - 246, - 117, 250, - 1 + 73 ], - "name": "SubscriptionTermsSkipped" - } - ], - "instructions": [ - { "accounts": [ { "name": "hook_state", @@ -109,8 +48,8 @@ ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -128,24 +67,23 @@ "name": "variant", "type": "bytes" } + ] + }, + { + "name": "before_action", + "docs": [ + "Pre-action hook callback; proposes subscription terms on SetBudget." ], "discriminator": [ - 100, - 247, + 5, 61, - 195, - 161, - 140, - 250, - 73 - ], - "docs": [ - "Post-action hook callback; validates terms on Fund, activates the", - "subscription on Complete, and cleans up proposed terms on Reject." + 71, + 243, + 197, + 108, + 160, + 137 ], - "name": "after_action" - }, - { "accounts": [ { "name": "hook_state", @@ -172,8 +110,8 @@ ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "action", @@ -191,29 +129,30 @@ "name": "variant", "type": "bytes" } + ] + }, + { + "name": "cleanup_proposed_terms", + "docs": [ + "Permissionless cleanup of the `ProposedTerms` PDA for expired jobs;", + "rent always refunds to the original provider." ], "discriminator": [ - 5, - 61, - 71, - 243, - 197, - 108, - 160, - 137 - ], - "docs": [ - "Pre-action hook callback; proposes subscription terms on SetBudget." + 188, + 204, + 138, + 242, + 152, + 146, + 239, + 238 ], - "name": "before_action" - }, - { "accounts": [ { + "name": "caller", "docs": [ "Permissionless trigger; the only authorization is that the job has expired." ], - "name": "caller", "signer": true }, { @@ -239,60 +178,60 @@ } }, { + "name": "job_account", "docs": [ - "(Expired) and identity (job_id) validated in handler." - ], - "name": "job_account" + "(Expired) and identity (job_key) validated in handler." + ] }, { + "name": "proposed_terms", "docs": [ "Manually closed after the provider check." ], - "name": "proposed_terms", "writable": true }, { + "name": "acp_state", "docs": [ "against `hook_state.acp_program`. Read only to source the treasury." - ], - "name": "acp_state" + ] }, { - "name": "platform_treasury", + "name": "sponsor", "writable": true } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" } + ] + }, + { + "name": "initialize", + "docs": [ + "Initializes the subscription-hook state. Caller must be the program's upgrade authority." ], "discriminator": [ - 188, - 204, - 138, - 242, + 175, + 175, + 109, + 31, + 13, 152, - 146, - 239, - 238 - ], - "docs": [ - "Permissionless cleanup of the `ProposedTerms` PDA for expired jobs;", - "rent always refunds to the original provider." + 155, + 237 ], - "name": "cleanup_proposed_terms" - }, - { "accounts": [ { "name": "authority", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "hook_state", + "writable": true, "pda": { "seeds": [ { @@ -311,14 +250,14 @@ ] } ] - }, - "writable": true + } }, { + "name": "hook_metadata", "docs": [ "Self-declared metadata read by multi-hook-router for completeness checks." ], - "name": "hook_metadata", + "writable": true, "pda": { "seeds": [ { @@ -340,49 +279,11 @@ ] } ] - }, - "writable": true + } }, { "name": "program_data", "pda": { - "program": { - "kind": "const", - "value": [ - 2, - 168, - 246, - 145, - 78, - 136, - 161, - 176, - 226, - 16, - 21, - 62, - 247, - 99, - 174, - 43, - 0, - 194, - 185, - 61, - 22, - 193, - 36, - 210, - 192, - 83, - 122, - 16, - 4, - 128, - 0, - 0 - ] - }, "seeds": [ { "kind": "const", @@ -421,12 +322,49 @@ 14 ] } - ] + ], + "program": { + "kind": "const", + "value": [ + 2, + 168, + 246, + 145, + 78, + 136, + 161, + 176, + 226, + 16, + 21, + 62, + 247, + 99, + 174, + 43, + 0, + 194, + 185, + 61, + 22, + 193, + 36, + 210, + 192, + 83, + 122, + 16, + 4, + 128, + 0, + 0 + ] + } } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -438,32 +376,34 @@ "name": "subscription_state_program", "type": "pubkey" } + ] + }, + { + "name": "pre_create_proposed_terms", + "docs": [ + "Pre-creates the proposed_terms PDA for a job, zeroed, rent paid by the", + "job's provider at a CPI depth the paymaster can prefund. Idempotent:", + "no-op when the account already exists." ], "discriminator": [ - 175, - 175, - 109, + 30, + 108, + 232, + 212, + 189, 31, - 13, - 152, - 155, - 237 - ], - "docs": [ - "Initializes the subscription-hook state. Caller must be the program's upgrade authority." + 122, + 234 ], - "name": "initialize" - }, - { "accounts": [ { + "name": "payer", "docs": [ "Rent payer; must be the job's provider (the wallet before_action", "debits, and the refund recipient stored in the live proposal later)." ], - "name": "payer", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "hook_state", @@ -488,61 +428,122 @@ } }, { + "name": "job", "docs": [ "via `deserialize_job` against `hook_state.acp_program`." - ], - "name": "job" + ] }, { + "name": "proposed_terms", "docs": [ "in handler." ], - "name": "proposed_terms", "writable": true }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" } - ], + ] + } + ], + "accounts": [ + { + "name": "HookMetadata", "discriminator": [ - 30, - 108, - 232, - 212, - 189, - 31, - 122, - 234 - ], - "docs": [ - "Pre-creates the proposed_terms PDA for a job, zeroed, rent paid by the", - "job's provider at a CPI depth the paymaster can prefund. Idempotent:", - "no-op when the account already exists." - ], - "name": "pre_create_proposed_terms" + 119, + 227, + 48, + 236, + 101, + 194, + 218, + 85 + ] + }, + { + "name": "HookState", + "discriminator": [ + 10, + 13, + 71, + 116, + 92, + 105, + 237, + 231 + ] + } + ], + "events": [ + { + "name": "ProposedTermsCleanedUp", + "discriminator": [ + 81, + 50, + 178, + 8, + 93, + 221, + 142, + 71 + ] + }, + { + "name": "SubscriptionActivated", + "discriminator": [ + 66, + 23, + 25, + 128, + 66, + 95, + 220, + 100 + ] + }, + { + "name": "SubscriptionTermsProposed", + "discriminator": [ + 168, + 155, + 140, + 139, + 180, + 239, + 130, + 153 + ] + }, + { + "name": "SubscriptionTermsSkipped", + "discriminator": [ + 224, + 53, + 137, + 103, + 246, + 117, + 250, + 1 + ] } ], - "metadata": { - "description": "Subscription Hook - subscription management for ACP", - "name": "subscription_hook", - "spec": "0.1.0", - "version": "0.1.0" - }, "types": [ { + "name": "HookMetadata", "docs": [ "Self-declared selectors this hook implements; read by the multi-hook router.", "Bitmask: bit 0 = SetBudget, 1 = Fund, 2 = Submit, 3 = Complete, 4 = Reject." ], - "name": "HookMetadata", "type": { + "kind": "struct", "fields": [ { "name": "required_selectors", @@ -552,16 +553,16 @@ "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "HookState", "docs": [ "Global hook state — stores config for the subscription hook." ], - "name": "HookState", "type": { + "kind": "struct", "fields": [ { "name": "acp_program", @@ -575,27 +576,27 @@ "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "ProposedTermsCleanedUp", "docs": [ "`ProposedTerms` PDA closed via `cleanup_proposed_terms` after job expiry." ], - "name": "ProposedTermsCleanedUp", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { + "name": "provider", "docs": [ "Provider recorded on the terms. Informational only — it is no longer the", "rent destination; see `rent_destination`." ], - "name": "provider", "type": "pubkey" }, { @@ -603,26 +604,26 @@ "type": "u64" }, { + "name": "rent_destination", "docs": [ "Where the reclaimed rent actually went (the platform treasury)." ], - "name": "rent_destination", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "SubscriptionActivated", "docs": [ "Subscription activated after `complete`." ], - "name": "SubscriptionActivated", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "package_id", @@ -640,20 +641,20 @@ "name": "duration", "type": "i64" } - ], - "kind": "struct" + ] } }, { + "name": "SubscriptionTermsProposed", "docs": [ "Provider proposed subscription terms during `set_budget`." ], - "name": "SubscriptionTermsProposed", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "package_id", @@ -663,21 +664,21 @@ "name": "duration", "type": "i64" } - ], - "kind": "struct" + ] } }, { + "name": "SubscriptionTermsSkipped", "docs": [ "Terms proposal skipped; client already has an active subscription for", "this (provider, package_id)." ], - "name": "SubscriptionTermsSkipped", "type": { + "kind": "struct", "fields": [ { - "name": "job_id", - "type": "u64" + "name": "job_key", + "type": "pubkey" }, { "name": "package_id", @@ -687,9 +688,8 @@ "name": "current_expiry", "type": "i64" } - ], - "kind": "struct" + ] } } ] -} +} \ No newline at end of file diff --git a/src/core/solana/idl/subscription_state.json b/src/core/solana/idl/subscription_state.json index b0189a3..f2e9341 100644 --- a/src/core/solana/idl/subscription_state.json +++ b/src/core/solana/idl/subscription_state.json @@ -1,146 +1,26 @@ { - "accounts": [ - { - "discriminator": [ - 105, - 124, - 131, - 88, - 127, - 32, - 75, - 16 - ], - "name": "StateConfig" - }, - { - "discriminator": [ - 21, - 49, - 228, - 3, - 101, - 108, - 246, - 154 - ], - "name": "SubscriptionExpiry" - }, - { - "discriminator": [ - 92, - 146, - 228, - 26, - 250, - 38, - 56, - 237 - ], - "name": "WriterRegistry" - } - ], - "errors": [ - { - "code": 6000, - "msg": "Subscription expiry must extend beyond current", - "name": "SubscriptionNotExtended" - }, - { - "code": 6001, - "msg": "Zero address not allowed", - "name": "ZeroAddress" - }, - { - "code": 6002, - "msg": "Unauthorized caller", - "name": "UnauthorizedCaller" - }, - { - "code": 6003, - "msg": "Writer not registered", - "name": "WriterNotRegistered" - }, - { - "code": 6004, - "msg": "Writer has been deactivated", - "name": "WriterDeactivated" - }, - { - "code": 6005, - "msg": "sub_expiry must be pre-created; the provider calls pre_create_sub_expiry", - "name": "SubExpiryNotPreCreated" - } - ], - "events": [ - { - "discriminator": [ - 83, - 253, - 94, - 222, - 179, - 41, - 95, - 127 - ], - "name": "AuthorityNominated" - }, - { - "discriminator": [ - 245, - 109, - 179, - 54, - 135, - 92, - 22, - 64 - ], - "name": "AuthorityTransferred" - }, - { - "discriminator": [ - 66, - 23, - 25, - 128, - 66, - 95, - 220, - 100 - ], - "name": "SubscriptionActivated" - }, + "metadata": { + "name": "subscription_state", + "version": "0.1.0", + "spec": "0.1.0", + "description": "Subscription State - shared subscription storage for ACP hooks" + }, + "instructions": [ { - "discriminator": [ - 85, - 84, - 253, - 78, - 229, - 105, - 159, - 221 + "name": "accept_authority", + "docs": [ + "Accepts a pending authority nomination, completing ownership transfer." ], - "name": "WriterAdded" - }, - { "discriminator": [ - 216, - 93, - 239, - 230, - 124, + 107, 86, - 161, - 210 + 198, + 91, + 33, + 12, + 107, + 160 ], - "name": "WriterRemoved" - } - ], - "instructions": [ - { "accounts": [ { "name": "new_authority", @@ -148,6 +28,7 @@ }, { "name": "state_config", + "writable": true, "pda": { "seeds": [ { @@ -168,29 +49,29 @@ ] } ] - }, - "writable": true + } } ], - "args": [], - "discriminator": [ - 107, - 86, - 198, - 91, - 33, - 12, - 107, - 160 - ], - "docs": [ - "Accepts a pending authority nomination, completing ownership transfer." - ], - "name": "accept_authority" + "args": [] }, { + "name": "activate_subscription", + "docs": [ + "Activates or extends a subscription. CPI-only -- requires a registered writer's signature." + ], + "discriminator": [ + 101, + 41, + 57, + 61, + 203, + 67, + 214, + 17 + ], "accounts": [ { + "name": "payer", "docs": [ "The job's provider. Named, never debited: this handler does not allocate", "(see below), so nothing is spent here. It stays in the account list", @@ -212,15 +93,14 @@ "", "refund the hook credits after this CPI returns." ], - "name": "payer", "writable": true }, { + "name": "writer_registry", "docs": [ "The calling program must be a registered writer.", "PDA seeded by the writer's program ID. Writer identity validated in handler." ], - "name": "writer_registry", "pda": { "seeds": [ { @@ -235,22 +115,23 @@ ] }, { - "account": "WriterRegistry", "kind": "account", - "path": "writer_registry.writer" + "path": "writer_registry.writer", + "account": "WriterRegistry" } ] } }, { + "name": "writer_signer", "docs": [ "The writer program's hook_state PDA — proves CPI origin.", "Only the registered writer program can produce a valid signature for this PDA." ], - "name": "writer_signer", "signer": true }, { + "name": "subscription_expiry", "docs": [ "Must already exist — allocated by `pre_create_sub_expiry`. Untyped here", "only so its absence surfaces as `SubExpiryNotPreCreated` rather than an", @@ -260,7 +141,7 @@ "", "deserialize in the handler before any field is trusted." ], - "name": "subscription_expiry", + "writable": true, "pda": { "seeds": [ { @@ -291,12 +172,11 @@ "path": "package_id" } ] - }, - "writable": true + } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -316,28 +196,28 @@ "name": "expiry", "type": "i64" } + ] + }, + { + "name": "add_writer", + "docs": [ + "Grants a program the writer role, allowing it to activate subscriptions via CPI." ], "discriminator": [ - 101, - 41, - 57, - 61, - 203, - 67, - 214, - 17 - ], - "docs": [ - "Activates or extends a subscription. CPI-only -- requires a registered writer's signature." + 95, + 14, + 80, + 179, + 133, + 216, + 154, + 108 ], - "name": "activate_subscription" - }, - { "accounts": [ { "name": "authority", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "state_config", @@ -365,6 +245,7 @@ }, { "name": "writer_registry", + "writable": true, "pda": { "seeds": [ { @@ -383,12 +264,11 @@ "path": "writer" } ] - }, - "writable": true + } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -396,31 +276,32 @@ "name": "writer", "type": "pubkey" } + ] + }, + { + "name": "initialize", + "docs": [ + "Initializes the subscription-state config PDA. Caller must be the program's upgrade authority." ], "discriminator": [ - 95, - 14, - 80, - 179, - 133, - 216, - 154, - 108 - ], - "docs": [ - "Grants a program the writer role, allowing it to activate subscriptions via CPI." + 175, + 175, + 109, + 31, + 13, + 152, + 155, + 237 ], - "name": "add_writer" - }, - { "accounts": [ { "name": "authority", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "state_config", + "writable": true, "pda": { "seeds": [ { @@ -441,49 +322,11 @@ ] } ] - }, - "writable": true + } }, { "name": "program_data", "pda": { - "program": { - "kind": "const", - "value": [ - 2, - 168, - 246, - 145, - 78, - 136, - 161, - 176, - 226, - 16, - 21, - 62, - 247, - 99, - 174, - 43, - 0, - 194, - 185, - 61, - 22, - 193, - 36, - 210, - 192, - 83, - 122, - 16, - 4, - 128, - 0, - 0 - ] - }, "seeds": [ { "kind": "const", @@ -522,31 +365,68 @@ 214 ] } - ] + ], + "program": { + "kind": "const", + "value": [ + 2, + 168, + 246, + 145, + 78, + 136, + 161, + 176, + 226, + 16, + 21, + 62, + 247, + 99, + 174, + 43, + 0, + 194, + 185, + 61, + 22, + 193, + 36, + 210, + 192, + 83, + 122, + 16, + 4, + 128, + 0, + 0 + ] + } } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], - "args": [], - "discriminator": [ - 175, - 175, - 109, - 31, - 13, - 152, - 155, - 237 - ], - "docs": [ - "Initializes the subscription-state config PDA. Caller must be the program's upgrade authority." - ], - "name": "initialize" + "args": [] }, { + "name": "nominate_authority", + "docs": [ + "Nominates a new authority for ownership transfer." + ], + "discriminator": [ + 148, + 182, + 144, + 91, + 186, + 12, + 118, + 18 + ], "accounts": [ { "name": "authority", @@ -554,6 +434,7 @@ }, { "name": "state_config", + "writable": true, "pda": { "seeds": [ { @@ -574,8 +455,7 @@ ] } ] - }, - "writable": true + } } ], "args": [ @@ -583,35 +463,38 @@ "name": "new_authority", "type": "pubkey" } + ] + }, + { + "name": "pre_create_sub_expiry", + "docs": [ + "Pre-creates the sub_expiry PDA with expiry = 0 (absent-equivalent),", + "rent paid by the provider at a CPI depth the paymaster can prefund.", + "Idempotent: no-op on an activated subscription." ], - "discriminator": [ - 148, - 182, - 144, - 91, - 186, - 12, - 118, - 18 - ], - "docs": [ - "Nominates a new authority for ownership transfer." + "discriminator": [ + 31, + 24, + 156, + 205, + 9, + 176, + 248, + 34 ], - "name": "nominate_authority" - }, - { "accounts": [ { + "name": "payer", "docs": [ "Rent payer; must be the provider named in the PDA seeds (the wallet", "activation debits, and the wallet the paymaster prefunds)." ], - "name": "payer", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "subscription_expiry", + "writable": true, "pda": { "seeds": [ { @@ -642,12 +525,11 @@ "path": "package_id" } ] - }, - "writable": true + } }, { - "address": "11111111111111111111111111111111", - "name": "system_program" + "name": "system_program", + "address": "11111111111111111111111111111111" } ], "args": [ @@ -663,25 +545,23 @@ "name": "package_id", "type": "u64" } + ] + }, + { + "name": "reactivate_writer", + "docs": [ + "Re-activates a previously deactivated writer." ], "discriminator": [ - 31, - 24, - 156, - 205, - 9, - 176, - 248, - 34 - ], - "docs": [ - "Pre-creates the sub_expiry PDA with expiry = 0 (absent-equivalent),", - "rent paid by the provider at a CPI depth the paymaster can prefund.", - "Idempotent: no-op on an activated subscription." + 62, + 184, + 214, + 49, + 203, + 151, + 133, + 51 ], - "name": "pre_create_sub_expiry" - }, - { "accounts": [ { "name": "authority", @@ -712,10 +592,11 @@ } }, { + "name": "writer_registry", "docs": [ "Existing (deactivated) writer registry PDA." ], - "name": "writer_registry", + "writable": true, "pda": { "seeds": [ { @@ -734,8 +615,7 @@ "path": "writer" } ] - }, - "writable": true + } } ], "args": [ @@ -743,28 +623,28 @@ "name": "writer", "type": "pubkey" } + ] + }, + { + "name": "remove_writer", + "docs": [ + "Revokes the writer role. PDA is kept alive for in-flight observability." ], "discriminator": [ - 62, - 184, - 214, - 49, - 203, - 151, - 133, - 51 - ], - "docs": [ - "Re-activates a previously deactivated writer." + 247, + 145, + 28, + 134, + 227, + 132, + 243, + 83 ], - "name": "reactivate_writer" - }, - { "accounts": [ { "name": "authority", - "signer": true, - "writable": true + "writable": true, + "signer": true }, { "name": "state_config", @@ -792,6 +672,7 @@ }, { "name": "writer_registry", + "writable": true, "pda": { "seeds": [ { @@ -810,8 +691,7 @@ "path": "writer" } ] - }, - "writable": true + } } ], "args": [ @@ -819,36 +699,157 @@ "name": "writer", "type": "pubkey" } - ], + ] + } + ], + "accounts": [ + { + "name": "StateConfig", "discriminator": [ - 247, - 145, - 28, - 134, - 227, - 132, - 243, - 83 - ], - "docs": [ - "Revokes the writer role. PDA is kept alive for in-flight observability." - ], - "name": "remove_writer" + 105, + 124, + 131, + 88, + 127, + 32, + 75, + 16 + ] + }, + { + "name": "SubscriptionExpiry", + "discriminator": [ + 21, + 49, + 228, + 3, + 101, + 108, + 246, + 154 + ] + }, + { + "name": "WriterRegistry", + "discriminator": [ + 92, + 146, + 228, + 26, + 250, + 38, + 56, + 237 + ] + } + ], + "events": [ + { + "name": "AuthorityNominated", + "discriminator": [ + 83, + 253, + 94, + 222, + 179, + 41, + 95, + 127 + ] + }, + { + "name": "AuthorityTransferred", + "discriminator": [ + 245, + 109, + 179, + 54, + 135, + 92, + 22, + 64 + ] + }, + { + "name": "SubscriptionActivated", + "discriminator": [ + 66, + 23, + 25, + 128, + 66, + 95, + 220, + 100 + ] + }, + { + "name": "WriterAdded", + "discriminator": [ + 85, + 84, + 253, + 78, + 229, + 105, + 159, + 221 + ] + }, + { + "name": "WriterRemoved", + "discriminator": [ + 216, + 93, + 239, + 230, + 124, + 86, + 161, + 210 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "SubscriptionNotExtended", + "msg": "Subscription expiry must extend beyond current" + }, + { + "code": 6001, + "name": "ZeroAddress", + "msg": "Zero address not allowed" + }, + { + "code": 6002, + "name": "UnauthorizedCaller", + "msg": "Unauthorized caller" + }, + { + "code": 6003, + "name": "WriterNotRegistered", + "msg": "Writer not registered" + }, + { + "code": 6004, + "name": "WriterDeactivated", + "msg": "Writer has been deactivated" + }, + { + "code": 6005, + "name": "SubExpiryNotPreCreated", + "msg": "sub_expiry must be pre-created; the provider calls pre_create_sub_expiry" } ], - "metadata": { - "description": "Subscription State - shared subscription storage for ACP hooks", - "name": "subscription_state", - "spec": "0.1.0", - "version": "0.1.0" - }, "types": [ { + "name": "AuthorityNominated", "docs": [ "Emitted when a new authority is nominated (first step of two-step transfer)." ], - "name": "AuthorityNominated", "type": { + "kind": "struct", "fields": [ { "name": "current_authority", @@ -858,16 +859,16 @@ "name": "pending_authority", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "AuthorityTransferred", "docs": [ "Emitted when a pending authority accepts ownership (second step of two-step transfer)." ], - "name": "AuthorityTransferred", "type": { + "kind": "struct", "fields": [ { "name": "old_authority", @@ -877,50 +878,50 @@ "name": "new_authority", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "StateConfig", "docs": [ "Global state -- stores admin authority. Singleton PDA seeded by `[\"state_config\"]`." ], - "name": "StateConfig", "type": { + "kind": "struct", "fields": [ { + "name": "authority", "docs": [ "Current program authority; can add/remove writers and transfer ownership." ], - "name": "authority", "type": "pubkey" }, { + "name": "pending_authority", "docs": [ "Nominated successor (two-step transfer). `None` until `nominate_authority` is called." ], - "name": "pending_authority", "type": { "option": "pubkey" } }, { + "name": "bump", "docs": [ "PDA bump seed." ], - "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "SubscriptionActivated", "docs": [ "Emitted when a subscription is activated or extended by a writer via CPI." ], - "name": "SubscriptionActivated", "type": { + "kind": "struct", "fields": [ { "name": "client", @@ -938,119 +939,118 @@ "name": "expiry", "type": "i64" } - ], - "kind": "struct" + ] } }, { + "name": "SubscriptionExpiry", "docs": [ "Subscription expiry per client/provider/package.", "Seeds: [\"sub_expiry\", client, provider, package_id]" ], - "name": "SubscriptionExpiry", "type": { + "kind": "struct", "fields": [ { + "name": "client", "docs": [ "The subscribing client." ], - "name": "client", "type": "pubkey" }, { + "name": "provider", "docs": [ "The service provider." ], - "name": "provider", "type": "pubkey" }, { + "name": "package_id", "docs": [ "Identifies which subscription package this tracks." ], - "name": "package_id", "type": "u64" }, { + "name": "expiry", "docs": [ "Unix timestamp when this subscription expires. Monotonically increasing." ], - "name": "expiry", "type": "i64" }, { + "name": "bump", "docs": [ "PDA bump seed." ], - "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "WriterAdded", "docs": [ "Emitted when a program is granted the writer role (or re-activated)." ], - "name": "WriterAdded", "type": { + "kind": "struct", "fields": [ { "name": "writer", "type": "pubkey" } - ], - "kind": "struct" + ] } }, { + "name": "WriterRegistry", "docs": [ "Writer registry — tracks authorized programs that can write subscription data.", "Seeds: [\"writer\", writer_pubkey]" ], - "name": "WriterRegistry", "type": { + "kind": "struct", "fields": [ { + "name": "writer", "docs": [ "The authorized program ID." ], - "name": "writer", "type": "pubkey" }, { + "name": "is_active", "docs": [ "Whether this writer is currently active; deactivated writers are kept alive." ], - "name": "is_active", "type": "bool" }, { + "name": "bump", "docs": [ "PDA bump seed." ], - "name": "bump", "type": "u8" } - ], - "kind": "struct" + ] } }, { + "name": "WriterRemoved", "docs": [ "Emitted when a writer's role is revoked (deactivated, PDA kept alive)." ], - "name": "WriterRemoved", "type": { + "kind": "struct", "fields": [ { "name": "writer", "type": "pubkey" } - ], - "kind": "struct" + ] } } ] -} +} \ No newline at end of file diff --git a/src/core/solana/multiHook.ts b/src/core/solana/multiHook.ts index 51f16b7..e76d9ba 100644 --- a/src/core/solana/multiHook.ts +++ b/src/core/solana/multiHook.ts @@ -37,23 +37,30 @@ async function pda(programAddress: Address, seeds: ReadonlyUint8Array[]): Promis // PDA derivations (program id is passed in per call) // --------------------------------------------------------------------------- +// A job's identity IS its account address — the program derives that address +// from ["job", client, seed] where `seed` is a caller-chosen u64 uniquifier, +// not a re-usable identifier. Every OTHER PDA that used to be seeded by a u64 +// job id is now seeded by that address (`job_key` in the Rust structs) instead: +// deriving one requires the job's address, never its seed value directly. export const acpStatePda = (acp: Address) => pda(acp, [utf8.encode("acp_state")]); -export const jobPda = (acp: Address, client: Address, jobId: bigint) => - pda(acp, [utf8.encode("job"), addr.encode(client), u64le(jobId)]); +export const jobPda = (acp: Address, client: Address, seed: bigint) => + pda(acp, [utf8.encode("job"), addr.encode(client), u64le(seed)]); export const hookWhitelistPda = (acp: Address, hook: Address) => pda(acp, [utf8.encode("hook_whitelist"), addr.encode(hook)]); export const vaultAuthorityPda = (acp: Address, job: Address) => pda(acp, [utf8.encode("vault_authority"), addr.encode(job)]); +export const vaultPda = (acp: Address, job: Address) => + pda(acp, [utf8.encode("vault"), addr.encode(job)]); export const hookStatePda = (hook: Address) => pda(hook, [utf8.encode("hook_state")]); export const hookMetadataPda = (hook: Address) => pda(hook, [utf8.encode("hook_metadata")]); export const routerStatePda = (router: Address) => pda(router, [utf8.encode("router_state")]); -export const hookRouterPda = (router: Address, jobId: bigint) => - pda(router, [utf8.encode("hook_router"), u64le(jobId)]); +export const hookRouterPda = (router: Address, job: Address) => + pda(router, [utf8.encode("hook_router"), addr.encode(job)]); -export const proposedTermsPda = (subHook: Address, jobId: bigint) => - pda(subHook, [utf8.encode("proposed_terms"), u64le(jobId)]); +export const proposedTermsPda = (subHook: Address, job: Address) => + pda(subHook, [utf8.encode("proposed_terms"), addr.encode(job)]); export const subExpiryPda = (subState: Address, client: Address, provider: Address, pkg: bigint) => pda(subState, [ @@ -66,14 +73,14 @@ export const writerRegistryPda = (subState: Address, writer: Address) => pda(subState, [utf8.encode("writer"), addr.encode(writer)]); export const stateConfigPda = (subState: Address) => pda(subState, [utf8.encode("state_config")]); -export const intentPda = (fundHook: Address, jobId: bigint, kind: 0 | 1) => - pda(fundHook, [utf8.encode("intent"), u64le(jobId), new Uint8Array([kind])]); -export const fundRequestIntentIdPda = (fundHook: Address, jobId: bigint) => - pda(fundHook, [utf8.encode("fund_request_intent_id"), u64le(jobId)]); -export const providerEscrowIntentIdPda = (fundHook: Address, jobId: bigint) => - pda(fundHook, [utf8.encode("provider_escrow_intent_id"), u64le(jobId)]); -export const escrowAuthorityPda = (fundHook: Address, jobId: bigint) => - pda(fundHook, [utf8.encode("escrow_authority"), u64le(jobId)]); +export const intentPda = (fundHook: Address, job: Address, kind: 0 | 1) => + pda(fundHook, [utf8.encode("intent"), addr.encode(job), new Uint8Array([kind])]); +export const fundRequestIntentIdPda = (fundHook: Address, job: Address) => + pda(fundHook, [utf8.encode("fund_request_intent_id"), addr.encode(job)]); +export const providerEscrowIntentIdPda = (fundHook: Address, job: Address) => + pda(fundHook, [utf8.encode("provider_escrow_intent_id"), addr.encode(job)]); +export const escrowAuthorityPda = (fundHook: Address, job: Address) => + pda(fundHook, [utf8.encode("escrow_authority"), addr.encode(job)]); // --------------------------------------------------------------------------- // Opt-params encoders (byte-for-byte compatible with what the deployed @@ -151,11 +158,13 @@ const PROPOSED_TERMS_DISCRIMINATOR = new Uint8Array([ ]); // On-chain ProposedTerms layout: 8-byte discriminator, -// job_id u64 LE, provider Pubkey, duration i64 LE, package_id u64 LE, bump u8. -const PROPOSED_TERMS_SIZE = 8 + 8 + 32 + 8 + 8 + 1; +// job_key Pubkey, provider Pubkey, duration i64 LE, package_id u64 LE, bump u8. +// job_key replaced a job_id:u64 first field when job identity became the +// job account's own address rather than a counter value. +const PROPOSED_TERMS_SIZE = 8 + 32 + 32 + 8 + 8 + 1; export type ProposedTerms = { - jobId: bigint; + job: Address; provider: Address; /** Subscription duration in seconds (i64; the hook rejects non-positive). */ duration: bigint; @@ -165,8 +174,8 @@ export type ProposedTerms = { /** * Read and decode the subscription hook's proposed_terms PDA for a job. * Returns null when the account does not exist, was closed, or does not - * decode as ProposedTerms for this jobId (defensive: length, discriminator, - * and job-id echo are all checked, so a layout drift in the on-chain program + * decode as ProposedTerms for this job (defensive: length, discriminator, + * and job-key echo are all checked, so a layout drift in the on-chain program * surfaces as null rather than as garbage terms). */ export async function fetchProposedTerms( @@ -177,10 +186,10 @@ export async function fetchProposedTerms( ) => { send: () => Promise<{ value: { data: unknown } | null }> }; }, subHook: Address, - jobId: bigint, + job: Address, commitment: "confirmed" | "finalized" | "processed" = "confirmed" ): Promise { - const pdaAddress = await proposedTermsPda(subHook, jobId); + const pdaAddress = await proposedTermsPda(subHook, job); const info = await rpc .getAccountInfo(pdaAddress, { encoding: "base64", commitment }) .send(); @@ -193,12 +202,81 @@ export async function fetchProposedTerms( if (data[i] !== PROPOSED_TERMS_DISCRIMINATOR[i]) return null; } const view = new DataView(data.buffer, data.byteOffset, data.byteLength); - const storedJobId = view.getBigUint64(8, true); - if (storedJobId !== jobId) return null; + const storedJobKey = getAddressDecoder().decode(data.subarray(8, 40)); + if (storedJobKey !== job) return null; return { - jobId: storedJobId, - provider: getAddressDecoder().decode(data.subarray(16, 48)), - duration: view.getBigInt64(48, true), - packageId: view.getBigUint64(56, true), + job: storedJobKey, + provider: getAddressDecoder().decode(data.subarray(40, 72)), + duration: view.getBigInt64(72, true), + packageId: view.getBigUint64(80, true), + }; +} + +// --------------------------------------------------------------------------- +// FundRequestIntentId raw reader +// --------------------------------------------------------------------------- + +/** + * Anchor discriminator for the fund-transfer hook's FundRequestIntentId + * account: sha256("account:FundRequestIntentId")[0..8]. `close_intent` — its + * last typed (`Account`) reference — was removed once + * the on-chain guards it existed for became unreachable, which drops the type + * from the IDL entirely and with it the generated fetcher. The account and its + * layout are otherwise unchanged, so this reads it raw, mirroring + * fetchProposedTerms above. + */ +const FUND_REQUEST_INTENT_ID_DISCRIMINATOR = new Uint8Array([ + 115, 10, 133, 59, 77, 84, 106, 104, +]); + +// On-chain layout: 8-byte discriminator, job_key Pubkey, intent_id u64 LE, bump u8. +const FUND_REQUEST_INTENT_ID_SIZE = 8 + 32 + 8 + 1; + +export type FundRequestIntentId = { + jobKey: Address; + intentId: bigint; + bump: number; +}; + +export type MaybeFundRequestIntentId = + | { exists: false } + | { exists: true; data: FundRequestIntentId }; + +/** + * Read and decode the fund-transfer hook's fund_request_intent_id PDA. + * `exists: false` covers "never created", "closed", and "wrong discriminator + * or length" alike — a layout drift in the on-chain program surfaces as + * exists:false rather than as garbage data, matching the generated + * fetchMaybe* convention this replaces. + */ +export async function fetchMaybeFundRequestIntentId( + rpc: { + getAccountInfo: ( + address: Address, + config: { encoding: "base64"; commitment: "confirmed" | "finalized" | "processed" } + ) => { send: () => Promise<{ value: { data: unknown } | null }> }; + }, + address: Address, + config: { commitment: "confirmed" | "finalized" | "processed" } +): Promise { + const info = await rpc + .getAccountInfo(address, { encoding: "base64", commitment: config.commitment }) + .send(); + const raw = info.value?.data; + const b64 = Array.isArray(raw) ? raw[0] : undefined; + if (typeof b64 !== "string" || b64.length === 0) return { exists: false }; + const data = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); + if (data.length < FUND_REQUEST_INTENT_ID_SIZE) return { exists: false }; + for (let i = 0; i < 8; i++) { + if (data[i] !== FUND_REQUEST_INTENT_ID_DISCRIMINATOR[i]) return { exists: false }; + } + const view = new DataView(data.buffer, data.byteOffset, data.byteLength); + return { + exists: true, + data: { + jobKey: getAddressDecoder().decode(data.subarray(8, 40)), + intentId: view.getBigUint64(40, true), + bump: data[48]!, + }, }; } diff --git a/src/core/solana/preCreate.ts b/src/core/solana/preCreate.ts index 7de0669..c41285c 100644 --- a/src/core/solana/preCreate.ts +++ b/src/core/solana/preCreate.ts @@ -61,7 +61,6 @@ export type PreCreateHookPdaArgs = { /** Rent payer — must be the job's provider (programs enforce this). */ payer: SolanaSigner; jobPda: Address; - jobId: bigint; clientAddress: Address; providerAddress: Address; /** Pre-create the fund-request intent (kind 0) + its map. */ @@ -106,11 +105,11 @@ export async function buildPreCreateHookPdaIxs( intentKinds.length > 0 ? await hookStatePda(args.fundHook!) : null; for (const kind of intentKinds) { const fundHook = args.fundHook!; - const intent = await intentPda(fundHook, args.jobId, kind); + const intent = await intentPda(fundHook, args.jobPda, kind); const intentMap = kind === INTENT_KIND_FUND_REQUEST - ? await fundRequestIntentIdPda(fundHook, args.jobId) - : await providerEscrowIntentIdPda(fundHook, args.jobId); + ? await fundRequestIntentIdPda(fundHook, args.jobPda) + : await providerEscrowIntentIdPda(fundHook, args.jobPda); ixs.push( toLike( getPreCreateIntentInstruction( @@ -120,7 +119,7 @@ export async function buildPreCreateHookPdaIxs( job: args.jobPda, intent, intentMap, - jobId: args.jobId, + jobKey: args.jobPda, kind, }, { programAddress: fundHook }, @@ -137,8 +136,8 @@ export async function buildPreCreateHookPdaIxs( payer: args.payer, hookState: await hookStatePda(args.subHook), job: args.jobPda, - proposedTerms: await proposedTermsPda(args.subHook, args.jobId), - jobId: args.jobId, + proposedTerms: await proposedTermsPda(args.subHook, args.jobPda), + jobKey: args.jobPda, }, { programAddress: args.subHook }, ), diff --git a/src/core/solana/routerLayout.ts b/src/core/solana/routerLayout.ts index 8e283b8..b13a139 100644 --- a/src/core/solana/routerLayout.ts +++ b/src/core/solana/routerLayout.ts @@ -163,10 +163,10 @@ export function routerContext(chainId: number): RouterContext { */ export async function routerPrefixAccounts( ctx: RouterContext, - jobId: bigint + job: Address ): Promise { return [ - ro(await mh.hookRouterPda(ctx.router, jobId)), + ro(await mh.hookRouterPda(ctx.router, job)), ro(await mh.routerStatePda(ctx.router)), ro(SYSVAR_INSTRUCTIONS_ID), ]; @@ -241,7 +241,6 @@ async function hookFraming( export async function buildSetBudgetFanOut( ctx: RouterContext, p: { - jobId: bigint; jobPda: Address; /** The provider signing setBudget; pays proposed_terms rent. */ seller: Address; @@ -263,7 +262,7 @@ export async function buildSetBudgetFanOut( w(subHookState), ro(SYSVAR_INSTRUCTIONS_ID), ws(p.seller), - w(await mh.proposedTermsPda(ctx.subHook, p.jobId)), + w(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), ro(p.jobPda), ro( await mh.subExpiryPda( @@ -287,8 +286,8 @@ export async function buildSetBudgetFanOut( w(fundHookState), ro(SYSVAR_INSTRUCTIONS_ID), ws(p.seller), - w(await mh.intentPda(ctx.fundHook, p.jobId, INTENT_KIND_FUND_REQUEST)), - w(await mh.fundRequestIntentIdPda(ctx.fundHook, p.jobId)), + w(await mh.intentPda(ctx.fundHook, p.jobPda, INTENT_KIND_FUND_REQUEST)), + w(await mh.fundRequestIntentIdPda(ctx.fundHook, p.jobPda)), ro(p.jobPda), ro(SYSTEM_PROGRAM_ID), ]; @@ -305,7 +304,7 @@ export async function buildSetBudgetFanOut( }, ]), extraAccounts: [ - ...(await routerPrefixAccounts(ctx, p.jobId)), + ...(await routerPrefixAccounts(ctx, p.jobPda)), ...(await hookFraming(ctx, ctx.subHook)), ...subSlice, ...(await hookFraming(ctx, ctx.fundHook)), @@ -324,7 +323,6 @@ export async function buildSetBudgetFanOut( export async function buildFundFanOut( ctx: RouterContext, p: { - jobId: bigint; jobPda: Address; proposedTerms: { duration: bigint; packageId: bigint } | null; fundIntent: { @@ -347,7 +345,7 @@ export async function buildFundFanOut( const subSlice: AccountMetaLike[] = [ w(subHookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.proposedTermsPda(ctx.subHook, p.jobId)), + ro(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), ]; const subParams = p.proposedTerms ? mh.encodeSubParams(p.proposedTerms.duration, p.proposedTerms.packageId) @@ -359,8 +357,8 @@ export async function buildFundFanOut( fundSlice = [ w(fundHookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.fundRequestIntentIdPda(ctx.fundHook, p.jobId)), - w(await mh.intentPda(ctx.fundHook, p.jobId, INTENT_KIND_FUND_REQUEST)), + ro(await mh.fundRequestIntentIdPda(ctx.fundHook, p.jobPda)), + w(await mh.intentPda(ctx.fundHook, p.jobPda, INTENT_KIND_FUND_REQUEST)), w(p.fundIntent.fromAta), w(p.fundIntent.recipientAta), ro(TOKEN_PROGRAM_ID), @@ -378,7 +376,7 @@ export async function buildFundFanOut( fundSlice = [ w(fundHookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.fundRequestIntentIdPda(ctx.fundHook, p.jobId)), + ro(await mh.fundRequestIntentIdPda(ctx.fundHook, p.jobPda)), ]; fundParams = new Uint8Array(0); } @@ -389,7 +387,7 @@ export async function buildFundFanOut( { accountCount: fundSlice.length, params: fundParams }, ]), extraAccounts: [ - ...(await routerPrefixAccounts(ctx, p.jobId)), + ...(await routerPrefixAccounts(ctx, p.jobPda)), ...(await hookFraming(ctx, ctx.subHook)), ...subSlice, ...(await hookFraming(ctx, ctx.fundHook)), @@ -407,7 +405,6 @@ export async function buildFundFanOut( export async function buildSubmitFanOut( ctx: RouterContext, p: { - jobId: bigint; jobPda: Address; seller: Address; escrow: { @@ -432,8 +429,8 @@ export async function buildSubmitFanOut( w(fundHookState), ro(SYSVAR_INSTRUCTIONS_ID), ws(p.seller), - w(await mh.intentPda(ctx.fundHook, p.jobId, INTENT_KIND_ESCROW)), - w(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobId)), + w(await mh.intentPda(ctx.fundHook, p.jobPda, INTENT_KIND_ESCROW)), + w(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobPda)), w(p.escrow.providerAta), w(p.escrow.escrowVault), ro(p.escrow.escrowAuthority), @@ -452,7 +449,7 @@ export async function buildSubmitFanOut( { accountCount: fundSlice.length, params: fundParams }, ]), extraAccounts: [ - ...(await routerPrefixAccounts(ctx, p.jobId)), + ...(await routerPrefixAccounts(ctx, p.jobPda)), ...(await hookFraming(ctx, ctx.fundHook)), ...fundSlice, ], @@ -461,18 +458,18 @@ export async function buildSubmitFanOut( /** * complete fan-out. The subscription hook activates the subscription via CPI - * into subscription-state (payer = provider, who MUST co-sign: sub-state's - * ActivateSubscription declares payer as Signer — it pays sub_expiry rent and - * receives the proposed_terms rent refund). The fund hook releases the escrow - * bond to the client. + * into subscription-state, payer = provider. Pre-F-118, ActivateSubscription + * declared payer as Signer because it allocated sub_expiry there; since + * sub_expiry is now pre-created at set_budget, activation allocates nothing + * and the provider's signature is no longer required. The fund hook releases + * the escrow bond to the client. * - * `requiredExtraSigner` is the provider address whenever terms exist; the - * caller must include that signer in the transaction or the send fails. + * `requiredExtraSigner` is always null post-F-118 — kept as a return field so + * a future signer requirement doesn't need a call-site shape change. */ export async function buildCompleteFanOut( ctx: RouterContext, p: { - jobId: bigint; jobPda: Address; provider: Address; clientAddress: Address; @@ -497,8 +494,8 @@ export async function buildCompleteFanOut( subSlice = [ w(subHookState), ro(SYSVAR_INSTRUCTIONS_ID), - w(await mh.proposedTermsPda(ctx.subHook, p.jobId)), - ws(p.provider), + w(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), + w(p.provider), ro(p.jobPda), ro(ctx.subState), ro(await mh.writerRegistryPda(ctx.subState, ctx.subHook)), @@ -521,7 +518,7 @@ export async function buildCompleteFanOut( subSlice = [ w(subHookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.proposedTermsPda(ctx.subHook, p.jobId)), + ro(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), ]; } @@ -530,8 +527,8 @@ export async function buildCompleteFanOut( fundSlice = [ w(fundHookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobId)), - w(await mh.intentPda(ctx.fundHook, p.jobId, INTENT_KIND_ESCROW)), + ro(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobPda)), + w(await mh.intentPda(ctx.fundHook, p.jobPda, INTENT_KIND_ESCROW)), w(p.escrow.escrowVault), w(p.escrow.clientAta), ro(p.escrow.escrowAuthority), @@ -545,7 +542,7 @@ export async function buildCompleteFanOut( fundSlice = [ w(fundHookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobId)), + ro(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobPda)), ]; } @@ -555,13 +552,13 @@ export async function buildCompleteFanOut( { accountCount: fundSlice.length, params: new Uint8Array(0) }, ]), extraAccounts: [ - ...(await routerPrefixAccounts(ctx, p.jobId)), + ...(await routerPrefixAccounts(ctx, p.jobPda)), ...(await hookFraming(ctx, ctx.subHook)), ...subSlice, ...(await hookFraming(ctx, ctx.fundHook)), ...fundSlice, ], - requiredExtraSigner: p.packageId !== null ? p.provider : null, + requiredExtraSigner: null, }; } @@ -582,7 +579,6 @@ export async function buildCompleteFanOut( export async function buildSubSetBudgetAccounts( ctx: SubscriptionContext, p: { - jobId: bigint; jobPda: Address; seller: Address; clientAddress: Address; @@ -597,7 +593,7 @@ export async function buildSubSetBudgetAccounts( w(hookState), ro(SYSVAR_INSTRUCTIONS_ID), ws(p.seller), - w(await mh.proposedTermsPda(ctx.subHook, p.jobId)), + w(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), ro(p.jobPda), ro( await mh.subExpiryPda( @@ -614,12 +610,12 @@ export async function buildSubSetBudgetAccounts( /** fund: post_fund validates the client's terms echo — remaining [sysvar, proposed_terms]. */ export async function buildSubFundAccounts( ctx: SubscriptionContext, - jobId: bigint + job: Address ): Promise { return [ w(await mh.hookStatePda(ctx.subHook)), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.proposedTermsPda(ctx.subHook, jobId)), + ro(await mh.proposedTermsPda(ctx.subHook, job)), ]; } @@ -640,15 +636,15 @@ export async function buildSubSubmitAccounts( /** * complete: post_complete activates the subscription — remaining [sysvar, - * proposed_terms(w), payer(signer,w), job, sub_state_program, - * writer_registry, subscription_expiry(w), system] after hook_state. The - * payer must be the provider and must SIGN (it pays sub_expiry rent and - * receives the proposed_terms refund). With no terms the minimal set no-ops. + * proposed_terms(w), payer(w), job, sub_state_program, writer_registry, + * subscription_expiry(w), system] after hook_state. Pre-F-118 the payer had + * to SIGN (it allocated sub_expiry there); since sub_expiry is now + * pre-created at set_budget, activation allocates nothing and no signature is + * required. With no terms the minimal set no-ops. */ export async function buildSubCompleteAccounts( ctx: SubscriptionContext, p: { - jobId: bigint; jobPda: Address; provider: Address; clientAddress: Address; @@ -665,7 +661,7 @@ export async function buildSubCompleteAccounts( accounts: [ w(hookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.proposedTermsPda(ctx.subHook, p.jobId)), + ro(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), ], requiredExtraSigner: null, }; @@ -674,8 +670,8 @@ export async function buildSubCompleteAccounts( accounts: [ w(hookState), ro(SYSVAR_INSTRUCTIONS_ID), - w(await mh.proposedTermsPda(ctx.subHook, p.jobId)), - ws(p.provider), + w(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), + w(p.provider), ro(p.jobPda), ro(ctx.subState), ro(await mh.writerRegistryPda(ctx.subState, ctx.subHook)), @@ -689,7 +685,7 @@ export async function buildSubCompleteAccounts( ), ro(SYSTEM_PROGRAM_ID), ], - requiredExtraSigner: p.provider, + requiredExtraSigner: null, }; } @@ -700,12 +696,12 @@ export async function buildSubCompleteAccounts( */ export async function buildSubRejectAccounts( ctx: SubscriptionContext, - p: { jobId: bigint; provider: Address } + p: { jobPda: Address; provider: Address } ): Promise { return [ w(await mh.hookStatePda(ctx.subHook)), ro(SYSVAR_INSTRUCTIONS_ID), - w(await mh.proposedTermsPda(ctx.subHook, p.jobId)), + w(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), w(p.provider), ]; } @@ -719,7 +715,6 @@ export async function buildSubRejectAccounts( export async function buildRejectFanOut( ctx: RouterContext, p: { - jobId: bigint; jobPda: Address; /** Rent refund recipient; must equal the proposed_terms provider. */ provider: Address; @@ -740,7 +735,7 @@ export async function buildRejectFanOut( const subSlice: AccountMetaLike[] = [ w(subHookState), ro(SYSVAR_INSTRUCTIONS_ID), - w(await mh.proposedTermsPda(ctx.subHook, p.jobId)), + w(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), w(p.provider), ]; @@ -749,8 +744,8 @@ export async function buildRejectFanOut( fundSlice = [ w(fundHookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobId)), - w(await mh.intentPda(ctx.fundHook, p.jobId, INTENT_KIND_ESCROW)), + ro(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobPda)), + w(await mh.intentPda(ctx.fundHook, p.jobPda, INTENT_KIND_ESCROW)), w(p.escrow.escrowVault), w(p.escrow.providerAta), ro(p.escrow.escrowAuthority), @@ -764,7 +759,7 @@ export async function buildRejectFanOut( fundSlice = [ w(fundHookState), ro(SYSVAR_INSTRUCTIONS_ID), - ro(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobId)), + ro(await mh.providerEscrowIntentIdPda(ctx.fundHook, p.jobPda)), ]; } @@ -774,7 +769,7 @@ export async function buildRejectFanOut( { accountCount: fundSlice.length, params: new Uint8Array(0) }, ]), extraAccounts: [ - ...(await routerPrefixAccounts(ctx, p.jobId)), + ...(await routerPrefixAccounts(ctx, p.jobPda)), ...(await hookFraming(ctx, ctx.subHook)), ...subSlice, ...(await hookFraming(ctx, ctx.fundHook)), From e6209d43b83c673446d98f17443b9279da102285 Mon Sep 17 00:00:00 2001 From: ariessa Date: Tue, 25 Aug 2026 05:41:04 +0800 Subject: [PATCH 3/3] fix: reject bugs --- src/clients/solanaAcpClient.ts | 32 ++++++++++++++++++++++++++------ src/core/solana/routerLayout.ts | 33 ++++++++++++++++++++++----------- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/clients/solanaAcpClient.ts b/src/clients/solanaAcpClient.ts index 3061448..7048f0b 100644 --- a/src/clients/solanaAcpClient.ts +++ b/src/clients/solanaAcpClient.ts @@ -104,7 +104,7 @@ import { getSetBudgetInstruction } from "../core/solana/generated/acp/instructio import { getFundInstruction } from "../core/solana/generated/acp/instructions/fund.js"; import { getSubmitInstructionAsync } from "../core/solana/generated/acp/instructions/submit.js"; import { getCompleteInstructionAsync } from "../core/solana/generated/acp/instructions/complete.js"; -import { getRejectInstructionAsync } from "../core/solana/generated/acp/instructions/reject.js"; +import { getRejectInstruction } from "../core/solana/generated/acp/instructions/reject.js"; import { getBatchConfigureHooksInstructionAsync } from "../core/solana/generated/multi-hook-router/instructions/batchConfigureHooks.js"; import { getJobCreatedDecoder } from "../core/solana/generated/acp/types/jobCreated.js"; import { fetchMaybeProviderEscrowIntentId } from "../core/solana/generated/fund-transfer-hook/accounts/providerEscrowIntentId.js"; @@ -1461,7 +1461,14 @@ export class SolanaAcpClient extends BaseAcpClient { } } - const ix = await getRejectInstructionAsync( + // Sync builder, deliberately not Async: the Async variant auto-derives a + // real vault PDA whenever this is omitted (findVaultPda), regardless of + // whether the job was ever funded — on a never-funded job that reverts + // AccountNotInitialized instead of taking the program's None-account + // convention (its own program id, read-only, as the sentinel — which is + // what an omitted account correctly resolves to here). + const TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address; + const ix = getRejectInstruction( { caller: signer, job: jobPda, @@ -1470,6 +1477,7 @@ export class SolanaAcpClient extends BaseAcpClient { ...(vaultAuthority ? { vaultAuthority } : {}), ...(clientTokenAccount ? { clientTokenAccount } : {}), sponsor: acpState.data.sponsor, + tokenProgram: TOKEN_PROGRAM_ID, ...(hookAddress ? { hookProgram: hookAddress } : {}), ...(hookAddress ? { hookWhitelist: await this.deriveHookWhitelistPda(chainId, hookAddress) } @@ -2403,10 +2411,15 @@ export class SolanaAcpClient extends BaseAcpClient { const sctx = subscriptionContext(chainId); const extraAccounts = await buildSubRejectAccounts(sctx, { jobPda: s.jobPda, - provider: s.job.data.provider, + sponsor: s.sponsor, }); - const ix = await getRejectInstructionAsync( + // Sync builder, deliberately not Async — see the plain reject() path: + // Async auto-derives a real (non-existent) vault PDA when omitted instead + // of the program's None-account sentinel, breaking reject() on a job that + // was never funded. + const TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address; + const ix = getRejectInstruction( { caller: s.signer, job: s.jobPda, @@ -2417,6 +2430,7 @@ export class SolanaAcpClient extends BaseAcpClient { ? { clientTokenAccount: s.clientTokenAccount } : {}), sponsor: s.sponsor, + tokenProgram: TOKEN_PROGRAM_ID, hookProgram: sctx.subHook, hookWhitelist: await this.deriveHookWhitelistPda(chainId, sctx.subHook), reason: s.reasonBytes, @@ -3174,11 +3188,16 @@ export class SolanaAcpClient extends BaseAcpClient { const fanOut = await buildRejectFanOut(ctx, { jobPda: s.jobPda, - provider: job.data.provider, + sponsor: s.sponsor, escrow, }); - const ix = await getRejectInstructionAsync( + // Sync builder, deliberately not Async — see the plain reject() path: + // Async auto-derives a real (non-existent) vault PDA when omitted instead + // of the program's None-account sentinel, breaking reject() on a job that + // was never funded. + const TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address; + const ix = getRejectInstruction( { caller: s.signer, job: s.jobPda, @@ -3189,6 +3208,7 @@ export class SolanaAcpClient extends BaseAcpClient { ? { clientTokenAccount: s.clientTokenAccount } : {}), sponsor: s.sponsor, + tokenProgram: TOKEN_PROGRAM_ID, hookProgram: ctx.router, hookWhitelist: await this.deriveHookWhitelistPda(chainId, ctx.router), reason: s.reasonBytes, diff --git a/src/core/solana/routerLayout.ts b/src/core/solana/routerLayout.ts index b13a139..c0dc5e9 100644 --- a/src/core/solana/routerLayout.ts +++ b/src/core/solana/routerLayout.ts @@ -691,24 +691,31 @@ export async function buildSubCompleteAccounts( /** * reject: post_reject closes proposed_terms — remaining [sysvar, - * proposed_terms(w), rent_recipient(w)]. The recipient must equal the - * proposing provider but does NOT sign, so reject stays a prepared builder. + * proposed_terms(w), acp_state(ro), sponsor(w)]. The recipient must equal + * acp_state.sponsor, matching close_proposed_terms/close_job_hook_accounts: + * under gas sponsorship the provider's wallet holds only prefunded sponsor + * lamports, so refunding it (the older "pays the proposing provider" design) + * would let the provider farm the sponsor by proposing and rejecting in a + * loop. The recipient does NOT sign, so reject stays a prepared builder. A + * missing proposed_terms PDA still no-ops safely with this same account set + * (post_reject returns before checking length once it sees the PDA unset). */ export async function buildSubRejectAccounts( ctx: SubscriptionContext, - p: { jobPda: Address; provider: Address } + p: { jobPda: Address; sponsor: Address } ): Promise { return [ w(await mh.hookStatePda(ctx.subHook)), ro(SYSVAR_INSTRUCTIONS_ID), w(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), - w(p.provider), + ro(await mh.acpStatePda(ctx.acp)), + w(p.sponsor), ]; } /** * reject fan-out. The subscription hook closes the proposed_terms PDA and - * refunds its rent to the provider — post_reject requires the recipient + * refunds its rent to acp_state.sponsor — post_reject requires the recipient * writable but NOT as a signer, so reject stays a single-signer prepared * builder. The fund hook returns the escrow bond to the provider. */ @@ -716,8 +723,8 @@ export async function buildRejectFanOut( ctx: RouterContext, p: { jobPda: Address; - /** Rent refund recipient; must equal the proposed_terms provider. */ - provider: Address; + /** proposed_terms rent refund recipient; must equal acp_state.sponsor. */ + sponsor: Address; escrow: { /** Escrow return destination (the provider's ATA in the escrow mint). */ providerAta: Address; @@ -729,14 +736,18 @@ export async function buildRejectFanOut( const subHookState = await mh.hookStatePda(ctx.subHook); const fundHookState = await mh.hookStatePda(ctx.fundHook); - // post_reject remaining: [sysvar, proposed_terms(w), rent_recipient(w)]; - // hook_state precedes as the named account. Safe when no terms exist — - // the hook no-ops on a missing proposed_terms PDA. + // post_reject remaining: [sysvar, proposed_terms(w), acp_state(ro), + // sponsor(w)]; hook_state precedes as the named account. Safe when no + // terms exist — the hook no-ops on a missing proposed_terms PDA once it + // sees the PDA unset, before checking this account count. See + // buildSubRejectAccounts (the standalone equivalent) for why the recipient + // is the sponsor, not the proposing provider. const subSlice: AccountMetaLike[] = [ w(subHookState), ro(SYSVAR_INSTRUCTIONS_ID), w(await mh.proposedTermsPda(ctx.subHook, p.jobPda)), - w(p.provider), + ro(await mh.acpStatePda(ctx.acp)), + w(p.sponsor), ]; let fundSlice: AccountMetaLike[];