Skip to content

Commit

Permalink
init mini game
Browse files Browse the repository at this point in the history
  • Loading branch information
Claudio La Barbera committed Sep 9, 2024
1 parent 99bfa50 commit 8c8b39a
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 59 deletions.
8 changes: 3 additions & 5 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
VITE_ENV="development"
VITE_TELEGRAM_BOT_URL="https://t.me/DisclosureGameBot"
VITE_TELEGRAM_MANIFEST_URL="https://thebatclaudio.github.io/ton-dapp-vue-template/tonconnect-manifest.json"
VITE_JETTON_SYMBOL="COIN"
VITE_TON_CONTRACT_ADDRESS="UQCxB9fUfl5s2s2J8IClW8cvSPoCpp3G1EXcu_j3kfjUhO0K"
VITE_API_BASE_URL="http://localhost/api"
VITE_TELEGRAM_BOT_URL="https://t.me/x2lotterybot"
VITE_TELEGRAM_MANIFEST_URL="https://gradoally.github.io/x2lottery/tonconnect-manifest.json"
VITE_TON_CONTRACT_ADDRESS="EQCuxD9t_gwXNlo9vNbgncgB_WB0M32EqLSOVOia6ceqtp9o"
3 changes: 0 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ services:
dockerfile: Dockerfile
expose:
- 80
environment:
VIRTUAL_HOST: disclosuregame.thebatclaud.io
LETSENCRYPT_HOST: disclosuregame.thebatclaud.io
volumes:
- .:/app
- /app/node_modules
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>
<title>TON DApp Vue Template</title>
<title>x2Lottery</title>
</head>
<body class="bg-gray-950 text-white h-screen select-none">
<div id="app" class="h-full"></div>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "ton-dapp-vue-template",
"name": "x2lottery",
"private": true,
"version": "0.1.0",
"type": "module",
Expand Down
Binary file modified public/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/loser.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions public/tonconnect-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"url": "https://thebatclaudio.github.io/ton-dapp-vue-template",
"name": "TON DApp Vue Template",
"iconUrl": "https://thebatclaudio.github.io/ton-dapp-vue-template/icon.png"
"url": "https://gradoally.github.io/x2lottery",
"name": "x2lottery",
"iconUrl": "https://gradoally.github.io/x2lottery/icon.png"
}
21 changes: 1 addition & 20 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { useRouter } from 'vue-router'
import { store } from './common/store'
import Navbar from './components/common/Navbar.vue'
const router = useRouter()
const loading = ref(true);
Expand Down Expand Up @@ -33,23 +31,6 @@ onMounted(async () => {
</keep-alive>
</router-view>
</div>
<div id="navbar-container" v-if="!loading" class="relative p-3 bg-black">
<Navbar class="relative z-20"></Navbar>
</div>
</div>
</div>
</template>

<style>
#navbar-container::before {
content: "";
position: absolute;
height: 40px;
top: -40px;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 1) 100%);
z-index: 10;
}
</style>
</template>
5 changes: 2 additions & 3 deletions src/common/store/telegram.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Reactive, reactive } from "vue";
import { TonConnectUI, TonConnectUiOptions, Account } from "@tonconnect/ui";
import { SendTransactionRequest } from "@tonconnect/ui";
import { InitDataParsed, retrieveLaunchParams } from '@telegram-apps/sdk';
import { beginCell, toNano } from "@ton/ton";

Expand All @@ -13,7 +12,7 @@ interface Telegram {
initTelegramData: () => void;
initWallet: () => Promise<void>;
initConnectWalletButton: (buttonRootId: string | null) => Promise<void>;
sendTransaction: (transaction: SendTransactionRequest) => Promise<void>;
playGame: () => Promise<void>;
}

