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 thecreateShuffleGame
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 SDKpkY
: 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 ofcards
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 therecord
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;
}
...
}