Skip to content

Commit

Permalink
Merge pull request #1 from Kwenta/upgradeable
Browse files Browse the repository at this point in the history
Upgradeability
  • Loading branch information
Flocqst authored Sep 13, 2024
2 parents e73f438 + 24a96c5 commit 15cfe3c
Show file tree
Hide file tree
Showing 19 changed files with 359 additions and 100 deletions.
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "lib/solady"]
path = lib/solady
url = https://github.com/Vectorized/solady
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
[![Foundry][foundry-badge]][foundry]
[![License: GPL-3.0][license-badge]][license]

[gha]: https://github.com/Kwenta/foundry-scaffold/actions
[gha-badge]: https://github.com/Kwenta/foundry-scaffold/actions/workflows/test.yml/badge.svg

[gha]: https://github.com/Kwenta/KSX/actions
[gha-badge]: https://github.com/Kwenta/KSX/actions/workflows/test.yml/badge.svg
[foundry]: https://getfoundry.sh/
[foundry-badge]: https://img.shields.io/badge/Built%20with-Foundry-FFDB1C.svg
[license]: https://opensource.org/license/GPL-3.0/
Expand Down
5 changes: 5 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ optimizer_runs = 1_000_000
[fmt]
line_length = 80
number_underscore = "thousands"
multiline_func_header = "all"
sort_imports = true
contract_new_lines = true
override_spacing = false
wrap_comments = true

