KIP-89: Creation of a Specialized Payments and Fast Escrow Court

Proposal: Create a Payments and Fast Escrow Court

TL;DR

I propose creating a Payments and Fast Escrow Court under the General Court to make Kleros more suitable for everyday commerce disputes like payments and escrows, with faster resolution and stronger juror incentives.

Why this is needed

Kleros is increasingly used for real commerce use cases such as payments, digital services, and escrowed transactions, but these cases need faster finality and lower friction than general dispute categories.

Two issues show up:

  • juror participation can be passive in lower-value cases, which hurts fast resolution needs
  • arbitration costs are often too high for small transactions, limiting adoption in real commerce

Proposal

Create a Payments and Fast Escrow Court under General Court (Court 0), optimized specifically for commerce-related disputes.

The goal is to:

  • keep disputes affordable for users
  • ensure jurors are actively engaged
  • reduce time to finality for payment workflows

This court is not meant to replace general courts, only to specialize in high-frequency commerce disputes.

Parameters

  • Parent Court: General Court (Court 0)
  • Minimum Stake: 100,000 PNK
  • Juror Fee: 0.0015 ETH
  • Vote Penalty: 5,000 PNK
  • Alpha: 0.5

Time periods

  • Evidence: 3 days
  • Voting: 2 days
  • Appeal: 1 day

These are shortened to support faster commerce settlement while still allowing basic participation.

Rationale

Higher stake requirements aim to ensure active juror participation.
Lower fees keep arbitration usable for small payments.
Stronger penalties discourage low-effort voting.
Shorter timelines reduce capital lock-up and improve user experience for commerce use cases.

Use cases

  • payment release disputes
  • escrow completion disagreements
  • milestone-based service disputes
  • simple delivery disputes
  • small to medium commerce transactions

Expected impact

If successful, this court could:

  • make Kleros more usable for consumer-facing applications
  • improve arbitration speed for payments and escrows
  • increase juror attentiveness in commerce cases
  • lower integration friction for developers
  • expand usage into higher-frequency transaction environments

Questions

  • Does a dedicated commerce court improve usability?
  • Are these stake and fee levels appropriate?
  • Is the penalty structure strong enough?
  • Are the shortened timelines practical?
  • Should scope stay strictly payments or include adjacent commerce disputes?
1 Like

A couple of points on the parameters you are proposing:

  • The Vote Penalty is determined by the Minimum Stake and Alpha, specifically Vote Penalty = Minimum Stake*Alpha, so the values you propose for those are not coherent. Minumum Stake = 100,000 PNK and Alpha = .05 would give Vote Penalty = 50,000 PNK.
  • What the appropriate Juror Fee is mostly depends on how hard you think the task is. So if the jurors can just be expected to spend a few minutes on these tasks, this fee could be appropriate. Maybe others have feedback on the expected difficulty of the task. Then, putting the juror fee you propose into the “Parameterize a New Court” section of this calculator we use, gives Vote Penalty = 5,000 PNK as a reasonable value for this level of fees. So, if you keep this Juror Fee, probably the way that you would want to make the values coherent would be to change Alpha to .05 (or correspondingly reduce the Minimum Stake) to keep the same Vote Penalty.
  • You are proposing putting this on Ethereum mainnet. Is the idea that you expect people to use the Kleros Escrow dapp for these cases? Or do you have some other application that you expect to generate these disputes (e.g. a third-party marketplace)? The current Escrow dapp is on mainnet, so putting the court on mainnet makes sense to interact with that, though it could be deployed on Gnosis or Arbitrum if there was an expectation that it would generate disputes on those courts. For the past year or so the gas prices on mainnet have been low enough to support having a court with such low fees with typical vote transaction fees only being a few cents, but it is still worth thinking about whether gas prices could tick up enough to become a factor. If suddenly gas fees were on the order of .001 ETH to vote, would it be viable for the use cases you are thinking about to just vote to raise the fees accordingly? Basically, the choice of which chain to put a new court on is mostly a question of where the applications generating the disputes are, and how much of an issue gas variability is.

