create-sbc-app 0.4.2 → 0.4.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
@@ -34,14 +34,14 @@ Arguments:
34
34
  Options:
35
35
  -V, --version output the version number
36
36
  -t, --template <type> Template to use: react, react-dynamic, react-para, or react-turnkey
37
- -c, --chain <chain> Chain to use: baseSepolia, base, or radiusTestnet
37
+ -c, --chain <chain> Chain to use: baseSepolia, base, radiusTestnet, or radius
38
38
  --api-key <apiKey> Your SBC API key for immediate configuration
39
39
  --wallet <wallet> Wallet integration (not yet implemented)
40
40
  -h, --help display help for command
41
41
 
42
42
  Examples:
43
43
  $ npx create-sbc-app my-app
44
- $ npx create-sbc-app my-app --template react --chain radiusTestnet
44
+ $ npx create-sbc-app my-app --template react --chain radius
45
45
  $ npx create-sbc-app my-app --template react-dynamic --chain base
46
46
  $ npx create-sbc-app my-app --template react-para --api-key your-key
47
47
  $ npx create-sbc-app my-app --template react-turnkey --chain base
@@ -56,6 +56,7 @@ Available Chains:
56
56
  - baseSepolia Base Sepolia testnet (default)
57
57
  - base Base mainnet
58
58
  - radiusTestnet Radius testnet (react template only - not supported by Dynamic, Para, or Turnkey)
59
+ - radius Radius mainnet (react template only - not supported by Dynamic, Para, or Turnkey)
59
60
  ```
60
61
 
61
62
  ## ✨ Features
@@ -98,7 +99,7 @@ The React template includes comprehensive, production-ready examples:
98
99
  npx create-sbc-app my-app
99
100
  ```
100
101
 
101
- **Supported Chains:** Base Sepolia, Base, Radius Testnet
102
+ **Supported Chains:** Base Sepolia, Base, Radius Testnet, Radius Mainnet
102
103
 
103
104
  **Features:**
104
105
  - Fast development setup
@@ -184,7 +185,7 @@ The template includes comprehensive environment configuration:
184
185
  # Your SBC API key (get from SBC dashboard)
185
186
  VITE_SBC_API_KEY=your_api_key_here
186
187
 
187
- # Supported chains: "baseSepolia" | "base" | "radiusTestnet"
188
+ # Supported chains: "baseSepolia" | "base" | "radiusTestnet" | "radius"
188
189
  VITE_CHAIN="baseSepolia"
189
190
  ```
190
191
 
@@ -215,7 +216,7 @@ cp .env.template .env
215
216
 
216
217
  # then ensure your .env has the environment variables set up
217
218
 
218
- # Supported chains: "baseSepolia" | "base" | "radiusTestnet"
219
+ # Supported chains: "baseSepolia" | "base" | "radiusTestnet" | "radius"
219
220
  VITE_CHAIN="baseSepolia"
220
221
  # Custom RPC URL (optional) - e.g. get one from Alchemy at https://dashboard.alchemy.com/apps
221
222
  VITE_RPC_URL=
package/bin/cli.js CHANGED
@@ -13,13 +13,13 @@ program
13
13
  .version('0.4.0')
14
14
  .argument('[project-directory]', 'Directory to create the new app in')
15
15
  .option('-t, --template <template>', 'Template to use: react, react-dynamic, react-para, or react-turnkey')
16
- .option('-c, --chain <chain>', 'Chain to use: baseSepolia, base, or radiusTestnet')
16
+ .option('-c, --chain <chain>', 'Chain to use: baseSepolia, base, radiusTestnet, or radius')
17
17
  .option('--api-key <apiKey>', 'Your SBC API key for immediate configuration')
18
18
  .option('--wallet <wallet>', 'Wallet integration (not yet implemented)')
19
19
  .addHelpText('after', `
20
20
  Examples:
21
21
  $ create-sbc-app my-app
22
- $ create-sbc-app my-app --template react --chain radiusTestnet
22
+ $ create-sbc-app my-app --template react --chain radius
23
23
  $ create-sbc-app my-app --template react --api-key your-api-key
24
24
 
25
25
  Available Templates:
@@ -32,6 +32,7 @@ Available Chains:
32
32
  - baseSepolia Base Sepolia testnet (default)
33
33
  - base Base mainnet
34
34
  - radiusTestnet Radius testnet
