poc(contracts): permissionles contract sign#12
poc(contracts): permissionles contract sign#12AdriaCarrera merged 2 commits intochore/dapp-examplefrom
Conversation
📝 WalkthroughWalkthroughThis update introduces a full-stack expansion for FastAuth with major changes across contracts, scripts, documentation, and a new example wallet application. Key changes include a new MPC mock contract, expanded JWT guard logic with detailed NEAR transaction types, refactored contract interfaces for MPC signing, and a React + Vite wallet example with Auth0 integration and transaction workflows. Supporting scripts, configuration, and documentation are also updated. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Wallet (React)
participant Auth0
participant FastAuthRelayer (JS)
participant FastAuth Contract
participant Guard Contract
participant MPC Contract
User->>Wallet (React): Clicks "Sign"
Wallet (React)->>Auth0: Requests JWT (loginWithRedirect)
Auth0-->>Wallet (React): Returns JWT
Wallet (React)->>FastAuthRelayer: sign(jwt)
FastAuthRelayer->>FastAuth Contract: sign(guard_id, jwt) [payable]
FastAuth Contract->>Guard Contract: verify(jwt)
Guard Contract-->>FastAuth Contract: (bool, sub, payload_bytes)
FastAuth Contract->>MPC Contract: sign(SignRequest(payload_hash, path, key_version))
MPC Contract-->>FastAuth Contract: SignResponse(signature)
FastAuth Contract-->>FastAuthRelayer: SignResponse(signature)
FastAuthRelayer-->>Wallet (React): SignResponse(signature)
Wallet (React)->>FastAuthRelayer: send(signature, tx)
FastAuthRelayer->>NEAR: Sends signed transaction
Possibly related PRs
Suggested labels
Note ⚡️ AI Code Reviews for VS Code, Cursor, WindsurfCodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback. Note ⚡️ Faster reviews with cachingCodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 30th. To opt out, configure ✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub. |
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
🛑 Comments failed to post (24)
.gitignore (1)
41-41:
⚠️ Potential issueFix the ignore pattern for FastAuth JSON files.
The entryfast-auth-.jsononly matches a literalfast-auth-.json. To ignore any JSON file starting withfast-auth-, update it to:-fast-auth-.json +fast-auth-*.json📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.-fast-auth-.json +fast-auth-*.json🤖 Prompt for AI Agents
In the .gitignore file at line 41, the pattern 'fast-auth-.json' only matches the exact filename 'fast-auth-.json'. To ignore all JSON files starting with 'fast-auth-', change the pattern to 'fast-auth-*.json' so it correctly matches any file with that prefix and a .json extension.examples/wallet/src/polyfills/buffer/index.ts (1)
1-3: 🛠️ Refactor suggestion
Fix TypeScript error on global
Bufferassignment.
Directly assigning toglobalThis.Buffercan produce a TS compile error (Property 'Buffer' does not exist on type 'GlobalThis'). CastglobalThistoanyor augment the global scope.Apply this diff:
import { Buffer } from "buffer"; -globalThis.Buffer = Buffer; +// Polyfill global Buffer for browser environments +;(globalThis as any).Buffer = Buffer;This ensures compatibility with TypeScript without additional declaration files.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.import { Buffer } from "buffer"; // Polyfill global Buffer for browser environments ;(globalThis as any).Buffer = Buffer;🤖 Prompt for AI Agents
In examples/wallet/src/polyfills/buffer/index.ts lines 1 to 3, assigning Buffer directly to globalThis.Buffer causes a TypeScript error because Buffer is not declared on the GlobalThis type. Fix this by casting globalThis to any before assignment, like (globalThis as any).Buffer = Buffer, to bypass the type error without needing extra declaration files.README.md (1)
43-43: 💡 Verification agent
🧩 Analysis chain
Verify that the
add_guardmethod is now permissionless.This aligns with the PR objective of enabling permissionless contract signing. Make sure the contract implementation actually removes the permissions from the
add_guardmethod.
🏁 Script executed:
#!/bin/bash # Search for the add_guard method implementation in the contract code fd -t f -e rs -e json "fa.json\$|fa/src" | xargs rg -A 10 "add_guard|AddGuard"Length of output: 6514
Remove ownership check from
add_guardand update READMEThe
add_guardmethod incontracts/fa/src/lib.rsstill enforces an owner-only guard via the call toself.only_owner(), so it isn’t permissionless yet. To complete this PR:
- In
contracts/fa/src/lib.rs, remove the line:self.only_owner();- In
README.md(around line 43), mark the checklist item as done:- [ ] `add_guard` method permissionless + [x] `add_guard` method permissionlessThis aligns the documentation with the updated, permissionless implementation.
🤖 Prompt for AI Agents
In README.md at line 43, update the checklist item for the `add_guard` method to mark it as done by changing "[ ]" to "[x]". Additionally, in contracts/fa/src/lib.rs, locate the `add_guard` method and remove the line that calls `self.only_owner();` to eliminate the ownership check and make the method permissionless as intended.examples/wallet/src/pages/Sign.tsx (1)
55-56:
⚠️ Potential issueAdd validation and sanitization for displayed parameters
The component displays parameters directly from URL query strings without validation or sanitization. This could lead to XSS vulnerabilities if malicious input is provided.
- Permissions: <span className="param-value">{permissions}</span> + Permissions: <span className="param-value">{sanitizeHtml(permissions)}</span>Consider adding a sanitization function or library like DOMPurify to sanitize the values before rendering them, and implement validation to ensure the parameters meet expected formats.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In examples/wallet/src/pages/Sign.tsx around lines 55 to 56, the permissions parameter is rendered directly from URL query strings without validation or sanitization, posing an XSS risk. To fix this, implement validation to check that permissions conform to expected formats and sanitize the value before rendering using a library like DOMPurify or a custom sanitization function to prevent injection of malicious code.examples/wallet/src/components/CreateAccount.tsx (4)
14-16: 🛠️ Refactor suggestion
Improve form validation with user-friendly messages
Replace error throwing with proper form validation and user feedback.
- if (!accountId) { - throw new Error("Account ID is required"); - } + if (!accountId) { + setError("Account ID is required"); + return; + } + + // Add validation for NEAR account ID format + const nearAccountIdRegex = /^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$/; + if (!nearAccountIdRegex.test(accountId)) { + setError("Invalid NEAR account ID format"); + return; + }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In examples/wallet/src/components/CreateAccount.tsx around lines 14 to 16, the code throws an error when accountId is missing, which is not user-friendly. Replace the error throwing with proper form validation logic that sets a validation state or message, and display this message in the UI to provide clear, user-friendly feedback instead of crashing the app.
17-18:
⚠️ Potential issueAdd proper error handling and loading state
The current implementation lacks error handling for the account creation process and doesn't provide user feedback on progress or results.
- console.log("Creating account", accountId, publicKey); - relayer.createAccount(accountId, publicKey); + setLoading(true); + setError(null); + try { + await relayer.createAccount(accountId, publicKey); + setSuccess("Account created successfully!"); + } catch (error) { + console.error("Error creating account:", error); + setError(`Failed to create account: ${error instanceof Error ? error.message : String(error)}`); + } finally { + setLoading(false); + }You'll need to add state variables for loading, error, and success states, and update the component to display appropriate feedback.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In examples/wallet/src/components/CreateAccount.tsx around lines 17 to 18, the account creation process lacks error handling and user feedback for loading and success states. Introduce state variables to track loading, error, and success statuses. Update the UI to reflect these states by showing a loading indicator during the process, displaying error messages if creation fails, and confirming success when the account is created. Wrap the createAccount call in a try-catch block to handle errors properly and update the states accordingly.
8-10: 🛠️ Refactor suggestion
Improve error handling with user-friendly messages
Instead of throwing errors, consider displaying user-friendly error messages to enhance the user experience.
- if (!relayer) { - throw new Error("Relayer not initialized"); - } + if (!relayer) { + // Set an error state and render an error message + setError("Service temporarily unavailable. Please try again later."); + return; + }You'll need to add a state variable for error messages and display it in the UI.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In examples/wallet/src/components/CreateAccount.tsx around lines 8 to 10, replace the thrown error "Relayer not initialized" with a state variable to hold error messages. Initialize a state variable for the error message, set it when the relayer is not initialized, and update the component's JSX to conditionally render this error message in a user-friendly way instead of throwing an error.
20-26: 🛠️ Refactor suggestion
Enhance the UI with feedback states
Update the UI to show loading states, error messages, and success confirmations to improve user experience.
return ( <div> + {error && <div className="error-message">{error}</div>} + {success && <div className="success-message">{success}</div>} <form onSubmit={handleSubmit}> <input type="text" placeholder="Enter account ID" name="accountid" /> - <button type="submit">Create Account</button> + <button type="submit" disabled={loading}> + {loading ? "Creating..." : "Create Account"} + </button> </form> </div> );🤖 Prompt for AI Agents
In examples/wallet/src/components/CreateAccount.tsx around lines 20 to 26, the form UI lacks feedback states. Enhance the component by adding state variables to track loading, error, and success statuses. Update the JSX to conditionally render messages or indicators based on these states, such as showing a loading spinner during submission, displaying error messages if submission fails, and confirming success when the account is created.examples/wallet/src/index.css (1)
57-63:
⚠️ Potential issueColor contrast issues in dark mode
The
spanandlielements are set to black (#000) color, which will have poor contrast against the dark background (#242424) when in dark mode. This creates accessibility issues for users.Consider modifying the color to adapt based on the color scheme:
span { - color: #000; + color: var(--text-color, #000); } li { - color: #000; + color: var(--text-color, #000); } @media (prefers-color-scheme: light) { :root { color: #213547; background-color: #ffffff; + --text-color: #000; } /* ... */ } @media (prefers-color-scheme: dark) { + :root { + --text-color: rgba(255, 255, 255, 0.87); + } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.span { color: var(--text-color, #000); } li { color: var(--text-color, #000); } @media (prefers-color-scheme: light) { :root { color: #213547; background-color: #ffffff; --text-color: #000; } /* ... */ } @media (prefers-color-scheme: dark) { :root { --text-color: rgba(255, 255, 255, 0.87); } }🤖 Prompt for AI Agents
In examples/wallet/src/index.css around lines 57 to 63, the span and li elements are styled with black color (#000), which causes poor contrast against the dark background (#242424) in dark mode. To fix this, update the CSS to use media queries or CSS variables that adjust the text color based on the user's color scheme preference, ensuring sufficient contrast in both light and dark modes for accessibility.examples/wallet/src/components/SignButton.tsx (2)
16-27: 🛠️ Refactor suggestion
Add error handling for sign operation.
The
handleSignmethod lacks proper error handling. If the relayer operation fails or if JWT decoding fails, the error is not caught or displayed to the user.const handleSign = async () => { + try { if (!relayer) { throw new Error("Relayer not initialized"); } const jwt = await getAccessTokenSilently(); const claims = jose.decodeJwt(jwt); const tx = Transaction.decode(claims["fatxn"] as Uint8Array<ArrayBufferLike>); setTx(tx); const result = await relayer?.sign(jwt); setSignature(JSON.parse(Buffer.from(result.status.SuccessValue, "base64").toString())); + } catch (error) { + console.error("Error signing transaction:", error); + // Consider adding state to display errors to the user + } };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const handleSign = async () => { try { if (!relayer) { throw new Error("Relayer not initialized"); } const jwt = await getAccessTokenSilently(); const claims = jose.decodeJwt(jwt); const tx = Transaction.decode(claims["fatxn"] as Uint8Array<ArrayBufferLike>); setTx(tx); const result = await relayer?.sign(jwt); setSignature(JSON.parse(Buffer.from(result.status.SuccessValue, "base64").toString())); } catch (error) { console.error("Error signing transaction:", error); // Consider adding state to display errors to the user } };🤖 Prompt for AI Agents
In examples/wallet/src/components/SignButton.tsx around lines 16 to 27, the handleSign function lacks error handling for potential failures in relayer operations and JWT decoding. Wrap the entire function body in a try-catch block to catch any errors thrown during these operations. In the catch block, handle the error appropriately by logging it or displaying a user-friendly error message to inform the user of the failure.
29-56: 🛠️ Refactor suggestion
Improve error handling and validation in the send method.
The
handleSendmethod has several potential points of failure without proper error handling or validation:
- No validation for signature format
- No error handling for cryptographic operations
- No handling for failed transaction sends
const handleSend = async () => { + try { if (!signature) throw new Error("no signature"); const secp256k1 = new EC("secp256k1"); // Inputs from MPC signer // @ts-expect-error const compressedR = signature["big_r"]["affine_point"] as string; // @ts-expect-error const sHex = signature["s"]["scalar"] as string; // @ts-expect-error const recoveryId = signature["recovery_id"] as number; + + // Validate required signature components + if (!compressedR || !sHex || recoveryId === undefined) { + throw new Error("Invalid signature format"); + } // 1. Decompress big_r to get x (used as r scalar) const point = secp256k1.keyFromPublic(compressedR, "hex").getPublic(); const r = point.getX().toArrayLike(Buffer, "be", 32); // 2. s is already the scalar const s = Buffer.from(sHex, "hex"); // 3. Normalize recovery ID const v = recoveryId & 0x01; // should be 0 or 1 // 4. Final 65-byte signature const sig = Buffer.concat([r, s, Buffer.from([v])]); await relayer?.send(sig, tx as Transaction); + // Consider adding success feedback to the user + } catch (error) { + console.error("Error sending transaction:", error); + // Consider adding state to display errors to the user + } };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const handleSend = async () => { try { if (!signature) throw new Error("no signature"); const secp256k1 = new EC("secp256k1"); // Inputs from MPC signer // @ts-expect-error const compressedR = signature["big_r"]["affine_point"] as string; // @ts-expect-error const sHex = signature["s"]["scalar"] as string; // @ts-expect-error const recoveryId = signature["recovery_id"] as number; // Validate required signature components if (!compressedR || !sHex || recoveryId === undefined) { throw new Error("Invalid signature format"); } // 1. Decompress big_r to get x (used as r scalar) const point = secp256k1.keyFromPublic(compressedR, "hex").getPublic(); const r = point.getX().toArrayLike(Buffer, "be", 32); // 2. s is already the scalar const s = Buffer.from(sHex, "hex"); // 3. Normalize recovery ID const v = recoveryId & 0x01; // should be 0 or 1 // 4. Final 65-byte signature const sig = Buffer.concat([r, s, Buffer.from([v])]); await relayer?.send(sig, tx as Transaction); // Consider adding success feedback to the user } catch (error) { console.error("Error sending transaction:", error); // Consider adding state to display errors to the user } };🤖 Prompt for AI Agents
In examples/wallet/src/components/SignButton.tsx around lines 29 to 56, the handleSend function lacks validation for the signature format, error handling for cryptographic operations, and handling for failed transaction sends. To fix this, add checks to validate the structure and types of the signature properties before use, wrap cryptographic operations in try-catch blocks to catch and handle errors gracefully, and handle possible failures from the relayer.send call by using try-catch or checking the result to provide appropriate error feedback or retries.examples/wallet/src/pages/Home.tsx (1)
19-19:
⚠️ Potential issueRemove JWT logging to console in production.
Logging JWT tokens to the console is a security risk in production environments as they contain sensitive information. Consider removing this or using a debug flag.
-<button onClick={() => getAccessTokenSilently().then(console.log)}>Get JWT</button> +<button onClick={() => getAccessTokenSilently().then(() => console.log("JWT token retrieved"))}>Get JWT</button>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.<button onClick={() => getAccessTokenSilently().then(() => console.log("JWT token retrieved"))}> Get JWT </button>🤖 Prompt for AI Agents
In examples/wallet/src/pages/Home.tsx at line 19, the code logs the JWT token to the console, which is a security risk in production. Modify the button's onClick handler to conditionally log the token only in development mode by checking an environment variable or debug flag, or remove the console.log entirely to avoid exposing sensitive information.examples/wallet/src/components/DerivePublicKeyButton.tsx (2)
14-20: 🛠️ Refactor suggestion
Add proper error handling for key derivation.
The
handleClickmethod throws errors but doesn't catch them, which could lead to unhandled exceptions and poor user experience.+const [error, setError] = useState<string | null>(null); +const [isLoading, setIsLoading] = useState(false); const handleClick = async () => { + setError(null); + setIsLoading(true); + try { if (!relayer) { throw new Error("Relayer not initialized"); } const publicKey = await relayer?.derivePublicKey(sub); setPublicKey(publicKey); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to derive public key"); + console.error("Error deriving public key:", err); + } finally { + setIsLoading(false); + } };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.// add loading and error state const [error, setError] = useState<string | null>(null); const [isLoading, setIsLoading] = useState(false); const handleClick = async () => { setError(null); setIsLoading(true); try { if (!relayer) { throw new Error("Relayer not initialized"); } const publicKey = await relayer?.derivePublicKey(sub); setPublicKey(publicKey); } catch (err) { setError(err instanceof Error ? err.message : "Failed to derive public key"); console.error("Error deriving public key:", err); } finally { setIsLoading(false); } };🤖 Prompt for AI Agents
In examples/wallet/src/components/DerivePublicKeyButton.tsx around lines 14 to 20, the handleClick function throws errors without catching them, risking unhandled exceptions. Wrap the asynchronous code inside handleClick with a try-catch block to catch any errors during key derivation. In the catch block, handle the error gracefully, such as by logging it or showing a user-friendly message, to improve user experience and prevent crashes.
22-52: 🛠️ Refactor suggestion
Add form validation and improve error handling.
The
handleTransfermethod lacks input validation for form fields and proper error handling for the asynchronous operations.+const [transferError, setTransferError] = useState<string | null>(null); +const [isTransferring, setIsTransferring] = useState(false); const handleTransfer = async (e: React.FormEvent<HTMLFormElement>) => { e.preventDefault(); + setTransferError(null); + setIsTransferring(true); + try { if (!relayer) { throw new Error("Relayer not initialized"); } if (!publicKey) { throw new Error("Public key not derived"); } const formData = new FormData(e.target as HTMLFormElement); const accountId = formData.get("accountid") as string; const receiverId = formData.get("receiverid") as string; const amount = formData.get("amount") as string; + + // Validate form inputs + if (!accountId || !receiverId || !amount) { + throw new Error("All fields are required"); + } + + // Validate amount is a valid number + const amountNum = parseFloat(amount); + if (isNaN(amountNum) || amountNum <= 0) { + throw new Error("Amount must be a positive number"); + } const tx = await relayer?.createTransfer(accountId, PublicKey.fromString(publicKey), receiverId, amount); - console.log("tx", tx); - console.log("encodedTransaction", encodeTransaction(tx).toString()); + const messageBytes: number[] = []; encodeTransaction(tx).forEach((b) => { messageBytes.push(b); }); await loginWithRedirect({ authorizationParams: { imageUrl: "https://media.licdn.com/dms/image/v2/D4D0BAQH5KL-Ge_0iug/company-logo_200_200/company-logo_200_200/0/1696280807541/peersyst_technology_logo?e=2147483647&v=beta&t=uFYvQ5g6HDoIprYhNNV_zC7tzlBkvmPRkWzuLuDpHtc", name: "Peersyst Technology", transaction: messageBytes, receiverId: tx.receiverId, actions: tx.actions, }, }); + } catch (err) { + setTransferError(err instanceof Error ? err.message : "Failed to create transfer"); + console.error("Error creating transfer:", err); + } finally { + setIsTransferring(false); + } };🤖 Prompt for AI Agents
In examples/wallet/src/components/DerivePublicKeyButton.tsx around lines 22 to 52, the handleTransfer function lacks validation for form inputs and does not handle errors from async calls. Add checks to ensure accountId, receiverId, and amount are valid and non-empty before proceeding. Wrap the asynchronous operations in try-catch blocks to catch and handle errors gracefully, providing user feedback or logging errors instead of throwing uncaught exceptions.contracts/fa/src/external_contracts.rs (2)
7-10:
⚠️ Potential issueMismatch between declared guard interface and actual callback signature
verifyis declared to return(bool, String, String), yetFastAuth::on_verify_callback(seecontracts/fa/src/lib.rsl.219-231) expects(bool, String, Vec<u8>).
If the guard implementation was already updated to return aVec<u8>(as hinted by the callback), this interface is now stale and cross-contract calls will fail to deserialize, panicking at runtime.- fn verify(&self, jwt: String) -> (bool, String, String); + fn verify(&self, jwt: String) -> (bool, String, Vec<u8>);Please align both sides of the interface before deploying.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.#[ext_contract(external_guard)] pub trait ExternalGuard { fn verify(&self, jwt: String) -> (bool, String, Vec<u8>); }🤖 Prompt for AI Agents
In contracts/fa/src/external_contracts.rs around lines 7 to 10, the ExternalGuard trait's verify method returns (bool, String, String), but the on_verify_callback in contracts/fa/src/lib.rs expects (bool, String, Vec<u8>). Update the verify method's return type to (bool, String, Vec<u8>) to match the callback signature and ensure proper deserialization during cross-contract calls.
41-44: 🛠️ Refactor suggestion
Consider making
signa mutating callMost MPC implementations persist some metadata (nonces, transcripts, etc.).
Declaring the trait method with&selfmarks it as a view for NEAR.
If the MPC contract needs to touch state, the method must be&mut self(i.e. a change-method) or the call will be rejected.- fn sign(&self, request: SignRequest) -> PromiseOrValue<SignResponse>; + fn sign(&mut self, request: SignRequest) -> PromiseOrValue<SignResponse>;Verify the actual MPC contract implementation and adjust as necessary.
🤖 Prompt for AI Agents
In contracts/fa/src/external_contracts.rs around lines 41 to 44, the sign method in the MPCContract trait is declared with &self, marking it as a view method. Since MPC implementations often modify state, change the method receiver from &self to &mut self to make it a mutating call. Verify the MPC contract implementation to confirm state changes and update the trait method signature accordingly to avoid call rejections.contracts/fa/src/lib.rs (1)
262-288:
⚠️ Potential issueAttached deposit is silently kept on failed verification
If verification fails, the callback returns an empty
Promise::new(env::current_account_id()), leaving the original deposit locked in the FastAuth contract.
Attackers could intentionally send invalid JWTs to drain users’ funds.Consider immediately refunding the caller:
- env::log_str("Guard verification failed"); - return Promise::new(env::current_account_id()); + env::log_str("Guard verification failed – refunding deposit"); + Promise::new(env::predecessor_account_id()).transfer(attached_deposit)Do the same for the
!verification_resultbranch.Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In contracts/fa/src/lib.rs around lines 262 to 288, when verification fails or is rejected, the attached deposit is not refunded and remains locked in the contract. To fix this, modify both error branches to create a Promise that transfers the attached_deposit back to the caller's account instead of returning an empty Promise. Use env::predecessor_account_id() as the recipient and attach the full attached_deposit amount for refunding.examples/wallet/src/services/fast-auth-relayer.ts (2)
10-14: 🛠️ Refactor suggestion
Hard-coded private key and account ID – security risk
Committing real credentials to the repo exposes the relayer account to anyone.
Inject them through environment variables or a secure secrets manager instead.-const RELAYER_PRIVATE_KEY = "..."; -const RELAYER_ACCOUNT_ID = "bosisthenear.testnet"; +const RELAYER_PRIVATE_KEY = process.env.RELAYER_PRIVATE_KEY!; +const RELAYER_ACCOUNT_ID = process.env.RELAYER_ACCOUNT_ID!;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.// TODO: Replace with your actual private key and account ID -const RELAYER_PRIVATE_KEY = "ed25519:5txww6eaySfKnDTXDRK7H425qpiTyk4biE6rPeC6qwdzYDv5Xw5S258yWXdafgdfwdEBcW3SvfKJ9L5BNVMnitmJ"; // Replace with your private key -const RELAYER_ACCOUNT_ID = "bosisthenear.testnet"; // Replace with your account ID +const RELAYER_PRIVATE_KEY = process.env.RELAYER_PRIVATE_KEY!; +const RELAYER_ACCOUNT_ID = process.env.RELAYER_ACCOUNT_ID!; const FAST_AUTH_CONTRACT_ID = "fa-poc.testnet";🤖 Prompt for AI Agents
In examples/wallet/src/services/fast-auth-relayer.ts around lines 10 to 14, the private key and account ID are hard-coded, posing a security risk. Remove the hard-coded values and instead read RELAYER_PRIVATE_KEY and RELAYER_ACCOUNT_ID from environment variables using process.env. Add appropriate checks to ensure these environment variables are set and provide clear error messages if they are missing.
105-119:
⚠️ Potential issueIncorrect receiver ID in
createAccounttransaction
createTransactionexpects(signer_id, public_key, receiver_id, ...).
Passing"testnet"asreceiver_idwill attempt to create an account named testnet, which obviously exists and will be rejected.- "testnet", + accountId,Additionally, a
CreateAccount+Transferaction pair is required; invoking afunctionCall("create_account", …)won't work.
Consider usingcreateAccount+transferactions fromnear-api-js/lib/transaction.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const tx = createTransaction( this.accountId, signerPublicKey, accountId, nonce, [ functionCall( "create_account", { new_public_key: publicKey, new_account_id: accountId }, 300000000000000n, BigInt(parseNearAmount("0")!), ), ], base_decode(accessKey.block_hash), );🤖 Prompt for AI Agents
In examples/wallet/src/services/fast-auth-relayer.ts around lines 105 to 119, the createTransaction call incorrectly uses "testnet" as the receiver_id, which should be the new account ID instead. Replace the functionCall action with a proper CreateAccount action followed by a Transfer action to fund the new account, using the appropriate action creators from near-api-js/lib/transaction. Ensure the receiver_id parameter is set to the new account ID to correctly target the account creation.contracts/fa-guard-jwt-rs256/src/jwt.rs (5)
16-34: 🛠️ Refactor suggestion
Field names use CamelCase instead of Rust snake_case; consider
renamefor JSON compatibilityAll structs (
Transaction,Actionpayloads, etc.) usesignerId,publicKey, … which is atypical for Rust and will surprise contributors & tooling.
If the JSON must stay in CamelCase to align with NEAR RPC, prefer idiomatic field names with explicit#[serde(rename = "...")]:pub struct Transaction { #[serde(rename = "signerId")] signer_id: AccountId, // … }Maintains readability and external compatibility.
🤖 Prompt for AI Agents
In contracts/fa-guard-jwt-rs256/src/jwt.rs between lines 16 and 34, the struct Transaction uses CamelCase field names which is against Rust's snake_case convention. To fix this, rename all fields to snake_case (e.g., signer_id, public_key) and add #[serde(rename = "...")] attributes with the original CamelCase names to each field to maintain JSON compatibility with NEAR RPC. This keeps the code idiomatic while preserving external interface expectations.
70-90: 🛠️ Refactor suggestion
AccessKeyrepresentation is incomplete and diverges from NEAR specThe real NEAR
AccessKeyincludes anonceand apermissionenum (FullAccess / FunctionCall).
This stripped-down version (onlyaccountId&publicKey) will prevent round-tripping full transactions and may break signature verification.Consider:
- Re-using
near_primitives::account::AccessKeyviaborsh/serdefeature flags, or- Replicating the complete structure (including
nonce&AccessKeyPermission).Failing to do so may cause silent data loss when tokens are deserialised.
🤖 Prompt for AI Agents
In contracts/fa-guard-jwt-rs256/src/jwt.rs between lines 70 and 90, the AccessKey struct is incomplete as it lacks the nonce and permission fields defined in the NEAR specification. To fix this, either import and reuse the full AccessKey struct from near_primitives::account with the appropriate borsh/serde feature flags enabled, or extend the current AccessKey struct to include the nonce field and a permission enum matching NEAR's AccessKeyPermission (FullAccess and FunctionCall). This ensures full compatibility and prevents data loss during serialization and deserialization.
2-3:
⚠️ Potential issue
Noncere-export is missing innear_sdk– will not compile
near_sdkcurrently re-exportsAccountId,CryptoHash,PublicKey, andGas, but does not re-export theNoncealias that lives innear_primitives::types.
use near_sdk::{ … Nonce };therefore fails to compile.-use near_sdk::{AccountId, CryptoHash, PublicKey, Gas, Nonce}; +use near_sdk::{AccountId, CryptoHash, PublicKey, Gas}; +use near_primitives::types::Nonce;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.use near_sdk::{AccountId, CryptoHash, PublicKey, Gas}; use near_primitives::types::Nonce; use near_sdk::borsh::{BorshDeserialize, BorshSerialize};🤖 Prompt for AI Agents
In contracts/fa-guard-jwt-rs256/src/jwt.rs at lines 2 to 3, the import statement tries to use Nonce from near_sdk, but Nonce is not re-exported there and causes a compilation error. To fix this, remove Nonce from the near_sdk import and instead import Nonce directly from near_primitives::types. Adjust the use statements accordingly to ensure Nonce is correctly imported from its original crate.
162-169:
⚠️ Potential issue
gasshould also use thedec_formatwrapper to avoid JS precision loss
Gasis an alias foru64; serialising it as a JSON number risks truncation in JS environments.
Apply the same decimal-string wrapper already used fordeposit/stake.- pub gas: Gas, + #[serde(with = "dec_format")] + pub gas: Gas,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.pub struct FunctionCallAction { pub methodName: String, #[serde_as(as = "Base64")] pub args: Vec<u8>, #[serde(with = "dec_format")] pub gas: Gas, #[serde(with = "dec_format")] pub deposit: u128, }🤖 Prompt for AI Agents
In contracts/fa-guard-jwt-rs256/src/jwt.rs around lines 162 to 169, the Gas field in FunctionCallAction is currently serialized as a JSON number, which can cause precision loss in JavaScript. To fix this, apply the dec_format serde wrapper to the gas field, similar to how it is applied to the deposit field, ensuring gas is serialized as a decimal string to avoid JS precision issues.
8-14:
⚠️ Potential issue
fatxnshould be Base64-encoded & struct should deriveDebug/Clone
Vec<u8>is currently serialised as a JSON array of numbers, producing JWTs that are both huge and non-portable.
Usingserde_with::Base64(already in the dependency list) keeps the token compact and matches the encoding you applied to other binary fields.-#[derive(Serialize, Deserialize)] -pub struct FaJwtCustomClaims { - /// The FastAuth permissions claim that specifies what actions are allowed - pub fatxn: Vec<u8>, - /// The subject identifier claim that uniquely identifies the user - pub sub: String, -} +#[serde_as] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct FaJwtCustomClaims { + /// Binary transaction payload – Base64-encoded inside the JWT + #[serde_as(as = "Base64")] + pub fatxn: Vec<u8>, + /// Subject identifier + pub sub: String, +}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.#[serde_as] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct FaJwtCustomClaims { /// Binary transaction payload – Base64-encoded inside the JWT #[serde_as(as = "Base64")] pub fatxn: Vec<u8>, /// Subject identifier pub sub: String, }🤖 Prompt for AI Agents
In contracts/fa-guard-jwt-rs256/src/jwt.rs around lines 8 to 14, the fatxn field is a Vec<u8> that serializes as a JSON array, making JWTs large and non-portable. To fix this, derive Debug and Clone for the FaJwtCustomClaims struct and apply serde_with::Base64 encoding to the fatxn field to ensure it is Base64-encoded during serialization, keeping the token compact and consistent with other binary fields.
poc(contracts): permissionles contract sign
Changes 🛠️
app/package
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Chores
.gitignorefiles to cover more development artifacts and environment-specific files.Tests
Style