Skip to content

Commit 3bca47c

Browse files
committed
test: dispute kit gated
1 parent 9d19c70 commit 3bca47c

File tree

4 files changed

+436
-9
lines changed

4 files changed

+436
-9
lines changed

contracts/deploy/utils/deployTokens.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,17 @@ export const deployERC721 = async (
6565
log: true,
6666
});
6767
};
68+
69+
export const deployERC1155 = async (
70+
hre: HardhatRuntimeEnvironment,
71+
deployer: string,
72+
name: string,
73+
ticker: string
74+
): Promise<Contract> => {
75+
return getContractOrDeploy(hre, ticker, {
76+
from: deployer,
77+
contract: "TestERC1155",
78+
args: [],
79+
log: true,
80+
});
81+
};
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity 0.8.24;
4+
5+
import {DisputeKitClassicBase, KlerosCore} from "./DisputeKitClassicBase.sol";
6+
7+
interface IBalanceHolder {
8+
/// @dev Returns the number of tokens in `owner` account.
9+
/// @dev Compatible with ERC-20 and ERC-721.
10+
/// @param owner The address of the owner.
11+
/// @return balance The number of tokens in `owner` account.
12+
function balanceOf(address owner) external view returns (uint256 balance);
13+
}
14+
15+
interface IBalanceHolderERC1155 {
16+
/// @dev Returns the balance of an ERC-1155 token.
17+
/// @param account The address of the token holder
18+
/// @param id ID of the token
19+
/// @return The token balance
20+
function balanceOf(address account, uint256 id) external view returns (uint256);
21+
}
22+
23+
/// @title DisputeKitGatedShutter
24+
/// Added functionality: shielded voting.
25+
/// Dispute kit implementation adapted from DisputeKitClassic
26+
/// - a drawing system: proportional to staked PNK with a non-zero balance of `tokenGate` where `tokenGate` is an ERC20, ERC721 or ERC1155
27+
/// - a vote aggregation system: plurality,
28+
/// - an incentive system: equal split between coherent votes,
29+
/// - an appeal system: fund 2 choices only, vote on any choice.
30+
contract DisputeKitGatedShutter is DisputeKitClassicBase {
31+
string public constant override version = "0.10.0";
32+
33+
// ************************************* //
34+
// * Events * //
35+
// ************************************* //
36+
37+
/// @dev Emitted when a vote is cast.
38+
/// @param _coreDisputeID The identifier of the dispute in the Arbitrator contract.
39+
/// @param _juror The address of the juror casting the vote commitment.
40+
/// @param _commit The commitment hash.
41+
/// @param _identity The Shutter identity used for encryption.
42+
/// @param _encryptedVote The Shutter encrypted vote.
43+
event CommitCastShutter(
44+
uint256 indexed _coreDisputeID,
45+
address indexed _juror,
46+
bytes32 indexed _commit,
47+
bytes32 _identity,
48+
bytes _encryptedVote
49+
);
50+
51+
// ************************************* //
52+
// * Constructor * //
53+
// ************************************* //
54+
55+
/// @custom:oz-upgrades-unsafe-allow constructor
56+
constructor() {
57+
_disableInitializers();
58+
}
59+
60+
/// @dev Initializer.
61+
/// @param _governor The governor's address.
62+
/// @param _core The KlerosCore arbitrator.
63+
function initialize(address _governor, KlerosCore _core) external reinitializer(1) {
64+
__DisputeKitClassicBase_initialize(_governor, _core);
65+
}
66+
67+
// ************************ //
68+
// * Governance * //
69+
// ************************ //
70+
71+
/// @dev Access Control to perform implementation upgrades (UUPS Proxiable)
72+
/// Only the governor can perform upgrades (`onlyByGovernor`)
73+
function _authorizeUpgrade(address) internal view override onlyByGovernor {
74+
// NOP
75+
}
76+
77+
// ************************************* //
78+
// * State Modifiers * //
79+
// ************************************* //
80+
81+
/// @dev Sets the caller's commit for the specified votes. It can be called multiple times during the
82+
/// commit period, each call overrides the commits of the previous one.
83+
/// `O(n)` where
84+
/// `n` is the number of votes.
85+
/// @param _coreDisputeID The ID of the dispute in Kleros Core.
86+
/// @param _voteIDs The IDs of the votes.
87+
/// @param _commit The commitment hash including the justification.
88+
/// @param _identity The Shutter identity used for encryption.
89+
/// @param _encryptedVote The Shutter encrypted vote.
90+
function castCommitShutter(
91+
uint256 _coreDisputeID,
92+
uint256[] calldata _voteIDs,
93+
bytes32 _commit,
94+
bytes32 _identity,
95+
bytes calldata _encryptedVote
96+
) external notJumped(_coreDisputeID) {
97+
_castCommit(_coreDisputeID, _voteIDs, _commit);
98+
emit CommitCastShutter(_coreDisputeID, msg.sender, _commit, _identity, _encryptedVote);
99+
}
100+
101+
function castVoteShutter(
102+
uint256 _coreDisputeID,
103+
uint256[] calldata _voteIDs,
104+
uint256 _choice,
105+
uint256 _salt,
106+
string memory _justification
107+
) external {
108+
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]];
109+
address juror = dispute.rounds[dispute.rounds.length - 1].votes[_voteIDs[0]].account;
110+
111+
// _castVote() ensures that all the _voteIDs do belong to `juror`
112+
_castVote(_coreDisputeID, _voteIDs, _choice, _salt, _justification, juror);
113+
}
114+
115+
// ************************************* //
116+
// * Public Views * //
117+
// ************************************* //
118+
119+
/**
120+
* @dev Computes the hash of a vote using ABI encoding
121+
* @param _choice The choice being voted for
122+
* @param _justification The justification for the vote
123+
* @param _salt A random salt for commitment
124+
* @return bytes32 The hash of the encoded vote parameters
125+
*/
126+
function hashVote(
127+
uint256 _choice,
128+
uint256 _salt,
129+
string memory _justification
130+
) public pure override returns (bytes32) {
131+
bytes32 justificationHash = keccak256(bytes(_justification));
132+
return keccak256(abi.encode(_choice, _salt, justificationHash));
133+
}
134+
135+
// ************************************* //
136+
// * Internal * //
137+
// ************************************* //
138+
139+
/// @dev Extracts token gating information from the extra data.
140+
/// @param _extraData The extra data bytes array with the following encoding:
141+
/// - bytes 0-31: uint96 courtID, not used here
142+
/// - bytes 32-63: uint256 minJurors, not used here
143+
/// - bytes 64-95: uint256 disputeKitID, not used here
144+
/// - bytes 96-127: uint256 packedTokenGateAndFlag (address tokenGate in bits 0-159, bool isERC1155 in bit 160)
145+
/// - bytes 128-159: uint256 tokenId
146+
/// @return tokenGate The address of the token contract used for gating access.
147+
/// @return isERC1155 True if the token is an ERC-1155, false for ERC-20/ERC-721.
148+
/// @return tokenId The token ID for ERC-1155 tokens (ignored for ERC-20/ERC-721).
149+
function _extraDataToTokenInfo(
150+
bytes memory _extraData
151+
) internal pure returns (address tokenGate, bool isERC1155, uint256 tokenId) {
152+
// Need at least 160 bytes to safely read the parameters
153+
if (_extraData.length < 160) return (address(0), false, 0);
154+
155+
assembly {
156+
// solium-disable-line security/no-inline-assembly
157+
let packedTokenGateIsERC1155 := mload(add(_extraData, 0x80)) // 4th parameter at offset 128
158+
tokenId := mload(add(_extraData, 0xA0)) // 5th parameter at offset 160 (moved up)
159+
160+
// Unpack address from lower 160 bits and bool from bit 160
161+
tokenGate := and(packedTokenGateIsERC1155, 0xffffffffffffffffffffffffffffffffffffffff)
162+
isERC1155 := and(shr(160, packedTokenGateIsERC1155), 1)
163+
}
164+
}
165+
166+
/// @inheritdoc DisputeKitClassicBase
167+
function _postDrawCheck(
168+
Round storage _round,
169+
uint256 _coreDisputeID,
170+
address _juror
171+
) internal view override returns (bool) {
172+
if (!super._postDrawCheck(_round, _coreDisputeID, _juror)) return false;
173+
174+
// Get the local dispute and extract token info from extraData
175+
uint256 localDisputeID = coreDisputeIDToLocal[_coreDisputeID];
176+
Dispute storage dispute = disputes[localDisputeID];
177+
(address tokenGate, bool isERC1155, uint256 tokenId) = _extraDataToTokenInfo(dispute.extraData);
178+
179+
// If no token gate is specified, allow all jurors
180+
if (tokenGate == address(0)) return true;
181+
182+
// Check juror's token balance
183+
if (isERC1155) {
184+
return IBalanceHolderERC1155(tokenGate).balanceOf(_juror, tokenId) > 0;
185+
} else {
186+
return IBalanceHolder(tokenGate).balanceOf(_juror) > 0;
187+
}
188+
}
189+
}

