create-qorechain-dapp 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.24;
3
+
4
+ /// @title Counter
5
+ /// @notice A minimal storage contract: a single number you can read, set, and
6
+ /// increment. Used as the smallest useful example of deploying and
7
+ /// interacting with a contract on the QoreChain EVM Engine.
8
+ contract Counter {
9
+ /// @notice The current count.
10
+ uint256 public count;
11
+
12
+ /// @notice Emitted whenever the count changes.
13
+ event CountChanged(uint256 newCount);
14
+
15
+ /// @param initial The starting value for the counter.
16
+ constructor(uint256 initial) {
17
+ count = initial;
18
+ emit CountChanged(initial);
19
+ }
20
+
21
+ /// @notice Increase the count by one.
22
+ function increment() external {
23
+ count += 1;
24
+ emit CountChanged(count);
25
+ }
26
+
27
+ /// @notice Set the count to an explicit value.
28
+ /// @param value The new count.
29
+ function set(uint256 value) external {
30
+ count = value;
31
+ emit CountChanged(value);
32
+ }
33
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "qorechain-evm-solidity-dapp",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "A QoreChain EVM dApp: a Solidity Counter contract deployed and exercised with viem via @qorechain/evm.",
7
+ "scripts": {
8
+ "deploy": "tsx scripts/deploy.ts",
9
+ "typecheck": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "@qorechain/evm": "^0.3.0",
13
+ "viem": "^2.0.0"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^22.7.5",
17
+ "tsx": "^4.19.0",
18
+ "typescript": "^5.6.3"
19
+ }
20
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * deploy.ts — deploy the Counter contract to the QoreChain EVM Engine and
3
+ * exercise it: read the initial count, increment it, and read it back.
4
+ *
5
+ * Uses @qorechain/evm:
6
+ * - createEvmClient({ endpoints }) → a viem-backed client (chain id auto-detected)
7
+ * - deployContract(walletClient, …) → deploy and get the tx hash
8
+ * - readContract / writeContract → typed reads and writes
9
+ *
10
+ * Requirements (see README):
11
+ * - QORE_EVM_RPC_URL pointing at a reachable QoreChain EVM JSON-RPC endpoint.
12
+ * - QORE_EVM_PRIVATE_KEY for a funded EVM account (0x-prefixed, 32 bytes).
13
+ */
14
+ import {
15
+ createEvmClient,
16
+ deployContract,
17
+ evmAccountFromPrivateKey,
18
+ readContract,
19
+ writeContract,
20
+ } from "@qorechain/evm";
21
+ import type { Address, Hex } from "viem";
22
+
23
+ import { counterAbi, counterBytecode } from "../contracts/Counter.artifact.js";
24
+
25
+ function requireEnv(name: string): string {
26
+ const value = process.env[name];
27
+ if (!value) {
28
+ throw new Error(`Missing required environment variable: ${name}`);
29
+ }
30
+ return value;
31
+ }
32
+
33
+ async function main(): Promise<void> {
34
+ const evmRpc = process.env.QORE_EVM_RPC_URL ?? "http://localhost:8545";
35
+ const privateKey = requireEnv("QORE_EVM_PRIVATE_KEY") as Hex;
36
+ const initialCount = BigInt(process.env.QORE_COUNTER_INITIAL ?? "0");
37
+
38
+ const client = await createEvmClient({ endpoints: { evmRpc } });
39
+ const account = evmAccountFromPrivateKey(privateKey);
40
+ const wallet = client.getWalletClient(account);
41
+
42
+ console.log(`network: QoreChain EVM (chain id ${await client.getChainId()})`);
43
+ console.log(`deployer: ${account.address}`);
44
+
45
+ // Deploy.
46
+ console.log(`\nDeploying Counter(initial=${initialCount})…`);
47
+ const deployHash = await deployContract(wallet, {
48
+ abi: counterAbi,
49
+ bytecode: counterBytecode,
50
+ args: [initialCount],
51
+ });
52
+ const receipt = await client.publicClient.waitForTransactionReceipt({
53
+ hash: deployHash,
54
+ });
55
+ const contract = receipt.contractAddress;
56
+ if (!contract) {
57
+ throw new Error("Deployment receipt did not include a contract address.");
58
+ }
59
+ console.log(`deployed: ${contract}`);
60
+
61
+ // Read.
62
+ const before = await readContract(client.publicClient, {
63
+ address: contract as Address,
64
+ abi: counterAbi,
65
+ functionName: "count",
66
+ });
67
+ console.log(`count (before): ${before}`);
68
+
69
+ // Write: increment. `account` and `chain` come from the wallet client; we pass
70
+ // them explicitly so the call type-checks against viem's strict signature.
71
+ console.log("\nCalling increment()…");
72
+ const incHash = await writeContract(wallet, {
73
+ address: contract as Address,
74
+ abi: counterAbi,
75
+ functionName: "increment",
76
+ account: wallet.account ?? null,
77
+ chain: wallet.chain,
78
+ });
79
+ await client.publicClient.waitForTransactionReceipt({ hash: incHash });
80
+
81
+ // Read again.
82
+ const after = await readContract(client.publicClient, {
83
+ address: contract as Address,
84
+ abi: counterAbi,
85
+ functionName: "count",
86
+ });
87
+ console.log(`count (after): ${after}`);
88
+ console.log("\nDone.");
89
+ }
90
+
91
+ main().catch((err: unknown) => {
92
+ console.error("\nDeploy failed.");
93
+ console.error(
94
+ "Check QORE_EVM_RPC_URL is reachable and QORE_EVM_PRIVATE_KEY funds an account.",
95
+ );
96
+ console.error(err instanceof Error ? err.message : err);
97
+ process.exitCode = 1;
98
+ });
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "target": "ES2021",
6
+ "module": "ESNext",
7
+ "moduleResolution": "Bundler",
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "verbatimModuleSyntax": true,
12
+ "lib": ["ES2022"],
13
+ "noEmit": true,
14
+ "types": ["node"]
15
+ },
16
+ "include": ["scripts", "contracts"]
17
+ }
@@ -0,0 +1,5 @@
1
+ # QoreChain endpoints for the web app (Vite exposes only VITE_-prefixed vars).
2
+ # Defaults target localhost so the app runs out of the box against a local node.
3
+ # Point these at a testnet (qorechain-diana) or mainnet (qorechain-vladi) node.
4
+ VITE_QORE_REST_URL=http://localhost:1317
5
+ VITE_QORE_EVM_RPC_URL=http://localhost:8545
@@ -0,0 +1,53 @@
1
+ # QoreChain full-stack web starter
2
+
3
+ A minimal [Vite](https://vitejs.dev) + React + TypeScript dApp that uses
4
+ [`@qorechain/sdk`](https://github.com/qorechain/qorechain-sdk) to:
5
+
6
+ - connect to QoreChain testnet (`createClient`),
7
+ - read a native balance for an address you enter, and
8
+ - read the tokenomics overview (`qor_getTokenomicsOverview`).
9
+
10
+ ## Prerequisites
11
+
12
+ - **Node.js >= 20**.
13
+ - A reachable QoreChain **REST** endpoint (`VITE_QORE_REST_URL`) for balances and
14
+ an **EVM JSON-RPC** endpoint (`VITE_QORE_EVM_RPC_URL`) for the `qor_*`
15
+ namespace. Both default to localhost. Point them at a testnet
16
+ (`qorechain-diana`) or mainnet (`qorechain-vladi`) node, or a local node.
17
+
18
+ ## Setup & run
19
+
20
+ ```sh
21
+ pnpm install
22
+ cp .env.example .env # the scaffolder already does this for you
23
+ pnpm dev # http://localhost:5173
24
+ ```
25
+
26
+ Configure endpoints in `.env` (only `VITE_`-prefixed vars are exposed to the
27
+ browser):
28
+
29
+ | Variable | Purpose | Default |
30
+ | ---------------------- | ---------------------- | ----------------------- |
31
+ | `VITE_QORE_REST_URL` | Cosmos REST (balances) | `http://localhost:1317` |
32
+ | `VITE_QORE_EVM_RPC_URL` | EVM JSON-RPC (`qor_*`) | `http://localhost:8545` |
33
+
34
+ ## Build / type-check
35
+
36
+ ```sh
37
+ pnpm typecheck # tsc --noEmit
38
+ pnpm build # type-check + vite build
39
+ ```
40
+
41
+ ## Using `@qorechain/sdk` before it is published
42
+
43
+ `@qorechain/sdk` is published to npm — once published, `pnpm install` just
44
+ works. Until then, scaffold with the CLI's `--local` flag to rewrite the
45
+ dependency to a `file:` link into the SDK monorepo, after building the workspace
46
+ packages:
47
+
48
+ ```sh
49
+ # at the qorechain-sdk monorepo root
50
+ pnpm -r build
51
+ # then scaffold with --local
52
+ npx create-qorechain-dapp my-dapp --template fullstack-web --local
53
+ ```
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>QoreChain dApp</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "qorechain-fullstack-web-dapp",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "A minimal Vite + React + TypeScript QoreChain dApp using @qorechain/sdk to read balances and tokenomics.",
7
+ "scripts": {
8
+ "dev": "vite",
9
+ "build": "tsc --noEmit && vite build",
10
+ "preview": "vite preview",
11
+ "typecheck": "tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "@qorechain/sdk": "^0.3.0",
15
+ "react": "^18.3.1",
16
+ "react-dom": "^18.3.1"
17
+ },
18
+ "devDependencies": {
19
+ "@types/react": "^18.3.0",
20
+ "@types/react-dom": "^18.3.0",
21
+ "@vitejs/plugin-react": "^4.3.0",
22
+ "typescript": "^5.6.3",
23
+ "vite": "^5.4.0"
24
+ }
25
+ }
@@ -0,0 +1,134 @@
1
+ import { useMemo, useState } from "react";
2
+
3
+ import { createClient, type QoreChainClient } from "@qorechain/sdk";
4
+
5
+ // Endpoints come from Vite env vars (VITE_-prefixed), with localhost defaults so
6
+ // the app runs out of the box against a local node. Override in `.env`.
7
+ const REST_URL = import.meta.env.VITE_QORE_REST_URL ?? "http://localhost:1317";
8
+ const EVM_RPC_URL =
9
+ import.meta.env.VITE_QORE_EVM_RPC_URL ?? "http://localhost:8545";
10
+
11
+ interface Balance {
12
+ denom: string;
13
+ amount: string;
14
+ }
15
+
16
+ export function App(): JSX.Element {
17
+ // One client for the app's lifetime.
18
+ const client: QoreChainClient = useMemo(
19
+ () =>
20
+ createClient({
21
+ network: "testnet",
22
+ endpoints: { rest: REST_URL, evmRpc: EVM_RPC_URL },
23
+ }),
24
+ [],
25
+ );
26
+
27
+ const [address, setAddress] = useState("");
28
+ const [balances, setBalances] = useState<Balance[] | null>(null);
29
+ const [tokenomics, setTokenomics] = useState<string | null>(null);
30
+ const [error, setError] = useState<string | null>(null);
31
+ const [loading, setLoading] = useState(false);
32
+
33
+ async function loadBalance(): Promise<void> {
34
+ setError(null);
35
+ setBalances(null);
36
+ if (!address.trim()) {
37
+ setError("Enter an address first.");
38
+ return;
39
+ }
40
+ setLoading(true);
41
+ try {
42
+ const res = await client.rest.getAllBalances(address.trim());
43
+ setBalances(res.balances as Balance[]);
44
+ } catch (err) {
45
+ setError(err instanceof Error ? err.message : String(err));
46
+ } finally {
47
+ setLoading(false);
48
+ }
49
+ }
50
+
51
+ async function loadTokenomics(): Promise<void> {
52
+ setError(null);
53
+ setTokenomics(null);
54
+ setLoading(true);
55
+ try {
56
+ const overview = await client.qor.getTokenomicsOverview();
57
+ setTokenomics(JSON.stringify(overview, null, 2));
58
+ } catch (err) {
59
+ setError(err instanceof Error ? err.message : String(err));
60
+ } finally {
61
+ setLoading(false);
62
+ }
63
+ }
64
+
65
+ return (
66
+ <main
67
+ style={{
68
+ fontFamily: "system-ui, sans-serif",
69
+ maxWidth: 720,
70
+ margin: "2rem auto",
71
+ padding: "0 1rem",
72
+ lineHeight: 1.5,
73
+ }}
74
+ >
75
+ <h1>QoreChain dApp</h1>
76
+ <p style={{ color: "#555" }}>
77
+ Network: <code>{client.network.name}</code> (
78
+ <code>{client.network.chainId ?? "n/a"}</code>) &middot; REST:{" "}
79
+ <code>{REST_URL}</code>
80
+ </p>
81
+
82
+ <section style={{ marginTop: "1.5rem" }}>
83
+ <h2>Native balance</h2>
84
+ <div style={{ display: "flex", gap: "0.5rem" }}>
85
+ <input
86
+ value={address}
87
+ onChange={(e) => setAddress(e.target.value)}
88
+ placeholder="qor1…"
89
+ style={{ flex: 1, padding: "0.5rem" }}
90
+ aria-label="QoreChain address"
91
+ />
92
+ <button onClick={loadBalance} disabled={loading}>
93
+ Get balance
94
+ </button>
95
+ </div>
96
+ {balances && (
97
+ <ul>
98
+ {balances.length === 0 && <li>(no balances)</li>}
99
+ {balances.map((b) => (
100
+ <li key={b.denom}>
101
+ {b.amount} {b.denom}
102
+ </li>
103
+ ))}
104
+ </ul>
105
+ )}
106
+ </section>
107
+
108
+ <section style={{ marginTop: "1.5rem" }}>
109
+ <h2>Tokenomics overview</h2>
110
+ <button onClick={loadTokenomics} disabled={loading}>
111
+ Read qor_getTokenomicsOverview
112
+ </button>
113
+ {tokenomics && (
114
+ <pre
115
+ style={{
116
+ background: "#f5f5f5",
117
+ padding: "1rem",
118
+ overflowX: "auto",
119
+ borderRadius: 6,
120
+ }}
121
+ >
122
+ {tokenomics}
123
+ </pre>
124
+ )}
125
+ </section>
126
+
127
+ {error && (
128
+ <p style={{ color: "#b00020", marginTop: "1rem" }} role="alert">
129
+ {error}
130
+ </p>
131
+ )}
132
+ </main>
133
+ );
134
+ }
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { createRoot } from "react-dom/client";
3
+
4
+ import { App } from "./App.js";
5
+
6
+ const container = document.getElementById("root");
7
+ if (!container) {
8
+ throw new Error("Root element #root not found");
9
+ }
10
+
11
+ createRoot(container).render(
12
+ <React.StrictMode>
13
+ <App />
14
+ </React.StrictMode>,
15
+ );
@@ -0,0 +1,10 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ interface ImportMetaEnv {
4
+ readonly VITE_QORE_REST_URL?: string;
5
+ readonly VITE_QORE_EVM_RPC_URL?: string;
6
+ }
7
+
8
+ interface ImportMeta {
9
+ readonly env: ImportMetaEnv;
10
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "target": "ES2021",
6
+ "module": "ESNext",
7
+ "moduleResolution": "Bundler",
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
12
+ "jsx": "react-jsx",
13
+ "noEmit": true,
14
+ "useDefineForClassFields": true,
15
+ "types": ["vite/client"]
16
+ },
17
+ "include": ["src", "vite.config.ts"]
18
+ }
@@ -0,0 +1,10 @@
1
+ import react from "@vitejs/plugin-react";
2
+ import { defineConfig } from "vite";
3
+
4
+ // Minimal Vite config for the QoreChain full-stack web starter.
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ server: {
8
+ port: 5173,
9
+ },
10
+ });