Skip to content

feat(vault): migrate governance to compact Soroban bridge#414

Closed
carrion256 wants to merge 1 commit into
refactor/vault-ergonomicsfrom
refactor/extract-governance
Closed

feat(vault): migrate governance to compact Soroban bridge#414
carrion256 wants to merge 1 commit into
refactor/vault-ergonomicsfrom
refactor/extract-governance

Conversation

@carrion256

@carrion256 carrion256 commented Apr 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • migrate Soroban vault governance to a compact execute_governance bridge
  • remove nested public VaultCommand governance dispatch and string/address bloat
  • keep runtime deploy WASM under the 128 KiB budget

Verification

  • cargo check -p templar-4626-proxy-soroban -p templar-soroban-runtime -p templar-soroban-governance --tests
  • just -f contract/vault/soroban/justfile size-budget-check
  • cargo test -p templar-soroban-runtime -p templar-soroban-governance -p templar-4626-proxy-soroban --tests

This change is Reviewable

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Governance Bridge Refactoring

Summary

Migrates Soroban vault governance from nested VaultCommand dispatch to a compact single-entry execute_governance(env, caller, payload) bridge, separating governance operations into a dedicated GovernanceCommand enum. This reduces per-operation overhead, eliminates string-based method routing, and improves WASM size compliance.

Key Changes

Command Separation

  • New GovernanceCommand enum: Consolidates governance-only operations (SetGovernanceConfig, SetGovernancePolicy, Skim) with structured payloads featuring optional fields for flexible governance actions
  • Reduced VaultCommand: Retains only public/user-facing operations (ResyncIdleBalance, CancelMigration, ExtendTtl)
  • New execute_governance entrypoint: Vault contract receives execute_governance(env, caller, payload) and decodes GovernanceCommand variants with error mapping to ContractError::InvalidInput

Abdication Migration

  • Storage key changed from symbol-based (DataKey::Abdicated(Symbol)) to kind-based (DataKey::Abdicated(GovernanceActionKind)), removing free-form string dependencies and improving type safety

Governance Contract Refactoring

  • execute_vault_governance_action derives GovernanceCommand payload via governance_payload_for_action, obtains current contract address, and invokes vault's execute_governance via authorize_and_invoke
  • Removes per-action string/symbol helpers (method_name_for_action, address_text, sdk_string_text)

Test Coverage

  • Comprehensive governance bridge tests covering authorization, malformed payload rejection, and storage effects
  • Mock vault implements execute_governance path with storage getters for all governance-modified fields

Critical Security Findings

  1. Authorization Enforcement ✓ VERIFIED

    • Vault's require_governance helper validates caller == stored_governance_address before any governance action
    • Governance contract passes itself as caller via authorize_and_invoke
    • Unauthorized callers receive ContractError::Unauthorized
  2. Payload Validation ✓ VERIFIED

    • Decode errors properly mapped to ContractError::InvalidInput
    • ensure_finished helper rejects trailing bytes across all decoders
    • No unsafe unwrapping of optional fields; required fields use required_address(), required_addresses(), required_i128() helpers
  3. Optional Field Semantics - REVIEW

    • GovernanceCommand uses optional fields (primary, many, accounts, target_ids, etc.) for flexible payloads
    • Verify each governance policy kind (FEES, RESTRICTIONS, CAP, GROUP, SUPPLY_QUEUE, REMOVE_MARKET, PAUSED) has well-defined field requirements to prevent state inconsistencies
  4. Contract Address Validation ✓ VERIFIED

    • apply_governance_config calls require_contract_address(&governance) before storing new governance address
    • Prevents setting invalid/EOA addresses as governance contract
  5. Storage Key Migration Risk

    • DataKey::Abdicated type change from Symbol to GovernanceActionKind requires careful handling if upgrading live contracts with existing abdication state
    • Verify migration path or confirm this is only for new deployments

Recommended Manual Review

  • Verify all GovernanceCommand variant field combinations in tests match intended governance policy semantics
  • Confirm fee timelock vs. immediate application logic correctly routes through governance bridge
  • Validate skim side effects (token address storage) are atomic with governance authorization
  • Review authorize_and_invoke implementation to ensure it correctly establishes governance contract as authorized caller context

Walkthrough

The PR refactors governance control flow from multiple discrete setter endpoints to a unified bridge entrypoint. A new execute_governance(env, caller, payload) contract method replaces direct setter calls, decoding GovernanceCommand payloads for config/policy/skim operations. Abdication storage now keys on GovernanceActionKind instead of symbol strings.

Changes