contracts/src/token/TestERC1155.sol

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity 0.8.24;
4+
5+
import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
6+
7+
contract TestERC1155 is ERC1155 {
8+
// ************************************* //
9+
// * Storage * //
10+
// ************************************* //
11+
12+
address public owner;
13+
uint256 private _nextTokenId;
14+
15+
// ************************************* //
16+
// * Constructor * //
17+
// ************************************* //
18+
19+
constructor() ERC1155("") {
20+
owner = msg.sender;
21+
}
22+
23+
// ************************************* //
24+
// * Function Modifiers * //
25+
// ************************************* //
26+
27+
modifier ownerOnly() {
28+
require(msg.sender == owner, "Owner only");
29+
_;
30+
}
31+
32+
// ************************************* //
33+
// * Governance * //
34+
// ************************************* //
35+
36+
function changeOwner(address _newOwner) external ownerOnly {
37+
owner = _newOwner;
38+
}
39+
40+
// ************************************* //
41+
// * State Modifiers * //
42+
// ************************************* //
43+
44+
function mint(address to, uint256 id, uint256 value, bytes memory data) external ownerOnly {
45+
_mint(to, id, value, data);
46+
}
47+
48+
function mintBatch(
49+
address to,
50+
uint256[] memory ids,
51+
uint256[] memory values,
52+
bytes memory data
53+
) external ownerOnly {
54+
_mintBatch(to, ids, values, data);
55+
}
56+
57+
function burn(address from, uint256 id, uint256 value) external ownerOnly {
58+
_burn(from, id, value);
59+
}
60+
61+
function burnBatch(address from, uint256[] memory ids, uint256[] memory values) external ownerOnly {
62+
_burnBatch(from, ids, values);
63+
}
64+
}

0 commit comments

Comments
 (0)