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

feat: snapshot proxy #1872

Merged
merged 5 commits into from
Feb 5, 2025
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
3 changes: 3 additions & 0 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Refresh the list of deployed contracts by running `./scripts/generateDeployments
- [EvidenceModule: proxy](https://arbiscan.io/address/0x48e052B4A6dC4F30e90930F1CeaAFd83b3981EB3), [implementation](https://arbiscan.io/address/0xE22500Fa27f696d06702367246bd17Bd2C8a4c5d)
- [KlerosCoreNeo: proxy](https://arbiscan.io/address/0x991d2df165670b9cac3B022f4B68D65b664222ea), [implementation](https://arbiscan.io/address/0x17c39AB53A7072b167A74a85D47b30385c98ae89)
- [KlerosCoreRulerNeo: proxy](https://arbiscan.io/address/0xc0169e0B19aE02ac4fADD689260CF038726DFE13), [implementation](https://arbiscan.io/address/0x85093b5EDa4F2e2E2fEDae34Da91239D6a08e324)
- [KlerosCoreSnapshotProxy](https://arbiscan.io/address/0xEF719a5B3352F607e6C4E17b7e0cDAd8322fEC95)
- [KlerosV2NeoEarlyUser](https://arbiscan.io/address/0xfE34a72c55e512601E7d491A9c5b36373cE34d63)
- [Pinakion](https://arbiscan.io/address/0x330bD769382cFc6d50175903434CCC8D206DCAE5)
- [PolicyRegistry: proxy](https://arbiscan.io/address/0x553dcbF6aB3aE06a1064b5200Df1B5A9fB403d3c), [implementation](https://arbiscan.io/address/0x15E5964C7751dF8563eA4bC000301582C79BC454)
Expand All @@ -39,6 +40,7 @@ Refresh the list of deployed contracts by running `./scripts/generateDeployments
- [DisputeTemplateRegistry: proxy](https://sepolia.arbiscan.io/address/0xe763d31Cb096B4bc7294012B78FC7F148324ebcb), [implementation](https://sepolia.arbiscan.io/address/0x7283c07CC5224B20f431B1fa0E6d6db3cA02de34)
- [EvidenceModule: proxy](https://sepolia.arbiscan.io/address/0xA88A9a25cE7f1d8b3941dA3b322Ba91D009E1397), [implementation](https://sepolia.arbiscan.io/address/0x63CF56e1c99E65E4a9eCDCC805F4735E016F2dc8)
- [KlerosCore: proxy](https://sepolia.arbiscan.io/address/0xE8442307d36e9bf6aB27F1A009F95CE8E11C3479), [implementation](https://sepolia.arbiscan.io/address/0x0766e4B8c4a3aAC9371a5A9D6119E8125Adcfd55)
- [KlerosCoreSnapshotProxy](https://sepolia.arbiscan.io/address/0xd74e61A4dB9C6c3F2C97b62a319aE194f616858C)
- [PNKFaucet](https://sepolia.arbiscan.io/address/0x9f6ffc13B685A68ae359fCA128dfE776458Df464)
- [PinakionV2](https://sepolia.arbiscan.io/address/0x34B944D42cAcfC8266955D07A80181D2054aa225)
- [PolicyRegistry: proxy](https://sepolia.arbiscan.io/address/0x2668c46A14af8997417138B064ca1bEB70769585), [implementation](https://sepolia.arbiscan.io/address/0xB958113f96950C7806d584eFBed964288d46a0B8)
Expand Down Expand Up @@ -87,6 +89,7 @@ Refresh the list of deployed contracts by running `./scripts/generateDeployments
- [KlerosCore: proxy](https://sepolia.arbiscan.io/address/0xA54e7A16d7460e38a8F324eF46782FB520d58CE8), [implementation](https://sepolia.arbiscan.io/address/0x91a373BBdE0532F86410682F362e2Cf685e95085)
- [KlerosCoreNeo: proxy](https://sepolia.arbiscan.io/address/0x26bf077037550e437605F07e25EfcAd510715C3A), [implementation](https://sepolia.arbiscan.io/address/0x3bE96b7eAF6A3640DBa1f7CE58776D5b790B74CB)
- [KlerosCoreRuler: proxy](https://sepolia.arbiscan.io/address/0x7ffcd32A0521645E6fCFd071A68F0e26957775a5), [implementation](https://sepolia.arbiscan.io/address/0x97e30A3A940856A3913437912C746f1aF6ccC76c)
- [KlerosCoreSnapshotProxy](https://sepolia.arbiscan.io/address/0x9300D415af6e747ADe3C6cbA09a3b3CD5fb0c091)
- [KlerosCoreUniversity: proxy](https://sepolia.arbiscan.io/address/0x5AB37F38778Bc175852fA353056591D91c744ce6), [implementation](https://sepolia.arbiscan.io/address/0xF74DaBfC5F5dbdBD07636637204d9C35326D2906)
- [KlerosV2NeoEarlyUser](https://sepolia.arbiscan.io/address/0x0d60Ff8bbCF49Bc5352328E7E28e141834d7750F)
- [PNKFaucet](https://sepolia.arbiscan.io/address/0x7EFE468003Ad6A858b5350CDE0A67bBED58739dD)
Expand Down
6 changes: 6 additions & 0 deletions contracts/deploy/00-home-chain-arbitration-neo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)

console.log(`core.changeArbitrableWhitelist(${resolver.address}, true)`);
await core.changeArbitrableWhitelist(resolver.address, true);

await deploy("KlerosCoreSnapshotProxy", {
from: deployer,
args: [deployer, core.target],
log: true,
});
};

deployArbitration.tags = ["ArbitrationNeo"];
Expand Down
7 changes: 7 additions & 0 deletions contracts/deploy/00-home-chain-arbitration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ChainlinkRNG, DisputeKitClassic, KlerosCore } from "../typechain-types"

const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
const { ethers, deployments, getNamedAccounts, getChainId } = hre;
const { deploy } = deployments;
const { ZeroAddress } = hre.ethers;
const RNG_LOOKAHEAD = 20;

Expand Down Expand Up @@ -94,6 +95,12 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
} catch (e) {
console.error("failed to change currency rates:", e);
}

await deploy("KlerosCoreSnapshotProxy", {
from: deployer,
args: [deployer, core.target],
log: true,
});
};

deployArbitration.tags = ["Arbitration"];
Expand Down
228 changes: 228 additions & 0 deletions contracts/deployments/arbitrum/KlerosCoreSnapshotProxy.json

Large diffs are not rendered by default.

228 changes: 228 additions & 0 deletions contracts/deployments/arbitrumSepolia/KlerosCoreSnapshotProxy.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions contracts/src/arbitration/view/KlerosCoreSnapshotProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: MIT

/// @custom:authors: [@unknownunknown1]
/// @custom:reviewers: []
/// @custom:auditors: []
/// @custom:bounties: []
/// @custom:deployments: []

pragma solidity 0.8.24;

import {ISortitionModule} from "../interfaces/ISortitionModule.sol";

interface IKlerosCore {
function sortitionModule() external view returns (ISortitionModule);
}

/// @title KlerosCoreSnapshotProxy
/// Proxy contract for V2 that exposes staked PNK with balanceOf() function for Snapshot voting.
contract KlerosCoreSnapshotProxy {
// ************************************* //
// * State Modifiers * //
// ************************************* //

IKlerosCore public core;
address public governor;
string public constant name = "Staked Pinakion";
string public constant symbol = "stPNK";
uint8 public constant decimals = 18;

// ************************************* //
// * Modifiers * //
// ************************************* //

modifier onlyByGovernor() {
require(governor == msg.sender, "Access not allowed: Governor only.");
_;
}

// ************************************* //
// * Constructor * //
// ************************************* //

/// @dev Constructor
/// @param _governor The governor of the contract.
/// @param _core KlerosCore to read the balance from.
constructor(address _governor, IKlerosCore _core) {
governor = _governor;
core = _core;
}

// ************************************* //
// * Governance * //
// ************************************* //

/// @dev Changes the `governor` storage variable.
/// @param _governor The new value for the `governor` storage variable.
function changeGovernor(address _governor) external onlyByGovernor {
governor = _governor;
}

/// @dev Changes the `core` storage variable.
/// @param _core The new value for the `core` storage variable.
function changeCore(IKlerosCore _core) external onlyByGovernor {
core = _core;
}

// ************************************* //
// * Public Views * //
// ************************************* //

/// @dev Returns the amount of PNK staked in KlerosV2 for a particular address.
/// Note: Proxy doesn't need to differentiate between courts so we pass 0 as courtID.
/// @param _account The address to query.
/// @return totalStaked Total amount staked in V2 by the address.
function balanceOf(address _account) external view returns (uint256 totalStaked) {
(totalStaked, , , ) = core.sortitionModule().getJurorBalance(_account, 0);
}
}
51 changes: 40 additions & 11 deletions contracts/test/foundry/KlerosCore.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ pragma solidity 0.8.24;

import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol"; // Import the console for logging
import {KlerosCoreMock, KlerosCoreBase, IArbitratorV2} from "../../src/test/KlerosCoreMock.sol";
import {KlerosCoreMock, KlerosCoreBase} from "../../src/test/KlerosCoreMock.sol";
import {IArbitratorV2} from "../../src/arbitration/KlerosCoreBase.sol";
import {IDisputeKit} from "../../src/arbitration/interfaces/IDisputeKit.sol";
import {DisputeKitClassic} from "../../src/arbitration/dispute-kits/DisputeKitClassic.sol";
import {DisputeKitClassic, DisputeKitClassicBase} from "../../src/arbitration/dispute-kits/DisputeKitClassic.sol";
import {DisputeKitSybilResistant} from "../../src/arbitration/dispute-kits/DisputeKitSybilResistant.sol";
import {ISortitionModule} from "../../src/arbitration/interfaces/ISortitionModule.sol";
import {SortitionModuleMock, SortitionModuleBase} from "../../src/test/SortitionModuleMock.sol";
Expand All @@ -16,6 +17,7 @@ import {TestERC20} from "../../src/token/TestERC20.sol";
import {ArbitrableExample, IArbitrableV2} from "../../src/arbitration/arbitrables/ArbitrableExample.sol";
import {DisputeTemplateRegistry} from "../../src/arbitration/DisputeTemplateRegistry.sol";
import "../../src/libraries/Constants.sol";
import {IKlerosCore, KlerosCoreSnapshotProxy} from "../../src/snapshot-proxy/KlerosCoreSnapshotProxy.sol";

contract KlerosCoreTest is Test {
event Initialized(uint64 version);
Expand Down Expand Up @@ -1278,6 +1280,33 @@ contract KlerosCoreTest is Test {
core.setStakeBySortitionModule(staker1, GENERAL_COURT, 1000, false);
}

function test_setStake_snapshotProxyCheck() public {
vm.prank(staker1);
core.setStake(GENERAL_COURT, 12346);

KlerosCoreSnapshotProxy snapshotProxy = new KlerosCoreSnapshotProxy(governor, IKlerosCore(address(core)));
assertEq(snapshotProxy.name(), "Staked Pinakion", "Wrong name of the proxy token");
assertEq(snapshotProxy.symbol(), "stPNK", "Wrong symbol of the proxy token");
assertEq(snapshotProxy.decimals(), 18, "Wrong decimals of the proxy token");
assertEq(snapshotProxy.governor(), msg.sender, "Wrong governor");
assertEq(address(snapshotProxy.core()), address(core), "Wrong core in snapshot proxy");
assertEq(snapshotProxy.balanceOf(staker1), 12346, "Wrong stPNK balance");

vm.prank(other);
vm.expectRevert(bytes("Access not allowed: Governor only."));
snapshotProxy.changeCore(IKlerosCore(other));
vm.prank(governor);
snapshotProxy.changeCore(IKlerosCore(other));
assertEq(address(snapshotProxy.core()), other, "Wrong core in snapshot proxy after change");

vm.prank(other);
vm.expectRevert(bytes("Access not allowed: Governor only."));
snapshotProxy.changeGovernor(other);
vm.prank(governor);
snapshotProxy.changeGovernor(other);
assertEq(snapshotProxy.governor(), other, "Wrong governor after change");
}

// *************************************** //
// * Disputes * //
// *************************************** //
Expand Down Expand Up @@ -1326,7 +1355,7 @@ contract KlerosCoreTest is Test {
uint256 nbChoices = 2;
vm.prank(disputer);
vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.DisputeCreation(disputeID, nbChoices, newExtraData);
emit DisputeKitClassicBase.DisputeCreation(disputeID, nbChoices, newExtraData);
vm.expectEmit(true, true, true, true);
emit IArbitratorV2.DisputeCreation(disputeID, arbitrable);
arbitrable.createDispute{value: 0.04 ether}("Action");
Expand Down Expand Up @@ -1592,7 +1621,7 @@ contract KlerosCoreTest is Test {

vm.prank(staker1);
vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.CommitCast(disputeID, staker1, voteIDs, commit);
emit DisputeKitClassicBase.CommitCast(disputeID, staker1, voteIDs, commit);
disputeKit.castCommit(disputeID, voteIDs, commit);

(, , , uint256 totalCommited, uint256 nbVoters, uint256 choiceCount) = disputeKit.getRoundInfo(disputeID, 0, 0);
Expand All @@ -1608,7 +1637,7 @@ contract KlerosCoreTest is Test {

vm.prank(staker1);
vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.CommitCast(disputeID, staker1, voteIDs, commit);
emit DisputeKitClassicBase.CommitCast(disputeID, staker1, voteIDs, commit);
disputeKit.castCommit(disputeID, voteIDs, commit);

(, , , totalCommited, nbVoters, choiceCount) = disputeKit.getRoundInfo(disputeID, 0, 0);
Expand Down Expand Up @@ -1913,7 +1942,7 @@ contract KlerosCoreTest is Test {

vm.prank(crowdfunder1);
vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.Contribution(disputeID, 0, 1, crowdfunder1, 0.21 ether);
emit DisputeKitClassicBase.Contribution(disputeID, 0, 1, crowdfunder1, 0.21 ether);
disputeKit.fundAppeal{value: 0.21 ether}(disputeID, 1); // Fund the losing choice. Total cost will be 0.63 (0.21 + 0.21 * (20000/10000))

assertEq(crowdfunder1.balance, 9.79 ether, "Wrong balance of the crowdfunder");
Expand All @@ -1922,9 +1951,9 @@ contract KlerosCoreTest is Test {

vm.prank(crowdfunder1);
vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.Contribution(disputeID, 0, 1, crowdfunder1, 0.42 ether);
emit DisputeKitClassicBase.Contribution(disputeID, 0, 1, crowdfunder1, 0.42 ether);
vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.ChoiceFunded(disputeID, 0, 1);
emit DisputeKitClassicBase.ChoiceFunded(disputeID, 0, 1);
disputeKit.fundAppeal{value: 5 ether}(disputeID, 1); // Deliberately overpay to check reimburse

assertEq(crowdfunder1.balance, 9.37 ether, "Wrong balance of the crowdfunder");
Expand Down Expand Up @@ -2117,7 +2146,7 @@ contract KlerosCoreTest is Test {
vm.expectEmit(true, true, true, true);
emit KlerosCoreBase.DisputeKitJump(disputeID, 1, newDkID, DISPUTE_KIT_CLASSIC);
vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.DisputeCreation(disputeID, 2, newExtraData);
emit DisputeKitClassicBase.DisputeCreation(disputeID, 2, newExtraData);
vm.expectEmit(true, true, true, true);
emit KlerosCoreBase.AppealDecision(disputeID, arbitrable);
vm.expectEmit(true, true, true, true);
Expand Down Expand Up @@ -2692,11 +2721,11 @@ contract KlerosCoreTest is Test {
assertEq(address(disputeKit).balance, 1.04 ether, "Wrong balance of the DK");

vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.Withdrawal(disputeID, 0, 1, crowdfunder1, 0.63 ether);
emit DisputeKitClassicBase.Withdrawal(disputeID, 0, 1, crowdfunder1, 0.63 ether);
disputeKit.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 0, 1);

vm.expectEmit(true, true, true, true);
emit DisputeKitClassic.Withdrawal(disputeID, 0, 2, crowdfunder2, 0.41 ether);
emit DisputeKitClassicBase.Withdrawal(disputeID, 0, 2, crowdfunder2, 0.41 ether);
disputeKit.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 0, 2);

assertEq(crowdfunder1.balance, 10 ether, "Wrong balance of the crowdfunder1");
Expand Down