Cohort / File(s) Summary
Documentation
contract/vault/soroban/README.md, contract/vault/soroban/STRIDE.md
Describes new governance bridge architecture: single execute_governance entrypoint with GovernanceCommand payload, boundary between governance and vault responsibilities, updated threat model tied to bridge validation and command dispatch.
Shared Types & Encoding
contract/vault/soroban/shared-types/src/lib.rs
Separates governance operations into new GovernanceCommand enum (SetGovernanceConfig, SetGovernancePolicy, Skim) with dedicated encoding/decoding; removes governance variants from VaultCommand; adds Debug trait to command types; introduces ensure_finished for trailing-byte validation.
Governance Contract
contract/vault/soroban/governance/src/lib.rs, contract/vault/soroban/governance/src/types.rs
Refactors execute_action to delegate to execute_vault_governance_action, which constructs GovernanceCommand payloads and invokes vault via execute_governance entrypoint; migrates abdication keying from Symbol to GovernanceActionKind; removes per-action string/symbol builders.
Governance Tests & Mocks
contract/vault/soroban/governance/src/tests.rs
Expands MockVault with execute_governance entrypoint, new storage getters for governance state (curator, skim recipient, fee accounts, restrictions, caps, supply queue), and new unit tests validating governance bridge behavior, timelock routing, and storage effects.
Vault Contract Entrypoints & Tests
contract/vault/soroban/src/contract/entrypoints.rs, contract/vault/soroban/src/tests.rs
Adds execute_governance entrypoint decoding GovernanceCommand and routing to internal handlers; refactors execute to dispatch via execute_public_command for non-governance VaultCommand; updates governance tests to use GovernanceCommand and removes VaultCommand governance variants from test assertions.
Integration Tests
contract/vault/soroban/tests/blend_e2e.rs, contract/vault/soroban/tests/integration_tests.rs
Updates governance-setting calls to use new execute_governance_command path with GovernanceCommand; refactors address serialization helper from address_text / sdk_text to address_wire / sdk_wire returning AllocString; retains standard vault operations via execute_command with updated address helpers.
Build Configuration
contract/vault/soroban/justfile
Adds check-4626-proxy recipe for compile check; removes set-curator admin operation recipe.

Sequence Diagram(s)

sequenceDiagram
    participant GovContract as Governance Contract
    participant VaultContract as Vault Contract
    participant Storage as Contract Storage
    
    GovContract->>GovContract: Proposal timelock matures
    GovContract->>GovContract: derive GovernanceCommand<br/>(SetGovernanceConfig,<br/>SetGovernancePolicy, or Skim)
    GovContract->>VaultContract: execute_governance(self, payload)
    VaultContract->>VaultContract: Decode GovernanceCommand
    VaultContract->>VaultContract: Validate caller == governance
    alt SetGovernanceConfig
        VaultContract->>Storage: Update governance config<br/>(curator, governance addr, etc.)
    else SetGovernancePolicy
        VaultContract->>Storage: Update governance policy<br/>(fees, supply queue, restrictions, caps)
    else Skim
        VaultContract->>Storage: Execute skim operation
    end
    VaultContract-->>GovContract: Result<(), ContractError>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • chore: update dependencies #404 — Touches governance contract dispatch logic (execute_action) in the same module with lint attributes, directly related to the refactored governance routing in this PR.

Suggested reviewers

  • peer2f00l
  • SmolinPavel
  • royalf00l
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: migrating Soroban vault governance to a compact bridge pattern.
Description check ✅ Passed The description is directly related to the changeset, explaining the governance migration, removal of nested dispatch, and WASM budget concerns that are evidenced throughout the file summaries.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 refactor/extract-governance

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • LINEAR integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

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

@carrion256
carrion256 force-pushed the refactor/extract-governance branch from 09b12ca to ec781b5 Compare April 23, 2026 09:44
@carrion256

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contract/vault/soroban/justfile (1)

1127-1130: ⚠️ Potential issue | 🟡 Minor

Remove stale set-curator help entry.

Line 1129 advertises just set-curator <new_curator_address>, but that recipe no longer exists in this file. This leaves operators with a broken command.

💡 Suggested doc cleanup
@@
 	`@echo` "ADMIN:"
 	`@echo` "  just pause / unpause"
