create-sbc-app 0.1.5 → 0.3.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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -12
  3. package/bin/cli.js +63 -9
  4. package/package.json +1 -2
  5. package/templates/README.md +11 -4
  6. package/templates/react/.env.template +2 -1
  7. package/templates/react/package.json.template +3 -5
  8. package/templates/react/src/App.tsx.template +94 -10
  9. package/templates/react-dynamic/.env.template +7 -0
  10. package/templates/react-dynamic/README.md.template +24 -0
  11. package/templates/react-dynamic/eslint.config.js.template +34 -0
  12. package/templates/react-dynamic/index.html.template +14 -0
  13. package/templates/react-dynamic/package.json.template +33 -0
  14. package/templates/react-dynamic/postcss.config.js.template +8 -0
  15. package/templates/react-dynamic/public/sbc-logo.png +0 -0
  16. package/templates/react-dynamic/src/App.css.template +5 -0
  17. package/templates/react-dynamic/src/App.tsx.template +344 -0
  18. package/templates/react-dynamic/src/env.d.ts.template +14 -0
  19. package/templates/react-dynamic/src/index.css.template +15 -0
  20. package/templates/react-dynamic/src/main.tsx.template +12 -0
  21. package/templates/react-dynamic/tailwind.config.js.template +13 -0
  22. package/templates/react-dynamic/tsconfig.json.template +18 -0
  23. package/templates/react-dynamic/vite.config.ts.template +11 -0
  24. package/templates/react-para/.env.template +7 -0
  25. package/templates/react-para/README.md.template +24 -0
  26. package/templates/react-para/eslint.config.js.template +34 -0
  27. package/templates/react-para/index.html.template +14 -0
  28. package/templates/react-para/package.json.template +35 -0
  29. package/templates/react-para/postcss.config.js.template +8 -0
  30. package/templates/react-para/public/sbc-logo.png +0 -0
  31. package/templates/react-para/src/App.tsx.template +361 -0
  32. package/templates/react-para/src/components/ConnectButton.tsx.template +99 -0
  33. package/templates/react-para/src/env.d.ts.template +14 -0
  34. package/templates/react-para/src/hooks/usePara.ts.template +34 -0
  35. package/templates/react-para/src/hooks/useParaViem.ts.template +61 -0
  36. package/templates/react-para/src/index.css.template +5 -0
  37. package/templates/react-para/src/main.tsx.template +12 -0
  38. package/templates/react-para/src/providers.tsx.template +39 -0
  39. package/templates/react-para/src/utils/permit.ts.template +217 -0
  40. package/templates/react-para/tailwind.config.js.template +13 -0
  41. package/templates/react-para/tsconfig.json.template +18 -0
  42. package/templates/react-para/vite.config.ts.template +73 -0
