Skip to content

Zcash: Orchard → Ironwood (NU6.3) migration UX - #6077

Merged
peachbits merged 3 commits into
developfrom
matthew/zec-ironwood-migration-gui
Aug 2, 2026
Merged

Zcash: Orchard → Ironwood (NU6.3) migration UX#6077
peachbits merged 3 commits into
developfrom
matthew/zec-ironwood-migration-gui

Conversation

@peachbits

@peachbits peachbits commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Zcash Ironwood (NU6.3) v1: migration card → locked max send-to-self

Pairs with react-native-zcash#71 and edge-currency-accountbased#1075 (both v1), on the released SDK 2.7.0-rc.2. Restructured 2026-07-21 to v1-only (scenes/notification/kebab moved to the stacked v2 PR): on login the wallet syncs; when the engine reports a sweep is worthwhile, a card appears on the Zcash wallet scene; tapping it prefills a locked max send-to-self in the ordinary send scene; the card clears via the status poll once Orchard empties by any means.

What's here

  1. Status util + hook — cleaner-validated access to otherMethods.getMigrationStatus, plus a polling hook safe to call unconditionally. Enabled on both platforms: zcash-android-sdk 2.7.0-rc.2 is published, so the Android bridge calls it directly. There is no platform gate — the earlier ZCASH_MIGRATION_PLATFORMS list was an always-true check once Android shipped, so it was removed rather than left as dead configuration.

  2. The card — its own component (ZcashMigrationCard) rather than an AlertCardUi4, because the help link is inline in the copy and that card cannot do it. Shape:

    • Title "Ironwood Upgrade", single CTA "Migrate", and an inline "Learn more" link at the end of the body — a nested EdgeText with its own onPress, matching the Stealth Send treatment.
    • Not dismissable. It withdraws itself when the Orchard balance empties, so a dismiss control would only ever hide a still-true prompt.
    • Recommended tone by design. Nothing is framed as required, because Orchard stays spendable post-fork and drains passively through ordinary spends — the copy says so explicitly ("Funds will migrate as you create transactions… Your funds stay safe and spendable either way"). The sweep is an accelerator for dormant funds: one clean uncorrelated reveal instead of repeated payment-linked ones.
    • Displays the Orchard-pool balance at risk (ZIP 318 requires the entry point to) from status.remainingOrchardZatoshi, and discloses that the migrated amount becomes public (ZIP 315 consent).
    • Tapping: getAddresses → own unified address → getMaxSpendablesend2 with address/amount/wallet tiles locked (the account-activation locked-send pattern), an info tile, and metadata.notes labeling. The spendInfo carries top-level otherParams.ironwoodMigration: true, which makes the engine quote and build an Orchard-only sweep — so the locked amount is the Orchard balance minus fee, not a whole-wallet max send. hiddenFeaturesMap deliberately omitted (scamWarning: false triggers the modal).
  3. Send-scene info tile fix — the migration tile's value is paragraph-length, and EdgeRow caps at 3 lines by default. That cap does not clip: EdgeText responds with adjustsFontSizeToFit / minimumFontScale={0.65}, so the text was rendering up to 35% smaller than its already-small 0.75rem and was hard to read. infoTiles entries now accept an optional maximumHeight, and the migration tile passes 'large' (unlimited lines). Opt-in on purpose: infoTiles is shared with GiftCardPurchase, both Fio settings scenes and WalletActions, which all pass short one-liners, so their rendering is untouched. Note this is a latent issue for any long infoTiles value app-wide, worth a separate look at whether 'large' should be the default.

  4. Pins — still reference the ironwood-ffi-6e29ab42 tarballs. Re-pinning is deliberately the last step before merge, since it depends on the final published SDKs.

Verification

  • eslint . + tsc + jest green (562 tests, 107 snapshots), per commit.
  • Built and run in the iOS simulator against react-native-zcash#71 and accountbased#1075. Ironwood is now active on mainnet (activation 3,428,143; chain past it), so this was exercised live rather than dormant: on a wallet holding Orchard funds the card renders with the real balance interpolated into the copy.
  • Non-zcash currency plugins were disabled locally while testing to keep the surface uncluttered. That stub is not in this branch.

