chore(slush): read pool, package and version ids from the registry - #97
Conversation
…stry The WAL locked-loop admin functions hardcoded the call-target package id, the pool object id and the shared Version object, all of which move on every slush upgrade or redeploy. They now come from the pool registry (`/public/config`) via the `ALPHALEND-SLUSH-WAL-SINGLE-LOOP` label, so an upgrade needs no SDK change. `version_object_id` was already in the registry payload but was dropped by the `SlushSingleAssetLooping` branch of `parsePoolLabelEntry`; it is now parsed into an optional `versionId` (optional so hand-written label fixtures stay valid), and `addExternalRewardsWalLockedTxb` throws when it is missing rather than falling back to a possibly stale constant. The two remaining `ADMIN` ids stay hardcoded on purpose: they name types (`distributor::RebalanceCap`, `alphalend_slush_pool::AdminCap`), and a Sui type tag carries the id of the package version that *defined* the struct, which never changes on upgrade. They also can't be derived from a neighbouring type or event string — the current slush package spans six defining ids — and a wrong type makes the owned-object query return nothing, surfacing as a bogus "no cap found" error. Documented that on the constant and at both call sites.
SlushLending and SlushSingleAssetLooping now pass instead of , so a slush upgrade needs no SDK change. maps for both types and the label fixtures carry it.
jangid
left a comment
There was a problem hiding this comment.
operate.md: clean — CI green. Registry-miss and staleness semantics are fail-safe (named throws on missing label/versionId; a stale cached packageId aborts on-chain within the 5-min TTL — strictly better than a hardcoded id needing an SDK release), first-package-id type-tag discipline is correct, and the live /public/config was verified to serve version_object_id for all slush pools, so there is no deployment-ordering risk. Non-blocking notes: src/admin/README.md says versionId is optional on SlushSingleAssetLoopingPoolLabel but the interface declares it required (making the guard in addExternalRewardsWalLockedTxb dead per the type system) — fix one side; VERSIONS.SLUSH/SLUSH_LOOP are now dead exports worth a deprecation note; TEST_POOLS fixtures 2-3 pair an old call-target package with the current Version object and would abort if used. Approvals: 2/2 — at threshold.
What
Slush code paths no longer hardcode values that move on every contract upgrade or redeploy. They now come from the pool registry (
GET {apiBaseUrl}/public/config→StrategyContext.getPoolLabels()).Admin (
src/admin/slushAdmin.ts) — three values resolved from theALPHALEND-SLUSH-WAL-SINGLE-LOOPlabel instead of constants:ADMIN.ALPHA_SLUSH_LATEST_PACKAGE_IDlabel.packageIdADMIN.ALPHA_SLUSH_WAL_LOOP_POOL_IDlabel.poolIdVERSIONS.SLUSHlabel.versionIdBoth
ADMINconstants are deleted —ADMINis internal (not exported fromsrc/index.ts) and nothing else referenced them.Strategies (
slushLending.ts,slushSingleAssetLooping.ts) — everytx.object(VERSIONS.SLUSH)becomestx.object(this.poolLabel.versionId). This matters beyond tidiness: version objects are per-pool, not per-strategy-type.ALPHALEND-SLUSH-STSUI-LOOPandALPHALEND-SLUSH-STSUI-SUI-LOOPsit on different ones (0x1140f0b4…vs0x146d1785…), so a single constant cannot stay correct as pools are added.Parser (
strategyContext.ts) —version_object_idwas already in the registry payload but was dropped for both slush types; it is now mapped toversionId, required on both labels, and theTEST_POOLSfixtures carry it (values read off chain per fixture's package lineage, not invented).Why the two remaining
ADMINids stay hardcodedALPHA_FIRST_PACKAGE_IDandALPHA_SLUSH_FIRST_PACKAGE_IDname types (distributor::RebalanceCap,alphalend_slush_pool::AdminCap), not call targets. A Sui type tag carries the id of the package version that first defined the struct, and that id never changes on upgrade — so they need no maintenance and deliberately do not come from the API.They also cannot be derived from the package prefix of a neighbouring type or event string. One package holds types defined across many upgrades; the type-origin table of the current slush package
0x8b7c85…a409spans six defining ids:Deriving the AdminCap address from the WAL pool's own event type would yield
0x3221f3…, andlistOwnedObjectswith a wrong type returns empty — surfacing as a bogus "No AdminCap found for address X" permissions error rather than a config error. Both call sites and the constant document this.Verification
tsc --noEmit,npm run build, ESLint and Prettier are clean.Behaviour is unchanged: every pool that previously received
VERSIONS.SLUSHresolves to that same object (0x146d1785…) from the registry today.Beyond that, deposit and withdraw were built and simulated on mainnet for all six
SlushLending/SlushSingleAssetLoopingpools — 12/12 succeeded, with the registryversionIdconfirmed present in each transaction's resolved inputs. Senders are discovered on chain (position-cap holders with a non-empty position for withdraws, asset holders for deposits); nothing is signed or executed.The green run is not vacuous: forcing a wrong-but-real
version::Versionobject (from the SlushLooping lineage) makes all 12 simulations fail withCommandArgumentError … TypeMismatchonargIdx 0— the version argument. So the simulation genuinely validates the object being sourced from the registry.Reproduce with
scripts/slushDryRun.ts(no private key or.envneeded):It also warns when the registry contains a slush pool its list does not cover, so a newly added pool surfaces instead of going silently untested.
Notes / follow-ups (not in this PR)
VERSIONS.ALPHA_EMBER(used byadmin/alphaVault.ts) and theVERSIONS/ALPHA_VERSIONSentries used by the LP, Lending and Looping strategies are likewiseversion_object_idin the registry payload, dropped by the remainingparsePoolLabelEntrybranches. Same treatment applies; it touches those strategy classes. Note LP calls take two version objects — the label supplies the pool's own, the second is a shared base version.PACKAGE_IDS.ALPHAFI_RECEIPTdoes double duty: thecreate_alphafi_receipt_v2call target and the defining id insideALPHAFI_TRANSFER_REQUEST_TYPE/..._KEY_TYPE. Both are correct today (the lineage has two versions: v10x18533807…definesAlphaFiReceipt, v20x8113301d…definesTransferRequest/TransferRequestKey, and v2 is latest), but on the next upgrade the call target must move to v3 while the type constants must stay at v2 — one constant cannot do both.@pythnetwork/pyth-sui-jsis CJS andrequire()s@mysten/sui's ESM build, so importingStrategyContextfails withERR_REQUIRE_ESMunder plain Node and Jest.tsxhandles it, which is why the dry-run script runs fine.