@@ -0,0 +1,217 @@
1
+ import { hexToBytes, bytesToHex } from 'viem/utils';
2
+ import { hashTypedData, recoverAddress } from 'viem';
3
+
4
+ export type PermitTypedData = {
5
+ domain: {
6
+ name: string;
7
+ version: string;
8
+ chainId: bigint;
9
+ verifyingContract: `0x${string}`;
10
+ };
11
+ types: {
12
+ EIP712Domain: Array<{ name: string; type: string }>;
13
+ Permit: Array<{ name: string; type: string }>;
14
+ };
15
+ message: {
16
+ owner: `0x${string}`;
17
+ spender: `0x${string}`;
18
+ value: bigint;
19
+ nonce: bigint;
20
+ deadline: bigint;
21
+ };
22
+ primaryType: 'Permit';
23
+ };
24
+
25
+ export function buildPermitTypedData(params: {
26
+ tokenName: string;
27
+ chainId: number;
28
+ tokenAddress: `0x${string}`;
29
+ owner: `0x${string}`;
30
+ spender: `0x${string}`;
31
+ value: bigint;
32
+ nonce: bigint;
33
+ deadline: bigint;
34
+ }): PermitTypedData {
35
+ const { tokenName, chainId, tokenAddress, owner, spender, value, nonce, deadline } = params;
36
+ return {
37
+ domain: {
38
+ name: tokenName,
39
+ version: '1',
40
+ chainId: BigInt(chainId),
41
+ verifyingContract: tokenAddress,
42
+ },
43
+ types: {
44
+ EIP712Domain: [
45
+ { name: 'name', type: 'string' },
46
+ { name: 'version', type: 'string' },
47
+ { name: 'chainId', type: 'uint256' },
48
+ { name: 'verifyingContract', type: 'address' },
49
+ ],
50
+ Permit: [
51
+ { name: 'owner', type: 'address' },
52
+ { name: 'spender', type: 'address' },
53
+ { name: 'value', type: 'uint256' },
54
+ { name: 'nonce', type: 'uint256' },
55
+ { name: 'deadline', type: 'uint256' },
56
+ ],
57
+ },
58
+ primaryType: 'Permit',
59
+ message: { owner, spender, value, nonce, deadline },
60
+ } as const;
61
+ }
62
+
63
+ export function hashPermitTypedData(typed: PermitTypedData): `0x${string}` {
64
+ return hashTypedData({
65
+ domain: typed.domain,
66
+ types: typed.types,
67
+ primaryType: typed.primaryType,
68
+ message: typed.message,
69
+ } as any);
70
+ }
71
+
72
+ export function hex32ToBase64(hex: `0x${string}`): string {
73
+ const bytes = hexToBytes(hex);
74
+ let binary = '';
75
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
76
+ return btoa(binary);
77
+ }
78
+
79
+ export function base64ToBytes(b64: string): Uint8Array {
80
+ const binary = atob(b64);
81
+ const len = binary.length;
82
+ const bytes = new Uint8Array(len);
83
+ for (let i = 0; i < len; i++) bytes[i] = binary.charCodeAt(i);
84
+ return bytes;
85
+ }
86
+
87
+ function isHexStringLike(s: string): boolean {
88
+ return /^[0-9a-fA-F]+$/.test(s);
89
+ }
90
+
91
+ export function normalizeSignatureToRSV(sig: string | { signatureBase64?: string; signature?: string }): {
92
+ r: `0x${string}`;
93
+ s: `0x${string}`;
94
+ v: number;
95
+ } {
96
+ let sigBytes: Uint8Array;
97
+ if (typeof sig === 'string') {
98
+ const trimmed = sig.trim();
99
+ if (trimmed.startsWith('0x')) {
100
+ sigBytes = hexToBytes(trimmed as `0x${string}`);
101
+ } else if (isHexStringLike(trimmed) && (trimmed.length === 128 || trimmed.length === 130)) {
102
+ sigBytes = hexToBytes(('0x' + trimmed) as `0x${string}`);
103
+ } else {
104
+ const b = base64ToBytes(trimmed);
105
+ const asString = Array.from(b).every((c) => {
106
+ const ch = String.fromCharCode(c);
107
+ return /[0-9a-fA-Fx]/.test(ch);
108
+ })
109
+ ? String.fromCharCode(...b)
110
+ : null;
111
+ if (asString && (asString.startsWith('0x') || isHexStringLike(asString))) {
112
+ const hexStr = asString.startsWith('0x') ? asString : '0x' + asString;
113
+ sigBytes = hexToBytes(hexStr as `0x${string}`);
114
+ } else {
115
+ sigBytes = b;
116
+ }
117
+ }
118
+ } else if (sig?.signatureBase64) {
119
+ const b = base64ToBytes(sig.signatureBase64);
120
+ const isAsciiHex = Array.from(b).every((c) => /[0-9a-fA-Fx]/.test(String.fromCharCode(c)));
121
+ if (isAsciiHex) {
122
+ const s = String.fromCharCode(...b);
123
+ const hexStr = s.startsWith('0x') ? s : '0x' + s;
124
+ sigBytes = hexToBytes(hexStr as `0x${string}`);
125
+ } else {
126
+ sigBytes = b;
127
+ }
128
+ } else if (sig?.signature) {
129
+ const s = sig.signature as string;
130
+ if (s.startsWith('0x') || isHexStringLike(s)) {
131
+ const hexStr = s.startsWith('0x') ? s : '0x' + s;
132
+ sigBytes = hexToBytes(hexStr as `0x${string}`);
133
+ } else {
134
+ const b = base64ToBytes(s);
135
+ const isAsciiHex = Array.from(b).every((c) => /[0-9a-fA-Fx]/.test(String.fromCharCode(c)));
136
+ if (isAsciiHex) {
137
+ const txt = String.fromCharCode(...b);
138
+ const hexStr = txt.startsWith('0x') ? txt : '0x' + txt;
139
+ sigBytes = hexToBytes(hexStr as `0x${string}`);
140
+ } else {
141
+ sigBytes = b;
142
+ }
143
+ }
144
+ } else {
145
+ throw new Error('Unsupported signature format from Para signMessage');
146
+ }
147
+
148
+ if (sigBytes.length === 65) {
149
+ const r = bytesToHex(sigBytes.slice(0, 32)) as `0x${string}`;
150
+ const s = bytesToHex(sigBytes.slice(32, 64)) as `0x${string}`;
151
+ let v = sigBytes[64];
152
+ if (v < 27) v += 27;
153
+ return { r, s, v };
154
+ }
155
+
156
+ if (sigBytes.length === 64) {
157
+ const rBytes = sigBytes.slice(0, 32);
158
+ const yParityAndS = sigBytes.slice(32, 64);
159
+ const yParity = (yParityAndS[0] & 0x80) ? 1 : 0;
160
+ const sBytes = new Uint8Array(yParityAndS);
161
+ sBytes[0] &= 0x7f;
162
+ const r = bytesToHex(rBytes) as `0x${string}`;
163
+ const s = bytesToHex(sBytes) as `0x${string}`;
164
+ const v = 27 + yParity;
165
+ return { r, s, v };
166
+ }
167
+
168
+ if (sigBytes[0] === 0x30) {
169
+ let offset = 2;
170
+ if (sigBytes[1] & 0x80) {
171
+ const lenOfLen = sigBytes[1] & 0x7f;
172
+ offset = 2 + lenOfLen;
173
+ }
174
+ if (sigBytes[offset] !== 0x02) throw new Error('Invalid DER signature (missing r)');
175
+ const rLen = sigBytes[offset + 1];
176
+ const rStart = offset + 2;
177
+ const rEnd = rStart + rLen;
178
+ const rRaw = sigBytes.slice(rStart, rEnd);
179
+ offset = rEnd;
180
+ if (sigBytes[offset] !== 0x02) throw new Error('Invalid DER signature (missing s)');
181
+ const sLen = sigBytes[offset + 1];
182
+ const sStart = offset + 2;
183
+ const sEnd = sStart + sLen;
184
+ const sRaw = sigBytes.slice(sStart, sEnd);
185
+ const r = bytesToHex(leftPad32(rRaw)) as `0x${string}`;
186
+ const s = bytesToHex(leftPad32(sRaw)) as `0x${string}`;
187
+ return { r, s, v: 0 };
188
+ }
189
+
190
+ throw new Error(`Unexpected signature length: ${sigBytes.length}`);
191
+ }
192
+
193
+ function leftPad32(src: Uint8Array): Uint8Array {
194
+ if (src.length === 32) return src;
195
+ const out = new Uint8Array(32);
196
+ out.set(src, 32 - src.length);
197
+ return out;
198
+ }
199
+
200
+ export async function deriveVForRS(params: {
201
+ digest: `0x${string}`;
202
+ r: `0x${string}`;
203
+ s: `0x${string}`;
204
+ owner: `0x${string}`;
205
+ }): Promise<number> {
206
+ const { digest, r, s, owner } = params;
207
+ for (const v of [27n, 28n]) {
208
+ try {
209
+ const addr = await recoverAddress({ hash: digest, signature: { r, s, v } });
210
+ if (addr.toLowerCase() === owner.toLowerCase()) return Number(v);
211
+ } catch {}
212
+ }
213
+ throw new Error('Failed to derive v that recovers owner');
214
+ }
215
+
216
+
217
+
@@ -0,0 +1,13 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: [
4
+ "./index.html",
5
+ "./src/**/*.{js,ts,jsx,tsx}",
6
+ ],
7
+ theme: {
8
+ extend: {},
9
+ },
10
+ plugins: [],
11
+ }
12
+
13
+
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "skipLibCheck": true,
7
+ "moduleResolution": "bundler",
8
+ "allowImportingTsExtensions": true,
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "noEmit": true,
12
+ "jsx": "react-jsx",
13
+ "strict": true
14
+ },
15
+ "include": ["src"]
16
+ }
17
+
18
+
@@ -0,0 +1,73 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import { nodePolyfills } from 'vite-plugin-node-polyfills'
4
+
5
+ // Strip sourceMappingURL comments from problematic vendor files to silence missing sourcemap warnings
6
+ function stripVendorSourcemaps() {
7
+ const vendorRe = /node_modules\/(?:@walletconnect|@cosmjs|@cosmsnap)\//;
8
+ const mapLine = /\/\/#\s*sourceMappingURL=.*$/gm;
9
+ return {
10
+ name: 'strip-vendor-sourcemaps',
11
+ enforce: 'post' as const,
12
+ transform(code: string, id: string) {
13
+ if (!vendorRe.test(id)) return null;
14
+ if (!mapLine.test(code)) return null;
15
+ const cleaned = code.replace(mapLine, '');
16
+ return { code: cleaned, map: null };
17
+ }
18
+ }
19
+ }
20
+
21
+ // Esbuild plugin variant for optimizeDeps stage
22
+ function esbuildStripVendorSourcemaps() {
23
+ const vendorRe = /node_modules\/(?:@walletconnect|@cosmjs|@cosmsnap)\//;
24
+ const mapLine = /\/\/#\s*sourceMappingURL=.*$/gm;
25
+ return {
26
+ name: 'esbuild-strip-vendor-sourcemaps',
27
+ setup(build: any) {
28
+ build.onLoad({ filter: /\.(mjs|cjs|js)$/ }, async (args: any) => {
29
+ if (!vendorRe.test(args.path)) return;
30
+ const fs = await import('fs/promises');
31
+ try {
32
+ const code = await fs.readFile(args.path, 'utf8');
33
+ if (!mapLine.test(code)) return;
34
+ const cleaned = code.replace(mapLine, '');
35
+ return { contents: cleaned, loader: 'js' };
36
+ } catch {
37
+ return;
38
+ }
39
+ });
40
+ }
41
+ }
42
+ }
43
+
44
+ export default defineConfig({
45
+ plugins: [react(), nodePolyfills(), stripVendorSourcemaps()],
46
+ define: { global: 'globalThis' },
47
+ server: { port: 3000 },
48
+ optimizeDeps: {
49
+ exclude: [
50
+ '@getpara/cosmos-wallet-connectors',
51
+ '@getpara/solana-wallet-connectors',
52
+ '@cosmjs/*',
53
+ '@cosmsnap/*'
54
+ ],
55
+ esbuildOptions: {
56
+ sourcemap: false,
57
+ plugins: [esbuildStripVendorSourcemaps()]
58
+ }
59
+ },
60
+ build: {
61
+ sourcemap: false,
62
+ rollupOptions: {
63
+ external: [
64
+ '@getpara/cosmos-wallet-connectors',
65
+ '@getpara/solana-wallet-connectors',
66
+ /@cosmjs\//,
67
+ /@cosmsnap\//
68
+ ]
69
+ }
70
+ }
71
+ })
72
+
73
+