-	`@echo` "  just set-curator <new_curator_address>"
 	`@echo` "  just set-metadata [name] [symbol] [decimals]"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contract/vault/soroban/justfile` around lines 1127 - 1130, The help section
in the justfile lists a non-existent recipe "set-curator <new_curator_address>"
causing a broken operator command; update the ADMIN help block by removing the
stale "just set-curator <new_curator_address>" line (or replace it with the
correct recipe name if one exists) so the displayed commands match actual
recipes (look for the ADMIN help echo lines that include "ADMIN:" and the
following three echo statements).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contract/vault/soroban/governance/src/types.rs`:
- Line 21: DataKey::Abdicated changed from carrying a Symbol to
Abdicated(GovernanceActionKind), which breaks reads of existing on-chain keys;
add a migration or fallback: implement a one-time migration function (e.g.,
migrate_legacy_abdicated_keys) that scans storage for DataKey::Abdicated(Symbol)
entries, converts them to DataKey::Abdicated(GovernanceActionKind) and rewrites
storage, or alter accessors is_abdicated() and require_not_abdicated() to try
both key formats (first check Abdicated(GovernanceActionKind) then fall back to
the legacy Abdicated(Symbol) lookup and treat a legacy hit as abdicated) and
mark/upgrade the key when found; reference DataKey::Abdicated,
GovernanceActionKind, is_abdicated(), require_not_abdicated() when locating
where to add the migration or fallback logic.

In `@contract/vault/soroban/justfile`:
- Around line 136-143: The new recipe check-4626-proxy is not listed in the help
output; update the justfile's help recipe (the target that prints available
commands) to include an entry for check-4626-proxy with a short description like
"Check the Soroban ERC-4626 proxy crate" so operators see it; locate the
help-related recipe in the justfile and add a line referencing check-4626-proxy
alongside the other documented recipes (use the exact recipe name
check-4626-proxy to ensure it matches).

In `@contract/vault/soroban/README.md`:
- Around line 15-20: The README incorrectly states that retained-config keys
ALLOCATORS, ALLOWED_ADAPTERS, and VIRTUAL_OFFSETS are reachable via
execute(payload); update the text to remove that claim and instead point
integrators to the governance entrypoint execute_governance(env, caller,
payload) (and note that SetTimelock/Other remain local to the governance
contract) so all references to execute(payload) as a path for config/governance
mutations are deleted or replaced accordingly (also update the repeated text at
lines 76-78).

In `@contract/vault/soroban/src/tests.rs`:
- Around line 383-390: Two governance tests call
SorobanVaultContract::execute_governance directly under
mock_all_auths_allowing_non_root_auth(), which bypasses the contract-to-contract
bridge and only exercises the post-auth handler path; update those tests
(test_governance_config_updates_allowed_adapters_without_supply_queue_constraint
and test_governance_policy_group_membership_empty_string_clears_membership) to
perform the governance call via env.invoke_contract using the same storage_tests
helper pattern used by test_execute_governance_bridge_happy_path so the full
authorization/bridge semantics are executed (replace direct
SorobanVaultContract::execute_governance calls with env.invoke_contract
invocations that target the SorobanVault contract and use the same payload
construction and caller setup as the other bridge tests).

In `@contract/vault/soroban/STRIDE.md`:
- Around line 157-159: The table row containing "Spoof.3" (the multi-line cell
describing execute_governance) is missing leading/trailing pipe characters on
continuation lines; update the Markdown table so multi-line cell content is
either placed on a single line or each wrapped line includes the table cell
delimiters (pipes) to satisfy markdown table linting — locate the row
referencing "Spoof.3" and "execute_governance" and add the missing
leading/trailing pipes for each continuation line or collapse the description
into one line to restore valid table formatting.

---

Outside diff comments:
In `@contract/vault/soroban/justfile`:
- Around line 1127-1130: The help section in the justfile lists a non-existent
recipe "set-curator <new_curator_address>" causing a broken operator command;
update the ADMIN help block by removing the stale "just set-curator
<new_curator_address>" line (or replace it with the correct recipe name if one
exists) so the displayed commands match actual recipes (look for the ADMIN help
echo lines that include "ADMIN:" and the following three echo statements).
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 32523839-7d7c-4edf-b1de-fe3171242b22

📥 Commits

Reviewing files that changed from the base of the PR and between 6b6ccc2 and ec781b5.

📒 Files selected for processing (11)
  • contract/vault/soroban/README.md
  • contract/vault/soroban/STRIDE.md
  • contract/vault/soroban/governance/src/lib.rs
  • contract/vault/soroban/governance/src/tests.rs
  • contract/vault/soroban/governance/src/types.rs
  • contract/vault/soroban/justfile
  • contract/vault/soroban/shared-types/src/lib.rs
  • contract/vault/soroban/src/contract/entrypoints.rs
  • contract/vault/soroban/src/tests.rs
  • contract/vault/soroban/tests/blend_e2e.rs
  • contract/vault/soroban/tests/integration_tests.rs

