Skip to content

Commit

Permalink
Merge pull request #40 from loreum-org/feat/upgradeable
Browse files Browse the repository at this point in the history
Feat/upgradeable
  • Loading branch information
xhad authored Sep 15, 2023
2 parents 5811183 + 4440e58 commit 116bcb9
Show file tree
Hide file tree
Showing 15 changed files with 256 additions and 126 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![License: BUSL 1.1](https://img.shields.io/badge/License-MIT.svg)](https://github.com/loreum-org/chamber/LICENSE)

The Chamber is a multisig wallet that enables liquid democracy for Treasury and Protocol Management by the token community. Due to their composability, Chambers are a protocol governance standard that enable Roles to be controlled by token holders through representative leaders, rather than a core group of static founders. This enables decentralized ownership of DeFi protocols.
Chambers provide the functionality of a multisig wallet where signers are determined by delegation of ERC20 governance token.
Chambers provide the functionality of a multisig wallet where signers are determined by delegation of ERC20 governance tokens.

The contract inherits upon instantiation existing ERC20 governance and ERC721 membership tokens. Delegations are made to ERC721 tokens which creates a leaderboard within the Chamber contract. The leaders are responsible for signing transactions and being the governors of the multisig. Each leader has a single vote that is not correlated to wallet balance, but rather by delegation of ERC20 governance tokens by the community against their NFT TokenId.

Expand All @@ -34,7 +34,7 @@ Chambers are composable by inheriting any exisitng governance ERC20 token accros

3. **DAO Governance**

Instantiating a Chambers with the same ERC20 and ERC721 tokens as used in common with the community creates a shared value system as voting power can migrate across the various Chambers.
Instantiating a Chamber with the same ERC20 and ERC721 tokens as used in common with the tokeneconmic model creates a shared value system. Voting power to control assets depends up control of token delegataion which can migrate, but not inflate or dilute voting power across the various Chambers. The scarcity of total supply extends to limit the authority of token balances.

```mermaid
erDiagram
Expand Down
10 changes: 7 additions & 3 deletions script/Registry.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.19;
import "forge-std/Script.sol";
import { Registry } from "../src/Registry.sol";
import { Chamber } from "../src/Chamber.sol";
import { Proxy } from "../src/Proxy.sol";
import "forge-std/console2.sol";

contract DeployRegistry is Script {
Expand All @@ -13,11 +14,14 @@ contract DeployRegistry is Script {
vm.startBroadcast();
Chamber chamber = new Chamber();
vm.stopBroadcast();
console2.log("Chamber address: ", address(chamber));
console2.log("Chamber Implementation address: ", address(chamber));

vm.startBroadcast();
Registry registry = new Registry(address(chamber));

Registry registryImpl = new Registry();
bytes memory data = abi.encodeWithSelector(Registry.initialize.selector, address(registryImpl), msg.sender);
Proxy registry = new Proxy(address(registryImpl), data, msg.sender);
vm.stopBroadcast();
console2.log("Registry address: ", address(registry));
console2.log("Registry Proxy address: ", address(registry));
}
}
4 changes: 2 additions & 2 deletions src/ProxyChamber.sol → src/Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

pragma solidity 0.8.19;

import { IProxyChamber } from "./interfaces/IProxyChamber.sol";
import { IProxy } from "./interfaces/IProxy.sol";
import { ERC1967Proxy } from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract ProxyChamber is IProxyChamber, ERC1967Proxy {
contract Proxy is IProxy, ERC1967Proxy {

modifier onlyAdmin() {
if(msg.sender != super._getAdmin()) revert notAdmin();
Expand Down
23 changes: 14 additions & 9 deletions src/Registry.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { Proxy } from "./Proxy.sol";
import { IChamber } from "./interfaces/IChamber.sol";
import { IRegistry } from "./interfaces/IRegistry.sol";
import { ProxyChamber } from "./ProxyChamber.sol";
import { Ownable } from "openzeppelin-contracts/contracts/access/Ownable.sol";
import { Initializable } from "openzeppelin-contracts/contracts/proxy/utils/Initializable.sol";

contract Registry is IRegistry, Ownable {
contract Registry is IRegistry, Initializable, Ownable {

/// @notice totalChamber The tsotal number of Chambers
/// @notice totalChamber The total number of Chambers
uint256 public totalChambers;

/// @notice chambers The Deployed Chambers
Expand All @@ -18,8 +19,12 @@ contract Registry is IRegistry, Ownable {
/// @notice chamerVersion is the latest version of the Chamber contract
address public chamberVersion;

/// @notice contructor receives the base Chamber implementation address
constructor(address _chamberVersion) Ownable() {
/// @notice contructor disables initializers
constructor() { _disableInitializers(); }

/// @inheritdoc IRegistry
function initialize(address _chamberVersion, address _owner) external initializer {
super._transferOwnership(_owner);
chamberVersion = _chamberVersion;
}

Expand All @@ -41,19 +46,19 @@ contract Registry is IRegistry, Ownable {
function deploy(address _memberToken, address _govToken) external returns (address) {

bytes memory data = abi.encodeWithSelector(IChamber.initialize.selector, _memberToken, _govToken);
ProxyChamber proxyChamber = new ProxyChamber(chamberVersion, data, msg.sender);
Proxy chamber = new Proxy(chamberVersion, data, msg.sender);

ChamberData memory chamberData = ChamberData({
chamber: address(proxyChamber),
chamber: address(chamber),
memberToken: _memberToken,
govToken: _govToken
});

chambers[totalChambers] = chamberData;
totalChambers++;

emit ChamberDeployed(address(proxyChamber), totalChambers, msg.sender, _memberToken, _govToken);
return address(proxyChamber);
emit ChamberDeployed(address(chamber), totalChambers, msg.sender, _memberToken, _govToken);
return address(chamber);
}
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IProxyChamber {
interface IProxy {

function getImplementation() external view returns (address);

Expand Down
6 changes: 6 additions & 0 deletions src/interfaces/IRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ interface IRegistry {
Functions
**************************************************/

function initialize(address _chamberVersion, address _owner) external;

function totalChambers() external returns (uint256);

function chambers(uint256 _index) external returns (address chamber, address memberToken, address govToken);

/// @notice Returns the Chamber implmentation address
function chamberVersion() external returns (address);

Expand Down
17 changes: 12 additions & 5 deletions test/Chamber.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import "../lib/forge-std/src/Test.sol";
import { Registry } from "../src/Registry.sol";
import { Chamber } from "../src/Chamber.sol";

import { DeployRegistry } from "../test/utils/DeployRegistry.sol";
import { IRegistry } from "../src/interfaces/IRegistry.sol";
import { IChamber } from "../src/interfaces/IChamber.sol";
import { MockERC20 } from "../lib/contract-utils/src/MockERC20.sol";
import { MockNFT } from "../lib/contract-utils/src/MockNFT.sol";
Expand All @@ -16,21 +18,26 @@ contract ChamberTest is Test {
MockERC20 mERC20;
MockNFT mNFT;
IChamber chamber;
IRegistry registry;

address registryProxyAddr;
address chamberProxyAddr;

function setUp() public {

mERC20 = new MockERC20("MockERC20", "mERC20", address(this));
mNFT = new MockNFT("MockNFT", "mNFT", address(this));

Registry registry = new Registry(address(new Chamber()));
address newChamber = registry.deploy(address(mNFT), address(mERC20));
chamber = IChamber(newChamber);
DeployRegistry registryDeployer = new DeployRegistry();
registryProxyAddr = registryDeployer.deploy(address(this));
chamberProxyAddr = IRegistry(registryProxyAddr).deploy(address(mNFT), address(mERC20));
chamber = IChamber(chamberProxyAddr);

USD = new MockERC20("US Dollar", "USD", address(chamber));
vm.deal(address(chamber), 100 ether);
}

function promoteExplorers() public {
function promoteMembers() public {

// Approve Chamber for large amount of LORE
mERC20.approve(address(chamber), 10_000_000_000 ether);
Expand All @@ -47,7 +54,7 @@ contract ChamberTest is Test {

function test_Chamber_proposal() public {

promoteExplorers();
promoteMembers();

// Create Proposal

Expand Down
86 changes: 86 additions & 0 deletions test/Proxy.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "../lib/forge-std/src/Test.sol";

import { Registry } from "../src/Registry.sol";
import { Chamber } from "../src/Chamber.sol";

import { IRegistry } from "../src/interfaces/IRegistry.sol";
import { IChamber } from "../src/interfaces/IChamber.sol";
import { IProxy } from "../src/interfaces/IProxy.sol";
import { DeployRegistry } from "../test/utils/DeployRegistry.sol";

import { MockERC20 } from "../lib/contract-utils/src/MockERC20.sol";
import { MockNFT } from "../lib/contract-utils/src/MockNFT.sol";
import { ERC1967Proxy } from "../lib/openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract ProxyUpgradeTest is Test {

MockERC20 mERC20;
MockNFT mERC721;

IRegistry registry;
IChamber chamber;

IProxy registryProxy;
IProxy chamberProxy;

address chamberProxyAddr;
address registryProxyAddr;

function setUp() public {

mERC20 = new MockERC20("MockERC20", "mERC20", address(this));
mERC721 = new MockNFT("MockNFT", "mNFT", address(this));

DeployRegistry registryDeployer = new DeployRegistry();
registryProxyAddr = registryDeployer.deploy(address(this));
chamberProxyAddr = IRegistry(registryProxyAddr).deploy(address(mERC721), address(mERC20));

chamberProxy = IProxy(chamberProxyAddr);
registryProxy = IProxy(registryProxyAddr);

chamber = IChamber(chamberProxyAddr);
registry = IRegistry(registryProxyAddr);
}

function test_Proxy_upgrade() public {
chamberProxy.getImplementation();
mERC20.approve(address(chamberProxy), 1000);
chamber.promote(1, 1);
(uint8[5] memory leaders, uint256[5] memory amounts) = chamber.getLeaderboard();
Chamber chamberV2 = new Chamber();

chamberProxy.upgradeTo(address(chamberV2));
(uint8[5] memory newLeaders, uint256[5] memory newAmounts) = chamber.getLeaderboard();
assertEq(newLeaders[0], leaders[0]);
assertEq(newAmounts[0], amounts[0]);
assertEq(chamberProxy.getImplementation(), address(chamberV2));
IChamber(address(chamberProxy)).getLeaderboard();
}

function test_Proxy_access() public {
Chamber chamberV2 = new Chamber();

vm.expectRevert();
chamberProxy.changeAdmin(address(0));

vm.startPrank(address(1));
vm.expectRevert();
chamberProxy.changeAdmin(address(1));
vm.stopPrank();

chamberProxy.changeAdmin(address(1));
assertEq(chamberProxy.getAdmin(), address(1));

vm.expectRevert();
chamberProxy.upgradeTo(address(chamberV2));
chamberProxy.getImplementation();

Chamber chamberV3 = new Chamber();
vm.prank(address(1));
chamberProxy.upgradeTo(address(chamberV3));
assertEq(chamberProxy.getImplementation(), address(chamberV3));
}
}
72 changes: 0 additions & 72 deletions test/ProxyUpgrade.t.sol

This file was deleted.

Loading

0 comments on commit 116bcb9

Please sign in to comment.