You have off-chain game play, so there is an element of trust in the game servers. You have separate funds in play from funds that are free to withdraw (unencumbered).
A simple formulation would be to transfer funds into the care of the game system and it's accounting process until funds are taken out of live play and available for withdraw.
In case that isn't clear.
Alice has 10 ether. She buys 5 chips for 5 ether and she has 5 ether available for withdrawal. She cash her chips in when they are not in play, but that is a game-server concern because it knows which ones are "free and clear" and which ones are in play. When Alice sells, say 7 chips (lucky Alice), she gets 7 ether back.
A contract can handle the deposit and withdrawal of ether. It can handle to trade for chips by taking the ether away (transfer to owner) and emitting an event. It is the game server's responsibility to credit her with casino chips. The contract will merely emit an event so it knows what it should do.
Similarly, when Alice cashes in her chips, the game server will take the chips away and transfer corresponding amount of ether into Alice's account. Alice can withdraw ether to exit the establishment.
This is an admittedly over-simplified example to give you some ideas.
pragma solidity 0.5.1;
contract Holdem {
mapping(address => uint) public balances;
event LogDeposit(address sender, uint amount);
event LogWithdrawal(address receiver, uint amount);
event LogTransfer(address sender, address receiver, uint amount);
function depost() public payable {
balances[msg.sender] += msg.value;
emit LogDeposit(msg.sender, msg.value);
}
function withdraw(uint amount) public {
uint bal = balances[msg.sender];
require(bal >= amount, "Insufficient Funds.");
balances[msg.sender] -= amount;
emit LogWithdrawal(msg.sender, amount);
msg.sender.transfer(amount);
}
function transfer(uint amount, address receiver) public {
uint bal = balances[msg.sender];
require(bal >= amount, "Insufficient Funds.");
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit LogTransfer(msg.sender, receiver, amount);
}
}
The transfer()
function lets Alice give to the house, and lets the house give to Alice. It emits an event that is sufficient for an honest house to account for funds received.
Astute observers will note that there is nothing stopping the house from withdrawing winnings (in the honest case) or everything (in the exit scam case). One would need to add strict restrictions on the house's ability to withdraw funds (chips should be collateralized) as well as evidence that the games are fair before the "off-chain" parts of such a system would be trustworthy.
Hope it helps.