跳到主要内容

Overview

zkShuffle Contracts provides two contract interfaces to help developers build their own card games:

  • IShuffleStateManager: The manager of shuffle state machine, which creates and registers shuffle state, deals and opens cards.
  • IBaseGame: Developers need to inherit this interface when writing their own game contracts.

IShuffleStateManager

createShuffleGame

function createShuffleGame(uint8 numPlayers) external returns (uint256)[src]

Creates a new game and returns the game ID of the newly created game. This could only be called by game contract.

Parameters:

  • numPlayers: The number of players.

Return:

  • gameId: The created shuffle game ID.

Example:

pragma solidity >=0.8.2 <0.9.0;
import "IBaseGame.sol";
import "IShuffleStateManager.sol";
contract Game is IBaseGame {
IShuffleStateManager public shuffle;
constructor(IShuffleStateManager _shuffle) {
shuffle = _shuffle;
}
function createGame() external {
uint256 shuffleId = shuffle.createShuffleGame(2);
...
}
...
}

register

function register(uint256 gameId, bytes calldata next) external[src]

Enters the register state, which can only be called by the game owner.

Parameters:

  • gameId: The created shuffle game ID.
  • next: The calldata that contains the function signature and parameters, and it will be executed in the next invocation.

Note: The gameId parameter in all the interfaces corresponds to the return value of the createShuffleGame function.

Note: register only initiates the registration phase so that players can register, it doesn't actually perform the registration.

playerRegister

function playerRegister(uint256 gameId, address signingAddr, uint256 pkX, uint256 pkY) external returns (uint256)[src]

Performs the registration for a certain player, which can be called by game contract or directly called by user.

Parameters:

  • gameId: The created shuffle game ID.
  • signingAddr: The signing address of the player, usually it's player's metamask address.
  • pkX: The X-coordinate of the public key generated by BabyJub curve. It can be generated by SDK
  • pkY: The Y-coordinate of the public key generated by BabyJub curve. It can be generated by SDK

Return:

  • pid: The player index in the game.

Note: If you don't have special needs, it's better to use SDK to do the registering.

Example:

pragma solidity >=0.8.2 <0.9.0;
import "IBaseGame.sol";
import "IShuffleStateManager.sol";
contract Game is IBaseGame {
IShuffleStateManager public shuffle;
uint256 public shuffleId;
constructor(IShuffleStateManager _shuffle) {
shuffle = _shuffle;
}
function createGame() external {
shuffleId = shuffle.createShuffleGame(2);
}
function registerGame(uint256 pkX, uint256 pkY) external {
bytes memory next = abi.encodeWithSelector(
this.dummy.selector
);
shuffle.register(shuffleId, next);
uint256 playerIdx = shuffle.playerRegister(
shuffleId,
msg.sender,
pkX,
pkY
);
...
}
function dummy() external {
...
}
...
}

shuffle

function shuffle(uint256 gameId, bytes calldata next) external[src]

Initiates the shuffle phase, which can only be called by game contract.

Parameters:

  • gameId: The created shuffle game ID.
  • next: The calldata that will be executed in the next invocation.

Note: shuffle only initiates the shuffle phase so that players can shuffle, it doesn't actually perform the shuffle.

dealCardsTo

function shuffle(uint256 gameId, BitMaps.BitMap256 memory cards, uint256 playerId, bytes calldata next) external[src]

Specifies a set of cards to be dealt to a player, which can only be called by game contract.

Parameters:

  • gameId: The created shuffle game ID.
  • cards: The cards to deal.
  • playerId: The player index in the shuffle game.
  • next: The calldata that will be executed in the next invocation.

Note: dealCardsTo only initiates the dealing phase so that players can deal, it doesn't actually perform the dealing.

Note: cards is represented in bitMap. For example: if the data of cards is 28, it corresponds to the binary representation 11100. This means that the cards with indexes [2, 3, 4] need to be dealt.

