Repository navigation
Deployment Strategies
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
This document defines the deployment strategy for the Sigvara protocol across testnet and mainnet using Foundry scripts. It covers environment configuration, network setup, proxy upgrade procedures with SplitAuthority governance separation, migration strategies, best practices per environment, step-by-step deployment and verification, governance parameter setup, gas optimization during deployment, transaction monitoring, rollback procedures, troubleshooting, and validation steps to ensure successful deployment.
The repository deploys UUPS proxies behind ERC1967 proxy storage slots, wires roles between Identity, Reputation, Staking, Oracle Bond, and Epoch Fees contracts, and records deployed addresses into a chain-specific JSON artifact under deployments/. Governance is separated from the deployer through a Safe and TimelockController via the SplitAuthority migration script.
The deployment surface consists of:
- Foundry configuration defining source layout, remappings, optimizer settings, RPC endpoints, and filesystem permissions for deployment artifacts.
- Core deployment scripts for the registry suite, oracle bond, epoch fees, the stateless reader, upgrades, and authority split.
- On-chain contracts implementing Identity, Reputation, and Staking as upgradeable registries.
- A testnet runbook describing Arc testnet setup, simulation, broadcast, verification, and post-deployment tasks.
graph TB
subgraph "Foundry Configuration"
F["foundry.toml"]
end
subgraph "Deployment Scripts"
D["script/Deploy.s.sol"]
OB["script/DeployOracleBond.s.sol"]
EF["script/DeployEpochFees.s.sol"]
U["script/Upgrade.s.sol"]
SA["script/SplitAuthority.s.sol"]
end
subgraph "Contracts"
ID["src/SigvaraIdentity.sol"]
RP["src/SigvaraReputation.sol"]
ST["src/SigvaraStaking.sol"]
end
subgraph "Documentation"
DT["docs/deploy-testnet.md"]
end
F --> D
F --> OB
F --> EF
F --> U
F --> SA
D --> ID
D --> RP
D --> ST
OB --> ST
EF --> ID
DT --> D
Diagram sources
- foundry.toml
- script/Deploy.s.sol
- script/DeployOracleBond.s.sol
- script/DeployEpochFees.s.sol
- script/Upgrade.s.sol
- script/SplitAuthority.s.sol
- src/SigvaraIdentity.sol:1-211
- src/SigvaraReputation.sol:1-221
- src/SigvaraStaking.sol:1-234
- docs/deploy-testnet.md
Section sources
- Deploy.s.sol: Deploys SVR token (or reuses an existing one), then deploys UUPS proxies for Identity, Reputation, and Staking; wires roles, and grants the pause guardian its role on all three when
GUARDIAN_ADDRESSis set; logs parameters; writesdeployments/{chainId}.jsononly on broadcast. - DeployOracleBond.s.sol: Deploys Oracle Bond behind a UUPS proxy, grants SLASHER_ROLE (and the guardian role, when set), validates initialization, and appends its address to the existing deployment artifact.
- DeployEpochFees.s.sol: Deploys Epoch Fees behind a UUPS proxy, grants ORACLE_ROLE, sets the epoch length and the operator bond registry, records the address as
epochFeesin the artifact, and leaves fee gating disabled until governance sets a non-zero fee. It refuses to run on Arc mainnet. - DeployReader.s.sol: Deploys
SigvaraReader, the stateless read for contracts that gate on an agent's standing. It has no proxy, no roles and nothing to initialize, and nothing in the protocol reads from it. Onmainonly; it has not been deployed anywhere. - Upgrade.s.sol: Upgrades a single target proxy to a freshly compiled implementation, reads the ERC1967 implementation slot before and after the upgrade, checks dependencies and that the proxy is the kind the target names, and supports optional init calldata. After the handover it cannot act as the timelock that holds
UPGRADER_ROLE. - SplitAuthority.s.sol: Multi-phase migration moving DEFAULT_ADMIN_ROLE and UPGRADER_ROLE off the deployer onto a Safe and TimelockController, with safety checks and read-only planning. It needs the four launch registries (
identity,reputation,staking,oracleBond) in the artifact in every phase that reads them (plan, grant, safe-calldata and renounce) and stops withMissingTargetat one that is missing, zero or without code;epochFeesis optional. Phases 3 and 5 refuse a timelock that is not the one phase 2 creates from the deployer, and phases 2 to 5 refuse a Safe that is an EIP-7702 delegated account. The renounce phase also holds the timelock and the Safe to the arrangement the ADR describes (see Governance and Upgrades) and refuses what falls short.
Key environment variables include DEPLOYER_PRIVATE_KEY, SVR_ADDRESS, ORACLE_ADDRESS, COMMITTEE_ADDRESS, GUARDIAN_ADDRESS, MINIMUM_STAKE, CHALLENGE_PERIOD, SCORE_CHALLENGE_WINDOW, UNBONDING_PERIOD, MATURITY_RATE_PER_DAY, SLASHER_ADDRESS, SLASH_BENEFICIARY, ORACLE_BOND_AMOUNT, ORACLE_UNBONDING, EPOCH_FEE, EPOCH_LENGTH, OPERATOR_BOND, REWARD_POOL, IDENTITY_ADDRESS, REPUTATION_ADDRESS, STAKING_ADDRESS, SMOKE_AGENT, TARGET, PROXY, INIT_CALLDATA, PHASE, SAFE, TIMELOCK, TIMELOCK_MIN_DELAY, and CONFIRM.
Section sources
- script/Deploy.s.sol:13-212
- script/DeployOracleBond.s.sol:9-112
- script/DeployEpochFees.s.sol:9-181
- script/Upgrade.s.sol:12-70
- script/SplitAuthority.s.sol:7-121
The protocol deploys three core registries behind UUPS proxies:
- SigvaraIdentity anchors agent identities and operator relationships.
- SigvaraReputation stores and finalizes oracle-proposed reputation scores with challenge windows and maturity.
- SigvaraStaking manages stake bonds, slashing workflows, and unbonding periods.
Additional components:
- SigvaraOracleBond manages operator performance bonds and slashing proceeds.
- SigvaraEpochFees enables fee-based coverage gating controlled by governance/oracle roles. It has never been deployed and is left out of the mainnet launch.
Governance separation:
- DEFAULT_ADMIN_ROLE and UPGRADER_ROLE are moved from the deployer to a Safe and TimelockController through SplitAuthority phases: the Safe ends up with the admin role and the timelock (48 hours by default) with the upgrader role.
graph TB
subgraph "Proxies"
P_ID["SigvaraIdentity Proxy"]
P_RP["SigvaraReputation Proxy"]
P_ST["SigvaraStaking Proxy"]
P_OB["SigvaraOracleBond Proxy"]
P_EF["SigvaraEpochFees Proxy"]
end
subgraph "Implementations"
I_ID["SigvaraIdentity Impl"]
I_RP["SigvaraReputation Impl"]
I_ST["SigvaraStaking Impl"]
I_OB["SigvaraOracleBond Impl"]
I_EF["SigvaraEpochFees Impl"]
end
subgraph "Roles & Governance"
GOV["Safe + TimelockController"]
ORA["Oracle"]
COM["Committee / Slasher"]
end
P_ID --> I_ID
P_RP --> I_RP
P_ST --> I_ST
P_OB --> I_OB
P_EF --> I_EF
GOV --> |UPGRADER_ROLE| P_ID
GOV --> |UPGRADER_ROLE| P_RP
GOV --> |UPGRADER_ROLE| P_ST
GOV --> |UPGRADER_ROLE| P_OB
GOV --> |UPGRADER_ROLE| P_EF
ORA --> |ORACLE_ROLE| P_RP
ORA --> |ORACLE_ROLE| P_EF
COM --> |SLASHING_COMMITTEE_ROLE| P_ST
COM --> |SLASHING_COMMITTEE_ROLE| P_RP
COM --> |SLASHER_ROLE| P_OB
Diagram sources
- script/Deploy.s.sol:131-180
- script/DeployOracleBond.s.sol:81-111
- script/DeployEpochFees.s.sol:82-181
- script/SplitAuthority.s.sol:20-24
- src/SigvaraIdentity.sol:37-42
- src/SigvaraReputation.sol:81-92
- src/SigvaraStaking.sol:59-65
- Network configuration is defined in foundry.toml with rpc_endpoints for arc_testnet and arc_mainnet. Gas on Arc chains is USDC at the EVM level.
- The testnet runbook documents chain ID 5042002, funding via faucet.circle.com, simulating before broadcasting, verifying role wiring, committing the deployments artifact, pointing the oracle service, updating frontend constants, and deploying the static site.
- Mainnet usage mirrors testnet commands but uses arc_mainnet and enforces stricter rules before anything is sent.
Deploy.s.solrefuses to run on chain 5042 withoutSVR_ADDRESS, with a bond token that has no code or is not 18 decimals, with the oracle or committee equal to the deployer or to each other, without aGUARDIAN_ADDRESSthat is none of them, withMINIMUM_STAKEat the testnet default or under the published 10,000 SVR, or with periods outside the floors and ceilings the contracts enforce.DeployOracleBond.s.solapplies the same kind of checks to its bond amount (not the default, not under 2,500,000 SVR), its unbonding period and its guardian. The variable is named in the error.
flowchart TD
Start(["Start"]) --> Env["Set environment variables<br/>DEPLOYER_PRIVATE_KEY, ORACLE_ADDRESS,<br/>COMMITTEE_ADDRESS, MINIMUM_STAKE"]
Env --> Simulate["Simulate deploy:<br/>forge script script/Deploy.s.sol --rpc-url arc_testnet -vvvv"]
Simulate --> Review{"Review output<br/>and warnings?"}
Review --> |No| Fix["Fix env or roles"]
Fix --> Simulate
Review --> |Yes| Broadcast["Broadcast deploy:<br/>forge script script/Deploy.s.sol --rpc-url arc_testnet --broadcast -vvvv"]
Broadcast --> Record["Record block number"]
Record --> Verify["Verify parameters and roles on-chain"]
Verify --> CheckProxies["Check each proxy on chain:<br/>record-implementations.mjs"]
CheckProxies --> CommitArtifact["Commit deployments/{chainId}.json"]
CommitArtifact --> PointOracle["Point oracle service at deployed proxies"]
PointOracle --> UpdateFrontend["Update frontend constants and docs"]
UpdateFrontend --> End(["Done"])
Diagram sources
Section sources
- Upgrade.s.sol deploys a fresh implementation for a chosen target, calls upgradeToAndCall on the proxy, verifies the ERC1967 implementation slot changed, and optionally executes init calldata atomically. That works only while the broadcasting key holds both roles, as the deployer does before the handover: reinitializers are admin-gated and the timelock is not the admin, so a timelocked upgrade lands with empty data and the Safe calls the reinitializer in a second transaction.
- Dependency checks prevent upgrading Reputation or Staking before their required identity functions exist, avoiding live breakage when implementations are out of order. A
PROXYoverride must also answer a getter only the target kind has, or the run reverts. - SplitAuthority.s.sol moves DEFAULT_ADMIN_ROLE and UPGRADER_ROLE off the deployer in five phases: plan, deploy-timelock, grant (
grant-adminis the old name and still works), safe-calldata, renounce. The grant phase gives admin to the Safe and upgrader to the timelock, and the deployer keeps both through it. Phase 4 has the Safe itself remove the deployer's admin role, which proves the Safe can execute against these contracts. Before phase 5, runscripts/check-role-holders.mjswithBEFORE_RENOUNCE=1: the script's own checks read the contracts it is shown, AccessControl cannot list holders, and only the grant history shows a holder nobody expected, who is cheap to remove now and slow to remove once the upgrader role answers only to the timelock. Phase 5 refuses unless all replacements are verified on-chain, including that the Safe is not one the deployer can drive alone (an owner at threshold 1), that the timelock is the one phase 2 created (its code, its creator, a delay of at least 48 hours, the Safe as proposer and canceller, open execution, no outside admin), and that on every registryUPGRADER_ROLEadministers itself and the slasher, committee and staking-core roles can be granted only through the timelock.
sequenceDiagram
participant Dev as "Developer"
participant Forge as "Forge Script"
participant Proxy as "UUPS Proxy"
participant Impl as "New Implementation"
participant Chain as "Chain State"
Dev->>Forge : Set TARGET, PROXY (optional), INIT_CALLDATA
Forge->>Forge : _checkDependencies(target, proxy)
Forge->>Chain : Read old impl via ERC1967 slot
Forge->>Impl : Deploy new implementation
Forge->>Proxy : upgradeToAndCall(newImpl, initData)
Forge->>Chain : Read new impl via ERC1967 slot
alt Slot changed
Forge-->>Dev : Success log
else Slot unchanged
Forge-->>Dev : Revert ImplementationUnchanged
end
Diagram sources
stateDiagram-v2
[*] --> Plan
Plan --> DeployTimelock : "PHASE=deploy-timelock"
DeployTimelock --> GrantAdmin : "PHASE=grant"
GrantAdmin --> SafeCalldata : "Execute from Safe"
SafeCalldata --> Renounce : "PHASE=renounce CONFIRM=yes-drop-the-deployer"
Renounce --> [*]
Diagram sources
- script/SplitAuthority.s.sol:175-185
- script/SplitAuthority.s.sol:278-303
- script/SplitAuthority.s.sol:309-343
- script/SplitAuthority.s.sol:360-393
- script/SplitAuthority.s.sol:439-493
Section sources
- script/Upgrade.s.sol:12-70
- script/Upgrade.s.sol:71-108
- script/Upgrade.s.sol:123-209
- script/SplitAuthority.s.sol:7-121
- script/SplitAuthority.s.sol:175-185
- script/SplitAuthority.s.sol:278-303
- script/SplitAuthority.s.sol:309-343
- script/SplitAuthority.s.sol:360-393
- script/SplitAuthority.s.sol:439-493
- Use Deploy.s.sol for initial registry deployment and role wiring.
- Use DeployOracleBond.s.sol to add operator bonding; it merges into the existing deployments artifact rather than replacing it.
- Use DeployEpochFees.s.sol to enable fee gating later; defaults keep gating disabled until governance sets a non-zero fee. It is refused on mainnet while the module is left out of the launch.
- Use DeployReader.s.sol at any time to add the stateless reader; nothing in the protocol depends on it.
- Use SplitAuthority.s.sol to move admin and upgrader roles off the deployer safely, with phase-gated execution and on-chain verification.
flowchart TD
A["Initial Deploy"] --> B["Deploy Oracle Bond"]
B --> C["Deploy Epoch Fees<br/>(refused on mainnet)"]
C --> D["Split Authority"]
D --> E["Governed Upgrades"]
Diagram sources
- script/Deploy.s.sol:131-212
- script/DeployOracleBond.s.sol:174-197
- script/DeployEpochFees.s.sol:9-52
- script/SplitAuthority.s.sol:20-24
Section sources
- script/Deploy.s.sol:131-212
- script/DeployOracleBond.s.sol:174-197
- script/DeployEpochFees.s.sol:9-52
- script/SplitAuthority.s.sol:20-24
- Development:
- Use local forks or testnets; simulate all scripts with -vvvv before broadcasting.
- Keep private keys out of the repository; export them in your shell session only.
- Validate role separation warnings and fix misconfigurations early.
- Staging/Testnet:
- Follow the testnet runbook: fund deployer, simulate, broadcast, record block number, verify parameters and roles, point oracle, update frontend constants, and commit the artifact.
- Use distinct addresses for deployer, oracle, and committee to avoid concentrated power.
- Production/Mainnet:
- Enforce strict role separation; the deployer must not be oracle or committee.
- Complete SplitAuthority migration before enabling governance-controlled upgrades.
- Use TimelockController with a meaningful minimum delay; proposers should be the Safe, executors open to allow public execution after delay.
- Name a pause guardian that is a different, lighter key from the deployer, the oracle and the committee (and the slasher, on the bond registry).
- A Safe with a threshold of one, or one that shares an owner with the committee, defeats the split.
check_safesinscripts/check-mainnet.shfails on both. Therenouncephase refuses a Safe with a threshold under 2 (DeployerCanActAlonewhen the deployer is an owner,SafeThresholdTooLowotherwise), one that cannot be asked for its threshold (SafeNotInspectable) and one that is an EIP-7702 delegated account (SafeIsADelegatedAccount); onlycheck_safes, given the owners the signers confirmed on their own devices, compares the Safe's owners with the committee's. - Run the matching check in
scripts/check-mainnet.shafter each step, andscripts/check-role-holders.mjstwice: withBEFORE_RENOUNCE=1after the Safe has removed the deployer's admin and before therenouncephase (the deployer is then expected to holdUPGRADER_ROLEbeside the timelock), and again without it after the handover. The replay starts at the artifact'sdeployBlock, whichDeploy.s.solwrites; leave it as written, because a later block hides the grants before it. - Record and verify implementation addresses post-upgrade using the repository’s recording utility referenced by Upgrade.s.sol.
Section sources
- docs/deploy-testnet.md:27-149
- script/Deploy.s.sol:214-232
- script/SplitAuthority.s.sol:20-24
- script/Upgrade.s.sol:61-69
- Prerequisites:
- Ensure lib/ is populated (
sh scripts/fetch-deps.shfetches OpenZeppelin and forge-std, each at a pinned commit; there are no submodules) and foundry.toml is configured. - Fund the deployer with USDC on Arc testnet if applicable.
- Ensure lib/ is populated (
- Deploy Core Registries:
- Export DEPLOYER_PRIVATE_KEY, ORACLE_ADDRESS, COMMITTEE_ADDRESS, MINIMUM_STAKE (and, on mainnet, SVR_ADDRESS and GUARDIAN_ADDRESS).
- Simulate: forge script script/Deploy.s.sol --rpc-url arc_testnet -vvvv.
- Broadcast: forge script script/Deploy.s.sol --rpc-url arc_testnet --broadcast -vvvv.
- Check every proxy in the artifact against the chain with
node scripts/record-implementations.mjs(runcd oracle && npm cionce first; it exits non-zero on an address that is not a proxy). Do not use--resume: the file is written when forge runs the script locally, before the transactions confirm. - Commit deployments/{chainId}.json.
- Deploy Oracle Bond:
- Export SVR_ADDRESS and optional SLASHER_ADDRESS, SLASH_BENEFICIARY, ORACLE_BOND_AMOUNT, ORACLE_UNBONDING, GUARDIAN_ADDRESS. The slasher and the beneficiary must be different addresses and both default to the deployer, so state at least one of them.
- Run DeployOracleBond.s.sol; it appends .oracleBond to the artifact.
- Deploy Epoch Fees:
- Export SVR_ADDRESS, IDENTITY_ADDRESS, optional ORACLE_ADDRESS, REWARD_POOL, EPOCH_FEE, EPOCH_LENGTH, OPERATOR_BOND (it defaults to the artifact's
oracleBond, so deploy the bond registry first). - Run DeployEpochFees.s.sol; fee gating remains disabled until set. It refuses to run on mainnet.
- Export SVR_ADDRESS, IDENTITY_ADDRESS, optional ORACLE_ADDRESS, REWARD_POOL, EPOCH_FEE, EPOCH_LENGTH, OPERATOR_BOND (it defaults to the artifact's
- Verify:
- Check minimum stake, challenge period, unbonding period, and role assignments on Identity, Reputation, and Staking.
- Confirm oracle and committee roles where applicable.
Section sources
- docs/deploy-testnet.md:47-149
- script/Deploy.s.sol:111-212
- script/DeployOracleBond.s.sol:65-111
- script/DeployEpochFees.s.sol:63-181
- The repository configures OpenZeppelin remappings and optimizer settings in foundry.toml.
- For Arc testnet/mainnet, do not add etherscan entries for chain IDs 5042002/5042 until native metadata support exists; see the note in foundry.toml regarding unknown chain IDs breaking forge script verification flows.
- After deployment, use cast call to verify key parameters and roles on-chain as documented in the testnet runbook.
Section sources
- Initial parameters:
- MINIMUM_STAKE: default 1,000 units of an 18-decimal token; override for non-18 decimals like USDC. On mainnet the default is refused and the figure may not be under 10,000 SVR.
- CHALLENGE_PERIOD: default 7 days.
- SCORE_CHALLENGE_WINDOW: default 6 hours.
- UNBONDING_PERIOD: default 21 days.
- MATURITY_RATE_PER_DAY: default 4 points/day.
- On
mainthe contracts refuse values outside these bounds in their initializers as well as their setters: challenge period 3 to 30 days, score window 1 hour to 7 days, unbonding period 1 to 90 days, maturity rate above 0 and at most 100 points a day. The mainnet scripts check the same bounds first and name the variable, except the maturity-rate floor, which the contract itself refuses.
- Oracle and Committee:
- ORACLE_ADDRESS: wallet granted ORACLE_ROLE on Reputation, which may propose scores.
- COMMITTEE_ADDRESS: initial SLASHING_COMMITTEE_ROLE holder.
- GUARDIAN_ADDRESS: granted GUARDIAN_ROLE on identity, reputation and staking (and on the bond registry). It can pause new inflows for three days at a time and nothing else. Required on mainnet.
- Oracle Bond:
- SLASHER_ADDRESS: granted SLASHER_ROLE.
- SLASH_BENEFICIARY: destination for slashed bonds. It may not be the slasher.
- ORACLE_BOND_AMOUNT: minimum bond (default 1,000 SVR; on mainnet the default is refused and the figure may not be under 2,500,000 SVR).
- ORACLE_UNBONDING: unbonding cooldown (default 7 days, between 1 and 90).
- Epoch Fees:
- EPOCH_FEE: initial per-epoch fee (default 0 disables gating).
- EPOCH_LENGTH: seconds in one epoch, at least 1 hour (default 1 day). An agent is charged once per epoch.
- REWARD_POOL: validator/oracle reward destination.
Section sources
- script/Deploy.s.sol:62-77
- script/Deploy.s.sol:115-121
- script/DeployOracleBond.s.sol:41-43
- script/DeployOracleBond.s.sol:69-73
- script/DeployEpochFees.s.sol:71-75
- foundry.toml enables optimizer with 200 runs and solc version 0.8.24, and pins
evm_version = "cancun"so a Foundry upgrade cannot change what is compiled (Arc runs Osaka, and cancun bytecode runs there). - On Arc networks, fee estimation may require --legacy flag for forge script runs.
- Avoid unnecessary state changes in deployment scripts; rely on deterministic initialization and minimal role grants.
- Prefer reusing existing tokens (SVR_ADDRESS) on mainnet to reduce deployment cost and complexity.
Section sources
- Use cast balance to confirm deployer funding.
- Record the deploy block number for oracle FROM_BLOCK configuration.
Deploy.s.solwrites the block it started at to the artifact asdeployBlock, andcheck-role-holders.mjsreplays from it. - Monitor contract creation transactions and role grant events via block explorers or RPC queries.
- Post-upgrade, verify the ERC1967 implementation slot changed and that dependency checks passed.
Section sources
- There is no automatic rollback; upgrades replace the implementation address. If an upgrade breaks functionality:
- Do not proceed with further upgrades until resolved.
- If SplitAuthority has been completed, propose a revert through the TimelockController with appropriate delay.
- If SplitAuthority is not complete, the deployer still holds UPGRADER_ROLE and can revert to a known-good implementation, but this should be avoided on mainnet.
- Always validate storage layout compatibility before upgrading; the repository emphasizes slot-pinning tests and fork rehearsals.
Section sources
The deployment scripts establish explicit dependencies:
- Staking depends on Identity and Reputation being wired.
- Reputation depends on Identity for operatorChangedAt and identity binding.
- Epoch Fees depends on Identity and SVR token.
- Oracle Bond depends on SVR token and governance roles.
graph LR
ST["SigvaraStaking"] --> ID["SigvaraIdentity"]
ST --> RP["SigvaraReputation"]
RP --> ID
EF["SigvaraEpochFees"] --> ID
EF --> SVR["SVR Token"]
OB["SigvaraOracleBond"] --> SVR
Diagram sources
- script/Deploy.s.sol:151-180
- script/DeployEpochFees.s.sol:82-181
- script/DeployOracleBond.s.sol:81-111
Section sources
- script/Deploy.s.sol:151-180
- script/DeployEpochFees.s.sol:82-181
- script/DeployOracleBond.s.sol:81-111
- Optimizer enabled with 200 runs reduces bytecode size and runtime costs.
- Use --legacy with forge script on Arc networks if fee estimation fails.
- Minimize role grants and initialization calls; batch operations where possible.
- Avoid deploying redundant tokens; reuse SVR_ADDRESS on mainnet.
- Monitor gas usage during simulation and broadcast; adjust MINIMUM_STAKE appropriately for non-18 decimal tokens.
Common issues and resolutions:
- Role separation warnings:
- If oracle or committee equals the deployer, the script warns on testnet and refuses on mainnet. Separate these roles before deploying.
-
DeployOracleBond.s.solrefuses one address as both slasher and beneficiary on every chain. Both default to the deployer, so a deploy that states neither stops before anything is sent. - On mainnet a missing
GUARDIAN_ADDRESS, or one equal to the deployer, the oracle, the committee or the slasher, is refused.
- Fee estimation failures:
- Add --legacy to forge script commands on Arc networks.
- Missing deployment artifact:
- Ensure you ran with --broadcast; dry runs do not write deployments/{chainId}.json.
- Artifact names a contract that does not exist:
- The file is written when forge runs the script locally, before the transactions confirm, so a failed or interrupted broadcast can leave it naming contracts that were never deployed, and
--resumerewrites it with addresses that do not exist. Start again instead, and runnode scripts/record-implementations.mjs, which fails on an address that is not a proxy.
- The file is written when forge runs the script locally, before the transactions confirm, so a failed or interrupted broadcast can leave it naming contracts that were never deployed, and
- Proxy upgrade dependency errors:
- Upgrade Identity before Reputation or Staking if they depend on newly added functions.
- Safe/Timelock migration failures:
- Verify Safe code exists and is reachable; ensure the timelock is the one phase 2 builds and creates from the deployer (a delay of at least 48 hours, the Safe as proposer and canceller, open execution, no admin but itself); execute safe-calldata from the Safe itself.
-
renouncestops with the name of what is missing, for exampleDeployerStillAdminwhen the Safe has not removed the deployer's admin role. On a proxy initialised by the older code, which the Arc testnet ones are,UPGRADER_ROLEand the slasher, committee and staking-core roles stay the admin's to grant, andrenouncerefuses. It refuses a weak timelock or Safe the same way, by name:TimelockDelayTooShort,TimelockCannotBeProposedTo,TimelockCannotBeCancelledBy,TimelockExecutionNotOpen,TimelockHasOutsideAdmin,TimelockHasNoCode,TimelockCodeNotRecognized,TimelockNotCreatedByDeployer,DeployerCanActAlone,SafeThresholdTooLow,SafeNotInspectableandSafeIsADelegatedAccount.
- Frontend mismatch:
- Update frontend constants and docs after deployment; verify asset cache busting and CDN cache clearing.
Section sources
- script/Deploy.s.sol:214-232
- foundry.toml:25-29
- script/Deploy.s.sol:363-392
- script/Upgrade.s.sol:123-148
- script/SplitAuthority.s.sol:525-591
- docs/deploy-testnet.md
The Sigvara deployment strategy centers on deterministic Foundry scripts, UUPS proxies, and careful role management. Testnet deployment follows a clear simulation-to-broadcast workflow with verification and oracle integration. Mainnet requires strict role separation and governance through Safe and TimelockController. Upgrades are guarded by dependency checks and ERC1967 slot verification, while SplitAuthority ensures irreversible authority transfer only after on-chain proof of Safe and Timelock readiness. Following the documented best practices, environment configurations, and troubleshooting steps will help ensure reliable and secure deployments across environments.
- DEPLOYER_PRIVATE_KEY: Required for broadcast operations.
- SVR_ADDRESS: Existing ERC-20 to use as bond token. Required on mainnet; otherwise
Deploy.s.soldeploys the testnet token.DeployOracleBond.s.solandDeployEpochFees.s.solalways require it. - ORACLE_ADDRESS: Wallet granted ORACLE_ROLE on Reputation and Epoch Fees.
- COMMITTEE_ADDRESS: Initial SLASHING_COMMITTEE_ROLE holder.
- GUARDIAN_ADDRESS: Key granted GUARDIAN_ROLE, which pauses new inflows for three days at a time. Required on mainnet.
- MINIMUM_STAKE: Minimum stake in wei; override for non-18 decimals.
- CHALLENGE_PERIOD: Slash challenge window in seconds.
- SCORE_CHALLENGE_WINDOW: Reputation score challenge window in seconds.
- UNBONDING_PERIOD: Unbonding cooldown in seconds.
- MATURITY_RATE_PER_DAY: Points released per day for reputation maturity.
- SLASHER_ADDRESS: Granted SLASHER_ROLE on Oracle Bond.
- SLASH_BENEFICIARY: Destination for slashed bonds.
- ORACLE_BOND_AMOUNT: Minimum bond amount.
- ORACLE_UNBONDING: Oracle unbonding cooldown.
- EPOCH_FEE: Initial per-epoch fee (0 disables gating).
- REWARD_POOL: Reward destination for validators/oracles.
- EPOCH_LENGTH: Seconds in one epoch for Epoch Fees, at least one hour (default one day).
- OPERATOR_BOND: Oracle Bond proxy for Epoch Fees (default:
oracleBondin the artifact). - IDENTITY_ADDRESS: Required for Epoch Fees deployment, and for the reader.
- REPUTATION_ADDRESS, STAKING_ADDRESS: Required for the reader deployment.
- SMOKE_AGENT: Optional registered agent the reader deployment reads back, to prove every registry call works.
- TARGET: Which proxy
Upgrade.s.solupgrades: identity, reputation, staking, oracleBond or epochFees. - PROXY: Override proxy address for upgrades.
- INIT_CALLDATA: Optional abi-encoded initializer call for upgrades.
- PHASE: Which
SplitAuthority.s.solphase to run (plan by default). - SAFE: Safe address for SplitAuthority migration.
- TIMELOCK: TimelockController address for SplitAuthority migration.
- TIMELOCK_MIN_DELAY: Minimum delay for TimelockController (default 48 hours, which is also the least
renounceaccepts). - CONFIRM: Must be yes-drop-the-deployer for renounce phase.
- CONFIRM_UNINSPECTABLE_SAFE:
yes-i-checked-the-thresholdlets the renounce phase go ahead when SAFE cannot be asked for its threshold (a multisig that is not a Safe). Say it only once the threshold and the owners were read some other way.
Section sources
- Agent Registration and Lifecycle
- Contract Overview and Architecture
- Economic Security Model and Incentives
- Evidence Verification and Merkle Proofs
- Key Management and Rotation
- Maturity Mechanism and Score Release
- Proposal Workflow and Challenge System
- Score Storage and Data Model
- SigvaraIdentity Contract
- SigvaraOracleBond Contract
- SigvaraReputation Contract
- SigvaraStaking Contract
- Slashing Lifecycle and Dispute Resolution
- Stake Management Functions
- Status Management and Control
- Token and Fee Contracts
- Unbonding Period and Withdrawal Security
- Anti-Manipulation and Security Measures
- Community Flag Processing
- Evidence Sources and Verification
- External Trust Integration
- Merkle Proof System
- Oracle Score Computation
- Payment-Backed Attestations
- Scoring Model and Formulas
- Agent Creation and Key Management
- Agent Operations
- Agent Registration Workflow
- Challenge-Response Authentication
- Gate Access Control
- Integration Patterns and Examples
- SDK Overview and Setup
- Verifier Functions