CurrentRestrictionMode,
CurrentRestrictionAccounts,
Abdicated(Symbol),
Abdicated(GovernanceActionKind),

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspect abdication key usage and any migration/fallback logic"
rg -n -C 3 'Abdicated|abdicate|is_abdicated|migrat|legacy|Symbol' contract/vault/soroban/governance/src

Repository: Templar-Protocol/contracts

Length of output: 12301


🏁 Script executed:

cd contract/vault/soroban/governance/src && git log --oneline -20 types.rs

Repository: Templar-Protocol/contracts

Length of output: 127


🏁 Script executed:

cd contract/vault/soroban/governance/src && git show HEAD:types.rs | head -30

Repository: Templar-Protocol/contracts

Length of output: 253


🏁 Script executed:

cd contract/vault/soroban/governance/src && git diff HEAD~1 types.rs | grep -A 5 -B 5 Abdicated || echo "No diff found in last commit"

Repository: Templar-Protocol/contracts

Length of output: 302


🏁 Script executed:

git show HEAD:contract/vault/soroban/governance/src/types.rs | head -40

Repository: Templar-Protocol/contracts

Length of output: 843


🏁 Script executed:

git log --all --oneline contract/vault/soroban/governance/src/types.rs | head -5

Repository: Templar-Protocol/contracts

Length of output: 211


🏁 Script executed:

git log --format="%H %s" | head -1

Repository: Templar-Protocol/contracts

Length of output: 160


🏁 Script executed:

git show bb6f46e:contract/vault/soroban/governance/src/types.rs 2>/dev/null | grep -A 10 "enum DataKey" || echo "File did not exist in prior commit"

Repository: Templar-Protocol/contracts

Length of output: 272


🏁 Script executed:

git diff bb6f46e ec781b5 -- contract/vault/soroban/governance/src/types.rs | grep -A 5 -B 5 "Abdicated"

Repository: Templar-Protocol/contracts

Length of output: 333


🏁 Script executed:

git diff bb6f46e ec781b5 -- contract/vault/soroban/governance/src/lib.rs | grep -A 10 -B 10 "migrat\|legacy\|fallback" || echo "No migration logic found in diff"

Repository: Templar-Protocol/contracts

Length of output: 102


🏁 Script executed:

grep -r "Abdicated(Symbol)" contract/vault/soroban/governance/src 2>/dev/null || echo "No legacy Symbol usage found"

Repository: Templar-Protocol/contracts

Length of output: 98


🏁 Script executed:

cat contract/vault/soroban/governance/src/lib.rs | sed -n '1362,1373p'

Repository: Templar-Protocol/contracts

Length of output: 423


Add migration or fallback logic for the Abdicated storage key type change.

DataKey::Abdicated(Symbol) changed to Abdicated(GovernanceActionKind) in the current commit. If existing on-chain abdication state uses the old Symbol-based key format, the new code will not retrieve those entries, causing is_abdicated() and require_not_abdicated() to return false. Previously abdicated actions would become executable again.

Add either:

  • A migration function that rewrites legacy Symbol keys to the new enum format, or
  • A fallback in is_abdicated() / require_not_abdicated() that checks both key formats

This must be in place before any deployment with existing state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contract/vault/soroban/governance/src/types.rs` at line 21,
DataKey::Abdicated changed from carrying a Symbol to
Abdicated(GovernanceActionKind), which breaks reads of existing on-chain keys;
add a migration or fallback: implement a one-time migration function (e.g.,
migrate_legacy_abdicated_keys) that scans storage for DataKey::Abdicated(Symbol)
entries, converts them to DataKey::Abdicated(GovernanceActionKind) and rewrites
storage, or alter accessors is_abdicated() and require_not_abdicated() to try
both key formats (first check Abdicated(GovernanceActionKind) then fall back to
the legacy Abdicated(Symbol) lookup and treat a legacy hit as abdicated) and
mark/upgrade the key when found; reference DataKey::Abdicated,
GovernanceActionKind, is_abdicated(), require_not_abdicated() when locating
where to add the migration or fallback logic.

Comment on lines +136 to +143
# Check the Soroban ERC-4626 proxy crate.
check-4626-proxy:
@echo "Checking Soroban ERC-4626 proxy crate..."
@RUSTFLAGS="${RUSTFLAGS:+$RUSTFLAGS }{{remap_rustflags}}" cargo check \
--manifest-path "{{root}}/Cargo.toml" \
-p templar-4626-proxy-soroban
@echo "✓ Checked: templar-4626-proxy-soroban"

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.

⚠️ Potential issue | 🟡 Minor

Document check-4626-proxy in help output.

Line 137 adds a new operator-facing recipe, but it is not exposed in the help command output, so it’s easy to miss.

💡 Suggested doc update
@@
 	`@echo` "BUILD:"
 	`@echo` "  just build                    Build vault WASM"
+	`@echo` "  just check-4626-proxy         Cargo-check ERC-4626 proxy crate"
 	`@echo` "  just build-blend-adapter      Build Blend adapter WASM"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contract/vault/soroban/justfile` around lines 136 - 143, The new recipe