I’m releasing an Escrow protocol, Payment Protocol and Dispute Protocol that all depend on Kleros;

I need the payment court for the Escrow and Payment protocol; I will check this calculator, thanks William!

It’s separate from the Kleros Escrow App in that I’ve built a library on top of it for people to build separate businesses on top of Escrow (or Payments or just plug Disputes into legacy systems) if they want, so it supports more funding assumptions than the V2 Escrow app.

The V2 App is still very nice, though by the way

I’ll support Arbitrum when you guys officially release V2, and I will add Gnosis after deploying on Ethereum. It should just be a few lines of code for additional chains.

The Dispute Protocol allows anyone to choose the court and number of jurors so a new court isn’t needed for that.

1 Like

Also @William I just took a look at the V2 repo, I didn’t know you guys we’re thinking of making the Dispute Kit Solidity native, do you mind explaining a little more why the team decided to do that?

I mean I don’t think it’s bad, just want to understand the architecture a bit more, I’m just glancing at the moment.

Either way, I’m releasing three trust minimized primitives built on Kleros, please don’t change the original V1 API too much!

P.S. @jaybuidl

In kleros-v2/contracts/src/arbitration/dispute-kits/DisputeKitClassicBase.sol at 374b4f28884d3e5a269bbdda62ab26d043987894 · kleros/kleros-v2

  1. Your read path for unknown disputes is broken. You use coreDisputeIDToLocal[_coreDisputeID] all over the public/view surface without checking that the dispute actually exists in this dispute kit. Since mappings default to 0, an unknown dispute ID can silently alias to local dispute 0 once at least one dispute exists. That means functions like currentRuling, getFundedChoices, areCommitsAllCast, areVotesAllCast, getCoherentCount, getRoundInfo, getVoteInfo, and getLocalDisputeRoundID can return data for the wrong dispute instead of rejecting the call.

  2. Your read path for unknown rounds is also broken for the same reason.
    dispute.coreRoundIDToLocal[_coreRoundID] also defaults to 0, so an unknown core round can silently resolve to local round 0. That means callers can get fake-but-valid-looking data for the wrong round. getRoundInfo, getVoteInfo, getCoherentCount, and isVoteActive are all exposed to that pattern.

  3. changeOwner and changeCore have no zero-address protection. That is a real footgun, not a style nit. One bad governance call and you can brick ownership or the entire core integration. Critical admin changes emit no events.

  4. Critical admin changes emit no events.
    changeOwner, changeCore, and changeJumpDisputeKitID all mutate highly sensitive state in complete silence. That is bad observability, bad ops hygiene, and bad governance UX

  5. The public API is inconsistent in the worst possible way.
    Invalid IDs do not fail cleanly. Sometimes they revert, sometimes they alias to dispute 0 / round 0, depending on what has already been created. That is a real correctness problem for any integrator, indexer, or frontend trying to consume this contract safely.

A. Personal Grievance, you seem to struggle with joining semantics and logic conceptually.

I won’t mention to the @kleros team that I tried to join your software team 2-3 times and never heard back, don’t worry about it!

@clesaege you can check my analysis above and see that it is true.

Actually here is the test result proof:

function test() public view {
(uint256 knownRuling, bool knownTied, bool knownOverridden) = kit.currentRuling(KNOWN_DISPUTE_ID);
(uint256 unknownRuling, bool unknownTied, bool unknownOverridden) = kit.currentRuling(UNKNOWN_DISPUTE_ID);

    assertEq(knownRuling, WINNING_CHOICE, "sanity: known dispute should use seeded ruling");
    assertEq(unknownRuling, knownRuling, "unknown dispute id should not alias dispute 0");
    assertEq(unknownTied, knownTied, "unknown dispute id should not alias dispute 0 tied flag");
    assertEq(unknownOverridden, knownOverridden, "unknown dispute id should not alias dispute 0 override flag");
}

