@devvmichael/create-stacks-app 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/README.md CHANGED
@@ -50,13 +50,13 @@ npx @devvmichael/create-stacks-app
50
50
  npx @devvmichael/create-stacks-app my-dapp [options]
51
51
 
52
52
  Options:
53
- -t, --template <name> Frontend template: nextjs, react, vue (default: nextjs)
53
+ -t, --template <name> Frontend template: nextjs, react, vue
54
54
  -c, --contracts <list> Contracts to include: counter,token,nft
55
55
  --typescript Use TypeScript (default: true)
56
56
  --no-typescript Use JavaScript
57
57
  --tailwind Include Tailwind CSS (default: true)
58
58
  --no-git Skip Git initialization
59
- --package-manager <pm> Package manager: npm, pnpm, yarn (default: pnpm)
59
+ --package-manager <pm> Package manager: npm, pnpm, yarn
60
60
  --skip-install Skip dependency installation
61
61
  -y, --yes Skip prompts, use defaults
62
62
  ```
@@ -1,4 +1,4 @@
1
- import type { ProjectConfig } from '../types/index.js';
1
+ import type { ProjectConfig } from "../types/index.js";
2
2
  export declare function checkClarinetInstallation(): Promise<boolean>;
3
3
  export declare function initializeClarinet(config: ProjectConfig): Promise<void>;
4
4
  export declare function updateClarinetConfig(projectPath: string, contracts: string[]): Promise<void>;
@@ -1,12 +1,12 @@
1
- import { exec } from 'child_process';
2
- import { promisify } from 'util';
3
- import ora from 'ora';
4
- import path from 'path';
5
- import fs from 'fs-extra';
1
+ import { exec } from "child_process";
2
+ import { promisify } from "util";
3
+ import ora from "ora";
4
+ import path from "path";
5
+ import fs from "fs-extra";
6
6
  const execAsync = promisify(exec);
7
7
  export async function checkClarinetInstallation() {
8
8
  try {
9
- await execAsync('clarinet --version');
9
+ await execAsync("clarinet --version");
10
10
  return true;
11
11
  }
12
12
  catch {
@@ -14,50 +14,42 @@ export async function checkClarinetInstallation() {
14
14
  }
15
15
  }
16
16
  export async function initializeClarinet(config) {
17
- const spinner = ora('Initializing Clarinet...').start();
17
+ const spinner = ora("Initializing Clarinet...").start();
18
18
  try {
19
19
  const { projectPath, projectName } = config;
20
- // Create Clarinet.toml manually instead of using clarinet new
21
- const clarinetToml = `[project]
22
- name = "${projectName}"
23
- description = ""
24
- authors = []
25
- telemetry = false
26
- cache_dir = "./.cache"
27
-
28
- [contracts]
29
- `;
30
- await fs.writeFile(path.join(projectPath, 'Clarinet.toml'), clarinetToml);
31
- // Create settings directory
32
- await fs.ensureDir(path.join(projectPath, 'settings'));
33
- // Create Devnet.toml
34
- const devnetToml = `[network]
35
- name = "devnet"
36
- deployment_fee_rate = 10
37
-
38
- [accounts.deployer]
39
- mnemonic = "twice particular affair smile push picture miss direct toss brass expose better"
40
- balance = 10_000_000_000_000_000
41
-
42
- [accounts.wallet_1]
43
- mnemonic = "sell invite acquire kitten believe struggle find damp current debris convince key"
44
- balance = 10_000_000_000_000_000
45
-
46
- [accounts.wallet_2]
47
- mnemonic = "hold excess usual excess ring elephant install account glad dry display sauce"
48
- balance = 10_000_000_000_000_000
49
- `;
50
- await fs.writeFile(path.join(projectPath, 'settings', 'Devnet.toml'), devnetToml);
51
- spinner.succeed('Clarinet initialized');
20
+ // Create a temporary directory for Clarinet initialization
21
+ const tempDir = path.join(projectPath, ".temp_clarinet");
22
+ await fs.ensureDir(tempDir);
23
+ // Run clarinet new in temp directory
24
+ try {
25
+ await execAsync(`cd "${tempDir}" && clarinet new "${projectName}"`);
26
+ }
27
+ catch (e) {
28
+ // If clarinet new fails, it might be because of directory structure
29
+ // specific error handling could be added here
30
+ throw new Error(`Clarinet initialization failed: ${e.message}`);
31
+ }
32
+ const sourceDir = path.join(tempDir, projectName);
33
+ // Copy generated Clarinet.toml
34
+ if (await fs.pathExists(path.join(sourceDir, "Clarinet.toml"))) {
35
+ await fs.copy(path.join(sourceDir, "Clarinet.toml"), path.join(projectPath, "Clarinet.toml"));
36
+ }
37
+ // Copy settings directory (Devnet.toml)
38
+ if (await fs.pathExists(path.join(sourceDir, "settings"))) {
39
+ await fs.copy(path.join(sourceDir, "settings"), path.join(projectPath, "settings"));
40
+ }
41
+ // Cleanup temp directory
42
+ await fs.remove(tempDir);
43
+ spinner.succeed("Clarinet initialized");
52
44
  }
53
45
  catch (error) {
54
- spinner.fail('Failed to initialize Clarinet');
46
+ spinner.fail("Failed to initialize Clarinet");
55
47
  throw error;
56
48
  }
57
49
  }
58
50
  export async function updateClarinetConfig(projectPath, contracts) {
59
- const clarinetTomlPath = path.join(projectPath, 'Clarinet.toml');
60
- let tomlContent = await fs.readFile(clarinetTomlPath, 'utf-8');
51
+ const clarinetTomlPath = path.join(projectPath, "Clarinet.toml");
52
+ let tomlContent = await fs.readFile(clarinetTomlPath, "utf-8");
61
53
  // Add contracts based on selection
62
54
  for (const contract of contracts) {
63
55
  tomlContent += `
