Smart Contract Development
  • Introduction
    • What is a Transaction
    • Accounts and Signing
    • What is a smart contract
  • Learning Solidity
    • Introduction
    • Module 1
      • Variable Types
      • Variable Scope: State & Local variables
      • Global variables
      • Functions
        • View and Pure
        • Shadowing in Fuctions
      • Mapping
      • Require
      • Events
    • Project #1: Simple Registry
    • Module 2
      • Constructor
      • Data Location: Value & Reference
      • Interface
      • Import
        • Importing in Foundry
      • Inheritance
      • ERC-20
      • Checks-effect-interaction pattern
    • Project #2: Basic Vault
    • Module 3
      • Payable
      • Receive
      • Fallback
      • Returns
    • Project #3: ERC20+ETH Wrapper
    • Module 4
      • Immutable and Constant
      • Fixed-point Math
      • Abstract contracts
      • ERC-4626
      • Modifier + Inheritance +Ownable
      • Type
    • Project #4: Fractional Wrapper
    • Module 5
      • If-else
      • Libraries
        • TransferHelper
      • Chainlink Oracle
    • Project #5: Collateralized Vault
  • Compendium
    • Solidity Basics
      • Variable Types
      • Value Types
        • address
        • enum
      • Reference Types
        • strings
        • mappings
        • struct
        • Arrays
        • Multi-Dimensional arrays
      • Global Objects
      • Functions
        • Function types
        • Constructor Function
        • Transaction vs Call
        • Require, Revert, Assert
      • Function signature + selectors
      • Payable
        • Payable + withdraw
        • msg.value & payable functions
      • Receive
      • Fallback function (sol v 0.8)
        • Fallback function (sol v 0.6)
      • call, staticcall, delegatecall
    • Return & Events
    • Control Variable Visibility
    • Local Variables (Storage v Memory)
    • Data Location and Assignment Behaviors
    • Modifiers & Inheritance & Import
      • import styles
    • Interface & Abstract Contracts
    • ABI & Debugging
    • Libraries
    • Conditional(ternary) operators
    • Smart Contract Life-cycle
      • Pausing Smart Contracts
      • Destroying Smart Contracts
    • Merkle Trie and MPT
    • Merkle Tree Airdrop
  • Try & catch
  • Ethereum Signatures
  • EVM, Storage, Opcodes
    • EVM
    • Wei, Ether, Gas
    • Storage
    • ByteCode and Opcodes
    • Transaction costs & Execution costs
  • Reading txn input data
  • Data Representation
  • Yul
    • Yul
      • Intro
      • Basic operations
      • Storage Slots
      • Storage of Arrays and Mappings
      • Memory Operations
      • Memory: how solidity uses memory
      • Memory: Return, Require, Tuples and Keccak256
      • Memory: Logs and Events
      • Inter-contract calls
      • calldata
      • free memory pointer
    • Yul Exercises
      • read state variable
      • read mapping
      • iterate Array, Return Sum
    • memory-safe
  • Upgradable Contracts
    • Upgradability & Proxies
    • UUPS Example
    • Minimal Proxy Example
    • TPP Example
    • 🚧Diamond
      • On Storage
  • Gas Opt
    • Block Limit
    • gasLimit & min cost
    • Solidity Optimiser
    • Memory v calldata
    • Memory caching vs direct storage vs pointers
    • < vs <=
    • reverting early
    • X && Y, ||
    • constant and immutable
    • caching sload into mload
    • Syntactic Sugar
    • using unchecked w/o require
    • Compact Strings
    • Calling a view function
    • Custom errors over require
    • usage of this.
      • multiple address(this)
  • ERCs & EIPs
    • ERC-20.sol
      • Core functions
      • transfer()
      • transferFrom()
      • TLDR transfer vs transferFrom
    • Landing
      • ERC721.sol
      • EIP-721
        • LooksRare
        • Page 1
      • ERC-1271
      • EIP-2981
      • ERC-165
      • EIP-1167: Minimal Proxy Contract
    • VRFConsumerBase
    • UniswapV2Library
  • Yield Mentorship 2022
    • Projects
      • #1 Simple Registry
      • #2 Basic Vault
      • #3 ERC20+ETH Wrapper
        • setFailTransferTrue
      • #4 Fractional Wrapper
      • #5 Collateralized Vault
        • Process
        • Vault.sol
        • Testing
        • Chainlink Oracles
        • Pricing + Decimal scaling
        • Refactor for Simplicity
      • #9 Flash Loan Vault
        • Implementing ERC3156
        • Full code for lender
        • Ex-rate calculation
    • State Inheritance Testing
    • Testing w/ Mocks
    • Yield Style Guide
    • Github Actions
    • TransferHelper.sol
    • math logic + internal fn
    • Interfaces: IERC20
  • Foundry
    • Overview
    • Importing Contracts
    • Testing
      • stdError.arithmeticError
      • assume vs bound
      • Traces
      • label & console2
      • std-storage
  • Smart Contract Security
    • Damn Vulnerable Defi
      • 1. Unstoppable
      • 2. Naive receiver
      • 3. Truster
      • 4. Side Entrance
      • 5. The Rewarder
      • 6. Selfie
      • 7. Compromised
      • 8. Puppet
      • 9. Puppet V2
      • 10 - Free Rider
    • Merkle Tree: shortened proof attack
  • Fixed-Point Math
    • AMM Math
  • Solidity Patterns
    • checks-effects-interactions pattern
    • Router // batch
    • claimDelegate: stack unique owners
    • claimDelegate: cache previous user
  • Array: dup/ascending check
  • Deployment
    • Behind the Scenes
    • Interacting with External Contracts
    • Logging, Events, Solidity, Bloom Filter
  • Misc
    • Mnemonic Phrases
    • Bidul Ideas
  • Archive
    • Brownie Framework
      • Brownie basics
        • storing wallets in .env
        • Deployment to ganache
        • Interacting with contract
        • Unit Testing
        • Testnet deployment
        • Interacting w/ deployed contract
        • Brownie console
      • Brownie Advanced
        • Dependencies: import contracts
        • helpful_scripts.py
        • verify and publish
        • Forking and Mocking
        • Mocking
        • Forking
      • Testing
      • Scripts Framework
        • deploy.py
        • get_accounts
        • deploy_mocks()
        • fund_with_<token>()
      • Brownie Networks
    • Brownie Projects
      • SharedWallet
        • Multiple Beneficiaries
        • Common Code Contract
        • Adding Events
        • Renounce Ownership
        • Separate Files
      • Supply Chain
        • ItemManager()
        • Adding Events
        • Adding unique address to each item
      • Lottery
      • Aave - Lending and Borrowing
        • Approve & Deposit
        • Borrow
      • NFT
      • Advanced Collectible
        • adv_deploy() + Testing
        • Create Metadata
        • Setting the TokenURI
    • node npm
    • Ganache
    • Truffle
    • Remix
    • Installing Env
