Skip to content

feat: Spell 2026-07-16 (SC-1573) - #174

Merged
lucas-manuel merged 20 commits into
masterfrom
feat/sc-1573-spell-20260716
Jul 15, 2026
Merged

feat: Spell 2026-07-16 (SC-1573)#174
lucas-manuel merged 20 commits into
masterfrom
feat/sc-1573-spell-20260716

Conversation

@supercontracts

@supercontracts supercontracts commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

2026-07-16 Spark Spell

Forum Post

https://forum.skyeco.com/t/july-16-2026-proposed-changes-to-spark-for-upcoming-spell/28029

Dependencies on Sky Core spell

None

Notes for Reviewers

None

Conditions for Spell Deployment

  • All spell-specific tests are passing.
  • All E2E tests are passing.
  • All newly deployed addresses have been reviewed and added to the spark-address-registry in a PR that has been merged.
  • All newly deployed addresses have been added to the Immunefi bug bounty program.
  • There is complete test coverage of all changes in the spell, including any relevant edge cases.
  • At least three approvals have been given from reviewers.
  • CODE_RABBIT.md comment has been posted, AI review performed.
  • Octane review performed.

Conditions for Spell Handoff to Sky

  • All spells payloads have been deployed and verified on Etherscan.
  • All spell-specific tests are passing (including spell bytecode verification).
  • All E2E tests are passing.
  • Spell caster is passing in CI.
  • At least three approvals have been given from reviewers on deployed spell.
  • Comment from at least three reviewers confirming address of mainnet spell payload.

Conditions for Spell Merge

  • Spell handoff performed to Sky team on Discord.
  • Spell handoff performed to Sky team on Signal.
  • Spell address confirmed to be correct in sky-ecosystem/spells-mainnet repo open PR through comments by at least three reviewers.
  • Sky deployed spell confirmed through a comment in this PR to contain correct Spark payload address by at least three reviewers.

Summary by CodeRabbit

  • New Features

    • Added support for additional networks and cross-chain execution paths.
    • Introduced new governance actions that update chain settings, roles, rate limits, and fund distribution.
    • Expanded payload handling to include new chain destinations.
  • Bug Fixes

    • Improved cross-chain test reliability and state isolation.
    • Updated test harness behavior to better handle pending bridge messages and fork-specific execution.
  • Tests

    • Added broader end-to-end coverage for new chains, vault flows, and payload bytecode checks.

@octane-security-app

Copy link
Copy Markdown

Summary by Octane

New Contracts

  • SparkEthereum_20260716.sol: The smart contract deactivates an old USDT vault, enables USDT and USDG bridging, and facilitates fund transfers for incentives and grants.
  • SparkRobinhood_20260716.sol: The smart contract updates roles and rate limits in the Spark Robinhood system, involving asset transfer controls and role assignments.

Updated Contracts

  • ChainIdUtils.sol: Added support for "Robinhood" with chain ID 4663 in the ChainIdUtils library.
  • SpellRunner.sol: Added Robinhood compatibility with a new Arbitrum native bridge integration.
  • SpellTests.sol: Added test_ROBINHOOD_PayloadBytecodeMatches function for Robinhood chain ID payload verification.

🔗 Commit Hash: 20510f7

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR onboards Robinhood and XLayer chains into the Spark protocol: adding chain-id helpers, cross-chain payload dispatch, new mainnet and Robinhood proposal contracts, test-harness wiring (forks, bridges, LayerZero rework), and new integration/E2E test suites, alongside CI and submodule updates.

Changes

Core chain support and proposal payloads

Layer / File(s) Summary
Chain ID and deal utility support
src/libraries/ChainIdUtils.sol, src/libraries/DealUtils.sol, src/interfaces/Interfaces.sol
Adds Robinhood/XLayer chain id constants and domain strings, USDG_ROBINHOOD whale-deal branch, and getImplementation() on ISparkVaultV2Like.
Payload cross-chain dispatch
src/SparkPayloadEthereum.sol
Adds PAYLOAD_ROBINHOOD/PAYLOAD_XLAYER fields and new Arbitrum/Optimism-style L1→L2 dispatch branches in execute().
Mainnet postExecute proposal
src/proposals/20260716/SparkEthereum_20260716.sol
New contract configuring rate limits, LZ recipient, USDS transfers, syrup conversion, and Grove ALM funding.
Robinhood role/rate-limit proposal
src/proposals/20260716/SparkRobinhood_20260716.sol
New contract swapping FREEZER/RELAYER roles and setting USDG rate-limit data.
CI, submodules, misc
.github/workflows/test.yml, lib/spark-address-registry, lib/xchain-helpers, diffs/...md
Adds RH_RPC_URL env var, bumps two submodule pointers, adds an unrelated diff artifact file.

Test harness and E2E test coverage

Layer / File(s) Summary
SpellRunner wiring
src/test-harness/SpellRunner.sol
Adds Robinhood/XLayer forks, executors, bridge init (incl. manual XLayer LayerZero bridge), payload resolution, and fallback logic.
SparkLiquidityLayerTests rework
src/test-harness/SparkLiquidityLayerTests.sol
Reworks LayerZero integration with log draining and fork snapshotting, adds patchedDeal usage, updates pre/post-execution integration arrays, adds Robinhood context.
Bytecode match tests
src/test-harness/SpellTests.sol
Adds Robinhood/XLayer payload bytecode match tests.
Ethereum/Robinhood/XLayer spell tests
src/proposals/20260716/Spell_20260716.t.sol
New test suites covering Morpho deactivation, role changes, USDG/USDT bridging, vault round trips, and treasury transfers.
XLayer vault and crosschain tests
src/proposals/20260716/XLayer_20260716.t.sol
New config, E2E, and crosschain setVsrBounds test suites with supporting payload/interface contracts.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: Priority: Medium, Status: Ready for Review

Suggested reviewers: lucas-manuel, certora-spark-review, TheMj0ln1r

Poem