@@ -1 +1 @@
1
- {"version":3,"file":"clarinet.js","sourceRoot":"","sources":["../../src/utils/clarinet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,UAAU,CAAC;AAG1B,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,yBAAyB;IAC7C,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAqB;IAC5D,MAAM,OAAO,GAAG,GAAG,CAAC,0BAA0B,CAAC,CAAC,KAAK,EAAE,CAAC;IAExD,IAAI,CAAC;QACH,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;QAE5C,8DAA8D;QAC9D,MAAM,YAAY,GAAG;UACf,WAAW;;;;;;;CAOpB,CAAC;QAEE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,YAAY,CAAC,CAAC;QAE1E,4BAA4B;QAC5B,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;QAEvD,qBAAqB;QACrB,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;CAetB,CAAC;QAEE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,aAAa,CAAC,EAAE,UAAU,CAAC,CAAC;QAElF,OAAO,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;QAC9C,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,EACnB,SAAmB;IAEnB,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAEjE,IAAI,WAAW,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;IAE/D,mCAAmC;IACnC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,WAAW,IAAI;aACN,QAAQ;oBACD,QAAQ;;;CAG3B,CAAC;IACA,CAAC;IAED,MAAM,EAAE,CAAC,SAAS,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"clarinet.js","sourceRoot":"","sources":["../../src/utils/clarinet.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devvmichael/create-stacks-app",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Scaffold full-stack Stacks blockchain applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,14 +1,11 @@
1
- import { useState, useEffect, useCallback } from 'react';
2
- import { AppConfig, UserSession, showConnect } from '@stacks/connect';
3
- import { StacksTestnet, StacksMainnet } from '@stacks/network';
4
- import { Header } from './components/Header';
5
- import { CounterInteraction } from './components/CounterInteraction';
6
-
7
- const appConfig = new AppConfig(['store_write', 'publish_data']);
8
- const userSession = new UserSession({ appConfig });
1
+ import { useState, useEffect, useCallback } from "react";
2
+ import { connect, disconnect, getLocalStorage } from "@stacks/connect";
3
+ import { StacksTestnet, StacksMainnet } from "@stacks/network";
4
+ import { Header } from "./components/Header";
5
+ import { CounterInteraction } from "./components/CounterInteraction";
9
6
 
10
7
  const network =
11
- import.meta.env.VITE_NETWORK === 'mainnet'
8
+ import.meta.env.VITE_NETWORK === "mainnet"
12
9
  ? new StacksMainnet()
13
10
  : new StacksTestnet();
14
11
 
@@ -16,29 +13,34 @@ function App() {
16
13
  const [address, setAddress] = useState<string | null>(null);
17
14
 
18
15
  useEffect(() => {
19
- if (userSession.isUserSignedIn()) {
20
- const userData = userSession.loadUserData();
21
- const networkKey = import.meta.env.VITE_NETWORK === 'mainnet' ? 'mainnet' : 'testnet';
22
- setAddress(userData.profile.stxAddress[networkKey]);
16
+ // Check local storage for existing session
17
+ const storage = getLocalStorage();
18
+ const networkKey =
19
+ import.meta.env.VITE_NETWORK === "mainnet" ? "mainnet" : "testnet";
20
+ if (storage?.addresses?.[networkKey]) {
21
+ setAddress(storage.addresses[networkKey]);
23
22
  }
24
23
  }, []);
25
24
 
26
- const handleConnect = useCallback(() => {
27
- showConnect({
28
- appDetails: {
29
- name: 'Stacks App',
30
- icon: window.location.origin + '/logo.svg',
31
- },
32
- redirectTo: '/',
33
- onFinish: () => {
25
+ const handleConnect = useCallback(async () => {
26
+ try {
27
+ const response = await connect();
28
+ // Access the first address from the response
29
+ const userAddress = response.addresses?.[0]?.address;
30
+ if (userAddress) {
31
+ setAddress(userAddress);
32
+ // Optional: reload if needed to reset state, or handle reactively
34
33
  window.location.reload();
35
- },
36
- userSession,
37
- });
34
+ }
35
+ } catch (error) {
36
+ console.error("Failed to connect:", error);
37
+ }
38
38
  }, []);
39
39
 
40
40
  const handleDisconnect = useCallback(() => {
41
- userSession.signUserOut('/');
41
+ disconnect();
42
+ setAddress(null);
43
+ window.location.reload();
42
44
  }, []);
43
45
 
44
46
  return (
@@ -51,7 +53,9 @@ function App() {
51
53
 
52
54
  <main className="flex-1 container mx-auto px-4 py-8">
53
55
  <div className="mb-8 text-center">
54
- <h1 className="mb-4 text-4xl font-bold">Welcome to Your Stacks App</h1>
56
+ <h1 className="mb-4 text-4xl font-bold">
57
+ Welcome to Your Stacks App
58
+ </h1>
55
59
  <p className="text-lg text-gray-400">
56
60
  A full-stack Stacks blockchain application
57
61
  </p>
@@ -71,19 +75,24 @@ function App() {
71
75
  <div className="card">
72
76
  <h3 className="font-semibold mb-2">📝 Edit Contracts</h3>
73
77
  <p className="text-sm text-gray-400">
74
- Modify contracts in <code className="bg-gray-800 px-1 rounded">contracts/</code>
78
+ Modify contracts in{" "}
79
+ <code className="bg-gray-800 px-1 rounded">contracts/</code>
75
80
  </p>
76
81
  </div>
77
82
  <div className="card">
78
83
  <h3 className="font-semibold mb-2">🧪 Run Tests</h3>
79
84
  <p className="text-sm text-gray-400">
80
- Run <code className="bg-gray-800 px-1 rounded">npm run test</code>
85
+ Run{" "}
86
+ <code className="bg-gray-800 px-1 rounded">npm run test</code>
81
87
  </p>
82
88
  </div>
83
89
  <div className="card">
84
90
  <h3 className="font-semibold mb-2">🚀 Deploy</h3>
85
91
  <p className="text-sm text-gray-400">
86
- Run <code className="bg-gray-800 px-1 rounded">npm run deploy:testnet</code>
92
+ Run{" "}
93
+ <code className="bg-gray-800 px-1 rounded">
94
+ npm run deploy:testnet
95
+ </code>
87
96
  </p>
88
97
  </div>
89
98
  </div>
@@ -1,7 +1,7 @@
1
- import { useState, useEffect, useCallback } from 'react';
2
- import { openContractCall } from '@stacks/connect';
3
- import { callReadOnlyFunction, cvToValue } from '@stacks/transactions';
4
- import type { StacksNetwork } from '@stacks/network';
1
+ import { useState, useEffect, useCallback } from "react";
2
+ import { request } from "@stacks/connect";
3
+ import { callReadOnlyFunction, cvToValue } from "@stacks/transactions";
4
+ import type { StacksNetwork } from "@stacks/network";
5
5
 
6
6
  interface CounterInteractionProps {
7
7
  network: StacksNetwork;
@@ -9,10 +9,16 @@ interface CounterInteractionProps {
9
9
  senderAddress: string | null;
10
10
  }
11
11
 
12
- const contractAddress = import.meta.env.VITE_CONTRACT_ADDRESS || 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM';
13
- const contractName = 'counter';
12
+ const contractAddress =
13
+ import.meta.env.VITE_CONTRACT_ADDRESS ||
14
+ "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";
15
+ const contractName = "counter";
14
16
 
15
- export function CounterInteraction({ network, isConnected, senderAddress }: CounterInteractionProps) {
17
+ export function CounterInteraction({
18
+ network,
19
+ isConnected,
20
+ senderAddress,
21
+ }: CounterInteractionProps) {
16
22
  const [counter, setCounter] = useState<number | null>(null);
17
23
  const [isLoading, setIsLoading] = useState(true);
18
24
  const [isIncrementing, setIsIncrementing] = useState(false);
@@ -23,15 +29,15 @@ export function CounterInteraction({ network, isConnected, senderAddress }: Coun
23
29
  const result = await callReadOnlyFunction({
24
30
  contractAddress,
25
31
  contractName,
26
- functionName: 'get-counter',
32
+ functionName: "get-counter",
27
33
  functionArgs: [],
28
34
  network,
29
35
  senderAddress: contractAddress,
30
36
  });
31
37
  const value = cvToValue(result);
32
- setCounter(value?.value ?? 0);
38
+ setCounter(value?.value ? Number(value.value) : 0);
33
39
  } catch (error) {
34
- console.error('Failed to fetch counter:', error);
40
+ console.error("Failed to fetch counter:", error);
35
41
  } finally {
36
42
  setIsLoading(false);
37
43
  }
@@ -45,18 +51,16 @@ export function CounterInteraction({ network, isConnected, senderAddress }: Coun
45
51
  if (!senderAddress) return;
46
52
  setIsIncrementing(true);
47
53
  try {
48
- await openContractCall({
49
- contractAddress,
50
- contractName,
51
- functionName: 'increment',
54
+ await request("stx_callContract", {
55
+ contract: `${contractAddress}.${contractName}`,
56
+ functionName: "increment",
52
57
  functionArgs: [],
53
- network,
54
- onFinish: () => {
55
- setTimeout(fetchCounter, 2000);
56
- },
58
+ postConditions: [],
57
59
  });
60
+
61
+ setTimeout(fetchCounter, 2000);
58
62
  } catch (error) {
59
- console.error('Increment failed:', error);
63
+ console.error("Increment failed:", error);
60
64
  } finally {
61
65
  setIsIncrementing(false);
62
66
  }
@@ -66,18 +70,16 @@ export function CounterInteraction({ network, isConnected, senderAddress }: Coun
66
70
  if (!senderAddress) return;
67
71
  setIsDecrementing(true);
68
72
  try {
69
- await openContractCall({
70
- contractAddress,
71
- contractName,
72
- functionName: 'decrement',
73
+ await request("stx_callContract", {
74
+ contract: `${contractAddress}.${contractName}`,
75
+ functionName: "decrement",
73
76
  functionArgs: [],
74
- network,
75
- onFinish: () => {
76
- setTimeout(fetchCounter, 2000);
77
- },
77
+ postConditions: [],
78
78
  });
79
+
80
+ setTimeout(fetchCounter, 2000);
79
81
  } catch (error) {
80
- console.error('Decrement failed:', error);
82
+ console.error("Decrement failed:", error);
81
83
  } finally {
82
84
  setIsDecrementing(false);
83
85
  }
@@ -88,8 +90,8 @@ export function CounterInteraction({ network, isConnected, senderAddress }: Coun
88
90
  <h2 className="text-2xl font-bold mb-4">Counter Contract</h2>
89
91
 
90
92
  <div className="mb-6 text-center">
91
- <div className="text-6xl font-bold text-stacks-purple">
92
- {isLoading ? '...' : counter}
93
+ <div className="text-6xl font-bold text-gray-100">
94
+ {isLoading ? "..." : counter}
93
95
  </div>
94
96
  <p className="text-sm text-gray-500 mt-2">Current count</p>
95
97
  </div>
@@ -101,14 +103,14 @@ export function CounterInteraction({ network, isConnected, senderAddress }: Coun
101
103
  disabled={isDecrementing || counter === 0}
102
104
  className="btn-secondary flex-1 disabled:opacity-50"
103
105
  >
104
- {isDecrementing ? 'Processing...' : '− Decrement'}
106
+ {isDecrementing ? "Processing..." : "− Decrement"}
105
107
  </button>
106
108
  <button
107
109
  onClick={handleIncrement}
108
110
  disabled={isIncrementing}
109
111
  className="btn-primary flex-1 disabled:opacity-50"
110
112
  >
111
- {isIncrementing ? 'Processing...' : '+ Increment'}
113
+ {isIncrementing ? "Processing..." : "+ Increment"}
112
114
  </button>
113
115
  </div>
114
116
  ) : (
@@ -1,13 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, onMounted } from 'vue';
3
- import { AppConfig, UserSession, showConnect } from '@stacks/connect';
3
+ import { connect, disconnect, getLocalStorage } from '@stacks/connect';
4
4
  import { StacksTestnet, StacksMainnet } from '@stacks/network';
5
5
  import AppHeader from './components/AppHeader.vue';
6
6
  import CounterInteraction from './components/CounterInteraction.vue';
7
7
 
8
- const appConfig = new AppConfig(['store_write', 'publish_data']);
9
- const userSession = new UserSession({ appConfig });
10
-
11
8
  const network =
12
9
  import.meta.env.VITE_NETWORK === 'mainnet'
13
10
  ? new StacksMainnet()
@@ -16,29 +13,30 @@ const network =
16
13
  const address = ref<string | null>(null);
17
14
 
18
15
  onMounted(() => {
19
- if (userSession.isUserSignedIn()) {
20
- const userData = userSession.loadUserData();
21
- const networkKey = import.meta.env.VITE_NETWORK === 'mainnet' ? 'mainnet' : 'testnet';
22
- address.value = userData.profile.stxAddress[networkKey];
16
+ const storage = getLocalStorage();
17
+ const networkKey = import.meta.env.VITE_NETWORK === 'mainnet' ? 'mainnet' : 'testnet';
18
+ if (storage?.addresses?.[networkKey]) {
19
+ address.value = storage.addresses[networkKey];
23
20
  }
24
21
  });
25
22
 
26
- function handleConnect() {
27
- showConnect({
28
- appDetails: {
29
- name: 'Stacks App',
30
- icon: window.location.origin + '/logo.svg',
31
- },
32
- redirectTo: '/',
33
- onFinish: () => {
23
+ async function handleConnect() {
24
+ try {
25
+ const response = await connect();
26
+ const userAddress = response.addresses?.[0]?.address;
27
+ if (userAddress) {
28
+ address.value = userAddress;
34
29
  window.location.reload();
35
- },
36
- userSession,
37
- });
30
+ }
31
+ } catch (error) {
32
+ console.error('Failed to connect:', error);
33
+ }
38
34
  }
39
35
 
40
36
  function handleDisconnect() {
41
- userSession.signUserOut('/');
37
+ disconnect();
38
+ address.value = null;
39
+ window.location.reload();
42
40
  }
43
41
  </script>
44
42
 
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, onMounted } from 'vue';
3
- import { openContractCall } from '@stacks/connect';
3
+ import { request } from '@stacks/connect';
4
4
  import { callReadOnlyFunction, cvToValue } from '@stacks/transactions';
5
5
  import type { StacksNetwork } from '@stacks/network';
6
6
 
@@ -29,7 +29,8 @@ async function fetchCounter() {
29
29
  senderAddress: contractAddress,
30
30
  });
31
31
  const value = cvToValue(result);
32
- counter.value = value?.value ?? 0;
32
+ // Handle both bigint (Stacks returns bigint now often) and number types safely
33
+ counter.value = value?.value ? Number(value.value) : 0;
33
34
  } catch (error) {
34
35
  console.error('Failed to fetch counter:', error);
35
36
  } finally {
@@ -45,16 +46,15 @@ async function handleIncrement() {
45
46
  if (!props.senderAddress) return;
46
47
  isIncrementing.value = true;
47
48
  try {
48
- await openContractCall({
49
- contractAddress,
50
- contractName,
49
+ await request('stx_callContract', {
50
+ contract: `${contractAddress}.${contractName}`,
51
51
  functionName: 'increment',
52
52
  functionArgs: [],
53
- network: props.network,
54
- onFinish: () => {
55
- setTimeout(fetchCounter, 2000);
56
- },
53
+ postConditions: [],
57
54
  });
55
+
56
+ // Speculatively refetch
57
+ setTimeout(fetchCounter, 2000);
58
58
  } catch (error) {
59
59
  console.error('Increment failed:', error);
60
60
  } finally {
@@ -66,16 +66,14 @@ async function handleDecrement() {
66
66
  if (!props.senderAddress) return;
67
67
  isDecrementing.value = true;
68
68
  try {
69
- await openContractCall({
70
- contractAddress,
71
- contractName,
69
+ await request('stx_callContract', {
70
+ contract: `${contractAddress}.${contractName}`,
72
71
  functionName: 'decrement',
73
72
  functionArgs: [],
74
- network: props.network,
75
- onFinish: () => {
76
- setTimeout(fetchCounter, 2000);
77
- },
73
+ postConditions: [],
78
74
  });
75
+
76
+ setTimeout(fetchCounter, 2000);
79
77
  } catch (error) {
80
78
  console.error('Decrement failed:', error);
81
79
  } finally {
@@ -89,7 +87,7 @@ async function handleDecrement() {
89
87
  <h2 class="text-2xl font-bold mb-4">Counter Contract</h2>
90
88
 
91
89
  <div class="mb-6 text-center">
92
- <div class="text-6xl font-bold text-stacks-purple">
90
+ <div class="text-6xl font-bold text-gray-100">
93
91
  {{ isLoading ? '...' : counter }}
94
92
  </div>
95
93
  <p class="text-sm text-gray-500 mt-2">Current count</p>