export const Telegram: Reactive<Telegram> = reactive<Telegram>({
Expand Down Expand Up @@ -48,7 +47,7 @@ export const Telegram: Reactive<Telegram> = reactive<Telegram>({
twaReturnUrl: import.meta.env.VITE_TELEGRAM_BOT_URL,
} as TonConnectUiOptions;
},
async sendTransaction() {
async playGame() {
const body = beginCell()
.storeUint(0, 32) // write 32 zero bits to indicate that a text comment will follow
.storeStringTail("Test transaction") // write our text comment
Expand Down
116 changes: 93 additions & 23 deletions src/components/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
import { ref, computed, onMounted } from 'vue'
import { initHapticFeedback } from '@telegram-apps/sdk';
import { FaceSmileIcon } from '@heroicons/vue/24/solid';
import { HeadsOrTails } from '../../wrappers/HeadsOrTails';
import Modal from '../modals/Modal.vue';
import Button from '../common/Button.vue';
import { store } from '../../common/store';
import { Address, TonClient } from '@ton/ton';
import { TonConnectSender } from '../../wrappers/TonConnectSender';
const jettonSymbol = ref(import.meta.env.VITE_JETTON_SYMBOL);
const hapticFeedback = initHapticFeedback();
const handleClick = (event) => {
hapticFeedback.impactOccurred('heavy');
store.incrementAmount();
};
const amount = ref(1);
const modalOpen = ref(false);
const modalTitle = ref('');
Expand All @@ -29,45 +29,115 @@ const sendTransaction = () => {
store.telegram.sendTransaction();
}
let isFlipping = false;
let currentDegrees = 0;
const playGame = async () => {
if (isFlipping) return;
const collectionAddress = import.meta.env.VITE_TON_CONTRACT_ADDRESS;
const address = Address.parse(collectionAddress);
const tonClient = new TonClient({
endpoint: 'https://toncenter.com/api/v2/jsonRPC'
});
const contractProvider = tonClient.open(HeadsOrTails.createFromAddress(address));
if (this.tonConnectUI) {
await contractProvider.sendBet(
new TonConnectSender(this.tonConnectUI),
toNano(amount.value)
);
coin.classList.add("flipping");
isFlipping = true;
// wip: i need to understand how to know sendBet response
const random = Math.floor(Math.random() * 4 + 9);
currentDegrees += 180 * random;
coin.style.transform = `rotateY(${currentDegrees}deg)`;
setTimeout(() => {
isFlipping = false;
coin.classList.remove("flipping");
}, 5000);
}
}
onMounted(() => {
store.telegram.initConnectWalletButton('ton-connect-button')
});
</script>
<template>
<div class="text-center flex flex-col h-full pt-10">
<div class="font-mono text-4xl sm:text-6xl pt-10">{{ store.currentAmount }}<small class="text-sm">{{ jettonSymbol }}</small>
</div>
<div class="flex-1 flex flex-col justify-center content-center">
<div class="flex justify-center">
<div class="cursor-pointer" @click="handleClick">
<FaceSmileIcon class="size-20" />
<div id="coin">
<div id="front"><img src="/icon.png" class="pulse w-[60vw]"></div>
<div id="back"><img src="/loser.png" class="pulse w-[60vw]"></div>
</div>
</div>
<div class="mt-12">
<Button @click="playGame()">
Play
</Button>
</div>
</div>
<div class="text-center text-3xl uppercase p-5">
<button id="ton-connect-button" type="button"></button>
</div>
<div class="flex justify-center">
<Button @click="sendTransaction()">
Send test transaction
</Button>
</div>
<div class="flex justify-center mt-2">
<Button @click="openModal()">
Open modal
</Button>
</div>
<div class="flex flex-col mt-5">
<div class="uppercase text-xs">Daily taps</div>
<div class="font-mono font-bold">{{ store.user?.daily_taps }}/{{ store.maxDailyTaps }}</div>
footer
</div>
</div>
<Modal :isOpen="modalOpen" @close="modalOpen = false" :confirmButtonEnabled="false"
:cancelButtonText="'Ok'" :title="modalTitle">
<Modal :isOpen="modalOpen" @close="modalOpen = false" :confirmButtonEnabled="false" :cancelButtonText="'Ok'"
:title="modalTitle">
<p class="text-center">
{{ modalDescription }}
</p>
</Modal>
</template>
</template>
<style>
#coin {
position: relative;
width: 200px;
height: 200px;
perspective: 1000px;
transition: transform 5s ease, box-shadow 0.2s ease;
transform-style: preserve-3d;
background-color: #020b13;
box-sizing: border-box;
border-radius: 50%;
align-self: center;
box-shadow: 0 10px 10px rgba(0, 0, 0, 0.2);
}
#coin.flipping {
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2);
}
#coin>* {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
backface-visibility: hidden;
border-radius: 50%;
margin: 16px;
}
#front {
z-index: 2;
}
#back {
transform: rotateY(180deg);
}
</style>
32 changes: 32 additions & 0 deletions src/wrappers/HeadsOrTails.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Address, beginCell, Cell, Contract, contractAddress, ContractProvider, Sender, SendMode } from '@ton/core';
export const Op = { Setup: 0, Deploy: 1, TopUp: 2, Withdraw: 3, };
export const Result = { ValueError: 0, Loss: 1, };
export type HeadsOrTailsConfig = { owner: Address, min_bet: number | bigint, max_bet: number | bigint, mul_num: number | bigint, mul_denom: number | bigint, };
export function headsOrTailsConfigToCell(config: HeadsOrTailsConfig): Cell { return beginCell()
.storeAddress(config.owner)
.storeCoins(config.min_bet)
.storeCoins(config.max_bet)
.storeUint(config.mul_num, 16)
.storeUint(config.mul_denom, 16)
.endCell(); }
export class HeadsOrTails implements Contract {
constructor(readonly address: Address, readonly init?: { code: Cell; data: Cell }) {}
static createFromAddress(address: Address) { return new HeadsOrTails(address); }
static createFromConfig(config: HeadsOrTailsConfig, code: Cell, workchain = 0) {
const data = headsOrTailsConfigToCell(config);
const init = { code, data };
return new HeadsOrTails(contractAddress(workchain, init), init); }
async sendDeploy(provider: ContractProvider, via: Sender, value: bigint) { await provider.internal(via, {
value, sendMode: SendMode.PAY_GAS_SEPARATELY, body: beginCell().storeUint(Op.Deploy, 32).endCell(), }); }
async sendBet(provider: ContractProvider, via: Sender, value: bigint) {
await provider.internal(via, { value, sendMode: SendMode.PAY_GAS_SEPARATELY, body: beginCell().endCell(),}); }
async sendTopUp(provider: ContractProvider, via: Sender, value: bigint) {
await provider.internal(via, { value, sendMode: SendMode.PAY_GAS_SEPARATELY, body: beginCell().storeUint(Op.TopUp, 32).endCell(),}); }
async sendSetup(provider: ContractProvider, via: Sender, value: bigint, owner: Address, min_bet: number | bigint, max_bet: number | bigint, mul_num: number | bigint, mul_denom: number | bigint, ) {
await provider.internal(via, { value, sendMode: SendMode.PAY_GAS_SEPARATELY, body: beginCell().storeUint(Op.Setup, 32).storeUint(0, 64)
.storeAddress(owner).storeCoins(min_bet).storeCoins(max_bet).storeUint(mul_num, 16).storeUint(mul_denom, 16).endCell(),}); }
async sendWithdraw(provider: ContractProvider, via: Sender, value: bigint, w_amount: bigint) {
await provider.internal(via, { value, sendMode: SendMode.PAY_GAS_SEPARATELY, body: beginCell().storeUint(Op.Withdraw, 32).storeUint(0, 64).storeCoins(w_amount).endCell(),}); }
async getData(provider: ContractProvider) {
const result = await provider.get('get_smc_data', []);
return result.stack.readCell(); } }
35 changes: 35 additions & 0 deletions src/wrappers/TonConnectSender.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Address, beginCell, Sender, SenderArguments, SendMode, storeStateInit } from "@ton/ton";
import { TonConnectUI } from "@tonconnect/ui";

export class TonConnectSender implements Sender {
#provider: TonConnectUI;
readonly address?: Address;

constructor(provider: TonConnectUI) {
this.#provider = provider;
if (provider.wallet) this.address = Address.parseRaw(provider.wallet?.account.address);
else this.address = undefined;
}

async send(args: SenderArguments): Promise<void> {
if (!(args.sendMode === undefined || args.sendMode == SendMode.PAY_GAS_SEPARATELY)) {
throw new Error("Deployer sender does not support `sendMode` other than `PAY_GAS_SEPARATELY`");
}

console.log('send')

await this.#provider.sendTransaction({
validUntil: Date.now() + 5 * 60 * 1000,
messages: [
{
address: args.to.toString(),
amount: args.value.toString(),
payload: args.body?.toBoc().toString("base64"),
stateInit: args.init
? beginCell().storeWritable(storeStateInit(args.init)).endCell().toBoc().toString("base64")
: undefined,
},
],
});
}
}

0 comments on commit 8c8b39a

Please sign in to comment.