Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgradeability #1

Merged
merged 20 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
jcmonte marked this conversation as resolved.
Show resolved Hide resolved

/// @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 */ )
jcmonte marked this conversation as resolved.
Show resolved Hide resolved
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
Loading