Powered by GitBook
On this page
  • Proxy
  • Implementation
  • Disable Initializer
  1. Upgradable Contracts

UUPS Example

EIP-1822

Proxy

  • UUPS proxy is fairly minimal compared to the transparent proxy setup.

  • Using OZ's implementation library, you don't need to do anything else other than simply deploying the ERC1967Proxy.sol contract and linking your implementation.

UUPSProxy
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {StakingPoolStorage} from "./StakingPoolStorage.sol";
import {ERC1967Proxy} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";

/// @title Proxy for UUPS implementation
/// @author Calnix
contract StakingPoolProxy is ERC1967Proxy {

    /**
     * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
     *
     * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
     * function call, and allows initializing the storage of the proxy like a Solidity constructor.
     */
    constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data){
    }


    /// @dev Returns the current implementation address.
    function implementation() external view returns (address impl) {
        return _implementation();
    }
}

Implementation

We will need to have our implementation contract inherit UUPSUpgradeable, which defines the upgradeability mechanism to be included in the implementation contract.

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {StakingPoolStorage} from "./StakingPoolStorage.sol";
import {SafeERC20, IERC20} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

import {ERC20Upgradeable} from "openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol";
import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";


/// @title A single-sided staking pool that is upgradeable
/// @author Calnix
/// @notice Stake TokenA, earn Token A as rewards
/// @dev Rewards are held in rewards vault, not in the staking pool. Necessary approvals are expected.
/// @dev Contract should be inherited and deployed. See MyStakingPool as example.
/// @dev Pool is only compatible with tokens of 18 dp precision.
contract StakingPoolBase is StakingPoolStorage, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
    using SafeERC20 for IERC20;

    /// @notice Initializes the Pool
    /// @dev To be called by proxy on deployment
    /// @param stakedToken Token accepted for staking
    /// @param rewardToken Token emitted as rewards
    /// @param rewardsVault Vault that holds rewards
    /// @param owner Owner of Pool
    /// @param name ERC20 name of staked token (if receive TokenA for staking, mint stkTokenA to user)
    /// @param symbol ERC20 symbol of staked token (if receive TokenA for staking, mint stkTokenA to user)
    function initialize(IERC20 stakedToken, IERC20 rewardToken, address rewardsVault, address owner, string memory name, string memory symbol) 
        external virtual initializer {
            STAKED_TOKEN = stakedToken;
            REWARD_TOKEN = rewardToken;
            REWARDS_VAULT = rewardsVault;

            __ERC20_init(name, symbol);
            __Ownable_init(owner);
            __UUPSUpgradeable_init();
    }
    
    //.... other functions.....
    
    /*//////////////////////////////////////////////////////////////
                             UPGRADEABILITY
    //////////////////////////////////////////////////////////////*/

    ///@dev override _authorizeUpgrade with onlyOwner for UUPS compliant implementation
    function _authorizeUpgrade(address newImplementation) internal onlyOwner override {}

Disable Initializer

constructor() {
    _disableInitializers();
} 

_disableInitializer() is called inside the constructor; prevents any further initialization. This is done so that in the context of the logic contract the initializer is locked.

Therefore an attacker will not able to call the initalizer() function in the state of the logic contract and perform any malicious activity (like self-destruct)

  • Note that the proxy contract state will still be able to call this function, this is because the constructor does not effect the state of the proxy contract.

  • When you upgrade your contract you need to follow the convention of inheriting the previous version and then adding any modifications.

  • It is not expected that you will need to initialize something every time. So, if you shouldn’t confuse this as preventing any upgrades.

Upgradeable smart contracts can have constructors

  • Upgradeable smart contracts cannot have any constructors which initialize any variable data.

  • Can have a constructor that initializes an immutable (constants are initialized inline).

Need to use reinitializer() in subsequent upgrades for the initializer function

More info:

PreviousUpgradability & ProxiesNextMinimal Proxy Example

Last updated 1 year ago

Inheriting from UUPSUpgradeable (and overriding the function with the relevant access control mechanism) will turn your contract into a UUPS compliant implementation.

Codebase:

_authorizeUpgrade
https://github.com/calnix/Upgradable-Staking-Pool
https://abhik.hashnode.dev/6-nuances-of-using-upgradeable-smart-contracts
https://ethereum.stackexchange.com/questions/132261/initializing-the-implementation-contract-when-using-uups-to-protect-against-atta