function test() public view {
(uint256 knownWinningChoice, bool knownTied, uint256 knownTotalVoted, uint256 knownTotalCommitted, uint256 knownNbVoters, uint256 knownChoiceCount) =
kit.getRoundInfo(KNOWN_DISPUTE_ID, 0, WINNING_CHOICE);

    (uint256 unknownWinningChoice, bool unknownTied, uint256 unknownTotalVoted, uint256 unknownTotalCommitted, uint256 unknownNbVoters, uint256 unknownChoiceCount) =
        kit.getRoundInfo(UNKNOWN_DISPUTE_ID, 0, WINNING_CHOICE);

    assertEq(unknownWinningChoice, knownWinningChoice, "unknown dispute id should not alias dispute 0 winning choice");
    assertEq(unknownTied, knownTied, "unknown dispute id should not alias dispute 0 tie flag");
    assertEq(unknownTotalVoted, knownTotalVoted, "unknown dispute id should not alias dispute 0 totalVoted");
    assertEq(unknownTotalCommitted, knownTotalCommitted, "unknown dispute id should not alias dispute 0 totalCommitted");
    assertEq(unknownNbVoters, knownNbVoters, "unknown dispute id should not alias dispute 0 voter count");
    assertEq(unknownChoiceCount, knownChoiceCount, "unknown dispute id should not alias dispute 0 choice count");
}

“If you guys end up actually wanting me to work for you, please let me know. But I won’t do it for free.”

I’m curious how you envisioned the dispute kits if not implemented as solidity contracts?

A dispute kit bundles the implementation of the following dispute mechanisms according to the models published in the Kleros white and yellow papers.: juror drawing, vote aggregation, incentives, hidden votes, appeals.

In the case of the Dispute Kit Classic, it behaves like v1: proportional PNK-weighted draws without extra eligibility criteria or juror address deduplication, plurality voting, equal reward split among coherent votes, optional commit-reveal (depending on court config), binary appeal funding with free choice.

This modular design lets us extend the protocol and experiment with new dispute resolution mechanisms without touching the Core contract.

This is not the current V2 version, the up-to-date one undergoing the audits/remediations is on the dev branch.

For write operations

It has been addressed: kleros-v2/contracts/src/arbitration/dispute-kits/DisputeKitClassic.sol at c5bc3cf988a989131bf0b1414e4d13da8438576b · kleros/kleros-v2 · GitHub

For read operations

As an integrator your entrypoint is KlerosCore which is responsible for maintaining the disputeIDs / DisputeKits relationship, in this case KlerosCore.currentRuling(). Not disputeKit.currentRuling().

As for protecting the user from himself, there are limits to this in a smart contract environment, cf. the Kleros solidity good practices. The user should really start by calling the correct contract, we can’t write solidity code to protect against every possible user misuse.

Same, this is by design. Such operation is access restricted to the governor and invalid changes must be picked up by the governance process. We know that the OpenZeppelin libraries push for extra validation or 2-step owner changes, it’s just not how we build contracts at Kleros.

@clesaege can also elaborate too on these style choices.


Back to the original proposal

I think it’s a great idea, especially with ERC-8183 picking up pace.

The real effort is figuring out an appropriate court policy which is a prerequisite to create the court.

That means finding the line between the policies specific to your Escrow use case (which belongs to the dispute policy) vs the policies generally applicable to the “payments and fast escrow” use cases intended to be resolved in this court.

If you wrote with a proper software engineering mindset, instead of “we can’t write solidity code to protect against every possible user misuse,” you’d understand how to build a system that is not heavily reliant on Solidity for multiple disputing scenarios, but instead you choose to make excuses for your poor coding practices and thinking.

And I knew you’d have some sort of excuse to defend your poor coding practices. It’s the only way a low-quality engineer like you could justify to higher-ups in a software project to keep you on with your poor code for all these years, but let the record show, I think you’re an extraordinarily incompetent developer, and that you were and you continue to be one of the major bottlenecks in the Kleros project over these years.

The level of work you do and the time it takes for results is simply embarrassing. And on top of your snail’s pace velocity, the results are also poor.