check-4626-proxy is not listed in the help output; update the justfile's help
recipe (the target that prints available commands) to include an entry for
check-4626-proxy with a short description like "Check the Soroban ERC-4626 proxy
crate" so operators see it; locate the help-related recipe in the justfile and
add a line referencing check-4626-proxy alongside the other documented recipes
(use the exact recipe name check-4626-proxy to ensure it matches).

Comment on lines +15 to +20
Governance timelock/orchestration lives in the dedicated `contract/vault/soroban/governance`
contract. The runtime still applies canonical governance state changes. Vault-bound governance
actions cross the contract boundary via `execute_governance(env, caller, payload)`, where the
payload carries a `GovernanceCommand`. `SetTimelock` and `Other` actions stay local to the
governance contract. The generic `execute(payload)` path remains for user flows and for the
retained execute-path config subset (`ALLOCATORS`, `ALLOWED_ADAPTERS`, `VIRTUAL_OFFSETS`).

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.

⚠️ Potential issue | 🟡 Minor

Remove the retained-execute config claim.

The runtime no longer accepts governance/config mutations through execute(payload). Documenting ALLOCATORS, ALLOWED_ADAPTERS, and VIRTUAL_OFFSETS as still reachable on that path will send integrators to a dead entrypoint; these bullets should point only to execute_governance(...).

