create-sbc-app 0.1.2 → 0.1.5
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
|
@@ -105,6 +105,8 @@ The template includes comprehensive environment configuration:
|
|
|
105
105
|
```bash
|
|
106
106
|
# Your SBC API key (get from SBC dashboard)
|
|
107
107
|
VITE_SBC_API_KEY=your_api_key_here
|
|
108
|
+
# "base" or "baseSepolia"
|
|
109
|
+
VITE_CHAIN="baseSepolia"
|
|
108
110
|
```
|
|
109
111
|
|
|
110
112
|
## 📱 UI Components
|
|
@@ -132,8 +134,14 @@ cd my-sbc-app
|
|
|
132
134
|
# Copy environment template
|
|
133
135
|
cp .env.template .env
|
|
134
136
|
|
|
135
|
-
#
|
|
136
|
-
|
|
137
|
+
# then ensure your .env has the environment variables set up
|
|
138
|
+
|
|
139
|
+
# "base" or "baseSepolia"
|
|
140
|
+
VITE_CHAIN="baseSepolia"
|
|
141
|
+
# Custom RPC URL (optional) - e.g. get one from Alchemey at https://dashboard.alchemy.com/apps
|
|
142
|
+
VITE_RPC_URL=
|
|
143
|
+
# Get your SBC API Key at https://dashboard.stablecoin.xyz
|
|
144
|
+
VITE_SBC_API_KEY=
|
|
137
145
|
```
|
|
138
146
|
|
|
139
147
|
### 3. Start Development
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-sbc-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Scaffold a new SBC App Kit project with one command.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-sbc-app": "bin/cli.js"
|
|
@@ -9,7 +9,14 @@
|
|
|
9
9
|
"build": "tsc",
|
|
10
10
|
"dev": "tsc --watch",
|
|
11
11
|
"start": "node bin/cli.js",
|
|
12
|
-
"prepublishOnly": "npm run build"
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"preversion": "npm run build && npm run test-pack",
|
|
14
|
+
"test-pack": "npm pack --dry-run",
|
|
15
|
+
"test-cli": "mkdir -p test-cli && cd test-cli && node ../bin/cli.js test-app --template react && rm -rf test-cli",
|
|
16
|
+
"prepare-publish": "npm run build && npm run test-pack && npm run test-cli",
|
|
17
|
+
"publish-patch": "npm run prepare-publish && npm version patch && npm publish",
|
|
18
|
+
"publish-minor": "npm run prepare-publish && npm version minor && npm publish",
|
|
19
|
+
"publish-major": "npm run prepare-publish && npm version major && npm publish"
|
|
13
20
|
},
|
|
14
21
|
"type": "module",
|
|
15
22
|
"dependencies": {
|
|
@@ -16,11 +16,14 @@ Copy the example environment file and add your SBC API key:
|
|
|
16
16
|
cp .env.example .env # Optional: for local overrides
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Edit `.env` and add your SBC API key:
|
|
19
|
+
Edit `.env` and add your SBC API key and Chain, with the optional for a custom RPC:
|
|
20
20
|
```bash
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
# "base" or "baseSepolia"
|
|
22
|
+
VITE_CHAIN="baseSepolia"
|
|
23
|
+
# Custom RPC URL (optional) - e.g. get one from Alchemey at https://dashboard.alchemy.com/apps
|
|
24
|
+
VITE_RPC_URL=
|
|
25
|
+
# Get your SBC API Key at https://dashboard.stablecoin.xyz
|
|
26
|
+
VITE_SBC_API_KEY=
|
|
24
27
|
```
|
|
25
28
|
|
|
26
29
|
> **Get your API key:** Visit the [SBC Dashboard](https://dashboard.stablecoin.xyz) to create an account and get your API key.
|
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
import { useState, useEffect, useRef, createContext, useContext } from 'react';
|
|
2
2
|
import { SbcProvider, WalletButton, useSbcApp, useUserOperation } from '@stablecoin.xyz/react';
|
|
3
|
-
import {
|
|
3
|
+
import { base, baseSepolia } from 'viem/chains';
|
|
4
4
|
import { createPublicClient, http, getAddress, parseSignature, WalletClient, PublicClient } from 'viem';
|
|
5
5
|
import { parseUnits, encodeFunctionData, erc20Abi } from 'viem';
|
|
6
6
|
import './App.css';
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
const
|
|
10
|
-
const
|
|
8
|
+
// Chain selection helpers
|
|
9
|
+
const chain = (import.meta.env.VITE_CHAIN === 'base') ? base : baseSepolia;
|
|
10
|
+
const rpcUrl = import.meta.env.VITE_RPC_URL;
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const SBC_TOKEN_ADDRESS = (chain) => {
|
|
13
|
+
if (chain.id === baseSepolia.id) {
|
|
14
|
+
return '0xf9FB20B8E097904f0aB7d12e9DbeE88f2dcd0F16';
|
|
15
|
+
} else if (chain.id === base.id) {
|
|
16
|
+
return '0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798';
|
|
17
|
+
}
|
|
18
|
+
throw new Error('Unsupported chain');
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const SBC_DECIMALS = (chain) => {
|
|
22
|
+
if (chain.id === baseSepolia.id) {
|
|
23
|
+
return 6;
|
|
24
|
+
} else if (chain.id === base.id) {
|
|
25
|
+
return 18;
|
|
26
|
+
}
|
|
27
|
+
throw new Error('Unsupported chain');
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const chainExplorer = (chain) => {
|
|
31
|
+
if (chain.id === baseSepolia.id) {
|
|
32
|
+
return 'https://sepolia.basescan.org';
|
|
33
|
+
} else if (chain.id === base.id) {
|
|
34
|
+
return 'https://basescan.org';
|
|
35
|
+
}
|
|
36
|
+
throw new Error('Unsupported chain');
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const publicClient = createPublicClient({ chain, transport: http() });
|
|
13
40
|
|
|
14
41
|
const erc20PermitAbi = [
|
|
15
42
|
...erc20Abi,
|
|
@@ -63,6 +90,10 @@ function WalletStatus({ onDisconnect }: { onDisconnect: () => void }) {
|
|
|
63
90
|
<label>Connection:</label>
|
|
64
91
|
<div className="value">Connected via wallet extension</div>
|
|
65
92
|
</div>
|
|
93
|
+
<div className="info-row">
|
|
94
|
+
<label>Chain:</label>
|
|
95
|
+
<div className="value">{chain.name}</div>
|
|
96
|
+
</div>
|
|
66
97
|
</div>
|
|
67
98
|
);
|
|
68
99
|
}
|
|
@@ -81,7 +112,7 @@ function SmartAccountInfo() {
|
|
|
81
112
|
setIsLoadingBalance(true);
|
|
82
113
|
try {
|
|
83
114
|
const balance = await publicClient.readContract({
|
|
84
|
-
address: SBC_TOKEN_ADDRESS as `0x${string}`,
|
|
115
|
+
address: SBC_TOKEN_ADDRESS(chain) as `0x${string}`,
|
|
85
116
|
abi: erc20Abi,
|
|
86
117
|
functionName: 'balanceOf',
|
|
87
118
|
args: [account.address as `0x${string}`],
|
|
@@ -107,7 +138,7 @@ function SmartAccountInfo() {
|
|
|
107
138
|
setIsLoadingBalance(true);
|
|
108
139
|
try {
|
|
109
140
|
const balance = await publicClient.readContract({
|
|
110
|
-
address: SBC_TOKEN_ADDRESS as `0x${string}`,
|
|
141
|
+
address: SBC_TOKEN_ADDRESS(chain) as `0x${string}`,
|
|
111
142
|
abi: erc20Abi,
|
|
112
143
|
functionName: 'balanceOf',
|
|
113
144
|
args: [account.address as `0x${string}`],
|
|
@@ -138,7 +169,7 @@ function SmartAccountInfo() {
|
|
|
138
169
|
const formatSbcBalance = (balance: string | null): string => {
|
|
139
170
|
if (!balance) return '0.00';
|
|
140
171
|
try {
|
|
141
|
-
return (Number(balance) / Math.pow(10, SBC_DECIMALS)).toFixed(2);
|
|
172
|
+
return (Number(balance) / Math.pow(10, SBC_DECIMALS(chain))).toFixed(2);
|
|
142
173
|
} catch {
|
|
143
174
|
return '0.00';
|
|
144
175
|
}
|
|
@@ -198,7 +229,7 @@ function SendSBCForm() {
|
|
|
198
229
|
try {
|
|
199
230
|
const ownerChecksum = getAddress(ownerAddress);
|
|
200
231
|
const spenderChecksum = getAddress(account.address);
|
|
201
|
-
const value = parseUnits('1', SBC_DECIMALS); // Send 1 SBC
|
|
232
|
+
const value = parseUnits('1', SBC_DECIMALS(chain)); // Send 1 SBC
|
|
202
233
|
const deadline = Math.floor(Date.now() / 1000) + 60 * 30; // 30 min
|
|
203
234
|
|
|
204
235
|
const signature = await getPermitSignature({
|
|
@@ -207,8 +238,8 @@ function SendSBCForm() {
|
|
|
207
238
|
owner: ownerChecksum,
|
|
208
239
|
spender: spenderChecksum,
|
|
209
240
|
value,
|
|
210
|
-
tokenAddress: SBC_TOKEN_ADDRESS,
|
|
211
|
-
chainId:
|
|
241
|
+
tokenAddress: SBC_TOKEN_ADDRESS(chain),
|
|
242
|
+
chainId: chain.id,
|
|
212
243
|
deadline,
|
|
213
244
|
});
|
|
214
245
|
|
|
@@ -231,8 +262,8 @@ function SendSBCForm() {
|
|
|
231
262
|
|
|
232
263
|
await sendUserOperation({
|
|
233
264
|
calls: [
|
|
234
|
-
{ to: SBC_TOKEN_ADDRESS as `0x${string}`, data: permitCallData },
|
|
235
|
-
{ to: SBC_TOKEN_ADDRESS as `0x${string}`, data: transferFromCallData },
|
|
265
|
+
{ to: SBC_TOKEN_ADDRESS(chain) as `0x${string}`, data: permitCallData },
|
|
266
|
+
{ to: SBC_TOKEN_ADDRESS(chain) as `0x${string}`, data: transferFromCallData },
|
|
236
267
|
],
|
|
237
268
|
});
|
|
238
269
|
} catch (err) {
|
|
@@ -286,7 +317,7 @@ function SendSBCForm() {
|
|
|
286
317
|
<div className="success-message">
|
|
287
318
|
<p>✅ Transaction Successful!</p>
|
|
288
319
|
<a
|
|
289
|
-
href={
|
|
320
|
+
href={`${chainExplorer(chain)}/tx/${data.transactionHash}`}
|
|
290
321
|
target="_blank"
|
|
291
322
|
rel="noopener noreferrer"
|
|
292
323
|
>
|
|
@@ -369,7 +400,8 @@ function ThemeToggle() {
|
|
|
369
400
|
export default function App() {
|
|
370
401
|
const sbcConfig = {
|
|
371
402
|
apiKey: import.meta.env.VITE_SBC_API_KEY || '{{apiKey}}',
|
|
372
|
-
chain
|
|
403
|
+
chain,
|
|
404
|
+
rpcUrl,
|
|
373
405
|
wallet: 'auto' as const,
|
|
374
406
|
debug: true,
|
|
375
407
|
walletOptions: { autoConnect: false },
|
|
@@ -462,7 +494,7 @@ async function getPermitSignature({
|
|
|
462
494
|
name: tokenName as string,
|
|
463
495
|
version: '1',
|
|
464
496
|
chainId: BigInt(chainId),
|
|
465
|
-
verifyingContract:
|
|
497
|
+
verifyingContract: tokenAddress as `0x${string}`,
|
|
466
498
|
};
|
|
467
499
|
|
|
468
500
|
const types = {
|