I’ve glanced some of your “thinking” on the old Kleros repo too, you are 100% responsible for the difficulty downstream developers face when attempting to integrate with Kleros. You are a coding-monkey, and you haven’t even graduated past a Junior level of conceptual programming skills.

And I’m not giving you free architecture advice, use some critical thinking and figure out other methods of software architecture by yourself.

I will say this though: what you promised and what you delivered don’t match. You promised a modular dispute-kit system where juror selection, voting, and appeal logic could be composed as independent pieces. What you delivered, even in the latest version, is a single monolithic contract where drawing, voting, appeal funding, coherence scoring, and round introspection are fused into one IDisputeKit boundary. The system swaps whole kits, not kit pieces. That is not modularity; that is a controlled catalog.

The latest revision made things slightly worse in one respect and only marginally better in another. The abstract DisputeKitClassicBase was collapsed into a concrete DisputeKitClassic, and in doing so _postDrawCheck, the one hook that let subclasses control draw eligibility, lost its virtual modifier entirely. It is now hardcoded. In exchange, getNextRoundSettings and getRewards became virtual, so subclasses can influence appeal routing and reward math. That is a net of two hooks gained, one lost. The vote aggregation (plurality), the commit-reveal lifecycle, and the appeal cost multipliers (10000/20000/5000) are still hardcoded and non-virtual. The Dispute, Round, and Vote structs are still copied verbatim across every variant, because Solidity does not let you override a struct the way you override a function. You cannot swap drawing, voting, or the appeal model independently; you duplicate the entire engine and bolt something on the side. That is kit-level swappability, not component-level modularity, and the difference matters. The new DisputeKitAsymmetricPlurality in the dev folder makes it concrete: asymmetric coherence scoring is a single behavioral change to one function. By the definition of modularity you used when making the promise, it would be one contract implementing one interface. What it actually required was copying the entire ~700 lines of drawing, voting, and appeal code that the developer did not touch, just to change one calculation at the end. That is a classic engine with a small set of controlled hooks, and calling it a modular platform is a meaningful overstatement of what was built.

But remember, you “can’t write solidity code to protect against every possible user misuse,” so in other words, you lack the architectural thinking for a modular system, how ironic.

That must be surprising to never hear back, ever wondered why?

Haven’t you sold all the PNK and was done with Kleros? KIP-66: Long-Term Juror Incentive Program - #39 by KlerosJuror

And please, be respectful with your comments, if not I will have to ban you. It’s fine to criticize the code or process or whatever you want, but without insults or ad hominem attacks. Thanks

3 Likes

A few clarifications, then I’ll step back from this exchange.

Free speech
Kleros doesn’t owe you a platform for that language. You don’t seem to know what free speech means.

Your applications
I’m not the person who handles the first rounds of developer applications, and I haven’t been for years. Initial screening has been going through our internal recruiter and other engineers. I only step in at the final round of interviews. Just did a quick check on our ATS and I don’t see any record of an application from you. It’s likely that what you sent didn’t make it past our spam filter, which is strict and tends to drop submissions that don’t address the screening questions properly. So no one is ghosting you.

V1 integration
The V1 integration surface predates my involvement at Kleros. I didn’t design or ship the API. Whatever “three trust-minimized primitives” you’ve built presumably integrate against an interface that existed before I joined the team. V2 is the version I led and its integration surface is intentionally not yet promoted for onchain integrations at this stage.

Duplication and validation
Both are documented Kleros engineering style. The guide explicitly says “Do not abstract contracts into more generic contracts if the deployed version will only use a subset of the functionality” and “limit smart contracts to blocking malicious behavior and let clients prevent the stupid ones.”

Reusability is ranked last in the priority hierarchy, after security, gas and auditability. These guidelines were authored by @clesaege, who you tagged in #7 for validation. If you have a problem with them, take it up with him. Blaming me personally for following standards written by the person you appealed to is amusing.

Kit-vs-component modularity
This is the one substantive technical point and I was initially happy to discuss it on the merits but I won’t be engaging with this kind of meltdown.

8 Likes