Skip to content

Commit 25b462b

Browse files
authored
Merge pull request #1872 from kleros/feat/snapshot-proxy
feat: snapshot proxy
2 parents 86c0288 + ff46e40 commit 25b462b

File tree

8 files changed

+818
-11
lines changed

8 files changed

+818
-11
lines changed

contracts/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Refresh the list of deployed contracts by running `./scripts/generateDeployments
1919
- [EvidenceModule: proxy](https://arbiscan.io/address/0x48e052B4A6dC4F30e90930F1CeaAFd83b3981EB3), [implementation](https://arbiscan.io/address/0xE22500Fa27f696d06702367246bd17Bd2C8a4c5d)
2020
- [KlerosCoreNeo: proxy](https://arbiscan.io/address/0x991d2df165670b9cac3B022f4B68D65b664222ea), [implementation](https://arbiscan.io/address/0x17c39AB53A7072b167A74a85D47b30385c98ae89)
2121
- [KlerosCoreRulerNeo: proxy](https://arbiscan.io/address/0xc0169e0B19aE02ac4fADD689260CF038726DFE13), [implementation](https://arbiscan.io/address/0x85093b5EDa4F2e2E2fEDae34Da91239D6a08e324)
22+
- [KlerosCoreSnapshotProxy](https://arbiscan.io/address/0xEF719a5B3352F607e6C4E17b7e0cDAd8322fEC95)
2223
- [KlerosV2NeoEarlyUser](https://arbiscan.io/address/0xfE34a72c55e512601E7d491A9c5b36373cE34d63)
2324
- [Pinakion](https://arbiscan.io/address/0x330bD769382cFc6d50175903434CCC8D206DCAE5)
2425
- [PolicyRegistry: proxy](https://arbiscan.io/address/0x553dcbF6aB3aE06a1064b5200Df1B5A9fB403d3c), [implementation](https://arbiscan.io/address/0x15E5964C7751dF8563eA4bC000301582C79BC454)
@@ -39,6 +40,7 @@ Refresh the list of deployed contracts by running `./scripts/generateDeployments
3940
- [DisputeTemplateRegistry: proxy](https://sepolia.arbiscan.io/address/0xe763d31Cb096B4bc7294012B78FC7F148324ebcb), [implementation](https://sepolia.arbiscan.io/address/0x7283c07CC5224B20f431B1fa0E6d6db3cA02de34)
4041
- [EvidenceModule: proxy](https://sepolia.arbiscan.io/address/0xA88A9a25cE7f1d8b3941dA3b322Ba91D009E1397), [implementation](https://sepolia.arbiscan.io/address/0x63CF56e1c99E65E4a9eCDCC805F4735E016F2dc8)
4142
- [KlerosCore: proxy](https://sepolia.arbiscan.io/address/0xE8442307d36e9bf6aB27F1A009F95CE8E11C3479), [implementation](https://sepolia.arbiscan.io/address/0x0766e4B8c4a3aAC9371a5A9D6119E8125Adcfd55)
43+
- [KlerosCoreSnapshotProxy](https://sepolia.arbiscan.io/address/0xd74e61A4dB9C6c3F2C97b62a319aE194f616858C)
4244
- [PNKFaucet](https://sepolia.arbiscan.io/address/0x9f6ffc13B685A68ae359fCA128dfE776458Df464)
4345
- [PinakionV2](https://sepolia.arbiscan.io/address/0x34B944D42cAcfC8266955D07A80181D2054aa225)
4446
- [PolicyRegistry: proxy](https://sepolia.arbiscan.io/address/0x2668c46A14af8997417138B064ca1bEB70769585), [implementation](https://sepolia.arbiscan.io/address/0xB958113f96950C7806d584eFBed964288d46a0B8)
@@ -87,6 +89,7 @@ Refresh the list of deployed contracts by running `./scripts/generateDeployments
8789
- [KlerosCore: proxy](https://sepolia.arbiscan.io/address/0xA54e7A16d7460e38a8F324eF46782FB520d58CE8), [implementation](https://sepolia.arbiscan.io/address/0x91a373BBdE0532F86410682F362e2Cf685e95085)
8890
- [KlerosCoreNeo: proxy](https://sepolia.arbiscan.io/address/0x26bf077037550e437605F07e25EfcAd510715C3A), [implementation](https://sepolia.arbiscan.io/address/0x3bE96b7eAF6A3640DBa1f7CE58776D5b790B74CB)
8991
- [KlerosCoreRuler: proxy](https://sepolia.arbiscan.io/address/0x7ffcd32A0521645E6fCFd071A68F0e26957775a5), [implementation](https://sepolia.arbiscan.io/address/0x97e30A3A940856A3913437912C746f1aF6ccC76c)
92+
- [KlerosCoreSnapshotProxy](https://sepolia.arbiscan.io/address/0x9300D415af6e747ADe3C6cbA09a3b3CD5fb0c091)
9093
- [KlerosCoreUniversity: proxy](https://sepolia.arbiscan.io/address/0x5AB37F38778Bc175852fA353056591D91c744ce6), [implementation](https://sepolia.arbiscan.io/address/0xF74DaBfC5F5dbdBD07636637204d9C35326D2906)
9194
- [KlerosV2NeoEarlyUser](https://sepolia.arbiscan.io/address/0x0d60Ff8bbCF49Bc5352328E7E28e141834d7750F)
9295
- [PNKFaucet](https://sepolia.arbiscan.io/address/0x7EFE468003Ad6A858b5350CDE0A67bBED58739dD)

contracts/deploy/00-home-chain-arbitration-neo.ts

+6
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
122122

123123
console.log(`core.changeArbitrableWhitelist(${resolver.address}, true)`);
124124
await core.changeArbitrableWhitelist(resolver.address, true);
125+
126+
await deploy("KlerosCoreSnapshotProxy", {
127+
from: deployer,
128+
args: [deployer, core.target],
129+
log: true,
130+
});
125131
};
126132

127133
deployArbitration.tags = ["ArbitrationNeo"];

contracts/deploy/00-home-chain-arbitration.ts

+7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { ChainlinkRNG, DisputeKitClassic, KlerosCore } from "../typechain-types"
1010

1111
const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
1212
const { ethers, deployments, getNamedAccounts, getChainId } = hre;
13+
const { deploy } = deployments;
1314
const { ZeroAddress } = hre.ethers;
1415
const RNG_LOOKAHEAD = 20;
1516

@@ -94,6 +95,12 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
9495
} catch (e) {
9596
console.error("failed to change currency rates:", e);
9697
}
98+
99+
await deploy("KlerosCoreSnapshotProxy", {
100+
from: deployer,
101+
args: [deployer, core.target],
102+
log: true,
103+
});
97104
};
98105

99106
deployArbitration.tags = ["Arbitration"];

contracts/deployments/arbitrum/KlerosCoreSnapshotProxy.json

+228
Large diffs are not rendered by default.

contracts/deployments/arbitrumSepolia/KlerosCoreSnapshotProxy.json

+228
Large diffs are not rendered by default.

contracts/deployments/arbitrumSepoliaDevnet/KlerosCoreSnapshotProxy.json

+228
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
/// @custom:authors: [@unknownunknown1]
4+
/// @custom:reviewers: []
5+
/// @custom:auditors: []
6+
/// @custom:bounties: []
7+
/// @custom:deployments: []
8+
9+
pragma solidity 0.8.24;
10+
11+
import {ISortitionModule} from "../interfaces/ISortitionModule.sol";
12+
13+
interface IKlerosCore {
14+
function sortitionModule() external view returns (ISortitionModule);
15+
}
16+
17+
/// @title KlerosCoreSnapshotProxy
18+
/// Proxy contract for V2 that exposes staked PNK with balanceOf() function for Snapshot voting.
19+
contract KlerosCoreSnapshotProxy {
20+
// ************************************* //
21+
// * State Modifiers * //
22+
// ************************************* //
23+
24+
IKlerosCore public core;
25+
address public governor;
26+
string public constant name = "Staked Pinakion";
27+
string public constant symbol = "stPNK";
28+
uint8 public constant decimals = 18;
29+
30+
// ************************************* //
31+
// * Modifiers * //
32+
// ************************************* //
33+
34+
modifier onlyByGovernor() {
35+
require(governor == msg.sender, "Access not allowed: Governor only.");
36+
_;
37+
}
38+
39+
// ************************************* //
40+
// * Constructor * //
41+
// ************************************* //
42+
43+
/// @dev Constructor
44+
/// @param _governor The governor of the contract.
45+
/// @param _core KlerosCore to read the balance from.
46+
constructor(address _governor, IKlerosCore _core) {
47+
governor = _governor;
48+
core = _core;
49+
}
50+
51+
// ************************************* //
52+
// * Governance * //
53+
// ************************************* //
54+
55+
/// @dev Changes the `governor` storage variable.
56+
/// @param _governor The new value for the `governor` storage variable.
57+
function changeGovernor(address _governor) external onlyByGovernor {
58+
governor = _governor;
59+
}
60+
61+
/// @dev Changes the `core` storage variable.
62+
/// @param _core The new value for the `core` storage variable.
63+
function changeCore(IKlerosCore _core) external onlyByGovernor {
64+
core = _core;
65+
}
66+
67+
// ************************************* //
68+
// * Public Views * //
69+
// ************************************* //
70+
71+
/// @dev Returns the amount of PNK staked in KlerosV2 for a particular address.
72+
/// Note: Proxy doesn't need to differentiate between courts so we pass 0 as courtID.
73+
/// @param _account The address to query.
74+
/// @return totalStaked Total amount staked in V2 by the address.
75+
function balanceOf(address _account) external view returns (uint256 totalStaked) {
76+
(totalStaked, , , ) = core.sortitionModule().getJurorBalance(_account, 0);
77+
}
78+
}

contracts/test/foundry/KlerosCore.t.sol

+40-11
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ pragma solidity 0.8.24;
33

44
import {Test} from "forge-std/Test.sol";
55
import {console} from "forge-std/console.sol"; // Import the console for logging
6-
import {KlerosCoreMock, KlerosCoreBase, IArbitratorV2} from "../../src/test/KlerosCoreMock.sol";
6+
import {KlerosCoreMock, KlerosCoreBase} from "../../src/test/KlerosCoreMock.sol";
7+
import {IArbitratorV2} from "../../src/arbitration/KlerosCoreBase.sol";
78
import {IDisputeKit} from "../../src/arbitration/interfaces/IDisputeKit.sol";
8-
import {DisputeKitClassic} from "../../src/arbitration/dispute-kits/DisputeKitClassic.sol";
9+
import {DisputeKitClassic, DisputeKitClassicBase} from "../../src/arbitration/dispute-kits/DisputeKitClassic.sol";
910
import {DisputeKitSybilResistant} from "../../src/arbitration/dispute-kits/DisputeKitSybilResistant.sol";
1011
import {ISortitionModule} from "../../src/arbitration/interfaces/ISortitionModule.sol";
1112
import {SortitionModuleMock, SortitionModuleBase} from "../../src/test/SortitionModuleMock.sol";
@@ -16,6 +17,7 @@ import {TestERC20} from "../../src/token/TestERC20.sol";
1617
import {ArbitrableExample, IArbitrableV2} from "../../src/arbitration/arbitrables/ArbitrableExample.sol";
1718
import {DisputeTemplateRegistry} from "../../src/arbitration/DisputeTemplateRegistry.sol";
1819
import "../../src/libraries/Constants.sol";
20+
import {IKlerosCore, KlerosCoreSnapshotProxy} from "../../src/snapshot-proxy/KlerosCoreSnapshotProxy.sol";
1921

2022
contract KlerosCoreTest is Test {
2123
event Initialized(uint64 version);
@@ -1278,6 +1280,33 @@ contract KlerosCoreTest is Test {
12781280
core.setStakeBySortitionModule(staker1, GENERAL_COURT, 1000, false);
12791281
}
12801282

1283+
function test_setStake_snapshotProxyCheck() public {
1284+
vm.prank(staker1);
1285+
core.setStake(GENERAL_COURT, 12346);
1286+
1287+
KlerosCoreSnapshotProxy snapshotProxy = new KlerosCoreSnapshotProxy(governor, IKlerosCore(address(core)));
1288+
assertEq(snapshotProxy.name(), "Staked Pinakion", "Wrong name of the proxy token");
1289+
assertEq(snapshotProxy.symbol(), "stPNK", "Wrong symbol of the proxy token");
1290+
assertEq(snapshotProxy.decimals(), 18, "Wrong decimals of the proxy token");
1291+
assertEq(snapshotProxy.governor(), msg.sender, "Wrong governor");
1292+
assertEq(address(snapshotProxy.core()), address(core), "Wrong core in snapshot proxy");
1293+
assertEq(snapshotProxy.balanceOf(staker1), 12346, "Wrong stPNK balance");
1294+
1295+
vm.prank(other);
1296+
vm.expectRevert(bytes("Access not allowed: Governor only."));
1297+
snapshotProxy.changeCore(IKlerosCore(other));
1298+
vm.prank(governor);
1299+
snapshotProxy.changeCore(IKlerosCore(other));
1300+
assertEq(address(snapshotProxy.core()), other, "Wrong core in snapshot proxy after change");
1301+
1302+
vm.prank(other);
1303+
vm.expectRevert(bytes("Access not allowed: Governor only."));
1304+
snapshotProxy.changeGovernor(other);
1305+
vm.prank(governor);
1306+
snapshotProxy.changeGovernor(other);
1307+
assertEq(snapshotProxy.governor(), other, "Wrong governor after change");
1308+
}
1309+
12811310
// *************************************** //
12821311
// * Disputes * //
12831312
// *************************************** //
@@ -1326,7 +1355,7 @@ contract KlerosCoreTest is Test {
13261355
uint256 nbChoices = 2;
13271356
vm.prank(disputer);
13281357
vm.expectEmit(true, true, true, true);
1329-
emit DisputeKitClassic.DisputeCreation(disputeID, nbChoices, newExtraData);
1358+
emit DisputeKitClassicBase.DisputeCreation(disputeID, nbChoices, newExtraData);
13301359
vm.expectEmit(true, true, true, true);
13311360
emit IArbitratorV2.DisputeCreation(disputeID, arbitrable);
13321361
arbitrable.createDispute{value: 0.04 ether}("Action");
@@ -1592,7 +1621,7 @@ contract KlerosCoreTest is Test {
15921621

15931622
vm.prank(staker1);
15941623
vm.expectEmit(true, true, true, true);
1595-
emit DisputeKitClassic.CommitCast(disputeID, staker1, voteIDs, commit);
1624+
emit DisputeKitClassicBase.CommitCast(disputeID, staker1, voteIDs, commit);
15961625
disputeKit.castCommit(disputeID, voteIDs, commit);
15971626

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

16091638
vm.prank(staker1);
16101639
vm.expectEmit(true, true, true, true);
1611-
emit DisputeKitClassic.CommitCast(disputeID, staker1, voteIDs, commit);
1640+
emit DisputeKitClassicBase.CommitCast(disputeID, staker1, voteIDs, commit);
16121641
disputeKit.castCommit(disputeID, voteIDs, commit);
16131642

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

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

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

19231952
vm.prank(crowdfunder1);
19241953
vm.expectEmit(true, true, true, true);
1925-
emit DisputeKitClassic.Contribution(disputeID, 0, 1, crowdfunder1, 0.42 ether);
1954+
emit DisputeKitClassicBase.Contribution(disputeID, 0, 1, crowdfunder1, 0.42 ether);
19261955
vm.expectEmit(true, true, true, true);
1927-
emit DisputeKitClassic.ChoiceFunded(disputeID, 0, 1);
1956+
emit DisputeKitClassicBase.ChoiceFunded(disputeID, 0, 1);
19281957
disputeKit.fundAppeal{value: 5 ether}(disputeID, 1); // Deliberately overpay to check reimburse
19291958

19301959
assertEq(crowdfunder1.balance, 9.37 ether, "Wrong balance of the crowdfunder");
@@ -2117,7 +2146,7 @@ contract KlerosCoreTest is Test {
21172146
vm.expectEmit(true, true, true, true);
21182147
emit KlerosCoreBase.DisputeKitJump(disputeID, 1, newDkID, DISPUTE_KIT_CLASSIC);
21192148
vm.expectEmit(true, true, true, true);
2120-
emit DisputeKitClassic.DisputeCreation(disputeID, 2, newExtraData);
2149+
emit DisputeKitClassicBase.DisputeCreation(disputeID, 2, newExtraData);
21212150
vm.expectEmit(true, true, true, true);
21222151
emit KlerosCoreBase.AppealDecision(disputeID, arbitrable);
21232152
vm.expectEmit(true, true, true, true);
@@ -2692,11 +2721,11 @@ contract KlerosCoreTest is Test {
26922721
assertEq(address(disputeKit).balance, 1.04 ether, "Wrong balance of the DK");
26932722

26942723
vm.expectEmit(true, true, true, true);
2695-
emit DisputeKitClassic.Withdrawal(disputeID, 0, 1, crowdfunder1, 0.63 ether);
2724+
emit DisputeKitClassicBase.Withdrawal(disputeID, 0, 1, crowdfunder1, 0.63 ether);
26962725
disputeKit.withdrawFeesAndRewards(disputeID, payable(crowdfunder1), 0, 1);
26972726

26982727
vm.expectEmit(true, true, true, true);
2699-
emit DisputeKitClassic.Withdrawal(disputeID, 0, 2, crowdfunder2, 0.41 ether);
2728+
emit DisputeKitClassicBase.Withdrawal(disputeID, 0, 2, crowdfunder2, 0.41 ether);
27002729
disputeKit.withdrawFeesAndRewards(disputeID, payable(crowdfunder2), 0, 2);
27012730

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

0 commit comments

Comments
 (0)