Contract Deployment Strategy
This document outlines the deployment strategy for the USPD protocol contracts, primarily managed by the Foundry script located at contracts/script/Token.s.sol
.
Current Strategy: Deterministic Addresses & Controlled Deployment
The current deployment utilizes the CREATE2 opcode via the CreateX minimal proxy factory (0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed
) for several key contracts, including the core tokens (cUSPDToken.sol
, UspdToken.sol
) and core logic proxies (PriceOracle
, StabilizerNFT
, OvercollateralizationReporter
).
CREATE2 Mechanism
CREATE2 allows the address of a contract to be determined before deployment based on:
- The deployer’s address (in this case, the CreateX factory).
- A chosen
salt
. - The hash of the contract’s initialization code (
init_code
), which includes creation bytecode and constructor arguments.
Salt Generation and Address Determinism
The deployment script (Token.s.sol
) utilizes the CREATE2 opcode via the CreateX minimal proxy factory (0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed
). The salts for CREATE2 are generated by taking the keccak256
hash of a unique contract identifier string (e.g., “USPD_TOKEN_V1”). This method makes the salt deployer-independent.
The final contract address is determined by:
- The CreateX factory address.
- The generated salt (derived from the contract identifier string).
- The hash of the contract’s initialization code (bytecode + constructor arguments).
This approach enables permissionless deployment to canonical addresses:
- Anyone using the same contract identifier string, the same CreateX factory, and the exact same contract initialization code (bytecode and constructor arguments) can deploy an instance of the contract to the same predetermined address on any chain.
- This facilitates achieving unified contract addresses across different chains, provided the contract’s initialization code remains identical. If constructor arguments differ for L1 vs. L2 configurations (as is the case for some contracts like
PoolSharesConversionRate
orcUSPDToken
due to differing dependencies), theinit_code_hash
will change, leading to different deployed addresses despite using the same salt identifier.
The core team intends to be the first to deploy and publicly designate the official instances at these canonical addresses. The contracts themselves contain logic to differentiate behavior based on the chainId
(e.g., L1 vs. L2), allowing the same bytecode to adapt to its deployment environment. Collateral management and primary minting via ETH occur exclusively on Mainnet (chain ID 1), while satellite chains utilize mint/burn functions primarily for bridging operations.
Upgradeable Core Logic Contracts (UUPS Proxies)
Several core logic contracts, particularly those managing the stabilization mechanism and oracle interactions, are designed to be upgradeable for bug fixes and future enhancements. These now utilize the UUPS (Universal Upgradeable Proxy Standard) pattern, typically via ERC1967Proxy
and OpenZeppelin’s UUPSUpgradeable
contract:
PriceOracle.sol
StabilizerNFT.sol
OvercollateralizationReporter.sol
The deployment script (Token.s.sol
) deploys both the implementation logic contract and an ERC1967Proxy
contract pointing to it. User interactions occur with the proxy address, which delegates calls to the current implementation. These proxies are deployed using the permissioned (deployer-dependent) CREATE2 salt method.
With UUPS proxies, the upgrade logic resides within the implementation contract itself. Upgrades are initiated by calling an upgradeTo(newImplementation)
function directly on the proxy, which is then authorized by a specific role (e.g., UPGRADER_ROLE
) within the implementation. This removes the need for a central ProxyAdmin
contract for these UUPS-based proxies.
Phased Role Management & Decentralization Plan
The management of critical administrative roles, such as the UPGRADER_ROLE
(for contract upgrades) and the DEFAULT_ADMIN_ROLE
(for general administrative functions like granting other roles), will follow a phased approach towards decentralization:
-
Initial Deployment & Test Phase (Hardware Wallet Control):
- The initial set of smart contracts will be deployed using a hardware wallet (e.g., Trezor or Ledger).
- During an initial testing period, estimated to be 2-4 weeks post-launch, this hardware wallet will retain the
UPGRADER_ROLE
andDEFAULT_ADMIN_ROLE
. This allows the core team to address any critical bugs or unforeseen issues rapidly.
-
Interim Phase (Multisignature Wallet Control):
- After the initial phase, the
UPGRADER_ROLE
andDEFAULT_ADMIN_ROLE
will be transferred to a secure multisignature wallet. - This multisig will initially consist of N=3 hardware wallets held by distinct core team members, requiring N-1 (i.e., 2 out of 3) signatures to authorize an action.
- The number of signers (N) may increase over time to further enhance security.
- This phase is expected to last approximately 3-12 months, depending on the stability of the ecosystem and the frequency of necessary updates or bug fixes.
- After the initial phase, the
-
Long-Term Phase (DAO Governance):
- Once the system demonstrates sustained stability and maturity, the
UPGRADER_ROLE
andDEFAULT_ADMIN_ROLE
will be transferred to a decentralized autonomous organization (DAO). - The specific implementation details for DAO voting mechanisms are currently under exploration. Considerations include whether voting rights will be tied to collateral provided by stabilizers, a dedicated governance token, or other means of weighted participation for actions such as contract upgrades or role management. The DAO will be built on standardized and audited smart contract architectures.
- Once the system demonstrates sustained stability and maturity, the
This phased approach aims to balance initial agility for security and bug fixing with a clear path towards progressive decentralization and community governance.
Access Control & Role Management
The contracts utilize OpenZeppelin’s AccessControl
pattern for managing permissions. Key roles include:
DEFAULT_ADMIN_ROLE
: Has broad permissions, including granting/revoking other roles and managing contract upgrades (viaProxyAdmin
). This role is assigned to the deployer address during the current deployment process. In the future roadmap involving post-deployment configuration, this role might be hardcoded in token/rate constructors to ensure core team control over initialization.UPDATER_ROLE
(cUSPDToken
,OvercollateralizationReporter
): Allows updating critical contract dependencies like the Oracle or Stabilizer addresses.MINTER_ROLE
(StabilizerNFT
): Allows minting new Stabilizer NFTs.SIGNER_ROLE
(PriceOracle
): Authorizes addresses to submit signed price attestations.- Other specific roles may exist within individual contracts.
Initially, the DEFAULT_ADMIN_ROLE
is controlled by the core team. As part of the move towards decentralization and increased security post-launch, the plan remains to eventually either:
- Renounce the
DEFAULT_ADMIN_ROLE
on mature contracts by transferring it toaddress(0)
. - Transfer the
DEFAULT_ADMIN_ROLE
to the future on-chain governance contract.
This action will be taken once the team is confident that no further administrative interventions are expected for the core contract logic.
Open Source & Verification
Transparency and security are paramount.
- The source code for both the smart contracts and the frontend interface is open-source and available in our public repositories (links to be added).
- We strive to verify the source code of all deployed contracts on block explorers (like Etherscan) for each supported chain.
- The use of deterministic addresses via CREATE2 enhances transparency. While addresses may differ between L1 and L2s under the current deployment, the future roadmap aims for unified addresses, further simplifying verification for users across networks.