Also applies to: 76-78

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contract/vault/soroban/README.md` around lines 15 - 20, The README
incorrectly states that retained-config keys ALLOCATORS, ALLOWED_ADAPTERS, and
VIRTUAL_OFFSETS are reachable via execute(payload); update the text to remove
that claim and instead point integrators to the governance entrypoint
execute_governance(env, caller, payload) (and note that SetTimelock/Other remain
local to the governance contract) so all references to execute(payload) as a
path for config/governance mutations are deleted or replaced accordingly (also
update the repeated text at lines 76-78).

Comment on lines +383 to +390
fn execute_governance_command(
env: &Env,
caller: &SdkAddress,
command: &GovernanceCommand,
) -> Result<(), crate::error::ContractError> {
let payload = Bytes::from_slice(env, &command.encode());
SorobanVaultContract::execute_governance(env.clone(), caller.clone(), payload)
}

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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

In Soroban SDK tests, does directly calling a contract function under mock_all_auths*exercise contract-to-contract authorization for a contractAddress, or is env.invoke_contract from a registered contract required to test that path?

💡 Result:

Directly calling a contract function via the generated ContractClient under env.mock_all_auths does not fully exercise contract-to-contract authorization for a contract Address. It primarily tests require_auth for account Addresses by mocking the check_auth calls on those contract accounts. To properly test the contract-to-contract authorization path (where one contract authorizes sub-invocations to another contract on its own behalf using env.authorize_as_current_contract and Address::require_auth for contract invoker auth), you need to use env.invoke_contract (or equivalent) from within a registered "caller" contract context, typically set up with env.as_contract(&caller_contract_id, || { ... }). Key evidence: - Standard client calls with mock_all_auths record auths for the top-level invocation and mock account auths (sources 3,17,22). - Contract account check_auth testing explicitly uses env.try_invoke_contract_check_auth to emulate host calls during require_auth, separate from normal function invocation (source 15). - Contract-to-contract auth uses InvokerContractAuthEntry for sub-invocations, authorized via authorize_as_current_contract before env.invoke_contract (sources 2,3,13,21). - No sources show client calls exercising invoker contract auth; all examples use direct client for account auth testing, and special utils or contract context for deeper paths. mock_all_auths skips check_auth entirely (source 2), so it mocks the account auth path but doesn't test the contract's custom auth logic. For full coverage, combine with try_invoke_contract_check_auth or actual C2C calls.

Citations:


🏁 Script executed:

# First, check the file structure and size
wc -l contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 110


🏁 Script executed:

# Read the context around lines 383-390 to see the full helper and surrounding tests
sed -n '350,420p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 2676


🏁 Script executed:

# Search for invoke_contract usage to check if contract-to-contract tests exist
rg "invoke_contract" contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 512


🏁 Script executed:

# Search for governance contract registration or setup patterns
rg "governance" contract/vault/soroban/src/tests.rs -A 2 -B 2 | head -100

Repository: Templar-Protocol/contracts

Length of output: 3132


🏁 Script executed:

# Find both execute_governance_command definitions with context
rg "fn execute_governance_command" contract/vault/soroban/src/tests.rs -A 5 -B 2

Repository: Templar-Protocol/contracts

Length of output: 484


🏁 Script executed:

# Find line numbers for both helpers
rg -n "fn execute_governance_command" contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 148


🏁 Script executed:

# Check which tests call which helper - search for calls to execute_governance_command
rg "execute_governance_command\(" contract/vault/soroban/src/tests.rs -B 5 | head -80

Repository: Templar-Protocol/contracts

Length of output: 1287


🏁 Script executed:

# Also check for direct calls to execute_governance to see the pattern
rg "SorobanVaultContract::execute_governance" contract/vault/soroban/src/tests.rs -B 3 -A 2

Repository: Templar-Protocol/contracts

Length of output: 688


🏁 Script executed:

# Get full context for both helpers to understand their usage
sed -n '383,395p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 668


🏁 Script executed:

# Get the second helper
sed -n '1939,1955p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 649


🏁 Script executed:

# Now check which tests call the first helper (lines 383-390) and which tests are affected
# Get all test functions that call execute_governance_command
rg "#\[.*test" contract/vault/soroban/src/tests.rs -A 50 | rg -B 50 "execute_governance_command" | head -150

Repository: Templar-Protocol/contracts

Length of output: 5787


🏁 Script executed:

# Check which helper the newer tests (around governance) use - search tests after line 1900
sed -n '1900,2100p' contract/vault/soroban/src/tests.rs | grep -n "execute_governance_command\|invoke_contract" | head -20

Repository: Templar-Protocol/contracts

Length of output: 145


🏁 Script executed:

# Check the structure - which module is the first helper in and which module is the second?
sed -n '350,395p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 1914


🏁 Script executed:

# Check what module contains line 1939
sed -n '1900,1950p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 2532


🏁 Script executed:

# Now let's see which tests actually USE the first helper (line 383)
# by checking test functions that call it before line 1900
sed -n '380,1900p' contract/vault/soroban/src/tests.rs | rg "#\[.*test" -A 30 | rg -B 30 "execute_governance_command" | head -100

Repository: Templar-Protocol/contracts

Length of output: 1040


🏁 Script executed:

# Check if there are tests using invoke_contract directly for governance (C2C pattern)
rg "test.*governance" contract/vault/soroban/src/tests.rs -i | head -30

Repository: Templar-Protocol/contracts

Length of output: 417


🏁 Script executed:

# Let's identify exactly which tests are NEW by looking for tests that call the first helper (before line 1900)
rg -n "fn test_" contract/vault/soroban/src/tests.rs | awk -F: '$1 < 1900 {print}' | tail -20

Repository: Templar-Protocol/contracts

Length of output: 1004


🏁 Script executed:

# And check which tests call the first helper (direct call)
sed -n '380,1900p' contract/vault/soroban/src/tests.rs | grep -n "fn test_" | tail -10

Repository: Templar-Protocol/contracts

Length of output: 564


🏁 Script executed:

# Let's be more specific - check the exact context of test_execute_governance_bridge_happy_path
rg -n "test_execute_governance_bridge_happy_path" contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 127


🏁 Script executed:

# Get the test to see which helper it uses
sed -n '2020,2100p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 3428


🏁 Script executed:

# Look at the actual new tests - check test_execute_governance_rejects_unauthorized_callers
sed -n '2630,2680p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 1951


🏁 Script executed:

# Check test_execute_governance_rejects_malformed_payload as well
sed -n '2680,2730p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 2296


🏁 Script executed:

# Determine which module each test belongs to by checking mod declarations
rg -n "^mod " contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 212


🏁 Script executed:

# Now let's understand which tests in contract_tests use the first helper
sed -n '286,1397p' contract/vault/soroban/src/tests.rs | grep -n "fn test_\|execute_governance_command"

Repository: Templar-Protocol/contracts

Length of output: 1576


🏁 Script executed:

# Check what specific governance tests exist in contract_tests (the module containing line 383)
sed -n '286,1397p' contract/vault/soroban/src/tests.rs | grep -n "test.*governance\|execute_governance_command" -i

Repository: Templar-Protocol/contracts

Length of output: 151


🏁 Script executed:

# Let's look at tests that DO use the first helper directly
sed -n '700,1100p' contract/vault/soroban/src/tests.rs | grep -B 10 "execute_governance_command"

Repository: Templar-Protocol/contracts

Length of output: 321


🏁 Script executed:

# Let me see the full test_execute_governance_bridge_happy_path to confirm it uses invoke_contract properly
sed -n '2608,2680p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 2995


🏁 Script executed:

# And check test_execute_governance_rejects_unauthorized_callers fully
sed -n '2650,2730p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 3414


🏁 Script executed:

# Check if test_set_virtual_offsets_updates_contract_storage (which uses first helper) properly sets up governance
sed -n '675,730p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 2665


🏁 Script executed:

# Let me check which tests in contract_tests (before line 1398) actually use execute_governance_command
sed -n '286,1397p' contract/vault/soroban/src/tests.rs | grep -B 20 "execute_governance_command" | head -50

Repository: Templar-Protocol/contracts

Length of output: 1521


🏁 Script executed:

# Check test_set_virtual_offsets_updates_contract_storage more carefully
sed -n '675,750p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 3507


🏁 Script executed:

# Key question: which module does test_execute_governance_bridge_happy_path belong to?
# It's at line 2608, which is after line 1910 (storage_tests), so it's in storage_tests
# Confirm by checking the module boundaries
sed -n '1908,1915p' contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 353


🏁 Script executed:

# Now verify that test_execute_governance_bridge_happy_path calls the storage_tests version 
# (4 parameters) not the contract_tests version (3 parameters)
sed -n '2608,2650p' contract/vault/soroban/src/tests.rs | grep -n "execute_governance_command"

Repository: Templar-Protocol/contracts

Length of output: 108


🏁 Script executed:

# Count parameters in the call at line 2630 area to confirm
sed -n '2625,2640p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 619


🏁 Script executed:

# Find the line numbers of ALL the governance-related test functions
rg -n "fn test.*governance\|fn test_execute_governance" contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 52


🏁 Script executed:

# Check where test_governance_config_updates_allowed_adapters is located
sed -n '1200,1300p' contract/vault/soroban/src/tests.rs | grep -n "test_governance\|fn test_"

Repository: Templar-Protocol/contracts

Length of output: 127


🏁 Script executed:

# Search for all governance tests mentioned in the review
rg "test_governance_config_updates_allowed_adapters\|test_governance_policy_group_membership\|test_execute_governance" contract/vault/soroban/src/tests.rs -n

Repository: Templar-Protocol/contracts

Length of output: 52


🏁 Script executed:

# Let's verify where test_set_virtual_offsets_updates_contract_storage is and whether it's in contract_tests
sed -n '670,710p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 1963


🏁 Script executed:

# Let me search for test_set_virtual_offsets by line number
grep -n "test_set_virtual_offsets_updates_contract_storage" contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 134


🏁 Script executed:

# And check if the new tests mentioned in the original review exist
grep -n "test_governance_config_updates\|test_governance_policy_group\|test_execute_governance_bridge\|test_execute_governance_rejects" contract/vault/soroban/src/tests.rs

Repository: Templar-Protocol/contracts

Length of output: 442


🏁 Script executed:

# Let me just search more broadly for all test functions that contain "governance"
grep -n "fn test" contract/vault/soroban/src/tests.rs | grep -i "govern"

Repository: Templar-Protocol/contracts

Length of output: 442


🏁 Script executed:

# Let me check test_set_virtual_offsets_updates_contract_storage - how does it use execute_governance_command?
sed -n '960,1000p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 1727


🏁 Script executed:

# Check whether test_governance_config_updates_allowed_adapters uses invoke_contract or direct call
sed -n '2490,2510p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 1087


🏁 Script executed:

# Verify test_execute_governance_rejects_unauthorized_callers uses invoke_contract
sed -n '2721,2760p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 1805


🏁 Script executed:

# Let me verify test_governance_config_updates_allowed_adapters continues to see how it invokes governance
sed -n '2490,2540p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 2306


🏁 Script executed:

# And check test_execute_governance_rejects_malformed_payload to see its full pattern
sed -n '2811,2860p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 1396


🏁 Script executed:

# Now I see the pattern. Let me verify: test_governance_config_updates_allowed_adapters 
# calls SorobanVaultContract::execute_governance directly (line 38), not through invoke_contract
# This is the issue! Let me confirm the exact call
sed -n '2525,2540p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 674


🏁 Script executed:

# And test_governance_policy_group_membership - check its implementation
sed -n '2540,2610p' contract/vault/soroban/src/tests.rs | cat -n

Repository: Templar-Protocol/contracts

Length of output: 3275


Some governance tests lack contract-to-contract bridge coverage.

Tests test_governance_config_updates_allowed_adapters_without_supply_queue_constraint and test_governance_policy_group_membership_empty_string_clears_membership call SorobanVaultContract::execute_governance directly under mock_all_auths_allowing_non_root_auth(), which only exercises the post-auth handler path, not the real contract-to-contract invocation. However, test_execute_governance_bridge_happy_path and the rejection tests do use the proper env.invoke_contract pattern via the storage_tests helper, so bridge coverage exists. Consider whether the earlier policy tests should also use the invoke_contract helper to ensure authorization semantics are properly exercised.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contract/vault/soroban/src/tests.rs` around lines 383 - 390, Two governance
tests call SorobanVaultContract::execute_governance directly under
mock_all_auths_allowing_non_root_auth(), which bypasses the contract-to-contract
bridge and only exercises the post-auth handler path; update those tests
(test_governance_config_updates_allowed_adapters_without_supply_queue_constraint
and test_governance_policy_group_membership_empty_string_clears_membership) to
perform the governance call via env.invoke_contract using the same storage_tests
helper pattern used by test_execute_governance_bridge_happy_path so the full
authorization/bridge semantics are executed (replace direct
SorobanVaultContract::execute_governance calls with env.invoke_contract
invocations that target the SorobanVault contract and use the same payload
construction and caller setup as the other bridge tests).

