Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions dapp/part-1/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -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?
69 changes: 69 additions & 0 deletions dapp/part-1/frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# 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) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/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:

```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...

// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,

// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```

You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
23 changes: 23 additions & 0 deletions dapp/part-1/frontend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs['recommended-latest'],
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])
14 changes: 14 additions & 0 deletions dapp/part-1/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!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/nodeSpecific.ts"></script>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
44 changes: 44 additions & 0 deletions dapp/part-1/frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "jq -r '\"VITE_CONTRACT_ADDRESS=\" + last(.tasks[]).output[0].address' ../pokeGame/.taq/testing-state.json > .env && vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@airgap/beacon-sdk": "^4.6.1",
"@taquito/beacon-wallet": "^23.0.1",
"@taquito/taquito": "^23.0.1",
"@tzkt/sdk-api": "^2.2.1",
"react": "^19.1.1",
"react-dom": "^19.1.1"
},
"devDependencies": {
"@airgap/beacon-types": "^4.6.1",
"@eslint/js": "^9.35.0",
"@types/react": "^19.1.13",
"@types/react-dom": "^19.1.9",
"@vitejs/plugin-react-swc": "^4.0.1",
"assert": "^2.1.0",
"buffer": "^6.0.3",
"crypto-browserify": "^3.12.1",
"eslint": "^9.35.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.4.0",
"https-browserify": "^1.0.0",
"os-browserify": "^0.3.0",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"stream-browserify": "^3.0.0",
"stream-http": "^3.2.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.43.0",
"url": "^0.11.4",
"vite": "^7.1.6"
}
}
1 change: 1 addition & 0 deletions dapp/part-1/frontend/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions dapp/part-1/frontend/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}
110 changes: 110 additions & 0 deletions dapp/part-1/frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import "./init";
import { NetworkType } from '@airgap/beacon-types';
import { BeaconWallet } from '@taquito/beacon-wallet';
import { TezosToolkit } from '@taquito/taquito';
import * as api from '@tzkt/sdk-api';
import { useEffect, useState } from 'react';
import './App.css';
import ConnectButton from './ConnectWallet';
import DisconnectButton from './DisconnectWallet';
import { type PokeGameWalletType } from './pokeGame.types';

function App() {
api.defaults.baseUrl = 'https://api.ghostnet.tzkt.io';

const [Tezos, setTezos] = useState<TezosToolkit>(
new TezosToolkit('https://rpc.ghostnet.teztnets.com')
);
const [wallet, setWallet] = useState<BeaconWallet>(
new BeaconWallet({
name: 'Poke game',
network: {
type: NetworkType.GHOSTNET,
}
})
);
Tezos.setWalletProvider(wallet);

useEffect(() => {
(async () => {
const activeAccount = await wallet.client.getActiveAccount();
if (activeAccount) {
setUserAddress(activeAccount.address);
const balanceMutez = await Tezos.tz.getBalance(activeAccount.address);
const balanceTez = balanceMutez.toNumber() / 10000000;
setUserBalance(balanceTez);
}
})();
}, []);

const [userAddress, setUserAddress] = useState<string>('');
const [balanceTez, setUserBalance] = useState<number>(0);
const [contracts, setContracts] = useState<Array<api.Contract>>([]);

const fetchContracts = () => {
(async () => {
setContracts(
await api.contractsGetSimilar(import.meta.env.VITE_CONTRACT_ADDRESS, {
includeStorage: true,
sort: { desc: 'id' },
})
);
})();
};

const poke = async (contract: api.Contract) => {
let c: PokeGameWalletType = await Tezos.wallet.at<PokeGameWalletType>(
'' + contract.address
);
try {
const op = await c.methodsObject.default().send();
await op.confirmation();
alert('Tx done');
} catch (error: any) {
console.table(`Error: ${JSON.stringify(error, null, 2)}`);
}
};

return (
<div className="App">
<header className="App-header">
<ConnectButton
Tezos={Tezos}
setTezos={setTezos}
setUserAddress={setUserAddress}
setUserBalance={setUserBalance}
wallet={wallet}
/>

<DisconnectButton
wallet={wallet}
setUserAddress={setUserAddress}
setUserBalance={setUserBalance}
/>

<div>
{userAddress ?
`I am ${userAddress} with ${balanceTez} tez`
: `Click "Connect with wallet."`
}
</div>
<br />
<button onClick={fetchContracts}>Fetch contracts</button>
<table>
<thead>
<tr>
<th>Contract address</th>
<th>Pokes and messages</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{contracts.map((contract, index) => <tr key={index}><td style={{borderStyle: "dotted"}}>{contract.address}</td><td style={{borderStyle: "dotted"}}>{contract.storage.join(", ")}</td><td style={{borderStyle: "dotted"}}><button onClick={() => poke(contract)}>Poke</button></td></tr>)}
</tbody>
</table>
</header>
</div>
);
}

export default App;
47 changes: 47 additions & 0 deletions dapp/part-1/frontend/src/ConnectWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import "./init";
import { NetworkType } from "@airgap/beacon-sdk";
import { BeaconWallet } from "@taquito/beacon-wallet";
import { TezosToolkit } from "@taquito/taquito";
import { Dispatch, SetStateAction } from "react";

type ButtonProps = {
Tezos: TezosToolkit;
setUserAddress: Dispatch<SetStateAction<string>>;
setUserBalance: Dispatch<SetStateAction<number>>;
wallet: BeaconWallet;
setTezos: Dispatch<SetStateAction<TezosToolkit>>;
};
const ConnectButton = ({
Tezos,
setTezos,
setUserAddress,
setUserBalance,
wallet,
}: ButtonProps): JSX.Element => {
const connectWallet = async (): Promise<void> => {
try {
await wallet.requestPermissions();
// gets user's address
const userAddress = await wallet.getPKH();
const balanceMutez = await Tezos.tz.getBalance(userAddress);
const balanceTez = balanceMutez.toNumber() / 10000000;
setUserBalance(balanceTez);
setUserAddress(userAddress);

Tezos.setWalletProvider(wallet);
setTezos(Tezos);
} catch (error) {
console.log(error);
}
};
return (
<div className="buttons">
<button className="button" onClick={connectWallet}>
<span>
<i className="fas fa-wallet"></i>&nbsp; Connect with wallet
</span>
</button>
</div>
);
};
export default ConnectButton;
6 changes: 6 additions & 0 deletions dapp/part-1/frontend/src/Counter.code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

export const CounterCode: { __type: 'CounterCode', protocol: string, code: object[] } = {
__type: 'CounterCode',
protocol: 'PsDELPH1Kxsxt8f9eWbxQeRxkjfbxoqM52jvs5Y5fBxWWh4ifpo',
code: JSON.parse(`[{"prim":"parameter","args":[{"prim":"or","args":[{"prim":"unit","annots":["%reset"]},{"prim":"or","args":[{"prim":"int","annots":["%decrement"]},{"prim":"int","annots":["%increment"]}]}]}]},{"prim":"storage","args":[{"prim":"int"}]},{"prim":"code","args":[[[[{"prim":"DUP"},{"prim":"CAR"},{"prim":"DIP","args":[[{"prim":"CDR"}]]}]],{"prim":"IF_LEFT","args":[[{"prim":"DROP","args":[{"int":"2"}]},{"prim":"PUSH","args":[{"prim":"int"},{"int":"0"}]}],[{"prim":"IF_LEFT","args":[[{"prim":"SWAP"},{"prim":"SUB"}],[{"prim":"ADD"}]]}]]},{"prim":"NIL","args":[{"prim":"operation"}]},{"prim":"PAIR"}]]}]`)
};
Loading