[rpc_endpoints]
mainnet = "${MAINNET_RPC_URL}"
Expand Down
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts-upgradeable
1 change: 1 addition & 0 deletions lib/solady
Submodule solady added at 678c91
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"coverage": "forge coverage --fork-url $(grep OPTIMISM_GOERLI_RPC_URL .env | cut -d '=' -f2)",
"coverage:generate-lcov": "forge coverage --fork-url $(grep OPTIMISM_GOERLI_RPC_URL .env | cut -d '=' -f2) --report lcov",
"analysis:slither": "slither .",
"gas-snapshot": "forge snapshot --fork-url $(grep OPTIMISM_GOERLI_RPC_URL .env | cut -d '=' -f2)",
"decode-custom-error": "npx @usecannon/cli decode synthetix-perps-market"
"gas-snapshot": "forge snapshot --fork-url $(grep OPTIMISM_GOERLI_RPC_URL .env | cut -d '=' -f2)"
},
"repository": {
"type": "git",
Expand Down
3 changes: 2 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
83 changes: 41 additions & 42 deletions script/Deploy.s.sol
Original file line number Diff line number Diff line change
@@ -1,81 +1,80 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.25;

// TODO: adapt deploy script to deploy the KSXVault contract
/*
import {BaseGoerliParameters} from
"script/utils/parameters/BaseGoerliParameters.sol";
import {BaseParameters} from "script/utils/parameters/BaseParameters.sol";
// proxy
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC1967Proxy as Proxy} from
"lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";

// contracts
import {KSXVault} from "src/KSXVault.sol";

// parameters
import {OptimismGoerliParameters} from
"script/utils/parameters/OptimismGoerliParameters.sol";
import {OptimismParameters} from
"script/utils/parameters/OptimismParameters.sol";

// forge utils
import {Script} from "lib/forge-std/src/Script.sol";
import {Counter} from "src/Counter.sol";

/// @title Kwenta deployment script
/// @title Kwenta KSX deployment script
/// @author Flocqst ([email protected])
contract Setup is Script {
function deploySystem() public returns (address) {
Counter counter = new Counter();
return address(counter);
}
}

/// @dev steps to deploy and verify on Base:
/// (1) load the variables in the .env file via `source .env`
/// (2) run `forge script script/Deploy.s.sol:DeployBase --rpc-url $BASE_RPC_URL --etherscan-api-key $BASESCAN_API_KEY --broadcast --verify -vvvv`
contract DeployBase is Setup, BaseParameters {
function run() public {
uint256 privateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(privateKey);
Setup.deploySystem();
vm.stopBroadcast();
function deploySystem(
address token,
address pDAO
)
public
returns (KSXVault ksxVault)
{
// Deploy KSX Vault Implementation
address ksxVaultImplementation = address(new KSXVault(pDAO));
ksxVault = KSXVault(
address(
new Proxy(
ksxVaultImplementation,
abi.encodeWithSignature("initialize(address)", token)
)
)
);
}
}

/// @dev steps to deploy and verify on Base Goerli:
/// (1) load the variables in the .env file via `source .env`
/// (2) run `forge script script/Deploy.s.sol:DeployBaseGoerli --rpc-url $BASE_GOERLI_RPC_URL --etherscan-api-key $BASESCAN_API_KEY --broadcast --verify -vvvv`
contract DeployBaseGoerli is Setup, BaseGoerliParameters {
function run() public {
uint256 privateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(privateKey);
Setup.deploySystem();
vm.stopBroadcast();
}
}

/// @dev steps to deploy and verify on Optimism:
/// (1) load the variables in the .env file via `source .env`
/// (2) run `forge script script/Deploy.s.sol:DeployOptimism --rpc-url $OPTIMISM_RPC_URL --etherscan-api-key $OPTIMISM_ETHERSCAN_API_KEY --broadcast --verify -vvvv`
/// (2) run `forge script script/Deploy.s.sol:DeployOptimism --rpc-url
/// $OPTIMISM_RPC_URL --etherscan-api-key $OPTIMISM_ETHERSCAN_API_KEY
/// --broadcast --verify -vvvv`
contract DeployOptimism is Setup, OptimismParameters {

function run() public {
uint256 privateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(privateKey);

Setup.deploySystem();
Setup.deploySystem({token: KWENTA, pDAO: PDAO});

vm.stopBroadcast();
}

}

/// @dev steps to deploy and verify on Optimism Goerli:
/// (1) load the variables in the .env file via `source .env`
/// (2) run `forge script script/Deploy.s.sol:DeployOptimismGoerli --rpc-url $OPTIMISM_GOERLI_RPC_URL --etherscan-api-key $OPTIMISM_ETHERSCAN_API_KEY --broadcast --verify -vvvv`
/// (2) run `forge script script/Deploy.s.sol:DeployOptimismGoerli --rpc-url
/// $OPTIMISM_GOERLI_RPC_URL --etherscan-api-key $OPTIMISM_ETHERSCAN_API_KEY
/// --broadcast --verify -vvvv`
contract DeployOptimismGoerli is Setup, OptimismGoerliParameters {

function run() public {
uint256 privateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(privateKey);

Setup.deploySystem();
Setup.deploySystem({token: KWENTA, pDAO: PDAO});

vm.stopBroadcast();
}

}
*/
4 changes: 0 additions & 4 deletions script/utils/parameters/BaseGoerliParameters.sol

This file was deleted.

4 changes: 0 additions & 4 deletions script/utils/parameters/BaseParameters.sol

This file was deleted.

12 changes: 11 additions & 1 deletion script/utils/parameters/OptimismGoerliParameters.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.25;

contract OptimismGoerliParameters {}
contract OptimismGoerliParameters {

/// @dev this is an EOA used on testnet only
address public constant PDAO = 0x1b4fCFE451A15218aEeC811B508B4aa3f2A35904;

// https://developers.circle.com/stablecoins/docs/usdc-on-test-networks#usdc-on-op-goerli
address public constant USDC = 0xe05606174bac4A6364B31bd0eCA4bf4dD368f8C6;

address public constant KWENTA = 0x920Cf626a271321C151D027030D5d08aF699456b;

}
12 changes: 11 additions & 1 deletion script/utils/parameters/OptimismParameters.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.25;

contract OptimismParameters {}
contract OptimismParameters {

address public constant PDAO = 0xe826d43961a87fBE71C91d9B73F7ef9b16721C07;

// https://optimistic.etherscan.io/token/0x0b2c639c533813f4aa9d7837caf62653d097ff85
address public constant USDC = 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85;

// https://optimistic.etherscan.io/token/0x920cf626a271321c151d027030d5d08af699456b
address public constant KWENTA = 0x920Cf626a271321C151D027030D5d08aF699456b;

}
72 changes: 65 additions & 7 deletions src/KSXVault.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,74 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

import {ERC4626Upgradeable} from
"@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import {ERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC4626} from
"@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import {ERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20Metadata} from
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import {IKSXVault} from "src/interfaces/IKSXVault.sol";

import {UUPSUpgradeable} from
"@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

/// @title Kwenta Example Contract
/// @title KSXVault Contract
/// @notice KSX ERC4626 Vault
/// @author Flocqst ([email protected])
contract KSXVault is ERC4626 {
constructor(address _token)
ERC4626(IERC20(_token))
ERC20("KSX Vault", "KSX")
{}
contract KSXVault is IKSXVault, ERC4626Upgradeable, UUPSUpgradeable {

/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/

/// @notice Kwenta owned/operated multisig address that
/// can authorize upgrades
/// @dev if this address is the zero address, then the
/// KSX vault will no longer be upgradeable
/// @dev making immutable because the pDAO address
/// will *never* change
address internal immutable pDAO;

/*///////////////////////////////////////////////////////////////
CONSTRUCTOR / INITIALIZER
///////////////////////////////////////////////////////////////*/

/// @dev disable default constructor to disable the implementation contract
/// Actual contract construction will take place in the initialize function
/// via proxy
/// @custom:oz-upgrades-unsafe-allow constructor
/// @param _pDAO Kwenta owned/operated multisig address that can authorize
/// upgrades
constructor(address _pDAO) {
_disableInitializers();

/// @dev pDAO address can be the zero address to
/// make the KSX vault non-upgradeable
pDAO = _pDAO;
}

/// @notice Initializes the contract
/// @param _token The address for the KWENTA ERC20 token
function initialize(address _token) external initializer {
__ERC20_init("KSX Vault", "KSX");
__ERC4626_init(IERC20(_token));
__UUPSUpgradeable_init();
}

/*//////////////////////////////////////////////////////////////
UPGRADE MANAGEMENT
//////////////////////////////////////////////////////////////*/

/// @inheritdoc UUPSUpgradeable
function _authorizeUpgrade(address /* _newImplementation */ )
internal
view
override
{
if (pDAO == address(0)) revert NonUpgradeable();
if (msg.sender != pDAO) revert OnlyPDAO();
}

}
25 changes: 25 additions & 0 deletions src/interfaces/IKSXVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

import "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";

/// @title Kwenta KSXVault Interface
/// @author Flocqst ([email protected])
interface IKSXVault {

/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/

/// @notice thrown when attempting to update
/// the KSXVault when caller is not the Kwenta pDAO
error OnlyPDAO();

/// @notice thrown when attempting to upgrade
/// the KSXVault when the KSXVault is not upgradeable
/// @dev the KSXVault is not upgradeable when
/// the pDAO has been set to the zero address
error NonUpgradeable();

}
28 changes: 23 additions & 5 deletions test/KSXVault.t.sol
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.25;

import {Test} from "forge-std/Test.sol";
import {KSXVault} from "../src/KSXVault.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Test} from "forge-std/Test.sol";
import {Bootstrap, KSXVault} from "test/utils/Bootstrap.sol";

contract KSXVaultTest is Bootstrap {

function setUp() public {
MockERC20 depositToken = new MockERC20("Deposit Token", "DT");
initializeLocal(address(depositToken), PDAOADDR);
}

function test_share_name() public {
assertEq(ksxVault.name(), "KSX Vault");
}

function test_share_symbol() public {
assertEq(ksxVault.symbol(), "KSX");
}

contract KSXVaultTest is Test {
function setUp() public {}
}

contract MockERC20 is ERC20 {
constructor(string memory name_, string memory symbol_)

constructor(
string memory name_,
string memory symbol_
)
ERC20(name_, symbol_)
{}

function mint(address to, uint256 amount) external {
_mint(to, amount);
}

}
Loading

0 comments on commit 15cfe3c

Please sign in to comment.