create-sbc-app 0.1.2 → 0.1.3
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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-sbc-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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": {
|
|
@@ -1,15 +1,41 @@
|
|
|
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 SBC_DECIMALS = 6;
|
|
8
|
+
// Chain selection helpers
|
|
9
|
+
const chain = (import.meta.env.VITE_CHAIN === 'base') ? base : baseSepolia;
|
|
11
10
|
|
|
12
|
-
const
|
|
11
|
+
const SBC_TOKEN_ADDRESS = (chain) => {
|
|
12
|
+
if (chain.id === baseSepolia.id) {
|
|
13
|
+
return '0xf9FB20B8E097904f0aB7d12e9DbeE88f2dcd0F16';
|
|
14
|
+
} else if (chain.id === base.id) {
|
|
15
|
+
return '0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798';
|
|
16
|
+
}
|
|
17
|
+
throw new Error('Unsupported chain');
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const SBC_DECIMALS = (chain) => {
|
|
21
|
+
if (chain.id === baseSepolia.id) {
|
|
22
|
+
return 6;
|
|
23
|
+
} else if (chain.id === base.id) {
|
|
24
|
+
return 18;
|
|
25
|
+
}
|
|
26
|
+
throw new Error('Unsupported chain');
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const chainExplorer = (chain) => {
|
|
30
|
+
if (chain.id === baseSepolia.id) {
|
|
31
|
+
return 'https://sepolia.basescan.org';
|
|
32
|
+
} else if (chain.id === base.id) {
|
|
33
|
+
return 'https://basescan.org';
|
|
34
|
+
}
|
|
35
|
+
throw new Error('Unsupported chain');
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const publicClient = createPublicClient({ chain, transport: http() });
|
|
13
39
|
|
|
14
40
|
const erc20PermitAbi = [
|
|
15
41
|
...erc20Abi,
|
|
@@ -63,6 +89,10 @@ function WalletStatus({ onDisconnect }: { onDisconnect: () => void }) {
|
|
|
63
89
|
<label>Connection:</label>
|
|
64
90
|
<div className="value">Connected via wallet extension</div>
|
|
65
91
|
</div>
|
|
92
|
+
<div className="info-row">
|
|
93
|
+
<label>Chain:</label>
|
|
94
|
+
<div className="value">{chain.name}</div>
|
|
95
|
+
</div>
|
|
66
96
|
</div>
|
|
67
97
|
);
|
|
68
98
|
}
|
|
@@ -81,7 +111,7 @@ function SmartAccountInfo() {
|
|
|
81
111
|
setIsLoadingBalance(true);
|
|
82
112
|
try {
|
|
83
113
|
const balance = await publicClient.readContract({
|
|
84
|
-
address: SBC_TOKEN_ADDRESS as `0x${string}`,
|
|
114
|
+
address: SBC_TOKEN_ADDRESS(chain) as `0x${string}`,
|
|
85
115
|
abi: erc20Abi,
|
|
86
116
|
functionName: 'balanceOf',
|
|
87
117
|
args: [account.address as `0x${string}`],
|
|
@@ -107,7 +137,7 @@ function SmartAccountInfo() {
|
|
|
107
137
|
setIsLoadingBalance(true);
|
|
108
138
|
try {
|
|
109
139
|
const balance = await publicClient.readContract({
|
|
110
|
-
address: SBC_TOKEN_ADDRESS as `0x${string}`,
|
|
140
|
+
address: SBC_TOKEN_ADDRESS(chain) as `0x${string}`,
|
|
111
141
|
abi: erc20Abi,
|
|
112
142
|
functionName: 'balanceOf',
|
|
113
143
|
args: [account.address as `0x${string}`],
|
|
@@ -138,7 +168,7 @@ function SmartAccountInfo() {
|
|
|
138
168
|
const formatSbcBalance = (balance: string | null): string => {
|
|
139
169
|
if (!balance) return '0.00';
|
|
140
170
|
try {
|
|
141
|
-
return (Number(balance) / Math.pow(10, SBC_DECIMALS)).toFixed(2);
|
|
171
|
+
return (Number(balance) / Math.pow(10, SBC_DECIMALS(chain))).toFixed(2);
|
|
142
172
|
} catch {
|
|
143
173
|
return '0.00';
|
|
144
174
|
}
|
|
@@ -198,7 +228,7 @@ function SendSBCForm() {
|
|
|
198
228
|
try {
|
|
199
229
|
const ownerChecksum = getAddress(ownerAddress);
|
|
200
230
|
const spenderChecksum = getAddress(account.address);
|
|
201
|
-
const value = parseUnits('1', SBC_DECIMALS); // Send 1 SBC
|
|
231
|
+
const value = parseUnits('1', SBC_DECIMALS(chain)); // Send 1 SBC
|
|
202
232
|
const deadline = Math.floor(Date.now() / 1000) + 60 * 30; // 30 min
|
|
203
233
|
|
|
204
234
|
const signature = await getPermitSignature({
|
|
@@ -207,8 +237,8 @@ function SendSBCForm() {
|
|
|
207
237
|
owner: ownerChecksum,
|
|
208
238
|
spender: spenderChecksum,
|
|
209
239
|
value,
|
|
210
|
-
tokenAddress: SBC_TOKEN_ADDRESS,
|
|
211
|
-
chainId:
|
|
240
|
+
tokenAddress: SBC_TOKEN_ADDRESS(chain),
|
|
241
|
+
chainId: chain.id,
|
|
212
242
|
deadline,
|
|
213
243
|
});
|
|
214
244
|
|
|
@@ -231,8 +261,8 @@ function SendSBCForm() {
|
|
|
231
261
|
|
|
232
262
|
await sendUserOperation({
|
|
233
263
|
calls: [
|
|
234
|
-
{ to: SBC_TOKEN_ADDRESS as `0x${string}`, data: permitCallData },
|
|
235
|
-
{ to: SBC_TOKEN_ADDRESS as `0x${string}`, data: transferFromCallData },
|
|
264
|
+
{ to: SBC_TOKEN_ADDRESS(chain) as `0x${string}`, data: permitCallData },
|
|
265
|
+
{ to: SBC_TOKEN_ADDRESS(chain) as `0x${string}`, data: transferFromCallData },
|
|
236
266
|
],
|
|
237
267
|
});
|
|
238
268
|
} catch (err) {
|
|
@@ -286,7 +316,7 @@ function SendSBCForm() {
|
|
|
286
316
|
<div className="success-message">
|
|
287
317
|
<p>✅ Transaction Successful!</p>
|
|
288
318
|
<a
|
|
289
|
-
href={
|
|
319
|
+
href={`${chainExplorer(chain)}/tx/${data.transactionHash}`}
|
|
290
320
|
target="_blank"
|
|
291
321
|
rel="noopener noreferrer"
|
|
292
322
|
>
|
|
@@ -369,7 +399,7 @@ function ThemeToggle() {
|
|
|
369
399
|
export default function App() {
|
|
370
400
|
const sbcConfig = {
|
|
371
401
|
apiKey: import.meta.env.VITE_SBC_API_KEY || '{{apiKey}}',
|
|
372
|
-
chain
|
|
402
|
+
chain,
|
|
373
403
|
wallet: 'auto' as const,
|
|
374
404
|
debug: true,
|
|
375
405
|
walletOptions: { autoConnect: false },
|
|
@@ -462,7 +492,7 @@ async function getPermitSignature({
|
|
|
462
492
|
name: tokenName as string,
|
|
463
493
|
version: '1',
|
|
464
494
|
chainId: BigInt(chainId),
|
|
465
|
-
verifyingContract:
|
|
495
|
+
verifyingContract: tokenAddress as `0x${string}`,
|
|
466
496
|
};
|
|
467
497
|
|
|
468
498
|
const types = {
|