@caatinga/cli 0.2.3 → 0.2.4
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.
- package/dist/index.js +23 -8
- package/package.json +2 -2
- package/templates/marketplace-with-token/README.md +21 -0
- package/templates/marketplace-with-token/caatinga.artifacts.json +10 -0
- package/templates/marketplace-with-token/caatinga.config.ts +30 -0
- package/templates/marketplace-with-token/caatinga.template.json +21 -0
- package/templates/marketplace-with-token/contracts/marketplace/Cargo.lock +1731 -0
- package/templates/marketplace-with-token/contracts/marketplace/Cargo.toml +24 -0
- package/templates/marketplace-with-token/contracts/marketplace/src/lib.rs +43 -0
- package/templates/marketplace-with-token/contracts/token/Cargo.lock +1731 -0
- package/templates/marketplace-with-token/contracts/token/Cargo.toml +24 -0
- package/templates/marketplace-with-token/contracts/token/src/lib.rs +13 -0
- package/templates/marketplace-with-token/index.html +12 -0
- package/templates/marketplace-with-token/package.json +28 -0
- package/templates/marketplace-with-token/src/App.tsx +57 -0
- package/templates/marketplace-with-token/src/main.ts +12 -0
- package/templates/marketplace-with-token/src/main.tsx +10 -0
- package/templates/marketplace-with-token/src/styles.css +157 -0
- package/templates/marketplace-with-token/tsconfig.json +21 -0
- package/templates/marketplace-with-token/vite.config.ts +6 -0
- package/templates/react-vite-counter/.env.example +5 -0
- package/templates/react-vite-counter/README.md +67 -0
- package/templates/react-vite-counter/caatinga.artifacts.json +10 -0
- package/templates/react-vite-counter/caatinga.config.ts +26 -0
- package/templates/react-vite-counter/caatinga.template.json +21 -0
- package/templates/react-vite-counter/contracts/counter/Cargo.lock +1731 -0
- package/templates/react-vite-counter/contracts/counter/Cargo.toml +24 -0
- package/templates/react-vite-counter/contracts/counter/src/lib.rs +36 -0
- package/templates/react-vite-counter/index.html +12 -0
- package/templates/react-vite-counter/package.json +30 -0
- package/templates/react-vite-counter/public/.gitkeep +1 -0
- package/templates/react-vite-counter/src/App.tsx +18 -0
- package/templates/react-vite-counter/src/caatinga.ts +20 -0
- package/templates/react-vite-counter/src/components/CounterCard.tsx +82 -0
- package/templates/react-vite-counter/src/components/WalletButton.tsx +69 -0
- package/templates/react-vite-counter/src/contracts/generated/counter.ts +58 -0
- package/templates/react-vite-counter/src/hooks/useStellarWallet.ts +70 -0
- package/templates/react-vite-counter/src/main.tsx +10 -0
- package/templates/react-vite-counter/src/styles.css +206 -0
- package/templates/react-vite-counter/src/wallet.ts +28 -0
- package/templates/react-vite-counter/tsconfig.json +22 -0
- package/templates/react-vite-counter/vite.config.ts +16 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "counter"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
rust-version = "1.84.0"
|
|
5
|
+
edition = "2021"
|
|
6
|
+
|
|
7
|
+
[lib]
|
|
8
|
+
crate-type = ["cdylib"]
|
|
9
|
+
|
|
10
|
+
[dependencies]
|
|
11
|
+
soroban-sdk = "22.0.1"
|
|
12
|
+
|
|
13
|
+
[dev-dependencies]
|
|
14
|
+
soroban-sdk = { version = "22.0.1", features = ["testutils"] }
|
|
15
|
+
|
|
16
|
+
[profile.release]
|
|
17
|
+
opt-level = "z"
|
|
18
|
+
overflow-checks = true
|
|
19
|
+
debug = 0
|
|
20
|
+
strip = "symbols"
|
|
21
|
+
debug-assertions = false
|
|
22
|
+
panic = "abort"
|
|
23
|
+
codegen-units = 1
|
|
24
|
+
lto = true
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#![no_std]
|
|
2
|
+
|
|
3
|
+
use soroban_sdk::{contract, contractimpl, Env};
|
|
4
|
+
|
|
5
|
+
#[contract]
|
|
6
|
+
pub struct CounterContract;
|
|
7
|
+
|
|
8
|
+
#[contractimpl]
|
|
9
|
+
impl CounterContract {
|
|
10
|
+
pub fn get(env: Env) -> u32 {
|
|
11
|
+
env.storage().instance().get(&"count").unwrap_or(0)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
pub fn increment(env: Env) -> u32 {
|
|
15
|
+
let count = Self::get(env.clone()) + 1;
|
|
16
|
+
env.storage().instance().set(&"count", &count);
|
|
17
|
+
count
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[cfg(test)]
|
|
22
|
+
mod test {
|
|
23
|
+
use super::*;
|
|
24
|
+
use soroban_sdk::Env;
|
|
25
|
+
|
|
26
|
+
#[test]
|
|
27
|
+
fn increments_counter() {
|
|
28
|
+
let env = Env::default();
|
|
29
|
+
let contract_id = env.register(CounterContract, ());
|
|
30
|
+
let client = CounterContractClient::new(&env, &contract_id);
|
|
31
|
+
|
|
32
|
+
assert_eq!(client.get(), 0);
|
|
33
|
+
assert_eq!(client.increment(), 1);
|
|
34
|
+
assert_eq!(client.get(), 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -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>__PROJECT_NAME__</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,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc && vite build",
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"caatinga:build": "caatinga build counter",
|
|
11
|
+
"caatinga:deploy": "caatinga deploy counter",
|
|
12
|
+
"caatinga:generate": "caatinga generate counter"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@caatinga/client": "^0.2.4",
|
|
16
|
+
"@caatinga/core": "^0.2.4",
|
|
17
|
+
"@creit.tech/xbull-wallet-connect": "github:Creit-Tech/xBull-Wallet-Connect",
|
|
18
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
19
|
+
"react": "^18.3.1",
|
|
20
|
+
"react-dom": "^18.3.1",
|
|
21
|
+
"stellar-wallets-kit": "github:Creit-Tech/Stellar-Wallets-Kit#v0.0.7",
|
|
22
|
+
"vite": "^6.0.6"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@caatinga/cli": "^0.2.4",
|
|
26
|
+
"@types/react": "^18.3.18",
|
|
27
|
+
"@types/react-dom": "^18.3.5",
|
|
28
|
+
"typescript": "^5.7.2"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CounterCard } from "./components/CounterCard";
|
|
2
|
+
import { WalletButton } from "./components/WalletButton";
|
|
3
|
+
|
|
4
|
+
export default function App() {
|
|
5
|
+
return (
|
|
6
|
+
<main className="app-shell">
|
|
7
|
+
<header className="topbar">
|
|
8
|
+
<div>
|
|
9
|
+
<p className="eyebrow">Caatinga</p>
|
|
10
|
+
<h1>__PROJECT_NAME__</h1>
|
|
11
|
+
</div>
|
|
12
|
+
<WalletButton />
|
|
13
|
+
</header>
|
|
14
|
+
|
|
15
|
+
<CounterCard />
|
|
16
|
+
</main>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createCaatingaClient } from "@caatinga/client";
|
|
2
|
+
import type { CaatingaArtifacts } from "@caatinga/core/browser";
|
|
3
|
+
import artifactsJson from "../caatinga.artifacts.json";
|
|
4
|
+
import * as Counter from "./contracts/generated/counter.js";
|
|
5
|
+
import { stellarWalletAdapter } from "./wallet.js";
|
|
6
|
+
|
|
7
|
+
const artifacts = artifactsJson as CaatingaArtifacts;
|
|
8
|
+
|
|
9
|
+
export const caatingaClient = createCaatingaClient({
|
|
10
|
+
network: {
|
|
11
|
+
name: "testnet",
|
|
12
|
+
rpcUrl: "https://soroban-testnet.stellar.org",
|
|
13
|
+
networkPassphrase: "Test SDF Network ; September 2015"
|
|
14
|
+
},
|
|
15
|
+
artifacts,
|
|
16
|
+
wallet: stellarWalletAdapter,
|
|
17
|
+
contracts: {
|
|
18
|
+
counter: { binding: Counter }
|
|
19
|
+
}
|
|
20
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
import { caatingaClient } from "../caatinga.js";
|
|
3
|
+
import { CaatingaError } from "@caatinga/core/browser";
|
|
4
|
+
|
|
5
|
+
function formatCaatingaError(error: unknown): string {
|
|
6
|
+
if (error instanceof CaatingaError) {
|
|
7
|
+
return `[${error.code}] ${error.message}\n\n${error.hint}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return error instanceof Error ? error.message : String(error);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function CounterCard() {
|
|
14
|
+
const [count, setCount] = useState<number | null>(null);
|
|
15
|
+
const [loading, setLoading] = useState(false);
|
|
16
|
+
const [error, setError] = useState<string | null>(null);
|
|
17
|
+
const formattedCount = useMemo(
|
|
18
|
+
() => (count === null ? "Unknown" : new Intl.NumberFormat().format(count)),
|
|
19
|
+
[count]
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const refresh = useCallback(async () => {
|
|
23
|
+
setLoading(true);
|
|
24
|
+
setError(null);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const nextCount = await caatingaClient.contract("counter").read<number>("get");
|
|
28
|
+
setCount(nextCount);
|
|
29
|
+
} catch (caught) {
|
|
30
|
+
setError(formatCaatingaError(caught));
|
|
31
|
+
} finally {
|
|
32
|
+
setLoading(false);
|
|
33
|
+
}
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
async function increment() {
|
|
37
|
+
setLoading(true);
|
|
38
|
+
setError(null);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const result = await caatingaClient.contract("counter").invoke<number>("increment");
|
|
42
|
+
if (typeof result.result === "number") {
|
|
43
|
+
setCount(result.result);
|
|
44
|
+
} else {
|
|
45
|
+
await refresh();
|
|
46
|
+
}
|
|
47
|
+
} catch (caught) {
|
|
48
|
+
setError(formatCaatingaError(caught));
|
|
49
|
+
} finally {
|
|
50
|
+
setLoading(false);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<section className="counter-panel" aria-labelledby="counter-title">
|
|
56
|
+
<div className="counter-panel__header">
|
|
57
|
+
<div>
|
|
58
|
+
<p className="eyebrow">Counter Contract</p>
|
|
59
|
+
<h2 id="counter-title">Counter</h2>
|
|
60
|
+
</div>
|
|
61
|
+
<span className="network-pill">testnet</span>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div className="counter-value">{formattedCount}</div>
|
|
65
|
+
|
|
66
|
+
<div className="counter-actions">
|
|
67
|
+
<button type="button" onClick={increment} disabled={loading}>
|
|
68
|
+
{loading ? "Incrementing…" : "Increment"}
|
|
69
|
+
</button>
|
|
70
|
+
<button className="secondary-button" type="button" onClick={refresh} disabled={loading}>
|
|
71
|
+
Refresh
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
{error ? (
|
|
76
|
+
<pre className="counter-error" role="alert">
|
|
77
|
+
{error}
|
|
78
|
+
</pre>
|
|
79
|
+
) : null}
|
|
80
|
+
</section>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { WalletNetwork, WalletType } from "../wallet.js";
|
|
2
|
+
import { useStellarWallet } from "../hooks/useStellarWallet.js";
|
|
3
|
+
|
|
4
|
+
function shortenAddress(address: string): string {
|
|
5
|
+
if (address.length <= 12) {
|
|
6
|
+
return address;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return `${address.slice(0, 4)}…${address.slice(-4)}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function WalletButton() {
|
|
13
|
+
const {
|
|
14
|
+
publicKey,
|
|
15
|
+
selectedWallet,
|
|
16
|
+
network,
|
|
17
|
+
loading,
|
|
18
|
+
error,
|
|
19
|
+
selectWallet,
|
|
20
|
+
selectNetwork,
|
|
21
|
+
connect,
|
|
22
|
+
disconnect
|
|
23
|
+
} = useStellarWallet();
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="wallet-shell">
|
|
27
|
+
<div className="wallet-controls">
|
|
28
|
+
<label>
|
|
29
|
+
<span>Wallet</span>
|
|
30
|
+
<select
|
|
31
|
+
value={selectedWallet}
|
|
32
|
+
onChange={(event) => void selectWallet(event.target.value as WalletType)}
|
|
33
|
+
>
|
|
34
|
+
<option value={WalletType.XBULL}>xBull</option>
|
|
35
|
+
<option value={WalletType.FREIGHTER}>Freighter</option>
|
|
36
|
+
<option value={WalletType.ALBEDO}>Albedo</option>
|
|
37
|
+
<option value={WalletType.RABET}>Rabet</option>
|
|
38
|
+
<option value={WalletType.WALLET_CONNECT}>WalletConnect</option>
|
|
39
|
+
</select>
|
|
40
|
+
</label>
|
|
41
|
+
<label>
|
|
42
|
+
<span>Network</span>
|
|
43
|
+
<select
|
|
44
|
+
value={network}
|
|
45
|
+
onChange={(event) => void selectNetwork(event.target.value as WalletNetwork)}
|
|
46
|
+
>
|
|
47
|
+
<option value={WalletNetwork.TESTNET}>Testnet</option>
|
|
48
|
+
<option value={WalletNetwork.PUBLIC}>Public</option>
|
|
49
|
+
</select>
|
|
50
|
+
</label>
|
|
51
|
+
</div>
|
|
52
|
+
<button
|
|
53
|
+
className="wallet-button"
|
|
54
|
+
type="button"
|
|
55
|
+
onClick={publicKey ? disconnect : () => void connect()}
|
|
56
|
+
disabled={loading}
|
|
57
|
+
aria-live="polite"
|
|
58
|
+
>
|
|
59
|
+
<span className={publicKey ? "status-dot status-dot--on" : "status-dot"} />
|
|
60
|
+
{loading ? "Connecting..." : publicKey ? shortenAddress(publicKey) : "Connect"}
|
|
61
|
+
</button>
|
|
62
|
+
{error ? (
|
|
63
|
+
<p className="wallet-error" role="alert">
|
|
64
|
+
{error}
|
|
65
|
+
</p>
|
|
66
|
+
) : null}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
type TransactionResult = {
|
|
2
|
+
txHash: string;
|
|
3
|
+
result?: unknown;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
type SignTransaction = (
|
|
7
|
+
xdr: string,
|
|
8
|
+
opts?: { networkPassphrase?: string; address?: string }
|
|
9
|
+
) => Promise<{ signedTxXdr: string }> | { signedTxXdr: string };
|
|
10
|
+
|
|
11
|
+
class ExampleTransaction {
|
|
12
|
+
constructor(
|
|
13
|
+
private readonly method: string,
|
|
14
|
+
private readonly result?: unknown
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
toXDR(): string {
|
|
18
|
+
return `example-${this.method}-xdr`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async prepare(): Promise<ExampleTransaction> {
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async signAndSend(input?: { signTransaction?: SignTransaction }): Promise<TransactionResult> {
|
|
26
|
+
const signed = input?.signTransaction
|
|
27
|
+
? await input.signTransaction(this.toXDR())
|
|
28
|
+
: { signedTxXdr: this.toXDR() };
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
txHash: `example-transaction-hash:${signed.signedTxXdr}`,
|
|
32
|
+
result: this.result
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class Client {
|
|
38
|
+
constructor(
|
|
39
|
+
private readonly input: {
|
|
40
|
+
contractId: string;
|
|
41
|
+
publicKey: string;
|
|
42
|
+
rpcUrl: string;
|
|
43
|
+
networkPassphrase: string;
|
|
44
|
+
}
|
|
45
|
+
) {}
|
|
46
|
+
|
|
47
|
+
increment(): ExampleTransaction {
|
|
48
|
+
return new ExampleTransaction("increment");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
get(): ExampleTransaction {
|
|
52
|
+
return new ExampleTransaction("get", 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe(): string {
|
|
56
|
+
return `${this.input.contractId}:${this.input.publicKey}`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { stellarWalletAdapter, WalletNetwork, WalletType } from "../wallet.js";
|
|
3
|
+
|
|
4
|
+
export function useStellarWallet() {
|
|
5
|
+
const [publicKey, setPublicKey] = useState<string | null>(null);
|
|
6
|
+
const [selectedWallet, setSelectedWallet] = useState<WalletType>(WalletType.XBULL);
|
|
7
|
+
const [network, setNetwork] = useState<WalletNetwork>(WalletNetwork.TESTNET);
|
|
8
|
+
const [loading, setLoading] = useState(false);
|
|
9
|
+
const [error, setError] = useState<string | null>(null);
|
|
10
|
+
|
|
11
|
+
const selectWallet = useCallback(async (wallet: WalletType) => {
|
|
12
|
+
setError(null);
|
|
13
|
+
await stellarWalletAdapter.setWallet(wallet);
|
|
14
|
+
setSelectedWallet(wallet);
|
|
15
|
+
setPublicKey(null);
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
const selectNetwork = useCallback(async (nextNetwork: WalletNetwork) => {
|
|
19
|
+
setError(null);
|
|
20
|
+
await stellarWalletAdapter.setNetwork(nextNetwork);
|
|
21
|
+
setNetwork(nextNetwork);
|
|
22
|
+
setPublicKey(null);
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const connect = useCallback(async () => {
|
|
26
|
+
setLoading(true);
|
|
27
|
+
setError(null);
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
if (selectedWallet === WalletType.WALLET_CONNECT) {
|
|
31
|
+
await stellarWalletAdapter.startWalletConnect();
|
|
32
|
+
await stellarWalletAdapter.connectWalletConnect();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const key = await stellarWalletAdapter.getPublicKey();
|
|
36
|
+
setPublicKey(key);
|
|
37
|
+
return key;
|
|
38
|
+
} catch (caught) {
|
|
39
|
+
const message = caught instanceof Error ? caught.message : String(caught);
|
|
40
|
+
setPublicKey(null);
|
|
41
|
+
setError(message);
|
|
42
|
+
throw caught;
|
|
43
|
+
} finally {
|
|
44
|
+
setLoading(false);
|
|
45
|
+
}
|
|
46
|
+
}, [selectedWallet]);
|
|
47
|
+
|
|
48
|
+
const disconnect = useCallback(() => {
|
|
49
|
+
setPublicKey(null);
|
|
50
|
+
setError(null);
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
stellarWalletAdapter.onWalletConnectSessionDeleted(() => {
|
|
55
|
+
setPublicKey(null);
|
|
56
|
+
});
|
|
57
|
+
}, []);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
publicKey,
|
|
61
|
+
selectedWallet,
|
|
62
|
+
network,
|
|
63
|
+
loading,
|
|
64
|
+
error,
|
|
65
|
+
selectWallet,
|
|
66
|
+
selectNetwork,
|
|
67
|
+
connect,
|
|
68
|
+
disconnect
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
color: #20232a;
|
|
3
|
+
background: #f4f2ec;
|
|
4
|
+
font-family:
|
|
5
|
+
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
6
|
+
font-synthesis: none;
|
|
7
|
+
text-rendering: optimizeLegibility;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
* {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
min-width: 320px;
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
margin: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
button {
|
|
21
|
+
min-height: 44px;
|
|
22
|
+
border: 0;
|
|
23
|
+
border-radius: 8px;
|
|
24
|
+
padding: 0 16px;
|
|
25
|
+
background: #1d6154;
|
|
26
|
+
color: #ffffff;
|
|
27
|
+
font: inherit;
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
button:hover {
|
|
33
|
+
background: #174d44;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.app-shell {
|
|
37
|
+
width: min(960px, calc(100vw - 32px));
|
|
38
|
+
margin: 0 auto;
|
|
39
|
+
padding: 24px 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.topbar {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: space-between;
|
|
46
|
+
gap: 16px;
|
|
47
|
+
margin-bottom: 32px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.topbar h1,
|
|
51
|
+
.counter-panel h2 {
|
|
52
|
+
margin: 0;
|
|
53
|
+
color: #16181d;
|
|
54
|
+
letter-spacing: 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.topbar h1 {
|
|
58
|
+
font-size: clamp(2rem, 8vw, 4rem);
|
|
59
|
+
line-height: 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.counter-panel h2 {
|
|
63
|
+
font-size: 1.35rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.eyebrow {
|
|
67
|
+
margin: 0 0 8px;
|
|
68
|
+
color: #697076;
|
|
69
|
+
font-size: 0.78rem;
|
|
70
|
+
font-weight: 800;
|
|
71
|
+
letter-spacing: 0.08em;
|
|
72
|
+
text-transform: uppercase;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.wallet-shell {
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
align-items: flex-end;
|
|
79
|
+
gap: 8px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.wallet-controls {
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-wrap: wrap;
|
|
85
|
+
justify-content: flex-end;
|
|
86
|
+
gap: 8px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.wallet-controls label {
|
|
90
|
+
display: grid;
|
|
91
|
+
gap: 4px;
|
|
92
|
+
color: #45515a;
|
|
93
|
+
font-size: 0.72rem;
|
|
94
|
+
font-weight: 800;
|
|
95
|
+
text-transform: uppercase;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.wallet-controls select {
|
|
99
|
+
min-height: 36px;
|
|
100
|
+
border: 1px solid #d9d5ca;
|
|
101
|
+
border-radius: 8px;
|
|
102
|
+
background: #ffffff;
|
|
103
|
+
color: #20232a;
|
|
104
|
+
font: inherit;
|
|
105
|
+
font-size: 0.86rem;
|
|
106
|
+
text-transform: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.wallet-error,
|
|
110
|
+
.counter-error {
|
|
111
|
+
margin: 0;
|
|
112
|
+
max-width: min(360px, 100%);
|
|
113
|
+
color: #8b1e1e;
|
|
114
|
+
font-size: 0.82rem;
|
|
115
|
+
line-height: 1.4;
|
|
116
|
+
white-space: pre-wrap;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.counter-error {
|
|
120
|
+
margin-top: 16px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.wallet-button,
|
|
124
|
+
.network-pill {
|
|
125
|
+
display: inline-flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
gap: 8px;
|
|
128
|
+
white-space: nowrap;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.status-dot {
|
|
132
|
+
width: 8px;
|
|
133
|
+
height: 8px;
|
|
134
|
+
border-radius: 999px;
|
|
135
|
+
background: #a9afb4;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.status-dot--on {
|
|
139
|
+
background: #66c887;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.counter-panel {
|
|
143
|
+
border: 1px solid #d9d5ca;
|
|
144
|
+
border-radius: 8px;
|
|
145
|
+
padding: clamp(20px, 5vw, 40px);
|
|
146
|
+
background: #fffdf7;
|
|
147
|
+
box-shadow: 0 18px 60px rgba(32, 35, 42, 0.08);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.counter-panel__header {
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: flex-start;
|
|
153
|
+
justify-content: space-between;
|
|
154
|
+
gap: 16px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.network-pill {
|
|
158
|
+
min-height: 32px;
|
|
159
|
+
border: 1px solid #d9d5ca;
|
|
160
|
+
border-radius: 999px;
|
|
161
|
+
padding: 0 12px;
|
|
162
|
+
color: #45515a;
|
|
163
|
+
font-weight: 700;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.counter-value {
|
|
167
|
+
margin: 48px 0;
|
|
168
|
+
color: #111318;
|
|
169
|
+
font-size: clamp(5rem, 24vw, 12rem);
|
|
170
|
+
font-weight: 900;
|
|
171
|
+
line-height: 0.9;
|
|
172
|
+
letter-spacing: 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.counter-actions {
|
|
176
|
+
display: flex;
|
|
177
|
+
flex-wrap: wrap;
|
|
178
|
+
gap: 12px;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.secondary-button {
|
|
182
|
+
border: 1px solid #d9d5ca;
|
|
183
|
+
background: #ffffff;
|
|
184
|
+
color: #20232a;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.secondary-button:hover {
|
|
188
|
+
background: #f4f2ec;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@media (max-width: 560px) {
|
|
192
|
+
.topbar,
|
|
193
|
+
.counter-panel__header {
|
|
194
|
+
align-items: stretch;
|
|
195
|
+
flex-direction: column;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.wallet-button,
|
|
199
|
+
.wallet-controls,
|
|
200
|
+
.wallet-controls label,
|
|
201
|
+
.wallet-controls select,
|
|
202
|
+
.counter-actions button {
|
|
203
|
+
width: 100%;
|
|
204
|
+
justify-content: center;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createStellarWalletsKitAdapter,
|
|
3
|
+
type StellarWalletsKitMetadata
|
|
4
|
+
} from "@caatinga/client/stellar-wallets-kit";
|
|
5
|
+
import { WalletNetwork, WalletType } from "stellar-wallets-kit";
|
|
6
|
+
|
|
7
|
+
export const stellarWalletAdapter = createStellarWalletsKitAdapter({
|
|
8
|
+
network: WalletNetwork.TESTNET,
|
|
9
|
+
selectedWallet: WalletType.XBULL,
|
|
10
|
+
walletConnectMetadata: getWalletConnectMetadata()
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export { WalletNetwork, WalletType };
|
|
14
|
+
|
|
15
|
+
function getWalletConnectMetadata(): StellarWalletsKitMetadata | undefined {
|
|
16
|
+
const projectId = import.meta.env.VITE_WALLETCONNECT_PROJECT_ID as string | undefined;
|
|
17
|
+
if (!projectId) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
projectId,
|
|
23
|
+
name: import.meta.env.VITE_APP_NAME ?? "__PROJECT_NAME__",
|
|
24
|
+
description: import.meta.env.VITE_APP_DESCRIPTION ?? "Caatinga counter dApp",
|
|
25
|
+
url: import.meta.env.VITE_APP_URL ?? window.location.origin,
|
|
26
|
+
icons: [import.meta.env.VITE_APP_ICON_URL ?? `${window.location.origin}/icon.png`]
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["DOM", "DOM.Iterable", "ES2020"],
|
|
6
|
+
"allowJs": false,
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"strict": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"module": "ESNext",
|
|
13
|
+
"moduleResolution": "bundler",
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
"types": ["vite/client"]
|
|
19
|
+
},
|
|
20
|
+
"include": ["src"],
|
|
21
|
+
"references": []
|
|
22
|
+
}
|