Title: feat(contracts): Contract upgradeability with timelock
Labels: contracts, security
Description:
Smart contracts need an upgrade path for bug fixes and new features
without requiring full redeployment. This issue adds a two-step
upgrade pattern with a 48-hour timelock to all Soroban contracts,
ensuring upgrades are transparent and time-locked against rushed
or malicious changes.
What Needs to Be Done:
- Add to each contract (invoice_registry, escrow, yield_oracle):
UpgradeProposal struct { new_wasm_hash: BytesN<32>, proposed_at: u64 }
propose_upgrade(admin, new_wasm_hash) — starts 48h timelock
execute_upgrade(admin) — callable only after 48h timelock expires
cancel_upgrade(admin) — cancels pending proposal
get_upgrade_proposal() — view, returns pending proposal or None
- Timelock: 48 hours = 172800 seconds
- Events:
("upgrade", "proposed") → { new_wasm_hash, proposed_at, executable_after }
("upgrade", "executed") → { new_wasm_hash, executed_at }
("upgrade", "canceled") → { canceled_by, canceled_at }
- DEVELOPMENT.md documentation:
- Step-by-step upgrade process
- How to verify the new WASM hash before proposing
- Emergency cancel procedure
Key Files:
- contracts/invoice_registry/src/lib.rs (update)
- contracts/escrow/src/lib.rs (update)
- contracts/yield_oracle/src/lib.rs (update)
- DEVELOPMENT.md (new or update)
Acceptance Criteria:
- propose_upgrade() requires admin.require_auth()
- execute_upgrade() fails if called before 48h
- execute_upgrade() fails if no proposal exists
- cancel_upgrade() removes pending proposal
- Events emitted on every action
- DEVELOPMENT.md explains full upgrade process
- cargo test passes with 95%+ coverage for upgrade paths
- Tests cover: propose, execute after timelock, execute before
timelock (should fail), cancel, unauthorized access
Security Notes:
- Only admin can propose, execute, or cancel
- Timelock cannot be shortened after proposal
- Proposal stores exact executable_after timestamp
Branch: feat/contract-upgradeability
Commit: feat(contracts): add two-step upgrade pattern with 48h timelock across all contracts
Title: feat(contracts): Contract upgradeability with timelock
Labels: contracts, security
Description:
Smart contracts need an upgrade path for bug fixes and new features
without requiring full redeployment. This issue adds a two-step
upgrade pattern with a 48-hour timelock to all Soroban contracts,
ensuring upgrades are transparent and time-locked against rushed
or malicious changes.
What Needs to Be Done:
UpgradeProposal struct { new_wasm_hash: BytesN<32>, proposed_at: u64 }
propose_upgrade(admin, new_wasm_hash) — starts 48h timelock
execute_upgrade(admin) — callable only after 48h timelock expires
cancel_upgrade(admin) — cancels pending proposal
get_upgrade_proposal() — view, returns pending proposal or None
("upgrade", "proposed") → { new_wasm_hash, proposed_at, executable_after }
("upgrade", "executed") → { new_wasm_hash, executed_at }
("upgrade", "canceled") → { canceled_by, canceled_at }
Key Files:
Acceptance Criteria:
timelock (should fail), cancel, unauthorized access
Security Notes:
Branch: feat/contract-upgradeability
Commit: feat(contracts): add two-step upgrade pattern with 48h timelock across all contracts