-
Notifications
You must be signed in to change notification settings - Fork 37
feat: improved notifications + session key renewal + fix vrf example #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
dbc7363
66955a1
11bdc2c
882e6c5
3317fe9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -56,7 +56,7 @@ const App: React.FC = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // gesture, and the resulting token is baked into the URL below. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [explorerToken, setExplorerToken] = useState<string | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isSubmitting, setIsSubmitting] = useState<boolean>(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [transactionError, setTransactionError] = useState<string | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [transactionError, setTransactionError] = useState<{ message: string; explorerUrl?: string } | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [transactionSuccess, setTransactionSuccess] = useState<{ message: string; explorerUrl?: string } | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [isLoading, setIsLoading] = useState<boolean>(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const counterProgramClient = useRef<Program | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -317,22 +317,25 @@ const App: React.FC = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): Promise<string | null> => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!tempKeypair) return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (ephemeral && !ephemeralConnection.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError("TEE ER connection not ready — auth token missing"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError({ message: "TEE ER connection not ready — auth token missing" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsSubmitting(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionSuccess(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const targetConnection = ephemeral ? ephemeralConnection.current! : provider.current.connection; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const layerLabel = ephemeral ? "ER" : "Base"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Hoist signature so the catch handler can include the explorer link for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // failed-on-chain txs (much easier to debug with a clickable link). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let signature: string | null = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { value: { blockhash, lastValidBlockHeight } } = await targetConnection.getLatestBlockhashAndContext(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!transaction.recentBlockhash) transaction.recentBlockhash = blockhash; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!transaction.feePayer) transaction.feePayer = tempKeypair.publicKey; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transaction.sign(tempKeypair); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const sendStart = performance.now(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const signature = await targetConnection.sendRawTransaction(transaction.serialize(), { skipPreflight: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| signature = await targetConnection.sendRawTransaction(transaction.serialize(), { skipPreflight: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const sendMs = Math.round(performance.now() - sendStart); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const confirmStart = performance.now(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -388,7 +391,23 @@ const App: React.FC = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return signature; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError(`Transaction failed: ${error}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If the tx made it on-chain (signature was returned by sendRawTransaction), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // attach an explorer link so the user can inspect the failure logs. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let explorerUrl: string | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (signature) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (ephemeral) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const token = await ensureAuthToken(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (token) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| explorerUrl = `https://explorer.solana.com/tx/${signature}?cluster=custom&customUrl=${encodeURIComponent(PRIVATE_ER_ENDPOINT + '?token=' + token)}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| explorerUrl = `https://explorer.solana.com/tx/${signature}?cluster=devnet`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: `Transaction failed: ${error}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| explorerUrl, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+394
to
+410
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrap If 🛡️ Proposed fix to handle potential ensureAuthToken failure let explorerUrl: string | undefined;
if (signature) {
if (ephemeral) {
- const token = await ensureAuthToken();
- if (token) {
- explorerUrl = `https://explorer.solana.com/tx/${signature}?cluster=custom&customUrl=${encodeURIComponent(PRIVATE_ER_ENDPOINT + '?token=' + token)}`;
+ try {
+ const token = await ensureAuthToken();
+ if (token) {
+ explorerUrl = `https://explorer.solana.com/tx/${signature}?cluster=custom&customUrl=${encodeURIComponent(PRIVATE_ER_ENDPOINT + '?token=' + token)}`;
+ }
+ } catch (tokenErr) {
+ console.error("Failed to get auth token for explorer URL:", tokenErr);
}
} else {
explorerUrl = `https://explorer.solana.com/tx/${signature}?cluster=devnet`;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setIsSubmitting(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -485,7 +504,7 @@ const App: React.FC = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const togglePrivacyTx = useCallback(async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!tempKeypair || !counterPda || !permissionPda || !counterProgramClient.current) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!ephemeralConnection.current) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError("ER connection not ready"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError({ message: "ER connection not ready" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const next = !isPrivate; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -545,7 +564,7 @@ const App: React.FC = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!token) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const fresh = await ensureWalletAuthToken(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!fresh) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError("Wallet declined the auth token"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTransactionError({ message: "Wallet declined the auth token" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token = fresh; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -660,7 +679,12 @@ const App: React.FC = () => { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {transactionError && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Alert type="error" message={transactionError} onClose={() => setTransactionError(null)}/>} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Alert | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type="error" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message={transactionError.message} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| href={transactionError.explorerUrl} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onClose={() => setTransactionError(null)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| />} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {transactionSuccess && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <Alert | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial | 💤 Low value
Consider adding aria-label for screen reader users.
The external link opens in a new tab but doesn't announce this to screen readers. Adding an
aria-labelwould improve accessibility.♿ Optional accessibility enhancement
if (href) { return ( - <a href={href} target="_blank" rel="noopener noreferrer" style={containerStyle}> + <a + href={href} + target="_blank" + rel="noopener noreferrer" + aria-label={`${message} (opens in new tab)`} + style={containerStyle} + > {message} <span style={{ marginLeft: 4 }}>↗</span> </a> ); }📝 Committable suggestion
🤖 Prompt for AI Agents