3. Deployment

Learn about deployment of your protected smart contract

Contents

Foundry

a. Non-Upgradeable

To deploy a non-upgradeable example below, you need to pass the router address into its constructor (find the CUBE3 Router address here). Let's deploy an example below:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@cube3/Cube3Protection.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Cube3ProtectedErc20 is ERC20, Cube3Protection {
    constructor(address _router)
        Cube3Protection(_router, msg.sender, true)
        ERC20("Cube3Token", "CTK")
    {
        _mint(msg.sender, 10000 * 10**decimals());
    }

    function mintCube3TokenBlockByName(
        address to,
        uint256 amount,
        bytes calldata cube3SecurePayload
    ) public cube3Protected(cube3SecurePayload) {
        _mint(to, amount);
    }
}

Run the following in your cmd:

forge create \
--rpc-url <RPC_URL> \
--private-key <PRIVATE_KEY> src/Cube3ProtectedErc20.sol:Cube3ProtectedErc20 \
--constructor-args <CUBE3-ROUTER-ADDRESS>

(Optional, but recommended) to verify this deployed contract, run:

forge verify-contract \
--chain-id 11155111 \ #sepolia
--watch \
--constructor-args $(cast abi-encode "constructor(address)" <CUBE3-ROUTER-ADDRESS>) \
--etherscan-api-key <ETHERSCAN-API-KEY> \
<YOUR-DEPLOYED-CONTRACT-ADDRESS> \
src/Cube3ProtectedErc20.sol:Cube3ProtectedErc20

b. Upgradeable

For upgradeable contracts, we have two steps:

  1. Deploy implementation

  2. Deploy Proxy that points to implementation

Let's deploy the implementation example below:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "@openzeppelin-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";
import "@cube3/upgradeable/Cube3ProtectionUpgradeable.sol";

contract Cube3ProtectedErc20UUPS is Cube3ProtectionUpgradeable, ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable {
    function initialize(address router, address admin, bool checkProtection) initializer public {
        // In this scenario, the contract owner is the same account as the integration's admin, which
        // has privileged access to the router.
        __Cube3ProtectionUpgradeable_init(router, admin, checkProtection);
        __UUPSUpgradeable_init();
        __ERC20_init("Cube3ProtectedToken", "CTK");

        _mint(admin, 10000 * 10**decimals());
    }

    function _authorizeUpgrade(address newImplementation)
    internal
    virtual
    override
    onlyOwner
    {}

    function mintCube3Protected(
        address to,
        uint256 amount,
        bytes calldata cube3SecurePayload
    ) public cube3Protected(cube3SecurePayload) {
        _mint(to, amount);
    }
}

Run the following in your cmd:

forge create \
--rpc-url <RPC_URL> \
--private-key <PRIVATE_KEY> src/Cube3ProtectedErc20UUPS.sol:Cube3ProtectedErc20UUPS \
--constructor-args <CUBE3-ROUTER-ADDRESS>

(Optional, but recommended) Let's verify the implementation contract (since it has no constructor, we don't need to provide it here):

forge verify-contract \
--chain-id 11155111 \ #sepolia
--watch \
--etherscan-api-key <ETHERSCAN-API-KEY> \
<YOUR-DEPLOYED-CONTRACT-ADDRESS> \
src/Cube3ProtectedErc20UUPS.sol:Cube3ProtectedErc20UUPS

Now that our implementation is deployed, save its address. Next, create a foundry script script/deployProxy.s.sol and paste the code below (update your implementation address):

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "../src/Cube3ProtectedErc20UUPS.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "forge-std/Script.sol";

contract DeployUUPSProxy is Script {
    function run() public {
        address _implementation = YOUR-IMPLEMENTATION-ADDRESS; // Replace with your protected contract address
        address _cube3RouterProxy = CUBE3-ROUTER-ADDRESS; // Replace with CUBE3 Router address

        vm.startBroadcast();

        // Deploy the proxy contract with the implementation address and initializer
        ERC1967Proxy cube3ProtectedTokenProxy = new ERC1967Proxy(_implementation, abi.encodeCall(Cube3ProtectedErc20UUPS.initialize, (_cube3RouterProxy, msg.sender, true)));

        vm.stopBroadcast();
        // Log the proxy address
        console.log("UUPS Proxy Address:", address(cube3ProtectedTokenProxy));
    }
}

