Skip to content

Instantly share code, notes, and snippets.

@lucas-manuel
Last active June 26, 2026 16:22
Show Gist options
  • Select an option

  • Save lucas-manuel/d91a07cf908774663500830be35d6458 to your computer and use it in GitHub Desktop.

Select an option

Save lucas-manuel/d91a07cf908774663500830be35d6458 to your computer and use it in GitHub Desktop.
checklist.md

Prime Agent Spells Reviewer Checklist

Spell: feat: Spell 2026-07-02 (SC-1557) Reviewed at commit: f4f79f45934fa0626aa984c3ed564d908876d982 Stage: Review Stage


Development Stage

Preparation

Spell Description & Comments

  • Spell PR has clear description (forum post link, dependencies on Sky Core spell: None, reviewer notes).
  • Spell contracts have clear descriptions (@title, @notice, @author, forum + vote links in all six payload contracts).
  • Every significant action and parameter change are clearly commented in the code (e.g. // 1. Set USDC Interest Rate Model to Target Very Low Liquidity, // Withdraw USDS from Base to Ethereum, // Grant SETTER_ROLE to ALMProxyFreezable).
  • Every significant action has valid source URL:
  • Every parameter change is clearly commented with before/after values (diff file documents: optimalUsageRatio: 95% → 98%, variableRateSlope1Spread: 125 bps → 100 bps; APY constant inline comment explains calculation).

Proposed Changes

  • LIST every forum post proposing changes for this particular Prime Agent:
  • Verify spell content matches the combined scope of the forum posts listed above:
    • [ETH] SparkLend — Set USDC IRM to Target Very Low Liquidity → SparkEthereum_20260702._postExecute() L40
    • [Base] SLL — Remove Excess Liquidity (USDS + sUSDS → Ethereum) → SparkBase_20260702
    • [OP] SLL — Remove Excess Liquidity (USDS + sUSDS → Ethereum) → SparkOptimism_20260702
    • [Unichain] SLL — Remove Excess Liquidity (USDS + sUSDS → Ethereum) → SparkUnichain_20260702
    • [Arbitrum] Spark Savings — Deploy spUSDT → SparkArbitrumOne_20260702
    • [ETH] SLL — Enable USDT Bridging to Arbitrum → SparkEthereum_20260702._postExecute() L43–56
    • [Avalanche] SLL — Add Timelock and Guardian to Avalanche Governance Bridge → SparkAvalanche_20260702
    • [ETH] SparkLend — Claim SparkLend Reserves is a recurring item and is included in SparkPayloadEthereum
  • Verify forum posts contain all new addresses directly or indirectly used in the spell:
    • NEW_USDC_IRM = 0xDE99e49E9e42B1d8490C38926e6C9A79010e6eF2 - new IRM address referenced in diff file. Confirmed against the forum post.
    • SPARK_VAULT_V2_SPUSDT = 0x45d91340B3B7B96985A72b5c678F7D9e8D664b62 - spUSDT vault address. Confirmed against the forum post.
    • ALM_PROXY_FREEZABLE = 0x4eE67c8Db1BAa6ddE99d936C7D313B5d31e8fa38 - new Arbitrum ALM_PROXY_FREEZABLE. Confirmed against the forum post.
    • USDT_OFT (Arbitrum) = 0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92 - Arbitrum USDT0 OFT. Confirmed against the forum post.
    • USDT_OFT (Ethereum) = 0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee - Ethereum USDT OFT; Confirmed against the forum post.
    • LZ endpoint IDs: Ethereum 30101, Arbitrum 30110 - matches LayerZero documentation and test harness constants.
  • IF the Prime Agent spell introduces a major change that can affect external parties, suggest Governance Facilitators to set Core Spell office hours to true. - Not required for this spell (no structural protocol changes affecting external integrators).

Contract Structure & Code Quality

  • The only external non-view function in each spell contract is execute().

  • There are no methods that can modify contract state after deployment.

  • No unused imports, interfaces, methods, or variables.

  • All function visibility modifiers are explicitly declared.

  • No redundant code or commented-out functionality - Note: Constructor payload address comments in SparkEthereum_20260702 are intentional pre-deployment placeholders (standard repo pattern); must be replaced with deployed addresses before finalization.

  • Addresses are fetched from the relevant protocol's address registry where present (Ethereum.*, Arbitrum.*, Base.*, Optimism.*, Unichain.*, Avalanche.*); new contracts defined as internal constant with source.

  • LIST every address used in the spell defined as constant or fetched from registry:

    • [ETH] 0xDE99e49E9e42B1d8490C38926e6C9A79010e6eF2 NEW_USDC_IRM - https://github.com/sparkdotfi/spark-spells/pull/170/files (diff file confirms this is the new IRM, 0x2961... is the old one)
    • [ETH] 0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee USDT_OFT (Ethereum OFT) - confirmed
    • [ARB] 0x4eE67c8Db1BAa6ddE99d936C7D313B5d31e8fa38 ALM_PROXY_FREEZABLE - confirmed
    • [ARB] 0x45d91340B3B7B96985A72b5c678F7D9e8D664b62 SPARK_VAULT_V2_SPUSDT - confirmed
    • [ARB] 0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92 USDT_OFT (Arbitrum USDT0 OFT) - confirmed
    • All other addresses fetched from spark-address-registry (Ethereum.*, Arbitrum.*, Base.*, Optimism.*, Unichain.*, Avalanche.*)

StarGuard Execution

  • N/A — StarGuard module not applicable for this Spark spell (no isExecutable() required per prior spell convention).

On-boarding New Contracts

  • LIST every new contract present in the spell:
  • Source code is verified on Etherscan/Arbiscan
  • Source code matches corresponding audited GitHub source code
  • Compilation optimizations match deployment settings
  • Consistent license: SPDX-License-Identifier: AGPL-3.0 across all new contracts.
  • LIST every constructor argument (known):
    • NEW_USDC_IRM: optimalUsageRatio = 0.98e27, baseVariableBorrowRate = 0, variableRateSlope1Spread = 0.01e27, variableRateSlope2 = 0.15e27, rateSource = 0x57027B6262083E3aC3c8B2EB99f7e8005f669973 (sUSDS/SSR)
  • Access control for SparkVaultV2 (spUSDT):
    • Default admin: 0x65d946e533748A998B1f0E430803e39A6388f7a1 (Spark Executor on Arbitrum) - verified in test test_ARBITRUM_sll_spUSDT_vaultConfiguration via Arbitrum.SPARK_EXECUTOR.
    • SETTER_ROLE granted to ALM_PROXY_FREEZABLE in spell.
    • TAKER_ROLE granted to Arbitrum.ALM_PROXY in spell.
    • Contract deployer has no access.

Dependency Checks

Interfaces

  • No unused static interfaces.
  • Declared static interfaces (IALMProxyFreezableLike, ISparkVaultV2, ITokenBridgeLike, IExecutor, ILZEndpointExtended) are not present in standard libraries; defined locally as required.
  • Interfaces match the deployed contracts (functions used in spell calls are declared; verified via Octane summary and CodeRabbit analysis).
  • Each static interface declares only functions actually used in the spell code.

Variable Declarations

  • Every contract variable is declared as constant or immutable.
  • Every precision variable matches expected value:
    • 1e27 = RAY ✓
    • SIX_PCT_APY = 1.000000001847694957439350562e27 verified via inline comment bc -l: e(l(1.06)/(60*60*24*365))
  • LIST every variable using precision:
  • Rates are expressed correctly: uint256(50_000_000e6) / 1 days
  • Rates match their source (forum post and governance poll).
  • No timestamps in this spell.
  • TIMELOCK_DELAY = 3 days - matches forum post specification ✓

Deployment & Execution Security

  • No selfdestruct() operations in the spell.
  • No delegatecall() to untrusted contracts.
  • No use of tx.origin for authorization.
  • No external calls that could revert and fail the entire spell execution.
  • No loops with unbounded gas consumption.
  • No timestamp-dependent logic that could cause issues across the GSM delay.
  • All math operations are safe (Solidity 0.8.25 built-in overflow checks; no unchecked blocks on user-supplied values).
  • No unchecked return values from external calls (SafeERC20 used for token approvals in SparkArbitrumOne_20260702; doCall on ALMProxy handles reverts atomically).

Access Control

  • Spell execution cannot be front-run by malicious actors.
  • No privileged functions accessible by unauthorized users.
  • Temporary CONTROLLER role grant/revoke pattern in L2 liquidity removal spells is correct and atomic within a single transaction.

Parameter Changes & Protocol Integration

  • Prime Agent protocol invariants are maintained after spell execution.
  • All parameter changes use the appropriate helper functions where available (LISTING_ENGINE.POOL_CONFIGURATOR(), RateLimitHelpers, setUnlimitedRateLimitData, setRateLimitData).
  • Parameter changes match the forum post exactly (see table below).
  • Spell interacts correctly with existing protocol components.
  • Proper error handling for all external interactions.

Specification-to-Code Parameter Mapping:

Spec Item Contract Code Location Value Status
[ETH] USDC IRM kink: 95% → 98% SparkEthereum_20260702 NEW_USDC_IRM / diff 0.98e27
[ETH] USDC IRM slope1 spread: 125 → 100 bps SparkEthereum_20260702 NEW_USDC_IRM / diff 0.01e27
[ETH] USDC IRM baseRate: 0% (unchanged) SparkEthereum_20260702 diff 0
[ETH] USDC IRM slope2: 15% (unchanged) SparkEthereum_20260702 diff 0.15e27
[ETH] Deploy new IRM & repoint via setReserveInterestRateStrategyAddress SparkEthereum_20260702 L40 LISTING_ENGINE.POOL_CONFIGURATOR().setReserveInterestRateStrategyAddress(Ethereum.USDC, NEW_USDC_IRM)
[ETH] NEW_USDC_IRM address SparkEthereum_20260702 L26 0xDE99e49E9e42B1d8490C38926e6C9A79010e6eF2
[ETH] ETH→ARB USDT0 maxAmount: 5M USDT SparkEthereum_20260702 L53 5_000_000e6
[ETH] ETH→ARB USDT0 slope: 50M USDT/day SparkEthereum_20260702 L54 uint256(50_000_000e6) / 1 days
[ETH] ETH→ARB LZ endpoint: 30110 SparkEthereum_20260702 L28 30110
[ETH] Ethereum USDT OFT SparkEthereum_20260702 L27 0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee
[Base] Withdraw all USDS to Ethereum SparkBase_20260702 L39, L45–55 balanceOf(Base.ALM_PROXY)bridgeERC20To(...)
[Base] Withdraw all sUSDS to Ethereum SparkBase_20260702 L40, L58–68 Same pattern
[OP] Withdraw all USDS to Ethereum SparkOptimism_20260702 L39, L45–55 Same pattern as Base
[OP] Withdraw all sUSDS to Ethereum SparkOptimism_20260702 L40, L58–68 Same pattern
[Unichain] Withdraw all USDS to Ethereum SparkUnichain_20260702 L39, L45–55 Same pattern as Base
[Unichain] Withdraw all sUSDS to Ethereum SparkUnichain_20260702 L40, L58–68 Same pattern
[ARB] spUSDT vault address SparkArbitrumOne_20260702 L52 0x45d91340B3B7B96985A72b5c678F7D9e8D664b62
[ARB] Default admin: Spark Executor SparkArbitrumOne_20260702 pre-existing on vault Asserted in test via Arbitrum.SPARK_EXECUTOR
[ARB] Setter role → ALM Proxy Freezable SparkArbitrumOne_20260702 L109 vault.grantRole(vault.SETTER_ROLE(), ALM_PROXY_FREEZABLE)
[ARB] Taker role → ALM Proxy SparkArbitrumOne_20260702 L112 vault.grantRole(vault.TAKER_ROLE(), Arbitrum.ALM_PROXY)
[ARB] Max yield: 6% SparkArbitrumOne_20260702 L59, L73 SIX_PCT_APY = 1.000000001847694957439350562e27
[ARB] Current yield at launch: 0% SparkArbitrumOne_20260702 L72 minVsr: 1e27
[ARB] Supply cap: 250M USDT SparkArbitrumOne_20260702 L71 supplyCap: 250_000_000e6
[ARB] SLL take rate limit: unlimited SparkArbitrumOne_20260702 L124–129 setUnlimitedRateLimitData(LIMIT_SPARK_VAULT_TAKE, vault)
[ARB] SLL transferAssets rate limit: unlimited SparkArbitrumOne_20260702 L131–137 setUnlimitedRateLimitData(LIMIT_ASSET_TRANSFER, asset, vault)
[ARB] ARB→ETH USDT0 LZ route: unlimited SparkArbitrumOne_20260702 L81–94 setLayerZeroRecipient + setUnlimitedRateLimitData
[ARB] LZ_ENDPOINT_ETHEREUM = 30101 SparkArbitrumOne_20260702 L55 30101
[ARB] Arbitrum USDT0 OFT SparkArbitrumOne_20260702 L53 0x14E4A1B13bf7F943c8ff7C51fb60FA964A298D92
[AVA] Timelock delay: 3 days SparkAvalanche_20260702 L20, L23 TIMELOCK_DELAY = 3 days
[AVA] Guardian: ALM_OPS_MULTISIG SparkAvalanche_20260702 L25–28 grantRole(GUARDIAN_ROLE(), Avalanche.ALM_OPS_MULTISIG)

Testing

  • LIST each spell action and corresponding test:
    • [ETH] setReserveInterestRateStrategyAddress(Ethereum.USDC, NEW_USDC_IRM) → tested via test_ETHEREUM_sparkLend_usdcIRMUpdate (asserts new IRM address, optimalUsageRatio 98%, slope1 spread 100 bps, slope2 15%)
    • [ETH] setLayerZeroRecipient(LZ_ENDPOINT_ARBITRUM, Arbitrum.ALM_PROXY) → tested via test_ETHEREUM_sll_usdtBridgingToArbitrum
    • [ETH] setRateLimitData(LIMIT_LAYERZERO_TRANSFER, USDT_OFT, 30110, 5M, 50M/day) → tested via test_ETHEREUM_sll_usdtBridgingToArbitrum
    • [Base] bridgeERC20To(Base.USDS, Ethereum.USDS, Ethereum.ALM_PROXY, balance, ...) → tested via test_BASE_sll_excessLiquidityRemoval (asserts pre-execution balances non-zero, post-execution balances zero)
    • [Base] bridgeERC20To(Base.SUSDS, Ethereum.SUSDS, ...) → same test
    • [OP] USDS bridge → tested via test_OPTIMISM_sll_excessLiquidityRemoval
    • [OP] sUSDS bridge → same test
    • [Unichain] USDS bridge → tested via test_UNICHAIN_sll_excessLiquidityRemoval
    • [Unichain] sUSDS bridge → same test
    • [ARB] proxy.grantRole(ALLOCATOR_ROLE, ALM_RELAYER_MULTISIG) → tested via test_ARBITRUM_ALMProxyFreezableConfiguration
    • [ARB] proxy.grantRole(ALLOCATOR_ROLE, ALM_BACKSTOP_RELAYER_MULTISIG) → tested via test_ARBITRUM_ALMProxyFreezableConfiguration
    • [ARB] proxy.grantRole(FREEZER_ROLE, ALM_FREEZER_MULTISIG) → tested via test_ARBITRUM_ALMProxyFreezableConfiguration
    • [ARB] vault.grantRole(SETTER_ROLE, ALM_PROXY_FREEZABLE) → tested via test_ARBITRUM_sll_spUSDT_vaultConfiguration
    • [ARB] vault.grantRole(TAKER_ROLE, Arbitrum.ALM_PROXY) → tested via test_ARBITRUM_sll_spUSDT_vaultConfiguration
    • [ARB] vault.setVsrBounds(1e27, SIX_PCT_APY) → tested via test_ARBITRUM_sll_spUSDT_vaultConfiguration
    • [ARB] vault.setDepositCap(250_000_000e6) → tested via test_ARBITRUM_sll_spUSDT_vaultConfiguration
    • [ARB] vault.deposit(1e6, address(1)) → tested via test_ARBITRUM_sll_spUSDT_vaultConfiguration (totalSupply + balanceOf assertions)
    • [ARB] setUnlimitedRateLimitData(LIMIT_SPARK_VAULT_TAKE, vault) → tested via test_ARBITRUM_sll_spUSDT_vaultConfiguration
    • [ARB] setUnlimitedRateLimitData(LIMIT_ASSET_TRANSFER, asset, vault) → tested via test_ARBITRUM_sll_spUSDT_vaultConfiguration
    • [ARB] foreignController.setLayerZeroRecipient(30101, Ethereum.ALM_PROXY) → tested via test_ARBITRUM_sll_spUSDT_usdt0RoundTrip
    • [ARB] rateLimits.setUnlimitedRateLimitData(LIMIT_LAYERZERO_TRANSFER, USDT_OFT, 30101) → tested via test_ARBITRUM_sll_spUSDT_usdt0RoundTrip
    • [AVA] SPARK_EXECUTOR.updateDelay(3 days) → tested via test_AVALANCHE_bridgeConfiguration (timelock queue/cancel/execute flow)
    • [AVA] SPARK_EXECUTOR.grantRole(GUARDIAN_ROLE, Avalanche.ALM_OPS_MULTISIG) → tested via test_AVALANCHE_bridgeConfiguration
  • End-to-end tests are sufficient: test_ARBITRUM_sll_spUSDT_usdt0RoundTrip covers full 10-step USDT0 lifecycle (deposit, take, bridge ARB→ETH, relay, warp+accrue, bridge ETH→ARB, relay, transferAsset, redeem); test_AVALANCHE_bridgeConfiguration covers queue/cancel/execute timelock flow.
  • All actions are covered by tests.
  • Integration tests verify the end-to-end execution flow (SLL E2E tests via SparkLiquidityLayerTests harness).
  • Gas tests ensure execution is possible within the existing block gas limit
  • All tests are passing in CI.
  • All tests listed above are not skipped (no .skip modifier observed in Spell_20260702.t.sol).
  • All tests are passing locally:
EXECUTED_TESTS_LOGS — Tests cannot be run because of missing paid Etherscan API

Pre-Deployment Checks

  • Actions listed in the Executive Sheet for this Star match the spell scope
  • Every Instruction text from the Executive Sheet is copied to the spell code as a comment
  • All actions present in the spell code are present in the forum post specification.
  • All actions in the forum post specification are present in the spell code.
  • Relevant checklist items have been re-verified after latest commits (18fcc81, 11155c2, 943e3e7, 0382c92,932fa99, f4f79f4) - changes are test fixes and partial review responses addressing lucas-manuel's comments.
  • IF no blockers were found, post the completed "Development Stage" checklist stage with the explicit "Good to deploy" note on top.

Deployment Stage

(To be completed after both reviewers provide explicit "Good to deploy")

  • Both reviewers gave explicit "Good to deploy".
  • A new comment in the PR contains link to the deployed spell(s) and Tenderly vnet(s).
  • The comment also contains codehash of the deployed mainnet spell.
  • The codehash matches one produced locally from the reviewed source code.
  • Every spell is verified on Etherscan or other primary block explorer for this chain.
  • Every spell code matches local source code at the "good to deploy" commit.
  • Etherscan settings (optimizer, EVM version, license) match local ones.
  • Every spell is deployed using standard CREATE (not CREATE2).
  • Tests are updated to execute against the deployed spell(s).
  • No test is skipped after deployment.
  • All tests are passing in CI at COMMIT_HASH.
  • All tests are passing locally at COMMIT_HASH:
EXECUTED_TESTS_LOGS

Simulation Checks

  • The Tenderly simulation shows all actions are executed successfully.
  • The Tenderly simulation shows no extra actions not present in the spell are executed.
  • The Tenderly simulation shows no reverts or out-of-gas errors.
  • IF no blockers were found, post the completed "Deployment Stage" checklist stage with the explicit "Good to handover" note on top.

Handover Stage

(To be completed after both reviewers provide explicit "Good to handover")

  • Both reviewers gave explicit "Good to handover".
  • All review comments have been addressed or resolved.
  • The spell address, the codehash and the direct execution are posted by the crafter in the #govops in the XXX spell YYYY-MM-DD deployed to 0x… with hash 0x…, direct execution: yes / no format.
  • Posted spell address matches spell address approved for handover.
  • Posted spell codehash matches codehash that you verified locally.
  • Posted direct execution value matches the forum post.
  • Confirm the address (via a separate "reply to" message, restating the address to avoid edits).
  • Ensure that no changes were made to the code since the spell was deployed and archived.
  • IF no blockers were found, post the completed "Handover Stage" checklist stage with the explicit pull request approval via 'Approve' review option.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment