@coinbase/create-cdp-app 0.0.37 → 0.0.39
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/dist/index.js +14 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/template-nextjs/README.md +1 -1
- package/template-nextjs/src/app/api/onramp/buy-options/route.ts +0 -2
- package/template-nextjs/src/components/EOATransaction.tsx +7 -7
- package/template-nextjs/src/components/FundWallet.tsx +17 -3
- package/template-nextjs/src/components/SignedInScreen.tsx +2 -2
- package/template-nextjs/src/components/SignedInScreenWithOnramp.tsx +149 -41
- package/template-nextjs/src/components/SolanaTransaction.tsx +106 -96
- package/template-nextjs/src/lib/onramp-api.ts +6 -2
- package/template-react/src/EOATransaction.tsx +7 -7
- package/template-react/src/SignedInScreen.tsx +2 -3
- package/template-react/src/SolanaTransaction.tsx +105 -96
- package/template-react-native/App.tsx +28 -6
- package/template-react-native/SolanaTransaction.tsx +368 -0
- package/template-react-native/Transaction.tsx +101 -1
- package/template-react-native/components/WalletHeader.tsx +37 -13
- package/template-react-native/env.example +1 -0
- package/template-react-native/index.ts +7 -0
- package/template-react-native/metro.config.js +19 -0
- package/template-react-native/package.json +3 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEvmAddress, useIsSignedIn } from "@coinbase/cdp-hooks";
|
|
3
|
+
import { useEvmAddress, useIsSignedIn, useSolanaAddress } from "@coinbase/cdp-hooks";
|
|
4
|
+
import { Connection, clusterApiUrl, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
|
|
4
5
|
import { useCallback, useEffect, useMemo, useState, lazy, Suspense } from "react";
|
|
5
6
|
import {
|
|
6
7
|
createPublicClient,
|
|
@@ -16,8 +17,8 @@ import FundWallet from "@/components/FundWallet";
|
|
|
16
17
|
import Header from "@/components/Header";
|
|
17
18
|
import UserBalance from "@/components/UserBalance";
|
|
18
19
|
|
|
19
|
-
// Dynamically determine component path
|
|
20
|
-
const
|
|
20
|
+
// Dynamically determine component path for EVM transactions
|
|
21
|
+
const getEVMComponentPath = () => {
|
|
21
22
|
const isSmartAccount = process.env.NEXT_PUBLIC_CDP_CREATE_ETHEREUM_ACCOUNT_TYPE === "smart";
|
|
22
23
|
|
|
23
24
|
if (isSmartAccount) {
|
|
@@ -27,7 +28,8 @@ const getComponentPath = () => {
|
|
|
27
28
|
}
|
|
28
29
|
};
|
|
29
30
|
|
|
30
|
-
const
|
|
31
|
+
const EVMTransactionComponent = lazy(() => import(/* @vite-ignore */ getEVMComponentPath()));
|
|
32
|
+
const SolanaTransactionComponent = lazy(() => import("@/components/SolanaTransaction"));
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
35
|
* Create a viem client to access user's balance on the Base network
|
|
@@ -45,7 +47,17 @@ const sepoliaClient = createPublicClient({
|
|
|
45
47
|
transport: http(),
|
|
46
48
|
});
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Create a Solana connection to access user's balance on Solana Mainnet
|
|
52
|
+
*/
|
|
53
|
+
const solanaMainnetConnection = new Connection(clusterApiUrl("mainnet-beta"));
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a Solana connection to access user's balance on Solana Devnet
|
|
57
|
+
*/
|
|
58
|
+
const solanaDevnetConnection = new Connection(clusterApiUrl("devnet"));
|
|
59
|
+
|
|
60
|
+
const useEvmBalance = (
|
|
49
61
|
address: Address | null,
|
|
50
62
|
client: PublicClient<Transport, typeof base | typeof baseSepolia, undefined, undefined>,
|
|
51
63
|
poll = false,
|
|
@@ -75,56 +87,152 @@ const useBalance = (
|
|
|
75
87
|
return { balance, formattedBalance, getBalance };
|
|
76
88
|
};
|
|
77
89
|
|
|
90
|
+
const useSolanaBalance = (address: string | null, connection: Connection, poll = false) => {
|
|
91
|
+
const [balance, setBalance] = useState<bigint | undefined>(undefined);
|
|
92
|
+
|
|
93
|
+
const formattedBalance = useMemo(() => {
|
|
94
|
+
if (balance === undefined) return undefined;
|
|
95
|
+
// Convert lamports to SOL
|
|
96
|
+
return formatSol(Number(balance));
|
|
97
|
+
}, [balance]);
|
|
98
|
+
|
|
99
|
+
const getBalance = useCallback(async () => {
|
|
100
|
+
if (!address) return;
|
|
101
|
+
try {
|
|
102
|
+
const lamports = await connection.getBalance(new PublicKey(address));
|
|
103
|
+
setBalance(BigInt(lamports));
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error("Error fetching Solana balance:", error);
|
|
106
|
+
setBalance(BigInt(0));
|
|
107
|
+
}
|
|
108
|
+
}, [address, connection]);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (!poll) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
getBalance();
|
|
115
|
+
const interval = setInterval(getBalance, 500);
|
|
116
|
+
return () => clearInterval(interval);
|
|
117
|
+
}, [getBalance, poll]);
|
|
118
|
+
|
|
119
|
+
return { balance, formattedBalance, getBalance };
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Format a Solana balance.
|
|
124
|
+
*
|
|
125
|
+
* @param lamports - The balance in lamports.
|
|
126
|
+
* @returns The formatted balance.
|
|
127
|
+
*/
|
|
128
|
+
function formatSol(lamports: number) {
|
|
129
|
+
const maxDecimalPlaces = 9;
|
|
130
|
+
const roundedStr = (lamports / LAMPORTS_PER_SOL).toFixed(maxDecimalPlaces);
|
|
131
|
+
return roundedStr.replace(/0+$/, "").replace(/\.$/, "");
|
|
132
|
+
}
|
|
133
|
+
|
|
78
134
|
/**
|
|
79
|
-
* The Signed In screen
|
|
135
|
+
* The Signed In screen with onramp support for both EVM and Solana
|
|
80
136
|
*/
|
|
81
137
|
export default function SignedInScreen() {
|
|
82
138
|
const { isSignedIn } = useIsSignedIn();
|
|
83
139
|
const { evmAddress } = useEvmAddress();
|
|
140
|
+
const { solanaAddress } = useSolanaAddress();
|
|
84
141
|
|
|
85
|
-
|
|
142
|
+
const { formattedBalance, getBalance } = useEvmBalance(evmAddress, client, true);
|
|
143
|
+
const { formattedBalance: formattedBalanceSepolia, getBalance: getBalanceSepolia } =
|
|
144
|
+
useEvmBalance(evmAddress, sepoliaClient, true);
|
|
86
145
|
|
|
87
|
-
const { formattedBalance, getBalance } =
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
true,
|
|
92
|
-
);
|
|
146
|
+
const { formattedBalance: formattedBalanceSolana, getBalance: getBalanceSolana } =
|
|
147
|
+
useSolanaBalance(solanaAddress, solanaMainnetConnection);
|
|
148
|
+
const { formattedBalance: formattedBalanceSolanaDevnet, getBalance: getBalanceSolanaDevnet } =
|
|
149
|
+
useSolanaBalance(solanaAddress, solanaDevnetConnection, true);
|
|
93
150
|
|
|
94
151
|
return (
|
|
95
152
|
<>
|
|
96
153
|
<Header />
|
|
97
154
|
<main className="main flex-col-container flex-grow">
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<
|
|
120
|
-
<
|
|
155
|
+
{evmAddress && (
|
|
156
|
+
<>
|
|
157
|
+
<p className="page-heading">Fund your EVM wallet on Base</p>
|
|
158
|
+
<div className="main-inner flex-col-container">
|
|
159
|
+
<div className="card card--user-balance">
|
|
160
|
+
<UserBalance balance={formattedBalance} />
|
|
161
|
+
</div>
|
|
162
|
+
<div className="card card--transaction">
|
|
163
|
+
{isSignedIn && (
|
|
164
|
+
<FundWallet
|
|
165
|
+
onSuccess={getBalance}
|
|
166
|
+
network="base"
|
|
167
|
+
cryptoCurrency="eth"
|
|
168
|
+
destinationAddress={evmAddress}
|
|
169
|
+
/>
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
<hr className="page-divider" />
|
|
174
|
+
<p className="page-heading">Send an EVM transaction on Base Sepolia</p>
|
|
175
|
+
<div className="main-inner flex-col-container">
|
|
176
|
+
<div className="card card--user-balance">
|
|
177
|
+
<UserBalance
|
|
121
178
|
balance={formattedBalanceSepolia}
|
|
122
|
-
|
|
179
|
+
faucetName="Base Sepolia Faucet"
|
|
180
|
+
faucetUrl="https://portal.cdp.coinbase.com/products/faucet"
|
|
181
|
+
/>
|
|
182
|
+
</div>
|
|
183
|
+
<div className="card card--transaction">
|
|
184
|
+
{isSignedIn && (
|
|
185
|
+
<Suspense fallback={<div>Loading transaction component...</div>}>
|
|
186
|
+
<EVMTransactionComponent
|
|
187
|
+
balance={formattedBalanceSepolia}
|
|
188
|
+
onSuccess={getBalanceSepolia}
|
|
189
|
+
/>
|
|
190
|
+
</Suspense>
|
|
191
|
+
)}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</>
|
|
195
|
+
)}
|
|
196
|
+
|
|
197
|
+
{solanaAddress && (
|
|
198
|
+
<>
|
|
199
|
+
{evmAddress && <hr className="page-divider" />}
|
|
200
|
+
<p className="page-heading">Fund your Solana wallet on Mainnet</p>
|
|
201
|
+
<div className="main-inner flex-col-container">
|
|
202
|
+
<div className="card card--user-balance">
|
|
203
|
+
<UserBalance balance={formattedBalanceSolana} />
|
|
204
|
+
</div>
|
|
205
|
+
<div className="card card--transaction">
|
|
206
|
+
{isSignedIn && (
|
|
207
|
+
<FundWallet
|
|
208
|
+
onSuccess={getBalanceSolana}
|
|
209
|
+
network="solana"
|
|
210
|
+
cryptoCurrency="sol"
|
|
211
|
+
destinationAddress={solanaAddress}
|
|
212
|
+
/>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
<hr className="page-divider" />
|
|
217
|
+
<p className="page-heading">Send a Solana transaction on Devnet</p>
|
|
218
|
+
<div className="main-inner flex-col-container">
|
|
219
|
+
<div className="card card--user-balance">
|
|
220
|
+
<UserBalance
|
|
221
|
+
balance={formattedBalanceSolanaDevnet}
|
|
222
|
+
faucetName="Solana Devnet Faucet"
|
|
223
|
+
faucetUrl="https://portal.cdp.coinbase.com/products/faucet?network=solana-devnet"
|
|
123
224
|
/>
|
|
124
|
-
</
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
225
|
+
</div>
|
|
226
|
+
<div className="card card--transaction">
|
|
227
|
+
{isSignedIn && (
|
|
228
|
+
<Suspense fallback={<div>Loading transaction component...</div>}>
|
|
229
|
+
<SolanaTransactionComponent onSuccess={getBalanceSolanaDevnet} />
|
|
230
|
+
</Suspense>
|
|
231
|
+
)}
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</>
|
|
235
|
+
)}
|
|
128
236
|
</main>
|
|
129
237
|
</>
|
|
130
238
|
);
|
|
@@ -2,126 +2,136 @@
|
|
|
2
2
|
|
|
3
3
|
import { Buffer } from "buffer";
|
|
4
4
|
|
|
5
|
-
import { useSolanaAddress
|
|
5
|
+
import { useSolanaAddress } from "@coinbase/cdp-hooks";
|
|
6
|
+
import {
|
|
7
|
+
SendSolanaTransactionButton,
|
|
8
|
+
type SendSolanaTransactionButtonProps,
|
|
9
|
+
} from "@coinbase/cdp-react/components/SendSolanaTransactionButton";
|
|
6
10
|
import { Button } from "@coinbase/cdp-react/components/ui/Button";
|
|
11
|
+
import { LoadingSkeleton } from "@coinbase/cdp-react/components/ui/LoadingSkeleton";
|
|
7
12
|
import {
|
|
8
13
|
PublicKey,
|
|
9
14
|
Transaction,
|
|
10
15
|
SystemProgram,
|
|
11
16
|
SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
|
|
12
17
|
} from "@solana/web3.js";
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
import { IconCheck, IconCopy } from "./Icons";
|
|
18
|
+
import { useMemo, useState } from "react";
|
|
16
19
|
|
|
17
20
|
interface Props {
|
|
21
|
+
balance?: string;
|
|
18
22
|
onSuccess?: () => void;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
|
-
*
|
|
26
|
+
* This component demonstrates how to send a Solana transaction.
|
|
23
27
|
*
|
|
24
|
-
* @param props - The component
|
|
28
|
+
* @param {Props} props - The props for the SolanaTransaction component.
|
|
29
|
+
* @param {string} [props.balance] - The user's balance.
|
|
30
|
+
* @param {() => void} [props.onSuccess] - A function to call when the transaction is successful.
|
|
31
|
+
* @returns A component that displays a transaction form and a transaction signature.
|
|
25
32
|
*/
|
|
26
33
|
export default function SolanaTransaction(props: Props) {
|
|
27
|
-
const { onSuccess } = props;
|
|
34
|
+
const { balance, onSuccess } = props;
|
|
28
35
|
const { solanaAddress } = useSolanaAddress();
|
|
36
|
+
const [transactionSignature, setTransactionSignature] = useState("");
|
|
29
37
|
const [error, setError] = useState("");
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
if (!solanaAddress)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
setError(
|
|
43
|
-
setSignedTransaction(null);
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const transaction = createAndEncodeTransaction(solanaAddress);
|
|
47
|
-
const result = await signSolanaTransaction({
|
|
48
|
-
solanaAccount: solanaAddress,
|
|
49
|
-
transaction,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
setSignedTransaction(result.signedTransaction);
|
|
53
|
-
onSuccess?.();
|
|
54
|
-
} catch (err) {
|
|
55
|
-
const errorMessage = err instanceof Error ? err.message : "Transaction signing failed";
|
|
56
|
-
setError(errorMessage);
|
|
57
|
-
} finally {
|
|
58
|
-
setIsLoading(false);
|
|
59
|
-
}
|
|
38
|
+
|
|
39
|
+
const hasBalance = useMemo(() => {
|
|
40
|
+
return balance && balance !== "0";
|
|
41
|
+
}, [balance]);
|
|
42
|
+
|
|
43
|
+
const transaction = useMemo(() => {
|
|
44
|
+
if (!solanaAddress) return "";
|
|
45
|
+
return createAndEncodeTransaction(solanaAddress);
|
|
46
|
+
}, [solanaAddress]);
|
|
47
|
+
|
|
48
|
+
const handleTransactionError: SendSolanaTransactionButtonProps["onError"] = error => {
|
|
49
|
+
setTransactionSignature("");
|
|
50
|
+
setError(error.message);
|
|
60
51
|
};
|
|
61
52
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
setIsCopied(true);
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error(error);
|
|
69
|
-
}
|
|
53
|
+
const handleTransactionSuccess: SendSolanaTransactionButtonProps["onSuccess"] = signature => {
|
|
54
|
+
setTransactionSignature(signature);
|
|
55
|
+
setError("");
|
|
56
|
+
onSuccess?.();
|
|
70
57
|
};
|
|
71
58
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}, 2000);
|
|
77
|
-
return () => clearTimeout(timeout);
|
|
78
|
-
}, [isCopied]);
|
|
59
|
+
const handleReset = () => {
|
|
60
|
+
setTransactionSignature("");
|
|
61
|
+
setError("");
|
|
62
|
+
};
|
|
79
63
|
|
|
80
64
|
return (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
65
|
+
<>
|
|
66
|
+
{balance === undefined && (
|
|
67
|
+
<>
|
|
68
|
+
<h2 className="card-title">Send a Solana transaction</h2>
|
|
69
|
+
<LoadingSkeleton className="loading--text" />
|
|
70
|
+
<LoadingSkeleton className="loading--btn" />
|
|
71
|
+
</>
|
|
72
|
+
)}
|
|
73
|
+
{balance !== undefined && (
|
|
74
|
+
<>
|
|
75
|
+
{!transactionSignature && error && (
|
|
76
|
+
<>
|
|
77
|
+
<h2 className="card-title">Oops</h2>
|
|
78
|
+
<p>{error}</p>
|
|
79
|
+
<Button className="tx-button" onClick={handleReset} variant="secondary">
|
|
80
|
+
Reset and try again
|
|
81
|
+
</Button>
|
|
82
|
+
</>
|
|
83
|
+
)}
|
|
84
|
+
{!transactionSignature && !error && (
|
|
85
|
+
<>
|
|
86
|
+
<h2 className="card-title">Send a Solana transaction</h2>
|
|
87
|
+
{hasBalance && solanaAddress && (
|
|
88
|
+
<>
|
|
89
|
+
<p>Send 1 Lamport to yourself on Solana Devnet</p>
|
|
90
|
+
<SendSolanaTransactionButton
|
|
91
|
+
account={solanaAddress}
|
|
92
|
+
network="solana-devnet"
|
|
93
|
+
transaction={transaction}
|
|
94
|
+
onError={handleTransactionError}
|
|
95
|
+
onSuccess={handleTransactionSuccess}
|
|
96
|
+
/>
|
|
97
|
+
</>
|
|
98
|
+
)}
|
|
99
|
+
{!hasBalance && (
|
|
100
|
+
<>
|
|
101
|
+
<p>
|
|
102
|
+
This example transaction sends a tiny amount of SOL from your wallet to itself.
|
|
103
|
+
</p>
|
|
104
|
+
<p>
|
|
105
|
+
Get some from{" "}
|
|
106
|
+
<a href="https://faucet.solana.com/" target="_blank" rel="noopener noreferrer">
|
|
107
|
+
Solana Devnet Faucet
|
|
108
|
+
</a>
|
|
109
|
+
</p>
|
|
110
|
+
</>
|
|
111
|
+
)}
|
|
112
|
+
</>
|
|
113
|
+
)}
|
|
114
|
+
{transactionSignature && (
|
|
115
|
+
<>
|
|
116
|
+
<h2 className="card-title">Transaction sent</h2>
|
|
117
|
+
<p>
|
|
118
|
+
Transaction signature:{" "}
|
|
119
|
+
<a
|
|
120
|
+
href={`https://explorer.solana.com/tx/${transactionSignature}?cluster=devnet`}
|
|
121
|
+
target="_blank"
|
|
122
|
+
rel="noopener noreferrer"
|
|
123
|
+
>
|
|
124
|
+
{transactionSignature.slice(0, 4)}...{transactionSignature.slice(-4)}
|
|
125
|
+
</a>
|
|
126
|
+
</p>
|
|
127
|
+
<Button variant="secondary" className="tx-button" onClick={handleReset}>
|
|
128
|
+
Send another transaction
|
|
129
|
+
</Button>
|
|
130
|
+
</>
|
|
131
|
+
)}
|
|
132
|
+
</>
|
|
133
|
+
)}
|
|
134
|
+
</>
|
|
125
135
|
);
|
|
126
136
|
}
|
|
127
137
|
|
|
@@ -24,10 +24,12 @@ export const getBuyOptions: FetchBuyOptions = async params => {
|
|
|
24
24
|
|
|
25
25
|
if (!response.ok) {
|
|
26
26
|
const errorData = await response.json();
|
|
27
|
+
console.error("getBuyOptions: API error:", errorData);
|
|
27
28
|
throw new Error(errorData.error || "Failed to fetch buy options");
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
const result = await response.json();
|
|
32
|
+
return result;
|
|
31
33
|
} catch (error) {
|
|
32
34
|
console.error("Error fetching buy options:", error);
|
|
33
35
|
throw error;
|
|
@@ -52,10 +54,12 @@ export const createBuyQuote: FetchBuyQuote = async request => {
|
|
|
52
54
|
|
|
53
55
|
if (!response.ok) {
|
|
54
56
|
const errorData = await response.json();
|
|
57
|
+
console.error("createBuyQuote: API error:", errorData);
|
|
55
58
|
throw new Error(errorData.error || "Failed to create buy quote");
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
const result = await response.json();
|
|
62
|
+
return result;
|
|
59
63
|
} catch (error) {
|
|
60
64
|
console.error("Error creating buy quote:", error);
|
|
61
65
|
throw error;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useEvmAddress } from "@coinbase/cdp-hooks";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
type
|
|
5
|
-
} from "@coinbase/cdp-react/components/
|
|
3
|
+
SendEvmTransactionButton,
|
|
4
|
+
type SendEvmTransactionButtonProps,
|
|
5
|
+
} from "@coinbase/cdp-react/components/SendEvmTransactionButton";
|
|
6
6
|
import { Button } from "@coinbase/cdp-react/components/ui/Button";
|
|
7
7
|
import { LoadingSkeleton } from "@coinbase/cdp-react/components/ui/LoadingSkeleton";
|
|
8
8
|
import { useMemo, useState } from "react";
|
|
@@ -30,7 +30,7 @@ function EOATransaction(props: Props) {
|
|
|
30
30
|
return balance && balance !== "0";
|
|
31
31
|
}, [balance]);
|
|
32
32
|
|
|
33
|
-
const transaction = useMemo<
|
|
33
|
+
const transaction = useMemo<SendEvmTransactionButtonProps["transaction"]>(() => {
|
|
34
34
|
return {
|
|
35
35
|
to: evmAddress, // Send to yourself for testing
|
|
36
36
|
value: 1000000000000n, // 0.000001 ETH in wei
|
|
@@ -40,12 +40,12 @@ function EOATransaction(props: Props) {
|
|
|
40
40
|
};
|
|
41
41
|
}, [evmAddress]);
|
|
42
42
|
|
|
43
|
-
const handleTransactionError:
|
|
43
|
+
const handleTransactionError: SendEvmTransactionButtonProps["onError"] = error => {
|
|
44
44
|
setTransactionHash("");
|
|
45
45
|
setError(error.message);
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
const handleTransactionSuccess:
|
|
48
|
+
const handleTransactionSuccess: SendEvmTransactionButtonProps["onSuccess"] = hash => {
|
|
49
49
|
setTransactionHash(hash);
|
|
50
50
|
setError("");
|
|
51
51
|
onSuccess?.();
|
|
@@ -82,7 +82,7 @@ function EOATransaction(props: Props) {
|
|
|
82
82
|
{hasBalance && evmAddress && (
|
|
83
83
|
<>
|
|
84
84
|
<p>Send 0.000001 ETH to yourself on Base Sepolia</p>
|
|
85
|
-
<
|
|
85
|
+
<SendEvmTransactionButton
|
|
86
86
|
account={evmAddress}
|
|
87
87
|
network="base-sepolia"
|
|
88
88
|
transaction={transaction}
|
|
@@ -47,7 +47,6 @@ function SignedInScreen() {
|
|
|
47
47
|
const { solanaAddress } = useSolanaAddress();
|
|
48
48
|
const [balance, setBalance] = useState<bigint | undefined>(undefined);
|
|
49
49
|
|
|
50
|
-
const isSolana = !!CDP_CONFIG.solana;
|
|
51
50
|
const address = isSolana ? solanaAddress : evmAddress;
|
|
52
51
|
|
|
53
52
|
const formattedBalance = useMemo(() => {
|
|
@@ -59,7 +58,7 @@ function SignedInScreen() {
|
|
|
59
58
|
// Convert wei to ETH
|
|
60
59
|
return formatEther(balance);
|
|
61
60
|
}
|
|
62
|
-
}, [balance
|
|
61
|
+
}, [balance]);
|
|
63
62
|
|
|
64
63
|
const getBalance = useCallback(async () => {
|
|
65
64
|
if (isSolana && solanaAddress) {
|
|
@@ -73,7 +72,7 @@ function SignedInScreen() {
|
|
|
73
72
|
});
|
|
74
73
|
setBalance(weiBalance);
|
|
75
74
|
}
|
|
76
|
-
}, [evmAddress, solanaAddress
|
|
75
|
+
}, [evmAddress, solanaAddress]);
|
|
77
76
|
|
|
78
77
|
useEffect(() => {
|
|
79
78
|
getBalance();
|