Skip to content

Commit

Permalink
feat: UUPS functions
Browse files Browse the repository at this point in the history
  • Loading branch information
johnson86tw committed Jan 8, 2025
1 parent 07e1eb3 commit e2188e1
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PRIVATE_KEY=
ETHERSCAN_API_KEY=
sepolia=https://ethereum-sepolia-rpc.publicnode.com
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## RoyaltyAutoClaim

- [去中心化領稿費機制實驗 HackMD](https://hackmd.io/@nic619/SkZDIp2GJl?utm_source=substack&utm_medium=email)
- [去中心化領稿費機制實驗 HackMD](https://hackmd.io/@nic619/SkZDIp2GJl?utm_source=substack&utm_medium=email)

### Rules

Owner
- 可以升級合約
- 可以指定 Admin
- 可以更改稿費幣種
- 可以轉移所有權

Admin
-
2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
28 changes: 28 additions & 0 deletions script/deploy_v1.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Script, console} from "forge-std/Script.sol";
import {UUPSProxy, RoyaltyAutoClaim} from "../src/RoyaltyAutoClaim.sol";

// forge script script/deploy_v1.s.sol --account dev --rpc-url $sepolia --broadcast --verify

contract DeployV1Script is Script {
function run() public {
address owner = msg.sender;
address admin = msg.sender;
address token = 0x0000000000000000000000000000000000000000;
address[] memory reviewers = new address[](0);

vm.startBroadcast();

RoyaltyAutoClaim royaltyAutoClaim = new RoyaltyAutoClaim();
UUPSProxy proxy = new UUPSProxy(
address(royaltyAutoClaim), abi.encodeCall(RoyaltyAutoClaim.initialize, (owner, admin, token, reviewers))
);

console.log("RoyaltyAutoClaim proxy at:", address(proxy));
console.log("RoyaltyAutoClaim implementation at:", address(royaltyAutoClaim));

vm.stopBroadcast();
}
}
3 changes: 3 additions & 0 deletions src/Counter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ pragma solidity ^0.8.13;
contract Counter {
uint256 public number;

event SetNumber(uint256 newNumber);

function setNumber(uint256 newNumber) public {
number = newNumber;
emit SetNumber(newNumber);
}

function increment() public {
Expand Down
49 changes: 48 additions & 1 deletion src/RoyaltyAutoClaim.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,51 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

contract RoyaltyAutoClaim {}
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract UUPSProxy is ERC1967Proxy {
constructor(address _implementation, bytes memory _data) payable ERC1967Proxy(_implementation, _data) {
(bool success,) = _implementation.delegatecall(_data);
require(success, "Initialization failed");
}
}

contract RoyaltyAutoClaim is UUPSUpgradeable, OwnableUpgradeable {
string internal constant RENUNCIATION_DISABLED = "Renouncing ownership is disabled";

address public admin;
address public token; // 稿費幣種
address[] public reviewers;

constructor() {
_disableInitializers();
}

function initialize(address _owner, address _admin, address _token, address[] memory _reviewers)
public
initializer
{
__Ownable_init(_owner);
admin = _admin;
token = _token;
reviewers = _reviewers;
}

// ================================ Ownership ================================

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

function changeAdmin(address _admin) public onlyOwner {
admin = _admin;
}

function changeRoyaltyToken(address _token) public onlyOwner {
token = _token;
}

function renounceOwnership() public pure override {
revert(RENUNCIATION_DISABLED);
}
}
43 changes: 43 additions & 0 deletions src/RoyaltyAutoClaimV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract RoyaltyAutoClaimV2 is UUPSUpgradeable, OwnableUpgradeable {
string internal constant RENUNCIATION_DISABLED = "Renouncing ownership is disabled";

address public admin;
address public token; // 稿費幣種
address[] public reviewers;

constructor() {
_disableInitializers();
}

function initialize(address _owner, address _admin, address _token, address[] memory _reviewers)
public
reinitializer(2)
{
__Ownable_init(_owner);
admin = _admin;
token = _token;
reviewers = _reviewers;
}

// ================================ Ownership ================================

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

function changeAdmin(address _admin) public onlyOwner {
admin = _admin;
}

function changeRoyaltyToken(address _token) public onlyOwner {
token = _token;
}

function renounceOwnership() public pure override {
revert(RENUNCIATION_DISABLED);
}
}
3 changes: 3 additions & 0 deletions test/Counter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ contract CounterTest is Test {

function setUp() public {
counter = new Counter();

vm.expectEmit(true, false, false, true, address(counter));
emit Counter.SetNumber(0);
counter.setNumber(0);
}

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

import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

contract MockV2 is UUPSUpgradeable, OwnableUpgradeable {
string internal constant RENUNCIATION_DISABLED = "Renouncing ownership is disabled";

address public admin;
address public token; // 稿費幣種
address[] public reviewers;

constructor() {
_disableInitializers();
}

function initialize(address _owner, address _admin, address _token, address[] memory _reviewers)
public
reinitializer(2)
{
__Ownable_init(_owner);
admin = _admin;
token = _token;
reviewers = _reviewers;
}

// ================================ Ownership ================================

function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

function changeAdmin(address _admin) public onlyOwner {
admin = _admin;
}

function changeRoyaltyToken(address _token) public onlyOwner {
token = _token;
}

function renounceOwnership() public pure override {
revert(RENUNCIATION_DISABLED);
}
}
54 changes: 52 additions & 2 deletions test/RoyaltyAutoClaim.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,59 @@
pragma solidity ^0.8.27;

import "forge-std/Test.sol";
import "../src/RoyaltyAutoClaim.sol";
import "./MockV2.sol";

contract RoyaltyAutoClaimTest is Test {
function setUp() public {}
RoyaltyAutoClaim v1;
address owner = vm.addr(1);
address admin = vm.addr(2);
address token = vm.addr(3);
address[] reviewers = new address[](3);
UUPSProxy proxy;

function test() public {}
function setUp() public {
reviewers[0] = vm.addr(4);
reviewers[1] = vm.addr(5);
reviewers[2] = vm.addr(6);

v1 = new RoyaltyAutoClaim();

proxy =
new UUPSProxy(address(v1), abi.encodeCall(RoyaltyAutoClaim.initialize, (owner, admin, token, reviewers)));

assertEq(RoyaltyAutoClaim(address(proxy)).owner(), owner);
assertEq(RoyaltyAutoClaim(address(proxy)).admin(), admin);
assertEq(RoyaltyAutoClaim(address(proxy)).token(), token);
assertEq(RoyaltyAutoClaim(address(proxy)).reviewers(0), reviewers[0]);
assertEq(RoyaltyAutoClaim(address(proxy)).reviewers(1), reviewers[1]);
assertEq(RoyaltyAutoClaim(address(proxy)).reviewers(2), reviewers[2]);
bytes32 v = vm.load(address(proxy), ERC1967Utils.IMPLEMENTATION_SLOT);
assertEq(address(uint160(uint256(v))), address(v1));
}

function test_upgradeToAndCall() public {
address newOwner = vm.randomAddress();
address newAdmin = vm.randomAddress();
address newToken = vm.randomAddress();
address[] memory newReviewers = new address[](3);
newReviewers[0] = vm.randomAddress();
newReviewers[1] = vm.randomAddress();

MockV2 v2 = new MockV2();

vm.prank(owner);
MockV2(address(proxy)).upgradeToAndCall(
address(v2), abi.encodeCall(MockV2.initialize, (newOwner, newAdmin, newToken, newReviewers))
);

assertEq(MockV2(address(proxy)).owner(), newOwner);
assertEq(MockV2(address(proxy)).admin(), newAdmin);
assertEq(MockV2(address(proxy)).token(), newToken);
assertEq(MockV2(address(proxy)).reviewers(0), newReviewers[0]);
assertEq(MockV2(address(proxy)).reviewers(1), newReviewers[1]);

bytes32 v = vm.load(address(proxy), ERC1967Utils.IMPLEMENTATION_SLOT);
assertEq(address(uint160(uint256(v))), address(v2));
}
}

0 comments on commit e2188e1

Please sign in to comment.