From 856e831162deb87ecaed9c0f07f1f0914231a24f Mon Sep 17 00:00:00 2001 From: Alejo Amiras Date: Sat, 4 Apr 2026 10:56:10 -0300 Subject: [PATCH] fix: register SubscriptionFPC in swap flow for sponsored swaps The SubscriptionFPC was only registered during the drip/claim flow (registerDripContracts), but sponsored swaps also need it. When a new account went directly to swap without claiming first, the wallet couldn't find the SubscriptionFPC artifact in PXE, causing "Contract artifact not found for class 0x0b1adf2a...". Now registerSwapContracts also checks and registers the SubscriptionFPC if configured, following the same pattern as registerDripContracts: - Checks metadata first to skip if already registered - Throws if configured FPC is not found on-chain (fail-fast, consistent with drip) - Registers with artifact and secret key in the same batch --- src/services/contractService.ts | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/services/contractService.ts b/src/services/contractService.ts index 9d50cd7..ec3d4ce 100644 --- a/src/services/contractService.ts +++ b/src/services/contractService.ts @@ -54,12 +54,21 @@ export async function registerSwapContracts( const { TokenContract, TokenContractArtifact } = await import('@aztec/noir-contracts.js/Token'); const { AMMContract, AMMContractArtifact } = await import('../../contracts/target/AMM'); + // Determine subscription FPC for sponsored swaps + const subFPC = network.subscriptionFPC; + const fpcAddress = subFPC ? AztecAddressClass.fromString(subFPC.address) : undefined; + // Check which contracts are already registered - const [ammMetadata, gregoCoinMetadata, gregoCoinPremiumMetadata] = await wallet.batch([ + const metadataChecks: { name: 'getContractMetadata'; args: [AztecAddress] }[] = [ { name: 'getContractMetadata', args: [ammAddress] }, { name: 'getContractMetadata', args: [gregoCoinAddress] }, { name: 'getContractMetadata', args: [gregoCoinPremiumAddress] }, - ]); + ]; + if (fpcAddress) { + metadataChecks.push({ name: 'getContractMetadata', args: [fpcAddress] }); + } + const metadataResults = await wallet.batch(metadataChecks); + const [ammMetadata, gregoCoinMetadata, gregoCoinPremiumMetadata] = metadataResults; // Reconstruct contract instances for unregistered contracts const [ammInstance, gregoCoinInstance, gregoCoinPremiumInstance] = await Promise.all([ @@ -100,6 +109,23 @@ export async function registerSwapContracts( registrationBatch.push({ name: 'registerContract', args: [gregoCoinPremiumInstance, undefined, undefined] }); } + // Register subscription FPC for sponsored swaps (if configured and not yet registered) + if (subFPC && fpcAddress) { + const fpcMetadata = metadataResults[3]; + if (!fpcMetadata?.result?.instance) { + const instance = await node.getContract(fpcAddress); + if (!instance) { + throw new Error(`Subscription FPC at ${subFPC.address} not found on-chain`); + } + const secretKey = Fr.fromString(subFPC.secretKey); + const { SubscriptionFPCContractArtifact } = await import('@gregojuice/contracts/artifacts/SubscriptionFPC'); + registrationBatch.push({ + name: 'registerContract', + args: [instance, SubscriptionFPCContractArtifact, secretKey], + }); + } + } + // Only call batch if there are contracts to register if (registrationBatch.length > 0) { await wallet.batch(registrationBatch);