Two new burrows, Robinhood and XLayer wide,
Bridges hop and payloads glide,
USDG and USDT cross the tide,
Tests dig deep with forks applied,
This rabbit thumps with review pride! 🐇🌉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description matches most required sections, but it omits the Addresses table required by the template. Add the Addresses section with the relevant variables, addresses, networks, and sources of truth, and ensure all template sections are present.
Linked Issues check ❓ Inconclusive SC-1573 provides no acceptance criteria beyond the title, so compliance with the linked issue cannot be verified. Add the issue's concrete requirements or acceptance criteria so the changes can be validated against them.
✅ Passed checks (3 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The diff appears focused on the 2026-07-16 spell, test harness, and related network support with no clear unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title is concise, descriptive, starts with a prefix, and ends with the required SC-1573 identifier.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sc-1573-spell-20260716

Comment @coderabbitai help to get the list of available commands.

@octane-security-app

Copy link
Copy Markdown

Overview

Vulnerabilities found: 1                                                                                
Warnings found: 1                                                                                

🔗 Commit Hash: 20510f7
🛡️ Octane Dashboard: All vulnerabilities

@supercontracts
supercontracts marked this pull request as ready for review July 8, 2026 11:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (5)
src/proposals/20260716/XLayer_20260716.t.sol (2)

505-511: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

No-op skip(0) statement.

skip(0) performs no time advancement and the comment already explains the delay is 0; the call itself is dead weight.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/proposals/20260716/XLayer_20260716.t.sol` around lines 505 - 511, The
test in XLayer_20260716.t.sol contains a no-op time advance in the relay flow,
where the Executor delay is already documented as zero. Remove the redundant
skip call from the sequence around relayMessagesToDestination so the step
remains clear and only meaningful state changes are left.

40-208: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Significant duplication between XLayerConfigTests and XLayerE2ETests.

Both contracts redeclare identical constants (ALM_PROXY, CONTROLLER, RATE_LIMITS, SPUSDT_VAULT, EXECUTOR, RELAYER_1, SETTER, USDT) and an almost-identical setUp(). Extracting a shared abstract base contract would reduce the risk of the two sets of constants drifting apart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/proposals/20260716/XLayer_20260716.t.sol` around lines 40 - 208, The two
test contracts duplicate the same environment constants and fork setup, so they
can drift independently. Extract the shared addresses and `setUp()` logic into a
common abstract base contract, then have `XLayerConfigTests` and
`XLayerE2ETests` inherit from it and keep only contract-specific constants like
`USER` or APY values in each subclass.
src/proposals/20260716/Spell_20260716.t.sol (1)

44-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Import OptionsBuilder through a remapping
This hard-codes lib/xchain-helpers/lib/devtools/... and ties the file to xchain-helpers' internal layout. A package alias for the OApp library would keep the import stable if that subtree moves.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/proposals/20260716/Spell_20260716.t.sol` at line 44, The import of
OptionsBuilder is hard-coded to the xchain-helpers internal path, which makes
Spell_20260716.t.sol brittle if that layout changes. Update the import to use
the existing remapping or package alias for the OApp library instead of
referencing the nested lib/xchain-helpers/lib/devtools path directly, so the
import is resolved through a stable symbol path.
src/test-harness/SparkLiquidityLayerTests.sol (1)

4682-4689: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: this now duplicates _getPostExecutionIntegrationsNoChange.

After dropping the appended entries, _getPostExecutionIntegrationsArbitrumOne is a plain copy identical to _getPostExecutionIntegrationsNoChange; the dispatcher could route ArbitrumOne there instead of keeping a near-duplicate helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test-harness/SparkLiquidityLayerTests.sol` around lines 4682 - 4689, The
ArbitrumOne post-execution integrations helper is now a near-duplicate of the
no-change helper, so remove the redundant copy by routing the ArbitrumOne branch
through _getPostExecutionIntegrationsNoChange instead of keeping separate
identical logic. Update the dispatcher and keep the shared behavior centralized
in _getPostExecutionIntegrationsNoChange so future changes only need one
implementation.
src/test-harness/SpellRunner.sol (1)

114-117: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider sourcing the XLayer RPC from an env var like the other chains.

Unichain and Robinhood read their RPC from vm.envString(...), but XLayer hardcodes the public endpoint https://rpc.xlayer.tech. Public endpoints tend to rate-limit/flake in CI (and this path already fans out ~20 forks per binary-search call). An XLAYER_RPC_URL env var would match the existing convention and improve CI reliability.

♻️ Suggested change
         setChain("xlayer", ChainData({
             name    : "XLayer",
-            rpcUrl  : "https://rpc.xlayer.tech",
+            rpcUrl  : vm.envString("XLAYER_RPC_URL"),
             chainId : 196
         }));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test-harness/SpellRunner.sol` around lines 114 - 117, The XLayer chain
setup in SpellRunner hardcodes the public RPC endpoint instead of following the
env-based pattern used by the other chains. Update the XLayer entry in the chain
registration logic to read the RPC URL from an XLAYER_RPC_URL environment
variable via vm.envString, matching the existing Unichain and Robinhood setup
and keeping the XLayer name/chainId configuration intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/libraries/DealUtils.sol`:
- Around line 81-87: The Robinhood/USDG branch in DealUtils.sol currently
subtracts amount - startingBalance unconditionally, which can underflow when
amount is below the starting balance. Update the balance adjustment logic in
that Robinhood USDG path to handle both directions explicitly: keep the top-up
flow for amount > startingBalance, and use the opposite delta for the drain case
when amount < startingBalance, so the transfer amount is always non-negative.
Refer to the Robinhood, USDG_ROBINHOOD, and USDG_ROBINHOOD_WHALE handling in the
balance adjustment helper to make the fix in the right branch.

In `@src/proposals/20260716/SparkEthereum_20260716.sol`:
- Around line 40-42: `SparkEthereum_20260716.sol` still uses placeholder dead
addresses for `ANCHORAGE_FEES_RECIPIENT` and `INCENTIVES_RECIPIENT`, so
`_postExecute()` in the proposal would transfer USDS to the wrong targets.
Replace those constants with the real recipient addresses (or wire them through
the intended configuration path) before execution, and verify the downstream
transfers in `_postExecute()` use the corrected values.
- Around line 56-58: The constructor in SparkEthereum_20260716 currently leaves
PAYLOAD_ROBINHOOD unset, so SparkPayloadEthereum.execute() will skip the
Robinhood leg. Update the SparkEthereum_20260716 constructor to initialize
PAYLOAD_ROBINHOOD with the deployed SparkRobinhood_20260716 address so the
payload is queued correctly.

In `@src/proposals/20260716/Spell_20260716.t.sol`:
- Around line 661-664: The fund-transfer test is still using placeholder
recipient constants, and both ANCHORAGE_FEES_RECIPIENT and INCENTIVES_RECIPIENT
point to the same dummy address marked TODO. Update the recipient values in
Spell_20260716.t.sol so the payload uses the finalized real addresses for the
treasury transfer flow, and verify the related
testETHEREUM_sparkTreasury_transfers assertions still match the intended
recipients before deployment.

In `@src/proposals/20260716/XLayer_20260716.t.sol`:
- Around line 210-341: The test still carries USDG/SPUSDG template leftovers,
which makes the USDT-based scenario confusing. In test_boundary_depositCap and
test_E2E, rename the misleading IERC20 variable and update the stale comments so
they consistently refer to USDT and the SPUSDT vault, using the identifiers
USDT, spusdtVault, and test_boundary_depositCap/test_E2E to locate the affected
spots. Keep the assertions and behavior unchanged, but make the narrative and
naming match the actual token/vault under test.
- Line 73: The XLayer fork setup is hardcoded to a public RPC endpoint, so
update both setUp() paths that call vm.createSelectFork in XLayer_20260716.t.sol
to read the URL from an environment variable instead of using the literal
xlayer.tech URL. Also change the XLayer chain config entry that currently pins
the same public endpoint to use that configurable value so the proposal tests no
longer depend on a public RPC.

---

Nitpick comments:
In `@src/proposals/20260716/Spell_20260716.t.sol`:
- Line 44: The import of OptionsBuilder is hard-coded to the xchain-helpers
internal path, which makes Spell_20260716.t.sol brittle if that layout changes.
Update the import to use the existing remapping or package alias for the OApp
library instead of referencing the nested lib/xchain-helpers/lib/devtools path
directly, so the import is resolved through a stable symbol path.

In `@src/proposals/20260716/XLayer_20260716.t.sol`:
- Around line 505-511: The test in XLayer_20260716.t.sol contains a no-op time
advance in the relay flow, where the Executor delay is already documented as
zero. Remove the redundant skip call from the sequence around
relayMessagesToDestination so the step remains clear and only meaningful state
changes are left.
- Around line 40-208: The two test contracts duplicate the same environment
constants and fork setup, so they can drift independently. Extract the shared
addresses and `setUp()` logic into a common abstract base contract, then have
`XLayerConfigTests` and `XLayerE2ETests` inherit from it and keep only
contract-specific constants like `USER` or APY values in each subclass.

In `@src/test-harness/SparkLiquidityLayerTests.sol`:
- Around line 4682-4689: The ArbitrumOne post-execution integrations helper is
now a near-duplicate of the no-change helper, so remove the redundant copy by
routing the ArbitrumOne branch through _getPostExecutionIntegrationsNoChange
instead of keeping separate identical logic. Update the dispatcher and keep the
shared behavior centralized in _getPostExecutionIntegrationsNoChange so future
changes only need one implementation.

In `@src/test-harness/SpellRunner.sol`:
- Around line 114-117: The XLayer chain setup in SpellRunner hardcodes the
public RPC endpoint instead of following the env-based pattern used by the other
chains. Update the XLayer entry in the chain registration logic to read the RPC
URL from an XLAYER_RPC_URL environment variable via vm.envString, matching the
existing Unichain and Robinhood setup and keeping the XLayer name/chainId
configuration intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6d9716e3-0c46-4f56-8029-9acb8d9f39a3

📥 Commits

Reviewing files that changed from the base of the PR and between 17b5536 and af2a486.

📒 Files selected for processing (22)
  • .github/workflows/test.yml
  • archive/20260702/SparkArbitrumOne_20260702.sol
  • archive/20260702/SparkAvalanche_20260702.sol
  • archive/20260702/SparkBase_20260702.sol
  • archive/20260702/SparkEthereum_20260702.sol
  • archive/20260702/SparkOptimism_20260702.sol
  • archive/20260702/SparkUnichain_20260702.sol
  • archive/20260702/Spell_20260702.t.sol
  • diffs/20260716-Ethereum-0xC13e21B648A5Ee794902342038FF3aDAB66BE987-pre_20260716-Ethereum-0xC13e21B648A5Ee794902342038FF3aDAB66BE987-post.md
  • lib/spark-address-registry
  • lib/xchain-helpers
  • src/SparkPayloadEthereum.sol
  • src/interfaces/Interfaces.sol
  • src/libraries/ChainIdUtils.sol
  • src/libraries/DealUtils.sol
  • src/proposals/20260716/SparkEthereum_20260716.sol
  • src/proposals/20260716/SparkRobinhood_20260716.sol
  • src/proposals/20260716/Spell_20260716.t.sol
  • src/proposals/20260716/XLayer_20260716.t.sol
  • src/test-harness/SparkLiquidityLayerTests.sol
  • src/test-harness/SpellRunner.sol
  • src/test-harness/SpellTests.sol

Comment thread src/libraries/DealUtils.sol
Comment thread src/proposals/20260716/SparkEthereum_20260716.sol Outdated
Comment thread src/proposals/20260716/SparkEthereum_20260716.sol
Comment thread src/proposals/20260716/Spell_20260716.t.sol Outdated
Comment thread src/proposals/20260716/XLayer_20260716.t.sol
Comment thread src/proposals/20260716/XLayer_20260716.t.sol Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 8, 2026
Comment thread src/proposals/20260716/Spell_20260716.t.sol
Comment thread src/proposals/20260716/XLayer_20260716.t.sol Outdated
Comment thread src/proposals/20260716/XLayer_20260716.t.sol
Comment thread src/proposals/20260716/XLayer_20260716.t.sol Outdated
Comment thread src/proposals/20260716/XLayer_20260716.t.sol
Comment thread src/proposals/20260716/XLayer_20260716.t.sol
Comment thread src/proposals/20260716/Spell_20260716.t.sol Outdated
Comment thread src/proposals/20260716/Spell_20260716.t.sol Outdated
Comment thread src/proposals/20260716/Spell_20260716.t.sol Outdated
Comment thread src/proposals/20260716/Spell_20260716.t.sol Outdated
Comment thread src/proposals/20260716/Spell_20260716.t.sol Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 9, 2026
* Forum: https://forum.skyeco.com/t/july-16-2026-proposed-changes-to-spark-for-upcoming-spell/28029
* Vote: https://snapshot.org/#/s:sparkfi.eth/proposal/0xd177bc28b65afb23dc39a5e7cfdded7084b3b722b230e08d7067b68fa0f4486a
*/
contract SparkXLayer_20260716 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a test for this before after state

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread src/proposals/20260716/Spell_20260716.t.sol
Comment thread src/proposals/20260716/XLayer_20260716.t.sol
TheMj0ln1r
TheMj0ln1r previously approved these changes Jul 9, 2026

@TheMj0ln1r TheMj0ln1r left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving spell for the deployment

  • SLL Activate SLL and Spark Savings Infra [Robinhood] - [1a] - DONE
    • SLL roles
      • Check new ALM_RELAYER address has RELAYER role on controller - 0x0ca8f938Aba2214eA11eb451e795A8ef7B720C18
      • Check Old relayer is revoked
      • Check new ALM_BACKSTOP_RELAYER address has RELAYER role on controller - 0x52CC27896e641Cbe88F0aD36480839961A47CdF8
      • Check Old backstop relayer is revoked
      • Check new ALM_FREEZER address has RELAYER role on controller - 0x2d5Aa449FB8C5646C81BC3C1D2034c2d37F17099
      • Check Old freezer is revoked
      • Verify the test that asserts role grants and revokes - test_ROBINHOOD_controllerRoleChanges
  • SLL Activate SLL and Spark Savings Infra [Robinhood] - [1b] - DONE
    • spUSDG config
      • Verify deposit cap is unchanged
      • Verify max rate is unchanged
  • SLL Activate SLL and Spark Savings Infra [Robinhood] - [1c] - DONE
    • SLL ratelimits
      • Check asset address against forum post - USDG=0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
      • Check recipient address against forum post - 0x17C0F5345d1144fdF670D14719077be3842E5087
      • Check maxAmount against forum post. - 50_000_000e6
      • Check slope against forum post. - 250_000_000e6/day
      • Verify the precision of the rate limit against the underlying asset - 6
      • Check rate limit before and after state in a test - test_ETHEREUM_sll_enableUsdgTransferToPaxosDeposit
      • Ensure that _testTransferAssetIntegration is run with new integration in spell tests
  • SLL Enable USDG Bridging to Robinhood [Ethereum] - [2] - DONE
    • SLL ratelimits - (Bridging is done via Paxos centralized bridge)
      • Check asset address against forum post - Ethereum.USDG
      • Check recipient address against forum post - PAXOS_USDG_DEPOSIT=0xf752cF318dfF2C01575c98741AA52e7a34d873Fd
      • Check maxAmount against forum post - 50_000_000e6
      • Check slope against forum post - 250_000_000e6/day
      • Verify the precision of the rate limit against the underlying asset - 6
      • Check rate limit before and after state in a test - test_ETHEREUM_sll_enableUsdgTransferToPaxosDeposit
      • Ensure that _testTransferAssetIntegration is run with new integration in spell tests
  • Deploy spUSDT [XLayer] - [3] - DONE
    • Review xlayer-deployment scripts and tests
      • Verify ALMProxy - DONE
        • Check that contract is verified on Oklink - 0x83A914C361bB729EB6BEBC8C7bA993667A0E6Df8

        • Check the release tag from which the contract is deployed - v1.10.0

        • Manually verify the bytecode of the contract

          ➜  xlayer-deployment git:(feat/verify) forge verify-bytecode 0x83A914C361bB729EB6BEBC8C7bA993667A0E6Df8 ALMProxy --constructor-args $DEPLOYER --rpc-url  https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract ALMProxy at address 0x83A914C361bB729EB6BEBC8C7bA993667A0E6Df8
          
          Creation code matched with status partial
          Runtime code matched with status partial
        • Verify constructor arguments

          • admin : (DEPLOYER, but replaced with EXECUTOR later)
        • Check that ALMProxy has only one admin (EXECUTOR) after deploy

        • Validate the test where the post deployment roles and state of ALMProxy is asserted - test_postDeployState

      • Verify RateLimits - DONE
        • Check that contract is verified on Oklink - 0x7F7E2286983994c4403Cf2B86758cE0e7bA666a8

        • Check the release tag from which the contract is deployed - v1.10.0

        • Manually verify the bytecode of the contract

          ➜  xlayer-deployment git:(feat/verify) forge verify-bytecode 0x7F7E2286983994c4403Cf2B86758cE0e7bA666a8 RateLimits --rpc-url https://rpc.xlayer.tech--verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract RateLimits at address 0x7F7E2286983994c4403Cf2B86758cE0e7bA666a8
          
          Creation code matched with status partial
          Runtime code matched with status partial
        • Verify constructor arguments

          • admin : (DEPLOYER, but replaced with EXECUTOR later)
        • Check that RateLimits has only one admin (EXECUTOR) after deploy

        • Validate the test where the post deployment roles and state of RateLimits is asserted - test_postDeployState

      • Verify Controller - DONE
        • Check that contract is verified on Oklink - 0xf9187C99Ee842beABE8e2e346d958315BFc9331f

        • Check the release tag from which the contract is deployed - v1.10.0

        • Check constructor arguments

          • Admin : (DEPLOYER, but replaced with EXECUTOR later)
          • Proxy: XLayer.ALM_PROXY
          • RateLimits: XLayer.ALM_RATE_LIMITS
          • PSM: address(0)
          • USDC: address(0)
          • CCTP: address(0)
        • Manually verify the bytecode of the contract

          # foundry.toml
          libraries = [
            "lib/spark-alm-controller/src/libraries/LayerZeroLib.sol:LayerZeroLib:0xa44a27901ee51d657f59c75a61521e332cdc2e2e"
          ]
          ➜  xlayer-deployment git:(feat/verify) forge verify-bytecode 0xf9187C99Ee842beABE8e2e346d958315BFc9331f ForeignController --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract ForeignController at address 0xf9187C99Ee842beABE8e2e346d958315BFc9331f
          
          Creation code matched with status partial
          Runtime code matched with status partial
        • Check that all libraries are verified on Oklink

          • LayerZeroLib - 0xa44a27901ee51d657f59c75a61521e332cdc2e2e
        • Verify bytecode of all libraries against latest production release

          • LayerZeroLib
          ➜  xlayer-deployment git:(feat/verify) ✗ forge verify-bytecode 0xa44a27901ee51d657f59c75a61521e332cdc2e2e LayerZeroLib --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract LayerZeroLib at address 0xA44A27901Ee51d657f59c75a61521E332cDC2E2E
          
          Creation code matched with status full
          Runtime code matched with status full
        • Check that Controller has only one admin (EXECUTOR) after deploy

        • Check that ALMProxy, RateLimits contract addresses linked to Controller are valid

        • Verify Controller has CONTROLLER_ROLE on ALMProxy and RateLimits

        • Check RELAYER_ROLE holders on controller

          • Relayer 1 : XLayer.ALM_RELAYER_MULTISIG
          • Relayer 2: XLayer.ALM_BACKSTOP_RELAYER_MULTISIG
        • Check FREEZER_ROLE holders on controller - XLayer.ALM_FREEZER_MULTISIG

        • Verify the test where post controller deployment state and roles are asserted - test_postDeployState

      • Verify ALMProxyFreezable - DONE
        • Check that contract is verified on oklink - 0x9449ed367C60ea757544fd990B57e1C2D0Ec3A94

        • Check the release tag from which the contract is deployed - diamond-pau v.1.12.0

        • Constructor args on explorer match expected deployment params - SPARK_EXECUTOR -

        • Manually verify bytecode

          ➜  diamond-pau git:(v1.12.0) ✗ forge verify-bytecode 0x9449ed367C60ea757544fd990B57e1C2D0Ec3A94 ALMProxyFreezable --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          Verifying bytecode for contract ALMProxyFreezable at address 0x9449ed367C60ea757544fd990B57e1C2D0Ec3A94
          
          Creation code matched with status partial
          Runtime code matched with status partial
        • Ensure ALM_RELAYER_MULTISIG is added as ALLOCATOR

        • Ensure ALM_BACKSTOP_RELAYER_MULTISIG is added as ALLOCATOR

        • Ensure ALM_FREEZER_MULTISIG is added as FREEZER

        • Add ALMProxyFreezable configuration test to check assert:

          • DEFAULT_ADMIN_ROLE is set to governance(executor)
          • ALM_RELAYER_MULTISIG is added as ALLOCATOR
          • ALM_BACKSTOP_RELAYER_MULTISIG is added as ALLOCATOR
          • ALM_FREEZER_MULTISIG is added as FREEZER
          • Check that all of the above are the ONLY roles set using events data
        • Test : test_almProxyFreezable_config

      • SparkVaultV2 deployment and onboarding - DONE
        • Verify contract on Oklink -XLayer.SPARK_VAULT_V2_SPUSDT

        • Verify bytecode against latest release - v1.0.1

          # Implementation
          ➜  xlayer-deployment git:(feat/verify) ✗ forge verify-bytecode 0xdCe929A335C75a1676EF5957A4D7a3b928C48820 SparkVault --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract SparkVault at address 0xdCe929A335C75a1676EF5957A4D7a3b928C48820
          Creation code matched with status full
          Runtime code matched with status full
          
          # Vault (Proxy)
          ➜  xlayer-deployment git:(feat/verify) ✗ forge verify-bytecode 0xc358c90D32375721Cb3924320Fdc2F8B694347Ca ERC1967Proxy --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          Verifying bytecode for contract ERC1967Proxy at address 0xc358c90D32375721Cb3924320Fdc2F8B694347Ca
          
          Creation code matched with status full
          Runtime code matched with status full
        • Check that SETTER_ROLE is set to the ALMProxyFreezable

          • XLayer.ALM_PROXY_FREEZABLE
        • Check that the TAKER_ROLE is set to the ALMProxy - XLAYER.ALM_PROXY

        • Check that the VSR bounds are set

          # minVSR = 1e27
          
          # maxVSR = SIX_PCT_APY = 1.000000001847694957439350562e27
        • Check that the vault is seeded, sending shares to address(1)

        • Check vault initialization

          • name : Spark Savings USDT
          • symbol: spUSDT
          • decimals: 6
          • deposit cap: 750_000_000e6
        • Ensure that transferAsset rate limit is set:

          • Check asset address is the underlying address of the vault - USDT
          • Check recipient address is the vault
          • Check maxAmount - unlimited
          • Check slope - unlimted
          • Verify the precision of the rate limit data against the underlying asset
        • Ensure that the take rate limit is set:

          • Check vault address is used
          • Check maxAmount - unlimited
          • Check slope - unlimited
          • Verify the precision of the rate limit data against the underlying asset
        • Verify the test where post deploy state config and roles are tested - test_vault_config

      • Verify Governance bridge setup - DONE
        • Verify Executor
          • Check that Executor is deployed - XLayer.SPARK_EXECUTOR

          • Check that Executor contract is verified on explorer

          • Manually verify that Executor is deployed from latest release

            ➜  spark-gov-relay git:(v1.0.1) forge verify-bytecode 0xCF5af6F53ceC74B791cb4182aC778ca9CD323510 Executor --rpc-url https://rpc.xlayer.tech --verifier-url"http://localhost:8443" --chain 196
            Warning: This is a nightly build of Foundry. It is recommended to use the latest stable version. To mute this warning set `FOUNDRY_DISABLE_NIGHTLY_WARNING` in your environment. 
            
            Verifying bytecode for contract Executor at address 0xCF5af6F53ceC74B791cb4182aC778ca9CD323510
            [⠊] Compiling...
            [⠊] Compiling 56 files with Solc 0.8.25
            [⠢] Solc 0.8.25 finished in 5.14s
            Compiler run successful!
            Creation code matched with status partial
            Runtime code matched with status partial
          • Check constructor arguments and validate

            • delay : 0
            • gracePeriod: 7 days
          • Verify the DEFAULT_ADMIN_ROLE holder - EXECUTOR

          • Verify GUARDIAN_ROLE holders - NO guardian

          • Verify SUBMISSION_ROLE holders - RECEIVER

        • Verify Receiver
          • Check the type of the Receiver (LZ, Arbitrum, Gnosis, etc) - Optimism

          • Check that correct Receiver is deployed and verified on explorer - XLayer.SPARK_RECEIVER

          • Manually verify that Receiver is deployed from latest release tag

            ➜  xchain-helpers git:(96c35ce) ✗ forge verify-bytecode 0x4bd50B9c00Ae19e8B59723F27645C7A5cCe7a4A0 OptimismReceiver --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
            Warning: This is a nightly build of Foundry. It is recommended to use the latest stable version. To mute this warning set `FOUNDRY_DISABLE_NIGHTLY_WARNING` in your environment.
            
            Verifying bytecode for contract OptimismReceiver at address 0x4bd50B9c00Ae19e8B59723F27645C7A5cCe7a4A0
            [⠊] Compiling...
            No files changed, compilation skipped
            Creation code matched with status partial
            Runtime code matched with status partial
          • Check constructor arguments and validate

            • L1Authority should be SPARK_PROXY
            • Target should be newly deployed Executor
        • Verify the e2e test of governance flow - test_crosschainE2E_setVsrBounds
          • Deploy payload on XLayer chain to set VSR
          • Deploy payload on mainnet to call receiver
          • PAUSE_PROXY calls SPARK_PROXY.exec() to execute mainnet payload
          • Relay messages over bridge
          • Wait executor delay (immediate - zero seconds)
          • Assert VSR values
          • Execute on EXECUTOR
          • Assert VSR values
      • Enable USDT0 bridging to Ethereum - DONE
        • Check layerZeroRecipient set in XLayer ForeignController
          • Check endpoint ID against LZ docs - 30101
          • Check ALMProxy address is Mainnet ALMProxy - Ethereum.ALM_PROXY
        • Check LZ rate limit is set for XLayer
          • Check OFT address - 0x94BCCa6bdfd6A61817Ab0E960bFedE4984505554
          • Check mainnet endpoint ID - 30101
          • maxAmount : unlimited
          • slope : unlimited
          • Test : test_controller_config, test_rateLimits_config
        • Review full e2e test - test_XLAYER_sll_spUSDT_usdt0RoundTrip
  • SLL Enable USDT0 Bridging to XLayer [Ethereum] - [4] - DONE
    • Check layerZeroRecipient set in MainnetController
      • Check endpoint ID against LZ docs - 30274
      • Check ALMProxy address is XLayer ALMProxy - 0x83A914C361bB729EB6BEBC8C7bA993667A0E6Df8
    • Check LZ rate limit is set for mainnet
      • Check OFT address - 0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee
      • Check XLayer endpoint ID - 30274
      • maxAmount : 5_000_000e6
      • slope : 100_000_000e6/day
      • Test : test_ETHEREUM_sll_enableUSDTBridgingToXLayer
    • Review full end to end test - test_XLAYER_sll_spUSDT_usdt0RoundTrip
  • SLL Deactivate Old USDT Morpho V2 Vault [Ethereum] - [5] - DONE
    • Check the address of old morpho vault v2 - 0xc7CDcFDEfC64631ED6799C95e3b110cd42F2bD22
    • Check deposit and withdraw ratelimits are set to zero
    • Verify test asserting old mopho vault have zero rate limits - test_ETHEREUM_sll_deactivateOldMorphoUsdtVault
  • Treasury Transfer [Ethereum] - [6] - DONE
    • Check receiver address against forum post or address registry - SPARK_FOUNDATION_MULTISIG=0x92e4629a4510AF5819d7D1601464C233599fF5ec
    • Check amount against forum post - 2_000_000e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers
  • Treasury Transfer [Ethereum] - [7] - DONE
    • Check receiver address against forum post or address registry - SPARK_ASSET_FOUNDATION_MULTISIG=0xEabCb8C0346Ac072437362f1692706BA5768A911
    • Check amount against forum post - 500_000e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers
  • SLL Transfer USDS to Grove [Ethereum] - [8] - DONE
    • Check Grove ALMProxy address - GROVE_ALM_PROXY=0x491EDFB0B8b608044e227225C715981a30F3A44E
    • Check syrupUSDC transfer amount - 85_943_747.637271e6
    • Check the syrupUSDC is converted to USDS amount
    • Check converted USDS amount is minted for spark ALMProxy
    • Check that minted USDS is transferred to Grove ALMProxy
    • Check temporary role (relayer, controller) grants of Payload is revoked after transfer
    • Verify the test that asserts the balance changes of Proxies from this transfer - test_ETHEREUM_sll_transferUsdsToGrove
  • Withdraw reserves [Ethereum] - [9] - DONE
    • Review SparkPayloadEthereum code for claiming all reserves
    • Ensure stablecoins go to ALMProxy and everything else goes to Liquidation multisig
    • Review that test decrements and increments funds from correct addresses for all reserves
  • Treasury Transfer [Ethereum] - [10a] - DONE
    • Check receiver address against forum post or address registry - SPARK_FOUNDATION_MULTISIG=0x92e4629a4510AF5819d7D1601464C233599fF5ec
    • Check amount against forum post - 1_100_000e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers
  • Treasury Transfer [Ethereum] - [10b] - DONE
    • Check receiver address against forum post or address registry - SPARK_ASSET_FOUNDATION_MULTISIG=0xEabCb8C0346Ac072437362f1692706BA5768A911
    • Check amount against forum post - 155_000e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers
  • Treasury Transfer [Ethereum] - [11] - DONE
    • Check receiver address against forum post or address registry - ALM_OPS_MULTISIG=0x2E1b01adABB8D4981863394bEa23a1263CBaeDfC
    • Check amount against forum post - 64_231e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 9, 2026
@unvariant-dev

Copy link
Copy Markdown
Collaborator

Currently Robinhood tests have major gaps, including:

  • No pre-spell deployment and configuration coverage (roles, deployer revocations, controller, executor, receiver and vault config), unlike X Layer which has a dedicated config test suite
  • Governance message delivery is never checked against the 1M gas limit used by the mainnet payload
  • The Morpho USDG vault integration setup (rate limits, max exchange rate) is not asserted anywhere
  • spUSDG vault parameters are not asserted, and the pre-spell rate limits are never verified to be unlimited
  • Multisig thresholds and owner counts are not verified

We recommend including these tests and have written a separate test file that can be just added to src/proposals/20260716: https://gist.github.com/unvariant-dev/49da05506f1ac8a3c98a67e7ae4010d3

@unvariant-dev
unvariant-dev self-requested a review July 9, 2026 20:55
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.25;

import { IRateLimits } from "spark-alm-controller/src/interfaces/IRateLimits.sol";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant dependency

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

bytes32 MORPHO_VAULT_V2_USDT_DEPOSIT_KEY = RateLimitHelpers.makeAddressKey(almController.LIMIT_4626_DEPOSIT(), OLD_MORPHO_VAULT_V2_USDT);
bytes32 MORPHO_VAULT_V2_USDT_WITHDRAW_KEY = RateLimitHelpers.makeAddressKey(almController.LIMIT_4626_WITHDRAW(), OLD_MORPHO_VAULT_V2_USDT);

rateLimits.setRateLimitData(MORPHO_VAULT_V2_USDT_DEPOSIT_KEY, 0, 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no “before” comment, although the spark-spell-checklist says: “Every parameter change is clearly commented with before/after values.”
Perhaps it should be added here and in other relevant places in the code as well.


import { IALMProxy } from "spark-alm-controller/src/interfaces/IALMProxy.sol";
import { IRateLimits } from "spark-alm-controller/src/interfaces/IRateLimits.sol";
import { MainnetController } from "spark-alm-controller/src/MainnetController.sol";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, the repo pins spark-alm-controller @ 3dbc7cb, which corresponds to the v1.9.0 release, but Ethereum.ALM_CONTROLLER, Robinhood.ALM_CONTROLLER, and XLayer.ALM_CONTROLLER were deployed from v1.10.0. Since both the spells and the test suite import controller sources from this library, consider updating the pin to v1.10.0 (984ec54)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@certora-spark-review certora-spark-review left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving the spell for deployment.

@unvariant-dev unvariant-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, we consider the spell ready for deployment and safe:

TheMj0ln1r
TheMj0ln1r previously approved these changes Jul 10, 2026
deluca-mike
deluca-mike previously approved these changes Jul 10, 2026

@deluca-mike deluca-mike left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • SLL Activate SLL and Spark Savings Infra [Robinhood] - [1a]
    • SLL roles
      • Check new ALM_RELAYER address has RELAYER role on controller - 0x0ca8f938Aba2214eA11eb451e795A8ef7B720C18
      • Check Old relayer is revoked
      • Check new ALM_BACKSTOP_RELAYER address has RELAYER role on controller - 0x52CC27896e641Cbe88F0aD36480839961A47CdF8
      • Check Old backstop relayer is revoked
      • Check new ALM_FREEZER address has RELAYER role on controller - 0x2d5Aa449FB8C5646C81BC3C1D2034c2d37F17099
      • Check Old freezer is revoked
      • Verify the test that asserts role grants and revokes - test_ROBINHOOD_roleChanges
  • SLL Activate SLL and Spark Savings Infra [Robinhood] - [1b]
    • spUSDG config
      • Verify deposit cap is unchanged
      • Verify max rate is unchanged
  • SLL Activate SLL and Spark Savings Infra [Robinhood] - [1c]
    • SLL ratelimits
      • Check asset address against forum post - USDG=0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
      • Check recipient address against forum post - 0x17C0F5345d1144fdF670D14719077be3842E5087
      • Check maxAmount against forum post. - 50_000_000e6
      • Check slope against forum post. - 250_000_000e6/day
      • Verify the precision of the rate limit against the underlying asset - 6
      • Check rate limit before and after state in a test - test_ETHEREUM_sll_enableUsdgTransferToPaxosDeposit
      • Ensure that _testTransferAssetIntegration is run with new integration in spell tests
  • SLL Enable USDG Bridging to Robinhood [Ethereum] - [2]
    • SLL ratelimits - (Bridging is done via Paxos centralized bridge)
      • Check asset address against forum post - Ethereum.USDG
      • Check recipient address against forum post - PAXOS_USDG_DEPOSIT=0xf752cF318dfF2C01575c98741AA52e7a34d873Fd
      • Check maxAmount against forum post - 50_000_000e6
      • Check slope against forum post - 250_000_000e6/day
      • Verify the precision of the rate limit against the underlying asset - 6
      • Check rate limit before and after state in a test - test_ETHEREUM_sll_enableUsdgTransferToPaxosDeposit
      • Ensure that _testTransferAssetIntegration is run with new integration in spell tests
  • Deploy spUSDT [XLayer] - [3]
    • Review xlayer-deployment scripts and tests
      • Verify ALMProxy
        • Check that contract is verified on Oklink - 0x83A914C361bB729EB6BEBC8C7bA993667A0E6Df8

        • Check the release tag from which the contract is deployed - v1.10.0

        • Manually verify the bytecode of the contract

          ➜  xlayer-deployment git:(feat/verify) forge verify-bytecode 0x83A914C361bB729EB6BEBC8C7bA993667A0E6Df8 ALMProxy --constructor-args $DEPLOYER --rpc-url  https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract ALMProxy at address 0x83A914C361bB729EB6BEBC8C7bA993667A0E6Df8
          
          Creation code matched with status partial
          Runtime code matched with status partial
        • Verify constructor arguments

          • admin : (DEPLOYER, but replaced with EXECUTOR later)
        • Check that ALMProxy has only one admin (EXECUTOR) after deploy

        • Validate the test where the post deployment roles and state of ALMProxy is asserted - test_postDeployState

      • Verify RateLimits
        • Check that contract is verified on Oklink - 0x7F7E2286983994c4403Cf2B86758cE0e7bA666a8

        • Check the release tag from which the contract is deployed - v1.10.0

        • Manually verify the bytecode of the contract

          ➜  xlayer-deployment git:(feat/verify) forge verify-bytecode 0x7F7E2286983994c4403Cf2B86758cE0e7bA666a8 RateLimits --rpc-url https://rpc.xlayer.tech--verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract RateLimits at address 0x7F7E2286983994c4403Cf2B86758cE0e7bA666a8
          
          Creation code matched with status partial
          Runtime code matched with status partial
        • Verify constructor arguments

          • admin : (DEPLOYER, but replaced with EXECUTOR later)
        • Check that RateLimits has only one admin (EXECUTOR) after deploy

        • Validate the test where the post deployment roles and state of RateLimits is asserted - test_postDeployState

      • Verify Controller
        • Check that contract is verified on Oklink - 0xf9187C99Ee842beABE8e2e346d958315BFc9331f

        • Check the release tag from which the contract is deployed - v1.10.0

        • Check constructor arguments

          • Admin : (DEPLOYER, but replaced with EXECUTOR later)
          • Proxy: XLayer.ALM_PROXY
          • RateLimits: XLayer.ALM_RATE_LIMITS
          • PSM: address(0)
          • USDC: address(0)
          • CCTP: address(0)
        • Manually verify the bytecode of the contract

          # foundry.toml
          libraries = [
            "lib/spark-alm-controller/src/libraries/LayerZeroLib.sol:LayerZeroLib:0xa44a27901ee51d657f59c75a61521e332cdc2e2e"
          ]
          ➜  xlayer-deployment git:(feat/verify) forge verify-bytecode 0xf9187C99Ee842beABE8e2e346d958315BFc9331f ForeignController --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract ForeignController at address 0xf9187C99Ee842beABE8e2e346d958315BFc9331f
          
          Creation code matched with status partial
          Runtime code matched with status partial
        • Check that all libraries are verified on Oklink

          • LayerZeroLib - 0xa44a27901ee51d657f59c75a61521e332cdc2e2e
        • Verify bytecode of all libraries against latest production release

          • LayerZeroLib
          ➜  xlayer-deployment git:(feat/verify) ✗ forge verify-bytecode 0xa44a27901ee51d657f59c75a61521e332cdc2e2e LayerZeroLib --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract LayerZeroLib at address 0xA44A27901Ee51d657f59c75a61521E332cDC2E2E
          
          Creation code matched with status full
          Runtime code matched with status full
        • Check that Controller has only one admin (EXECUTOR) after deploy

        • Check that ALMProxy, RateLimits contract addresses linked to Controller are valid

        • Verify Controller has CONTROLLER_ROLE on ALMProxy and RateLimits

        • Check RELAYER_ROLE holders on controller

          • Relayer 1 : XLayer.ALM_RELAYER_MULTISIG
          • Relayer 2: XLayer.ALM_BACKSTOP_RELAYER_MULTISIG
        • Check FREEZER_ROLE holders on controller - XLayer.ALM_FREEZER_MULTISIG

        • Verify the test where post controller deployment state and roles are asserted - test_postDeployState

      • Verify ALMProxyFreezable
        • Check that contract is verified on oklink - 0x9449ed367C60ea757544fd990B57e1C2D0Ec3A94

        • Check the release tag from which the contract is deployed - diamond-pau v.1.12.0

        • Constructor args on explorer match expected deployment params - SPARK_EXECUTOR -

        • Manually verify bytecode

          ➜  diamond-pau git:(v1.12.0) ✗ forge verify-bytecode 0x9449ed367C60ea757544fd990B57e1C2D0Ec3A94 ALMProxyFreezable --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          Verifying bytecode for contract ALMProxyFreezable at address 0x9449ed367C60ea757544fd990B57e1C2D0Ec3A94
          
          Creation code matched with status partial
          Runtime code matched with status partial
        • Ensure ALM_RELAYER_MULTISIG is added as ALLOCATOR

        • Ensure ALM_BACKSTOP_RELAYER_MULTISIG is added as ALLOCATOR

        • Ensure ALM_FREEZER_MULTISIG is added as FREEZER

        • Add ALMProxyFreezable configuration test to check assert:

          • DEFAULT_ADMIN_ROLE is set to governance(executor)
          • ALM_RELAYER_MULTISIG is added as ALLOCATOR
          • ALM_BACKSTOP_RELAYER_MULTISIG is added as ALLOCATOR
          • ALM_FREEZER_MULTISIG is added as FREEZER
          • Check that all of the above are the ONLY roles set using events data
        • Test : test_almProxyFreezable_config

      • SparkVaultV2 deployment and onboarding
        • Verify contract on Oklink -XLayer.SPARK_VAULT_V2_SPUSDT

        • Verify bytecode against latest release - v1.0.1

          # Implementation
          ➜  xlayer-deployment git:(feat/verify) ✗ forge verify-bytecode 0xdCe929A335C75a1676EF5957A4D7a3b928C48820 SparkVault --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          
          Verifying bytecode for contract SparkVault at address 0xdCe929A335C75a1676EF5957A4D7a3b928C48820
          Creation code matched with status full
          Runtime code matched with status full
          
          # Vault (Proxy)
          ➜  xlayer-deployment git:(feat/verify) ✗ forge verify-bytecode 0xc358c90D32375721Cb3924320Fdc2F8B694347Ca ERC1967Proxy --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
          Verifying bytecode for contract ERC1967Proxy at address 0xc358c90D32375721Cb3924320Fdc2F8B694347Ca
          
          Creation code matched with status full
          Runtime code matched with status full
        • Check that SETTER_ROLE is set to the ALMProxyFreezable

          • XLayer.ALM_PROXY_FREEZABLE
        • Check that the TAKER_ROLE is set to the ALMProxy - XLAYER.ALM_PROXY

        • Check that the VSR bounds are set

          # minVSR = 1e27
          # maxVSR = SIX_PCT_APY = 1.000000001847694957439350562e27
        • Check that the vault is seeded, sending shares to address(1)

        • Check vault initialization

          • name : Spark Savings USDT
          • symbol: spUSDT
          • decimals: 6
          • deposit cap: 750_000_000e6
        • Ensure that transferAsset rate limit is set:

          • Check asset address is the underlying address of the vault - USDT
          • Check recipient address is the vault
          • Check maxAmount - unlimited
          • Check slope - unlimted
          • Verify the precision of the rate limit data against the underlying asset
        • Ensure that the take rate limit is set:

          • Check vault address is used
          • Check maxAmount - unlimited
          • Check slope - unlimited
          • Verify the precision of the rate limit data against the underlying asset
        • Verify the test where post deploy state config and roles are tested - test_vault_config

      • Verify Governance bridge setup
        • Verify Executor
          • Check that Executor is deployed - XLayer.SPARK_EXECUTOR

          • Check that Executor contract is verified on explorer

          • Manually verify that Executor is deployed from latest release

            ➜  spark-gov-relay git:(v1.0.1) forge verify-bytecode 0xCF5af6F53ceC74B791cb4182aC778ca9CD323510 Executor --rpc-url https://rpc.xlayer.tech --verifier-url"http://localhost:8443" --chain 196
            Warning: This is a nightly build of Foundry. It is recommended to use the latest stable version. To mute this warning set `FOUNDRY_DISABLE_NIGHTLY_WARNING` in your environment. 
            
            Verifying bytecode for contract Executor at address 0xCF5af6F53ceC74B791cb4182aC778ca9CD323510
            [⠊] Compiling...
            [⠊] Compiling 56 files with Solc 0.8.25
            [⠢] Solc 0.8.25 finished in 5.14s
            Compiler run successful!
            Creation code matched with status partial
            Runtime code matched with status partial
          • Check constructor arguments and validate

            • delay : 0
            • gracePeriod: 7 days
          • Verify the DEFAULT_ADMIN_ROLE holder - EXECUTOR

          • Verify GUARDIAN_ROLE holders - NO guardian

          • Verify SUBMISSION_ROLE holders - RECEIVER

        • Verify Receiver
          • Check the type of the Receiver (LZ, Arbitrum, Gnosis, etc) - Optimism

          • Check that correct Receiver is deployed and verified on explorer - XLayer.SPARK_RECEIVER

          • Manually verify that Receiver is deployed from latest release tag

            ➜  xchain-helpers git:(96c35ce) ✗ forge verify-bytecode 0x4bd50B9c00Ae19e8B59723F27645C7A5cCe7a4A0 OptimismReceiver --rpc-url https://rpc.xlayer.tech --verifier-url "http://localhost:8443" --chain 196
            Warning: This is a nightly build of Foundry. It is recommended to use the latest stable version. To mute this warning set `FOUNDRY_DISABLE_NIGHTLY_WARNING` in your environment.
            
            Verifying bytecode for contract OptimismReceiver at address 0x4bd50B9c00Ae19e8B59723F27645C7A5cCe7a4A0
            [⠊] Compiling...
            No files changed, compilation skipped
            Creation code matched with status partial
            Runtime code matched with status partial
          • Check constructor arguments and validate

            • L1Authority should be SPARK_PROXY
            • Target should be newly deployed Executor
        • Verify the e2e test of governance flow - test_crosschainE2E_setVsrBounds
          • Deploy payload on XLayer chain to set VSR
          • Deploy payload on mainnet to call receiver
          • PAUSE_PROXY calls SPARK_PROXY.exec() to execute mainnet payload
          • Relay messages over bridge
          • Wait executor delay (immediate - zero seconds)
          • Assert VSR values
          • Execute on EXECUTOR
          • Assert VSR values
      • Enable USDT0 bridging to Ethereum
        • Check layerZeroRecipient set in XLayer ForeignController
          • Check endpoint ID against LZ docs - 30101
          • Check ALMProxy address is Mainnet ALMProxy - Ethereum.ALM_PROXY
        • Check LZ rate limit is set for XLayer
          • Check OFT address - 0x94BCCa6bdfd6A61817Ab0E960bFedE4984505554
          • Check mainnet endpoint ID - 30101
          • maxAmount : unlimited
          • slope : unlimited
          • Test : test_controller_config, test_rateLimits_config
        • Review full e2e test - test_XLAYER_sll_spUSDT_usdt0RoundTrip
  • SLL Enable USDT0 Bridging to XLayer [Ethereum] - [4]
    • Check layerZeroRecipient set in MainnetController
      • Check endpoint ID against LZ docs - 30274
      • Check ALMProxy address is XLayer ALMProxy - 0x83A914C361bB729EB6BEBC8C7bA993667A0E6Df8
    • Check LZ rate limit is set for mainnet
      • Check OFT address - 0x6C96dE32CEa08842dcc4058c14d3aaAD7Fa41dee
      • Check XLayer endpoint ID - 30274
      • maxAmount : 5_000_000e6
      • slope : 100_000_000e6/day
      • Test : test_ETHEREUM_sll_enableUSDTBridgingToXLayer
    • Review full end to end test - test_XLAYER_sll_spUSDT_usdt0RoundTrip
  • SLL Deactivate Old USDT Morpho V2 Vault [Ethereum] - [5]
    • Check the address of old morpho vault v2 - 0xc7CDcFDEfC64631ED6799C95e3b110cd42F2bD22
    • Check deposit and withdraw ratelimits are set to zero
    • Verify test asserting old mopho vault have zero rate limits - test_ETHEREUM_sll_deactivateOldMorphoUsdtVault
  • Treasury Transfer [Ethereum] - [6]
    • Check receiver address against forum post or address registry
    • Check amount against forum post - 2_000_000e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers
  • Treasury Transfer [Ethereum] - [7]
    • Check receiver address against forum post or address registry
    • Check amount against forum post - 500_000e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers
  • SLL Transfer USDS to Grove [Ethereum] - [8]
    • Check Grove ALMProxy address - GROVE_ALM_PROXY=0x491EDFB0B8b608044e227225C715981a30F3A44E
    • Check syrupUSDC transfer amount - 85_943_747.637271e6
    • Check the syrupUSDC is converted to USDS amount
    • Check converted USDS amount is minted for spark ALMProxy
    • Check that minted USDS is transferred to Grove ALMProxy
    • Check temporary role (relayer, controller) grants of Payload is revoked after transfer
    • Verify the test that asserts the balance changes of Proxies from this transfer - test_ETHEREUM_sll_transferUsdsToGrove
  • Withdraw reserves [Ethereum] - [9]
    • Review SparkPayloadEthereum code for claiming all reserves
    • Ensure stablecoins go to ALMProxy and everything else goes to Liquidation multisig
    • Review that test decrements and increments funds from correct addresses for all reserves
  • Treasury Transfer [Ethereum] - [10a]
    • Check receiver address against forum post or address registry - SPARK_FOUNDATION_MULTISIG=0x92e4629a4510AF5819d7D1601464C233599fF5ec
    • Check amount against forum post - 1_100_000e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers
  • Treasury Transfer [Ethereum] - [10b]
    • Check receiver address against forum post or address registry - SPARK_ASSET_FOUNDATION_MULTISIG=0xEabCb8C0346Ac072437362f1692706BA5768A911
    • Check amount against forum post - 155_000e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers
  • Treasury Transfer [Ethereum] - [11]
    • Check receiver address against forum post or address registry - ALM_OPS_MULTISIG=0x2E1b01adABB8D4981863394bEa23a1263CBaeDfC
    • Check amount against forum post - 64_231e18
    • Review that test decrements and increments funds from correct addresses - test_ETHEREUM_sparkTreasury_transfers

lucas-manuel
lucas-manuel previously approved these changes Jul 10, 2026

@lucas-manuel lucas-manuel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving for deployment.

  • Check forum post link
  • Check description for all payloads
  • Check poll links
  • Check spark-address-registry commit matches current HEAD
  • SLL Activate SLL and Spark Savings Infra (Roles) [Robinhood] - [1a] - DONE
    • SLL roles (don’t check until registry is merged)
      • Check ALM_BACKSTOP_RELAYER_MULTISIG matches registry and forum
      • Check ALM_FREEZER_MULTISIG matches registry and forum
      • Check new ALM_BACKSTOP_RELAYER_MULTISIG address has RELAYER role on controller
      • Check Old backstop relayer is revoked
      • Check new ALM_FREEZER_MULTISIG address has FREEZER role on controller
      • Check Old freezer is revoked
      • Check ALMProxyFreezable ALLOCATOR and FREEZER role addresses against forum post
      • Verify the test that asserts role grants and revokes
  • SLL Activate SLL and Spark Savings Infra (Onchain params) [Robinhood] - [1b] - DONE
    • spUSDG config
      • Verify deposit cap is unchanged
      • Verify max rate is unchanged
  • SLL Activate SLL and Spark Savings Infra (Rate Limits) [Robinhood] - [1c] - DONE
    • SLL ratelimits
      • Check asset address against forum post
      • Check recipient address against forum post
      • Check maxAmount against forum post
      • Check slope against forum post
      • Verify the precision of the rate limit against the underlying asset
      • Check rate limit before and after state in a test
      • Ensure that _testTransferAssetIntegration is run with new integration in spell tests
  • SLL Enable USDG Bridging to Robinhood [Ethereum] - [2] - DONE
    • SLL ratelimits - (Bridging is done via Paxos centralized bridge)
      • Check asset address against forum post
      • Check recipient address against forum post
      • Check maxAmount against forum post
      • Check slope against forum post
      • Verify the precision of the rate limit against the underlying asset
      • Check rate limit before and after state in a test
      • Ensure that _testTransferAssetIntegration is run with new integration in spell tests
  • Deploy spUSDT [XLayer] - [3]
    • Review xlayer-deployment scripts and tests
      • Verify ALMProxy - DONE
        • Check that contract is verified on Oklink -
        • Check the release tag from which the contract is deployed - v1.10.0
        • Manually verify the bytecode of the contract
        • Verify constructor arguments
          • admin : (DEPLOYER, but replaced with EXECUTOR later)
        • Manually check that ALMProxy has only one admin (EXECUTOR) after deploy
        • Validate the test where the post deployment roles and state of ALMProxy is asserted - test_postDeployState
      • Verify RateLimits - DONE
        • Check that contract is verified on Oklink -
        • Check the release tag from which the contract is deployed - v1.10.0
        • Manually verify the bytecode of the contract
        • Verify constructor arguments
          • admin : (DEPLOYER, but replaced with EXECUTOR later)
        • Manually check that RateLimits has only one admin (EXECUTOR) after deploy
        • Validate the test where the post deployment roles and state of RateLimits is asserted - test_postDeployState
      • Verify Controller - DONE
        • Check that contract is verified on Oklink -
        • Check the release tag from which the contract is deployed - v1.10.0
        • Check constructor arguments
          • Admin : (DEPLOYER, but replaced with EXECUTOR later)
          • Proxy: ALM_PROXY
          • RateLimits: RATE_LIIMITS
          • PSM: address(0)
          • USDC: address(0)
          • CCTP: address(0)
        • Manually verify the bytecode of the contract
        • Check that all libraries are verified on Oklink
          • LayerZeroLib -
        • Verify bytecode of all libraries against latest production release
          • LayerZeroLib
        • Manually check that Controller has only one admin (EXECUTOR) after deploy
        • Manually check that Controller doens’t have any additional FREEZER or RELAYER roles granted
        • Check that ALMProxy, RateLimits contract addresses linked to Controller are valid
        • Verify Controller has CONTROLLER_ROLE on ALMProxy and RateLimits
        • Check RELAYER_ROLE holders on controller against forum post
          • Relayer 1
          • Relayer 2
        • Check FREEZER_ROLE holders on controller
        • Verify the test where post controller deployment state and roles are asserted - test_postDeployState
      • SparkVaultV2 deployment and onboarding - DONE
        • Verify contract on Oklink -
        • Verify bytecode against latest release - v1.0.1
        • Check that SETTER_ROLE is set to the ALMProxyFreezable
        • Check that the TAKER_ROLE is set to the ALMProxy -
        • Check that the VSR bounds are set to 0 and 6%
        • Check that the vault is seeded, sending shares to address(1)
        • Check vault initialization
          • name : Spark Savings USDT
          • symbol: spUSDT
          • decimals: 6
          • deposit cap: 750m
        • Ensure that transferAsset rate limit is set:
          • Check asset address is the underlying address of the vault -
          • Check recipient address is the vault
          • Check maxAmount
          • Check slope
          • Verify the precision of the rate limit data against the underlying asset
        • Ensure that the take rate limit is set:
          • Check vault address is used
          • Check maxAmount
          • Check slope
          • Verify the precision of the rate limit data against the underlying asset
        • Verify the test where post deploy state config and roles are tested - test_vault_config
        • Verify the integration test of deployed vault - test_e2e
          • SLL sets VSR
          • User deposits into spUSDT
          • Warp no interest
          • SLL takes funds
          • SLL bridges funds to mainnet
          • spUSDT accrues interest
          • SLL bridges funds to XLayer
          • SLL transfers into spUSDT
          • User withdraws from spUSDT
      • Verify Governance bridge setup - DONE
        • Verify Executor
          • Check that Executor is deployed -
          • Check that Executor contract is verified on explorer
          • Manually verify that Executor is deployed from latest release
          • Check constructor arguments and validate
            • delay : 0
            • gracePeriod: 7 days
          • Manually verify the DEFAULT_ADMIN_ROLE holder - ONLY Executor
          • Verify GUARDIAN_ROLE holders - None
          • Verify SUBMISSION_ROLE holders - ONLY Receiver
        • Verify Receiver
          • Check the type of the Receiver (LZ, Arbitrum, Gnosis, etc) - Optimism
          • Check that correct Receiver is deployed and verified on explorer -
          • Manually verify that Receiver is deployed from latest release tag
          • Check constructor arguments and validate
            • L1Authority should be SPARK_PROXY
            • Target should be newly deployed Executor -
      • Enable USDT0 bridging to Ethereum - DONE
        • Check layerZeroRecipient set in XLayer ForeignController
          • Check endpoint ID against LZ docs - 30101
          • Check ALMProxy address is Mainnet ALMProxy -
          • Manually check events on Block explorer to check that no other addresses were set as LZ recipients
        • Check LZ rate limit is set for XLayer
        • Review full e2e test
  • SLL Enable USDT0 Bridging to XLayer [Ethereum] - 4 - DONE
  • SLL Deactivate Old USDT Morpho V2 Vault [Ethereum] - 5 - DONE
    • Check the address of old morpho vault v2
    • Check deposit and withdraw ratelimits are set to zero
    • Verify test asserting old mopho vault have zero rate limits
  • Treasury Transfer (Spark Foundation Incentives) [Ethereum] - [6] DONE
    • Check receiver address against forum post or address registry
    • Check amount against forum post - 2m for incentives
    • Review that test decrements and increments funds from correct addresses
  • Treasury Transfer (SAF Anchorage Fees) [Ethereum] - [7] - DONE
    • Check receiver address against forum post or address registry
    • Check amount against forum post - 500k
    • Review that test decrements and increments funds from correct addresses
  • SLL Transfer USDS to Grove [Ethereum] - [8] DONE
  • Withdraw reserves [Ethereum] - [9] DONE
    • Review SparkPayloadEthereum code for claiming all reserves
    • Ensure stablecoins go to ALMProxy and everything else goes to Liquidation multisig
    • Review that test decrements and increments funds from correct addresses for all reserves
  • Treasury Transfer (Spark Foundation Grant) [Ethereum] - [10] DONE
    • Check receiver address against forum post or address registry
    • Check amount against forum post - 1.1m
    • Review that test decrements and increments funds from correct addresses
  • Treasury Transfer (SAF Grant) [Ethereum] - [11] DONE
    • Check receiver address against forum post or address registry
    • Check amount against forum post - 155k
    • Review that test decrements and increments funds from correct addresses
  • Treasury Transfer (SPK buybacks) [Ethereum] - [12] DONE
    • Check receiver address against forum post or address registry
    • Check amount against forum post - 64,231
    • Review that test decrements and increments funds from correct addresses

@supercontracts
supercontracts dismissed stale reviews from lucas-manuel and deluca-mike via 5889042 July 10, 2026 14:59

@TheMj0ln1r TheMj0ln1r left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming the spell is correctly deployed on Ethereum at 0xC1090e8fEE666868622a2F1e870185F944108Ee2

@deluca-mike

deluca-mike commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator
~/Projects/spark-spells on  feat/sc-1573-spell-20260716 ⌚ 11:42:12
$ forge verify-bytecode 0xC1090e8fEE666868622a2F1e870185F944108Ee2 SparkEthereum_20260716\

Verifying bytecode for contract SparkEthereum_20260716 at address 0xC1090e8fEE666868622a2F1e870185F944108Ee2
[⠢] Compiling...
No files changed, compilation skipped
Creation code matched with status partial
Runtime code matched with status partial

@lucas-manuel lucas-manuel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming and approving deployment at 0xC1090e8fEE666868622a2F1e870185F944108Ee2

cast codehash 0xC1090e8fEE666868622a2F1e870185F944108Ee2
0xa7b0b6c470169f40016d354a8944f9e5f3f787159fec5192694e8f1ddbc7f26f

Review checklist: https://gist.github.com/lucas-manuel/7dd8526199fec7fabb580c012f5de192

@certora-spark-review certora-spark-review left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming and approving the spell deployment at 0xC1090e8fEE666868622a2F1e870185F944108Ee2.

Review checklist - https://gist.github.com/certora-spark-review/5edb28cc7c3fd6c67523ca146208869b

@unvariant-dev

Copy link
Copy Markdown
Collaborator

Confirming and approving spell deployment at 0xC1090e8fEE666868622a2F1e870185F944108Ee2

Deployment checklist: https://gist.github.com/unvariant-dev/6208db73a7b62f31033355ef63d6b512

@lucas-manuel
lucas-manuel merged commit c24ddf5 into master Jul 15, 2026
1 of 4 checks passed
@lucas-manuel
lucas-manuel deleted the feat/sc-1573-spell-20260716 branch July 15, 2026 02:47
@coderabbitai coderabbitai Bot mentioned this pull request Aug 4, 2026
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants