/**
* The edgeless casino contract v2 holds the players's funds and provides state channel functionality.
* The casino has at no time control over the players's funds.
* State channels can be updated and closed from both parties: the player and the casino.
* author: Julia Altenried
**/
pragma solidity ^0.4.21;
contract SafeMath {
function safeSub(uint a, uint b) pure internal returns(uint) {
assert(b <= a);
return a - b;
}
function safeSub(int a, int b) pure internal returns(int) {
if(b < 0) assert(a - b > a);
else assert(a - b <= a);
return a - b;
}
function safeAdd(uint a, uint b) pure internal returns(uint) {
uint c = a + b;
assert(c >= a && c >= b);
return c;
}
function safeMul(uint a, uint b) pure internal returns (uint) {
uint c = a * b;
assert(a == 0 || c / a == b);
return c;
}
}
contract Token {
function transferFrom(address sender, address receiver, uint amount) public returns(bool success);
function transfer(address receiver, uint amount) public returns(bool success);
function balanceOf(address holder) public view returns(uint);
}
contract Owned {
address public owner;
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function Owned() public{
owner = msg.sender;
}
}
/** owner should be able to close the contract is nobody has been using it for at least 30 days */
contract Mortal is Owned {
/** contract can be closed by the owner anytime after this timestamp if non-zero */
uint public closeAt;
/** the edgeless token contract */
Token edg;
function Mortal(address tokenContract) internal{
edg = Token(tokenContract);
}
/**
* lets the owner close the contract if there are no player funds on it or if nobody has been using it for at least 30 days
*/
function closeContract(uint playerBalance) internal{
if(closeAt == 0) closeAt = now + 30 days;
if(closeAt < now || playerBalance == 0){
edg.transfer(owner, edg.balanceOf(address(this)));
selfdestruct(owner);
}
}
/**
* in case close has been called accidentally.
**/
function open() onlyOwner public{
closeAt = 0;
}
/**
* make sure the contract is not in process of being closed.
**/
modifier isAlive {
require(closeAt == 0);
_;
}
/**
* delays the time of closing.
**/
modifier keepAlive {
if(closeAt > 0) closeAt = now + 30 days;
_;
}
}
contract RequiringAuthorization is Mortal {
/** indicates if an address is authorized to act in the casino's name */
mapping(address => bool) public authorized;
/** tells if an address is allowed to receive funds from the bankroll **/
mapping(address => bool) public allowedReceiver;
modifier onlyAuthorized {
require(authorized[msg.sender]);
_;
}
/**
* Constructor. Authorize the owner.
* */
function RequiringAuthorization() internal {
authorized[msg.sender] = true;
allowedReceiver[msg.sender] = true;
}
/**
* authorize a address to call game functions and set configs.
* @param addr the address to be authorized
**/
function authorize(address addr) public onlyOwner {
authorized[addr] = true;
}
/**
* deauthorize a address to call game functions and set configs.
* @param addr the address to be deauthorized
**/
function deauthorize(address addr) public onlyOwner {
authorized[addr] = false;
}
/**
* allow authorized wallets to withdraw funds from the bonkroll to this address
* @param receiver the receiver's address
* */
function allowReceiver(address receiver) public onlyOwner {
allowedReceiver[receiver] = true;
}
/**
* disallow authorized wallets to withdraw funds from the bonkroll to this address
* @param receiver the receiver's address
* */
function disallowReceiver(address receiver) public onlyOwner {
allowedReceiver[receiver] = false;
}
/**
* changes the owner of the contract. revokes authorization of the old owner and authorizes the new one.
* @param newOwner the address of the new owner
* */
function changeOwner(address newOwner) public onlyOwner {
deauthorize(owner);
authorize(newOwner);
disallowReceiver(owner);
allowReceiver(newOwner);
owner = newOwner;
}
}
contract ChargingGas is RequiringAuthorization, SafeMath {
/** 1 EDG has 5 decimals **/
uint public constant oneEDG = 100000;
/** the price per kgas and GWei in tokens (with decimals) */
uint public gasPrice;
/** the amount of gas used per transaction in kGas */
mapping(bytes4 => uint) public gasPerTx;
/** the number of tokens (5 decimals) payed by the users to cover the gas cost */
uint public gasPayback;
function ChargingGas(uint kGasPrice) internal{
//deposit, withdrawFor, updateChannel, updateBatch, transferToNewContract
bytes4[5] memory signatures = [bytes4(0x3edd1128),0x9607610a, 0xde48ff52, 0xc97b6d1f, 0x6bf06fde];
//amount of gas consumed by the above methods in GWei
uint[5] memory gasUsage = [uint(146), 100, 65, 50, 85];
setGasUsage(signatures, gasUsage);
setGasPrice(kGasPrice);
}
/**
* sets the amount of gas consumed by methods with the given sigantures.
* only called from the edgeless casino constructor.
* @param signatures an array of method-signatures
* gasNeeded the amount of gas consumed by these methods
* */
function setGasUsage(bytes4[5] signatures, uint[5] gasNeeded) public onlyOwner {
require(signatures.length == gasNeeded.length);
for (uint8 i = 0; i < signatures.length; i++)
gasPerTx[signatures[i]] = gasNeeded[i];
}
/**
* updates the price per 1000 gas in EDG.
* @param price the new gas price (with decimals, max 0.1 EDG)
**/
function setGasPrice(uint price) public onlyAuthorized {
require(price < oneEDG/10);
gasPrice = price;
}
/**
* returns the gas cost of the called function.
* */
function getGasCost() internal view returns(uint) {
return safeMul(safeMul(gasPerTx[msg.sig], gasPrice), tx.gasprice) / 1000000000;
}
}
contract CasinoBank is ChargingGas {
/** the total balance of all players with virtual decimals **/
uint public playerBalance;
/** the balance per player in edgeless tokens with virtual decimals */
mapping(address => uint) public balanceOf;
/** in case the user wants/needs to call the withdraw function from his own wallet, he first needs to request a withdrawal */
mapping(address => uint) public withdrawAfter;
/** a number to count withdrawal signatures to ensure each signature is different even if withdrawing the same amount to the same address */
mapping(address => uint) public withdrawCount;
/** the maximum amount of tokens the user is allowed to deposit (with decimals) */
uint public maxDeposit;
/** the maximum withdrawal of tokens the user is allowed to withdraw on one day (only enforced when the tx is not sent from an authorized wallet) **/
uint public maxWithdrawal;
/** waiting time for withdrawal if not requested via the server **/
uint public waitingTime;
/** the address of the predecessor **/
address public predecessor;
/** informs listeners how many tokens were deposited for a player */
event Deposit(address _player, uint _numTokens, uint _gasCost);
/** informs listeners how many tokens were withdrawn from the player to the receiver address */
event Withdrawal(address _player, address _receiver, uint _numTokens, uint _gasCost);
/**
* Constructor.
* @param depositLimit the maximum deposit allowed
* predecessorAddr the address of the predecessing contract
* */
function CasinoBank(uint depositLimit, address predecessorAddr) internal {
maxDeposit = depositLimit * oneEDG;
maxWithdrawal = maxDeposit;
waitingTime = 24 hours;
predecessor = predecessorAddr;
}
/**
* accepts deposits for an arbitrary address.
* retrieves tokens from the message sender and adds them to the balance of the specified address.
* edgeless tokens do not have any decimals, but are represented on this contract with decimals.
* @param receiver address of the receiver
* numTokens number of tokens to deposit (0 decimals)
* chargeGas indicates if the gas cost is subtracted from the user's edgeless token balance
**/
function deposit(address receiver, uint numTokens, bool chargeGas) public isAlive {
require(numTokens > 0);
uint value = safeMul(numTokens, oneEDG);
uint gasCost;
if (chargeGas) {
gasCost = getGasCost();
value = safeSub(value, gasCost);
gasPayback = safeAdd(gasPayback, gasCost);
}
uint newBalance = safeAdd(balanceOf[receiver], value);
require(newBalance <= maxDeposit);
assert(edg.transferFrom(msg.sender, address(this), numTokens));
balanceOf[receiver] = newBalance;
playerBalance = safeAdd(playerBalance, value);
emit Deposit(receiver, numTokens, gasCost);
}
/**
* If the user wants/needs to withdraw his funds himself, he needs to request the withdrawal first.
* This method sets the earliest possible withdrawal date to 'waitingTime from now (default 90m, but up to 24h).
* Reason: The user should not be able to withdraw his funds, while the the last game methods have not yet been mined.
**/
function requestWithdrawal() public {
withdrawAfter[msg.sender] = now + waitingTime;
}
/**
* In case the user requested a withdrawal and changes his mind.
* Necessary to be able to continue playing.
**/
function cancelWithdrawalRequest() public {
withdrawAfter[msg.sender] = 0;
}
/**
* withdraws an amount from the user balance if the waiting time passed since the request.
* @param amount the amount of tokens to withdraw
**/
function withdraw(uint amount) public keepAlive {
require(amount <= maxWithdrawal);
require(withdrawAfter[msg.sender] > 0 && now > withdrawAfter[msg.sender]);
withdrawAfter[msg.sender] = 0;
uint value = safeMul(amount, oneEDG);
balanceOf[msg.sender] = safeSub(balanceOf[msg.sender], value);
playerBalance = safeSub(playerBalance, value);
assert(edg.transfer(msg.sender, amount));
emit Withdrawal(msg.sender, msg.sender, amount, 0);
}
/**
* lets the owner withdraw from the bankroll
* @param receiver the receiver's address
* numTokens the number of tokens to withdraw (0 decimals)
**/
function withdrawBankroll(address receiver, uint numTokens) public onlyAuthorized {
require(numTokens <= bankroll());
require(allowedReceiver[receiver]);
assert(edg.transfer(receiver, numTokens));
}
/**
* withdraw the gas payback to the owner
**/
function withdrawGasPayback() public onlyAuthorized {
uint payback = gasPayback / oneEDG;
assert(payback > 0);
gasPayback = safeSub(gasPayback, payback * oneEDG);
assert(edg.transfer(owner, payback));
}
/**
* returns the current bankroll in tokens with 0 decimals
**/
function bankroll() view public returns(uint) {
return safeSub(edg.balanceOf(address(this)), safeAdd(playerBalance, gasPayback) / oneEDG);
}
/**
* updates the maximum deposit.
* @param newMax the new maximum deposit (0 decimals)
**/
function setMaxDeposit(uint newMax) public onlyAuthorized {
maxDeposit = newMax * oneEDG;
}
/**
* updates the maximum withdrawal.
* @param newMax the new maximum withdrawal (0 decimals)
**/
function setMaxWithdrawal(uint newMax) public onlyAuthorized {
maxWithdrawal = newMax * oneEDG;
}
/**
* sets the time the player has to wait for his funds to be unlocked before withdrawal (if not withdrawing with help of the casino server).
* the time may not be longer than 24 hours.
* @param newWaitingTime the new waiting time in seconds
* */
function setWaitingTime(uint newWaitingTime) public onlyAuthorized {
require(newWaitingTime <= 24 hours);
waitingTime = newWaitingTime;
}
/**
* transfers an amount from the contract balance to the owner's wallet.
* @param receiver the receiver address
* amount the amount of tokens to withdraw (0 decimals)
* v,r,s the signature of the player
**/
function withdrawFor(address receiver, uint amount, uint8 v, bytes32 r, bytes32 s) public onlyAuthorized keepAlive {
address player = ecrecover(keccak256(receiver, amount, withdrawCount[receiver]), v, r, s);
withdrawCount[receiver]++;
uint gasCost = getGasCost();
uint value = safeAdd(safeMul(amount, oneEDG), gasCost);
gasPayback = safeAdd(gasPayback, gasCost);
balanceOf[player] = safeSub(balanceOf[player], value);
playerBalance = safeSub(playerBalance, value);
assert(edg.transfer(receiver, amount));
emit Withdrawal(player, receiver, amount, gasCost);
}
/**
* transfers the player's tokens directly to the new casino contract after an update.
* @param newCasino the address of the new casino contract
* v, r, s the signature of the player
* chargeGas indicates if the gas cost is payed by the player.
* */
function transferToNewContract(address newCasino, uint8 v, bytes32 r, bytes32 s, bool chargeGas) public onlyAuthorized keepAlive {
address player = ecrecover(keccak256(address(this), newCasino), v, r, s);
uint gasCost = 0;
if(chargeGas) gasCost = getGasCost();
uint value = safeSub(balanceOf[player], gasCost);
require(value > oneEDG);
//fractions of one EDG cannot be withdrawn
value /= oneEDG;
playerBalance = safeSub(playerBalance, balanceOf[player]);
balanceOf[player] = 0;
assert(edg.transfer(newCasino, value));
emit Withdrawal(player, newCasino, value, gasCost);
CasinoBank cb = CasinoBank(newCasino);
assert(cb.credit(player, value));
}
/**
* receive a player balance from the predecessor contract.
* @param player the address of the player to credit the value for
* value the number of tokens to credit (0 decimals)
* */
function credit(address player, uint value) public returns(bool) {
require(msg.sender == predecessor);
uint valueWithDecimals = safeMul(value, oneEDG);
balanceOf[player] = safeAdd(balanceOf[player], valueWithDecimals);
playerBalance = safeAdd(playerBalance, valueWithDecimals);
emit Deposit(player, value, 0);
return true;
}
/**
* lets the owner close the contract if there are no player funds on it or if nobody has been using it for at least 30 days
* */
function close() public onlyOwner {
closeContract(playerBalance);
}
}
contract EdgelessCasino is CasinoBank{
/** the most recent known state of a state channel */
mapping(address => State) public lastState;
/** fired when the state is updated */
event StateUpdate(address player, uint128 count, int128 winBalance, int difference, uint gasCost);
/** fired if one of the parties chooses to log the seeds and results */
event GameData(address player, bytes32[] serverSeeds, bytes32[] clientSeeds, int[] results, uint gasCost);
struct State{
uint128 count;
int128 winBalance;
}
/**
* creates a new edgeless casino contract.
* @param predecessorAddress the address of the predecessing contract
* tokenContract the address of the Edgeless token contract
* depositLimit the maximum deposit allowed
* kGasPrice the price per kGas in WEI
**/
function EdgelessCasino(address predecessorAddress, address tokenContract, uint depositLimit, uint kGasPrice) CasinoBank(depositLimit, predecessorAddress) Mortal(tokenContract) ChargingGas(kGasPrice) public{
}
/**
* updates several state channels at once. can be called by authorized wallets only.
* 1. determines the player address from the signature.
* 2. verifies if the signed game-count is higher than the last known game-count of this channel.
* 3. updates the balances accordingly. This means: It checks the already performed updates for this channel and computes
* the new balance difference to add or subtract from the player‘s balance.
* @param winBalances array of the current wins or losses
* gameCounts array of the numbers of signed game moves
* v,r,s array of the players's signatures
* chargeGas indicates if the gas costs should be subtracted from the players's balances
* */
function updateBatch(int128[] winBalances, uint128[] gameCounts, uint8[] v, bytes32[] r, bytes32[] s, bool chargeGas) public onlyAuthorized{
require(winBalances.length == gameCounts.length);
require(winBalances.length == v.length);
require(winBalances.length == r.length);
require(winBalances.length == s.length);
require(winBalances.length <= 50);
address player;
uint gasCost = 0;
if(chargeGas)
gasCost = getGasCost();
gasPayback = safeAdd(gasPayback, safeMul(gasCost, winBalances.length));
for(uint8 i = 0; i < winBalances.length; i++){
player = ecrecover(keccak256(winBalances[i], gameCounts[i]), v[i], r[i], s[i]);
_updateState(player, winBalances[i], gameCounts[i], gasCost);
}
}
/**
* updates a state channel. can be called by both parties.
* 1. verifies the signature.
* 2. verifies if the signed game-count is higher than the last known game-count of this channel.
* 3. updates the balances accordingly. This means: It checks the already performed updates for this channel and computes
* the new balance difference to add or subtract from the player‘s balance.
* @param winBalance the current win or loss
* gameCount the number of signed game moves
* v,r,s the signature of either the casino or the player
* chargeGas indicates if the gas costs should be subtracted from the player's balance
* */
function updateState(int128 winBalance, uint128 gameCount, uint8 v, bytes32 r, bytes32 s, bool chargeGas) public{
address player = determinePlayer(winBalance, gameCount, v, r, s);
uint gasCost = 0;
if(player == msg.sender)//if the player closes the state channel himself, make sure the signer is a casino wallet
require(authorized[ecrecover(keccak256(player, winBalance, gameCount), v, r, s)]);
else if (chargeGas){//subtract the gas costs from the player balance only if the casino wallet is the sender
gasCost = getGasCost();
gasPayback = safeAdd(gasPayback, gasCost);
}
_updateState(player, winBalance, gameCount, gasCost);
}
/**
* internal method to perform the actual state update.
* @param player the player address
* winBalance the player's win balance
* gameCount the player's game count
* */
function _updateState(address player, int128 winBalance, uint128 gameCount, uint gasCost) internal {
State storage last = lastState[player];
require(gameCount > last.count);
int difference = updatePlayerBalance(player, winBalance, last.winBalance, gasCost);
lastState[player] = State(gameCount, winBalance);
emit StateUpdate(player, gameCount, winBalance, difference, gasCost);
}
/**
* determines if the msg.sender or the signer of the passed signature is the player. returns the player's address
* @param winBalance the current winBalance, used to calculate the msg hash
* gameCount the current gameCount, used to calculate the msg.hash
* v, r, s the signature of the non-sending party
* */
function determinePlayer(int128 winBalance, uint128 gameCount, uint8 v, bytes32 r, bytes32 s) view internal returns(address){
if (authorized[msg.sender])//casino is the sender -> player is the signer
return ecrecover(keccak256(winBalance, gameCount), v, r, s);
else
return msg.sender;
}
/**
* computes the difference of the win balance relative to the last known state and adds it to the player's balance.
* in case the casino is the sender, the gas cost in EDG gets subtracted from the player's balance.
* @param player the address of the player
* winBalance the current win-balance
* lastWinBalance the win-balance of the last known state
* gasCost the gas cost of the tx
* */
function updatePlayerBalance(address player, int128 winBalance, int128 lastWinBalance, uint gasCost) internal returns(int difference){
difference = safeSub(winBalance, lastWinBalance);
int outstanding = safeSub(difference, int(gasCost));
uint outs;
if(outstanding < 0){
outs = uint256(outstanding * (-1));
playerBalance = safeSub(playerBalance, outs);
balanceOf[player] = safeSub(balanceOf[player], outs);
}
else{
outs = uint256(outstanding);
assert(bankroll() * oneEDG > outs);
playerBalance = safeAdd(playerBalance, outs);
balanceOf[player] = safeAdd(balanceOf[player], outs);
}
}
/**
* logs some seeds and game results for players wishing to have their game history logged by the contract
* @param serverSeeds array containing the server seeds
* clientSeeds array containing the client seeds
* results array containing the results
* v, r, s the signature of the non-sending party (to make sure the correct results are logged)
* */
function logGameData(bytes32[] serverSeeds, bytes32[] clientSeeds, int[] results, uint8 v, bytes32 r, bytes32 s) public{
address player = determinePlayer(serverSeeds, clientSeeds, results, v, r, s);
uint gasCost;
//charge gas in case the server is logging the results for the player
if(player != msg.sender){
gasCost = (57 + 768 * serverSeeds.length / 1000)*gasPrice;
balanceOf[player] = safeSub(balanceOf[player], gasCost);
playerBalance = safeSub(playerBalance, gasCost);
gasPayback = safeAdd(gasPayback, gasCost);
}
emit GameData(player, serverSeeds, clientSeeds, results, gasCost);
}
/**
* determines if the msg.sender or the signer of the passed signature is the player. returns the player's address
* @param serverSeeds array containing the server seeds
* clientSeeds array containing the client seeds
* results array containing the results
* v, r, s the signature of the non-sending party
* */
function determinePlayer(bytes32[] serverSeeds, bytes32[] clientSeeds, int[] results, uint8 v, bytes32 r, bytes32 s) view internal returns(address){
address signer = ecrecover(keccak256(serverSeeds, clientSeeds, results), v, r, s);
if (authorized[msg.sender])//casino is the sender -> player is the signer
return signer;
else if (authorized[signer])
return msg.sender;
else
revert();
}
}
{
"compilationTarget": {
"EdgelessCasino.sol": "EdgelessCasino"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"serverSeeds","type":"bytes32[]"},{"name":"clientSeeds","type":"bytes32[]"},{"name":"results","type":"int256[]"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"logGameData","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"bankroll","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"}],"name":"disallowReceiver","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"signatures","type":"bytes4[5]"},{"name":"gasNeeded","type":"uint256[5]"}],"name":"setGasUsage","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"lastState","outputs":[{"name":"count","type":"uint128"},{"name":"winBalance","type":"int128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newMax","type":"uint256"}],"name":"setMaxWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"numTokens","type":"uint256"},{"name":"chargeGas","type":"bool"}],"name":"deposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"close","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"withdrawAfter","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"withdrawCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxDeposit","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"playerBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newCasino","type":"address"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"chargeGas","type":"bool"}],"name":"transferToNewContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oneEDG","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxWithdrawal","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"amount","type":"uint256"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"withdrawFor","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"changeOwner","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"closeAt","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"waitingTime","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes4"}],"name":"gasPerTx","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"allowedReceiver","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"predecessor","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"authorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newMax","type":"uint256"}],"name":"setMaxDeposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"}],"name":"allowReceiver","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"price","type":"uint256"}],"name":"setGasPrice","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"winBalances","type":"int128[]"},{"name":"gameCounts","type":"uint128[]"},{"name":"v","type":"uint8[]"},{"name":"r","type":"bytes32[]"},{"name":"s","type":"bytes32[]"},{"name":"chargeGas","type":"bool"}],"name":"updateBatch","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"requestWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"winBalance","type":"int128"},{"name":"gameCount","type":"uint128"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"chargeGas","type":"bool"}],"name":"updateState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"gasPayback","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"cancelWithdrawalRequest","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"receiver","type":"address"},{"name":"numTokens","type":"uint256"}],"name":"withdrawBankroll","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newWaitingTime","type":"uint256"}],"name":"setWaitingTime","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"player","type":"address"},{"name":"value","type":"uint256"}],"name":"credit","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"withdrawGasPayback","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"open","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"gasPrice","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"predecessorAddress","type":"address"},{"name":"tokenContract","type":"address"},{"name":"depositLimit","type":"uint256"},{"name":"kGasPrice","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"player","type":"address"},{"indexed":false,"name":"count","type":"uint128"},{"indexed":false,"name":"winBalance","type":"int128"},{"indexed":false,"name":"difference","type":"int256"},{"indexed":false,"name":"gasCost","type":"uint256"}],"name":"StateUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"player","type":"address"},{"indexed":false,"name":"serverSeeds","type":"bytes32[]"},{"indexed":false,"name":"clientSeeds","type":"bytes32[]"},{"indexed":false,"name":"results","type":"int256[]"},{"indexed":false,"name":"gasCost","type":"uint256"}],"name":"GameData","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_player","type":"address"},{"indexed":false,"name":"_numTokens","type":"uint256"},{"indexed":false,"name":"_gasCost","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_player","type":"address"},{"indexed":false,"name":"_receiver","type":"address"},{"indexed":false,"name":"_numTokens","type":"uint256"},{"indexed":false,"name":"_gasCost","type":"uint256"}],"name":"Withdrawal","type":"event"}]