Merge branch 'dev'
This commit is contained in:
commit
fe2a91f10b
190 changed files with 29209 additions and 13 deletions
3
.env
Normal file
3
.env
Normal file
|
@ -0,0 +1,3 @@
|
|||
FROSTFS_HUB_IMAGE=truecloudlab/frostfs
|
||||
AIO_IMAGE=truecloudlab/frostfs-aio
|
||||
AIO_VERSION=1.2.8
|
20
.github/workflows/github-actions-demo.yml
vendored
Normal file
20
.github/workflows/github-actions-demo.yml
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
name: GitHub Actions Demo
|
||||
run-name: ${{ github.actor }} is testing out GitHub Actions 🚀
|
||||
on: [push]
|
||||
jobs:
|
||||
init:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [ 18.x ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Starting NodeJs ${{ matrix.node-version}}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: build
|
||||
run: |
|
||||
yarn
|
||||
yarn build
|
||||
working-directory: frontend/casino
|
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
# IDE
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
# tempfiles
|
||||
.DS_Store
|
||||
*~
|
||||
.cache
|
||||
|
||||
temp
|
||||
tmp
|
1
Craps/config.json
Executable file
1
Craps/config.json
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"Craps","abi":{"methods":[{"name":"_deploy","offset":0,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"onNEP17Payment","offset":540,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"playCraps","offset":95,"parameters":[{"name":"bet","type":"Integer"},{"name":"firstSum","type":"Integer"},{"name":"secondSum","type":"Integer"}],"returntype":"Void","safe":false}],"events":[{"name":"Crup number","parameters":[{"name":"int","type":"Integer"}]},{"name":"Random number","parameters":[{"name":"int","type":"Integer"}]},{"name":"gameResult","parameters":[{"name":"int","type":"Integer"}]},{"name":"playerBalance","parameters":[{"name":"int","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null}
|
96
Craps/craps.go
Normal file
96
Craps/craps.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package Craps
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
zaCoinHashKey = "zaCoinHash"
|
||||
)
|
||||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
if isUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
args := data.(struct {
|
||||
zaCoinHash interop.Hash160
|
||||
})
|
||||
|
||||
if len(args.zaCoinHash) != interop.Hash160Len {
|
||||
panic("invalid hash of zaCoin contract")
|
||||
}
|
||||
|
||||
|
||||
ctx := storage.GetContext()
|
||||
storage.Put(ctx, zaCoinHashKey, args.zaCoinHash)
|
||||
}
|
||||
|
||||
func PlayCraps(bet int, firstSum int, secondSum int) {
|
||||
ctx := storage.GetContext()
|
||||
playerOwner := runtime.GetScriptContainer().Sender
|
||||
playerContract := runtime.GetExecutingScriptHash()
|
||||
|
||||
if bet <= 0 {
|
||||
panic("Invalid bet amount")
|
||||
}
|
||||
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
playerBalance := contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
if playerBalance < bet {
|
||||
panic("Insufficient funds")
|
||||
}
|
||||
|
||||
|
||||
isWin := isWinner(firstSum, secondSum)
|
||||
if (isWin){
|
||||
changePlayerBalance(playerContract, playerOwner, bet)
|
||||
runtime.Notify("gameResult", int(1))
|
||||
} else {
|
||||
changePlayerBalance(playerOwner, playerContract, bet)
|
||||
runtime.Notify("gameResult", int(0))
|
||||
}
|
||||
playerBalance = contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
runtime.Notify("playerBalance", playerBalance)
|
||||
}
|
||||
|
||||
func isWinner(firstSum int, secondSum int) bool {
|
||||
if (!((firstSum >= 2 && firstSum <= 12) && (secondSum >= 2 && secondSum <= 12))){
|
||||
panic("first and second sum should be from 2 to 12")
|
||||
}
|
||||
|
||||
sum := 0
|
||||
for i:=0; i<2; i++ {
|
||||
crap := (runtime.GetRandom() % 6) + 1
|
||||
runtime.Notify("Crup number", i+1)
|
||||
runtime.Notify("Random number", crap)
|
||||
sum += crap
|
||||
}
|
||||
|
||||
return sum == firstSum || sum == secondSum
|
||||
}
|
||||
|
||||
|
||||
func OnNEP17Payment(from interop.Hash160, amount int, data any) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
callingHash := runtime.GetCallingScriptHash()
|
||||
if !callingHash.Equals(zaCoinHash) {
|
||||
panic("only ZC is accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func changePlayerBalance(sender interop.Hash160, recipient interop.Hash160, balanceChange int) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
transferred := contract.Call(zaCoinHash, "transfer", contract.All, sender, recipient, balanceChange, nil).(bool)
|
||||
if !transferred {
|
||||
panic("failed to transfer zaCoins")
|
||||
}
|
||||
}
|
||||
|
BIN
Craps/craps.nef
Executable file
BIN
Craps/craps.nef
Executable file
Binary file not shown.
26
Craps/craps.yml
Normal file
26
Craps/craps.yml
Normal file
|
@ -0,0 +1,26 @@
|
|||
name: Craps
|
||||
supportedstandards: []
|
||||
events:
|
||||
- name: "Crup number"
|
||||
parameters:
|
||||
- name: "int"
|
||||
type: "Integer"
|
||||
- name: "Random number"
|
||||
parameters:
|
||||
- name: "int"
|
||||
type: "Integer"
|
||||
- name: "gameResult"
|
||||
parameters:
|
||||
- name: "int"
|
||||
type: "Integer"
|
||||
- name: "playerBalance"
|
||||
parameters:
|
||||
- name: "int"
|
||||
type: "Integer"
|
||||
permissions:
|
||||
- methods: '*'
|
||||
events:
|
||||
- "Crup number"
|
||||
- "Random number"
|
||||
- "playerBalance"
|
||||
- "gameResult"
|
5
Craps/go.mod
Normal file
5
Craps/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module Craps
|
||||
|
||||
go 1.21.4
|
||||
|
||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231121104256-0493ddbd70b2
|
2
Craps/go.sum
Normal file
2
Craps/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231121104256-0493ddbd70b2 h1:hPVF8iMmsQ15GSemj1ma6C9BkwfAugEXsUAVTEniK5M=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231121104256-0493ddbd70b2/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
|
20
README.md
20
README.md
|
@ -1,21 +1,17 @@
|
|||
# Онлайн казино ZaSlot
|
||||
### Проект представляет собой сайт, с возможностью подключить свой кошелек через Neon, пополнить баланс с нашей внутреигровой валютой и поиграть в различные игры (смарт-контракты)
|
||||
|
||||
Весь функционал доступен и из терминала. Вот ряд команд, которые может использовать пользователь, чтобы поиграть в наш проект:
|
||||
### Получение нашей внутреигровой валюты на ваш кошелек
|
||||
* Способ ниже позволяет получить 50 ZaCoin`ов если на кошельке их меньше 5:
|
||||
Весь функционал доступен и из терминала. Ряд команд, для взаимодействия с контрактами представлен ниже
|
||||
### Получение внутреигровой валюты на кошелек
|
||||
* Получение 50 ZaCoin`ов в случае, если на балансе меньше 5 токенов:
|
||||
```
|
||||
neo-go contract invokefunction -r https://testnet2.neo.coz.io:443 -w <json файл вашего кошелька> 66621b53f51303d29a224cb6e5deeec30ad2631b getZaCoin -- '<адрес вашего кошелька>'
|
||||
```
|
||||
* Также можно сделать минт монетки:
|
||||
```
|
||||
neo-go contract invokefunction -r https://testnet2.neo.coz.io:443 -w <json файл вашего кошелька> fdfb5c2974779e9cb9347e083a80054feae55a2d mint hash160:<адрес вашего кошелька>
|
||||
```
|
||||
### Проверка баланса
|
||||
```
|
||||
neo-go wallet nep17 balance -w <json файл вашего кошелька> -r https://testnet2.neo.coz.io:443
|
||||
```
|
||||
### Вызов контрактов с играми:
|
||||
### Игровые контракты:
|
||||
* Камень, ножницы, бумага (или RPS):
|
||||
```
|
||||
neo-go contract invokefunction -r https://testnet2.neo.coz.io:443 -w <json файл вашего кошелька> ae71ad7c8174530cddecf483e8094539f84fcd7f playRPS int:<1 - камень, 2 - ножницы, 3 - бумага> int:<ставка> -- '<адрес вашего кошелька>:CalledByEntry,CustomContracts:fdfb5c2974779e9cb9347e083a80054feae55a2d'
|
||||
|
@ -30,25 +26,23 @@ neo-go contract invokefunction -r https://testnet2.neo.coz.io:443 -w <json фа
|
|||
```
|
||||
* Кости (Craps):
|
||||
```
|
||||
neo-go contract invokefunction -r https://testnet2.neo.coz.io:443 -w <json файл вашего кошелька> 490e3778e5247eccea7ff960e7253047b4c72545 playCraps int:<ставка> int:<число на первой кости> int:<число на второй кости> -- '<адрес вашего кошелька>:CalledByEntry,CustomContracts:fdfb5c2974779e9cb9347e083a80054feae55a2d'
|
||||
neo-go contract invokefunction -r https://testnet2.neo.coz.io:443 -w <json файл вашего кошелька> 490e3778e5247eccea7ff960e7253047b4c72545 playCraps int:<ставка> int:<число от 2 до 12> int:<число от 2 до 12> -- '<адрес вашего кошелька>:CalledByEntry,CustomContracts:fdfb5c2974779e9cb9347e083a80054feae55a2d'
|
||||
```
|
||||
### Посмотреть что возвращает контракт:
|
||||
### Получение уведомлений:
|
||||
```
|
||||
curl -d '{ "jsonrpc": "2.0", "id": 1, "method": "getapplicationlog", "params": ["<хэш транзакции>"] }' https://testnet2.neo.coz.io:443 | json_pp;
|
||||
```
|
||||
|
||||
|
||||
### Что касается так называемого фронтена
|
||||
Сейчас пойдёт маленький ликбез.
|
||||
Была проделана познавательная работа по подключению Neo-блокчейна к клиенту.
|
||||
Первым шагом был поиск необходимого sdk для подключения кошельков. Выбор сразу же упал на @cityofzion/wallet-connect-sdk, но на момент ресёрча, а именно ноябрь 2023 - WalletConnector (кор либы) не работал в РФ уже как 2 месяца и предпосылок к его разблокировке не было, и именно поэтому ресёрч продолжился и ничем хорошим не закончился. Дальше в декабре поступила радостная новость, что библиотека для так называемых децентрализованных приложений наконец-то начала снова работать => окончательный выбор остался на ней (будто бы был выбор).
|
||||
|
||||
Вторым шагом пошла вёрстка всего, ну тут всё понятно
|
||||
|
||||
Третьим шагом началось использование библиотеки вместе с кошельком Neon. Библиотека отваливалась каждый раз, когда ей захочется, но дело было сделано. Далее после долгих попыток вызвать контракт методом тыка (по-другому никак) началась проблема получения данных с транзакции. Оказывается, ни один из существующих эксплореров под нео не предоставляет необходимого Api для этого (???) Поэтому пришлось писать свой слушатель через поллинг к одному из эксплореров.
|
||||
|
||||
#### Теперь к технической части
|
||||
|
||||
Стек: React, TS, @cityofzion/wallet-connect-sdk-react, tailwind
|
||||
|
||||
Вся работа с web3 лежит в папке frontend/casino/web3
|
||||
Вся работа с web3 лежит в папке frontend/casino/web3
|
||||
|
|
1
RPS/config.json
Normal file
1
RPS/config.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"rps","abi":{"methods":[{"name":"_deploy","offset":0,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"onNEP17Payment","offset":538,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"playRPS","offset":95,"parameters":[{"name":"playerChoice","type":"Integer"},{"name":"bet","type":"Integer"}],"returntype":"Void","safe":false}],"events":[{"name":"computerChoice","parameters":[{"name":"int","type":"Integer"}]},{"name":"gameResult","parameters":[{"name":"int","type":"Integer"}]},{"name":"playerBalance","parameters":[{"name":"int","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null}
|
5
RPS/go.mod
Normal file
5
RPS/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module rps
|
||||
|
||||
go 1.21.5
|
||||
|
||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0
|
2
RPS/go.sum
Normal file
2
RPS/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0 h1:N+dMIBmteXjJpkH6UZ7HmNftuFxkqszfGLbhsEctnv0=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231127165613-b35f351f0ba0/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
|
110
RPS/rps.go
Normal file
110
RPS/rps.go
Normal file
|
@ -0,0 +1,110 @@
|
|||
package rps
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
zaCoinHashKey = "zaCoinHash"
|
||||
)
|
||||
|
||||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
if isUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
args := data.(struct {
|
||||
zaCoinHash interop.Hash160
|
||||
})
|
||||
|
||||
if len(args.zaCoinHash) != interop.Hash160Len {
|
||||
panic("Invalid hash of zaCoin contract")
|
||||
}
|
||||
|
||||
ctx := storage.GetContext()
|
||||
storage.Put(ctx, zaCoinHashKey, args.zaCoinHash)
|
||||
}
|
||||
|
||||
func PlayRPS(playerChoice int, bet int) {
|
||||
|
||||
ctx := storage.GetContext()
|
||||
playerOwner := runtime.GetScriptContainer().Sender
|
||||
playerContract := runtime.GetExecutingScriptHash()
|
||||
|
||||
if bet <= 0 {
|
||||
panic("Invalid bet amount")
|
||||
}
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
playerBalance := contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
if playerBalance < bet {
|
||||
panic("Insufficient funds")
|
||||
}
|
||||
|
||||
if playerChoice <= 0 || playerChoice > 3 {
|
||||
panic("invalid player choice")
|
||||
}
|
||||
|
||||
computerChoice := (runtime.GetRandom() % 3) + 1
|
||||
runtime.Notify("computerChoice", computerChoice)
|
||||
|
||||
result := isWinner(playerChoice, computerChoice)
|
||||
|
||||
if result == 1 {
|
||||
changePlayerBalance(playerContract, playerOwner, bet)
|
||||
runtime.Notify("gameResult", result)
|
||||
} else if result == 0 {
|
||||
changePlayerBalance(playerOwner, playerContract, bet)
|
||||
runtime.Notify("gameResult", result)
|
||||
} else {
|
||||
runtime.Log("game tied")
|
||||
runtime.Notify("gameResult", result)
|
||||
}
|
||||
|
||||
playerBalance = contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
runtime.Notify("playerBalance", playerBalance)
|
||||
}
|
||||
|
||||
func isWinner(playerChoice int, computerChoice int) int {
|
||||
|
||||
if playerChoice == computerChoice {
|
||||
return 2
|
||||
}
|
||||
|
||||
if playerChoice == 1 && computerChoice == 3 {
|
||||
return 1
|
||||
}
|
||||
|
||||
if playerChoice == 3 && computerChoice == 2 {
|
||||
return 1
|
||||
}
|
||||
|
||||
if playerChoice == 2 && computerChoice == 1 {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func OnNEP17Payment(from interop.Hash160, amount int, data any) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
callingHash := runtime.GetCallingScriptHash()
|
||||
if !callingHash.Equals(zaCoinHash) {
|
||||
panic("only ZC is accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func changePlayerBalance(sender interop.Hash160, recipient interop.Hash160, balanceChange int) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
transferred := contract.Call(zaCoinHash, "transfer", contract.All, sender, recipient, balanceChange, nil).(bool)
|
||||
if !transferred {
|
||||
panic("failed to transfer zaCoins")
|
||||
}
|
||||
}
|
BIN
RPS/rps.nef
Normal file
BIN
RPS/rps.nef
Normal file
Binary file not shown.
21
RPS/rps.yml
Normal file
21
RPS/rps.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
name: rps
|
||||
supportedstandards: []
|
||||
events:
|
||||
- name: computerChoice
|
||||
parameters:
|
||||
- name: int
|
||||
type: Integer
|
||||
- name: gameResult
|
||||
parameters:
|
||||
- name: int
|
||||
type: Integer
|
||||
- name: playerBalance
|
||||
parameters:
|
||||
- name: int
|
||||
type: Integer
|
||||
permissions:
|
||||
- methods: '*'
|
||||
events:
|
||||
- computerChoice
|
||||
- playerBalance
|
||||
- gameResult
|
100
Roulette/Roulette.go
Normal file
100
Roulette/Roulette.go
Normal file
|
@ -0,0 +1,100 @@
|
|||
package Roulette
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
zaCoinHashKey = "zaCoinHash"
|
||||
)
|
||||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
if isUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
args := data.(struct {
|
||||
zaCoinHash interop.Hash160
|
||||
})
|
||||
|
||||
if len(args.zaCoinHash) != interop.Hash160Len {
|
||||
panic("invalid hash of zaCoin contract")
|
||||
}
|
||||
|
||||
ctx := storage.GetContext()
|
||||
storage.Put(ctx, zaCoinHashKey, args.zaCoinHash)
|
||||
}
|
||||
|
||||
func PlayRoulette(bet int, selectedNumber int) {
|
||||
ctx := storage.GetContext()
|
||||
playerOwner := runtime.GetScriptContainer().Sender
|
||||
playerContract := runtime.GetExecutingScriptHash()
|
||||
|
||||
if bet <= 0 {
|
||||
panic("Invalid bet amount")
|
||||
}
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
playerBalance := contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
if playerBalance < bet {
|
||||
panic("Insufficient funds")
|
||||
}
|
||||
|
||||
if selectedNumber < 1 || selectedNumber > 36 {
|
||||
panic("Illegal number selected for roulette")
|
||||
}
|
||||
|
||||
isWin := isWinner(selectedNumber)
|
||||
if isWin {
|
||||
winAmount := calculateWinAmount(bet, selectedNumber)
|
||||
changePlayerBalance(playerContract, playerOwner, winAmount)
|
||||
runtime.Notify("gameResult", int(1))
|
||||
} else {
|
||||
changePlayerBalance(playerOwner, playerContract, bet)
|
||||
runtime.Notify("gameResult", int(0))
|
||||
}
|
||||
playerBalance = contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
runtime.Notify("playerBalance", playerBalance)
|
||||
}
|
||||
|
||||
func isWinner(selectedNumber int) bool {
|
||||
rouletteNumber := (runtime.GetRandom() % 36) + 1
|
||||
runtime.Notify("rouletteNumber", rouletteNumber)
|
||||
runtime.Log("rouletteNumber " + string(rouletteNumber))
|
||||
return rouletteNumber == selectedNumber
|
||||
}
|
||||
|
||||
func calculateWinAmount(bet int, selectedNumber int) int {
|
||||
coefs := []int{10, 20, 30, 2}
|
||||
if selectedNumber == 36 {
|
||||
return bet * coefs[0]
|
||||
} else if selectedNumber == 18 {
|
||||
return bet * coefs[1]
|
||||
} else if selectedNumber == 2 {
|
||||
return bet * coefs[2]
|
||||
} else {
|
||||
return bet * coefs[3]
|
||||
}
|
||||
}
|
||||
|
||||
func OnNEP17Payment(from interop.Hash160, amount int, data any) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
callingHash := runtime.GetCallingScriptHash()
|
||||
if !callingHash.Equals(zaCoinHash) {
|
||||
panic("only ZC is accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func changePlayerBalance(sender interop.Hash160, recipient interop.Hash160, balanceChange int) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
transferred := contract.Call(zaCoinHash, "transfer", contract.All, sender, recipient, balanceChange, nil).(bool)
|
||||
if !transferred {
|
||||
panic("failed to transfer zaCoins")
|
||||
}
|
||||
}
|
1
Roulette/config.json
Normal file
1
Roulette/config.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"Roulette","abi":{"methods":[{"name":"_deploy","offset":0,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"onNEP17Payment","offset":562,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"playRoulette","offset":95,"parameters":[{"name":"bet","type":"Integer"},{"name":"selectedNumber","type":"Integer"}],"returntype":"Void","safe":false}],"events":[{"name":"rouletteNumber","parameters":[{"name":"int","type":"Integer"}]},{"name":"gameResult","parameters":[{"name":"int","type":"Integer"}]},{"name":"playerBalance","parameters":[{"name":"int","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null}
|
5
Roulette/go.mod
Normal file
5
Roulette/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module Roulette
|
||||
|
||||
go 1.21.5
|
||||
|
||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231214154544-3766206f441a
|
2
Roulette/go.sum
Normal file
2
Roulette/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231214154544-3766206f441a h1:1AVlB7TD9HC+PutOO1rzNhJ/6t1ieGX1ULIj2ReH+g4=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231214154544-3766206f441a/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
|
BIN
Roulette/roulette.nef
Executable file
BIN
Roulette/roulette.nef
Executable file
Binary file not shown.
21
Roulette/roulette.yml
Normal file
21
Roulette/roulette.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
name: Roulette
|
||||
supportedstandards: []
|
||||
events:
|
||||
- name: "rouletteNumber"
|
||||
parameters:
|
||||
- name: "int"
|
||||
type: "Integer"
|
||||
- name: "gameResult"
|
||||
parameters:
|
||||
- name: "int"
|
||||
type: "Integer"
|
||||
- name: "playerBalance"
|
||||
parameters:
|
||||
- name: "int"
|
||||
type: "Integer"
|
||||
permissions:
|
||||
- methods: '*'
|
||||
events:
|
||||
- "rouletteNumber"
|
||||
- "gameResult"
|
||||
- "playerBalance"
|
1
SlotMashine/config.json
Executable file
1
SlotMashine/config.json
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"SlotMashine","abi":{"methods":[{"name":"_deploy","offset":0,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"onNEP17Payment","offset":517,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false},{"name":"rollSlot","offset":95,"parameters":[{"name":"bet","type":"Integer"}],"returntype":"Void","safe":false}],"events":[{"name":"SlotResult","parameters":[{"name":"array","type":"Array"}]},{"name":"gameResult","parameters":[{"name":"int","type":"Integer"}]},{"name":"playerBalance","parameters":[{"name":"int","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null}
|
21
SlotMashine/config.yml
Normal file
21
SlotMashine/config.yml
Normal file
|
@ -0,0 +1,21 @@
|
|||
name: SlotMashine
|
||||
supportedstandards: []
|
||||
events:
|
||||
- name: SlotResult
|
||||
parameters:
|
||||
- name: array
|
||||
type: Array
|
||||
- name: gameResult
|
||||
parameters:
|
||||
- name: int
|
||||
type: Integer
|
||||
- name: playerBalance
|
||||
parameters:
|
||||
- name: int
|
||||
type: Integer
|
||||
permissions:
|
||||
- methods: '*'
|
||||
events:
|
||||
- SlotResult
|
||||
- gameResult
|
||||
- playerBalance
|
5
SlotMashine/go.mod
Normal file
5
SlotMashine/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module SlotMashine
|
||||
|
||||
go 1.21.5
|
||||
|
||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231214154544-3766206f441a
|
2
SlotMashine/go.sum
Normal file
2
SlotMashine/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231214154544-3766206f441a h1:1AVlB7TD9HC+PutOO1rzNhJ/6t1ieGX1ULIj2ReH+g4=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231214154544-3766206f441a/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
|
98
SlotMashine/slot.go
Normal file
98
SlotMashine/slot.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package SlotMashine
|
||||
|
||||
import (
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
zaCoinHashKey = "zaCoinHash"
|
||||
)
|
||||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
if isUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
args := data.(struct {
|
||||
zaCoinHash interop.Hash160
|
||||
})
|
||||
|
||||
if len(args.zaCoinHash) != interop.Hash160Len {
|
||||
panic("Invalid hash of zaCoin contract")
|
||||
}
|
||||
|
||||
|
||||
ctx := storage.GetContext()
|
||||
storage.Put(ctx, zaCoinHashKey, args.zaCoinHash)
|
||||
}
|
||||
|
||||
func RollSlot(bet int) {
|
||||
ctx := storage.GetContext()
|
||||
playerOwner := runtime.GetScriptContainer().Sender
|
||||
playerContract := runtime.GetExecutingScriptHash()
|
||||
|
||||
if bet <= 0 {
|
||||
panic("Invalid bet amount")
|
||||
}
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
playerBalance := contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
|
||||
if playerBalance < bet {
|
||||
panic("Insufficient funds")
|
||||
}
|
||||
|
||||
res := roll()
|
||||
if (res == 0){
|
||||
changePlayerBalance(playerOwner, playerContract, bet)
|
||||
runtime.Notify("gameResult", int(0))
|
||||
} else {
|
||||
win := res * bet
|
||||
changePlayerBalance(playerContract, playerOwner, win)
|
||||
runtime.Notify("gameResult", int(1))
|
||||
}
|
||||
playerBalance = contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
runtime.Notify("playerBalance", playerBalance)
|
||||
}
|
||||
|
||||
func roll() int {
|
||||
var result []int
|
||||
for i:=0; i<3; i++ {
|
||||
wheel := (runtime.GetRandom() % 8) + 1
|
||||
result = append(result, wheel)
|
||||
runtime.Log("WheelNumber=" + string(i + 1) +", value="+string(wheel))
|
||||
}
|
||||
runtime.Notify("SlotResult", result)
|
||||
|
||||
|
||||
if (result[0] == result[1] && result[0] == result[2]){
|
||||
return result[0]
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
func OnNEP17Payment(from interop.Hash160, amount int, data any) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
callingHash := runtime.GetCallingScriptHash()
|
||||
if !callingHash.Equals(zaCoinHash) {
|
||||
panic("Only ZC is accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func changePlayerBalance(sender interop.Hash160, recipient interop.Hash160, balanceChange int) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
transferred := contract.Call(zaCoinHash, "transfer", contract.All, sender, recipient, balanceChange, nil).(bool)
|
||||
if !transferred {
|
||||
panic("failed to transfer zaCoins")
|
||||
}
|
||||
}
|
BIN
SlotMashine/slot.nef
Executable file
BIN
SlotMashine/slot.nef
Executable file
Binary file not shown.
1
ZaCoin/config.json
Executable file
1
ZaCoin/config.json
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"ZaCoin","abi":{"methods":[{"name":"balanceOf","offset":583,"parameters":[{"name":"holder","type":"Hash160"}],"returntype":"Integer","safe":true},{"name":"decimals","offset":559,"parameters":[],"returntype":"Integer","safe":true},{"name":"isUsableAddress","offset":254,"parameters":[{"name":"addr","type":"ByteArray"}],"returntype":"Boolean","safe":false},{"name":"mint","offset":633,"parameters":[{"name":"to","type":"Hash160"}],"returntype":"Void","safe":false},{"name":"symbol","offset":554,"parameters":[],"returntype":"String","safe":true},{"name":"totalSupply","offset":564,"parameters":[],"returntype":"Integer","safe":true},{"name":"transfer","offset":606,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Boolean","safe":false}],"events":[{"name":"Transfer","parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":["onNEP17Payment"]}],"supportedstandards":["NEP-17"],"trusts":[],"extra":null}
|
14
ZaCoin/config.yml
Normal file
14
ZaCoin/config.yml
Normal file
|
@ -0,0 +1,14 @@
|
|||
name: "ZaCoin"
|
||||
supportedstandards: ["NEP-17"]
|
||||
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply"]
|
||||
events:
|
||||
- name: Transfer
|
||||
parameters:
|
||||
- name: from
|
||||
type: Hash160
|
||||
- name: to
|
||||
type: Hash160
|
||||
- name: amount
|
||||
type: Integer
|
||||
permissions:
|
||||
- methods: ["onNEP17Payment"]
|
5
ZaCoin/go.mod
Normal file
5
ZaCoin/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module ZaCoin
|
||||
|
||||
go 1.21.4
|
||||
|
||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5
|
2
ZaCoin/go.sum
Normal file
2
ZaCoin/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5 h1:09CpI5uwsxb1EeFPIKQRwwWlfCmDD/Dwwh01lPiQScM=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
|
160
ZaCoin/token.go
Normal file
160
ZaCoin/token.go
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
The implementation of the token logic has been copied "as is" from neo-go:
|
||||
https://github.com/nspcc-dev/neo-go/blob/7a1bf77585a37302ddfae78b2c88ed472f66cbcc/examples/token/nep17/nep17.go
|
||||
|
||||
The code is subject to the following license:
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2023 NeoSPCC (@nspcc-dev), Anthony De Meulemeester (@anthdm), City of Zion community (@CityOfZion)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
package ZaCoin
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
// Token holds all token info
|
||||
type Token struct {
|
||||
// Token name
|
||||
Name string
|
||||
// Ticker symbol
|
||||
Symbol string
|
||||
// Amount of decimals
|
||||
Decimals int
|
||||
// Token owner address
|
||||
Owner []byte
|
||||
// Total tokens * multiplier
|
||||
TotalSupply int
|
||||
// Storage key for circulation value
|
||||
CirculationKey string
|
||||
}
|
||||
|
||||
// getIntFromDB is a helper that checks for nil result of storage.Get and returns
|
||||
// zero as the default value.
|
||||
func getIntFromDB(ctx storage.Context, key []byte) int {
|
||||
var res int
|
||||
val := storage.Get(ctx, key)
|
||||
if val != nil {
|
||||
res = val.(int)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetSupply gets the token totalSupply value from VM storage
|
||||
func (t Token) GetSupply(ctx storage.Context) int {
|
||||
return getIntFromDB(ctx, []byte(t.CirculationKey))
|
||||
}
|
||||
|
||||
// BalanceOf gets the token balance of a specific address
|
||||
func (t Token) BalanceOf(ctx storage.Context, holder []byte) int {
|
||||
return getIntFromDB(ctx, holder)
|
||||
}
|
||||
|
||||
// Transfer token from one user to another
|
||||
func (t Token) Transfer(ctx storage.Context, from, to interop.Hash160, amount int, data any) bool {
|
||||
amountFrom := t.CanTransfer(ctx, from, to, amount)
|
||||
if amountFrom == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
if amountFrom == 0 {
|
||||
storage.Delete(ctx, from)
|
||||
}
|
||||
|
||||
if amountFrom > 0 {
|
||||
diff := amountFrom - amount
|
||||
storage.Put(ctx, from, diff)
|
||||
}
|
||||
|
||||
amountTo := getIntFromDB(ctx, to)
|
||||
totalAmountTo := amountTo + amount
|
||||
if totalAmountTo != 0 {
|
||||
storage.Put(ctx, to, totalAmountTo)
|
||||
}
|
||||
|
||||
runtime.Notify("Transfer", from, to, amount)
|
||||
if to != nil && management.GetContract(to) != nil {
|
||||
contract.Call(to, "onNEP17Payment", contract.All, from, amount, data)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CanTransfer returns the amount it can transfer
|
||||
func (t Token) CanTransfer(ctx storage.Context, from []byte, to []byte, amount int) int {
|
||||
if len(to) != 20 || !IsUsableAddress(from) {
|
||||
return -1
|
||||
}
|
||||
|
||||
amountFrom := getIntFromDB(ctx, from)
|
||||
if amountFrom < amount {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Tell Transfer the result is equal - special case since it uses Delete
|
||||
if amountFrom == amount {
|
||||
return 0
|
||||
}
|
||||
|
||||
// return amountFrom value back to Transfer, reduces extra Get
|
||||
return amountFrom
|
||||
}
|
||||
|
||||
// IsUsableAddress checks if the sender is either the correct Neo address or SC address
|
||||
func IsUsableAddress(addr []byte) bool {
|
||||
if len(addr) == 20 {
|
||||
|
||||
if runtime.CheckWitness(addr) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if a smart contract is calling scripthash
|
||||
callingScriptHash := runtime.GetCallingScriptHash()
|
||||
if callingScriptHash.Equals(addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Mint initial supply of tokens
|
||||
func (t Token) Mint(ctx storage.Context, to interop.Hash160) bool {
|
||||
if !IsUsableAddress(t.Owner) {
|
||||
return false
|
||||
}
|
||||
minted := storage.Get(ctx, []byte("minted"))
|
||||
if minted != nil && minted.(bool) == true {
|
||||
return false
|
||||
}
|
||||
|
||||
storage.Put(ctx, to, t.TotalSupply)
|
||||
storage.Put(ctx, []byte("minted"), true)
|
||||
storage.Put(ctx, []byte(t.CirculationKey), t.TotalSupply)
|
||||
var from interop.Hash160
|
||||
runtime.Notify("Transfer", from, to, t.TotalSupply)
|
||||
return true
|
||||
}
|
BIN
ZaCoin/zacoin.nef
Executable file
BIN
ZaCoin/zacoin.nef
Executable file
Binary file not shown.
57
ZaCoin/zacoin_contract.go
Normal file
57
ZaCoin/zacoin_contract.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package ZaCoin
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/lib/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
func getToken() Token {
|
||||
owner := address.ToHash160("NXbLSnHA8dNuMUPUSNNivx7XFucN1w5bRq")
|
||||
token := Token{
|
||||
Name: "ZaCoin",
|
||||
Symbol: "ZC",
|
||||
Decimals: 0,
|
||||
Owner: owner,
|
||||
TotalSupply: 1000000,
|
||||
CirculationKey: "TokenCirculation",
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
// Symbol returns the token symbol.
|
||||
func Symbol() string {
|
||||
return getToken().Symbol
|
||||
}
|
||||
|
||||
// Decimals returns the number of digits after decimal point.
|
||||
func Decimals() int {
|
||||
return getToken().Decimals
|
||||
}
|
||||
|
||||
// TotalSupply returns the total amount of tokens.
|
||||
func TotalSupply() int {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
return getToken().GetSupply(ctx)
|
||||
}
|
||||
|
||||
// BalanceOf returns the amount of tokens owned by the specified address.
|
||||
func BalanceOf(holder interop.Hash160) int {
|
||||
ctx := storage.GetReadOnlyContext()
|
||||
return getToken().BalanceOf(ctx, holder)
|
||||
}
|
||||
|
||||
// Transfer moves token from one address to another.
|
||||
func Transfer(from interop.Hash160, to interop.Hash160, amount int, data any) bool {
|
||||
ctx := storage.GetContext()
|
||||
return getToken().Transfer(ctx, from, to, amount, data)
|
||||
}
|
||||
|
||||
// Mint generates initial supply of tokens.
|
||||
func Mint(to interop.Hash160) {
|
||||
ctx := storage.GetContext()
|
||||
minted := getToken().Mint(ctx, to)
|
||||
if !minted {
|
||||
panic("Failed to mint initial supply")
|
||||
}
|
||||
}
|
1
ZaCoinRefill/config.json
Executable file
1
ZaCoinRefill/config.json
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"ZaCoinRefill","abi":{"methods":[{"name":"_deploy","offset":0,"parameters":[{"name":"data","type":"Any"},{"name":"isUpdate","type":"Boolean"}],"returntype":"Void","safe":false},{"name":"getZaCoin","offset":95,"parameters":[],"returntype":"Void","safe":false},{"name":"onNEP17Payment","offset":244,"parameters":[{"name":"from","type":"Hash160"},{"name":"amount","type":"Integer"},{"name":"data","type":"Any"}],"returntype":"Void","safe":false}],"events":[{"name":"playerBalance","parameters":[{"name":"int","type":"Integer"}]}]},"features":{},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"extra":null}
|
5
ZaCoinRefill/go.mod
Normal file
5
ZaCoinRefill/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
|||
module ZaCoinRefill
|
||||
|
||||
go 1.21.6
|
||||
|
||||
require github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5
|
2
ZaCoinRefill/go.sum
Normal file
2
ZaCoinRefill/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5 h1:09CpI5uwsxb1EeFPIKQRwwWlfCmDD/Dwwh01lPiQScM=
|
||||
github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20231020160724-c3955f87d1b5/go.mod h1:J/Mk6+nKeKSW4wygkZQFLQ6SkLOSGX5Ga0RuuuktEag=
|
68
ZaCoinRefill/zaCoinRefill.go
Normal file
68
ZaCoinRefill/zaCoinRefill.go
Normal file
|
@ -0,0 +1,68 @@
|
|||
package ZaCoinRefill
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
zaCoinHashKey = "zaCoinHash"
|
||||
minZaCoin = 5
|
||||
zaCoinForTransfer = 50
|
||||
)
|
||||
|
||||
|
||||
func _deploy(data interface{}, isUpdate bool) {
|
||||
if isUpdate {
|
||||
return
|
||||
}
|
||||
|
||||
args := data.(struct {
|
||||
zaCoinHash interop.Hash160
|
||||
})
|
||||
|
||||
if len(args.zaCoinHash) != interop.Hash160Len {
|
||||
panic("Invalid hash of zaCoin contract")
|
||||
}
|
||||
|
||||
ctx := storage.GetContext()
|
||||
storage.Put(ctx, zaCoinHashKey, args.zaCoinHash)
|
||||
}
|
||||
|
||||
func GetZaCoin() {
|
||||
ctx := storage.GetContext()
|
||||
playerOwner := runtime.GetScriptContainer().Sender
|
||||
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
playerBalance := contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
if playerBalance < minZaCoin {
|
||||
changePlayerBalance(ctx, playerOwner, zaCoinForTransfer)
|
||||
}
|
||||
|
||||
playerBalance = contract.Call(zaCoinHash, "balanceOf", contract.ReadStates, playerOwner).(int)
|
||||
runtime.Notify("playerBalance", playerBalance)
|
||||
|
||||
}
|
||||
|
||||
func OnNEP17Payment(from interop.Hash160, amount int, data any) {
|
||||
ctx := storage.GetContext()
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
|
||||
callingHash := runtime.GetCallingScriptHash()
|
||||
if !callingHash.Equals(zaCoinHash) {
|
||||
panic("only ZC is accepted")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func changePlayerBalance(ctx storage.Context, playerOwner interop.Hash160, transferAmount int) {
|
||||
zaCoinHash := storage.Get(ctx, zaCoinHashKey).(interop.Hash160)
|
||||
playerContract := runtime.GetExecutingScriptHash()
|
||||
|
||||
transferred := contract.Call(zaCoinHash, "transfer", contract.All, playerContract, playerOwner, transferAmount, nil).(bool)
|
||||
if !transferred {
|
||||
panic("failed to transfer zaCoins")
|
||||
}
|
||||
}
|
BIN
ZaCoinRefill/zaCoinRefill.nef
Executable file
BIN
ZaCoinRefill/zaCoinRefill.nef
Executable file
Binary file not shown.
11
ZaCoinRefill/zaCoinRefill.yml
Normal file
11
ZaCoinRefill/zaCoinRefill.yml
Normal file
|
@ -0,0 +1,11 @@
|
|||
name: ZaCoinRefill
|
||||
supportedstandards: []
|
||||
events:
|
||||
- name: "playerBalance"
|
||||
parameters:
|
||||
- name: "int"
|
||||
type: "Integer"
|
||||
permissions:
|
||||
- methods: "*"
|
||||
events:
|
||||
- "playerBalance"
|
18
frontend/casino/.eslintrc.cjs
Normal file
18
frontend/casino/.eslintrc.cjs
Normal file
|
@ -0,0 +1,18 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
24
frontend/casino/.gitignore
vendored
Normal file
24
frontend/casino/.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
30
frontend/casino/README.md
Normal file
30
frontend/casino/README.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default {
|
||||
// other rules...
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
13
frontend/casino/index.html
Normal file
13
frontend/casino/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
16933
frontend/casino/package-lock.json
generated
Normal file
16933
frontend/casino/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
68
frontend/casino/package.json
Normal file
68
frontend/casino/package.json
Normal file
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite --host",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"lint:fix": "eslint --fix \"**/*.{ts,tsx}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^2.8.2",
|
||||
"@cityofzion/wallet-connect-sdk-react": "^3.2.0",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@metamask/rpc-errors": "^6.1.0",
|
||||
"@mui/icons-material": "^5.14.19",
|
||||
"@mui/material": "^5.14.19",
|
||||
"@nextui-org/react": "^1.0.0-beta.10",
|
||||
"@stitches/react": "^1.2.8",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"axios": "^1.6.5",
|
||||
"eslint-plugin-mobx": "^0.0.9",
|
||||
"framer-motion": "^10.16.12",
|
||||
"jdenticon": "^3.2.0",
|
||||
"luxon": "^3.4.4",
|
||||
"mobx": "^6.12.0",
|
||||
"mobx-react-lite": "^4.0.5",
|
||||
"postcss": "^8.4.32",
|
||||
"react": "^18.2.0",
|
||||
"react-aria": "^3.30.0",
|
||||
"react-datepicker": "^4.24.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.48.2",
|
||||
"react-number-format": "^5.3.1",
|
||||
"react-router-dom": "^6.20.0",
|
||||
"react-stately": "^3.28.0",
|
||||
"reseter.css": "^2.0.0",
|
||||
"swagger-typescript-api": "^12.0.2",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"viem": "^1.12.1",
|
||||
"wagmi": "1.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/luxon": "^3.3.7",
|
||||
"@types/react": "^18.2.37",
|
||||
"@types/react-datepicker": "^4.19.3",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-n": "^16.3.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-storybook": "^0.6.15",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"prettier": "^3.1.0",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
6
frontend/casino/postcss.config.js
Normal file
6
frontend/casino/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
1
frontend/casino/public/vite.svg
Normal file
1
frontend/casino/public/vite.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
12
frontend/casino/src/App.tsx
Normal file
12
frontend/casino/src/App.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { RouterProvider } from 'react-router-dom'
|
||||
|
||||
import { Providers } from './providers.tsx'
|
||||
import {router} from "./app/pages/router.tsx";
|
||||
|
||||
export const App = function App() {
|
||||
return (
|
||||
<Providers>
|
||||
<RouterProvider router={router} />
|
||||
</Providers>
|
||||
)
|
||||
}
|
0
frontend/casino/src/app/UIkit/index.ts
Normal file
0
frontend/casino/src/app/UIkit/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { observer } from 'mobx-react-lite'
|
||||
import {type FC} from 'react'
|
||||
import { Outlet } from 'react-router-dom'
|
||||
|
||||
import { useScrollTop } from '../../../hooks/useScrollTop.ts'
|
||||
import { AppNav } from '../AppNav'
|
||||
import { Footer } from '../Footer/Footer.tsx'
|
||||
|
||||
export const AppLayout: FC = observer(() => {
|
||||
useScrollTop()
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppNav />
|
||||
<Outlet />
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
})
|
|
@ -0,0 +1 @@
|
|||
export * from './AppLayout.tsx'
|
37
frontend/casino/src/app/components/App/AppNav/AppNav.tsx
Normal file
37
frontend/casino/src/app/components/App/AppNav/AppNav.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import CoinIcon from "../../icons/CoinIcon";
|
||||
import {ConnectStateButton} from "../../../web3/connect";
|
||||
import chipImg from '../../../../assets/icons/chip.png'
|
||||
import {useStores} from "../../../hooks/useStores.tsx";
|
||||
import {observer} from "mobx-react-lite";
|
||||
import {CheckBalanceButton} from "../../../web3/balance/CheckBalanceButton.tsx";
|
||||
import {FaucetButton} from "../../web3/FaucetButton.tsx";
|
||||
|
||||
export const AppNav = observer(() => {
|
||||
const { userStore } = useStores()
|
||||
return (
|
||||
<div style={{ height: 'max-content' }} className="bg-[#30333C] text-white py-2 z-50 sticky top-0">
|
||||
<div className="container mx-auto">
|
||||
<div className="flex flex-row justify-between">
|
||||
<a href="/" className="flex flex-row items-center">
|
||||
<img src={chipImg} alt="Icon" className="w-[53px] h-[53px] mr-[15px]" />
|
||||
<span className="font-semibold text-3xl">
|
||||
<span className="bg-gradient-to-b from-white via-blue-400 to-red-400 bg-clip-text text-transparent">Z</span>aSlot
|
||||
<span className="text-gray-400">.bet</span>
|
||||
</span>
|
||||
</a>
|
||||
<div className="flex flex-row gap-8">
|
||||
<div className="rounded-[100px] bg-[#2D313D] flex flex-row items-center justify-between shadow-inset">
|
||||
<div className="flex flex-row gap-[3px] items-center px-[15px] py-[11px]">
|
||||
<CoinIcon />
|
||||
{userStore.balance ? <span> {
|
||||
parseFloat(userStore.balance) > 5 ? userStore.balance : <FaucetButton/>
|
||||
}</span> : <CheckBalanceButton/>}
|
||||
</div>
|
||||
<ConnectStateButton/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
1
frontend/casino/src/app/components/App/AppNav/index.ts
Normal file
1
frontend/casino/src/app/components/App/AppNav/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './AppNav.tsx'
|
38
frontend/casino/src/app/components/App/Footer/Footer.tsx
Normal file
38
frontend/casino/src/app/components/App/Footer/Footer.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
export const Footer = () => {
|
||||
return (
|
||||
<div style={{ height: 'max-content ' }} className="flex flex-row justify-between px-[290px] py-[50px] bg-gradient-to-b from-[#222630] via-gray-900 to-[#171A21] text-[#959CAF]" >
|
||||
<div className="flex flex-col gap-[18px]">
|
||||
<a href="#" className="underline">Privacy policy</a>
|
||||
<a href="#" className="underline">Terms of use</a>
|
||||
</div>
|
||||
<div className="flex flex-col gap-[18px]">
|
||||
<p className="uppercase font-semibold">Contacts</p>
|
||||
<div className="flex flex-row gap-[5px]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22" fill="none">
|
||||
<path d="M14.6667 1.83337H7.33333C6.8471 1.83337 6.38079 2.02653 6.03697 2.37034C5.69315 2.71416 5.5 3.18048 5.5 3.66671V18.3334C5.5 18.8196 5.69315 19.2859 6.03697 19.6297C6.38079 19.9736 6.8471 20.1667 7.33333 20.1667H14.6667C15.1529 20.1667 15.6192 19.9736 15.963 19.6297C16.3068 19.2859 16.5 18.8196 16.5 18.3334V3.66671C16.5 3.18048 16.3068 2.71416 15.963 2.37034C15.6192 2.02653 15.1529 1.83337 14.6667 1.83337ZM11.9167 19.25H10.0833V18.3334H11.9167V19.25ZM14.6667 17.4167H7.33333V4.58337H14.6667V17.4167Z" fill="#959CAF" />
|
||||
</svg>
|
||||
<a type="tel" className="underline">8 (999) 888-88-88</a>
|
||||
</div>
|
||||
<div className="flex flex-row gap-[5px]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22" fill="none">
|
||||
<g clip-path="url(#clip0_37_332)">
|
||||
<path d="M12.0872 0.423909C12.859 0.420947 13.6309 0.428704 14.4025 0.447178L14.6077 0.454582C14.8446 0.463043 15.0784 0.47362 15.3608 0.486313C16.4861 0.539197 17.254 0.71689 17.9278 0.97814C18.6259 1.24679 19.2139 1.61064 19.802 2.19872C20.3397 2.72715 20.7559 3.36635 21.0215 4.07189C21.2828 4.74564 21.4605 5.51458 21.5134 6.63997C21.5261 6.92131 21.5366 7.15612 21.5451 7.39304L21.5514 7.59824C21.5702 8.3695 21.5783 9.14098 21.5758 9.91247L21.5768 10.7015V12.0871C21.5794 12.8589 21.5713 13.6308 21.5525 14.4024L21.5461 14.6076C21.5377 14.8445 21.5271 15.0782 21.5144 15.3606C21.4615 16.486 21.2817 17.2539 21.0215 17.9277C20.7567 18.6339 20.3405 19.2737 19.802 19.8019C19.2731 20.3395 18.6336 20.7557 17.9278 21.0214C17.254 21.2827 16.4861 21.4604 15.3608 21.5132C15.0784 21.5259 14.8446 21.5365 14.6077 21.545L14.4025 21.5513C13.6309 21.5701 12.859 21.5782 12.0872 21.5756L11.2982 21.5767H9.91365C9.14181 21.5793 8.36997 21.5712 7.59836 21.5524L7.39317 21.546C7.14208 21.5369 6.89105 21.5263 6.64009 21.5143C5.5147 21.4614 4.74682 21.2816 4.07201 21.0214C3.3662 20.7563 2.72688 20.3401 2.19884 19.8019C1.66052 19.2733 1.24398 18.6337 0.978262 17.9277C0.717012 17.2539 0.53932 16.486 0.486435 15.3606C0.474655 15.1097 0.464078 14.8586 0.454704 14.6076L0.449416 14.4024C0.429916 13.6308 0.421101 12.8589 0.422974 12.0871V9.91247C0.420022 9.14099 0.427779 8.3695 0.446243 7.59824L0.453647 7.39304C0.462108 7.15612 0.472685 6.92131 0.485377 6.63997C0.538262 5.51352 0.715954 4.7467 0.977204 4.07189C1.24308 3.36601 1.66044 2.72698 2.1999 2.19977C2.72757 1.66115 3.36649 1.24423 4.07201 0.97814C4.74682 0.71689 5.51365 0.539197 6.64009 0.486313L7.39317 0.454582L7.59836 0.449294C8.36961 0.429803 9.14109 0.420988 9.91259 0.422851L12.0872 0.423909ZM10.9999 5.71237C10.2992 5.70246 9.60349 5.83191 8.95324 6.09321C8.30299 6.35451 7.71116 6.74243 7.21214 7.23445C6.71312 7.72646 6.31686 8.31275 6.04639 8.95924C5.77593 9.60572 5.63665 10.2995 5.63665 11.0003C5.63665 11.7011 5.77593 12.3949 6.04639 13.0414C6.31686 13.6879 6.71312 14.2741 7.21214 14.7662C7.71116 15.2582 8.30299 15.6461 8.95324 15.9074C9.60349 16.1687 10.2992 16.2981 10.9999 16.2882C12.4025 16.2882 13.7476 15.7311 14.7394 14.7393C15.7312 13.7475 16.2884 12.4024 16.2884 10.9998C16.2884 9.59719 15.7312 8.25205 14.7394 7.26027C13.7476 6.26849 12.4025 5.71237 10.9999 5.71237ZM10.9999 7.82775C11.4214 7.81999 11.8402 7.89629 12.2319 8.0522C12.6236 8.20812 12.9803 8.44052 13.2811 8.73583C13.582 9.03114 13.8209 9.38344 13.9841 9.77215C14.1473 10.1609 14.2314 10.5782 14.2314 10.9998C14.2315 11.4213 14.1476 11.8387 13.9845 12.2275C13.8215 12.6162 13.5826 12.9686 13.2819 13.264C12.9811 13.5594 12.6245 13.7919 12.2329 13.948C11.8412 14.104 11.4225 14.1805 11.001 14.1729C10.1594 14.1729 9.35232 13.8385 8.75725 13.2435C8.16218 12.6484 7.82788 11.8413 7.82788 10.9998C7.82788 10.1582 8.16218 9.35114 8.75725 8.75607C9.35232 8.161 10.1594 7.8267 11.001 7.8267L10.9999 7.82775ZM16.5528 4.12583C16.2116 4.13949 15.8889 4.28465 15.6523 4.5309C15.4157 4.77715 15.2835 5.1054 15.2835 5.44689C15.2835 5.78838 15.4157 6.11663 15.6523 6.36288C15.8889 6.60913 16.2116 6.75429 16.5528 6.76795C16.9034 6.76795 17.2397 6.62865 17.4877 6.38071C17.7356 6.13276 17.8749 5.79648 17.8749 5.44583C17.8749 5.09518 17.7356 4.7589 17.4877 4.51096C17.2397 4.26301 16.9034 4.12372 16.5528 4.12372V4.12583Z" fill="#959CAF" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_37_332">
|
||||
<rect width="22" height="22" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
<a href="#" className="underline">zaslot.bet.use</a>
|
||||
</div>
|
||||
<div className="flex flex-row gap-[5px]">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22" fill="none">
|
||||
<path d="M19.9375 0H2.0625C0.928125 0 0 0.928125 0 2.0625V19.9375C0 21.0719 0.928125 22 2.0625 22H19.9375C21.0719 22 22 21.0719 22 19.9375V2.0625C22 0.928125 21.0719 0 19.9375 0ZM17.8186 15.4L15.807 15.4303C15.807 15.4303 15.3725 15.5169 14.806 15.125C14.0539 14.6094 13.3444 13.2646 12.7902 13.4406C12.232 13.6166 12.2485 14.8239 12.2485 14.8239C12.2485 14.8239 12.2526 15.0824 12.1234 15.2185C11.9859 15.3684 11.7109 15.3986 11.7109 15.3986H10.813C10.813 15.3986 8.8275 15.5196 7.0785 13.6964C5.17 11.7109 3.487 7.7715 3.487 7.7715C3.487 7.7715 3.388 7.513 3.49525 7.38925C3.61488 7.24762 3.938 7.23937 3.938 7.23937L6.09125 7.227C6.09125 7.227 6.29338 7.26138 6.43913 7.36863C6.56013 7.45525 6.62337 7.62163 6.62337 7.62163C6.62337 7.62163 6.97125 8.503 7.43188 9.29775C8.32975 10.8488 8.7505 11.1884 9.05575 11.0247C9.4985 10.7841 9.36512 8.82887 9.36512 8.82887C9.36512 8.82887 9.37337 8.11938 9.14237 7.80587C8.96225 7.56113 8.62263 7.48825 8.47688 7.47038C8.35588 7.4525 8.55387 7.17338 8.81238 7.0455C9.19875 6.85575 9.88212 6.84337 10.6906 6.85162C11.3176 6.85575 11.5032 6.89838 11.748 6.95475C12.4919 7.13488 12.2375 7.8265 12.2375 9.48612C12.2375 10.0196 12.1426 10.7663 12.5249 11.011C12.6885 11.1183 13.0914 11.0275 14.102 9.31425C14.5791 8.50163 14.9394 7.54875 14.9394 7.54875C14.9394 7.54875 15.0164 7.37687 15.1374 7.304C15.2625 7.23112 15.4303 7.25313 15.4303 7.25313L17.6949 7.24075C17.6949 7.24075 18.3741 7.15963 18.4855 7.469C18.601 7.79075 18.2325 8.54288 17.3085 9.77625C15.7919 11.8003 15.62 11.6105 16.8836 12.7792C18.0868 13.8971 18.3356 14.4416 18.3796 14.5104C18.8691 15.3354 17.8214 15.4 17.8214 15.4H17.8186Z" fill="#959CAF" />
|
||||
</svg>
|
||||
<a href="#" className="underline">id.za.slot</a>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
};
|
1
frontend/casino/src/app/components/App/Footer/index.ts
Normal file
1
frontend/casino/src/app/components/App/Footer/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './Footer.tsx'
|
113
frontend/casino/src/app/components/App/Slot/Reel.tsx
Normal file
113
frontend/casino/src/app/components/App/Slot/Reel.tsx
Normal file
|
@ -0,0 +1,113 @@
|
|||
import { useState, useEffect, useRef, useLayoutEffect } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import symbols2 from "../../../pages/SlotGamePage/symbols2";
|
||||
|
||||
|
||||
interface ReelProps {
|
||||
isHorizontal: boolean;
|
||||
cellCount: string | number;
|
||||
rng: boolean;
|
||||
rngReverse: boolean;
|
||||
}
|
||||
|
||||
function Reel({ isHorizontal, rng, rngReverse, cellCount }: ReelProps) {
|
||||
const carousel = useRef<HTMLDivElement | null>(null);
|
||||
const refs = useRef<(HTMLDivElement | null)[]>([]);
|
||||
const cells = refs.current.map((value) => value);
|
||||
const [selectedIndex, setSelectedIndex] = useState<number | null>(null);
|
||||
const [width, setWidth] = useState<number>(0);
|
||||
const [height, setHeight] = useState<number>(0);
|
||||
let rotateFn = isHorizontal ? "rotateY" : "rotateX";
|
||||
let radius: number;
|
||||
let theta: number;
|
||||
let cellAngle: number;
|
||||
|
||||
function randomNumberInRange(min: number, max: number): number {
|
||||
// 👇️ get number between min (inclusive) and max (inclusive)
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
return Number(cellCount) < 7
|
||||
? setSelectedIndex(selectedIndex! + randomNumberInRange(3, 6))
|
||||
: setSelectedIndex(selectedIndex! + randomNumberInRange(6, 14));
|
||||
}, [rng]);
|
||||
|
||||
useEffect(() => {
|
||||
return Number(cellCount) < 7
|
||||
? setSelectedIndex(selectedIndex! + randomNumberInRange(-3, -6))
|
||||
: setSelectedIndex(selectedIndex! + randomNumberInRange(-6, -14));
|
||||
}, [rngReverse]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
setWidth(carousel.current!.offsetWidth);
|
||||
setHeight(carousel.current!.offsetHeight);
|
||||
setSelectedIndex(0);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
theta = 360 / Number(cellCount);
|
||||
rotateFn = isHorizontal ? "rotateY" : "rotateX";
|
||||
const angle = theta * selectedIndex! * -1;
|
||||
carousel.current!.style.transform = `translateZ(${-radius}px) ${rotateFn}(${angle}deg)`;
|
||||
}, [selectedIndex]);
|
||||
|
||||
function rotateCarousel() {
|
||||
const angles = theta * selectedIndex! * -1;
|
||||
carousel.current!.style.transform = `translateZ(${-radius}px) ${rotateFn}(${angles}deg)`;
|
||||
}
|
||||
|
||||
function changeCarousel() {
|
||||
theta = 360 / Number(cellCount);
|
||||
const cellSize = isHorizontal ? width : height;
|
||||
radius = Math.round(cellSize / 2 / Math.tan(Math.PI / Number(cellCount)));
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let i = 0; i < cells.length; i++) {
|
||||
const cell = cells[i];
|
||||
if (i < Number(cellCount)) {
|
||||
cell!.style.opacity = "1";
|
||||
cellAngle = theta * i;
|
||||
cell!.style.transform = `${rotateFn}(${cellAngle}deg) translateZ(${radius}px)`;
|
||||
rotateCarousel();
|
||||
} else {
|
||||
cell!.style.opacity = "0";
|
||||
cell!.style.transform = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onOrientationChange() {
|
||||
rotateFn = isHorizontal ? "rotateY" : "rotateX";
|
||||
changeCarousel();
|
||||
}
|
||||
|
||||
// set initials
|
||||
onOrientationChange();
|
||||
|
||||
return (
|
||||
<div className="scene">
|
||||
<div className="carousel" ref={carousel}>
|
||||
{symbols2.map((c, index) => (
|
||||
<div
|
||||
key={c.id}
|
||||
className={`carousel__cell flex bg-white ${c.transform}`}
|
||||
ref={(element) => {
|
||||
refs.current[index] = element;
|
||||
}}
|
||||
>
|
||||
<img src={c.src} alt="..." className="h-24 w-24 m-auto" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reel.propTypes = {
|
||||
isHorizontal: PropTypes.bool.isRequired,
|
||||
cellCount: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
||||
.isRequired,
|
||||
rng: PropTypes.bool.isRequired,
|
||||
rngReverse: PropTypes.bool.isRequired,
|
||||
};
|
||||
export default Reel;
|
1
frontend/casino/src/app/components/App/index.ts
Normal file
1
frontend/casino/src/app/components/App/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './AppLayout'
|
20
frontend/casino/src/app/components/Loading/Loading.tsx
Normal file
20
frontend/casino/src/app/components/Loading/Loading.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import LoadingImg from '../../../assets/LoadingScreen.gif'
|
||||
import { styled } from '../../../styles'
|
||||
|
||||
const LoadingStyle = styled('div', {
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
})
|
||||
|
||||
const Loading = () => {
|
||||
return (
|
||||
<LoadingStyle>
|
||||
<img style={{ width: '100vw' }} src={LoadingImg} />
|
||||
</LoadingStyle>
|
||||
)
|
||||
}
|
||||
|
||||
export default Loading
|
15
frontend/casino/src/app/components/icons/CoinIcon.tsx
Normal file
15
frontend/casino/src/app/components/icons/CoinIcon.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
const CoinIcon: FC = () => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="18" height="21" viewBox="0 0 18 21" fill="none">
|
||||
<path d="M9 19.8182C13.4183 19.8182 17 15.6504 17 10.5091C17 5.36783 13.4183 1.20001 9 1.20001C4.58172 1.20001 1 5.36783 1 10.5091C1 15.6504 4.58172 19.8182 9 19.8182Z" fill="#E5A425" stroke="white" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M7.3999 6.7854H9.3999H10.1999C11.0835 6.7854 11.7999 7.61898 11.7999 8.64722C11.7999 9.67545 11.0835 10.509 10.1999 10.509H7.3999V6.7854Z" fill="#E5A425" />
|
||||
<path d="M7.40005 6.78552V10.5092H10.2C11.0837 10.5092 11.8 9.67558 11.8 8.64734C11.8 7.61911 11.0837 6.78552 10.2 6.78552H9.40005M7.40005 6.78552H5.80005H7.40005ZM7.40005 6.78552V4.92371V6.78552ZM7.40005 6.78552H9.40005H7.40005ZM9.40005 6.78552V4.92371V6.78552Z" fill="#E5A425" />
|
||||
<path d="M7.40005 6.78552V10.5092H10.2C11.0837 10.5092 11.8 9.67558 11.8 8.64734C11.8 7.61911 11.0837 6.78552 10.2 6.78552H9.40005M7.40005 6.78552H5.80005M7.40005 6.78552V4.92371M7.40005 6.78552H9.40005M9.40005 6.78552V4.92371" stroke="white" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M7.3999 10.509H10.9999C11.8835 10.509 12.5999 11.3426 12.5999 12.3709C12.5999 13.3991 11.8835 14.2327 10.9999 14.2327H9.3999H7.3999V10.509Z" fill="#E5A425" />
|
||||
<path d="M7.40005 14.2327V10.509H11C11.8837 10.509 12.6 11.3426 12.6 12.3709C12.6 13.3991 11.8837 14.2327 11 14.2327H9.40005M7.40005 14.2327V16.0945V14.2327ZM7.40005 14.2327H5.80005H9.40005M7.40005 14.2327H9.40005H7.40005ZM9.40005 14.2327V16.0945V14.2327Z" fill="#E5A425" />
|
||||
<path d="M7.40005 14.2327V10.509H11C11.8837 10.509 12.6 11.3426 12.6 12.3709C12.6 13.3991 11.8837 14.2327 11 14.2327H9.40005M7.40005 14.2327V16.0945M7.40005 14.2327H5.80005H9.40005M7.40005 14.2327H9.40005M9.40005 14.2327V16.0945" stroke="white" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>;
|
||||
}
|
||||
|
||||
export default CoinIcon;
|
16
frontend/casino/src/app/components/icons/GameIcon.tsx
Normal file
16
frontend/casino/src/app/components/icons/GameIcon.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
const GameIcon: FC = () => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||
<g clip-path="url(#clip0_37_606)">
|
||||
<path d="M7.5 5.625L9 7.125L10.5 5.625V1.5H7.5V5.625ZM7.5 12.375L9 10.875L10.5 12.375V16.5H7.5V12.375ZM12.375 10.5L10.875 9L12.375 7.5H16.5V10.5H12.375ZM5.625 7.5L7.125 9L5.625 10.5H1.5V7.5H5.625Z" stroke="#226DBC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_37_606">
|
||||
<rect width="18" height="18" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>;
|
||||
}
|
||||
|
||||
export default GameIcon;
|
9
frontend/casino/src/app/components/icons/GamepadIcon.tsx
Normal file
9
frontend/casino/src/app/components/icons/GamepadIcon.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
const GamepadIcon: FC = () => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="30" height="25,5" viewBox="0 0 40 34" fill="none">
|
||||
<path d="M38.4359 16.2014L38.4359 16.2014C37.5563 11.6279 36.4615 8.16056 35.1174 5.68242C33.7762 3.20968 32.2088 1.7602 30.3929 1.14333C29.6152 0.880986 28.8098 0.750311 28.0014 0.75462L27.9987 0.754634C26.9464 0.754627 26.0237 1.0477 24.9621 1.38994C24.9621 1.38996 24.962 1.38997 24.962 1.38998L24.9554 1.3921C23.6803 1.80357 22.1782 2.28829 19.9983 2.28829C17.8176 2.28829 16.3144 1.80418 15.0364 1.39259L15.0314 1.39099L15.031 1.39085C13.9685 1.04763 13.0467 0.754627 11.9978 0.754627H11.9958C11.1567 0.751276 10.3206 0.881868 9.51092 1.14331C7.70708 1.75766 6.14607 3.20455 4.80497 5.67592C3.4604 8.15369 2.35955 11.6217 1.46711 16.1969C0.50784 21.1185 0.27397 25.0896 0.713068 27.9881C1.15408 30.8993 2.24818 32.5999 3.82309 33.2268L3.82604 33.2279C4.26678 33.4066 4.7285 33.4981 5.19244 33.5C6.27338 33.4997 7.28481 32.9819 8.2197 32.1809C9.15372 31.3806 9.9666 30.3356 10.6427 29.3679L38.4359 16.2014ZM38.4359 16.2014C39.3268 20.8311 39.6475 24.366 39.4385 27.0303C39.1628 30.5147 37.9743 32.5007 36.0874 33.2412L36.0863 33.2416M38.4359 16.2014L36.0863 33.2416M36.0863 33.2416C34.8995 33.7106 33.6643 33.5474 32.3679 32.6805C31.3811 32.0201 30.362 30.9496 29.258 29.3678L29.2579 29.3678M36.0863 33.2416L29.2579 29.3678M29.2579 29.3678C28.3199 28.024 27.2812 27.1152 25.8034 26.5579C24.3511 26.0103 22.5105 25.8163 19.9983 25.8163C17.1781 25.8163 15.25 26.0978 13.8154 26.693M29.2579 29.3678L13.8154 26.693M13.8154 26.693C12.3574 27.2978 11.4509 28.2091 10.6429 29.3676L13.8154 26.693ZM25.1094 8.15637L25.4868 8.48443L25.1094 8.15637C24.8126 8.49772 24.6168 8.9248 24.5379 9.3808C24.459 9.83683 24.4991 10.3101 24.655 10.7431C24.811 11.1761 25.0789 11.557 25.4346 11.8304C25.7916 12.1047 26.2199 12.2571 26.6653 12.2571C27.2644 12.2571 27.822 11.9828 28.2212 11.5236C28.6185 11.0667 28.8321 10.4602 28.8321 9.84001C28.8321 9.3763 28.7128 8.91844 28.4835 8.52378C28.2541 8.12879 27.9211 7.80943 27.5182 7.61747C27.1139 7.42483 26.6638 7.37281 26.2287 7.47236C25.7946 7.57168 25.4068 7.81436 25.1094 8.15637ZM32.1504 12.7413L31.7181 12.9925L32.1504 12.7413C31.9209 12.3464 31.588 12.027 31.1851 11.835C30.7807 11.6424 30.3307 11.5904 29.8955 11.6899C29.4614 11.7892 29.0737 12.0319 28.7763 12.3739C28.4795 12.7153 28.2837 13.1424 28.2048 13.5984C28.1259 14.0544 28.166 14.5277 28.3219 14.9606C28.4778 15.3937 28.7457 15.7746 29.1015 16.048C29.4584 16.3223 29.8867 16.4747 30.3322 16.4747C30.9312 16.4747 31.4889 16.2004 31.8881 15.7412C32.2854 15.2843 32.499 14.6778 32.499 14.0576C32.499 13.5939 32.3797 13.136 32.1504 12.7413ZM10.1644 16.0912V18.6586C10.1644 19.1771 10.3429 19.6861 10.6776 20.0711C11.0143 20.4583 11.4872 20.6922 11.9978 20.6922C12.5084 20.6922 12.9813 20.4583 13.318 20.0711C13.6528 19.6861 13.8312 19.1771 13.8312 18.6586V16.0912H15.9981C16.5087 16.0912 16.9816 15.8573 17.3182 15.4701C17.653 15.0851 17.8315 14.5761 17.8315 14.0576C17.8315 13.5391 17.653 13.0301 17.3182 12.6451C16.9816 12.2578 16.5087 12.0239 15.9981 12.0239H13.8312V9.4566C13.8312 8.93807 13.6528 8.4291 13.318 8.04407C12.9813 7.65683 12.5084 7.42293 11.9978 7.42293C11.4872 7.42293 11.0143 7.65683 10.6776 8.04407C10.3429 8.4291 10.1644 8.93807 10.1644 9.4566V12.0239H7.99759C7.48697 12.0239 7.01408 12.2578 6.6774 12.6451C6.34265 13.0301 6.16418 13.5391 6.16418 14.0576C6.16418 14.5761 6.34265 15.0851 6.6774 15.4701C7.01408 15.8573 7.48697 16.0912 7.99759 16.0912H10.1644ZM21.7678 16.048C22.1247 16.3223 22.553 16.4747 22.9985 16.4747C23.5975 16.4747 24.1552 16.2004 24.5544 15.7412C24.9516 15.2843 25.1652 14.6778 25.1652 14.0576C25.1652 13.5939 25.0459 13.136 24.8167 12.7413C24.5872 12.3464 24.2543 12.027 23.8514 11.835C23.447 11.6424 22.9969 11.5904 22.5618 11.6899C22.1277 11.7892 21.7399 12.0319 21.4425 12.3739C21.1458 12.7153 20.9499 13.1424 20.871 13.5984C20.7922 14.0544 20.8322 14.5277 20.9881 14.9606C21.1441 15.3937 21.412 15.7746 21.7678 16.048ZM25.4341 20.2651C25.7911 20.5397 26.2196 20.6922 26.6653 20.6922C27.2634 20.6922 27.8203 20.4188 28.2194 19.9609C28.6165 19.5053 28.8307 18.9002 28.8321 18.281C28.8331 17.8171 28.7147 17.3587 28.4861 16.9633C28.2573 16.5676 27.9248 16.2473 27.5221 16.0544C27.1179 15.8608 26.6676 15.8079 26.2321 15.9067C25.7976 16.0053 25.4092 16.2475 25.1113 16.5893C24.814 16.9305 24.6176 17.3576 24.5383 17.8138C24.459 18.27 24.4988 18.7435 24.6545 19.1768C24.8103 19.6102 25.0782 19.9914 25.4341 20.2651Z" fill="black" stroke="#959CAF" />
|
||||
</svg>;
|
||||
}
|
||||
|
||||
export default GamepadIcon;
|
29
frontend/casino/src/app/components/icons/ProfileIcon.tsx
Normal file
29
frontend/casino/src/app/components/icons/ProfileIcon.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
const ProfileIcon: FC = () => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="58" height="58" viewBox="0 0 58 58" fill="none">
|
||||
<g filter="url(#filter0_d_37_572)">
|
||||
<rect x="4" y="4" width="42" height="42" rx="10" fill="#4F5563" />
|
||||
<path d="M39.745 31C39.4345 30.1224 38.8595 29.3626 38.0993 28.8254C37.339 28.2881 36.4309 27.9997 35.5 28H14.5C13.3065 28 12.1619 28.4741 11.318 29.318C10.4741 30.1619 10 31.3065 10 32.5V33.5725C10 39.1495 16.315 43 25 43C29.593 43 33.523 41.8645 36.2065 40H25V38.5H37.951C38.5915 37.8085 39.0955 37.054 39.4435 36.25H25V34.75H39.895C39.9647 34.3614 39.9998 33.9673 40 33.5725V32.5H25V31H39.745ZM34 16C33.9998 15.6239 33.9768 15.2483 33.931 14.875H25V13.375H33.61C33.368 12.5802 33.0166 11.823 32.566 11.125H25V9.625H31.3525C30.0926 8.36945 28.4889 7.51539 26.7439 7.17065C24.9989 6.82591 23.1909 7.00597 21.5482 7.68807C19.9055 8.37018 18.5018 9.52375 17.5142 11.0031C16.5266 12.4825 15.9996 14.2213 15.9996 16C15.9996 17.7787 16.5266 19.5175 17.5142 20.9969C18.5018 22.4762 19.9055 23.6298 21.5482 24.3119C23.1909 24.994 24.9989 25.1741 26.7439 24.8293C28.4889 24.4846 30.0926 23.6305 31.3525 22.375H25V20.875H32.566C33.013 20.185 33.367 19.429 33.6115 18.625H25V17.125H33.931C33.9768 16.7517 33.9998 16.376 34 16Z" fill="url(#paint0_linear_37_572)" />
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_37_572" x="0" y="0" width="58" height="58" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix" />
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha" />
|
||||
<feOffset dx="4" dy="4" />
|
||||
<feGaussianBlur stdDeviation="4" />
|
||||
<feComposite in2="hardAlpha" operator="out" />
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_37_572" />
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_37_572" result="shape" />
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_37_572" x1="10" y1="25" x2="40" y2="25" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#171A21" />
|
||||
<stop offset="0.536458" stop-color="#171A21" />
|
||||
<stop offset="1" stop-color="#171A21" stop-opacity="0.05" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>;
|
||||
}
|
||||
|
||||
export default ProfileIcon;
|
9
frontend/casino/src/app/components/icons/SearchIcon.tsx
Normal file
9
frontend/casino/src/app/components/icons/SearchIcon.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
const SearchIcon: FC = () => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
|
||||
<path d="M14.1667 14.1667L17.5 17.5M2.5 9.16667C2.5 10.9348 3.20238 12.6305 4.45262 13.8807C5.70286 15.131 7.39856 15.8333 9.16667 15.8333C10.9348 15.8333 12.6305 15.131 13.8807 13.8807C15.131 12.6305 15.8333 10.9348 15.8333 9.16667C15.8333 7.39856 15.131 5.70286 13.8807 4.45262C12.6305 3.20238 10.9348 2.5 9.16667 2.5C7.39856 2.5 5.70286 3.20238 4.45262 4.45262C3.20238 5.70286 2.5 7.39856 2.5 9.16667Z" stroke="#959CAF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>;
|
||||
}
|
||||
|
||||
export default SearchIcon;
|
9
frontend/casino/src/app/components/icons/SportIcon.tsx
Normal file
9
frontend/casino/src/app/components/icons/SportIcon.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
const SportIcon: FC = () => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21" fill="none">
|
||||
<path d="M10.5 2.13281C8.84513 2.13281 7.22742 2.62354 5.85144 3.54294C4.47547 4.46234 3.40302 5.76911 2.76973 7.29802C2.13644 8.82692 1.97074 10.5093 2.29359 12.1324C2.61644 13.7554 3.41334 15.2463 4.58351 16.4165C5.75368 17.5867 7.24457 18.3836 8.86765 18.7064C10.4907 19.0293 12.1731 18.8636 13.702 18.2303C15.2309 17.597 16.5377 16.5245 17.4571 15.1486C18.3765 13.7726 18.8672 12.1549 18.8672 10.5C18.8646 8.28168 17.9822 6.15497 16.4136 4.58638C14.845 3.01779 12.7183 2.13542 10.5 2.13281ZM16.8714 14.2259H13.9363L13.0766 13.043L14.1668 9.86672L15.5613 9.41473L17.8533 11.1727C17.7553 12.2504 17.42 13.2931 16.8714 14.2259ZM7.06371 14.2259H4.12864C3.5806 13.2929 3.24583 12.2503 3.14836 11.1727L5.44032 9.41473L6.83485 9.86672L7.9234 13.043L7.06371 14.2259ZM4.1836 6.67898L4.7775 8.68137L3.13688 9.93973C3.22377 8.78548 3.58244 7.66815 4.1836 6.67898ZM8.82328 12.6328L7.80528 9.6682L10.5 7.81594L13.1947 9.6682L12.1767 12.6328H8.82328ZM16.2217 8.68137L16.8164 6.67898C17.4173 7.66822 17.7757 8.78555 17.8623 9.93973L16.2217 8.68137ZM16.0855 5.67738L15.2578 8.47793L13.8633 8.93156L10.9922 6.96281V5.50922L13.527 3.76687C14.5086 4.21013 15.3817 4.86211 16.0855 5.67738ZM12.3802 3.36328L10.5 4.65281L8.61985 3.36328C9.85238 3.03954 11.1476 3.03954 12.3802 3.36328ZM7.47387 3.76605L10.0078 5.50922V6.95953L7.13672 8.92828L5.74219 8.47465L4.9145 5.67738C5.61864 4.86191 6.49203 4.20967 7.47387 3.76605ZM4.81934 15.2102H6.96692L7.72079 17.343C6.59304 16.8814 5.59649 16.1488 4.81934 15.2102ZM8.89465 17.7064L7.86516 14.7976L8.72321 13.6172H12.2768L13.1348 14.7976L12.1054 17.7064C11.048 17.9416 9.95197 17.9416 8.89465 17.7064ZM13.2792 17.3389L14.0331 15.2061H16.1807C15.4035 16.1447 14.407 16.8773 13.2792 17.3389Z" fill="#959CAF" />
|
||||
</svg>;
|
||||
}
|
||||
|
||||
export default SportIcon;
|
17
frontend/casino/src/app/components/icons/WalletIcon.tsx
Normal file
17
frontend/casino/src/app/components/icons/WalletIcon.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { FC } from 'react';
|
||||
|
||||
const WalletIcon: FC = () => {
|
||||
return <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<g clip-path="url(#clip0_37_601)">
|
||||
<path d="M15.7308 8.95333H7.70811C7.27822 8.92021 6.85258 9.0577 6.52331 9.33606C6.19404 9.61443 5.98764 10.0113 5.94878 10.4407L5.94811 10.4467C5.98753 10.8817 6.19808 11.2833 6.53348 11.5632C6.86888 11.8431 7.30168 11.9784 7.73678 11.9393L7.73078 11.94H15.7308C15.7811 11.9447 15.8319 11.9392 15.8801 11.924C15.9284 11.9088 15.9731 11.8841 16.0116 11.8513C16.0502 11.8186 16.0818 11.7785 16.1047 11.7334C16.1275 11.6883 16.1412 11.6391 16.1448 11.5887V9.306C16.1317 9.20844 16.0837 9.11893 16.0096 9.05408C15.9356 8.98923 15.8405 8.95343 15.7421 8.95333H15.7301H15.7308ZM7.70811 11.3787C7.51956 11.3787 7.33524 11.3228 7.17847 11.218C7.02169 11.1132 6.8995 10.9644 6.82735 10.7902C6.75519 10.616 6.73631 10.4243 6.7731 10.2393C6.80988 10.0544 6.90068 9.88455 7.034 9.75123C7.16733 9.6179 7.3372 9.5271 7.52212 9.49032C7.70705 9.45353 7.89874 9.47241 8.07294 9.54457C8.24713 9.61672 8.39602 9.73892 8.50078 9.89569C8.60553 10.0525 8.66144 10.2368 8.66144 10.4253V10.428C8.66144 10.9533 8.23544 11.3787 7.71078 11.3787H7.70811ZM2.19678 0V4.26933H3.10878V2.44533C3.20078 2.468 3.30744 2.48267 3.41678 2.48667H3.41944C3.74899 2.48579 4.06479 2.35453 4.29788 2.12157C4.53096 1.88861 4.66239 1.57288 4.66344 1.24333C4.66124 1.1351 4.64647 1.02749 4.61944 0.922667L4.62144 0.932667H9.61611C9.59204 1.03369 9.5782 1.13687 9.57478 1.24067V1.24333C9.57566 1.57288 9.70691 1.88868 9.93987 2.12177C10.1728 2.35485 10.4886 2.48628 10.8181 2.48733C10.9301 2.48333 11.0374 2.46867 11.1401 2.44333L11.1294 2.44533V4.26933H12.0634V0H2.19678ZM12.4561 3.75067H13.6168C13.5298 3.49809 13.3775 3.27307 13.1752 3.09858C12.9729 2.92408 12.728 2.80633 12.4654 2.75733L12.4561 2.756V3.75067Z" fill="#959CAF" />
|
||||
<path d="M7.64733 8.456H15.606V6.03133C15.5719 5.67166 15.3979 5.33976 15.1214 5.10719C14.845 4.87462 14.4882 4.75998 14.128 4.788H14.1327H1.49267C1.4819 4.77625 1.46719 4.76889 1.45133 4.76733C1.3637 4.72615 1.28867 4.66228 1.23405 4.58233C1.17942 4.50238 1.14718 4.40927 1.14067 4.31266V4.31133C1.15128 4.19142 1.19828 4.07761 1.27537 3.98516C1.35245 3.8927 1.45595 3.826 1.572 3.794L1.576 3.79333V2.73666C0.704667 2.778 0 4.02133 0 5.576V14.7573C0.0343941 15.1166 0.208341 15.448 0.48446 15.6803C0.760579 15.9127 1.11683 16.0275 1.47667 16H1.472H14.112C14.4703 16.0264 14.8247 15.9118 15.0996 15.6804C15.3745 15.4491 15.5481 15.1195 15.5833 14.762L15.584 14.7567V12.332H7.66933C6.38467 12.332 5.348 11.4613 5.348 10.4047C5.348 9.32733 6.384 8.45666 7.648 8.45666L7.64733 8.456Z" fill="#959CAF" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_37_601">
|
||||
<rect width="16" height="16" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>;
|
||||
}
|
||||
|
||||
export default WalletIcon;
|
32
frontend/casino/src/app/components/web3/DicePlayButton.tsx
Normal file
32
frontend/casino/src/app/components/web3/DicePlayButton.tsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
import {FC, useEffect} from "react";
|
||||
import {usePlayCraps} from "../../web3/functions/Craps/usePlayCraps.ts";
|
||||
|
||||
interface DicePlayButtonProps {
|
||||
value: number
|
||||
onSuccess: (result: unknown) => void
|
||||
onLoading: () => void
|
||||
secondValue: number
|
||||
}
|
||||
|
||||
export const DicePlayButton: FC<DicePlayButtonProps> = ({ value, onSuccess, onLoading, secondValue }) => {
|
||||
const { playCraps, result, isLoading } = usePlayCraps()
|
||||
|
||||
useEffect(() => {
|
||||
if (result) onSuccess(result)
|
||||
}, [result])
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) onLoading()
|
||||
}, [isLoading])
|
||||
|
||||
return (
|
||||
<button onClick={() => { playCraps(value, secondValue) }} className="bg-rose-700 rounded-[100px] shadow text-white text-2xl font-bold py-5 px-10 hover:bg-rose-600">
|
||||
{
|
||||
isLoading && 'Loading'
|
||||
}
|
||||
{
|
||||
(!isLoading && !result) && 'Играть'
|
||||
}
|
||||
</button>
|
||||
)
|
||||
}
|
18
frontend/casino/src/app/components/web3/FaucetButton.tsx
Normal file
18
frontend/casino/src/app/components/web3/FaucetButton.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {observer} from "mobx-react-lite";
|
||||
import {useEffect} from "react";
|
||||
import {useStores} from "../../hooks/useStores.tsx";
|
||||
import {useFaucet} from "../../web3/functions/Faucet/useFaucet.ts";
|
||||
|
||||
export const FaucetButton = observer(() => {
|
||||
const { faucet, result } = useFaucet()
|
||||
const { userStore } = useStores()
|
||||
useEffect(() => {
|
||||
if (result) userStore.setBalance(result.balance)
|
||||
}, [result])
|
||||
|
||||
return (
|
||||
<button onClick={faucet}>
|
||||
Get coins
|
||||
</button>
|
||||
);
|
||||
});
|
14
frontend/casino/src/app/components/web3/RPSPlayButton.tsx
Normal file
14
frontend/casino/src/app/components/web3/RPSPlayButton.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {usePlayRPS} from "../../web3/functions/RPS/usePlayRPS.ts";
|
||||
|
||||
export const RPSPlayButton = () => {
|
||||
|
||||
const { playRPS } = usePlayRPS()
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => { playRPS('1') }}
|
||||
className="bg-rose-700 rounded-[100px] shadow text-white text-2xl font-bold py-5 px-10 hover:bg-rose-600 inline-block max-w-[200px]">
|
||||
Играть
|
||||
</button >
|
||||
)
|
||||
}
|
29
frontend/casino/src/app/components/web3/SlotPlayButton.tsx
Normal file
29
frontend/casino/src/app/components/web3/SlotPlayButton.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import PropTypes from "prop-types";
|
||||
import {useSlotMachine} from "../../web3/functions/SlotMachine/useSlotMachine.ts";
|
||||
|
||||
interface SlotPlayButtonProps {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const SlotPlayButton: React.FC<SlotPlayButtonProps> = ({ onClick }) => {
|
||||
|
||||
const { playSlot } = useSlotMachine()
|
||||
|
||||
return (
|
||||
<button
|
||||
className="bg-rose-700 rounded-[100px] shadow text-white text-2xl font-bold py-5 px-10 hover:bg-rose-600 max-w-[200px]"
|
||||
onClick={() => {
|
||||
onClick()
|
||||
playSlot()
|
||||
}}
|
||||
>
|
||||
Крутить
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
SlotPlayButton.propTypes = {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default SlotPlayButton;
|
4
frontend/casino/src/app/hooks/index.ts
Normal file
4
frontend/casino/src/app/hooks/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export * from './useDOMRef.ts'
|
||||
export * from './useMediaMui.ts'
|
||||
export * from './useScrollTop.ts'
|
||||
export * from './useScrollWindow.ts'
|
11
frontend/casino/src/app/hooks/useDOMRef.ts
Normal file
11
frontend/casino/src/app/hooks/useDOMRef.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { type Ref, type RefObject, useImperativeHandle, useRef } from 'react'
|
||||
|
||||
export function useDOMRef<T extends HTMLElement = HTMLElement> (
|
||||
ref?: RefObject<T | null> | Ref<T | null>,
|
||||
) {
|
||||
const domRef = useRef<T>(null)
|
||||
|
||||
useImperativeHandle(ref, () => domRef.current)
|
||||
|
||||
return domRef
|
||||
}
|
21
frontend/casino/src/app/hooks/useDidMountEffect.tsx
Normal file
21
frontend/casino/src/app/hooks/useDidMountEffect.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { useEffect, useRef } from 'react'
|
||||
|
||||
const NUMBER_OF_RERENDERS_IN_DEV = 2
|
||||
|
||||
const numberOfRerendersInCurrentMode =
|
||||
!process.env.NODE_ENV || process.env.NODE_ENV === 'development'
|
||||
? NUMBER_OF_RERENDERS_IN_DEV
|
||||
: 1
|
||||
|
||||
/** Applies the same behaivour as useEffect, but does not render on first load */
|
||||
export const useAfterDidMountEffect = (
|
||||
effect: React.EffectCallback,
|
||||
deps?: React.DependencyList | undefined,
|
||||
) => {
|
||||
const rerendersCount = useRef(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (rerendersCount.current >= numberOfRerendersInCurrentMode) effect()
|
||||
else rerendersCount.current++
|
||||
}, deps)
|
||||
}
|
51
frontend/casino/src/app/hooks/useIntervalAsync.ts
Normal file
51
frontend/casino/src/app/hooks/useIntervalAsync.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import {IGetResult} from "../web3/functions/utils/useGetResult.ts";
|
||||
|
||||
const useIntervalAsync = <R = unknown>(fn: ({tx, notificationKey}: IGetResult) => Promise<R>, ms: number) => {
|
||||
const runningCount = useRef(0)
|
||||
const timeout = useRef<number>()
|
||||
const mountedRef = useRef(false)
|
||||
const [allRunsCount, setAllRunsCount] = useState(0)
|
||||
|
||||
const next = useCallback(
|
||||
(handler: TimerHandler) => {
|
||||
if (mountedRef.current && runningCount.current === 0) {
|
||||
setAllRunsCount(prevState => prevState + 1)
|
||||
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
||||
timeout.current = window.setTimeout(handler, ms)
|
||||
}
|
||||
},
|
||||
[ms],
|
||||
)
|
||||
|
||||
const run = useCallback(async (tx: string, notificationKey?: string) => {
|
||||
runningCount.current += 1
|
||||
const result = await fn({tx, notificationKey})
|
||||
runningCount.current -= 1
|
||||
|
||||
next(run)
|
||||
|
||||
return result
|
||||
}, [fn, next])
|
||||
|
||||
useEffect(() => {
|
||||
mountedRef.current = true
|
||||
|
||||
return () => {
|
||||
mountedRef.current = false
|
||||
window.clearTimeout(timeout.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const flush = useCallback(() => {
|
||||
window.clearTimeout(timeout.current)
|
||||
}, [])
|
||||
|
||||
return {
|
||||
flush,
|
||||
run,
|
||||
allRunsCount,
|
||||
}
|
||||
}
|
||||
|
||||
export default useIntervalAsync
|
35
frontend/casino/src/app/hooks/useMediaMui.ts
Normal file
35
frontend/casino/src/app/hooks/useMediaMui.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { useMediaQuery } from '@mui/material'
|
||||
|
||||
interface adaptiveType {
|
||||
sm?: string
|
||||
md?: string
|
||||
lg?: string
|
||||
xl?: string
|
||||
defaultValue: string
|
||||
}
|
||||
|
||||
export function useMediaMui() {
|
||||
const smValue = useMediaQuery('(max-width:600px)')
|
||||
const mdValue = useMediaQuery('(max-width:900px)')
|
||||
const lgValue = useMediaQuery('(max-width:1200px)')
|
||||
const xlValue = useMediaQuery('(max-width:1536px)')
|
||||
|
||||
const adaptive = ({ sm, xl, md, lg, defaultValue }: adaptiveType): string => {
|
||||
if (smValue && sm) {
|
||||
return sm
|
||||
}
|
||||
if (mdValue && md) {
|
||||
return md
|
||||
}
|
||||
if (lgValue && lg) {
|
||||
return lg
|
||||
}
|
||||
if (xlValue && xl) {
|
||||
return xl
|
||||
}
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
return { smValue, mdValue, lgValue, xlValue, adaptive }
|
||||
}
|
10
frontend/casino/src/app/hooks/useScrollTop.ts
Normal file
10
frontend/casino/src/app/hooks/useScrollTop.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { useEffect } from 'react'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
|
||||
export function useScrollTop() {
|
||||
const { pathname } = useLocation()
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0)
|
||||
}, [pathname])
|
||||
}
|
22
frontend/casino/src/app/hooks/useScrollWindow.ts
Normal file
22
frontend/casino/src/app/hooks/useScrollWindow.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
|
||||
export const useScrollWindow = () => {
|
||||
const [scrollY, setScrollY] = useState(0)
|
||||
|
||||
function logit() {
|
||||
setScrollY(window.pageYOffset)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
function watchScroll() {
|
||||
window.addEventListener('scroll', logit)
|
||||
}
|
||||
watchScroll()
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', logit)
|
||||
}
|
||||
})
|
||||
|
||||
return scrollY
|
||||
}
|
39
frontend/casino/src/app/hooks/useStatusState.ts
Normal file
39
frontend/casino/src/app/hooks/useStatusState.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { useCallback, useState } from 'react'
|
||||
import {stringifyError} from "../utils/error/stringifyError.ts";
|
||||
|
||||
export function useStatusState<ResultType, Arguments = void>() {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [error, setError] = useState<string>()
|
||||
const [result, setResult] = useState<ResultType>()
|
||||
|
||||
const wrapPromise = useCallback((call: (args: Arguments) => Promise<ResultType>) => {
|
||||
return async (args: Arguments) => {
|
||||
setIsLoading(true)
|
||||
setError(undefined)
|
||||
setResult(undefined)
|
||||
try {
|
||||
const result = await call(args)
|
||||
setIsLoading(false)
|
||||
setResult(result)
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
setIsLoading(false)
|
||||
setError(stringifyError(err))
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return {
|
||||
statuses: {
|
||||
isLoading,
|
||||
error,
|
||||
result,
|
||||
},
|
||||
setIsLoading,
|
||||
setError,
|
||||
setResult,
|
||||
wrapPromise,
|
||||
}
|
||||
}
|
18
frontend/casino/src/app/hooks/useStores.tsx
Normal file
18
frontend/casino/src/app/hooks/useStores.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { createContext, type PropsWithChildren, useContext } from 'react'
|
||||
|
||||
import { type RootStore, rootStore } from '../stores/RootStore'
|
||||
|
||||
export const StoreContext = createContext<RootStore>(rootStore)
|
||||
|
||||
export function StoreProvider({
|
||||
children,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
}: PropsWithChildren): JSX.Element {
|
||||
return (
|
||||
<StoreContext.Provider value={rootStore}>{children}</StoreContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useStores(): RootStore {
|
||||
return useContext(StoreContext)
|
||||
}
|
53
frontend/casino/src/app/pages/DiceGamePage/DiceGamePage.tsx
Normal file
53
frontend/casino/src/app/pages/DiceGamePage/DiceGamePage.tsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import ContainerLayout from "../../utils/ContainerLayout"
|
||||
import { DicePlayButton } from "../../components/web3/DicePlayButton"
|
||||
import diceImg from '../../../assets/img/dice-img.png'
|
||||
import {useState} from "react";
|
||||
import {useStores} from "../../hooks/useStores.tsx";
|
||||
import {observer} from "mobx-react-lite";
|
||||
|
||||
export const DiceGamePage = observer(() => {
|
||||
const [diceValue, setDiceValue] = useState<string | undefined>()
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false)
|
||||
const { userStore } = useStores()
|
||||
|
||||
const onSuccess = (result: unknown) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
setDiceValue(result.value)
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error
|
||||
userStore.setBalance(result.balance ?? '0')
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
||||
const onLoading = () => {
|
||||
setIsLoading(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<ContainerLayout>
|
||||
<div className="flex flex-col items-center text-white">
|
||||
<p className="text-[46px] font-bold uppercase">
|
||||
брось кубики онлайн!
|
||||
</p>
|
||||
<div className="justify-center flex pointer-events-none">
|
||||
<div className="absolute w-[710px] h-[456px] bg-lime-800 rounded-[187px] blur-[300px]" />
|
||||
</div>
|
||||
<div className="flex flex-row min-w-full justify-center gap-10 relative mt-12">
|
||||
<img src={diceImg} alt="Dice" />
|
||||
<img src={diceImg} alt="Dice" />
|
||||
</div>
|
||||
{/* Кнопка для бека */}
|
||||
<DicePlayButton onLoading={onLoading} onSuccess={onSuccess} value={5} secondValue={6}/>
|
||||
<div className="flex flex-row justify-around w-full font-semibold">
|
||||
<div className="bg-gray-500 rounded-[30px] py-5 px-10 text-2xl">
|
||||
Вы загадали: 5 и 6
|
||||
</div>
|
||||
{(diceValue && !isLoading) && <div className="bg-gray-800 rounded-[30px] py-5 px-10 text-2xl">
|
||||
Выпало число: {diceValue}
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
</ContainerLayout>
|
||||
)
|
||||
})
|
94
frontend/casino/src/app/pages/MainPage/MainPage.tsx
Normal file
94
frontend/casino/src/app/pages/MainPage/MainPage.tsx
Normal file
|
@ -0,0 +1,94 @@
|
|||
import ContainerLayout from "../../utils/ContainerLayout";
|
||||
import GamepadIcon from "../../components/icons/GamepadIcon";
|
||||
import {Link} from "react-router-dom";
|
||||
import casinoBg from '../../../assets/img/casino-bg-image.png'
|
||||
import diceSmall from '../../../assets/img/dices-small.png'
|
||||
import rpsSmall from '../../../assets/img/rps-small.png'
|
||||
import slotMachine from '../../../assets/img/slot-machine-small.png'
|
||||
|
||||
export const MainPage = () => {
|
||||
return (
|
||||
<ContainerLayout>
|
||||
<div className="absolute w-[1235px] h-[629px] overflow-hidden">
|
||||
<img
|
||||
src={casinoBg}
|
||||
alt="Icon"
|
||||
className="w-full h-full object-cover filter brightness-80 blur-[4px]"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-[#171A21] via-[rgba(22, 25, 32, 0.10)] to-[#171A21] via-[rgba(22,22,22,0)]" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-[#171A21] via-[rgba(22, 25, 32, 0.10)] to-[#171A21] via-[rgba(22,22,22,0)]" />
|
||||
</div>
|
||||
<div className="w-full h-max-content bg-gray-800 rounded-[25px] p-[30px] text-white flex flex-col gap-[30px] relative z-10 mt-[400px]">
|
||||
<div className="flex flex-row justify-between">
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<GamepadIcon />
|
||||
<span className="font-bold">
|
||||
Games
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<div className="w-max-content h-max-content bg-gray-700 rounded-md px-[10px] cursor-pointer">
|
||||
<span className="font-bold">❮</span>
|
||||
</div>
|
||||
<div className="w-max-content h-max-content bg-gray-700 rounded-md px-[10px] cursor-pointer">
|
||||
<span className="font-bold">❯</span>
|
||||
</div>
|
||||
<div className="w-max-content h-max-content bg-gray-700 rounded-md px-[10px] cursor-pointer">
|
||||
<span className="font-bold">See all</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between">
|
||||
{/* dices */}
|
||||
<div className="w-[374px] h-[187.35px] bg-[#323846] rounded-[23px] border-2 border-gray-700 flex flex-row items-center justify-between px-4 py-6 gap-3">
|
||||
<div className="rounded-full bg-neutral-800 inline-block">
|
||||
<img
|
||||
src={diceSmall}
|
||||
alt="Icon"
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col h-full justify-between">
|
||||
<div className="flex flex-row justify-between min-w-[200px] my-auto items-center">
|
||||
<span className="text-xl font-bold">DICE</span>
|
||||
<Link to="dice" className="min-w-[100px] max-h-[45px] bg-blue-500 rounded-[10px] border border-blue-400 font-bold px-3 items-center flex justify-center">Play now</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Rock paper scissorstel */}
|
||||
<div className="w-[404px] h-[187.35px] bg-[#323846] rounded-[23px] border-2 border-gray-700 flex flex-row items-center justify-between px-4 py-6 gap-3">
|
||||
<div className="rounded-full w-[140px] bg-neutral-800 inline-block">
|
||||
<img
|
||||
src={rpsSmall}
|
||||
alt="Icon"
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col h-full justify-between">
|
||||
<div className="flex flex-row justify-between min-w-[200px] my-auto">
|
||||
<span className="text-xl font-bold uppercase">Rock paper scissors</span>
|
||||
<Link to="/rock-paper-scissors" className="min-w-[100px]max-h-[45px] bg-blue-500 rounded-[10px] border border-blue-400 font-bold px-3 items-center flex justify-center py-2">Play now</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* slot */}
|
||||
<div className="w-[374px] h-[187.35px] bg-[#323846] rounded-[23px] border-2 border-gray-700 flex flex-row items-center justify-between px-4 py-6 gap-3">
|
||||
<div className="rounded-full bg-neutral-800 inline-block">
|
||||
<img
|
||||
src={slotMachine}
|
||||
alt="Icon"
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col h-full justify-between">
|
||||
<div className="flex flex-row justify-between min-w-[200px] my-auto">
|
||||
<span className="text-xl font-bold uppercase">Slot machine</span>
|
||||
<Link to="/slot" className="min-w-[100px]max-h-[45px] bg-blue-500 rounded-[10px] border border-blue-400 font-bold px-3 items-center flex justify-center py-2">Play now</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ContainerLayout>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
export const NotMainPage = () => {
|
||||
return (
|
||||
<h1 className="text-3xl font-bold underline">
|
||||
Not Main Page
|
||||
</h1>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
import { useState } from 'react';
|
||||
import ContainerLayout from "../../utils/ContainerLayout"
|
||||
import { RPSPlayButton } from "../../components/web3/RPSPlayButton"
|
||||
|
||||
|
||||
export const RockPaperScissorsGamePage = () => {
|
||||
const [userChoice, setUserChoice] = useState('src/assets/img/RPS-rock.png');
|
||||
|
||||
const handleImageClick = () => {
|
||||
// cycle
|
||||
if (userChoice === 'src/assets/img/RPS-rock.png') {
|
||||
setUserChoice('src/assets/img/RPS-paper.png');
|
||||
} else if (userChoice === 'src/assets/img/RPS-paper.png') {
|
||||
setUserChoice('src/assets/img/RPS-scissors.png');
|
||||
} else {
|
||||
setUserChoice('src/assets/img/RPS-rock.png');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ContainerLayout>
|
||||
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="rounded">
|
||||
<p className="text-[46px] font-bold uppercase bg-slate-400 rounded inline-block p-4 m-2">
|
||||
ты
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded">
|
||||
<p className="text-[46px] font-bold uppercase bg-slate-400 rounded inline-block p-4 m-2">
|
||||
не ты
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="w-[500px] h-[500px]">
|
||||
<img src={userChoice} alt="your_hand" onClick={handleImageClick} />
|
||||
</div>
|
||||
<div className="w-[500px] h-[500px]">
|
||||
<img src="src/assets/img/RPS-rock.png" alt="casino_hand" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 items-center">
|
||||
<img src="src/assets/img/RPS-rules.png" alt="rules" />
|
||||
<div className="max-h-[100px] flex items-center justify-center">
|
||||
<RPSPlayButton />
|
||||
</div>
|
||||
<div className="bg-green-700 shadow text-white text-2xl font-bold py-4 px-6">
|
||||
<p>won: 5 times</p>
|
||||
<p>lost: 2 times</p>
|
||||
</div>
|
||||
</div>
|
||||
</ContainerLayout>
|
||||
)
|
||||
}
|
63
frontend/casino/src/app/pages/SlotGamePage/SlotGamePage.tsx
Normal file
63
frontend/casino/src/app/pages/SlotGamePage/SlotGamePage.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import ContainerLayout from "../../utils/ContainerLayout.tsx";
|
||||
import Reel from "../../components/App/Slot/Reel.tsx";
|
||||
import SlotPlayButton from "../../components/web3/SlotPlayButton.tsx";
|
||||
|
||||
export const SlotGamePage = () => {
|
||||
const [isHorizontal, setIsHorizontal] = useState(false);
|
||||
const [rng, setRng] = useState(false);
|
||||
const [rngReverse, setRngReverse] = useState(false);
|
||||
|
||||
const mql = window.matchMedia("(orientation: portrait)");
|
||||
|
||||
mql.onchange = (e) => {
|
||||
if (e.matches) {
|
||||
setIsHorizontal(true);
|
||||
} else {
|
||||
setIsHorizontal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const spinReels = () => {
|
||||
// Trigger the spinning effect in each reel
|
||||
setRng(!rng);
|
||||
setRngReverse(!rngReverse);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (mql.matches) {
|
||||
setIsHorizontal(true);
|
||||
} else {
|
||||
setIsHorizontal(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<ContainerLayout>
|
||||
<div className="flex flex-row gap-4">
|
||||
<div className="max-h-[180px] bg-green-700 flex flex-col justify-between shadow text-white text-2xl font-bold py-4 px-6 rounded-xl">
|
||||
<div className="flex flex-col">
|
||||
<p>выигрыш:</p>
|
||||
<span className="text-yellow-400">5000</span>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<p>ставка:</p>
|
||||
<span className="text-yellow-400">10 000</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="min-h-[800px] w-full flex items-center justify-center">
|
||||
<div className="flex flex-row portrait:flex-col justify-center items-center my-auto">
|
||||
<Reel rng={rng} rngReverse={rngReverse} cellCount={7} isHorizontal={isHorizontal} />
|
||||
<Reel rng={rng} rngReverse={rngReverse} cellCount={7} isHorizontal={isHorizontal} />
|
||||
<Reel rng={rng} rngReverse={rngReverse} cellCount={7} isHorizontal={isHorizontal} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-[100px]">
|
||||
<SlotPlayButton onClick={spinReels} />
|
||||
</div>
|
||||
</div>
|
||||
</ContainerLayout>
|
||||
);
|
||||
};
|
82
frontend/casino/src/app/pages/SlotGamePage/symbols2.ts
Normal file
82
frontend/casino/src/app/pages/SlotGamePage/symbols2.ts
Normal file
|
@ -0,0 +1,82 @@
|
|||
import bar from '../../../assets/img/slot/bar-512.png'
|
||||
import bell from '../../../assets/img/slot/bell-512.png'
|
||||
import cherries from '../../../assets/img/slot/cherries-512.png'
|
||||
import crown from '../../../assets/img/slot/crown-512.png'
|
||||
import diamond from '../../../assets/img/slot/diamond-512.png'
|
||||
import horseshoe from '../../../assets/img/slot/horseshoe-512.png'
|
||||
import seven from '../../../assets/img/slot/seven-512.png'
|
||||
import watermelon from '../../../assets/img/slot/watermelon-512.png'
|
||||
|
||||
const symbols2 = [
|
||||
{
|
||||
transform: 'rotateY(0deg) translateZ(288px)',
|
||||
src: seven,
|
||||
id: 1,
|
||||
},
|
||||
{
|
||||
transform: 'rotateY(40deg) translateZ(288px)',
|
||||
src: bar,
|
||||
id: 2,
|
||||
},
|
||||
{
|
||||
transform: 'rotateY(80deg) translateZ(288px)',
|
||||
src: bell,
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
transform: 'rotateY(120deg) translateZ(288px)',
|
||||
src: cherries,
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
transform: 'rotateY(160deg) translateZ(288px)',
|
||||
src: horseshoe,
|
||||
id: 5,
|
||||
},
|
||||
{
|
||||
transform: 'rotateY(200deg) translateZ(288px)',
|
||||
src: bar,
|
||||
id: 6,
|
||||
},
|
||||
{
|
||||
transform: 'rotateY(240deg) translateZ(288px)',
|
||||
src: crown,
|
||||
id: 7,
|
||||
},
|
||||
{
|
||||
transform: 'rotateY(280deg) translateZ(288px)',
|
||||
src: diamond,
|
||||
id: 8,
|
||||
},
|
||||
{
|
||||
transform: 'rotateY(320deg) translateZ(288px)',
|
||||
src: bar,
|
||||
id: 9,
|
||||
},
|
||||
{
|
||||
src: horseshoe,
|
||||
id: 10,
|
||||
},
|
||||
{
|
||||
src: bar,
|
||||
id: 11,
|
||||
},
|
||||
{
|
||||
src: seven,
|
||||
id: 12,
|
||||
},
|
||||
{
|
||||
src: bar,
|
||||
id: 13,
|
||||
},
|
||||
{
|
||||
src: watermelon,
|
||||
id: 14,
|
||||
},
|
||||
{
|
||||
src: cherries,
|
||||
id: 15,
|
||||
},
|
||||
]
|
||||
|
||||
export default symbols2
|
43
frontend/casino/src/app/pages/router.tsx
Normal file
43
frontend/casino/src/app/pages/router.tsx
Normal file
|
@ -0,0 +1,43 @@
|
|||
import { createBrowserRouter, Navigate, type RouteObject } from 'react-router-dom'
|
||||
|
||||
import { AppLayout } from '../components/App'
|
||||
import { MainPage } from "./MainPage/MainPage.tsx";
|
||||
import { NotMainPage } from "./NotMainPage/NotMainPage.tsx";
|
||||
import { DiceGamePage } from './DiceGamePage/DiceGamePage.tsx';
|
||||
import { RockPaperScissorsGamePage } from './RockPaperScissorsGamePage/RockPaperScissorsGamePage.tsx';
|
||||
import { SlotGamePage } from './SlotGamePage/SlotGamePage.tsx';
|
||||
|
||||
const routes: RouteObject[] = [
|
||||
{
|
||||
path: '/',
|
||||
element: <MainPage />,
|
||||
},
|
||||
{
|
||||
path: '/example',
|
||||
element: <NotMainPage />,
|
||||
},
|
||||
{
|
||||
path: '/dice',
|
||||
element: <DiceGamePage />,
|
||||
},
|
||||
{
|
||||
path: '/rock-paper-scissors',
|
||||
element: <RockPaperScissorsGamePage />,
|
||||
},
|
||||
{
|
||||
path: '/slot',
|
||||
element: <SlotGamePage />,
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <Navigate replace to={'/'} />,
|
||||
},
|
||||
]
|
||||
|
||||
export const router = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <AppLayout />,
|
||||
children: routes,
|
||||
},
|
||||
])
|
11
frontend/casino/src/app/stores/RootStore.ts
Normal file
11
frontend/casino/src/app/stores/RootStore.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { UserStore } from './User/UserStore'
|
||||
|
||||
export class RootStore {
|
||||
userStore: UserStore
|
||||
|
||||
constructor() {
|
||||
this.userStore = new UserStore()
|
||||
}
|
||||
}
|
||||
|
||||
export const rootStore = new RootStore()
|
13
frontend/casino/src/app/stores/User/UserStore.ts
Normal file
13
frontend/casino/src/app/stores/User/UserStore.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { makeAutoObservable } from 'mobx'
|
||||
|
||||
export class UserStore {
|
||||
balance: string | undefined = undefined
|
||||
constructor() {
|
||||
makeAutoObservable(this)
|
||||
}
|
||||
|
||||
setBalance(balance: string) {
|
||||
this.balance = balance
|
||||
console.log(balance)
|
||||
}
|
||||
}
|
17
frontend/casino/src/app/utils/ContainerLayout.tsx
Normal file
17
frontend/casino/src/app/utils/ContainerLayout.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
|
||||
type ContainerLayoutProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const ContainerLayout: React.FC<ContainerLayoutProps> = ({ children }) => {
|
||||
return (
|
||||
<div className="flex bg-[#171A21]">
|
||||
<div className="container mx-auto relative py-6">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContainerLayout;
|
26
frontend/casino/src/app/utils/error/stringifyError.ts
Normal file
26
frontend/casino/src/app/utils/error/stringifyError.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
export function stringifyError(error: unknown): string {
|
||||
console.log(error)
|
||||
if (typeof error === 'string') {
|
||||
return error
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
if (error.stack) {
|
||||
return error.stack
|
||||
} else {
|
||||
return `${error.name}: ${error.message}`
|
||||
}
|
||||
}
|
||||
let str
|
||||
try {
|
||||
str = JSON.stringify(error)
|
||||
if (str === '{}') {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
str = error + ''
|
||||
}
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
str = (error + '')
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
0
frontend/casino/src/app/utils/index.ts
Normal file
0
frontend/casino/src/app/utils/index.ts
Normal file
18
frontend/casino/src/app/web3/balance/CheckBalanceButton.tsx
Normal file
18
frontend/casino/src/app/web3/balance/CheckBalanceButton.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import {useCheckBalance} from "./useCheckBalance.ts";
|
||||
import {observer} from "mobx-react-lite";
|
||||
import {useEffect} from "react";
|
||||
import {useStores} from "../../hooks/useStores.tsx";
|
||||
|
||||
export const CheckBalanceButton = observer(() => {
|
||||
const { checkBalance, result } = useCheckBalance()
|
||||
const { userStore } = useStores()
|
||||
useEffect(() => {
|
||||
if (result) userStore.setBalance(result.balance)
|
||||
}, [result])
|
||||
|
||||
return (
|
||||
<button onClick={checkBalance}>
|
||||
Check balance
|
||||
</button>
|
||||
);
|
||||
});
|
36
frontend/casino/src/app/web3/balance/useCheckBalance.ts
Normal file
36
frontend/casino/src/app/web3/balance/useCheckBalance.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import {useWalletConnect} from "@cityofzion/wallet-connect-sdk-react";
|
||||
import {useCallback} from "react";
|
||||
import {useGetResult} from "../functions/utils/useGetResult.ts";
|
||||
import {config} from "../functions/config/config.ts";
|
||||
|
||||
export const useCheckBalance = () => {
|
||||
const wcSdk = useWalletConnect()
|
||||
const { getResult, ...statuses } = useGetResult()
|
||||
|
||||
const checkBalance = useCallback(async () => {
|
||||
console.log(config.zaCoin.contractAddress)
|
||||
const address = wcSdk.getAccountAddress()
|
||||
|
||||
if (!address) return
|
||||
|
||||
const resp = await wcSdk.invokeFunction({
|
||||
invocations: [{
|
||||
scriptHash: config.zaCoin.contractAddress,
|
||||
operation: 'balanceOf',
|
||||
args: [
|
||||
{ type: 'Hash160', value: address },
|
||||
]
|
||||
}],
|
||||
signers: [{
|
||||
scopes: 'Global',
|
||||
}]
|
||||
})
|
||||
console.log(resp)
|
||||
await getResult(resp, 'balance')
|
||||
}, [wcSdk, getResult])
|
||||
|
||||
return {
|
||||
checkBalance,
|
||||
...statuses
|
||||
}
|
||||
}
|
1
frontend/casino/src/app/web3/connect/index.ts
Normal file
1
frontend/casino/src/app/web3/connect/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './ui'
|
10
frontend/casino/src/app/web3/connect/lib/config.ts
Normal file
10
frontend/casino/src/app/web3/connect/lib/config.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
export const wcOptions = {
|
||||
projectId: '0a899bd03bff0aea6f838649d79b1983', // the ID of your project on Wallet Connect website
|
||||
relayUrl: 'wss://relay.walletconnect.com', // we are using walletconnect's official relay server
|
||||
metadata: {
|
||||
name: 'MyApplicationName', // your application name to be displayed on the wallet
|
||||
description: 'My Application description', // description to be shown on the wallet
|
||||
url: 'https://myapplicationdescription.app/', // url to be linked on the wallet
|
||||
icons: ['https://myapplicationdescription.app/myappicon.png'] // icon to be shown on the wallet
|
||||
}
|
||||
};
|
39
frontend/casino/src/app/web3/connect/lib/useConnect.ts
Normal file
39
frontend/casino/src/app/web3/connect/lib/useConnect.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import {useWalletConnect} from "@cityofzion/wallet-connect-sdk-react";
|
||||
import {useCallback, useEffect, useState} from "react";
|
||||
|
||||
export const useConnect = () => {
|
||||
const wcSdk = useWalletConnect()
|
||||
const [isConnected, setIsConnected] = useState<boolean>(false)
|
||||
|
||||
const checkIsConnected = useCallback(() => {
|
||||
const isConnectedResult = wcSdk.isConnected()
|
||||
setIsConnected(isConnectedResult)
|
||||
}, [wcSdk])
|
||||
|
||||
const connect = useCallback(async () => {
|
||||
await wcSdk.connect('neo3:testnet', ['invokeFunction', 'testInvoke', 'signMessage','verifyMessage'])
|
||||
|
||||
checkIsConnected()
|
||||
}, [wcSdk, checkIsConnected])
|
||||
|
||||
const disconnect = useCallback(async () => {
|
||||
if (!wcSdk.isConnected()) {
|
||||
setIsConnected(false)
|
||||
}
|
||||
|
||||
await wcSdk.disconnect()
|
||||
|
||||
checkIsConnected()
|
||||
}, [wcSdk, checkIsConnected, setIsConnected])
|
||||
|
||||
useEffect(() => {
|
||||
checkIsConnected()
|
||||
console.log(isConnected)
|
||||
}, [wcSdk, checkIsConnected])
|
||||
|
||||
return {
|
||||
connect,
|
||||
disconnect,
|
||||
isConnected
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import {useConnect} from "../../lib/useConnect.ts";
|
||||
import WalletIcon from "../../../../components/icons/WalletIcon.tsx";
|
||||
|
||||
export const ConnectButton = () => {
|
||||
const { connect } = useConnect()
|
||||
return (
|
||||
<div
|
||||
onClick={connect}
|
||||
className="flex flex-row items-center rounded-[100px] border-[2px] border-[#4F5563] bg-[#3B414F] px-[14px] py-[11px] gap-[3px] cursor-pointer">
|
||||
{/* todo: load from .vg file in assets */}
|
||||
<WalletIcon/>
|
||||
<button>
|
||||
Connect
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
import {useConnect} from "../../lib/useConnect.ts";
|
||||
import {DisconnectButton} from "../DisconnectButton/DisconnectButton.tsx";
|
||||
import {ConnectButton} from "../ConnectButton/ConnectButton.tsx";
|
||||
|
||||
export const ConnectStateButton = () => {
|
||||
const { isConnected } = useConnect()
|
||||
if (isConnected) return <DisconnectButton/>
|
||||
|
||||
return <ConnectButton/>
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export * from './ConnectStateButton.tsx'
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue