Skip to Content
DocumentationUSPDContracts and Deployments

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:

  1. The deployer’s address (in this case, the CreateX factory).
  2. A chosen salt.
  3. 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:

  1. The CreateX factory address.
  2. The generated salt (derived from the contract identifier string).
  3. 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 or cUSPDToken due to differing dependencies), the init_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:

  1. 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 and DEFAULT_ADMIN_ROLE. This allows the core team to address any critical bugs or unforeseen issues rapidly.
  2. Interim Phase (Multisignature Wallet Control):

    • After the initial phase, the UPGRADER_ROLE and DEFAULT_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.
  3. Long-Term Phase (DAO Governance):

    • Once the system demonstrates sustained stability and maturity, the UPGRADER_ROLE and DEFAULT_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.

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 (via ProxyAdmin). 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:

  1. Renounce the DEFAULT_ADMIN_ROLE on mature contracts by transferring it to address(0).
  2. 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.