$SPLIT is live — the game below is still a simulation

split or steal.

Stake, seal your choice, and you are matched with whoever commits next. No lobby. No waiting room. You will never meet them, and the record is permanent.

game#1
stake · 1,000 $SPLIT — the same for both sides, always
0
your games
you split
0
net $SPLIT
crowd steals

the payoff

four outcomes, no negotiation

Neither of you can see the other's choice until both are sealed in. There is no chat, no signal and no second round with the same person.

they split
they steal
you
split
1,000 each
The pot divides evenly. The only outcome where nothing is destroyed and nobody is robbed.
you get nothing
They take both stakes. Your loss is their entire profit, and it is on the record with your address on it.
you
steal
2,000 to you
You take both stakes. So does the record — every wallet you did this to is permanently listed.
both burned
Neither of you gets anything and both stakes are destroyed. Betrayal is the supply sink.
the record

every pair, forever

Each settled game writes both addresses and both choices to the chain. A wallet that always steals is a wallet everyone can see always steals — that is the actual thing at stake here.

player a
player b
outcome
the share

somebody did this to you, on purpose

post it
commit–reveal

how the choice stays sealed

Your choice is never on chain in the open until the game is over. What you submit is a hash, and the contract cannot read it — it can only check it later.

01 · COMMIT

You send a hash

keccak(choice, salt, you). The stake moves with it. Nobody — including the contract — can tell split from steal.

02 · MATCH

The next commit pairs with you

One open commit at a time, per wallet. The moment somebody else commits, you are matched and the reveal window opens.

03 · REVEAL

Both send the salt

The contract recomputes the hash. If it matches, your choice counts. You cannot change it now — that is the entire point of step one.

04 · NO-SHOW

Silence loses

If the window closes and only one side revealed, that side takes both stakes. If neither revealed, both are burned. Walking away is not an escape.

contract

the whole thing

Not yet deployed. No owner, no admin, no pause, no upgrade and no rescue function — the same as everything else here.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract SplitSteal {
    uint8 constant SPLIT = 1;
    uint8 constant STEAL = 2;
    uint256 public immutable stake;
    uint64  public constant WINDOW = 7200; // blocks to reveal

    struct Game {
        address a; address b;
        bytes32 ha; bytes32 hb;
        uint8 ca; uint8 cb;
        uint64 deadline;
        bool settled;
    }

    mapping(uint256 => Game) public games;
    uint256 public openGame;  // waiting for a partner, 0 if none
    uint256 public count;

    function commit(bytes32 h) external returns (uint256 id) {
        token.transferFrom(msg.sender, address(this), stake);

        if (openGame == 0) {                      // you are player a
            id = ++count;
            games[id] = Game(msg.sender, address(0), h, 0, 0, 0, 0, false);
            openGame = id;
        } else {                                  // you are player b
            id = openGame;
            Game storage g = games[id];
            require(g.a != msg.sender, "no self-match");
            g.b = msg.sender; g.hb = h;
            g.deadline = uint64(block.number) + WINDOW;
            openGame = 0;
        }
        emit Committed(id, msg.sender);
    }

    function reveal(uint256 id, uint8 choice, bytes32 salt) external {
        Game storage g = games[id];
        require(g.b != address(0) && !g.settled);
        bytes32 h = keccak256(abi.encode(choice, salt, msg.sender));
        if (msg.sender == g.a && h == g.ha) g.ca = choice;
        else if (msg.sender == g.b && h == g.hb) g.cb = choice;
        else revert BadReveal();
        if (g.ca != 0 && g.cb != 0) _settle(id);
    }

    function settle(uint256 id) external {   // after the window, anybody
        require(block.number > games[id].deadline);
        _settle(id);
    }

    function _settle(uint256 id) internal {
        Game storage g = games[id];
        require(!g.settled); g.settled = true;
        uint256 pot = stake * 2;

        if (g.ca == SPLIT && g.cb == SPLIT) { _pay(g.a, stake); _pay(g.b, stake); }
        else if (g.ca == SPLIT && g.cb == STEAL) { _pay(g.b, pot); }
        else if (g.ca == STEAL && g.cb == SPLIT) { _pay(g.a, pot); }
        else if (g.ca != 0 && g.cb == 0)        { _pay(g.a, pot); } // b no-showed
        else if (g.ca == 0 && g.cb != 0)        { _pay(g.b, pot); } // a no-showed
        else _burn(pot);                          // both stole, or both silent

        emit Settled(id, g.a, g.b, g.ca, g.cb);
    }
}