The matches contract is a simple escrow contract that allows for an independent oracle to redistribute tokens added to the match. The idea is to support multiplayer gaming. This contract will be developed further in the future, to add new features such as:
Match defines maximum loss allowed by oracle for joiners to cross-compare
Match has support for player backpacks once player contract comes online, so that items can swap player backpacks without the player having to add those items directly to the match
Match CLI support for Merkle roots
Build out logic for minimum_allowed_entry_time
Draft - Nobody can join the match in this state, but it is updatable by its authority.
Initialized - Entrants can join provided they meet at least one TokenValidation. No TokenValidations means anybody can join. Oracles cannot effect a match in this state. Can initialize the match in this state if you wish.
Started - Entrants can join provided the match has join_allowed_during_start is set to true. Entrants can leave if leave_allowed is set to true. This state can only be reached by the match authority updating the match from the Initialized state.
Finalized - This state can only be reached by the finalized boolean on the oracle being flipped to true. Then update_match_from_oracle can be called permissionlessly to get Match into this state. Once here, entrant’s tokens can be paid out to the original entrants, or new recipients, based on the TokenDeltas present in the oracle. Note: An oracle need not be created for a given match until you wish to Finalize the match.
PaidOut - This state is reached once all tokens have been removed from the oracle.
Deactivated - This state can be reached via a direct call to update_match while in Draft or Initialized state, or from PaidOut state. The Match MUST be in this state before drain_match is available to reclaim lamports.
The layout of Match is fairly simple - one defines a match that acts as a state machine, and gives it an oracle that can be controlled by a third party that can define outcomes. The match can define TokenValidations by which people are allowed to enter, and the oracle can define TokenDeltas by which tokens are redistributed to the entrants in the match.
Let’s use this live example JSON to illustrate how to create, enter, and complete a match:
This JSON actually is setup to do a few different things, at different points among the lifetime of the match. We’ll go over each as we do it. First, let’s create the oracle!
This uses the “oracleState” hash to seed and create an oracle. Whatever is in this hash file will be placed in the oracle every time the above command is run. Alternatively, if oracleState is null and winOracle is set, the expectation is that the oracle is not owned by the Matches program and you’re choosing not to use this endpoint to update it, but it still fits the standard.
Now let’s create the match:
Checking the token entry validation in our JSON, or via the show match call, run here, we can see that only one type of token can be added:
We happen to know that the first entry in the tokensToJoin list is the token whose parent matches, see:
We can also see that when this token is added, it will do a CPI call to a specific validation we set. This is entirely optional - removing the callback from the JSON would remove this requirement. It just allows the match author to add additional custom logic beyond the standard filters.
To add this token to the match, we run:
This command uses the “tokensToJoin” array in the JSON file and either iterates through and adds each token to the match escrow OR if -i is provided, only adds that index (as we are doing here). If we then try to add index 1, we will see it fail due to our token entry validation.
After this, we can change the entry for matchState to started in the JSON, then issue the update using the update command.
At this point, now we must use the oracle to determine the outcome of the match. Let’s update the Oracle hash to the following:
Please note that this JSON/update call combo will only work if your oracle is an internally produced one. If it is external, the external party must do their own writing. To issue this oracle state to the on-chain oracle, run
Now that the oracle has been updated, you’ll need to notify the match that the oracle has changed and it should “listen” to it. This will move the match from the Started state to the Finalized state.
Note that this call is permissionless! Anybody can do this. Now if we run show_match, we’ll see that the state has changed. Now it is up to permissionless cranks to follow the TokenTransfers provided and issue out the tokens to either original owners or new owners.
In this case, this will issue a single command to the cluster to move the token to its new owner. Now the match will have entered its final state, PaidOut, because all tokens have left the match escrow. Let’s drain the match and oracle and reclaim lamports!