Comment on lines +157 to +159
| | **Spoof.3** — `execute_governance` verifies the outer caller is the governance contract
before decoding or dispatching the payload, preventing direct invocation that would bypass
governance timelocks. Interactions: I8, I19–I22. |

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.

🧹 Nitpick | 🔵 Trivial

Fix table formatting for consistent Markdown linting.

Static analysis flagged missing table pipes on multi-line cells. While the content renders correctly in most viewers, proper table formatting improves maintainability.

📝 Example fix for multi-line table cells

Consider either:

  1. Keeping table cell content on a single line (may be long)
  2. Using proper continuation with trailing pipes:
-| | **Spoof.3** — `execute_governance` verifies the outer caller is the governance contract
-before decoding or dispatching the payload, preventing direct invocation that would bypass
-governance timelocks. Interactions: I8, I19–I22. |
+| | **Spoof.3** — `execute_governance` verifies the outer caller is the governance contract before decoding or dispatching the payload, preventing direct invocation that would bypass governance timelocks. Interactions: I8, I19–I22. |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| | **Spoof.3**`execute_governance` verifies the outer caller is the governance contract
before decoding or dispatching the payload, preventing direct invocation that would bypass
governance timelocks. Interactions: I8, I19–I22. |
| | **Spoof.3**`execute_governance` verifies the outer caller is the governance contract before decoding or dispatching the payload, preventing direct invocation that would bypass governance timelocks. Interactions: I8, I19–I22. |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 157-157: Table pipe style
Expected: leading_and_trailing; Actual: leading_only; Missing trailing pipe

