-
Notifications
You must be signed in to change notification settings - Fork 49
Release #2012
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
base: master
Are you sure you want to change the base?
Release #2012
Changes from all commits
1ff55f8
036e897
f20c9d4
54d83a7
9e8aa9e
7a5466b
1ca9066
29ef9a1
3925b6f
ad47783
349783f
f370669
3f532d2
20ee602
282b91d
411b384
367549c
30382a0
e5cf1df
e7a0759
dde0dc7
4e95c72
2b51d5e
4adedb5
47aaca4
78508f5
f5f2530
929955c
0b75799
411560e
10294ce
7449259
a35ceb0
b38c702
70b21f9
2542150
a3742fa
2a1edb7
70e4b7c
5ac93ef
85f01b6
e99f3e6
62fd505
4449e04
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#! /usr/bin/env bash | ||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" | ||
|
||
declare -A rpcUrls | ||
rpcUrls["arbitrum"]=$(mesc url arbitrum_alchemy) | ||
rpcUrls["arbitrumSepolia"]=$(mesc url arbitrumSepolia_alchemy) | ||
rpcUrls["arbitrumSepoliaDevnet"]=$(mesc url arbitrumSepolia_alchemy) | ||
jaybuidl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# event Initialized(uint64 version); | ||
eventTopic=0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2 | ||
|
||
for c in arbitrum arbitrumSepolia arbitrumSepoliaDevnet; do | ||
echo "--------------------------------" | ||
echo "$c" | ||
echo "--------------------------------" | ||
for f in "$SCRIPT_DIR"/../deployments/"$c"/*_Proxy.json; do | ||
address=$(jq -r .address "$f") | ||
block=$(jq -r .receipt.blockNumber "$f") | ||
jaybuidl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
basename "$f" | ||
results=$(cast logs --from-block "$block" --to-block latest $eventTopic --address "$address" --rpc-url "${rpcUrls[$c]}" --json | jq -r .[].data) | ||
initializer=$(cast --to-dec "$(echo "$results" | tail -n1)") | ||
version=$(cast call --rpc-url "${rpcUrls[$c]}" "$address" "version()(string)" --json 2>/dev/null | jq -r '.[0]') | ||
echo "$initializer" @v"$version" | ||
echo | ||
done | ||
done |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -593,7 +593,8 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable | |||||||||||||||||
/// @dev Draws jurors for the dispute. Can be called in parts. | ||||||||||||||||||
/// @param _disputeID The ID of the dispute. | ||||||||||||||||||
/// @param _iterations The number of iterations to run. | ||||||||||||||||||
function draw(uint256 _disputeID, uint256 _iterations) external { | ||||||||||||||||||
/// @return nbDrawnJurors The total number of jurors drawn in the round. | ||||||||||||||||||
function draw(uint256 _disputeID, uint256 _iterations) external returns (uint256 nbDrawnJurors) { | ||||||||||||||||||
Dispute storage dispute = disputes[_disputeID]; | ||||||||||||||||||
uint256 currentRound = dispute.rounds.length - 1; | ||||||||||||||||||
Round storage round = dispute.rounds[currentRound]; | ||||||||||||||||||
|
@@ -616,6 +617,7 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable | |||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
round.drawIterations += i; | ||||||||||||||||||
return round.drawnJurors.length; | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @dev Appeals the ruling of a specified dispute. | ||||||||||||||||||
|
@@ -981,18 +983,34 @@ abstract contract KlerosCoreBase is IArbitratorV2, Initializable, UUPSProxiable | |||||||||||||||||
(ruling, tied, overridden) = disputeKit.currentRuling(_disputeID); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/// @dev Gets the round info for a specified dispute and round. | ||||||||||||||||||
/// @dev This function must not be called from a non-view function because it returns a dynamic array which might be very large, theoretically exceeding the block gas limit. | ||||||||||||||||||
/// @param _disputeID The ID of the dispute. | ||||||||||||||||||
/// @param _round The round to get the info for. | ||||||||||||||||||
/// @return round The round info. | ||||||||||||||||||
function getRoundInfo(uint256 _disputeID, uint256 _round) external view returns (Round memory) { | ||||||||||||||||||
return disputes[_disputeID].rounds[_round]; | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+986
to
993
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add bounds checking for array access. The function directly accesses Add input validation to provide better error handling: function getRoundInfo(uint256 _disputeID, uint256 _round) external view returns (Round memory) {
+ if (_disputeID >= disputes.length) revert InvalidDisputeID();
+ if (_round >= disputes[_disputeID].rounds.length) revert InvalidRoundNumber();
return disputes[_disputeID].rounds[_round];
} You'll need to add the corresponding custom error declarations: error InvalidDisputeID();
error InvalidRoundNumber(); 🤖 Prompt for AI Agents
|
||||||||||||||||||
|
||||||||||||||||||
/// @dev Gets the PNK at stake per juror for a specified dispute and round. | ||||||||||||||||||
/// @param _disputeID The ID of the dispute. | ||||||||||||||||||
/// @param _round The round to get the info for. | ||||||||||||||||||
/// @return pnkAtStakePerJuror The PNK at stake per juror. | ||||||||||||||||||
function getPnkAtStakePerJuror(uint256 _disputeID, uint256 _round) external view returns (uint256) { | ||||||||||||||||||
return disputes[_disputeID].rounds[_round].pnkAtStakePerJuror; | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
999
to
1001
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add bounds checking for array access. Similar to Apply this fix: function getPnkAtStakePerJuror(uint256 _disputeID, uint256 _round) external view returns (uint256) {
+ if (_disputeID >= disputes.length) revert InvalidDisputeID();
+ if (_round >= disputes[_disputeID].rounds.length) revert InvalidRoundNumber();
return disputes[_disputeID].rounds[_round].pnkAtStakePerJuror;
} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||
|
||||||||||||||||||
/// @dev Gets the number of rounds for a specified dispute. | ||||||||||||||||||
/// @param _disputeID The ID of the dispute. | ||||||||||||||||||
/// @return The number of rounds. | ||||||||||||||||||
function getNumberOfRounds(uint256 _disputeID) external view returns (uint256) { | ||||||||||||||||||
return disputes[_disputeID].rounds.length; | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
1006
to
1008
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add bounds checking for dispute ID. The function should validate the Apply this fix: function getNumberOfRounds(uint256 _disputeID) external view returns (uint256) {
+ if (_disputeID >= disputes.length) revert InvalidDisputeID();
return disputes[_disputeID].rounds.length;
} 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||
|
||||||||||||||||||
/// @dev Checks if a given dispute kit is supported by a given court. | ||||||||||||||||||
/// @param _courtID The ID of the court to check the support for. | ||||||||||||||||||
/// @param _disputeKitID The ID of the dispute kit to check the support for. | ||||||||||||||||||
/// @return Whether the dispute kit is supported or not. | ||||||||||||||||||
function isSupported(uint96 _courtID, uint256 _disputeKitID) external view returns (bool) { | ||||||||||||||||||
return courts[_courtID].supportedDisputeKits[_disputeKitID]; | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
1014
to
1016
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add bounds checking for court ID. The function should validate the Apply this fix: function isSupported(uint96 _courtID, uint256 _disputeKitID) external view returns (bool) {
+ if (_courtID >= courts.length) revert InvalidCourtID();
return courts[_courtID].supportedDisputeKits[_disputeKitID];
} Add the corresponding custom error declaration: error InvalidCourtID(); 🤖 Prompt for AI Agents
|
||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,7 +62,8 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi | |
mapping(uint256 => uint256) public coreDisputeIDToLocal; // Maps the dispute ID in Kleros Core to the local dispute ID. | ||
bool public singleDrawPerJuror; // Whether each juror can only draw once per dispute, false by default. | ||
mapping(uint256 localDisputeID => mapping(uint256 localRoundID => mapping(address drawnAddress => bool))) | ||
public alreadyDrawn; // 'true' if the address has already been drawn, false by default. To be added to the Round struct when fully redeploying rather than upgrading. | ||
public alreadyDrawn; // True if the address has already been drawn, false by default. To be added to the Round struct when fully redeploying rather than upgrading. | ||
mapping(uint256 coreDisputeID => bool) public coreDisputeIDToActive; // True if this dispute kit is active for this core dispute ID. | ||
|
||
// ************************************* // | ||
// * Events * // | ||
|
@@ -195,6 +196,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi | |
Dispute storage dispute = disputes.push(); | ||
dispute.numberOfChoices = _numberOfChoices; | ||
dispute.extraData = _extraData; | ||
dispute.jumped = false; // Possibly true if this DK has jumped in a previous round. | ||
|
||
// New round in the Core should be created before the dispute creation in DK. | ||
dispute.coreRoundIDToLocal[core.getNumberOfRounds(_coreDisputeID) - 1] = dispute.rounds.length; | ||
|
@@ -204,6 +206,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi | |
round.tied = true; | ||
|
||
coreDisputeIDToLocal[_coreDisputeID] = localDisputeID; | ||
coreDisputeIDToActive[_coreDisputeID] = true; | ||
emit DisputeCreation(_coreDisputeID, _numberOfChoices, _extraData); | ||
} | ||
|
||
|
@@ -250,6 +253,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi | |
(, , KlerosCore.Period period, , ) = core.disputes(_coreDisputeID); | ||
require(period == KlerosCoreBase.Period.commit, "The dispute should be in Commit period."); | ||
require(_commit != bytes32(0), "Empty commit."); | ||
require(coreDisputeIDToActive[_coreDisputeID], "Not active for core dispute ID"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainVerify mechanism for deactivating disputes. The active state check correctly prevents operations on inactive disputes. However, ensure there's a mechanism to set 🏁 Script executed: #!/bin/bash
# Search for code that sets coreDisputeIDToActive to false
rg -A 5 -B 5 "coreDisputeIDToActive.*false|coreDisputeIDToActive.*= false" Length of output: 1198 🏁 Script executed: #!/bin/bash
# Find all occurrences of coreDisputeIDToActive in Solidity code within contracts
rg -n "coreDisputeIDToActive" -C 5 --include '*.sol' contracts/src Length of output: 508 🏁 Script executed: #!/bin/bash
# Properly search Solidity files for coreDisputeIDToActive occurrences
rg -n "coreDisputeIDToActive" -C 5 -g '*.sol' contracts/src || \
fd --extension sol -x rg -n "coreDisputeIDToActive" -C 5 {} Length of output: 8609 🏁 Script executed: #!/bin/bash
# Search for mapping deletion of coreDisputeIDToActive
rg -n "delete coreDisputeIDToActive" -g '*.sol' contracts/src
# Inspect jump / changeKit logic in DisputeKitClassicBase
rg -n "function jump" -C 5 contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol
rg -n "modifier notJumped" -C 5 contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol Length of output: 654 🏁 Script executed: #!/bin/bash
# Inspect functions restricted to KlerosCore for deactivation logic
rg -n "onlyByCore" -C 10 contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol
# Search across contracts for any assignment that deactivates a dispute
rg -n "coreDisputeIDToActive.*= false" -C 5 contracts/src Length of output: 3136 🏁 Script executed: #!/bin/bash
# List all Solidity files in the dispute-kits directory
find contracts/src/arbitration/dispute-kits -maxdepth 1 -type f -name '*.sol'
# Search for any changeKit or deactivate functions in dispute-kits
rg -n "changeKit|deactivate|setActive" -C 5 contracts/src/arbitration/dispute-kits Length of output: 417 🏁 Script executed: #!/bin/bash
# Search for dispute kit change or deactivation logic in entire contracts
rg -n "changeDisputeKit|setDisputeKit|replaceDisputeKit" -C 5 contracts/src Length of output: 77 Add a deactivation API for disputes in DisputeKitClassicBase.sol We only ever set
This will ensure 🤖 Prompt for AI Agents
|
||
|
||
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; | ||
Round storage round = dispute.rounds[dispute.rounds.length - 1]; | ||
|
@@ -279,6 +283,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi | |
(, , KlerosCore.Period period, , ) = core.disputes(_coreDisputeID); | ||
require(period == KlerosCoreBase.Period.vote, "The dispute should be in Vote period."); | ||
require(_voteIDs.length > 0, "No voteID provided"); | ||
require(coreDisputeIDToActive[_coreDisputeID], "Not active for core dispute ID"); | ||
|
||
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; | ||
require(_choice <= dispute.numberOfChoices, "Choice out of bounds"); | ||
|
@@ -325,6 +330,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi | |
function fundAppeal(uint256 _coreDisputeID, uint256 _choice) external payable notJumped(_coreDisputeID) { | ||
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; | ||
require(_choice <= dispute.numberOfChoices, "There is no such ruling to fund."); | ||
require(coreDisputeIDToActive[_coreDisputeID], "Not active for core dispute ID"); | ||
|
||
(uint256 appealPeriodStart, uint256 appealPeriodEnd) = core.appealPeriod(_coreDisputeID); | ||
require(block.timestamp >= appealPeriodStart && block.timestamp < appealPeriodEnd, "Appeal period is over."); | ||
|
@@ -404,6 +410,7 @@ abstract contract DisputeKitClassicBase is IDisputeKit, Initializable, UUPSProxi | |
(, , , bool isRuled, ) = core.disputes(_coreDisputeID); | ||
require(isRuled, "Dispute should be resolved."); | ||
require(!core.paused(), "Core is paused"); | ||
require(coreDisputeIDToActive[_coreDisputeID], "Not active for core dispute ID"); | ||
|
||
Dispute storage dispute = disputes[coreDisputeIDToLocal[_coreDisputeID]]; | ||
Round storage round = dispute.rounds[dispute.coreRoundIDToLocal[_coreRoundID]]; | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1393,6 +1393,7 @@ contract KlerosCoreTest is Test { | |||||
assertEq(jumped, false, "jumped should be false"); | ||||||
assertEq(extraData, newExtraData, "Wrong extra data"); | ||||||
assertEq(disputeKit.coreDisputeIDToLocal(0), disputeID, "Wrong local disputeID"); | ||||||
assertEq(disputeKit.coreDisputeIDToActive(0), true, "Wrong disputes length"); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix misleading assertion message. The assertion message "Wrong disputes length" doesn't match what's being tested. The test is checking - assertEq(disputeKit.coreDisputeIDToActive(0), true, "Wrong disputes length");
+ assertEq(disputeKit.coreDisputeIDToActive(0), true, "Dispute should be active"); 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||
|
||||||
( | ||||||
uint256 winningChoice, | ||||||
|
@@ -1783,7 +1784,7 @@ contract KlerosCoreTest is Test { | |||||
(address account, bytes32 commit, uint256 choice, bool voted) = disputeKit.getVoteInfo(0, 0, 0); // Dispute - Round - VoteID | ||||||
assertEq(account, staker1, "Wrong drawn account"); | ||||||
assertEq(commit, bytes32(0), "Commit should be empty"); | ||||||
assertEq(choice, 2, "Choice should be empty"); | ||||||
assertEq(choice, 2, "Choice should be 2"); | ||||||
assertEq(voted, true, "Voted should be true"); | ||||||
|
||||||
assertEq(disputeKit.isVoteActive(0, 0, 0), true, "Vote should be active"); // Dispute - Round - VoteID | ||||||
|
@@ -2827,4 +2828,98 @@ contract KlerosCoreTest is Test { | |||||
assertEq(crowdfunder2.balance, 10 ether, "Wrong balance of the crowdfunder2"); | ||||||
assertEq(address(disputeKit).balance, 0, "Wrong balance of the DK"); | ||||||
} | ||||||
|
||||||
function test_castVote_differentDK() public { | ||||||
DisputeKitClassic dkLogic = new DisputeKitClassic(); | ||||||
// Create a new DK to check castVote. | ||||||
bytes memory initDataDk = abi.encodeWithSignature("initialize(address,address)", governor, address(core)); | ||||||
|
||||||
UUPSProxy proxyDk = new UUPSProxy(address(dkLogic), initDataDk); | ||||||
DisputeKitClassic newDisputeKit = DisputeKitClassic(address(proxyDk)); | ||||||
|
||||||
vm.prank(governor); | ||||||
core.addNewDisputeKit(newDisputeKit); | ||||||
|
||||||
uint256 newDkID = 2; | ||||||
uint256[] memory supportedDK = new uint256[](1); | ||||||
bytes memory newExtraData = abi.encodePacked(uint256(GENERAL_COURT), DEFAULT_NB_OF_JURORS, newDkID); | ||||||
|
||||||
vm.prank(governor); | ||||||
vm.expectEmit(true, true, true, true); | ||||||
emit KlerosCoreBase.DisputeKitEnabled(GENERAL_COURT, newDkID, true); | ||||||
supportedDK[0] = newDkID; | ||||||
core.enableDisputeKits(GENERAL_COURT, supportedDK, true); | ||||||
assertEq(core.isSupported(GENERAL_COURT, newDkID), true, "New DK should be supported by General court"); | ||||||
|
||||||
vm.prank(staker1); | ||||||
core.setStake(GENERAL_COURT, 20000); | ||||||
|
||||||
// Create one dispute for the old DK and two disputes for the new DK. | ||||||
vm.prank(disputer); | ||||||
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action"); | ||||||
|
||||||
arbitrable.changeArbitratorExtraData(newExtraData); | ||||||
|
||||||
vm.prank(disputer); | ||||||
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action"); | ||||||
|
||||||
vm.prank(disputer); | ||||||
arbitrable.createDispute{value: feeForJuror * DEFAULT_NB_OF_JURORS}("Action"); | ||||||
|
||||||
uint256 disputeID = 2; // Use the latest dispute for reference. This is the ID in the core contract | ||||||
|
||||||
vm.warp(block.timestamp + minStakingTime); | ||||||
sortitionModule.passPhase(); // Generating | ||||||
vm.roll(block.number + rngLookahead + 1); | ||||||
sortitionModule.passPhase(); // Drawing phase | ||||||
|
||||||
KlerosCoreBase.Round memory round = core.getRoundInfo(disputeID, 0); | ||||||
assertEq(round.disputeKitID, newDkID, "Wrong DK ID"); | ||||||
|
||||||
core.draw(disputeID, DEFAULT_NB_OF_JURORS); | ||||||
// Draw jurors for the old DK as well to prepare round.votes array | ||||||
core.draw(0, DEFAULT_NB_OF_JURORS); | ||||||
|
||||||
vm.warp(block.timestamp + timesPerPeriod[0]); | ||||||
core.passPeriod(disputeID); // Vote | ||||||
|
||||||
// Check that the new DK has the info but not the old one. | ||||||
|
||||||
assertEq(disputeKit.coreDisputeIDToActive(disputeID), false, "Should be false for old DK"); | ||||||
|
||||||
// This is the DK where dispute was created. Core dispute points to index 1 because new DK has two disputes. | ||||||
assertEq(newDisputeKit.coreDisputeIDToLocal(disputeID), 1, "Wrong local dispute ID for new DK"); | ||||||
assertEq(newDisputeKit.coreDisputeIDToActive(disputeID), true, "Should be active for new DK"); | ||||||
(uint256 numberOfChoices, , bytes memory extraData) = newDisputeKit.disputes(1); | ||||||
assertEq(numberOfChoices, 2, "Wrong numberOfChoices in new DK"); | ||||||
assertEq(extraData, newExtraData, "Wrong extra data"); | ||||||
|
||||||
uint256[] memory voteIDs = new uint256[](3); | ||||||
voteIDs[0] = 0; | ||||||
voteIDs[1] = 1; | ||||||
voteIDs[2] = 2; | ||||||
|
||||||
// Deliberately cast votes using the old DK to see if the exception will be caught. | ||||||
vm.prank(staker1); | ||||||
vm.expectRevert(bytes("Not active for core dispute ID")); | ||||||
disputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ"); | ||||||
|
||||||
// And check the new DK. | ||||||
vm.prank(staker1); | ||||||
newDisputeKit.castVote(disputeID, voteIDs, 2, 0, "XYZ"); | ||||||
|
||||||
( | ||||||
uint256 winningChoice, | ||||||
bool tied, | ||||||
uint256 totalVoted, | ||||||
uint256 totalCommited, | ||||||
, | ||||||
uint256 choiceCount | ||||||
) = newDisputeKit.getRoundInfo(disputeID, 0, 2); | ||||||
assertEq(winningChoice, 2, "Wrong winning choice"); | ||||||
assertEq(tied, false, "tied should be false"); | ||||||
assertEq(totalVoted, 3, "totalVoted should be 3"); | ||||||
assertEq(totalCommited, 0, "totalCommited should be 0"); | ||||||
assertEq(choiceCount, 3, "choiceCount should be 3"); | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
[ | ||
{ | ||
"type": "json", | ||
"source": { | ||
"value": "**Kleros Moderate:** Did the user, **degenape6** (ID: 1554345080), break the Telegram group, ***[Kleros Trading Group]()*** (ID: -1001151472172), ***[rules](https://cdn.kleros.link/ipfs/Qme3Qbj9rKUNHUe9vj9rqCLnTVUCWKy2YfveQF8HiuWQSu/Kleros%20Moderate%20Community%20Rules.pdf)*** due to conduct related to the ***[message](https://t.me/c/1151472172/116662)*** (***[backup](https://cdn.kleros.link/ipfs/QmVbFrZR1bcyQzZjvLyXwL9ekDxrqHERykdreRxXrw4nqg/animations_file_23.mp4)***)?", | ||
"value": { | ||
"question": "**Kleros Moderate:** Did the user, **degenape6** (ID: 1554345080), break the Telegram group, ***[Kleros Trading Group]()*** (ID: -1001151472172), ***[rules](https://cdn.kleros.link/ipfs/Qme3Qbj9rKUNHUe9vj9rqCLnTVUCWKy2YfveQF8HiuWQSu/Kleros%20Moderate%20Community%20Rules.pdf)*** due to conduct related to the ***[message](https://t.me/c/1151472172/116662)*** (***[backup](https://cdn.kleros.link/ipfs/QmVbFrZR1bcyQzZjvLyXwL9ekDxrqHERykdreRxXrw4nqg/animations_file_23.mp4)***)?", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Empty link target – renders as broken Markdown. The Markdown reference -"***[Kleros Trading Group]()***"
+“***Kleros Trading Group***” # or provide a valid URL
🤖 Prompt for AI Agents
|
||
"type": "single-select", | ||
"answers": [ | ||
{ | ||
|
Uh oh!
There was an error while loading. Please reload this page.