openCards

function openCards(uint256 gameId, uint256 playerId, uint8 openningNum, bytes calldata next) external[src]

Specifies a player to open a number of cards, which can only be called by game contract.

Parameters:

  • gameId: The created shuffle game ID.
  • playerId: The player index in the shuffle game.
  • openningNum: The number of cards that the player wants to open.
  • next: The calldata that will be executed in the next invocation.

register shuffle dealCardsTo openCards, these four functions all specify next as the next operation to be executed, but their execution timing is different. | function | when next will be called | |:--------|:--------| | register | after all the players have registered | | shuffle | after all the players have shuffled the deck | | dealCardsTo | after all the players except for the one with the playerId have dealt cards | | openCards | after each player has opened cards |

endGame

function openCards(uint256 gameId) external[src]

Ends the game, which can only be called by game contract.

Parameters:

  • gameId: The created shuffle game ID.

curPlayerIndex

function curPlayerIndex(uint256 gameId) external returns(uint256)[src]

Gets the player index of which player needs to take action.

Parameters:

  • gameId: The created shuffle game ID.

Return:

  • currentPlayerIdx: The currentPlayerIdx indicates which player needs to take action.

getPlayerIdx

function getPlayerIdx(uint256 gameId, address player) external returns(uint256)[src]

Gets the player index of player in gameId game.

Parameters:

  • gameId: The created shuffle game ID.
  • player: The player's address.

Return:

  • playerIdx: The player index in the shuffle game.

INVALID_INDEX

INVALID_INDEX is a constant with a value of 999999, used to represent an invalid card index and player index.

Example:

pragma solidity >=0.8.2 <0.9.0;
import "IBaseGame.sol";
import "IShuffleStateManager.sol";
contract Game is IBaseGame {
IShuffleStateManager public shuffle;
uint256 public shuffleId;
...
function getPlayerIndex() external {
uint256 playerIdx = shuffle.getPlayerIdx(shuffleId, msg.sender);
require(playerIdx != shuffle.INVALID_INDEX());
...
}
}

getNumCards

function getNumCards(uint256 gameId) external returns(uint256)[src]

Gets the number of cards.

Parameters:

  • gameId: The created shuffle game ID.

Return:

  • numCards: The number of cards.

queryCardValue

function queryCardValue(uint256 gameId, uint256 cardIndex) external returns(uint256)[src]

Queries the card value of cardIndex-th card in gameId.

Parameters:

  • gameId: The created shuffle game ID.
  • cardIndex: The index of a card in the deck.

Return:

  • cardValue: The actual card value in the deck.

getDecryptRecord

function getDecryptRecord(uint256 gameId, uint256 cardIdx) external returns(BitMaps.BitMap256 memory)[src]

Gets the decryption record (i.e., which players have decrypted this card).

Parameters:

  • gameId: The created shuffle game ID.
  • cardIdx: The index of a card in the deck.

Return:

  • record: It indicates which players have already dealt this card. For example, if the record value is 5, it corresponds to the binary representation 101. This means that the player with indexes [0, 2] has dealt the card.

queryAggregatedPk

function queryAggregatedPk(uint256 gameId) external returns(uint px, uint py)[src]

Queries the aggregated public key for card shuffling/dealing in gameId-th game. The public key is an elliptic curve point on BN254 G1 curve.

Parameters:

  • gameId: The created shuffle game ID.

Return:

  • pkX: The X-coordinate of the aggregated public key.
  • pkY: The Y-coordinate of the aggregated public key.

IBaseGame

cardConfig

Developers need to implement the cardConfig interface, which represents the number of cards included in the game.

Example:

pragma solidity >=0.8.2 <0.9.0;
import "IBaseGame.sol";
contract Game is IBaseGame {
// This indicates that the game contract is based on a deck of 30 cards.
function cardConfig() external pure override returns (DeckConfig) {
return DeckConfig.Deck30Card;
}
...
}