Repository navigation
Conversation
Part A of the #109 split (theme and shell). No screen content changes. - Puls3Breakpoints: one set of named layout breakpoints instead of raw 600/640/700/900/960/1000 numbers across screens and widgets, plus isCompactLayout(). - Phone type scale below 640 px (14 px body, 18-28 px titles), set by the app root; desktop keeps the brand sizes. - Compact visual density and denser inputs on phones; PrimaryButton is 44 px tall there (52 on desktop) inside a 48 px tap target. - Bottom navigation bar on phones (Studio, Marketplace); the top bar keeps the logo and a wallet chip that shrinks instead of overflowing. - ScreenHeader and SkeletonBox building blocks for the following parts. Tests: bottom bar on phones only, compact type and button size, and no overflow on every screen at 320, 360, 390 and 414 px. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Part B of the #109 split. Refs #27: the testnet end-to-end criterion waits for the deploy endpoint (#18) and the relay (#96). - DeployFlow + DeployFlowController: preparing -> waiting for signature -> registering on-chain -> activating -> live. Never runs twice, resumes from the failed step, keeps the prepared and signed transaction across a lost response, verifies every backend answer, and stops if the flow is closed mid-run. - Presentational widgets: DeployStepper, phase card, error panel and success, with skeletons until values arrive. - Errors with a recovery action and no reload: signature rejected, transaction failed, backend error, plus wallet unavailable, wrong network, account changed, connection lost/timeout, invalid response, expired preparation (prepares again) and a wallet that never answers (times out; the sheet can be closed while the wallet prompt is open). Review fixes: - The demo gateway is labelled: a banner says nothing is registered on Stellar, and the result reads "Demo deploy only" with no explorer link, no agent page and no made-up hash. Demo agents are not listed. - WalletPort.signTransaction only signs and returns the signed XDR; the server submits (API contract #77, Decision A). MockWallet returns a marked signed placeholder instead of a hash. - Real agents start at a 0.0 rating; explorer links use stellarExpertTxUrl; the unused SuccessPanel, DeployProgressView and AppScope.ids are removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #25. Built on the deploy flow (#112). The real-wallet acceptance on Testnet (screenshot) and the server relay (#96/#18) are pending. - WalletPort keeps one signing boundary: connect, disconnect, address, network, signTransaction (returns the signed XDR, the server submits) and signAuthEntry. Typed errors: rejected, not installed, locked/ unavailable, wrong network, account changed, invalid payload. - FreighterWallet over a small JS bridge (freighter-api 6.0.1). Before any prompt it checks the payload (#69): Testnet passphrase, no fee-bump envelopes, no trailing bytes, auth entries with a non-zero expiration and address credentials only. After signing it checks the account did not change. - MockWallet for tests and the demo console (--dart-define=WALLET=mock); Freighter is the default on the web. - Wallet sheet (S09): disconnected, connecting, connected (Testnet badge, copyable address, disconnect), cancelled, wrong network, not installed (install link), locked, with a retry in every error. - Demo flows (deploy, hire) sign a harmless, never-submitted Testnet envelope so a real wallet shows its prompt; they stay labelled as demos. Wallet errors show human messages. Tests: 19 for the Freighter adapter checks, 11 for the connection states, chip and phone layout. 151 passing, analyze clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t guide Refs #25. - WalletStatus is one explicit state: disconnected, connecting, connected, signing, signed, rejected, wrongNetwork, error. No mix of flags. - WalletController blocks signing before any prompt when the wallet is not on Testnet, for every adapter. A network or account switch during signing forgets the session, so the next attempt connects again; the deploy flow reconnects before re-signing. - Wallet sheet: "Waiting for wallet confirmation…", "Transaction signed", "Signature rejected", "Wrong network", "Account changed", "Transaction refused" and "Wallet connection failed", each with a retry. - MockWallet keeps its account across a disconnect, like a real wallet. - README: wallet architecture, how to test with Freighter, and the commands to build and test. Tests: signing, signed, rejected signature, signing error, wrong network blocks signing, disconnect, and the panel states. 160 passing, analyze clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying puls3 with
|
| Latest commit: |
f58652e
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://17777abf.puls3-4lw.pages.dev |
| Branch Preview URL: | https://feat-25-wallet-connection.puls3-4lw.pages.dev |
TOMOKI977
left a comment
There was a problem hiding this comment.
Really solid security work here. I checked every claim in the PR body against the code, and each check is actually enforced:
- Testnet only, at
connect()and again before each prompt. - Fee-bump envelopes refused.
- Canonical re-encode, so trailing bytes are rejected.
- Transaction and operation sources bound to the connected account.
- Auth entries with zero expiration refused.
- After signing, the body is compared unchanged and the signature is verified against the connected key with Dart's hard-coded Testnet hash.
The demo envelope is a single manage_data that can't move value. Nothing sensitive is logged. The WalletPort seam, the sealed exceptions and the single WalletStatus make it easy to follow.
I'm requesting changes for one issue, the same one I raised on #112. It is now reachable with a real wallet:
Starting a deploy with no connected wallet can trap the user. The preparing step awaits _wallet.connect() without a timeout (deploy_flow_controller.dart:189). With Freighter that call is an untimed requestAccess() (freighter_bridge.js:32, freighter_bridge_web.dart:105). While preparing, the sheet hides Close and blocks back (deploy_flow.dart:109-114), and it can't be dismissed (deploy_sheet.dart:20-21). If Freighter never settles the promise (popup ignored, blocked, or stuck on the lock screen), the only way out is reloading the page. An already-connected wallet avoids it (wallet_controller.dart:74-75), but Studio doesn't require connecting first. A timeout on wallet calls in the bridge or controller fixes it for both flows, as would letting the user close while the wallet step is pending. Please add a test with a connect() that never completes.
Also please fix two tests that can't fail as written. freighter_wallet_test.dart:130-131 and :184-188 check wallet.address / bridge.signCalls before the future settles. Use await expectLater(...) first, as you already do at 134-142.
Not blocking, worth a follow-up:
- Unknown errors read as "you rejected".
_translatemaps anything without a known code toWalletSignatureRejected(freighter_bridge_web.dart:123-124). A JSTypeErroror an extension crash tells the user "You rejected the transaction" with nothing logged. A genericWalletUnavailable(or an error state) plus a log would be more honest. - The bridge loads
@stellar/freighter-apifrom esm.sh at runtime (freighter_bridge.js:6), with no integrity pin and no CSP, andindex.htmlblocks app startup on that import. A slow CDN delays the app for everyone, and a CDN that is down shows "Freighter not found" even when it is installed. Your comment already notes that production should bundle it. Let's track that as an issue before the public demo. main.dart:19-26falls back toMockWalletfor anyWALLETvalue other than exactlyfreighter. An assert or a log would catch a typo.WalletControllerhas no in-flight guard onconnect/_sign/disconnect. For example, disconnecting during a pending connect ends up connected again (freighter_wallet.dart:43).- Untested branches: the
signAuthEntrysession checks and its post-sign checks (signer mismatch, signature length, bad base64), ADDRESS_V2 credentials, a fee-bump or unsigned envelope returned by the wallet, and theWalletAccountChangedpath through the controller. - Smaller notes:
wallet_sheet.dart:28uses a raw600instead ofPuls3Breakpoints.narrowGutter._Connectingand_Signingduplicate the progress bar block. The README port list is missingsignAuthEntry.
|
@Pericena Heads-up: CI changes when #114 (Serverpod 4.0.4) merges. Once it lands on
Specific to this PR: after rebasing #112, rebase this one onto it (or onto |
Refs #25 — stacked on #112 (base
feat/27-deploy-flow). Review after #111 and #112.What
WalletPortis the only signing boundary:connect,disconnect,address,network,signTransaction(returns the signed XDR; the server submits, contract docs: add API contract between Flutter app and Serverpod #77 Decision A) andsignAuthEntry.FreighterWalletover a small JS bridge (@stellar/freighter-api6.0.1), from the feat(spike): prove Flutter Web Freighter signing and Testnet SAC payments #70 spike. Before any prompt it applies the chore(frontend): enforce pre-prompt checks in the #68 wallet spike adapter #69 checks: Testnet passphrase, no fee-bump envelopes, no trailing bytes, transactions only for the connected account, auth entries with non-zero expiration. After signing it verifies the transaction is unchanged and signed by the connected account.WalletControllerexposes one explicitWalletStatus: disconnected, connecting, connected, signing, signed, rejected, wrongNetwork, error. It blocks signing before any prompt when the wallet is not on Testnet (any adapter); a network/account switch forgets the session.MockWalletfor tests and the demo console (--dart-define=WALLET=mock); Freighter is the default on the web.Security
The app only handles the public address, network, unsigned XDR and signed XDR. No secret keys are stored or logged. Mainnet is not supported.
Tests
flutter analyze: no issues.Pending (why
Refs, notCloses)