35
+ - radius Radius mainnet
35
36
  `)
36
37
  .action(async (dir, options) => {
37
38
  if (options.wallet) {
@@ -47,7 +48,8 @@ Available Chains:
47
48
  const chainChoices = [
48
49
  { title: 'Base Sepolia (testnet)', value: 'baseSepolia' },
49
50
  { title: 'Base (mainnet)', value: 'base' },
50
- { title: 'Radius Testnet', value: 'radiusTestnet' }
51
+ { title: 'Radius Testnet', value: 'radiusTestnet' },
52
+ { title: 'Radius (mainnet)', value: 'radius' }
51
53
  ];
52
54
  // Use provided argument or prompt for project directory
53
55
  let projectDir = dir && dir.trim() ? dir.trim() : '';
@@ -83,7 +85,7 @@ Available Chains:
83
85
  }
84
86
  }
85
87
  // Use provided option or prompt for chain
86
- let chain = options.chain && ['baseSepolia', 'base', 'radiusTestnet'].includes(options.chain) ? options.chain : '';
88
+ let chain = options.chain && ['baseSepolia', 'base', 'radiusTestnet', 'radius'].includes(options.chain) ? options.chain : '';
87
89
  if (!chain) {
88
90
  const res = await prompts({
89
91
  type: 'select',
@@ -97,14 +99,14 @@ Available Chains:
97
99
  process.exit(1);
98
100
  }
99
101
  chain = res.chain;
100
- if (!chain || !['baseSepolia', 'base', 'radiusTestnet'].includes(chain)) {
102
+ if (!chain || !['baseSepolia', 'base', 'radiusTestnet', 'radius'].includes(chain)) {
101
103
  console.log('Chain selection is required.');
102
104
  process.exit(1);
103
105
  }
104
106
  }
105
107
  // Validate template + chain compatibility
106
- if (['react-dynamic', 'react-para', 'react-turnkey'].includes(template) && chain === 'radiusTestnet') {
107
- console.log(`\nError: The ${template} template does not support radiusTestnet.`);
108
+ if (['react-dynamic', 'react-para', 'react-turnkey'].includes(template) && (chain === 'radiusTestnet' || chain === 'radius')) {
109
+ console.log(`\nError: The ${template} template does not support ${chain}.`);
108
110
  console.log('Please use baseSepolia or base chain instead.\n');
109
111
  process.exit(1);
110
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sbc-app",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Scaffold a new SBC App Kit project with one command.",
5
5
  "bin": {
6
6
  "create-sbc-app": "bin/cli.js"
@@ -1,5 +1,5 @@
1
1
  # SBC App Kit Configuration
2
- # Supported chains: "baseSepolia" | "base" | "radiusTestnet"
2
+ # Supported chains: "baseSepolia" | "base" | "radiusTestnet" | "radius"
3
3
  VITE_CHAIN="{{chain}}"
4
4
  # Custom RPC URL (optional) - e.g. get one from Alchemey at https://dashboard.alchemy.com/apps
5
5
  VITE_RPC_URL=
@@ -1,6 +1,6 @@
1
1
  import { useState, useEffect, useRef, createContext, useContext } from 'react';
2
2
  import { SbcProvider, WalletButton, useSbcApp, useUserOperation } from '@stablecoin.xyz/react';
3
- import { radiusTestnet, TestSBC_CONTRACT_ADDRESS } from '@stablecoin.xyz/core';
3
+ import { radiusTestnet, radius, SBC_CONTRACT_ADDRESS_RADIUS } from '@stablecoin.xyz/core';
4
4
  import { base, baseSepolia } from 'viem/chains';
5
5
  import { createPublicClient, http, getAddress, parseSignature, WalletClient, PublicClient } from 'viem';
6
6
  import { parseUnits, encodeFunctionData, erc20Abi } from 'viem';
@@ -11,22 +11,20 @@ const getChain = () => {
11
11
  const chainEnv = import.meta.env.VITE_CHAIN;
12
12
  if (chainEnv === 'base') return base;
13
13
  if (chainEnv === 'radiusTestnet') return radiusTestnet;
14
+ if (chainEnv === 'radius') return radius;
14
15
  return baseSepolia;
15
16
  };
16
17
 
17
18
  const chain = getChain();
18
19
  const rpcUrl = import.meta.env.VITE_RPC_URL;
19
20
 
20
- // Radius testnet uses TestSBC (a test token for development)
21
- const TEST_SBC_DECIMALS = 6;
22
-
23
21
  const SBC_TOKEN_ADDRESS = (chain) => {
24
22
  if (chain.id === baseSepolia.id) {
25
23
  return '0xf9FB20B8E097904f0aB7d12e9DbeE88f2dcd0F16';
26
24
  } else if (chain.id === base.id) {
27
25
  return '0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798';
28
- } else if (chain.id === radiusTestnet.id) {
29
- return TestSBC_CONTRACT_ADDRESS;
26
+ } else if (chain.id === radiusTestnet.id || chain.id === radius.id) {
27
+ return SBC_CONTRACT_ADDRESS_RADIUS;
30
28
  }
31
29
  throw new Error('Unsupported chain');
32
30
  };
@@ -36,18 +34,13 @@ const SBC_DECIMALS = (chain) => {
36
34
  return 6;
37
35
  } else if (chain.id === base.id) {
38
36
  return 18;
39
- } else if (chain.id === radiusTestnet.id) {
40
- return TEST_SBC_DECIMALS;
37
+ } else if (chain.id === radiusTestnet.id || chain.id === radius.id) {
38
+ return 6; // SBC token on Radius has 6 decimals
41
39
  }
42
40
  throw new Error('Unsupported chain');
43
41
  };
44
42
 
45
- const getTokenSymbol = (chain) => {
46
- if (chain.id === radiusTestnet.id) {
47
- return 'TestSBC';
48
- }
49
- return 'SBC';
50
- };
43
+ const getTokenSymbol = () => 'SBC';
51
44
 
52
45
  const chainExplorer = (chain) => {
53
46
  if (chain.id === baseSepolia.id) {
@@ -56,6 +49,8 @@ const chainExplorer = (chain) => {
56
49
  return 'https://basescan.org';
57
50
  } else if (chain.id === radiusTestnet.id) {
58
51
  return 'https://testnet.radiustech.xyz/testnet/explorer';
52
+ } else if (chain.id === radius.id) {
53
+ return 'https://network.radiustech.xyz';
59
54
  }
60
55
  throw new Error('Unsupported chain');
61
56
  };
@@ -2,7 +2,7 @@ import { DynamicContextProvider, useDynamicContext, DynamicUserProfile, DynamicW
2
2
  import { EthereumWalletConnectors } from '@dynamic-labs/ethereum';
3
3
  import { ZeroDevSmartWalletConnectors } from '@dynamic-labs/ethereum-aa';
4
4
  import { useSbcDynamic } from '@stablecoin.xyz/react';
5
- import { radiusTestnet, TestSBC_CONTRACT_ADDRESS } from '@stablecoin.xyz/core';
5
+ import { radiusTestnet, radius, SBC_CONTRACT_ADDRESS_RADIUS } from '@stablecoin.xyz/core';
6
6
  import { baseSepolia, base, type Chain } from 'viem/chains';
7
7
  import { createPublicClient, http, getAddress, parseUnits, encodeFunctionData, erc20Abi } from 'viem';
8
8
  import { useEffect, useState } from 'react';
@@ -12,38 +12,34 @@ const getChain = () => {
12
12
  const chainEnv = import.meta.env.VITE_CHAIN;
13
13
  if (chainEnv === 'base') return base;
14
14
  if (chainEnv === 'radiusTestnet') return radiusTestnet;
15
+ if (chainEnv === 'radius') return radius;
15
16
  return baseSepolia;
16
17
  };
17
18
 
18
19
  const chain = getChain();
19
20
  const rpcUrl = import.meta.env.VITE_RPC_URL;
20
21
 
21
- // Radius testnet uses TestSBC (a test token for development)
22
- const TEST_SBC_DECIMALS = 6;
23
-
24
22
  const SBC_TOKEN_ADDRESS = (chain: Chain) => {
25
23
  if (chain.id === baseSepolia.id) return '0xf9FB20B8E097904f0aB7d12e9DbeE88f2dcd0F16';
26
24
  if (chain.id === base.id) return '0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798';
27
- if (chain.id === radiusTestnet.id) return TestSBC_CONTRACT_ADDRESS;
25
+ if (chain.id === radiusTestnet.id || chain.id === radius.id) return SBC_CONTRACT_ADDRESS_RADIUS;
28
26
  throw new Error('Unsupported chain');
29
27
  };
30
28
 
31
29
  const SBC_DECIMALS = (chain: Chain) => {
32
30
  if (chain.id === baseSepolia.id) return 6;
33
31
  if (chain.id === base.id) return 18;
34
- if (chain.id === radiusTestnet.id) return TEST_SBC_DECIMALS;
32
+ if (chain.id === radiusTestnet.id || chain.id === radius.id) return 6;
35
33
  throw new Error('Unsupported chain');
36
34
  };
37
35
 
38
- const getTokenSymbol = (chain: Chain) => {
39
- if (chain.id === radiusTestnet.id) return 'TestSBC';
40
- return 'SBC';
41
- };
36
+ const getTokenSymbol = () => 'SBC';
42
37
 
43
38
  const chainExplorer = (chain: Chain) => {
44
39
  if (chain.id === baseSepolia.id) return 'https://sepolia.basescan.org';
45
40
  if (chain.id === base.id) return 'https://basescan.org';
46
41
  if (chain.id === radiusTestnet.id) return 'https://testnet.radiustech.xyz/testnet/explorer';
42
+ if (chain.id === radius.id) return 'https://network.radiustech.xyz';
47
43
  return '';
48
44
  };
49
45
 
@@ -2,7 +2,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
2
  import { Environment, ParaProvider } from '@getpara/react-sdk';
3
3
  import '@getpara/react-sdk/styles.css';
4
4
  import { useSbcPara } from '@stablecoin.xyz/react';
5
- import { radiusTestnet, TestSBC_CONTRACT_ADDRESS } from '@stablecoin.xyz/core';
5
+ import { radiusTestnet, radius, SBC_CONTRACT_ADDRESS_RADIUS } from '@stablecoin.xyz/core';
6
6
  import { useAccount, useWallet, useSignMessage } from '@getpara/react-sdk';
7
7
  import { baseSepolia, base, type Chain } from 'viem/chains';
8
8
  import { createPublicClient, http, getAddress, parseUnits, encodeFunctionData, erc20Abi } from 'viem';
@@ -18,33 +18,28 @@ const getChain = () => {
18
18
  const chainEnv = import.meta.env.VITE_CHAIN;
19
19
  if (chainEnv === 'base') return base;
20
20
  if (chainEnv === 'radiusTestnet') return radiusTestnet;
21
+ if (chainEnv === 'radius') return radius;
21
22
  return baseSepolia;
22
23
  };
23
24
 
24
25
  const chain = getChain();
25
26
  const rpcUrl = import.meta.env.VITE_RPC_URL;
26
27
 
27
- // Radius testnet uses TestSBC (a test token for development)
28
- const TEST_SBC_DECIMALS = 6;
29
-
30
28
  const SBC_TOKEN_ADDRESS = (chain: Chain) => {
31
29
  if (chain.id === baseSepolia.id) return '0xf9FB20B8E097904f0aB7d12e9DbeE88f2dcd0F16';
32
30
  if (chain.id === base.id) return '0xfdcC3dd6671eaB0709A4C0f3F53De9a333d80798';
33
- if (chain.id === radiusTestnet.id) return TestSBC_CONTRACT_ADDRESS;
31
+ if (chain.id === radiusTestnet.id || chain.id === radius.id) return SBC_CONTRACT_ADDRESS_RADIUS;
34
32
  throw new Error('Unsupported chain');
35
33
  };
36
34
 
37
35
  const SBC_DECIMALS = (chain: Chain) => {
38
36
  if (chain.id === baseSepolia.id) return 6;
39
37
  if (chain.id === base.id) return 18;
40
- if (chain.id === radiusTestnet.id) return TEST_SBC_DECIMALS;
38
+ if (chain.id === radiusTestnet.id || chain.id === radius.id) return 6;
41
39
  throw new Error('Unsupported chain');
42
40
  };
43
41
 
44
- const getTokenSymbol = (chain: Chain) => {
45
- if (chain.id === radiusTestnet.id) return 'TestSBC';
46
- return 'SBC';
47
- };
42
+ const getTokenSymbol = () => 'SBC';
48
43
 
49
44
  const publicClient = createPublicClient({ chain, transport: http(rpcUrl) });
50
45
 
@@ -5,11 +5,10 @@ export type PermitTypedData = {
5
5
  domain: {
6
6
  name: string;
7
7
  version: string;
8
- chainId: bigint;
8
+ chainId: number;
9
9
  verifyingContract: `0x${string}`;
10
10
  };
11
11
  types: {
12
- EIP712Domain: Array<{ name: string; type: string }>;
13
12
  Permit: Array<{ name: string; type: string }>;
14
13
  };
15
14
  message: {
@@ -50,7 +49,7 @@ export function buildPermitTypedData(params: {
50
49
  ],
51
50
  },
52
51
  primaryType: 'Permit',
53
- message: { owner, spender, value: value.toString(), nonce: nonce.toString(), deadline: deadline.toString() },
52
+ message: { owner, spender, value, nonce, deadline },
54
53
  } as const;
55
54
  }
56
55
 
@@ -1668,18 +1668,19 @@ async function getPermitSignature({
1668
1668
  { name: 'nonce', type: 'uint256' },
1669
1669
  { name: 'deadline', type: 'uint256' },
1670
1670
  ],
1671
- };
1671
+ } as const;
1672
1672
 
1673
1673
  const message = {
1674
1674
  owner: ownerChecksum,
1675
1675
  spender: spenderChecksum,
1676
- value: value.toString(),
1677
- nonce: nonce.toString(),
1678
- deadline: deadline.toString(),
1676
+ value: value,
1677
+ nonce: nonce as bigint,
1678
+ deadline: BigInt(deadline),
1679
1679
  };
1680
1680
 
1681
+ // walletClient.account is available on the client
1681
1682
  const signature = await walletClient.signTypedData({
1682
- account: ownerChecksum as `0x${string}`,
1683
+ account: walletClient.account!,
1683
1684
  domain,
1684
1685
  types,
1685
1686
  primaryType: 'Permit',