@caatinga/cli 0.2.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.
- package/LICENSE +21 -0
- package/README.md +68 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +244 -0
- package/package.json +62 -0
- package/templates/marketplace-with-token/README.md +19 -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.toml +23 -0
- package/templates/marketplace-with-token/contracts/marketplace/src/lib.rs +13 -0
- package/templates/marketplace-with-token/contracts/token/Cargo.toml +23 -0
- package/templates/marketplace-with-token/contracts/token/src/lib.rs +13 -0
- package/templates/marketplace-with-token/package.json +17 -0
- package/templates/marketplace-with-token/src/main.ts +10 -0
- package/templates/marketplace-with-token/tsconfig.json +11 -0
- package/templates/react-vite-counter/README.md +58 -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.toml +23 -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 +29 -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/components/CounterCard.tsx +29 -0
- package/templates/react-vite-counter/src/components/WalletButton.tsx +12 -0
- package/templates/react-vite-counter/src/contracts/generated/.gitkeep +1 -0
- package/templates/react-vite-counter/src/main.tsx +10 -0
- package/templates/react-vite-counter/src/styles.css +155 -0
- package/templates/react-vite-counter/tsconfig.json +21 -0
- package/templates/react-vite-counter/vite.config.ts +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Kaleido contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# @caatinga/cli
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g @caatinga/cli
|
|
7
|
+
caatinga --help
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
- Node.js `>=20`
|
|
13
|
+
- Stellar CLI `>=22.0.0` and `<=25.2.0` available on `PATH`
|
|
14
|
+
- A Caatinga project with `caatinga.config.ts` for project commands such as `build`, `deploy`, `generate`, and `invoke`
|
|
15
|
+
|
|
16
|
+
If your local machine is on a newer Stellar CLI, `--allow-untested-stellar-cli` is the local-only escape hatch. CI and release workflows should stay on the supported range.
|
|
17
|
+
|
|
18
|
+
## Commands
|
|
19
|
+
|
|
20
|
+
- `caatinga init <projectName>` creates a project from a bundled template and writes `caatinga.artifacts.json`
|
|
21
|
+
- `caatinga build [contract]` builds one configured contract through Stellar CLI and defaults to `counter` when omitted
|
|
22
|
+
- `caatinga deploy [contract] --source <identity> [--network <network>] [--force] [--no-deps]` deploys contracts and records contract IDs in `caatinga.artifacts.json`
|
|
23
|
+
- `caatinga generate <contract> [--network <network>]` generates TypeScript bindings from a deployed contract ID
|
|
24
|
+
- `caatinga invoke <contract.method> --source <identity> [args...]` invokes a deployed contract method through the configured workflow
|
|
25
|
+
|
|
26
|
+
The supported CLI flow is `init -> build -> deploy -> generate -> invoke`.
|
|
27
|
+
|
|
28
|
+
## Supported Inputs
|
|
29
|
+
|
|
30
|
+
- `--source` accepts a Stellar CLI identity alias or public `G...` account address
|
|
31
|
+
- `--network <network>` selects a configured network such as `testnet`
|
|
32
|
+
- `invoke` expects a `<contract.method>` target and forwards extra args to the underlying Stellar contract invocation
|
|
33
|
+
- `deploy --no-deps` is supported only when deploying a single named contract
|
|
34
|
+
|
|
35
|
+
Unsupported input posture:
|
|
36
|
+
|
|
37
|
+
- secret keys and seed phrases are not supported CLI inputs
|
|
38
|
+
- undocumented private flags or internal repo file paths are not part of the package contract
|
|
39
|
+
|
|
40
|
+
## Error Behavior
|
|
41
|
+
|
|
42
|
+
`@caatinga/cli` emits documented `CAATINGA_*` error codes for automation. Consumers should match on the error code, not human-readable text.
|
|
43
|
+
|
|
44
|
+
Common codes include:
|
|
45
|
+
|
|
46
|
+
- `CAATINGA_CONFIG_NOT_FOUND`
|
|
47
|
+
- `CAATINGA_INVALID_CONFIG`
|
|
48
|
+
- `CAATINGA_STELLAR_CLI_NOT_FOUND`
|
|
49
|
+
- `CAATINGA_BUILD_FAILED`
|
|
50
|
+
- `CAATINGA_DEPLOY_FAILED`
|
|
51
|
+
- `CAATINGA_BINDINGS_FAILED`
|
|
52
|
+
- `CAATINGA_INVOKE_FAILED`
|
|
53
|
+
- `CAATINGA_CONTRACT_ID_NOT_FOUND`
|
|
54
|
+
- `CAATINGA_SOURCE_ACCOUNT_REQUIRED`
|
|
55
|
+
- `CAATINGA_TEMPLATE_MANIFEST_NOT_FOUND`
|
|
56
|
+
- `CAATINGA_TEMPLATE_INCOMPATIBLE`
|
|
57
|
+
|
|
58
|
+
## Relationship To `@caatinga/core`
|
|
59
|
+
|
|
60
|
+
`@caatinga/cli` is the supported end-user entrypoint for Caatinga's command workflow. It intentionally stays thin and delegates config loading, artifacts, command orchestration, and shared error primitives to `@caatinga/core`.
|
|
61
|
+
|
|
62
|
+
If you want the stable packaged workflow, prefer the CLI contract over importing `@caatinga/core` directly.
|
|
63
|
+
|
|
64
|
+
## Versioning And Stability
|
|
65
|
+
|
|
66
|
+
This package is the primary supported consumer surface for the Caatinga workflow. Stability applies to the documented commands, inputs, and `CAATINGA_*` error contract.
|
|
67
|
+
|
|
68
|
+
Undocumented internals, private module paths, and reserved hidden commands such as `caatinga dev` are not part of the stability promise.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/program.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/build.command.ts
|
|
7
|
+
import { buildContract, loadConfig } from "@caatinga/core";
|
|
8
|
+
|
|
9
|
+
// src/utils/errors.ts
|
|
10
|
+
import { toCaatingaError } from "@caatinga/core";
|
|
11
|
+
|
|
12
|
+
// src/utils/logger.ts
|
|
13
|
+
import chalk from "chalk";
|
|
14
|
+
var logger = {
|
|
15
|
+
info(message) {
|
|
16
|
+
console.log(message);
|
|
17
|
+
},
|
|
18
|
+
success(message) {
|
|
19
|
+
console.log(chalk.green(message));
|
|
20
|
+
},
|
|
21
|
+
warn(message) {
|
|
22
|
+
console.warn(chalk.yellow(message));
|
|
23
|
+
},
|
|
24
|
+
error(message) {
|
|
25
|
+
console.error(chalk.red(message));
|
|
26
|
+
},
|
|
27
|
+
muted(message) {
|
|
28
|
+
console.log(chalk.gray(message));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/utils/errors.ts
|
|
33
|
+
function printError(error) {
|
|
34
|
+
const caatingaError = toCaatingaError(error);
|
|
35
|
+
logger.error(`Error: ${caatingaError.message}`);
|
|
36
|
+
logger.info("");
|
|
37
|
+
logger.info(`Code: ${caatingaError.code}`);
|
|
38
|
+
if (caatingaError.hint) {
|
|
39
|
+
logger.info(`Hint: ${caatingaError.hint}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function runCliAction(action) {
|
|
43
|
+
try {
|
|
44
|
+
await action();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
printError(error);
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/commands/build.command.ts
|
|
52
|
+
function registerBuildCommand(program2) {
|
|
53
|
+
program2.command("build").description("Build a configured Soroban contract").argument("[contract]", "Contract name", "counter").option("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").action((contractName, options) => runCliAction(async () => {
|
|
54
|
+
const config = await loadConfig();
|
|
55
|
+
const result = await buildContract({
|
|
56
|
+
config,
|
|
57
|
+
contractName,
|
|
58
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli === true
|
|
59
|
+
});
|
|
60
|
+
logger.success("Contract built");
|
|
61
|
+
logger.info("");
|
|
62
|
+
logger.info(`Contract: ${result.contract.name}`);
|
|
63
|
+
logger.info(`WASM: ${result.contract.config.wasm}`);
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/commands/deploy.command.ts
|
|
68
|
+
import { deployContractGraph, CaatingaError, CaatingaErrorCode, loadConfig as loadConfig2 } from "@caatinga/core";
|
|
69
|
+
function registerDeployCommand(program2) {
|
|
70
|
+
program2.command("deploy").description("Deploy one or all configured Soroban contracts").argument("[contract]", "Contract name").option("-n, --network <network>", "Configured network name").requiredOption("-s, --source <source>", "Stellar CLI identity alias or public account address").option("--force", "Redeploy contracts even if artifacts already contain contract IDs").option("--no-deps", "Do not deploy missing dependencies for a selected contract").option("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").action((contractName, options) => runCliAction(async () => {
|
|
71
|
+
if (options.deps === false && !contractName) {
|
|
72
|
+
throw new CaatingaError(
|
|
73
|
+
"`--no-deps` requires a contract name.",
|
|
74
|
+
CaatingaErrorCode.INVALID_CONFIG,
|
|
75
|
+
"Select a single contract or omit `--no-deps` to deploy the full graph."
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const config = await loadConfig2();
|
|
79
|
+
const result = await deployContractGraph({
|
|
80
|
+
config,
|
|
81
|
+
contractName,
|
|
82
|
+
networkName: options.network,
|
|
83
|
+
source: options.source,
|
|
84
|
+
includeDependencies: options.deps !== false,
|
|
85
|
+
force: options.force === true,
|
|
86
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli === true
|
|
87
|
+
});
|
|
88
|
+
logger.success("Deploy complete");
|
|
89
|
+
logger.info("");
|
|
90
|
+
logger.info(`Network: ${result.network.name}`);
|
|
91
|
+
for (const contract of result.deployedContracts) {
|
|
92
|
+
logger.info(`Contract: ${contract.name}`);
|
|
93
|
+
logger.info(`Contract ID: ${contract.contractId}`);
|
|
94
|
+
}
|
|
95
|
+
logger.info("Artifacts updated: caatinga.artifacts.json");
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/commands/dev.command.ts
|
|
100
|
+
function registerDevCommand(program2) {
|
|
101
|
+
const dev = program2.command("dev", { hidden: true }).description("Reserved \u2014 not available in pre-v1").action(() => runCliAction(async () => {
|
|
102
|
+
logger.error(
|
|
103
|
+
"caatinga dev is not available yet. Use: caatinga build, deploy, generate, invoke."
|
|
104
|
+
);
|
|
105
|
+
process.exitCode = 1;
|
|
106
|
+
}));
|
|
107
|
+
dev.helpOption(false);
|
|
108
|
+
dev.configureHelp({ visibleOptions: () => [] });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/commands/generate.command.ts
|
|
112
|
+
import { generateBindings, loadConfig as loadConfig3 } from "@caatinga/core";
|
|
113
|
+
function registerGenerateCommand(program2) {
|
|
114
|
+
program2.command("generate").description("Generate TypeScript bindings for a deployed contract").argument("<contract>", "Contract name").option("-n, --network <network>", "Configured network name").option("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").action((contractName, options) => runCliAction(async () => {
|
|
115
|
+
const config = await loadConfig3();
|
|
116
|
+
const result = await generateBindings({
|
|
117
|
+
config,
|
|
118
|
+
contractName,
|
|
119
|
+
networkName: options.network,
|
|
120
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli === true
|
|
121
|
+
});
|
|
122
|
+
logger.success("Client generated");
|
|
123
|
+
logger.info("");
|
|
124
|
+
logger.info(`Contract: ${result.contractName}`);
|
|
125
|
+
logger.info(`Network: ${result.network.name}`);
|
|
126
|
+
logger.info(`Output: ${result.outputDir}`);
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/commands/init.command.ts
|
|
131
|
+
import path2 from "path";
|
|
132
|
+
import { createProjectFromTemplate } from "@caatinga/core";
|
|
133
|
+
|
|
134
|
+
// src/utils/template-path.ts
|
|
135
|
+
import { access } from "fs/promises";
|
|
136
|
+
import path from "path";
|
|
137
|
+
import { fileURLToPath } from "url";
|
|
138
|
+
import { CaatingaError as CaatingaError2, CaatingaErrorCode as CaatingaErrorCode2 } from "@caatinga/core";
|
|
139
|
+
async function resolveTemplateDir(templateName) {
|
|
140
|
+
const envTemplatesDir = process.env.CAATINGA_TEMPLATES_DIR;
|
|
141
|
+
const candidates = [
|
|
142
|
+
envTemplatesDir ? path.join(envTemplatesDir, templateName) : void 0,
|
|
143
|
+
path.resolve(process.cwd(), "packages", "templates", templateName),
|
|
144
|
+
...candidatePathsFromModule(templateName)
|
|
145
|
+
].filter((candidate) => Boolean(candidate));
|
|
146
|
+
for (const candidate of candidates) {
|
|
147
|
+
try {
|
|
148
|
+
await access(candidate);
|
|
149
|
+
return candidate;
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
throw new CaatingaError2(
|
|
154
|
+
`Template "${templateName}" was not found.`,
|
|
155
|
+
CaatingaErrorCode2.TEMPLATE_NOT_FOUND,
|
|
156
|
+
"Set CAATINGA_TEMPLATES_DIR or run from a Caatinga checkout that includes packages/templates."
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
function candidatePathsFromModule(templateName) {
|
|
160
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
161
|
+
const start = path.dirname(currentFile);
|
|
162
|
+
const candidates = [];
|
|
163
|
+
let dir = start;
|
|
164
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
165
|
+
candidates.push(path.join(dir, "packages", "templates", templateName));
|
|
166
|
+
candidates.push(path.join(dir, "templates", templateName));
|
|
167
|
+
candidates.push(path.join(dir, "node_modules", "@caatinga", "templates", templateName));
|
|
168
|
+
dir = path.dirname(dir);
|
|
169
|
+
}
|
|
170
|
+
return candidates;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/commands/init.command.ts
|
|
174
|
+
function registerInitCommand(program2) {
|
|
175
|
+
program2.command("init").description("Create a new Caatinga dApp from a template").argument("<projectName>", "Project directory to create").option("-t, --template <template>", "Template name", "react-vite-counter").action((projectName, options) => runCliAction(async () => {
|
|
176
|
+
const templateDir = await resolveTemplateDir(options.template);
|
|
177
|
+
const targetDir = path2.resolve(process.cwd(), projectName);
|
|
178
|
+
const normalizedProjectName = path2.basename(targetDir);
|
|
179
|
+
const projectDirectory = path2.isAbsolute(projectName) ? targetDir : projectName;
|
|
180
|
+
const result = await createProjectFromTemplate({
|
|
181
|
+
projectName: normalizedProjectName,
|
|
182
|
+
targetDir,
|
|
183
|
+
templateDir
|
|
184
|
+
});
|
|
185
|
+
logger.success("Project created");
|
|
186
|
+
logger.info("");
|
|
187
|
+
logger.info(`Project: ${normalizedProjectName}`);
|
|
188
|
+
logger.info(`Template: ${result.template.name}@${result.template.version}`);
|
|
189
|
+
logger.info(`Path: ${targetDir}`);
|
|
190
|
+
logger.info("");
|
|
191
|
+
logger.info("Next steps:");
|
|
192
|
+
logger.info(` cd ${projectDirectory}`);
|
|
193
|
+
logger.info(" npm install");
|
|
194
|
+
logger.info(" npx caatinga build counter");
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/commands/invoke.command.ts
|
|
199
|
+
import { invokeContract, loadConfig as loadConfig4 } from "@caatinga/core";
|
|
200
|
+
function registerInvokeCommand(program2) {
|
|
201
|
+
program2.command("invoke").description("Invoke a deployed contract function").argument("<target>", "Invoke target in contract.method format").argument("[args...]", "Arguments forwarded to Stellar CLI after the method name").option("-n, --network <network>", "Configured network name").requiredOption("-s, --source <source>", "Stellar CLI identity alias or public account address").option("--allow-untested-stellar-cli", "Allow local use of a Stellar CLI version newer than Caatinga's tested maximum").allowUnknownOption(true).allowExcessArguments(true).action((target, args, options) => runCliAction(async () => {
|
|
202
|
+
const config = await loadConfig4();
|
|
203
|
+
const result = await invokeContract({
|
|
204
|
+
config,
|
|
205
|
+
target,
|
|
206
|
+
args,
|
|
207
|
+
networkName: options.network,
|
|
208
|
+
source: options.source,
|
|
209
|
+
allowUntestedStellarCli: options.allowUntestedStellarCli === true
|
|
210
|
+
});
|
|
211
|
+
logger.success("Invoke complete");
|
|
212
|
+
logger.info("");
|
|
213
|
+
logger.info(`Network: ${result.network.name}`);
|
|
214
|
+
logger.info(`Contract: ${result.target.contractName}`);
|
|
215
|
+
logger.info(`Method: ${result.target.method}`);
|
|
216
|
+
if (result.result) {
|
|
217
|
+
logger.info("");
|
|
218
|
+
logger.info(result.result);
|
|
219
|
+
}
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// src/version.ts
|
|
224
|
+
var CAATINGA_CLI_VERSION = "0.2.0";
|
|
225
|
+
|
|
226
|
+
// src/program.ts
|
|
227
|
+
function createProgram() {
|
|
228
|
+
const program2 = new Command();
|
|
229
|
+
program2.name("caatinga").description("Developer toolkit for Stellar/Soroban dApps").version(CAATINGA_CLI_VERSION);
|
|
230
|
+
registerInitCommand(program2);
|
|
231
|
+
registerDevCommand(program2);
|
|
232
|
+
registerBuildCommand(program2);
|
|
233
|
+
registerDeployCommand(program2);
|
|
234
|
+
registerGenerateCommand(program2);
|
|
235
|
+
registerInvokeCommand(program2);
|
|
236
|
+
return program2;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/index.ts
|
|
240
|
+
var program = createProgram();
|
|
241
|
+
void program.parseAsync(process.argv).catch((error) => {
|
|
242
|
+
console.error(error);
|
|
243
|
+
process.exitCode = 1;
|
|
244
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caatinga/cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Caatinga CLI for building dApps on Stellar/Soroban",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"stellar",
|
|
7
|
+
"soroban",
|
|
8
|
+
"cli",
|
|
9
|
+
"dapp",
|
|
10
|
+
"smart-contracts",
|
|
11
|
+
"blockchain",
|
|
12
|
+
"web3",
|
|
13
|
+
"caatinga"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/Dione-b/caatinga.git",
|
|
18
|
+
"directory": "packages/cli"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/Dione-b/caatinga#readme",
|
|
21
|
+
"author": "Caatinga contributors",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"bin": {
|
|
28
|
+
"caatinga": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"main": "./dist/index.js",
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"import": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"templates",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@caatinga/core": "^0.2.0",
|
|
47
|
+
"chalk": "^5.4.1",
|
|
48
|
+
"commander": "^12.1.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"tsup": "^8.3.5",
|
|
52
|
+
"tsx": "^4.19.2",
|
|
53
|
+
"typescript": "^5.7.2",
|
|
54
|
+
"vitest": "^2.1.8"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup src/index.ts --format esm --dts --clean && rm -rf ./templates && cp -r ../templates ./templates",
|
|
58
|
+
"dev": "tsx src/index.ts",
|
|
59
|
+
"test": "vitest run --pool=threads",
|
|
60
|
+
"typecheck": "tsc --noEmit"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
Experimental Caatinga multi-contract template.
|
|
4
|
+
|
|
5
|
+
## Deploy
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npx caatinga build token
|
|
10
|
+
npx caatinga build marketplace
|
|
11
|
+
npx caatinga deploy --network testnet --source alice
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Deploy order:
|
|
15
|
+
|
|
16
|
+
1. `token`
|
|
17
|
+
2. `marketplace`
|
|
18
|
+
|
|
19
|
+
`marketplace.deployArgs.tokenContractId` resolves from `${contracts.token.contractId}` after the token deploy writes `caatinga.artifacts.json`.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { defineConfig } from "@caatinga/core";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
project: "__PROJECT_NAME__",
|
|
5
|
+
defaultNetwork: "testnet",
|
|
6
|
+
contracts: {
|
|
7
|
+
token: {
|
|
8
|
+
path: "./contracts/token",
|
|
9
|
+
wasm: "./contracts/token/target/wasm32-unknown-unknown/release/token.wasm"
|
|
10
|
+
},
|
|
11
|
+
marketplace: {
|
|
12
|
+
path: "./contracts/marketplace",
|
|
13
|
+
wasm: "./contracts/marketplace/target/wasm32-unknown-unknown/release/marketplace.wasm",
|
|
14
|
+
dependsOn: ["token"],
|
|
15
|
+
deployArgs: {
|
|
16
|
+
tokenContractId: "${contracts.token.contractId}"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
networks: {
|
|
21
|
+
testnet: {
|
|
22
|
+
rpcUrl: "https://soroban-testnet.stellar.org",
|
|
23
|
+
networkPassphrase: "Test SDF Network ; September 2015"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
frontend: {
|
|
27
|
+
framework: "vite-react",
|
|
28
|
+
bindingsOutput: "./src/contracts/generated"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "marketplace-with-token",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Experimental multi-contract Soroban template with token dependency injection.",
|
|
5
|
+
"caatinga": {
|
|
6
|
+
"compatibleCore": "^0.1.0",
|
|
7
|
+
"templateVersion": 1
|
|
8
|
+
},
|
|
9
|
+
"frontend": {
|
|
10
|
+
"framework": "vite-react",
|
|
11
|
+
"packageManager": "npm"
|
|
12
|
+
},
|
|
13
|
+
"contracts": {
|
|
14
|
+
"path": "contracts",
|
|
15
|
+
"default": "marketplace"
|
|
16
|
+
},
|
|
17
|
+
"files": {
|
|
18
|
+
"config": "caatinga.config.ts",
|
|
19
|
+
"artifacts": "caatinga.artifacts.json"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "marketplace"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[lib]
|
|
7
|
+
crate-type = ["cdylib"]
|
|
8
|
+
|
|
9
|
+
[dependencies]
|
|
10
|
+
soroban-sdk = "22.0.1"
|
|
11
|
+
|
|
12
|
+
[dev-dependencies]
|
|
13
|
+
soroban-sdk = { version = "22.0.1", features = ["testutils"] }
|
|
14
|
+
|
|
15
|
+
[profile.release]
|
|
16
|
+
opt-level = "z"
|
|
17
|
+
overflow-checks = true
|
|
18
|
+
debug = 0
|
|
19
|
+
strip = "symbols"
|
|
20
|
+
debug-assertions = false
|
|
21
|
+
panic = "abort"
|
|
22
|
+
codegen-units = 1
|
|
23
|
+
lto = true
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "token"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[lib]
|
|
7
|
+
crate-type = ["cdylib"]
|
|
8
|
+
|
|
9
|
+
[dependencies]
|
|
10
|
+
soroban-sdk = "22.0.1"
|
|
11
|
+
|
|
12
|
+
[dev-dependencies]
|
|
13
|
+
soroban-sdk = { version = "22.0.1", features = ["testutils"] }
|
|
14
|
+
|
|
15
|
+
[profile.release]
|
|
16
|
+
opt-level = "z"
|
|
17
|
+
overflow-checks = true
|
|
18
|
+
debug = 0
|
|
19
|
+
strip = "symbols"
|
|
20
|
+
debug-assertions = false
|
|
21
|
+
panic = "abort"
|
|
22
|
+
codegen-units = 1
|
|
23
|
+
lto = true
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__PROJECT_NAME__",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"caatinga:build": "caatinga build token && caatinga build marketplace",
|
|
8
|
+
"caatinga:deploy": "caatinga deploy --network testnet"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@caatinga/core": "^0.2.0"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@caatinga/cli": "^0.2.0",
|
|
15
|
+
"typescript": "^5.7.2"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const templateId = "marketplace-with-token";
|
|
2
|
+
|
|
3
|
+
export function describeDeployFlow(): string {
|
|
4
|
+
return [
|
|
5
|
+
"1. caatinga build token",
|
|
6
|
+
"2. caatinga build marketplace",
|
|
7
|
+
"3. caatinga deploy --network testnet --source <identity>",
|
|
8
|
+
"marketplace.deployArgs.tokenContractId resolves from caatinga.artifacts.json"
|
|
9
|
+
].join("\n");
|
|
10
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# __PROJECT_NAME__
|
|
2
|
+
|
|
3
|
+
Caatinga counter dApp for Stellar/Soroban.
|
|
4
|
+
|
|
5
|
+
## CLI Flow
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install
|
|
9
|
+
npx caatinga build counter
|
|
10
|
+
npx caatinga deploy counter --network testnet --source alice
|
|
11
|
+
npx caatinga generate counter --network testnet
|
|
12
|
+
npx caatinga invoke counter.increment --network testnet --source alice
|
|
13
|
+
npm run dev
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Use a Stellar CLI identity alias or public account address for `--source`; do not pass seed phrases or secret keys.
|
|
17
|
+
|
|
18
|
+
## Client Smoke Path
|
|
19
|
+
|
|
20
|
+
After `caatinga generate`, wire generated bindings to the client:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createCaatingaClient } from "@caatinga/client";
|
|
24
|
+
import { freighterWalletAdapter } from "@caatinga/client/freighter";
|
|
25
|
+
import * as Counter from "./contracts/generated/counter";
|
|
26
|
+
import artifacts from "../caatinga.artifacts.json";
|
|
27
|
+
|
|
28
|
+
export const caatingaClient = createCaatingaClient({
|
|
29
|
+
network: {
|
|
30
|
+
name: "testnet",
|
|
31
|
+
rpcUrl: "https://soroban-testnet.stellar.org",
|
|
32
|
+
networkPassphrase: "Test SDF Network ; September 2015"
|
|
33
|
+
},
|
|
34
|
+
artifacts,
|
|
35
|
+
wallet: freighterWalletAdapter,
|
|
36
|
+
contracts: {
|
|
37
|
+
counter: {
|
|
38
|
+
binding: Counter
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Build XDR without wallet signing:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const tx = await caatingaClient.contract("counter").buildXdr("increment");
|
|
48
|
+
console.log(tx.preparedXdr);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Invoke through Freighter:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const result = await caatingaClient.contract("counter").invoke("increment", {
|
|
55
|
+
debugXdr: true
|
|
56
|
+
});
|
|
57
|
+
console.log(result.transactionHash);
|
|
58
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineConfig } from "@caatinga/core";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
project: "__PROJECT_NAME__",
|
|
5
|
+
defaultNetwork: "testnet",
|
|
6
|
+
contracts: {
|
|
7
|
+
counter: {
|
|
8
|
+
path: "./contracts/counter",
|
|
9
|
+
wasm: "./contracts/counter/target/wasm32-unknown-unknown/release/counter.wasm"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
networks: {
|
|
13
|
+
testnet: {
|
|
14
|
+
rpcUrl: "https://soroban-testnet.stellar.org",
|
|
15
|
+
networkPassphrase: "Test SDF Network ; September 2015"
|
|
16
|
+
},
|
|
17
|
+
mainnet: {
|
|
18
|
+
rpcUrl: "https://mainnet.sorobanrpc.com",
|
|
19
|
+
networkPassphrase: "Public Global Stellar Network ; September 2015"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
frontend: {
|
|
23
|
+
framework: "vite-react",
|
|
24
|
+
bindingsOutput: "./src/contracts/generated"
|
|
25
|
+
}
|
|
26
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-vite-counter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Minimal Vite + React + Soroban counter dApp.",
|
|
5
|
+
"caatinga": {
|
|
6
|
+
"compatibleCore": "^0.1.0",
|
|
7
|
+
"templateVersion": 1
|
|
8
|
+
},
|
|
9
|
+
"frontend": {
|
|
10
|
+
"framework": "vite-react",
|
|
11
|
+
"packageManager": "npm"
|
|
12
|
+
},
|
|
13
|
+
"contracts": {
|
|
14
|
+
"path": "contracts",
|
|
15
|
+
"default": "counter"
|
|
16
|
+
},
|
|
17
|
+
"files": {
|
|
18
|
+
"config": "caatinga.config.ts",
|
|
19
|
+
"artifacts": "caatinga.artifacts.json"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "counter"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[lib]
|
|
7
|
+
crate-type = ["cdylib"]
|
|
8
|
+
|
|
9
|
+
[dependencies]
|
|
10
|
+
soroban-sdk = "22.0.1"
|
|
11
|
+
|
|
12
|
+
[dev-dependencies]
|
|
13
|
+
soroban-sdk = { version = "22.0.1", features = ["testutils"] }
|
|
14
|
+
|
|
15
|
+
[profile.release]
|
|
16
|
+
opt-level = "z"
|
|
17
|
+
overflow-checks = true
|
|
18
|
+
debug = 0
|
|
19
|
+
strip = "symbols"
|
|
20
|
+
debug-assertions = false
|
|
21
|
+
panic = "abort"
|
|
22
|
+
codegen-units = 1
|
|
23
|
+
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,29 @@
|
|
|
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.0",
|
|
16
|
+
"@caatinga/core": "^0.2.0",
|
|
17
|
+
"@stellar/freighter-api": "^4.0.0",
|
|
18
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
19
|
+
"vite": "^6.0.6",
|
|
20
|
+
"react": "^18.3.1",
|
|
21
|
+
"react-dom": "^18.3.1"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@caatinga/cli": "^0.2.0",
|
|
25
|
+
"@types/react": "^18.3.18",
|
|
26
|
+
"@types/react-dom": "^18.3.5",
|
|
27
|
+
"typescript": "^5.7.2"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -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,29 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export function CounterCard() {
|
|
4
|
+
const [count, setCount] = useState(0);
|
|
5
|
+
const formattedCount = useMemo(() => new Intl.NumberFormat().format(count), [count]);
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<section className="counter-panel" aria-labelledby="counter-title">
|
|
9
|
+
<div className="counter-panel__header">
|
|
10
|
+
<div>
|
|
11
|
+
<p className="eyebrow">Counter Contract</p>
|
|
12
|
+
<h2 id="counter-title">Counter</h2>
|
|
13
|
+
</div>
|
|
14
|
+
<span className="network-pill">testnet</span>
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div className="counter-value">{formattedCount}</div>
|
|
18
|
+
|
|
19
|
+
<div className="counter-actions">
|
|
20
|
+
<button type="button" onClick={() => setCount((value) => value + 1)}>
|
|
21
|
+
Increment
|
|
22
|
+
</button>
|
|
23
|
+
<button className="secondary-button" type="button" onClick={() => setCount(0)}>
|
|
24
|
+
Reset
|
|
25
|
+
</button>
|
|
26
|
+
</div>
|
|
27
|
+
</section>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
|
|
3
|
+
export function WalletButton() {
|
|
4
|
+
const [connected, setConnected] = useState(false);
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<button className="wallet-button" type="button" onClick={() => setConnected((value) => !value)}>
|
|
8
|
+
<span className={connected ? "status-dot status-dot--on" : "status-dot"} />
|
|
9
|
+
{connected ? "Connected" : "Connect"}
|
|
10
|
+
</button>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,155 @@
|
|
|
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-button,
|
|
76
|
+
.network-pill {
|
|
77
|
+
display: inline-flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
gap: 8px;
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.status-dot {
|
|
84
|
+
width: 8px;
|
|
85
|
+
height: 8px;
|
|
86
|
+
border-radius: 999px;
|
|
87
|
+
background: #a9afb4;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.status-dot--on {
|
|
91
|
+
background: #66c887;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.counter-panel {
|
|
95
|
+
border: 1px solid #d9d5ca;
|
|
96
|
+
border-radius: 8px;
|
|
97
|
+
padding: clamp(20px, 5vw, 40px);
|
|
98
|
+
background: #fffdf7;
|
|
99
|
+
box-shadow: 0 18px 60px rgba(32, 35, 42, 0.08);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.counter-panel__header {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: flex-start;
|
|
105
|
+
justify-content: space-between;
|
|
106
|
+
gap: 16px;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.network-pill {
|
|
110
|
+
min-height: 32px;
|
|
111
|
+
border: 1px solid #d9d5ca;
|
|
112
|
+
border-radius: 999px;
|
|
113
|
+
padding: 0 12px;
|
|
114
|
+
color: #45515a;
|
|
115
|
+
font-weight: 700;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.counter-value {
|
|
119
|
+
margin: 48px 0;
|
|
120
|
+
color: #111318;
|
|
121
|
+
font-size: clamp(5rem, 24vw, 12rem);
|
|
122
|
+
font-weight: 900;
|
|
123
|
+
line-height: 0.9;
|
|
124
|
+
letter-spacing: 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.counter-actions {
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-wrap: wrap;
|
|
130
|
+
gap: 12px;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.secondary-button {
|
|
134
|
+
border: 1px solid #d9d5ca;
|
|
135
|
+
background: #ffffff;
|
|
136
|
+
color: #20232a;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.secondary-button:hover {
|
|
140
|
+
background: #f4f2ec;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@media (max-width: 560px) {
|
|
144
|
+
.topbar,
|
|
145
|
+
.counter-panel__header {
|
|
146
|
+
align-items: stretch;
|
|
147
|
+
flex-direction: column;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.wallet-button,
|
|
151
|
+
.counter-actions button {
|
|
152
|
+
width: 100%;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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": "Node",
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"isolatedModules": true,
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx"
|
|
18
|
+
},
|
|
19
|
+
"include": ["src"],
|
|
20
|
+
"references": []
|
|
21
|
+
}
|