(MD055, table-pipe-style)


[warning] 158-158: Table pipe style
Expected: leading_and_trailing; Actual: no_leading_or_trailing; Missing leading pipe

(MD055, table-pipe-style)


[warning] 158-158: Table pipe style
Expected: leading_and_trailing; Actual: no_leading_or_trailing; Missing trailing pipe

(MD055, table-pipe-style)


[warning] 158-158: Table column count
Expected: 2; Actual: 1; Too few cells, row will be missing data

(MD056, table-column-count)


[warning] 159-159: Table pipe style
Expected: leading_and_trailing; Actual: trailing_only; Missing leading pipe

(MD055, table-pipe-style)


[warning] 159-159: Table column count
Expected: 2; Actual: 1; Too few cells, row will be missing data

(MD056, table-column-count)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contract/vault/soroban/STRIDE.md` around lines 157 - 159, The table row
containing "Spoof.3" (the multi-line cell describing execute_governance) is
missing leading/trailing pipe characters on continuation lines; update the
Markdown table so multi-line cell content is either placed on a single line or
each wrapped line includes the table cell delimiters (pipes) to satisfy markdown
table linting — locate the row referencing "Spoof.3" and "execute_governance"
and add the missing leading/trailing pipes for each continuation line or
collapse the description into one line to restore valid table formatting.

@carrion256

Copy link
Copy Markdown
Collaborator Author

Superseded by the SPR-managed stack: #417 -> #418 -> #419.

@carrion256 carrion256 closed this Apr 23, 2026
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.

1 participant