bank.gno
3.45 Kb · 116 lines
1package gnodice
2
3import (
4 "chain"
5 "chain/banker"
6 "chain/runtime/unsafe"
7)
8
9// La banque du jeu : son solde est celui de l'adresse du contrat.
10// Une partie de ce solde est « réservée » : c'est le gain maximal promis aux
11// parties en cours. Seul le reste (« disponible ») peut couvrir de nouvelles
12// mises ou être retiré par le propriétaire.
13
14// Fund alimente la banque. N'importe qui peut l'appeler en envoyant des GNOT.
15func Fund(cur realm) {
16 caller := cur.Previous()
17 if !caller.IsUserCall() {
18 panic("gnodice: Fund doit être appelé directement depuis un wallet")
19 }
20 sent := unsafe.OriginSend()
21 if len(sent) != 1 || sent[0].Denom != Denom || sent[0].Amount <= 0 {
22 panic("gnodice: envoie des GNOT (et uniquement des GNOT) pour alimenter la banque")
23 }
24 totalFunded += sent[0].Amount
25 chain.Emit("BankFunded", "from", caller.Address().String(), "amount", itoa64(sent[0].Amount))
26}
27
28// Withdraw retire des GNOT DISPONIBLES vers le propriétaire. L'argent réservé
29// aux parties en cours ne peut pas être retiré. amount est en ugnot.
30func Withdraw(cur realm, amount int64) {
31 assertOwner(cur)
32 if amount <= 0 {
33 panic("gnodice: montant invalide")
34 }
35 if amount > available() {
36 panic("gnodice: montant supérieur au solde disponible (hors réserve des parties en cours)")
37 }
38 totalWithdrawn += amount
39 chain.Emit("BankWithdrawn", "to", owner.String(), "amount", itoa64(amount))
40 pay(cur, owner, amount) // en dernier
41 checkLowBankroll()
42}
43
44// Bankroll renvoie le solde total de la banque en ugnot.
45func Bankroll() int64 {
46 return banker.NewReadonlyBanker().GetCoin(realmAddr, Denom)
47}
48
49// Reserved renvoie la somme réservée aux parties en cours, en ugnot.
50func Reserved() int64 {
51 return reserved
52}
53
54// Available renvoie le solde libre (non réservé), en ugnot.
55func Available() int64 {
56 return available()
57}
58
59// MaxCoverableBet renvoie la plus grosse mise (en ugnot) acceptée en ce moment.
60// Une mise b arrive dans la banque puis réserve 5 x b : il faut donc
61// disponible + b >= 5 x b, soit b <= disponible / 4.
62// Renvoie 0 si le jeu est en pause ou si la limite du jour est atteinte.
63func MaxCoverableBet() int64 {
64 if paused || dailyLimitReached(now()) {
65 return 0
66 }
67 max := available() / (PayoutMultiplier - 1)
68 if max > MaxBet {
69 return MaxBet
70 }
71 if max < 0 {
72 return 0
73 }
74 return max
75}
76
77func available() int64 {
78 return Bankroll() - reserved
79}
80
81// rollDay remet le compteur de gains à zéro quand on change de jour.
82func rollDay(t int64) {
83 day := t / 86400
84 if day != payoutDay {
85 payoutDay = day
86 payoutToday = 0
87 }
88}
89
90func dailyLimitReached(t int64) bool {
91 if t/86400 != payoutDay {
92 return false
93 }
94 return payoutToday >= dailyPayoutLimit
95}
96
97// recordPayout compte un gain payé. Coupe-circuit : si les gains du jour
98// dépassent la limite (signe possible d'une attaque ou d'un croupier
99// compromis), le jeu se met en pause tout seul. Le gain en cours est quand
100// même payé : le joueur a gagné honnêtement.
101func recordPayout(amount int64) {
102 rollDay(now())
103 payoutToday += amount
104 if payoutToday >= dailyPayoutLimit && !paused {
105 paused = true
106 chain.Emit("CircuitBreakerTripped", "payoutToday", itoa64(payoutToday), "limit", itoa64(dailyPayoutLimit))
107 chain.Emit("PauseChanged", "paused", "true", "by", "circuit-breaker")
108 }
109}
110
111// checkLowBankroll émet une alerte quand le solde disponible passe sous le seuil.
112func checkLowBankroll() {
113 if a := available(); a < lowBankroll {
114 chain.Emit("BankrollLow", "available", itoa64(a), "threshold", itoa64(lowBankroll))
115 }
116}