To run this script, run the following in your cmd:

forge script script/deployProxy.s.sol \
-vvvvv \
--rpc-url <RPC-URL> \
--broadcast \
--chain sepolia \
--evm-version shanghai \
--private-key <PRIVATE-KEY>                               

Hardhat

a. Non-Upgradeable

To deploy a non-upgradeable example below, you need to pass the router address into its constructor (find the CUBE3 Router address here). Let's deploy an example below:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@cube3/Cube3Protection.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Cube3ProtectedErc20 is ERC20, Cube3Protection {
    constructor(address _router)
        Cube3Protection(_router, msg.sender, true)
        ERC20("Cube3Token", "CTK")
    {
        _mint(msg.sender, 10000 * 10**decimals());
    }

    function mintCube3TokenBlockByName(
        address to,
        uint256 amount,
        bytes calldata cube3SecurePayload
    ) public cube3Protected(cube3SecurePayload) {
        _mint(to, amount);
    }
}

Update scripts/deploy.ts file with the following:

import {ethers} from "hardhat";

async function main() {
  const [deployer] = await ethers.getSigners();

  console.log("Deploying contracts with the account:", deployer.address);

  const Cube3ProtectedErc20 = await ethers.getContractFactory("Cube3ProtectedErc20");
  const contract = await Cube3ProtectedErc20.deploy("Router proxy address");

  await contract.waitForDeployment();
  const address = await contract.getAddress()
  console.log(`Cube3ProtectedErc20 deployed to: ${address}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Lastly, to deploy Cube3ProtectedErc20 contract, run the following in cmd:

npx hardhat run scripts/deploy.ts --network sepolia

b. Upgradeable

For upgradeable contracts, we have two steps:

  1. Deploy implementation

  2. Deploy Proxy that points to implementation

Let's deploy the implementation example below:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "@openzeppelin-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin-upgradeable/contracts/access/OwnableUpgradeable.sol";
import "@cube3/upgradeable/Cube3ProtectionUpgradeable.sol";

contract Cube3ProtectedErc20UUPS is Cube3ProtectionUpgradeable, ERC20Upgradeable, UUPSUpgradeable, OwnableUpgradeable {
    function initialize(address router, address admin, bool checkProtection) initializer public {
        // In this scenario, the contract owner is the same account as the integration's admin, which
        // has privileged access to the router.
        __Cube3ProtectionUpgradeable_init(router, admin, checkProtection);
        __UUPSUpgradeable_init();
        __ERC20_init("Cube3ProtectedToken", "CTK");

        _mint(admin, 10000 * 10**decimals());
    }

    function _authorizeUpgrade(address newImplementation)
    internal
    virtual
    override
    onlyOwner
    {}

    function mintCube3Protected(
        address to,
        uint256 amount,
        bytes calldata cube3SecurePayload
    ) public cube3Protected(cube3SecurePayload) {
        _mint(to, amount);
    }
}

Update scripts/deploy.ts file with the following:

import {ethers, upgrades} from "hardhat";
import { getImplementationAddress } from '@openzeppelin/upgrades-core';

async function main() {
  const [deployer] = await ethers.getSigners();

  console.log("Deploying contracts with the account:", deployer.address);

  const Cube3ProtectedErc20UUPS = await ethers.getContractFactory("Cube3ProtectedErc20UUPS");
  const cube3ProtectedErc20UUPS = await upgrades.deployProxy(
    Cube3ProtectedErc20UUPS,
    [deployer.address],
    {
      kind: 'transparent',
      initializer: 'initialize',
      unsafeAllow: ['constructor', 'state-variable-immutable'],
    }
  );

  await cube3ProtectedErc20UUPS.waitForDeployment();

  const proxyAddress = await cube3ProtectedErc20UUPS.getAddress()
  console.log("Cube3ProtectedErc20UUPS PROXY deployed to:", proxyAddress);

  const currentImplAddress = await getImplementationAddress(deployer.provider, proxyAddress);
  console.log("Cube3ProtectedErc20UUPS IMPLEMENTATION deployed to:", currentImplAddress);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Lastly, to deploy Cube3ProtectedErc20 contract, run the following in cmd:

npx hardhat run scripts/deploy.ts --network sepolia

Last updated