From 8d3259300c67fe716d2bbba63194f7bbc2d5b386 Mon Sep 17 00:00:00 2001 From: "John McClure (pickleback)" Date: Mon, 22 Jun 2026 20:52:36 -0500 Subject: [PATCH] fix(sdk): sync AppController ABI with deployed v1.5.1 (add containerPolicy) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bundled AppController ABI was stale relative to the deployed v1.5.1 contract. The on-chain `Release` struct gained a 4th field, `ContainerPolicy containerPolicy`, but the CLI's ABI still described the 3-field struct. This shifted the computed function selector for `upgradeApp`/`createApp`/`createAppWithIsolatedBilling` (e.g. upgradeApp 0xd80a956b vs deployed 0xe71ad404), so the AppController dispatcher found no match and reverted with empty data — surfaced as "EstimateGasExecutionError: execution reverted for an unknown reason" during gas estimation. Every app deploy/upgrade was broken. Fixes: - Replace the bundled ABI with the authoritative compiled v1.5.1 ABI. This also restores the missing `pendingReleaseBlockNumber` field in the AppConfig return tuple (a latent decode bug in getApps*) and adds the createEmptyApp/confirmUpgrade/getBillingAccount entries. - Add a ContainerPolicy type and EMPTY_CONTAINER_POLICY to the Release type, set at both Release construction sites, and pass it through the deploy + upgrade encoders. containerPolicy is only emitted on the release event and consumed off-chain, so an empty policy preserves the CLI's existing behavior. Verified end-to-end against sepolia-dev: gas estimation now succeeds and an upgrade tx mines with status 1, emitting ReleasePublished + AppUpgraded. --- .../src/client/common/abis/AppController.json | 489 +++++++++++++++--- .../sdk/src/client/common/contract/caller.ts | 2 + .../sdk/src/client/common/release/prebuilt.ts | 2 + .../sdk/src/client/common/release/prepare.ts | 3 +- packages/sdk/src/client/common/types/index.ts | 28 + 5 files changed, 457 insertions(+), 67 deletions(-) diff --git a/packages/sdk/src/client/common/abis/AppController.json b/packages/sdk/src/client/common/abis/AppController.json index 4608a2ed..9f1f8645 100644 --- a/packages/sdk/src/client/common/abis/AppController.json +++ b/packages/sdk/src/client/common/abis/AppController.json @@ -10,27 +10,27 @@ { "name": "_permissionController", "type": "address", - "internalType": "contractIPermissionController" + "internalType": "contract IPermissionController" }, { "name": "_releaseManager", "type": "address", - "internalType": "contractIReleaseManager" + "internalType": "contract IReleaseManager" }, { "name": "_computeAVSRegistrar", "type": "address", - "internalType": "contractIComputeAVSRegistrar" + "internalType": "contract IComputeAVSRegistrar" }, { "name": "_computeOperator", "type": "address", - "internalType": "contractIComputeOperator" + "internalType": "contract IComputeOperator" }, { "name": "_appBeacon", "type": "address", - "internalType": "contractIBeacon" + "internalType": "contract IBeacon" } ], "stateMutability": "nonpayable" @@ -56,7 +56,7 @@ { "name": "", "type": "address", - "internalType": "contractIBeacon" + "internalType": "contract IBeacon" } ], "stateMutability": "view" @@ -104,7 +104,7 @@ { "name": "", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "stateMutability": "view" @@ -117,7 +117,7 @@ { "name": "", "type": "address", - "internalType": "contractIComputeAVSRegistrar" + "internalType": "contract IComputeAVSRegistrar" } ], "stateMutability": "view" @@ -130,11 +130,24 @@ { "name": "", "type": "address", - "internalType": "contractIComputeOperator" + "internalType": "contract IComputeOperator" } ], "stateMutability": "view" }, + { + "type": "function", + "name": "confirmUpgrade", + "inputs": [ + { + "name": "app", + "type": "address", + "internalType": "contract IApp" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "createApp", @@ -147,17 +160,17 @@ { "name": "release", "type": "tuple", - "internalType": "structIAppController.Release", + "internalType": "struct IAppController.Release", "components": [ { "name": "rmsRelease", "type": "tuple", - "internalType": "structIReleaseManagerTypes.Release", + "internalType": "struct IReleaseManagerTypes.Release", "components": [ { "name": "artifacts", "type": "tuple[]", - "internalType": "structIReleaseManagerTypes.Artifact[]", + "internalType": "struct IReleaseManagerTypes.Artifact[]", "components": [ { "name": "digest", @@ -187,6 +200,62 @@ "name": "encryptedEnv", "type": "bytes", "internalType": "bytes" + }, + { + "name": "containerPolicy", + "type": "tuple", + "internalType": "struct IAppController.ContainerPolicy", + "components": [ + { + "name": "args", + "type": "string[]", + "internalType": "string[]" + }, + { + "name": "cmdOverride", + "type": "string[]", + "internalType": "string[]" + }, + { + "name": "env", + "type": "tuple[]", + "internalType": "struct IAppController.EnvVar[]", + "components": [ + { + "name": "key", + "type": "string", + "internalType": "string" + }, + { + "name": "value", + "type": "string", + "internalType": "string" + } + ] + }, + { + "name": "envOverride", + "type": "tuple[]", + "internalType": "struct IAppController.EnvVar[]", + "components": [ + { + "name": "key", + "type": "string", + "internalType": "string" + }, + { + "name": "value", + "type": "string", + "internalType": "string" + } + ] + }, + { + "name": "restartPolicy", + "type": "string", + "internalType": "string" + } + ] } ] } @@ -195,7 +264,7 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "stateMutability": "nonpayable" @@ -212,17 +281,17 @@ { "name": "release", "type": "tuple", - "internalType": "structIAppController.Release", + "internalType": "struct IAppController.Release", "components": [ { "name": "rmsRelease", "type": "tuple", - "internalType": "structIReleaseManagerTypes.Release", + "internalType": "struct IReleaseManagerTypes.Release", "components": [ { "name": "artifacts", "type": "tuple[]", - "internalType": "structIReleaseManagerTypes.Artifact[]", + "internalType": "struct IReleaseManagerTypes.Artifact[]", "components": [ { "name": "digest", @@ -252,6 +321,62 @@ "name": "encryptedEnv", "type": "bytes", "internalType": "bytes" + }, + { + "name": "containerPolicy", + "type": "tuple", + "internalType": "struct IAppController.ContainerPolicy", + "components": [ + { + "name": "args", + "type": "string[]", + "internalType": "string[]" + }, + { + "name": "cmdOverride", + "type": "string[]", + "internalType": "string[]" + }, + { + "name": "env", + "type": "tuple[]", + "internalType": "struct IAppController.EnvVar[]", + "components": [ + { + "name": "key", + "type": "string", + "internalType": "string" + }, + { + "name": "value", + "type": "string", + "internalType": "string" + } + ] + }, + { + "name": "envOverride", + "type": "tuple[]", + "internalType": "struct IAppController.EnvVar[]", + "components": [ + { + "name": "key", + "type": "string", + "internalType": "string" + }, + { + "name": "value", + "type": "string", + "internalType": "string" + } + ] + }, + { + "name": "restartPolicy", + "type": "string", + "internalType": "string" + } + ] } ] } @@ -260,7 +385,45 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "createEmptyApp", + "inputs": [ + { + "name": "salt", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "app", + "type": "address", + "internalType": "contract IApp" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "createEmptyAppWithIsolatedBilling", + "inputs": [ + { + "name": "salt", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "app", + "type": "address", + "internalType": "contract IApp" } ], "stateMutability": "nonpayable" @@ -299,50 +462,50 @@ }, { "type": "function", - "name": "getBillingType", + "name": "getAppCreator", "inputs": [ { "name": "app", "type": "address", - "internalType": "address" + "internalType": "contract IApp" } ], "outputs": [ { "name": "", - "type": "uint8", - "internalType": "uint8" + "type": "address", + "internalType": "address" } ], "stateMutability": "view" }, { "type": "function", - "name": "getAppCreator", + "name": "getAppLatestReleaseBlockNumber", "inputs": [ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "outputs": [ { "name": "", - "type": "address", - "internalType": "address" + "type": "uint32", + "internalType": "uint32" } ], "stateMutability": "view" }, { "type": "function", - "name": "getAppLatestReleaseBlockNumber", + "name": "getAppOperatorSetId", "inputs": [ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "outputs": [ @@ -356,12 +519,12 @@ }, { "type": "function", - "name": "getAppOperatorSetId", + "name": "getAppPendingReleaseBlockNumber", "inputs": [ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "outputs": [ @@ -380,14 +543,14 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "outputs": [ { "name": "", "type": "uint8", - "internalType": "enumIAppController.AppStatus" + "internalType": "enum IAppController.AppStatus" } ], "stateMutability": "view" @@ -411,12 +574,12 @@ { "name": "apps", "type": "address[]", - "internalType": "contractIApp[]" + "internalType": "contract IApp[]" }, { "name": "appConfigsMem", "type": "tuple[]", - "internalType": "structIAppController.AppConfig[]", + "internalType": "struct IAppController.AppConfig[]", "components": [ { "name": "creator", @@ -433,10 +596,15 @@ "type": "uint32", "internalType": "uint32" }, + { + "name": "pendingReleaseBlockNumber", + "type": "uint32", + "internalType": "uint32" + }, { "name": "status", "type": "uint8", - "internalType": "enumIAppController.AppStatus" + "internalType": "enum IAppController.AppStatus" } ] } @@ -467,12 +635,12 @@ { "name": "apps", "type": "address[]", - "internalType": "contractIApp[]" + "internalType": "contract IApp[]" }, { "name": "appConfigsMem", "type": "tuple[]", - "internalType": "structIAppController.AppConfig[]", + "internalType": "struct IAppController.AppConfig[]", "components": [ { "name": "creator", @@ -489,10 +657,15 @@ "type": "uint32", "internalType": "uint32" }, + { + "name": "pendingReleaseBlockNumber", + "type": "uint32", + "internalType": "uint32" + }, { "name": "status", "type": "uint8", - "internalType": "enumIAppController.AppStatus" + "internalType": "enum IAppController.AppStatus" } ] } @@ -523,12 +696,12 @@ { "name": "apps", "type": "address[]", - "internalType": "contractIApp[]" + "internalType": "contract IApp[]" }, { "name": "appConfigsMem", "type": "tuple[]", - "internalType": "structIAppController.AppConfig[]", + "internalType": "struct IAppController.AppConfig[]", "components": [ { "name": "creator", @@ -545,10 +718,15 @@ "type": "uint32", "internalType": "uint32" }, + { + "name": "pendingReleaseBlockNumber", + "type": "uint32", + "internalType": "uint32" + }, { "name": "status", "type": "uint8", - "internalType": "enumIAppController.AppStatus" + "internalType": "enum IAppController.AppStatus" } ] } @@ -579,12 +757,12 @@ { "name": "apps", "type": "address[]", - "internalType": "contractIApp[]" + "internalType": "contract IApp[]" }, { "name": "appConfigsMem", "type": "tuple[]", - "internalType": "structIAppController.AppConfig[]", + "internalType": "struct IAppController.AppConfig[]", "components": [ { "name": "creator", @@ -601,16 +779,59 @@ "type": "uint32", "internalType": "uint32" }, + { + "name": "pendingReleaseBlockNumber", + "type": "uint32", + "internalType": "uint32" + }, { "name": "status", "type": "uint8", - "internalType": "enumIAppController.AppStatus" + "internalType": "enum IAppController.AppStatus" } ] } ], "stateMutability": "view" }, + { + "type": "function", + "name": "getBillingAccount", + "inputs": [ + { + "name": "app", + "type": "address", + "internalType": "contract IApp" + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getBillingType", + "inputs": [ + { + "name": "app", + "type": "address", + "internalType": "contract IApp" + } + ], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "enum IAppController.BillingType" + } + ], + "stateMutability": "view" + }, { "type": "function", "name": "getMaxActiveAppsPerUser", @@ -677,7 +898,7 @@ { "name": "", "type": "address", - "internalType": "contractIPermissionController" + "internalType": "contract IPermissionController" } ], "stateMutability": "view" @@ -690,7 +911,7 @@ { "name": "", "type": "address", - "internalType": "contractIReleaseManager" + "internalType": "contract IReleaseManager" } ], "stateMutability": "view" @@ -733,7 +954,7 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "outputs": [], @@ -746,7 +967,7 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "outputs": [], @@ -764,7 +985,7 @@ { "name": "apps", "type": "address[]", - "internalType": "contractIApp[]" + "internalType": "contract IApp[]" } ], "outputs": [], @@ -777,7 +998,7 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "outputs": [], @@ -790,7 +1011,7 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "outputs": [], @@ -803,7 +1024,7 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" }, { "name": "metadataURI", @@ -821,22 +1042,22 @@ { "name": "app", "type": "address", - "internalType": "contractIApp" + "internalType": "contract IApp" }, { "name": "release", "type": "tuple", - "internalType": "structIAppController.Release", + "internalType": "struct IAppController.Release", "components": [ { "name": "rmsRelease", "type": "tuple", - "internalType": "structIReleaseManagerTypes.Release", + "internalType": "struct IReleaseManagerTypes.Release", "components": [ { "name": "artifacts", "type": "tuple[]", - "internalType": "structIReleaseManagerTypes.Artifact[]", + "internalType": "struct IReleaseManagerTypes.Artifact[]", "components": [ { "name": "digest", @@ -866,6 +1087,62 @@ "name": "encryptedEnv", "type": "bytes", "internalType": "bytes" + }, + { + "name": "containerPolicy", + "type": "tuple", + "internalType": "struct IAppController.ContainerPolicy", + "components": [ + { + "name": "args", + "type": "string[]", + "internalType": "string[]" + }, + { + "name": "cmdOverride", + "type": "string[]", + "internalType": "string[]" + }, + { + "name": "env", + "type": "tuple[]", + "internalType": "struct IAppController.EnvVar[]", + "components": [ + { + "name": "key", + "type": "string", + "internalType": "string" + }, + { + "name": "value", + "type": "string", + "internalType": "string" + } + ] + }, + { + "name": "envOverride", + "type": "tuple[]", + "internalType": "struct IAppController.EnvVar[]", + "components": [ + { + "name": "key", + "type": "string", + "internalType": "string" + }, + { + "name": "value", + "type": "string", + "internalType": "string" + } + ] + }, + { + "name": "restartPolicy", + "type": "string", + "internalType": "string" + } + ] } ] } @@ -906,7 +1183,7 @@ "name": "app", "type": "address", "indexed": true, - "internalType": "contractIApp" + "internalType": "contract IApp" }, { "name": "operatorSetId", @@ -925,7 +1202,7 @@ "name": "app", "type": "address", "indexed": true, - "internalType": "contractIApp" + "internalType": "contract IApp" }, { "name": "metadataURI", @@ -944,7 +1221,7 @@ "name": "app", "type": "address", "indexed": true, - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "anonymous": false @@ -957,7 +1234,7 @@ "name": "app", "type": "address", "indexed": true, - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "anonymous": false @@ -970,7 +1247,7 @@ "name": "app", "type": "address", "indexed": true, - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "anonymous": false @@ -983,7 +1260,7 @@ "name": "app", "type": "address", "indexed": true, - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "anonymous": false @@ -996,7 +1273,7 @@ "name": "app", "type": "address", "indexed": true, - "internalType": "contractIApp" + "internalType": "contract IApp" } ], "anonymous": false @@ -1009,7 +1286,7 @@ "name": "app", "type": "address", "indexed": true, - "internalType": "contractIApp" + "internalType": "contract IApp" }, { "name": "rmsReleaseId", @@ -1021,17 +1298,17 @@ "name": "release", "type": "tuple", "indexed": false, - "internalType": "structIAppController.Release", + "internalType": "struct IAppController.Release", "components": [ { "name": "rmsRelease", "type": "tuple", - "internalType": "structIReleaseManagerTypes.Release", + "internalType": "struct IReleaseManagerTypes.Release", "components": [ { "name": "artifacts", "type": "tuple[]", - "internalType": "structIReleaseManagerTypes.Artifact[]", + "internalType": "struct IReleaseManagerTypes.Artifact[]", "components": [ { "name": "digest", @@ -1061,6 +1338,62 @@ "name": "encryptedEnv", "type": "bytes", "internalType": "bytes" + }, + { + "name": "containerPolicy", + "type": "tuple", + "internalType": "struct IAppController.ContainerPolicy", + "components": [ + { + "name": "args", + "type": "string[]", + "internalType": "string[]" + }, + { + "name": "cmdOverride", + "type": "string[]", + "internalType": "string[]" + }, + { + "name": "env", + "type": "tuple[]", + "internalType": "struct IAppController.EnvVar[]", + "components": [ + { + "name": "key", + "type": "string", + "internalType": "string" + }, + { + "name": "value", + "type": "string", + "internalType": "string" + } + ] + }, + { + "name": "envOverride", + "type": "tuple[]", + "internalType": "struct IAppController.EnvVar[]", + "components": [ + { + "name": "key", + "type": "string", + "internalType": "string" + }, + { + "name": "value", + "type": "string", + "internalType": "string" + } + ] + }, + { + "name": "restartPolicy", + "type": "string", + "internalType": "string" + } + ] } ] } @@ -1112,6 +1445,25 @@ ], "anonymous": false }, + { + "type": "event", + "name": "UpgradeConfirmed", + "inputs": [ + { + "name": "app", + "type": "address", + "indexed": true, + "internalType": "contract IApp" + }, + { + "name": "pendingReleaseBlockNumber", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + } + ], + "anonymous": false + }, { "type": "error", "name": "AccountHasActiveApps", @@ -1167,6 +1519,11 @@ "name": "MoreThanOneArtifact", "inputs": [] }, + { + "type": "error", + "name": "NoPendingUpgrade", + "inputs": [] + }, { "type": "error", "name": "SignatureExpired", diff --git a/packages/sdk/src/client/common/contract/caller.ts b/packages/sdk/src/client/common/contract/caller.ts index 0999f9f0..88f17d07 100644 --- a/packages/sdk/src/client/common/contract/caller.ts +++ b/packages/sdk/src/client/common/contract/caller.ts @@ -254,6 +254,7 @@ export async function prepareDeployBatch( }, publicEnv: bytesToHex(release.publicEnv) as Hex, encryptedEnv: bytesToHex(release.encryptedEnv) as Hex, + containerPolicy: release.containerPolicy, }; const functionName = options.billTo === "app" ? "createAppWithIsolatedBilling" : "createApp"; @@ -750,6 +751,7 @@ export async function prepareUpgradeBatch( }, publicEnv: bytesToHex(release.publicEnv) as Hex, encryptedEnv: bytesToHex(release.encryptedEnv) as Hex, + containerPolicy: release.containerPolicy, }; const upgradeData = encodeFunctionData({ diff --git a/packages/sdk/src/client/common/release/prebuilt.ts b/packages/sdk/src/client/common/release/prebuilt.ts index ca91bf89..bfa5e119 100644 --- a/packages/sdk/src/client/common/release/prebuilt.ts +++ b/packages/sdk/src/client/common/release/prebuilt.ts @@ -1,6 +1,7 @@ import { parseAndValidateEnvFile } from "../env/parser"; import { encryptRSAOAEPAndAES256GCM, getAppProtectedHeaders } from "../encryption/kms"; import { getKMSKeysForEnvironment } from "../utils/keys"; +import { EMPTY_CONTAINER_POLICY } from "../types"; import type { EnvironmentConfig, Logger, Release } from "../types"; export interface CreateReleaseFromImageDigestOptions { @@ -74,6 +75,7 @@ export async function createReleaseFromImageDigest( }, publicEnv: new Uint8Array(Buffer.from(JSON.stringify(publicEnv))), encryptedEnv: new Uint8Array(Buffer.from(encryptedEnvStr)), + containerPolicy: EMPTY_CONTAINER_POLICY, }; } diff --git a/packages/sdk/src/client/common/release/prepare.ts b/packages/sdk/src/client/common/release/prepare.ts index 59fbc70e..2cc954c3 100644 --- a/packages/sdk/src/client/common/release/prepare.ts +++ b/packages/sdk/src/client/common/release/prepare.ts @@ -14,7 +14,7 @@ import { REGISTRY_PROPAGATION_WAIT_SECONDS } from "../constants"; import { parseAndValidateEnvFile } from "../env/parser"; -import { Release, EnvironmentConfig, Logger, AppId } from "../types"; +import { Release, EnvironmentConfig, Logger, AppId, EMPTY_CONTAINER_POLICY } from "../types"; export interface PrepareReleaseOptions { dockerfilePath?: string; @@ -177,6 +177,7 @@ export async function prepareRelease( }, publicEnv: new Uint8Array(Buffer.from(JSON.stringify(publicEnv))), encryptedEnv: new Uint8Array(Buffer.from(encryptedEnvStr)), + containerPolicy: EMPTY_CONTAINER_POLICY, }; return { diff --git a/packages/sdk/src/client/common/types/index.ts b/packages/sdk/src/client/common/types/index.ts index c42a8d71..9f7165aa 100644 --- a/packages/sdk/src/client/common/types/index.ts +++ b/packages/sdk/src/client/common/types/index.ts @@ -246,6 +246,33 @@ export interface EnvironmentConfig { usdcCreditsAddress?: Address; } +/** + * Container execution policy carried on a release. The AppController does not + * read these fields on-chain — they are emitted on the AppUpgraded/AppCreated + * release event and consumed off-chain by the platform/operator. The CLI does + * not currently surface any of these knobs, so an empty policy is sent (see + * EMPTY_CONTAINER_POLICY). The field is still required because it is part of + * the AppController `Release` struct ABI; omitting it shifts the selector and + * the on-chain call reverts in the dispatcher. + */ +export interface ContainerPolicy { + args: string[]; + cmdOverride: string[]; + env: Array<{ key: string; value: string }>; + envOverride: Array<{ key: string; value: string }>; + restartPolicy: string; +} + +/** An empty container policy — preserves the CLI's pre-existing behavior of not + * overriding container args/cmd/env or setting a restart policy. */ +export const EMPTY_CONTAINER_POLICY: ContainerPolicy = { + args: [], + cmdOverride: [], + env: [], + envOverride: [], + restartPolicy: "", +}; + export interface Release { rmsRelease: { artifacts: Array<{ @@ -256,6 +283,7 @@ export interface Release { }; publicEnv: Uint8Array; // JSON bytes encryptedEnv: Uint8Array; // Encrypted string bytes + containerPolicy: ContainerPolicy; } export interface ParsedEnvironment {