This document outlines the mandatory formal verification steps, configuration parameters, and emergency readiness checks that must be successfully executed before and during the deployment of the NeuroWealthVault smart contract to the Stellar Mainnet.
- Key Management Setup (Separate Owner & Agent Keys)
- Initialization Parameters & Deployment Verification
- Administrative Caps & Deposit Limits Configuration
- Blend Pool Integration & Address Verification
- DEX Pool Integration & Address Verification
- Emergency Procedures & Pause Drill Runbook
- Upgrade & Governance Multisig Plan
- Third-Party Security Audit & Formal Sign-off
To uphold the principle of least privilege and prevent single points of failure, the Owner and Agent keys must be completely separate and generated independently.
- Owner (Cold/Multisig): Holds sensitive administrative capabilities like contract pausing, unpausing, TVL/cap changes, and contract upgrades. This key represents a high-value target and should be kept securely offline (e.g., hardware wallet or multi-signature account setup).
- AI Agent (Hot): Used by the automated backend system to submit frequent rebalancing signals and assets updates (
rebalanceandupdate_total_assets). Since it lives in a hot environment (server memory), it faces a higher compromise risk. - The Risk: If the Owner and Agent keys are the same, a compromise of the AI agent backend would immediately compromise the ownership and control of the entire contract, enabling an attacker to upgrade the contract or block users from withdrawing.
- Generate Independent Keypairs: Ensure that the Owner address (
$G_{owner}$ ) and Agent address ($G_{agent}$ ) are completely separate and do not share any key material. - Establish Key Storage Environments:
- Owner private key: Saved in a secure offline HSM, multi-sig hardware wallet, or Stellar Multisig account.
- Agent private key: Stored in a secure environment variables vault (e.g., AWS Secrets Manager, Vault, Supabase Vault) with restricted read access.
- Pre-Launch Address Verification:
- Query testnet/mainnet deploy keys:
owner_addr=$(stellar contract invoke --id $VAULT_CONTRACT_ID --network mainnet -- get_owner) agent_addr=$(stellar contract invoke --id $VAULT_CONTRACT_ID --network mainnet -- get_agent)
- Verify that
$owner_addr!=$agent_addr.
- Query testnet/mainnet deploy keys:
Automated check —
scripts/verify-deployment.shasserts owner address, agent address, and owner ≠ agent separation in one command:VAULT_CONTRACT_ID=C... NETWORK=mainnet \ OWNER_ADDRESS=G... AGENT_ADDRESS=G... AGENT_SECRET_KEY=S... \ USDC_TOKEN_ADDRESS=G... \ ./scripts/verify-deployment.sh
Initialization of the NeuroWealthVault uses a cryptographic commitment to protect against front-running. The deployer key must immediately call initialize after deployment.
- The contract verifies that the
deployeraddress combined with the deployedsaltcryptographically reproduces the contract address and requires deployer's authentication (deployer.require_auth()). - After successful initialization, the temporary deployer key has no administrative powers.
- Deployer Key Separation: Generate a clean, single-use
deployerkeypair. Fund it with enough native XLM to cover deployment fees. - Parameter Configuration Verification: Double-check the mainnet initialization arguments before submitting the transaction:
-
--deployer: Address of the temporary deployer key. -
--owner: Verified cold/multisig owner address. -
--agent: Verified AI agent address. -
--usdc_token: Official Stellar Mainnet USDC Token address (GBBD67VQMKA676776SGXN6776...- verify on Stellar Expert). -
--salt: A securely generated 32-byte hash.
-
- Execute and Discard Deployer Key:
stellar contract invoke \ --id $VAULT_CONTRACT_ID \ --source deployer \ --network mainnet \ -- \ initialize \ --deployer $DEPLOYER_ADDRESS \ --owner $OWNER_ADDRESS \ --agent $AGENT_ADDRESS \ --usdc_token $USDC_TOKEN_ADDRESS \ --salt $SALT
- Post-Init Read Verification:
- Run
get_ownerto confirm it returns$OWNER_ADDRESS. - Run
get_agentto confirm it returns$AGENT_ADDRESS. - Run
get_usdc_tokento confirm it returns$USDC_TOKEN_ADDRESS.
- Run
- Discard Deployer Key: Erase/discard the temporary deployer key. It should never be reused.
To limit financial risk and systemic exposure during the initial stages of launch, safety caps must be configured.
- TVL Cap: Prevents the vault from accepting more than a specific aggregate deposit, limiting the overall capital at risk.
- User Deposit Cap: Limits exposure per single user, preventing whales from dominating the pool and mitigating risks of heavy individual exposure.
- Deposit Limits (Min/Max): Enforces transaction thresholds (minimum of 1 USDC to protect against dust attacks and first-depositor inflation attacks).
- Initial TVL Cap Setup: Determine the conservative launch phase TVL cap (e.g., $100,000 USD represented as
100000000000base units - 7 decimals). - Initial User Deposit Cap Setup: Determine the initial limit per user (e.g., $5,000 USD represented as
5000000000base units). - Enforce Caps: Call
set_capsvia the Owner key:stellar contract invoke \ --id $VAULT_CONTRACT_ID \ --source owner \ --network mainnet \ -- \ set_caps \ --user_deposit_cap 5000000000 \ --tvl_cap 100000000000 - Set Transaction Limits: Call
set_deposit_limits(e.g., min 1 USDC, max 5,000 USDC):stellar contract invoke \ --id $VAULT_CONTRACT_ID \ --source owner \ --network mainnet \ -- \ set_deposit_limits \ --min 1000000 \ --max 5000000000 - Verify Settings: Query getters
get_tvl_cap,get_user_deposit_cap,get_min_deposit, andget_max_depositto verify correctness.
Automated check —
scripts/verify-deployment.shfetches all four caps and compares them against your declared expected values:VAULT_CONTRACT_ID=C... NETWORK=mainnet \ OWNER_ADDRESS=G... AGENT_ADDRESS=G... AGENT_SECRET_KEY=S... \ USDC_TOKEN_ADDRESS=G... \ EXPECTED_TVL_CAP=100000000000 \ EXPECTED_USER_DEPOSIT_CAP=5000000000 \ EXPECTED_MIN_DEPOSIT=1000000 \ EXPECTED_MAX_DEPOSIT=5000000000 \ ./scripts/verify-deployment.shThe script exits non-zero if any cap does not match or if an
EXPECTED_*variable is missing.
The NeuroWealth AI agent deploys assets into Blend lending pools. Registering the correct, verified mainnet contract address for Blend is critical.
- Deploying to an incorrect or malicious pool address can lead to instant loss of principal funds.
- While the contract's
set_blend_poolmethod performs interface probing by callingbalance()to confirm the contract conforms to the expected Blend pool structure, this does not guarantee the address belongs to the genuine Blend protocol.
- Retrieve Official Blend Registries: Match the Blend mainnet pool address against:
- Official Blend Protocol documentation.
- Verified GitHub repository resources or Blend UI configurations.
- The verified on-chain deployment logs on a block explorer (Stellar Expert).
- Perform Interface/State Verification: Call the pool's read methods directly on the mainnet RPC to check pool parameters.
- Register Verified Blend Pool: Call
set_blend_poolusing the Owner key:stellar contract invoke \ --id $VAULT_CONTRACT_ID \ --source owner \ --network mainnet \ -- \ set_blend_pool \ --owner $OWNER_ADDRESS \ --pool_address $VERIFIED_BLEND_POOL_ADDRESS
- Read Verification: Query
get_blend_poolon the vault to confirm the registered address matches the verified Blend pool address.
Automated check — set
BLEND_POOL_ADDRESSandscripts/verify-deployment.shwill assert thatget_blend_pool()returns that exact address (not null):VAULT_CONTRACT_ID=C... NETWORK=mainnet \ OWNER_ADDRESS=G... AGENT_ADDRESS=G... AGENT_SECRET_KEY=S... \ USDC_TOKEN_ADDRESS=G... \ BLEND_POOL_ADDRESS=C... \ ./scripts/verify-deployment.sh
The NeuroWealth AI agent deploys assets into DEX liquidity pools for active trading strategies. Registering the correct, verified mainnet contract address for the target DEX pool is critical.
- Deploying to an incorrect, unverified, or malicious pool address could result in permanent loss of funds or slippage exploitation.
- Interface validation alone does not confirm that the DEX pool is genuine or safe. Address verification against trusted registries is mandatory before deployment.
- Retrieve Official DEX Registries: Match the DEX pool mainnet address against official protocol documentation and verified on-chain deployment logs.
- Perform Interface/State Verification: Verify DEX pool parameters and liquidity depth.
- Register Verified DEX Pool: Call
set_dex_poolusing the Owner key:stellar contract invoke \ --id $VAULT_CONTRACT_ID \ --source owner \ --network mainnet \ -- \ set_dex_pool \ --owner $OWNER_ADDRESS \ --pool_address $VERIFIED_DEX_POOL_ADDRESS
- Read Verification: Query
get_dex_poolon the vault to confirm the registered address matches the verified DEX pool address.
Automated check — set
DEX_POOL_ADDRESSandscripts/verify-deployment.shwill assert thatget_dex_pool()returns that exact address (not null):VAULT_CONTRACT_ID=C... NETWORK=mainnet \ OWNER_ADDRESS=G... AGENT_ADDRESS=G... AGENT_SECRET_KEY=S... \ USDC_TOKEN_ADDRESS=G... \ DEX_POOL_ADDRESS=C... \ ./scripts/verify-deployment.sh
Before deploying to Mainnet, the team must run an on-chain Pause Drill on Testnet to guarantee emergency mechanisms function as intended and operators are trained in execution.
For detailed incident response procedures, refer to:
- Owner-Compromise Response Runbook
- Agent-Key Compromise Runbook - Detection, timelock rotation, and user communication for agent-key incidents
- The
pausefunction blocks all deposits, withdrawals, and rebalances during an active hack, protocol compromise, or market emergency. - Operators must be familiar with the latency, transaction structure, and consequences of pausing/unpausing the contract.
-
Trigger Emergency Pause: Owner invokes
pauseon testnet.stellar contract invoke --id $TESTNET_VAULT_CONTRACT_ID --source owner --network testnet -- pause --owner $OWNER_ADDRESS
-
Verify State Updates: Confirm
is_paused()returnstrue. -
Verify Security Invariants (Deposits): Attempt a test deposit.
-
Expected Result: The transaction MUST fail and revert with
VaultError::Paused(Error Code35).
-
Expected Result: The transaction MUST fail and revert with
-
Verify Security Invariants (Withdrawals): Attempt a test withdrawal.
-
Expected Result: The transaction MUST fail and revert with
VaultError::Paused(Error Code35).
-
Expected Result: The transaction MUST fail and revert with
-
Verify Security Invariants (Rebalances): Attempt an AI agent rebalance trigger.
-
Expected Result: The transaction MUST fail and revert with
VaultError::Paused(Error Code35).
-
Expected Result: The transaction MUST fail and revert with
-
Trigger Resume (Unpause): Owner invokes
unpause.stellar contract invoke --id $TESTNET_VAULT_CONTRACT_ID --source owner --network testnet -- unpause --owner $OWNER_ADDRESS
-
Verify Resumed Operation: Verify that
is_paused()returnsfalse, and normal deposits, withdrawals, and rebalances execute successfully.
- Testnet Drill Completed successfully: Sign off on the drill.
The pause drill above proves the mechanism works; this drill measures how fast
the organization can use it. It exercises the full detection → response
chain — monitoring alert fires, on-call is paged, emergency_pause lands
on-chain — and records the elapsed time as the incident Recovery Time
Objective (RTO) baseline for mainnet.
Cadence: run once before mainnet launch, then quarterly. Rotate which operator is on-call so every keyholder has executed a pause under time pressure at least once.
Roles:
- Drill conductor — injects the signal, keeps the timestamp log, does not assist the responder.
- On-call responder — receives the page and executes the runbook exactly as they would in a real incident (no pre-warming of CLI sessions or keys).
Procedure (all timestamps in UTC, captured by the conductor):
-
T0 — Inject simulated exploit signal. Without pre-announcing the exact
time, the conductor triggers one of the monitoring alerts from
monitoring.md against the devnet deployment — e.g. submit
transactions that trip
withdrawal_spike, or fire the alert rule directly in the monitoring stack with a[DRILL]prefix. - T1 — Alert fired. Timestamp when the monitoring system actually emits the page/notification.
- T2 — Responder acknowledged. Timestamp when the on-call operator acks the page.
-
T3 —
emergency_pausesubmitted. Responder runs, from the standard runbook (no shortcuts):stellar contract invoke --id $DEVNET_VAULT_CONTRACT_ID --source owner --network testnet -- emergency_pause --owner $OWNER_ADDRESS
-
T4 — Pause confirmed on-chain. Timestamp of the ledger that includes
the transaction; verify
is_paused()returnstrueand theEmergencyPausedEventwas emitted. -
Debrief. Compute
RTO = T4 − T0. Record every blocker encountered (key retrieval friction, missing docs, RPC issues, alert routing delays) and file an issue for each. Unpause the devnet vault and confirm normal operation resumes.
RTO target (mainnet): T4 − T0 ≤ 15 minutes, with T3 − T2 ≤ 5 minutes.
A drill exceeding the target must be re-run after the identified blockers are
fixed; do not sign off mainnet readiness on a failed drill.
Drill log (append one row per drill; keep raw timestamp notes in the incident-response log):
| Date (UTC) | Responder | T0 signal | T1 alert | T2 ack | T3 submitted | T4 confirmed | RTO (T4−T0) | Target met | Blockers / follow-ups |
|---|---|---|---|---|---|---|---|---|---|
- Game-day drill executed in devnet with all five timestamps captured in the log above.
- Measured RTO recorded and ≤ 15-minute target (re-run after fixes if not).
- Blockers filed as issues and assigned owners.
The Owner key holds upgrade privileges. To secure the contract against single-key compromise or loss, the owner account should be configured with multi-signature security.
- Soroban allows upgrading contract code. An attacker possessing the owner key could upload a malicious WASM binary to hijack user funds.
- The instant
upgrade()entrypoint has been replaced by a two-step timelocked flow (Issue #316):schedule_upgrade→ waitUPGRADE_TIMELOCK_LEDGERS(17,280 ledgers ≈ 24 h) →execute_upgrade, withcancel_upgradeas the escape hatch. The timelock is the last line of defence if the multisig itself is compromised — it converts an instant code swap into a 24-hour, publicly observable event. - Stellar natively supports multi-signature operations directly at the account level through account signer thresholds and weights.
- WASM Hash Verification Gate: Before calling
schedule_upgradeon mainnet, the WASM hash must match a CI-published hash from a signed git tag release build.- Verify CI workflow ran on the intended release tag (e.g.,
v2.1.0). - Confirm the CI build artifact WASM hash is recorded in
CHANGELOG.mdunder that version. - Run
stellar contract installon mainnet and verify the returned hash byte-for-byte matches the CI-published hash. - Record the matching hash and CI job URL in the release ticket for audit trail.
- Rationale: This gate ensures the exact bytecode deployed to mainnet was built from a tagged, reviewable commit in git and is not locally-modified or compromised.
- Verify CI workflow ran on the intended release tag (e.g.,
- Configure Owner Multisig Account: Configure the mainnet Owner address with multiple signers (e.g., 2-of-3 or 3-of-5 setup).
- Threshold Settings:
- Low threshold (e.g., 1): For triggering simple operations or
pause()(allows fast emergency response with a single hot trigger key). - Medium threshold (e.g., 2 or 3): For configuring caps, setting Blend pools, and
unpause(). - High threshold (e.g., 3): For calling
schedule_upgrade()andexecute_upgrade()(requires multi-party consensus to push new code).
- Low threshold (e.g., 1): For triggering simple operations or
- Keep
cancel_upgrade()reachable at a low threshold. It is the escape hatch during the timelock window and must not be blocked by an unavailable co-signer.
- Threshold Settings:
- Document Signer Distribution: Ensure keys are distributed securely across key parties using hardware wallets (e.g., Ledger).
- Upgrade Verification Procedure: Ensure any future WASM upgrades are:
- Built inside a deterministic environment (e.g., Docker container with exact Rust toolchain versions).
- Checked against WASM size limits using standard optimization tools (
wasm-opt -Oz). - Signatures collected offline from all co-signers before broadcast.
The timelock must be exercised end-to-end on testnet before mainnet deployment. The unit tests in neurowealth-vault/contracts/vault/src/tests/test_upgrade_timelock.rs cover the gates by advancing the simulated ledger, but they cannot verify the WASM swap or the Version bump — the dummy hash they schedule is not installed on-chain. Only a real network run proves the full cycle.
Set TESTNET_VAULT_CONTRACT_ID, OWNER_ADDRESS, and NEW_WASM_HASH (the hex hash returned by stellar contract install) before starting.
Part A — get_pending_upgrade returns the correct hash and expiry
-
A1. Confirm a clean starting state: with nothing scheduled, the getter must return
null.stellar contract invoke --id $TESTNET_VAULT_CONTRACT_ID --source owner --network testnet -- get_pending_upgrade -
A2. Record the current ledger sequence from RPC (
getLatestLedger) — call itL. -
A3. Schedule the upgrade.
stellar contract invoke --id $TESTNET_VAULT_CONTRACT_ID --source owner --network testnet -- schedule_upgrade --owner $OWNER_ADDRESS --new_wasm_hash $NEW_WASM_HASH
-
Expected Result: Success, and an
UpgradeScheduledEvent(topicupg_sched) carryingnew_wasm_hashandeffective_ledger.
-
Expected Result: Success, and an
-
A4. Verify the pending state: re-run
get_pending_upgrade.-
Expected Result:
(wasm_hash, effective_ledger)wherewasm_hashbyte-for-byte equals$NEW_WASM_HASHandeffective_ledger ≈ L + 17280. Confirm the delta is exactlyUPGRADE_TIMELOCK_LEDGERS, not a shortened test value.
-
Expected Result:
-
A5. Verify the "only one pending" guard: call
schedule_upgradeagain with any hash.-
Expected Result: MUST fail with
VaultError::TimelockAlreadyPending(Error Code48).
-
Expected Result: MUST fail with
-
A6. Verify the execute gate holds before expiry: call
execute_upgradeimmediately.stellar contract invoke --id $TESTNET_VAULT_CONTRACT_ID --source owner --network testnet -- execute_upgrade --owner $OWNER_ADDRESS
-
Expected Result: MUST fail with
VaultError::TimelockNotExpired(Error Code50). Confirmget_version()is unchanged and the deployed code still behaves as the old build — a pending proposal must have no effect on the running contract.
-
Expected Result: MUST fail with
Part B — cancel_upgrade clears the pending state
-
B1. Cancel the proposal scheduled in Part A.
stellar contract invoke --id $TESTNET_VAULT_CONTRACT_ID --source owner --network testnet -- cancel_upgrade --owner $OWNER_ADDRESS
-
Expected Result: Success, and an
UpgradeCancelledEvent(topicupg_cncl) carrying the cancelled hash.
-
Expected Result: Success, and an
-
B2. Verify both storage keys are cleared:
get_pending_upgradeMUST returnnull(PendingUpgradeHashandUpgradeTimelockExpiryare both removed). -
B3. Verify cancel is idempotency-guarded: call
cancel_upgradeagain.-
Expected Result: MUST fail with
VaultError::NoTimelockPending(Error Code49).
-
Expected Result: MUST fail with
-
B4. Verify
execute_upgradeis also blocked after cancel.-
Expected Result: MUST fail with
VaultError::NoTimelockPending(Error Code49).
-
Expected Result: MUST fail with
-
B5. Verify the escape hatch survives a pause: schedule again,
pause()the vault, then callcancel_upgrade.-
Expected Result:
schedule_upgradeandexecute_upgradeare pause-gated and MUST fail withVaultError::Paused(Error Code35), butcancel_upgradeMUST succeed. Unpause afterwards.
-
Expected Result:
Part C — full cycle: schedule → wait out the timelock → execute → verify version bump
-
C1. Record
get_version()before starting — call itV. -
C2. Install the new WASM on testnet and capture its hash.
stellar contract install --wasm target/wasm32-unknown-unknown/release/neurowealth_vault.wasm --source owner --network testnet
- A hash that is not installed on-chain will trap at
execute_upgradetime, after the 24-hour wait. Verify installation before scheduling.
- A hash that is not installed on-chain will trap at
-
C3. Schedule the upgrade with that hash and note
effective_ledgerfromget_pending_upgrade. -
C4. Wait out the real timelock. Testnet has no ledger fast-forward: the 17,280 ledgers take ≈ 24 hours of wall-clock time. Do not shorten
UPGRADE_TIMELOCK_LEDGERSfor this drill — the point is to confirm the mainnet constant. Pollget_pending_upgradeduring the window and confirm the hash never changes. -
C5. Execute once the current ledger sequence
>= effective_ledger.stellar contract invoke --id $TESTNET_VAULT_CONTRACT_ID --source owner --network testnet -- execute_upgrade --owner $OWNER_ADDRESS
-
Expected Result: Success, and an
UpgradedEvent(topicupgraded) withold_version=Vandnew_version=V + 1.
-
Expected Result: Success, and an
-
C6. Verify the version bump:
get_version()MUST returnV + 1. -
C7. Verify the pending state was cleared by execution:
get_pending_upgradeMUST returnnull, and a freshschedule_upgradeMUST be accepted (no leftoverTimelockAlreadyPending). -
C8. Verify storage survived the upgrade: re-check
get_total_assets(),get_total_shares(),get_owner(),get_agent(), and a sampleget_shares(user)against the values recorded before C5. -
C9. Run the release's migration entrypoint if it ships one, then re-run the state checks in C8. The current contract has no
migrate(); see UPGRADE_MIGRATION.md for the pattern a future release would follow. -
C10. Sign off: record the drill's ledger numbers, WASM hashes, and transaction hashes in the release ticket.
Note: operational runbooks for scheduling, monitoring, and executing a production upgrade live in UPGRADE_MIGRATION.md. This drill is the pre-mainnet verification that the timelock itself behaves correctly. The agent-rotation timelock (Issue #317) follows the same shape — see the Agent Update Timelock section of ARCHITECTURE.md.
The harvest() entry-point reuses the same MinRebalanceInterval / LastRebalanceLedger
cooldown mechanism as rebalance(). An incorrectly set cooldown can either allow runaway
harvesting (too low) or lock the AI agent out of yield compounding (too high). The
circuit-breaker (MaxConsecutiveFailures) automatically suspends the agent when the configured
threshold of consecutive protocol failures is reached, preventing a stuck external pool from
draining gas indefinitely.
- Harvest cooldown —
harvest()checksLastRebalanceLedgerbefore executing. If the elapsed ledgers since the last rebalance or harvest is belowMinRebalanceInterval, the call panics withVaultError::RebalanceCooldownActive(Error Code43). A zero interval disables the guard entirely. - Circuit-breaker — after
MaxConsecutiveFailuressuccessive protocol errors the agent is suspended. The default (DEFAULT_MAX_CONSECUTIVE_FAILURES) is applied when the vault was initialized before the circuit-breaker feature shipped. Setting the threshold to0disables the breaker (not recommended in production).
- Choose a harvest cooldown interval. A typical starting point is 720 ledgers (≈ 1 hour).
Set it with:
stellar contract invoke \ --id $VAULT_CONTRACT_ID \ --source owner \ --network mainnet \ -- \ set_rebalance_cooldown \ --interval 720 - Verify the cooldown is stored correctly:
stellar contract invoke --id $VAULT_CONTRACT_ID --network mainnet -- get_rebalance_cooldown # Expected: 720 (or whatever value you configured)
- Verify
harvest()respects the cooldown. Immediately after a harvest, attempt a second call from the agent key.-
Expected Result: MUST fail with
VaultError::RebalanceCooldownActive(Error Code43).
-
Expected Result: MUST fail with
- Choose a circuit-breaker threshold. A value of
3–5is recommended; this trips automatic suspension after that many consecutive protocol failures without blocking normal operations during transient outages.stellar contract invoke \ --id $VAULT_CONTRACT_ID \ --source owner \ --network mainnet \ -- \ set_max_consecutive_failures \ --threshold 5 - Verify the circuit-breaker threshold:
stellar contract invoke --id $VAULT_CONTRACT_ID --network mainnet -- get_max_consecutive_failures # Expected: 5
- Confirm the circuit-breaker trips correctly on testnet. Simulate consecutive harvest
failures (e.g., by draining the Blend pool mock) and confirm that after
thresholdfailures the agent is suspended and subsequent calls revert.
Automated check — add
EXPECTED_REBALANCE_COOLDOWNandEXPECTED_MAX_CONSECUTIVE_FAILURESto theverify-deployment.shinvocation to assert both values in one step:VAULT_CONTRACT_ID=C... NETWORK=mainnet \ OWNER_ADDRESS=G... AGENT_ADDRESS=G... AGENT_SECRET_KEY=S... \ USDC_TOKEN_ADDRESS=G... \ EXPECTED_REBALANCE_COOLDOWN=720 \ EXPECTED_MAX_CONSECUTIVE_FAILURES=5 \ ./scripts/verify-deployment.sh
No smart contract should be deployed on-chain without an independent security audit and formal sign-off.
- Run Pre-Audit Scans & Tests: Confirm all unit tests pass locally:
- Run
cargo testand verify 100% success rate on comprehensive tests.
- Run
- Complete Third-Party Professional Audit:
- Secure a professional smart contract auditing firm (e.g., CertiK, Zellic, OpenZeppelin, Halborn).
- Resolve and fix any identified vulnerabilities (High, Medium, Low, Informational).
- Receive final audit sign-off documentation.
- Verify Findings In Codebase: Verify that critical fixes (such as
withdraw_all()balance protection, andupdate_total_assetsbalance checks) are compile-ready and active. - Final Sign-Off: Gather signatures from the lead developers, security auditors, and product leads before deploying the finalized bytecode.