Notes for review

  • The sweep is Orchard-only (the engine drives the SDK's proposeOrchardToIronwoodMigration), so Sapling and transparent funds are untouched — matching ZIP 318's "balance at risk" being the Orchard-pool balance specifically.
  • Engine-side dust floor prevents prompting for sweeps that would be mostly fee.
  • The info-server asset-status card and this card live in different containers (InfoCardCarousel vs the vertical stack), so they can coexist. In practice the asset card is capped to outdated builds by maxBuildNum, but that is a config guarantee rather than a code one.
  • The guided flow (options/plan/progress scenes, notification center, kebab) is the stacked v2 PR, held for live validation.


Note

Medium Risk
Touches Zcash send quoting and max-spend with a new engine flag and user-facing migration flows, but reuses the existing send scene with locked tiles rather than a separate signing path; dependency pins add release coupling to the Ironwood SDK builds.

Overview
Adds Zcash NU6.3 (Orchard → Ironwood) UX on the wallet scene: when the engine reports migration state required, a warning card shows the Orchard-pool balance at risk (ZIP 318) and recommended copy that migrated amounts become public on-chain (ZIP 315).

Migrate prefills the ordinary Send scene with a locked max send-to-self to the wallet’s unified address, using otherParams.ironwoodMigration so quoting/build is an Orchard-only sweep (not a whole-wallet max). Address, amount, and wallet tiles stay locked; an info tile explains the migration.

New getZcashMigrationStatus (cleaner-validated otherMethods.getMigrationStatus) and useZcashMigrationStatus polling hook drive visibility; the card hides when Orchard is empty. ZcashMigrationCard is a dedicated component (inline Learn more link). SendScene2 infoTiles can set maximumHeight: 'large' so long migration text doesn’t shrink via EdgeText font scaling.

Dependencies pin react-native-zcash and edge-currency-accountbased Ironwood FFI tarballs; locale strings and optional zcashMigrationLearnMoreUrl in app config. Unit tests cover the migration util.

Reviewed by Cursor Bugbot for commit e7ad9e6. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI review requested due to automatic review settings July 24, 2026 20:00
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from dae76da to 58a1bb5 Compare July 24, 2026 20:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Adds the first-pass (v1) Zcash Orchard → Ironwood (NU6.3) migration UX: detect when an Orchard-only sweep is worthwhile, surface a recommendation card on the wallet scene, and route the user into a locked “send max to self” flow using the existing SendScene2.

Changes:

  • Introduces a typed migration-status util (cleaners) plus a polling hook to safely query wallet.otherMethods.getMigrationStatus (iOS-gated).
  • Adds a Zcash-only “migration recommended” card to the wallet scene that navigates into send2 with locked address/amount/wallet tiles and an otherParams.ironwoodMigration flag.
  • Adds localized strings + a Jest test suite for the migration util, and updates the changelog (plus dependency pin updates in excluded manifests).

Reviewed changes

Copilot reviewed 6 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/util/zcashMigration.ts Typed + validated access to engine migration status, with platform gating and error fallback.
src/hooks/useZcashMigrationStatus.ts Polling hook wrapper around getZcashMigrationStatus for unconditional use in shared UI.
src/components/themed/TransactionListTop.tsx Renders the migration card and wires navigation into a locked SendScene2 send-to-self sweep flow.
src/locales/en_US.ts Adds en_US string entries for the migration card and send info tile.
src/tests/zcashMigration.test.ts Adds tests for the util’s capability gating and cleaner validation.
CHANGELOG.md Notes the new migration card + send-to-self flow and iOS-only gating.
package.json Updated (content excluded by policy).
package-lock.json Updated (content excluded by policy).
src/locales/strings/enUS.json Updated (content excluded by policy).
Files excluded by content exclusion policy (3)
  • package-lock.json
  • package.json
  • src/locales/strings/enUS.json

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/locales/en_US.ts Outdated
Comment thread src/components/themed/TransactionListTop.tsx
Comment thread src/__tests__/zcashMigration.test.ts
Copilot AI review requested due to automatic review settings July 26, 2026 03:56
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from 58a1bb5 to ffae678 Compare July 26, 2026 03:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 6 out of 9 changed files in this pull request and generated 1 comment.

Files excluded by content exclusion policy (3)
  • package-lock.json
  • package.json
  • src/locales/strings/enUS.json
Comments suppressed due to low confidence (2)

src/locales/en_US.ts:776

  • zcash_migration_recommended_body_1s says "%s of your ZEC", but the caller passes a formatted string that already includes the denomination (e.g. "1.23 ZEC"), resulting in awkward text like "1.23 ZEC of your ZEC".
  zcash_migration_recommended_body_1s:
    "Zcash's Ironwood network upgrade added a new shielded pool, and %s of your ZEC is still in the older Orchard pool. We recommend moving it with a single send to yourself. The amount you move will be publicly visible on the blockchain. Your funds stay safe and spendable either way.",

src/components/themed/TransactionListTop.tsx:706

  • If getAddresses doesn't return a unifiedAddress, the button becomes a no-op with no user feedback. Throwing an error here will surface a modal via the existing .catch(showError) handler.
      const unifiedAddress = addresses.find(
        address => address.addressType === 'unifiedAddress'
      )
      if (unifiedAddress == null) return
      const spendInfo: EdgeSpendInfo = {

Comment thread src/hooks/useZcashMigrationStatus.ts
Copilot AI review requested due to automatic review settings July 27, 2026 15:58
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from ffae678 to b3b206d Compare July 27, 2026 15:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 6 out of 9 changed files in this pull request and generated 1 comment.

Files excluded by content exclusion policy (3)
  • package-lock.json
  • package.json
  • src/locales/strings/enUS.json
Comments suppressed due to low confidence (2)

src/hooks/useZcashMigrationStatus.ts:22

  • This project generally uses the custom useHandler hook instead of React.useCallback (see src/hooks/useHandler.ts and e.g. src/hooks/useMount.ts:11) to keep callback identity stable without dependency churn. Using useHandler here keeps the polling callback stable even if the wallet object reference changes unexpectedly.
  const refresher = React.useCallback(
    async () => await getZcashMigrationStatus(wallet),
    [wallet]
  )

src/tests/zcashMigration.test.ts:5

  • These tests implicitly rely on whatever Platform.OS the Jest RN environment provides. Since getZcashMigrationStatus gates on Platform.OS, the first test can become flaky if the test environment changes (e.g. runs as 'web'). Mock react-native Platform.OS in this test to make the expectations deterministic.
import { describe, expect, it } from '@jest/globals'
import type { EdgeCurrencyWallet } from 'edge-core-js'

import { getZcashMigrationStatus } from '../util/zcashMigration'

Comment thread src/util/zcashMigration.ts Outdated
Copilot AI review requested due to automatic review settings July 27, 2026 23:18
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from 861507d to 707b62a Compare July 27, 2026 23:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 6 out of 9 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (3)
  • package-lock.json
  • package.json
  • src/locales/strings/enUS.json
Comments suppressed due to low confidence (5)

src/components/themed/TransactionListTop.tsx:706

  • If getAddresses doesn't return a unifiedAddress, the button press silently no-ops. From the user's perspective this looks like a broken button; it would be better to surface an error (caught by the existing .catch(showError)) so the user gets feedback and we get diagnostics.
      const unifiedAddress = addresses.find(
        address => address.addressType === 'unifiedAddress'
      )
      if (unifiedAddress == null) return

src/locales/en_US.ts:776

  • The %s substitution passed to zcash_migration_recommended_body_1s already includes the denomination (e.g. "0.123 ZEC"), but the string also says "of your ZEC". This produces awkward duplicated output like "0.123 ZEC of your ZEC".
  zcash_migration_recommended_body_1s:
    "Zcash's Ironwood network upgrade added a new shielded pool, and %s of your ZEC is still in the older Orchard pool. We recommend moving it with a single send to yourself. The amount you move will be publicly visible on the blockchain. Your funds stay safe and spendable either way.",

src/components/themed/TransactionListTop.tsx:698

  • migrationStatus.remainingOrchardZatoshi is treated as a numeric string and passed into biggystring.div. If the engine ever returns a non-integer string (or an empty string), div can throw during render and crash the wallet scene. Consider guarding the value (or validating it in the status cleaner) before formatting.
    const orchardDisplayAmount = formatNumber(
      div(
        migrationStatus.remainingOrchardZatoshi,
        displayDenomination.multiplier,
        DECIMAL_PRECISION

src/util/zcashMigration.ts:62

  • getZcashMigrationStatus is intended to be polled. Logging a warning on every failed poll can spam production logs and make real issues harder to find. Consider gating this warning to dev builds (or throttling it).
    console.warn('getZcashMigrationStatus failed', error)

CHANGELOG.md:5

  • This entry says the migration card is "iOS only until the Android SDK ships Ironwood", but the PR description says the Android SDK is published and the feature is enabled on both platforms. The changelog line should be updated to match the actual behavior shipped by this PR.
- added: Zcash: Orchard -> Ironwood (NU6.3) migration card on the wallet scene - when the engine reports a sweep is worthwhile, it prefills a locked max send-to-self through the ordinary send scene (recommended-tone: funds stay spendable either way). iOS only until the Android SDK ships Ironwood.

Copilot AI review requested due to automatic review settings July 28, 2026 18:26
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from 707b62a to 5b0ad4a Compare July 28, 2026 18:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 6 out of 9 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (3)
  • package-lock.json
  • package.json
  • src/locales/strings/enUS.json
Comments suppressed due to low confidence (2)

src/hooks/useZcashMigrationStatus.ts:24

  • useRefresher schedules a timer and then also triggers an immediate refresh when defaultValue == null, which results in two concurrent polling loops when called as useRefresher(..., undefined, delay). In this hook that means double (and potentially compounding) getMigrationStatus calls and extra renders.
  const refresher = React.useCallback(
    async () => await getZcashMigrationStatus(wallet),
    [wallet]
  )
  return useRefresher(refresher, undefined, delay)

src/components/themed/TransactionListTop.tsx:727

  • If getMaxSpendable returns '0' (e.g. Orchard balance is entirely consumed by the fee, or engine returns 0 for any reason), this will navigate to send2 with a locked zero amount. That creates a dead-end UX and may surface confusing send errors. Consider seeding nativeAmount: '0' (same pattern as SendScene2) and explicitly blocking/throwing on a '0' max spendable result before navigating.
      const spendInfo: EdgeSpendInfo = {
        tokenId: null,
        spendTargets: [{ publicAddress: unifiedAddress.publicAddress }],
        metadata: { notes: lstrings.zcash_migration_tx_notes },
        // Top-level otherParams reaches the engine intact (per-target

Copilot AI review requested due to automatic review settings July 29, 2026 22:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 8 out of 11 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (3)
  • package-lock.json
  • package.json
  • src/locales/strings/enUS.json
Comments suppressed due to low confidence (2)

src/components/themed/TransactionListTop.tsx:728

  • If getMaxSpendable returns '0' (e.g. fee >= remaining Orchard balance due to a race or fee change), this will navigate to SendScene2 with the amount tile locked, leaving the user unable to adjust the amount and likely hitting an error later. Add an explicit guard before navigating.
      const migrationAmount = await wallet.getMaxSpendable(spendInfo)
      navigation.push('send2', {

src/util/zcashMigration.ts:58

  • getZcashMigrationStatus is intended to be polled; logging a console.warn on every failure can create noisy logs and performance/telemetry issues if the engine method throws repeatedly (e.g. during temporary engine states). Consider removing the warning or otherwise throttling it.
  } catch (error) {
    console.warn('getZcashMigrationStatus failed', error)
    return undefined
  }

@peachbits
peachbits marked this pull request as ready for review July 29, 2026 22:57
Copilot AI review requested due to automatic review settings July 29, 2026 22:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 8 out of 11 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (3)
  • package-lock.json
  • package.json
  • src/locales/strings/enUS.json
Comments suppressed due to low confidence (3)

src/hooks/useZcashMigrationStatus.ts:22

  • This hook uses React.useCallback even though the project has useHandler specifically as a better useCallback (stable identity without dependency arrays). Using useHandler here avoids restarting the useRefresher effect/timer when the wallet object identity changes and matches the established pattern elsewhere in the repo.
  const refresher = React.useCallback(
    async () => await getZcashMigrationStatus(wallet),
    [wallet]
  )

src/util/zcashMigration.ts:41

  • isMigrationCapable assumes wallet.otherMethods always exists. In unit tests and potentially some wallet-like objects, otherMethods can be missing, which causes getZcashMigrationStatus to reject and surfaces an avoidable error via useRefresher's showError path. Guard otherMethods with optional chaining so non-migration wallets cleanly return undefined.
const isMigrationCapable = (wallet: EdgeCurrencyWallet): boolean =>
  wallet.currencyInfo.pluginId === 'zcash' &&
  typeof wallet.otherMethods.getMigrationStatus === 'function'

src/components/cards/ZcashMigrationCard.tsx:85

  • The card body uses EdgeText with numberOfLines={10} and the default adjustsFontSizeToFit behavior, which can shrink the paragraph (and potentially truncate the inline “Learn more” link) on smaller screens / longer locales. Since this copy is already paragraph-length, prefer unlimited lines and disable font-size-to-fit shrinking for readability.
        <EdgeText style={styles.text} numberOfLines={10}>
          {sprintf(
            lstrings.zcash_migration_recommended_body_1s,
            orchardBalanceText
          )}{' '}
          <EdgeText style={styles.learnMoreLink} onPress={handleLearnMore}>
            {lstrings.zcash_migration_learn_more_button}
          </EdgeText>
        </EdgeText>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d7a04bc. Configure here.

Comment thread package-lock.json Outdated
Comment thread src/components/cards/ZcashMigrationCard.tsx
Comment thread src/components/cards/ZcashMigrationCard.tsx
Copilot AI review requested due to automatic review settings July 29, 2026 23:22
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from d7a04bc to e7ad9e6 Compare July 29, 2026 23:22
Copilot AI review requested due to automatic review settings August 1, 2026 00:18
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from e7ad9e6 to 33e295b Compare August 1, 2026 00:18

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 11 out of 14 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (3)
  • package-lock.json
  • package.json
  • src/locales/strings/enUS.json
Suppressed comments (1)

src/util/zcashMigration.ts:28

  • remainingOrchardZatoshi is later passed into biggystring.div(...), but the cleaner currently only uses asString, which allows non-numeric strings (e.g. "abc"). If the engine ever returns an unexpected value, this can survive cleaning and then throw at render-time when formatting the card. Consider validating this field as a numeric biggystring (using the repo's asBiggystring) so malformed engine data safely fails cleaning and the UI simply suppresses the card.
export const asZcashMigrationStatus = asObject({
  state: asValue('notNeeded', 'required', 'scheduled', 'complete', 'error'),
  completedTransfers: asMaybe(asNumber, 0),
  totalTransfers: asMaybe(asNumber, 0),
  remainingOrchardZatoshi: asMaybe(asString, '0'),

// an item leaves the ones after it parked at their old offsets,
// a full item-width off-screen. Remounting on a slot change
// establishes the transform fresh, which is always correct.
key={`${itemIndex}-${keyExtractor(item, itemIndex)}`}

@j0ntz j0ntz Aug 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous issue was a PITA too here. Great catch, thanks.

Copilot AI review requested due to automatic review settings August 1, 2026 05:59
@j0ntz
j0ntz force-pushed the matthew/zec-ironwood-migration-gui branch from 33e295b to ea565cc Compare August 1, 2026 05:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review is ineligible. To be eligible to request a review, you need a paid Copilot license, or your organization must enable Copilot code review.

@cursor

cursor Bot commented Aug 1, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@j0ntz

j0ntz commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

📸 Test evidence: real on-chain Orchard to Ironwood migration (local build of the full stack)

migration card detected

migration card detected

migration proposal

migration proposal

broadcast success

broadcast success

onchain explorer mined

onchain explorer mined

post migration card gone

post migration card gone

Captured by the agent's in-app test run (build-and-test).

@j0ntz

j0ntz commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Blocking integration finding from local verification of this branch (full details, since the test evidence comment references it).

The iOS link fails with 20,117 duplicate symbols when this gui branch is built against react-native-zcash@zcash-spm-deps-binary.

Cause: react-native-zcash 0.13.0 now vendors grpc-swift + SwiftNIO into ios/vendored/libZcashDeps.xcframework (the SwiftPM-only-deps change), while react-native-piratechain 0.6.1 still declares them as CocoaPods deps:

react-native-piratechain (0.6.1):
  - gRPC-Swift (~> 1.8)      -> CGRPCZlib, SwiftNIOHTTP2, ...

So both a CocoaPods NIOHTTP2.framework and the vendored libZcashDeps.a land on the app link line:

duplicate symbol "NIOHTTP2.HTTP2StreamChannel.read0() -> ()" in:
    .../Debug-iphonesimulator/SwiftNIOHTTP2/NIOHTTP2.framework/NIOHTTP2[27](HTTP2StreamChannel.o)
    .../XCFrameworkIntermediates/react-native-zcash/libZcashDeps.a[arm64][391](HTTP2StreamChannel.o)

react-native-piratechain is the only consumer pulling gRPC-Swift in, so it is the single collision point.

To finish the migration test I unlinked piratechain locally (uncommitted): platforms: { ios: null } in react-native.config.js plus piratechain: false in corePlugins.ts. That is throwaway test scaffolding, not a proposed fix.

A real resolution is needed before this can land, e.g. give piratechain the same vendored-binary treatment, or have zcash consume the CocoaPods gRPC-Swift rather than vendoring it, or dedupe the link inputs in the Podfile. Flagging rather than guessing at the right shape, since it spans both native modules.

Copilot AI review requested due to automatic review settings August 2, 2026 17:30
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from ea565cc to a02ab5a Compare August 2, 2026 17:30
@cursor

cursor Bot commented Aug 2, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.

Tip: disable this comment in your organization's Code Review settings.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • src/locales/strings/enUS.json
Suppressed comments (2)

src/util/zcashMigration.ts:57

  • getZcashMigrationStatus is used from a polling hook (default 10s). Logging a console.warn on every transient engine / cleaner failure can spam device logs and error reporting, especially if the engine changes the status shape or throws repeatedly. Consider gating the warning to dev builds (or otherwise throttling) since the function already degrades to undefined.
  } catch (error) {
    console.warn('getZcashMigrationStatus failed', error)
    return undefined

src/components/cards/ZcashMigrationCard.tsx:88

  • The inline "Learn more" text is tappable but doesn’t declare a link/button role, which can make it harder to discover with screen readers. Adding accessibilityRole="link" will improve accessibility without changing UI behavior.
          <EdgeText style={styles.learnMoreLink} onPress={handleLearnMore}>
            {lstrings.zcash_migration_learn_more_button}
          </EdgeText>

peachbits and others added 3 commits August 2, 2026 10:56
Typed access to the engine's v1 Orchard -> Ironwood detection surface (wallet.otherMethods.getMigrationStatus, cleaner-validated) plus a polling hook safe to call unconditionally from shared scenes. iOS-gated until the Android bridge lands. Strings use a recommended tone by design: the sweep is worthwhile, not mandatory - Orchard stays spendable post-fork and drains passively through ordinary spends, so nothing is framed as "required".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Zcash wallet scene's migration card (shown when the engine reports a sweep is worthwhile) prefills the ordinary send scene with the wallet's own unified address and its max spendable amount, with the address/amount/wallet tiles locked - the account-activation locked-send pattern. Post-activation, plain proposals route outputs and change into Ironwood, so this is a normal transaction through the normal pipeline: fee and final amount come from the engine's makeSpend, and the tx is labeled via metadata notes.

Copy is deliberately recommended-tone, not required: Orchard stays spendable post-fork and drains passively through ordinary spends. The card clears via the status poll once Orchard empties by any means (sweep sent, or drained organically). A max send sweeps every shielded pool into Ironwood - the strings say "shielded ZEC", not just Orchard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An item's whole on-screen position comes from an animated transform
derived from its index, while its React key is the item's own id. When
the card list shrinks, a surviving item shifts to a lower slot without
remounting, and the transform is never re-applied: the view stays parked
at its old offset, a full item-width off-screen. The container keeps its
fixed height and the pagination dots do not depend on width, so what is
left on screen is an empty gap with a lone dot.

Make the slot part of the item's identity so a shift remounts it, which
establishes the transform fresh. Verified on an Android emulator and an
iOS simulator.

Not Zcash-specific: the trigger is the info server's `noBalance` card
being filtered out once `useIsAccountFunded` resolves, which races the
carousel mounting. It surfaces here because the Ironwood migration card
changes the wallet scene's height, but it can strand a card on any
scene whose carousel list shrinks after mount.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 2, 2026 17:57
@peachbits
peachbits force-pushed the matthew/zec-ironwood-migration-gui branch from a02ab5a to 7ba8593 Compare August 2, 2026 17:57
@cursor

cursor Bot commented Aug 2, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • src/locales/strings/enUS.json
Suppressed comments (1)

src/hooks/useZcashMigrationStatus.ts:24

  • useRefresher schedules a setTimeout(handleTimeout, delay) and then (when defaultValue == null) immediately calls handleTimeout() again. With defaultValue set to undefined here, that can create two concurrent polling timers (double-calling getZcashMigrationStatus every interval). Consider inlining the polling logic in this hook so you can do an immediate fetch without also scheduling an initial delayed timer.
  const refresher = React.useCallback(
    async () => await getZcashMigrationStatus(wallet),
    [wallet]
  )
  return useRefresher(refresher, undefined, delay)

@peachbits
peachbits enabled auto-merge August 2, 2026 18:31
@peachbits
peachbits merged commit a9b0c93 into develop Aug 2, 2026
7 checks passed
@peachbits
peachbits deleted the matthew/zec-ironwood-migration-gui branch August 2, 2026 18:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants