From ac0146fcd9ebc6619c4d52f8d1110b27e0a07402 Mon Sep 17 00:00:00 2001 From: "Eugene Y. Q. Shen" Date: Sat, 14 Sep 2024 16:41:50 -0700 Subject: [PATCH] copy files over from github.com/plumenetwork/p --- p/script/DeployToken.s.sol | 32 +++++++++ p/script/UpgradeToken.s.sol | 29 +++++++++ p/script/deploy.sh | 7 ++ p/script/upgrade.sh | 6 ++ p/src/Empty.sol | 8 +++ p/src/P.sol | 115 +++++++++++++++++++++++++++++++++ p/src/interfaces/IDeploy.sol | 16 +++++ p/src/interfaces/IDeployer.sol | 54 ++++++++++++++++ p/src/interfaces/IP.sol | 38 +++++++++++ p/src/proxy/PProxy.sol | 10 +++ 10 files changed, 315 insertions(+) create mode 100644 p/script/DeployToken.s.sol create mode 100644 p/script/UpgradeToken.s.sol create mode 100644 p/script/deploy.sh create mode 100644 p/script/upgrade.sh create mode 100644 p/src/Empty.sol create mode 100644 p/src/P.sol create mode 100644 p/src/interfaces/IDeploy.sol create mode 100644 p/src/interfaces/IDeployer.sol create mode 100644 p/src/interfaces/IP.sol create mode 100644 p/src/proxy/PProxy.sol diff --git a/p/script/DeployToken.s.sol b/p/script/DeployToken.s.sol new file mode 100644 index 0000000..4fec446 --- /dev/null +++ b/p/script/DeployToken.s.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { Empty } from "../src/Empty.sol"; +import { P } from "../src/P.sol"; +import { IDeployer } from "../src/interfaces/IDeployer.sol"; +import { PProxy } from "../src/proxy/PProxy.sol"; +import "forge-std/Script.sol"; + +contract DeployScript is Script { + + bytes32 private constant DEPLOY_SALT = keccak256("P"); + address private constant DEPLOYER_ADDRESS = 0x6513Aedb4D1593BA12e50644401D976aebDc90d8; + + function run(address admin) external { + vm.startBroadcast(); + + P pImpl = new P(); + console.log("pImpl deployed to:", address(pImpl)); + + address pProxy = IDeployer(DEPLOYER_ADDRESS).deploy( + abi.encodePacked( + type(PProxy).creationCode, abi.encode(pImpl, abi.encodeWithSelector(P.initialize.selector, admin)) + ), + DEPLOY_SALT + ); + console.log("pProxy deployed to:", pProxy); + + vm.stopBroadcast(); + } + +} diff --git a/p/script/UpgradeToken.s.sol b/p/script/UpgradeToken.s.sol new file mode 100644 index 0000000..e8904c0 --- /dev/null +++ b/p/script/UpgradeToken.s.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { P } from "../src/P.sol"; +import { IDeployer } from "../src/interfaces/IDeployer.sol"; +import { PProxy } from "../src/proxy/PProxy.sol"; +import "forge-std/Script.sol"; + +interface IUpgradeableProxy { + + function upgradeToAndCall(address, bytes memory) external payable; + +} + +contract DeployScript is Script { + + function run(address proxy) external { + vm.startBroadcast(); + + P pImpl = new P(); + console.log("pImpl deployed to:", address(pImpl)); + + IUpgradeableProxy(proxy).upgradeToAndCall(address(pImpl), ""); + console.log("pProxy upgraded at:", proxy); + + vm.stopBroadcast(); + } + +} diff --git a/p/script/deploy.sh b/p/script/deploy.sh new file mode 100644 index 0000000..7bc836c --- /dev/null +++ b/p/script/deploy.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +forge script script/DeployToken.s.sol \ + --rpc-url https://ethereum-rpc.publicnode.com \ + --verify --verifier etherscan --verifier-url https://api.etherscan.io/api \ + --etherscan-api-key $ETHERSCAN_API_KEY \ + --gas-estimate-multiplier 200 --broadcast -i 1 --sig "run(address)" "$1" diff --git a/p/script/upgrade.sh b/p/script/upgrade.sh new file mode 100644 index 0000000..17cbd90 --- /dev/null +++ b/p/script/upgrade.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +forge script script/UpgradeToken.s.sol \ + --rpc-url https://ethereum-sepolia-rpc.publicnode.com \ + --verify --verifier etherscan --verifier-url https://api-sepolia.etherscan.io/api \ + --broadcast -i 1 --sig "run(address)" "$1" diff --git a/p/src/Empty.sol b/p/src/Empty.sol new file mode 100644 index 0000000..6f9c2e2 --- /dev/null +++ b/p/src/Empty.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +contract Empty { + + constructor() { } + +} diff --git a/p/src/P.sol b/p/src/P.sol new file mode 100644 index 0000000..fd93415 --- /dev/null +++ b/p/src/P.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import { ERC20BurnableUpgradeable } from + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol"; +import { ERC20PausableUpgradeable } from + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PausableUpgradeable.sol"; +import { ERC20PermitUpgradeable } from + "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; + +/** + * @title P Token + * @notice This ERC20 contract represents the gas token of Plume Network + */ +contract P is + Initializable, + AccessControlUpgradeable, + ERC20Upgradeable, + ERC20BurnableUpgradeable, + ERC20PausableUpgradeable, + ERC20PermitUpgradeable, + UUPSUpgradeable +{ + + bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); + bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE"); + bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + /** + * @notice Initialize the contract + * @param admin The address of the admin + */ + function initialize(address admin) public initializer { + __ERC20_init("Plume", "P"); + __ERC20Burnable_init(); + __ERC20Pausable_init(); + __AccessControl_init(); + __UUPSUpgradeable_init(); + __ERC20Permit_init("Plume"); + + _grantRole(DEFAULT_ADMIN_ROLE, admin); + _grantRole(ADMIN_ROLE, admin); + _grantRole(MINTER_ROLE, admin); + _grantRole(BURNER_ROLE, admin); + _grantRole(PAUSER_ROLE, admin); + _grantRole(UPGRADER_ROLE, admin); + } + + ////////////////////////////////////////////////////// + // Admin functions for minting, burning and pausing // + ////////////////////////////////////////////////////// + + /** + * @notice Mint new tokens + * @dev Only the minter can mint new tokens + * @param to The address to mint the tokens to + * @param amount The amount of tokens to mint + */ + function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) { + _mint(to, amount); + } + + /** + * @notice Burn tokens + * @dev Only the burner can burn tokens + * @param from The address to burn the tokens from + * @param amount The amount of tokens to burn + */ + function burn(address from, uint256 amount) external onlyRole(BURNER_ROLE) { + _burn(from, amount); + } + + /** + * @notice Pause the contract + * @dev Only the pauser can pause the contract + */ + function pause() external onlyRole(PAUSER_ROLE) { + _pause(); + } + + /** + * @notice Unpause the contract + * @dev Only the pauser can unpause the contract + */ + function unpause() external onlyRole(PAUSER_ROLE) { + _unpause(); + } + + //////////////////////////////////////////// + // Internal functions for UUPSUpgradeable // + //////////////////////////////////////////// + + function _authorizeUpgrade(address newImplementation) internal override onlyRole(UPGRADER_ROLE) { } + + function _update( + address from, + address to, + uint256 value + ) internal override(ERC20Upgradeable, ERC20PausableUpgradeable) { + super._update(from, to, value); + } + +} diff --git a/p/src/interfaces/IDeploy.sol b/p/src/interfaces/IDeploy.sol new file mode 100644 index 0000000..418666f --- /dev/null +++ b/p/src/interfaces/IDeploy.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +// https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/main/contracts/interfaces/IDeploy.sol + +pragma solidity ^0.8.0; + +/** + * @title IDeploy Interface + * @notice This interface defines the errors for a contract that is responsible for deploying new contracts. + */ +interface IDeploy { + + error EmptyBytecode(); + error AlreadyDeployed(); + error DeployFailed(); + +} diff --git a/p/src/interfaces/IDeployer.sol b/p/src/interfaces/IDeployer.sol new file mode 100644 index 0000000..558b464 --- /dev/null +++ b/p/src/interfaces/IDeployer.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +// https://github.com/axelarnetwork/axelar-gmp-sdk-solidity/blob/main/contracts/interfaces/IDeployer.sol + +pragma solidity ^0.8.0; + +import { IDeploy } from "./IDeploy.sol"; + +/** + * @title IDeployer Interface + * @notice This interface defines the contract responsible for deploying and optionally initializing new contracts + * via a specified deployment method. + */ +interface IDeployer is IDeploy { + + error DeployInitFailed(); + + event Deployed(address indexed deployedAddress, address indexed sender, bytes32 indexed salt, bytes32 bytecodeHash); + + /** + * @notice Deploys a contract using a deployment method defined by derived contracts. + * @param bytecode The bytecode of the contract to be deployed + * @param salt A salt to influence the contract address + * @return deployedAddress_ The address of the deployed contract + */ + function deploy(bytes memory bytecode, bytes32 salt) external payable returns (address deployedAddress_); + + /** + * @notice Deploys a contract using a deployment method defined by derived contracts and initializes it. + * @param bytecode The bytecode of the contract to be deployed + * @param salt A salt to influence the contract address + * @param init Init data used to initialize the deployed contract + * @return deployedAddress_ The address of the deployed contract + */ + function deployAndInit( + bytes memory bytecode, + bytes32 salt, + bytes calldata init + ) external payable returns (address deployedAddress_); + + /** + * @notice Returns the address where a contract will be stored if deployed via {deploy} or {deployAndInit} by + * `sender`. + * @param bytecode The bytecode of the contract + * @param sender The address that will deploy the contract + * @param salt The salt that will be used to influence the contract address + * @return deployedAddress_ The address that the contract will be deployed to + */ + function deployedAddress( + bytes calldata bytecode, + address sender, + bytes32 salt + ) external view returns (address deployedAddress_); + +} diff --git a/p/src/interfaces/IP.sol b/p/src/interfaces/IP.sol new file mode 100644 index 0000000..7db9cd3 --- /dev/null +++ b/p/src/interfaces/IP.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.25; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; + +interface IP is IERC20, IERC20Metadata, IERC20Permit { + + /** + * @notice Mint new tokens + * @dev Only the minter can mint new tokens + * @param to The address to mint the tokens to + * @param amount The amount of tokens to mint + */ + function mint(address to, uint256 amount) external; + + /** + * @notice Burn tokens + * @dev Only the burner can burn tokens + * @param from The address to burn the tokens from + * @param amount The amount of tokens to burn + */ + function burn(address from, uint256 amount) external; + + /** + * @notice Pause the contract + * @dev Only the pauser can pause the contract + */ + function pause() external; + + /** + * @notice Unpause the contract + * @dev Only the pauser can unpause the contract + */ + function unpause() external; + +} diff --git a/p/src/proxy/PProxy.sol b/p/src/proxy/PProxy.sol new file mode 100644 index 0000000..960189a --- /dev/null +++ b/p/src/proxy/PProxy.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract PProxy is ERC1967Proxy { + + constructor(address _logic, bytes memory _data) ERC1967Proxy(_logic, _data) { } + +}