@devvmichael/create-stacks-app 0.1.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/dist/commands/add.d.ts +8 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +215 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/create.d.ts +3 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +63 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/deploy.d.ts +7 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +159 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/project.d.ts +4 -0
- package/dist/prompts/project.d.ts.map +1 -0
- package/dist/prompts/project.js +124 -0
- package/dist/prompts/project.js.map +1 -0
- package/dist/templates/installer.d.ts +4 -0
- package/dist/templates/installer.d.ts.map +1 -0
- package/dist/templates/installer.js +91 -0
- package/dist/templates/installer.js.map +1 -0
- package/dist/types/index.d.ts +40 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/clarinet.d.ts +5 -0
- package/dist/utils/clarinet.d.ts.map +1 -0
- package/dist/utils/clarinet.js +72 -0
- package/dist/utils/clarinet.js.map +1 -0
- package/dist/utils/filesystem.d.ts +4 -0
- package/dist/utils/filesystem.d.ts.map +1 -0
- package/dist/utils/filesystem.js +158 -0
- package/dist/utils/filesystem.js.map +1 -0
- package/dist/utils/git.d.ts +2 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +21 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +45 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/package-manager.d.ts +5 -0
- package/dist/utils/package-manager.d.ts.map +1 -0
- package/dist/utils/package-manager.js +65 -0
- package/dist/utils/package-manager.js.map +1 -0
- package/dist/utils/validation.d.ts +5 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +41 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +52 -0
- package/templates/base/editorconfig +12 -0
- package/templates/base/gitignore +13 -0
- package/templates/base/prettierrc +7 -0
- package/templates/contracts/counter/counter.clar +43 -0
- package/templates/contracts/counter/counter.test.ts +127 -0
- package/templates/contracts/defi/sip010-trait.clar +11 -0
- package/templates/contracts/defi/staking-pool.clar +20 -0
- package/templates/contracts/marketplace/nft-marketplace.clar +44 -0
- package/templates/contracts/marketplace/nft-trait.clar +8 -0
- package/templates/contracts/marketplace/sip009-nft.clar +25 -0
- package/templates/contracts/nft/nft.clar +111 -0
- package/templates/contracts/nft/nft.test.ts +204 -0
- package/templates/contracts/token/token.clar +67 -0
- package/templates/contracts/token/token.test.ts +139 -0
- package/templates/frontends/nextjs/template/.env.example +2 -0
- package/templates/frontends/nextjs/template/app/globals.css +40 -0
- package/templates/frontends/nextjs/template/app/layout.tsx +36 -0
- package/templates/frontends/nextjs/template/app/page.tsx +42 -0
- package/templates/frontends/nextjs/template/app/providers.tsx +31 -0
- package/templates/frontends/nextjs/template/components/contracts/counter-interaction.tsx +80 -0
- package/templates/frontends/nextjs/template/components/header.tsx +24 -0
- package/templates/frontends/nextjs/template/components/wallet/connect-button.tsx +44 -0
- package/templates/frontends/nextjs/template/hooks/use-contract-call.ts +52 -0
- package/templates/frontends/nextjs/template/hooks/use-contract-read.ts +49 -0
- package/templates/frontends/nextjs/template/hooks/use-stacks.ts +26 -0
- package/templates/frontends/nextjs/template/lib/contracts.ts +29 -0
- package/templates/frontends/nextjs/template/lib/stacks.ts +18 -0
- package/templates/frontends/nextjs/template/next.config.js +6 -0
- package/templates/frontends/nextjs/template/package.json +29 -0
- package/templates/frontends/nextjs/template/postcss.config.js +6 -0
- package/templates/frontends/nextjs/template/public/logo.svg +3 -0
- package/templates/frontends/nextjs/template/tailwind.config.js +18 -0
- package/templates/frontends/nextjs/template/tsconfig.json +26 -0
- package/templates/frontends/react/template/.env.example +2 -0
- package/templates/frontends/react/template/index.html +13 -0
- package/templates/frontends/react/template/package.json +29 -0
- package/templates/frontends/react/template/postcss.config.js +6 -0
- package/templates/frontends/react/template/public/logo.svg +3 -0
- package/templates/frontends/react/template/src/App.tsx +100 -0
- package/templates/frontends/react/template/src/components/CounterInteraction.tsx +121 -0
- package/templates/frontends/react/template/src/components/Header.tsx +39 -0
- package/templates/frontends/react/template/src/index.css +33 -0
- package/templates/frontends/react/template/src/main.tsx +10 -0
- package/templates/frontends/react/template/tailwind.config.js +15 -0
- package/templates/frontends/react/template/tsconfig.json +25 -0
- package/templates/frontends/react/template/tsconfig.node.json +10 -0
- package/templates/frontends/react/template/vite.config.ts +12 -0
- package/templates/frontends/vue/template/.env.example +2 -0
- package/templates/frontends/vue/template/index.html +13 -0
- package/templates/frontends/vue/template/package.json +27 -0
- package/templates/frontends/vue/template/postcss.config.js +6 -0
- package/templates/frontends/vue/template/public/logo.svg +3 -0
- package/templates/frontends/vue/template/src/App.vue +98 -0
- package/templates/frontends/vue/template/src/components/AppHeader.vue +39 -0
- package/templates/frontends/vue/template/src/components/CounterInteraction.vue +120 -0
- package/templates/frontends/vue/template/src/env.d.ts +16 -0
- package/templates/frontends/vue/template/src/main.ts +5 -0
- package/templates/frontends/vue/template/src/style.css +33 -0
- package/templates/frontends/vue/template/tailwind.config.js +15 -0
- package/templates/frontends/vue/template/tsconfig.json +25 -0
- package/templates/frontends/vue/template/tsconfig.node.json +10 -0
- package/templates/frontends/vue/template/vite.config.ts +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PackageManager } from "../types/index.js";
|
|
2
|
+
export declare function detectPackageManager(): Promise<PackageManager>;
|
|
3
|
+
export declare function installDependencies(projectPath: string, pm: PackageManager): Promise<void>;
|
|
4
|
+
export declare function getRunCommand(pm: PackageManager, script: string): string;
|
|
5
|
+
//# sourceMappingURL=package-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-manager.d.ts","sourceRoot":"","sources":["../../src/utils/package-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIxD,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,cAAc,CAAC,CAiBpE;AAED,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,cAAc,GACjB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAQxE"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import { exec } from "child_process";
|
|
3
|
+
import { promisify } from "util";
|
|
4
|
+
const execAsync = promisify(exec);
|
|
5
|
+
export async function detectPackageManager() {
|
|
6
|
+
const checks = [
|
|
7
|
+
{ pm: "pnpm", command: "pnpm --version" },
|
|
8
|
+
{ pm: "yarn", command: "yarn --version" },
|
|
9
|
+
{ pm: "npm", command: "npm --version" },
|
|
10
|
+
];
|
|
11
|
+
for (const { pm, command } of checks) {
|
|
12
|
+
try {
|
|
13
|
+
await execAsync(command);
|
|
14
|
+
return pm;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return "npm"; // fallback
|
|
21
|
+
}
|
|
22
|
+
export async function installDependencies(projectPath, pm) {
|
|
23
|
+
console.log("Installing dependencies...");
|
|
24
|
+
try {
|
|
25
|
+
// Install root dependencies
|
|
26
|
+
await runCommand(pm, ["install"], projectPath);
|
|
27
|
+
// Install frontend dependencies
|
|
28
|
+
// For frontend, we want to respect the package manager choice
|
|
29
|
+
await runCommand(pm, ["install"], `${projectPath}/frontend`);
|
|
30
|
+
console.log("Dependencies installed");
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.error("Failed to install dependencies");
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function getRunCommand(pm, script) {
|
|
38
|
+
const commands = {
|
|
39
|
+
npm: `npm run ${script}`,
|
|
40
|
+
pnpm: `pnpm ${script}`,
|
|
41
|
+
yarn: `yarn ${script}`,
|
|
42
|
+
};
|
|
43
|
+
return commands[pm];
|
|
44
|
+
}
|
|
45
|
+
function runCommand(command, args, cwd) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
const child = spawn(command, args, {
|
|
48
|
+
cwd,
|
|
49
|
+
stdio: "inherit",
|
|
50
|
+
shell: true,
|
|
51
|
+
});
|
|
52
|
+
child.on("close", (code) => {
|
|
53
|
+
if (code !== 0) {
|
|
54
|
+
reject(new Error(`Command ${command} ${args.join(" ")} failed`));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
resolve();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
child.on("error", (err) => {
|
|
61
|
+
reject(err);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=package-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"package-manager.js","sourceRoot":"","sources":["../../src/utils/package-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAGjC,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,MAAM,MAAM,GAAmD;QAC7D,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE;QACzC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE;QACzC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE;KACxC,CAAC;IAEF,KAAK,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,MAAM,EAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC;YACzB,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,CAAC,WAAW;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,WAAmB,EACnB,EAAkB;IAElB,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAE1C,IAAI,CAAC;QACH,4BAA4B;QAC5B,MAAM,UAAU,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;QAE/C,gCAAgC;QAChC,8DAA8D;QAC9D,MAAM,UAAU,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,GAAG,WAAW,WAAW,CAAC,CAAC;QAE7D,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAChD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,EAAkB,EAAE,MAAc;IAC9D,MAAM,QAAQ,GAAmC;QAC/C,GAAG,EAAE,WAAW,MAAM,EAAE;QACxB,IAAI,EAAE,QAAQ,MAAM,EAAE;QACtB,IAAI,EAAE,QAAQ,MAAM,EAAE;KACvB,CAAC;IAEF,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,SAAS,UAAU,CACjB,OAAe,EACf,IAAc,EACd,GAAW;IAEX,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YACjC,GAAG;YACH,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,KAAK,CAAC,WAAW,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;YACnE,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function validateProjectName(name: string): void;
|
|
2
|
+
export declare function validateProjectPath(projectPath: string): Promise<void>;
|
|
3
|
+
export declare function validatePackageManager(pm: string): void;
|
|
4
|
+
export declare function validateContracts(contracts: string[]): void;
|
|
5
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAiBA,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAKtD;AAED,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe5E;AAED,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAOvD;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAS3D"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
const projectNameSchema = z
|
|
5
|
+
.string()
|
|
6
|
+
.min(1, 'Project name is required')
|
|
7
|
+
.max(50, 'Project name must be less than 50 characters')
|
|
8
|
+
.regex(/^[a-z0-9-]+$/, 'Project name must contain only lowercase letters, numbers, and hyphens')
|
|
9
|
+
.refine((name) => !name.startsWith('-') && !name.endsWith('-'), 'Project name cannot start or end with a hyphen');
|
|
10
|
+
export function validateProjectName(name) {
|
|
11
|
+
const result = projectNameSchema.safeParse(name);
|
|
12
|
+
if (!result.success) {
|
|
13
|
+
throw new Error(result.error.errors[0].message);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function validateProjectPath(projectPath) {
|
|
17
|
+
if (await fs.pathExists(projectPath)) {
|
|
18
|
+
throw new Error(`Directory already exists at ${projectPath}. Please choose a different name or remove the existing directory.`);
|
|
19
|
+
}
|
|
20
|
+
const parentDir = path.dirname(projectPath);
|
|
21
|
+
try {
|
|
22
|
+
await fs.access(parentDir, fs.constants.W_OK);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
throw new Error(`No write permission in directory ${parentDir}. Please check your permissions.`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function validatePackageManager(pm) {
|
|
29
|
+
const validPMs = ['npm', 'pnpm', 'yarn'];
|
|
30
|
+
if (!validPMs.includes(pm)) {
|
|
31
|
+
throw new Error(`Invalid package manager: ${pm}. Must be one of: ${validPMs.join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function validateContracts(contracts) {
|
|
35
|
+
const validContracts = ['counter', 'token', 'nft'];
|
|
36
|
+
const invalid = contracts.filter((c) => !validContracts.includes(c));
|
|
37
|
+
if (invalid.length > 0) {
|
|
38
|
+
throw new Error(`Invalid contracts: ${invalid.join(', ')}. Valid options are: ${validContracts.join(', ')}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,iBAAiB,GAAG,CAAC;KACxB,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,0BAA0B,CAAC;KAClC,GAAG,CAAC,EAAE,EAAE,8CAA8C,CAAC;KACvD,KAAK,CACJ,cAAc,EACd,wEAAwE,CACzE;KACA,MAAM,CACL,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EACtD,gDAAgD,CACjD,CAAC;AAEJ,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAClD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,WAAmB;IAC3D,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,+BAA+B,WAAW,oEAAoE,CAC/G,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,oCAAoC,SAAS,kCAAkC,CAChF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,EAAU;IAC/C,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,4BAA4B,EAAE,qBAAqB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAmB;IACnD,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAErE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,sBAAsB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,wBAAwB,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5F,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@devvmichael/create-stacks-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scaffold full-stack Stacks blockchain applications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"create-stacks-app": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"templates"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"dev": "tsc --watch",
|
|
17
|
+
"clean": "rm -rf dist",
|
|
18
|
+
"lint": "eslint src --ext .ts",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"stacks",
|
|
24
|
+
"blockchain",
|
|
25
|
+
"clarity",
|
|
26
|
+
"smart-contracts",
|
|
27
|
+
"scaffold",
|
|
28
|
+
"cli",
|
|
29
|
+
"create-app"
|
|
30
|
+
],
|
|
31
|
+
"author": "",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"chalk": "^5.3.0",
|
|
35
|
+
"commander": "^11.1.0",
|
|
36
|
+
"fs-extra": "^11.2.0",
|
|
37
|
+
"handlebars": "^4.7.8",
|
|
38
|
+
"inquirer": "^9.2.12",
|
|
39
|
+
"ora": "^7.0.1",
|
|
40
|
+
"zod": "^3.22.4"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/fs-extra": "^11.0.4",
|
|
44
|
+
"@types/inquirer": "^9.0.7",
|
|
45
|
+
"@types/node": "^20.10.0",
|
|
46
|
+
"typescript": "^5.3.3",
|
|
47
|
+
"vitest": "^1.0.4"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
;; Counter Contract
|
|
2
|
+
;; A simple contract demonstrating state management in Clarity
|
|
3
|
+
|
|
4
|
+
;; Data variables
|
|
5
|
+
(define-data-var counter uint u0)
|
|
6
|
+
(define-data-var owner principal tx-sender)
|
|
7
|
+
|
|
8
|
+
;; Error codes
|
|
9
|
+
(define-constant ERR-NOT-OWNER (err u403))
|
|
10
|
+
|
|
11
|
+
;; Public functions
|
|
12
|
+
|
|
13
|
+
;; Increment the counter by 1
|
|
14
|
+
(define-public (increment)
|
|
15
|
+
(ok (var-set counter (+ (var-get counter) u1))))
|
|
16
|
+
|
|
17
|
+
;; Decrement the counter by 1
|
|
18
|
+
(define-public (decrement)
|
|
19
|
+
(let ((current (var-get counter)))
|
|
20
|
+
(asserts! (> current u0) (err u400))
|
|
21
|
+
(ok (var-set counter (- current u1)))))
|
|
22
|
+
|
|
23
|
+
;; Reset the counter to 0 (owner only)
|
|
24
|
+
(define-public (reset)
|
|
25
|
+
(begin
|
|
26
|
+
(asserts! (is-eq tx-sender (var-get owner)) ERR-NOT-OWNER)
|
|
27
|
+
(ok (var-set counter u0))))
|
|
28
|
+
|
|
29
|
+
;; Set the counter to a specific value (owner only)
|
|
30
|
+
(define-public (set-counter (value uint))
|
|
31
|
+
(begin
|
|
32
|
+
(asserts! (is-eq tx-sender (var-get owner)) ERR-NOT-OWNER)
|
|
33
|
+
(ok (var-set counter value))))
|
|
34
|
+
|
|
35
|
+
;; Read-only functions
|
|
36
|
+
|
|
37
|
+
;; Get the current counter value
|
|
38
|
+
(define-read-only (get-counter)
|
|
39
|
+
(ok (var-get counter)))
|
|
40
|
+
|
|
41
|
+
;; Get the contract owner
|
|
42
|
+
(define-read-only (get-owner)
|
|
43
|
+
(ok (var-get owner)))
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { Cl } from "@stacks/transactions";
|
|
3
|
+
|
|
4
|
+
const accounts = simnet.getAccounts();
|
|
5
|
+
const deployer = accounts.get("deployer")!;
|
|
6
|
+
const wallet1 = accounts.get("wallet_1")!;
|
|
7
|
+
|
|
8
|
+
describe("Counter Contract", () => {
|
|
9
|
+
describe("Initialization", () => {
|
|
10
|
+
it("should start at zero", () => {
|
|
11
|
+
const result = simnet.callReadOnlyFn(
|
|
12
|
+
"counter",
|
|
13
|
+
"get-counter",
|
|
14
|
+
[],
|
|
15
|
+
deployer
|
|
16
|
+
);
|
|
17
|
+
expect(result.result).toBeOk(Cl.uint(0));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should have deployer as owner", () => {
|
|
21
|
+
const result = simnet.callReadOnlyFn(
|
|
22
|
+
"counter",
|
|
23
|
+
"get-owner",
|
|
24
|
+
[],
|
|
25
|
+
deployer
|
|
26
|
+
);
|
|
27
|
+
expect(result.result).toBeOk(Cl.principal(deployer));
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("Increment", () => {
|
|
32
|
+
it("should increment counter by 1", () => {
|
|
33
|
+
const block = simnet.callPublicFn("counter", "increment", [], deployer);
|
|
34
|
+
expect(block.result).toBeOk(Cl.bool(true));
|
|
35
|
+
|
|
36
|
+
const result = simnet.callReadOnlyFn(
|
|
37
|
+
"counter",
|
|
38
|
+
"get-counter",
|
|
39
|
+
[],
|
|
40
|
+
deployer
|
|
41
|
+
);
|
|
42
|
+
expect(result.result).toBeOk(Cl.uint(1));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should allow anyone to increment", () => {
|
|
46
|
+
const block = simnet.callPublicFn("counter", "increment", [], wallet1);
|
|
47
|
+
expect(block.result).toBeOk(Cl.bool(true));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("Decrement", () => {
|
|
52
|
+
it("should decrement counter by 1", () => {
|
|
53
|
+
// First increment
|
|
54
|
+
simnet.callPublicFn("counter", "increment", [], deployer);
|
|
55
|
+
|
|
56
|
+
const block = simnet.callPublicFn("counter", "decrement", [], deployer);
|
|
57
|
+
expect(block.result).toBeOk(Cl.bool(true));
|
|
58
|
+
|
|
59
|
+
const result = simnet.callReadOnlyFn(
|
|
60
|
+
"counter",
|
|
61
|
+
"get-counter",
|
|
62
|
+
[],
|
|
63
|
+
deployer
|
|
64
|
+
);
|
|
65
|
+
expect(result.result).toBeOk(Cl.uint(0));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should fail when counter is at zero", () => {
|
|
69
|
+
const block = simnet.callPublicFn("counter", "decrement", [], deployer);
|
|
70
|
+
expect(block.result).toBeErr(Cl.uint(400));
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("Reset", () => {
|
|
75
|
+
it("should allow owner to reset", () => {
|
|
76
|
+
// First increment
|
|
77
|
+
simnet.callPublicFn("counter", "increment", [], deployer);
|
|
78
|
+
simnet.callPublicFn("counter", "increment", [], deployer);
|
|
79
|
+
|
|
80
|
+
const block = simnet.callPublicFn("counter", "reset", [], deployer);
|
|
81
|
+
expect(block.result).toBeOk(Cl.bool(true));
|
|
82
|
+
|
|
83
|
+
const result = simnet.callReadOnlyFn(
|
|
84
|
+
"counter",
|
|
85
|
+
"get-counter",
|
|
86
|
+
[],
|
|
87
|
+
deployer
|
|
88
|
+
);
|
|
89
|
+
expect(result.result).toBeOk(Cl.uint(0));
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("should prevent non-owner from resetting", () => {
|
|
93
|
+
const block = simnet.callPublicFn("counter", "reset", [], wallet1);
|
|
94
|
+
expect(block.result).toBeErr(Cl.uint(403));
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe("Set Counter", () => {
|
|
99
|
+
it("should allow owner to set counter", () => {
|
|
100
|
+
const block = simnet.callPublicFn(
|
|
101
|
+
"counter",
|
|
102
|
+
"set-counter",
|
|
103
|
+
[Cl.uint(100)],
|
|
104
|
+
deployer
|
|
105
|
+
);
|
|
106
|
+
expect(block.result).toBeOk(Cl.bool(true));
|
|
107
|
+
|
|
108
|
+
const result = simnet.callReadOnlyFn(
|
|
109
|
+
"counter",
|
|
110
|
+
"get-counter",
|
|
111
|
+
[],
|
|
112
|
+
deployer
|
|
113
|
+
);
|
|
114
|
+
expect(result.result).toBeOk(Cl.uint(100));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("should prevent non-owner from setting counter", () => {
|
|
118
|
+
const block = simnet.callPublicFn(
|
|
119
|
+
"counter",
|
|
120
|
+
"set-counter",
|
|
121
|
+
[Cl.uint(100)],
|
|
122
|
+
wallet1
|
|
123
|
+
);
|
|
124
|
+
expect(block.result).toBeErr(Cl.uint(403));
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
(define-trait sip010-trait
|
|
2
|
+
(
|
|
3
|
+
(transfer (uint principal principal (optional (buff 34))) (response bool uint))
|
|
4
|
+
(get-name () (response (string-ascii 32) uint))
|
|
5
|
+
(get-symbol () (response (string-ascii 32) uint))
|
|
6
|
+
(get-decimals () (response uint uint))
|
|
7
|
+
(get-balance (principal) (response uint uint))
|
|
8
|
+
(get-total-supply () (response uint uint))
|
|
9
|
+
(get-token-uri () (response (optional (string-utf8 256)) uint))
|
|
10
|
+
)
|
|
11
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
;; Staking Pool
|
|
2
|
+
(use-trait sip010-trait .sip010-trait.sip010-trait)
|
|
3
|
+
|
|
4
|
+
(define-map stakes principal uint)
|
|
5
|
+
|
|
6
|
+
(define-public (stake (token <sip010-trait>) (amount uint))
|
|
7
|
+
(begin
|
|
8
|
+
(try! (contract-call? token transfer amount tx-sender (as-contract tx-sender) none))
|
|
9
|
+
(map-set stakes tx-sender (+ (default-to u0 (map-get? stakes tx-sender)) amount))
|
|
10
|
+
(ok true)))
|
|
11
|
+
|
|
12
|
+
(define-public (unstake (token <sip010-trait>) (amount uint))
|
|
13
|
+
(begin
|
|
14
|
+
(asserts! (>= (default-to u0 (map-get? stakes tx-sender)) amount) (err u100))
|
|
15
|
+
(try! (as-contract (contract-call? token transfer amount tx-sender tx-sender none)))
|
|
16
|
+
(map-set stakes tx-sender (- (default-to u0 (map-get? stakes tx-sender)) amount))
|
|
17
|
+
(ok true)))
|
|
18
|
+
|
|
19
|
+
(define-read-only (get-stake (staker principal))
|
|
20
|
+
(ok (default-to u0 (map-get? stakes staker))))
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
;; NFT Marketplace
|
|
2
|
+
(use-trait nft-trait .nft-trait.nft-trait)
|
|
3
|
+
|
|
4
|
+
(define-constant ERR-NOT-AUTHORIZED (err u100))
|
|
5
|
+
(define-constant ERR-LISTING-NOT-FOUND (err u101))
|
|
6
|
+
(define-constant ERR-WRONG-PRICE (err u102))
|
|
7
|
+
|
|
8
|
+
(define-map listings
|
|
9
|
+
{ contract: principal, token-id: uint }
|
|
10
|
+
{ price: uint, seller: principal }
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
(define-public (list-asset (nft-asset-contract <nft-trait>) (token-id uint) (price uint))
|
|
14
|
+
(let ((listing-id { contract: (contract-of nft-asset-contract), token-id: token-id }))
|
|
15
|
+
;; Transfer NFT to contract
|
|
16
|
+
(try! (contract-call? nft-asset-contract transfer token-id tx-sender (as-contract tx-sender)))
|
|
17
|
+
;; Create listing
|
|
18
|
+
(map-set listings listing-id { price: price, seller: tx-sender })
|
|
19
|
+
(ok true)))
|
|
20
|
+
|
|
21
|
+
(define-public (unlist-asset (nft-asset-contract <nft-trait>) (token-id uint))
|
|
22
|
+
(let (
|
|
23
|
+
(listing-id { contract: (contract-of nft-asset-contract), token-id: token-id })
|
|
24
|
+
(listing (unwrap! (map-get? listings listing-id) ERR-LISTING-NOT-FOUND))
|
|
25
|
+
)
|
|
26
|
+
(asserts! (is-eq tx-sender (get seller listing)) ERR-NOT-AUTHORIZED)
|
|
27
|
+
;; Return NFT
|
|
28
|
+
(as-contract (try! (contract-call? nft-asset-contract transfer token-id tx-sender (get seller listing))))
|
|
29
|
+
(map-delete listings listing-id)
|
|
30
|
+
(ok true)))
|
|
31
|
+
|
|
32
|
+
(define-public (purchase-asset (nft-asset-contract <nft-trait>) (token-id uint))
|
|
33
|
+
(let (
|
|
34
|
+
(listing-id { contract: (contract-of nft-asset-contract), token-id: token-id })
|
|
35
|
+
(listing (unwrap! (map-get? listings listing-id) ERR-LISTING-NOT-FOUND))
|
|
36
|
+
(price (get price listing))
|
|
37
|
+
(seller (get seller listing))
|
|
38
|
+
)
|
|
39
|
+
;; Transfer STX to seller
|
|
40
|
+
(try! (stx-transfer? price tx-sender seller))
|
|
41
|
+
;; Transfer NFT to buyer
|
|
42
|
+
(as-contract (try! (contract-call? nft-asset-contract transfer token-id tx-sender tx-sender)))
|
|
43
|
+
(map-delete listings listing-id)
|
|
44
|
+
(ok true)))
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
(define-trait nft-trait
|
|
2
|
+
(
|
|
3
|
+
(get-last-token-id () (response uint uint))
|
|
4
|
+
(get-token-uri (uint) (response (optional (string-ascii 256)) uint))
|
|
5
|
+
(get-owner (uint) (response (optional principal) uint))
|
|
6
|
+
(transfer (uint principal principal) (response bool uint))
|
|
7
|
+
)
|
|
8
|
+
)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
;; SIP-009 NFT Contract
|
|
2
|
+
(impl-trait .nft-trait.nft-trait)
|
|
3
|
+
|
|
4
|
+
(define-non-fungible-token my-nft uint)
|
|
5
|
+
(define-data-var last-token-id uint u0)
|
|
6
|
+
|
|
7
|
+
(define-public (transfer (token-id uint) (sender principal) (recipient principal))
|
|
8
|
+
(begin
|
|
9
|
+
(try! (nft-transfer? my-nft token-id sender recipient))
|
|
10
|
+
(ok true)))
|
|
11
|
+
|
|
12
|
+
(define-public (mint (recipient principal))
|
|
13
|
+
(let ((token-id (+ (var-get last-token-id) u1)))
|
|
14
|
+
(try! (nft-mint? my-nft token-id recipient))
|
|
15
|
+
(var-set last-token-id token-id)
|
|
16
|
+
(ok token-id)))
|
|
17
|
+
|
|
18
|
+
(define-read-only (get-last-token-id)
|
|
19
|
+
(ok (var-get last-token-id)))
|
|
20
|
+
|
|
21
|
+
(define-read-only (get-token-uri (token-id uint))
|
|
22
|
+
(ok none))
|
|
23
|
+
|
|
24
|
+
(define-read-only (get-owner (token-id uint))
|
|
25
|
+
(ok (nft-get-owner? my-nft token-id)))
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
;; SIP-009 Non-Fungible Token
|
|
2
|
+
;; A standard NFT implementation
|
|
3
|
+
|
|
4
|
+
(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait)
|
|
5
|
+
|
|
6
|
+
;; NFT Configuration
|
|
7
|
+
(define-constant CONTRACT-OWNER tx-sender)
|
|
8
|
+
(define-constant COLLECTION-NAME "MyNFTCollection")
|
|
9
|
+
(define-constant COLLECTION-SYMBOL "MNFT")
|
|
10
|
+
|
|
11
|
+
;; Error codes
|
|
12
|
+
(define-constant ERR-OWNER-ONLY (err u100))
|
|
13
|
+
(define-constant ERR-NOT-TOKEN-OWNER (err u101))
|
|
14
|
+
(define-constant ERR-TOKEN-EXISTS (err u102))
|
|
15
|
+
(define-constant ERR-TOKEN-NOT-FOUND (err u103))
|
|
16
|
+
(define-constant ERR-LISTING-NOT-FOUND (err u104))
|
|
17
|
+
(define-constant ERR-WRONG-PRICE (err u105))
|
|
18
|
+
|
|
19
|
+
;; Storage
|
|
20
|
+
(define-non-fungible-token my-nft uint)
|
|
21
|
+
(define-data-var last-token-id uint u0)
|
|
22
|
+
(define-data-var base-token-uri (string-ascii 256) "")
|
|
23
|
+
|
|
24
|
+
;; Token metadata storage
|
|
25
|
+
(define-map token-metadata uint {
|
|
26
|
+
uri: (string-ascii 256)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
;; Marketplace listings
|
|
30
|
+
(define-map listings uint {
|
|
31
|
+
price: uint,
|
|
32
|
+
seller: principal
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
;; SIP-009 Functions
|
|
36
|
+
|
|
37
|
+
(define-public (transfer (token-id uint) (sender principal) (recipient principal))
|
|
38
|
+
(begin
|
|
39
|
+
(asserts! (is-eq tx-sender sender) ERR-NOT-TOKEN-OWNER)
|
|
40
|
+
(map-delete listings token-id)
|
|
41
|
+
(nft-transfer? my-nft token-id sender recipient)))
|
|
42
|
+
|
|
43
|
+
(define-read-only (get-last-token-id)
|
|
44
|
+
(ok (var-get last-token-id)))
|
|
45
|
+
|
|
46
|
+
(define-read-only (get-token-uri (token-id uint))
|
|
47
|
+
(match (map-get? token-metadata token-id)
|
|
48
|
+
metadata (ok (some (get uri metadata)))
|
|
49
|
+
(ok none)))
|
|
50
|
+
|
|
51
|
+
(define-read-only (get-owner (token-id uint))
|
|
52
|
+
(ok (nft-get-owner? my-nft token-id)))
|
|
53
|
+
|
|
54
|
+
;; Mint function
|
|
55
|
+
(define-public (mint (recipient principal) (token-uri (string-ascii 256)))
|
|
56
|
+
(let
|
|
57
|
+
((token-id (+ (var-get last-token-id) u1)))
|
|
58
|
+
(try! (nft-mint? my-nft token-id recipient))
|
|
59
|
+
(map-set token-metadata token-id { uri: token-uri })
|
|
60
|
+
(var-set last-token-id token-id)
|
|
61
|
+
(ok token-id)))
|
|
62
|
+
|
|
63
|
+
;; Batch mint function
|
|
64
|
+
(define-public (mint-batch (recipient principal) (uris (list 10 (string-ascii 256))))
|
|
65
|
+
(let
|
|
66
|
+
((start-id (var-get last-token-id)))
|
|
67
|
+
(fold mint-single uris (ok start-id))))
|
|
68
|
+
|
|
69
|
+
(define-private (mint-single (uri (string-ascii 256)) (prev-result (response uint uint)))
|
|
70
|
+
(match prev-result
|
|
71
|
+
prev-id
|
|
72
|
+
(let
|
|
73
|
+
((new-id (+ prev-id u1)))
|
|
74
|
+
(try! (nft-mint? my-nft new-id tx-sender))
|
|
75
|
+
(map-set token-metadata new-id { uri: uri })
|
|
76
|
+
(var-set last-token-id new-id)
|
|
77
|
+
(ok new-id))
|
|
78
|
+
err-code (err err-code)))
|
|
79
|
+
|
|
80
|
+
;; Admin functions
|
|
81
|
+
(define-public (set-base-uri (new-base-uri (string-ascii 256)))
|
|
82
|
+
(begin
|
|
83
|
+
(asserts! (is-eq tx-sender CONTRACT-OWNER) ERR-OWNER-ONLY)
|
|
84
|
+
(ok (var-set base-token-uri new-base-uri))))
|
|
85
|
+
|
|
86
|
+
;; Marketplace functions
|
|
87
|
+
|
|
88
|
+
(define-public (list-for-sale (token-id uint) (price uint))
|
|
89
|
+
(let
|
|
90
|
+
((owner (unwrap! (nft-get-owner? my-nft token-id) ERR-TOKEN-NOT-FOUND)))
|
|
91
|
+
(asserts! (is-eq tx-sender owner) ERR-NOT-TOKEN-OWNER)
|
|
92
|
+
(ok (map-set listings token-id { price: price, seller: tx-sender }))))
|
|
93
|
+
|
|
94
|
+
(define-public (unlist (token-id uint))
|
|
95
|
+
(let
|
|
96
|
+
((listing (unwrap! (map-get? listings token-id) ERR-LISTING-NOT-FOUND)))
|
|
97
|
+
(asserts! (is-eq tx-sender (get seller listing)) ERR-NOT-TOKEN-OWNER)
|
|
98
|
+
(ok (map-delete listings token-id))))
|
|
99
|
+
|
|
100
|
+
(define-public (buy (token-id uint))
|
|
101
|
+
(let
|
|
102
|
+
((listing (unwrap! (map-get? listings token-id) ERR-LISTING-NOT-FOUND))
|
|
103
|
+
(price (get price listing))
|
|
104
|
+
(seller (get seller listing)))
|
|
105
|
+
(try! (stx-transfer? price tx-sender seller))
|
|
106
|
+
(try! (nft-transfer? my-nft token-id seller tx-sender))
|
|
107
|
+
(map-delete listings token-id)
|
|
108
|
+
(ok token-id)))
|
|
109
|
+
|
|
110
|
+
(define-read-only (get-listing (token-id uint))
|
|
111
|
+
(map-get? listings token-id))
|