Skip to content

fix: restore swap history order id and surface required recipient entry (OK-59978, OK-58326) - #12850

Open
haicongliao wants to merge 16 commits into
OneKeyHQ:xfrom
haicongliao:fix/swap-order-id-and-recipient-entry
Open

fix: restore swap history order id and surface required recipient entry (OK-59978, OK-58326)#12850
haicongliao wants to merge 16 commits into
OneKeyHQ:xfrom
haicongliao:fix/swap-order-id-and-recipient-entry

Conversation

@haicongliao

@haicongliao haicongliao commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

OK-59978 OK-58326


OK-59978 — Swap history order id

  • Restore the Order ID row that was removed in fix: swap history order id, network logo and no-provider button(OK-57251 OK-57730 OK-57545) #12487. It now renders only when the provider returns orderSupportUrl (e.g. CoW), so providers without a third-party order explorer stay hidden as OK-57251 intended.
  • Use the provider order id kept in ctx (the CoW order uid) instead of the internal service order id, so the explorer link actually resolves for Stock orders — previously the link carried an internal UUID and explorer.cow.fi returned "No results found".
  • Shorten the displayed id to a single line while copying and linking the full value (InfoItem gained an optional copyContent).
  • Show the explorer link next to the tx hash for same-chain swaps, matching the cross-chain layout.

OK-58326 — Swap recipient entry

  • Surface the recipient entry row when the quote requires a manually entered recipient (e.g. a single-network private-key wallet doing a cross-chain swap), even while the custom recipient setting is off. The action button deep-link from [OK-58614][OK-58443] Fix Swap recipient entry and token search #12685 is kept as a second entry.
  • Fix SwapSmoothReveal never expanding on native: a normal-flow child of the height-0 animated wrapper measures 0 under Yoga, so the measured height stayed 0 forever and the row was permanently hidden on iOS/Android. Content is now measured with absolute positioning. This also unhides the stock trade alert and the cost-savings reveal on native.
  • Hold the "recipient required" verdict across quote refresh cycles (the same settled-hold pattern as OK-58977) so the row no longer collapses and re-expands on every quote round.

Verification

  • 52 unit tests pass (swapHistoryIdentity, swapHistoryTransactionIds, useSwapAccount.utils), including new cases for the provider-order-id fallback and the recipient entry conditions.
  • Verified on the iOS simulator (recipient row expands and stays put across quote cycles) and on desktop (a real CoW Stock order shows 订单编号 0x0d99606c…6a85790c under 渠道商 CoW Swap).

Note on CI type check

yarn tsc:staged reports one error in packages/kit/src/views/Market/MarketHomeV2/layouts/MobileLayout.native.tsx:873 (tabPressAnimationEnabled is not declared on the tab container props). This is pre-existing on x — introduced by #12842 — and unrelated to the files in this PR. Reproduced on a clean x checkout with only a whitespace change to trigger the check.

@sidmorizon

Copy link
Copy Markdown
Contributor

@codex review

@sidmorizon

Copy link
Copy Markdown
Contributor

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

Comment thread packages/kit/src/views/Swap/pages/modal/SwapHistoryDetailModal.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37f9e3dc63

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/kit/src/views/Swap/pages/components/SwapSmoothReveal.tsx Outdated
Comment thread packages/kit/src/views/Swap/pages/components/SwapActionsState.tsx Outdated
@zhaono1

zhaono1 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review 结论

需要修改后复审,7.4/10,当前不建议合入。

已按当前 head 37f9e3dc63 复核;相对最新 x 无 base 漂移。CI 为 17 pass / 0 failed / 0 pendinggit diff --check 通过,但 3 个 review threads 仍未解决,其中 2 个是已确认的正确性问题,另 1 个是成立的跨平台边界问题。

合入前需要处理

  1. [P2] 收款方必填状态会跨报价作用域残留thread
    该状态属于 iOS/Android main UI runtime 的单份 JS heap;没有 bg readiness 或共享 native resource 参与。旧报价把 ref 置为 true 后,切到 native Limit / 切换账户或 token 时,quote 被清空但 ref 没有按作用域重置,入口可能继续错误显示。建议按 swap type + account + from/to token/network 对状态做 scope/reset,同时保留同一作用域内 quote refresh 防闪烁,并补一个真实生命周期回归测试;当前 pure-function 测试覆盖不到这个问题。

  2. [P2] 中等长度 Order ID 的缩写会重复字符thread
    leadingLength=24trailingLength=20,但默认 minLength=14;15–43 位 ID 会发生切片重叠。请把阈值至少设为 44(或改成专用 formatter),并补边界用例。

  3. [P2] 原生 Yoga 修复被无条件施加到 shared componentthread
    原评论的 P1 严重度偏高,但技术边界成立:问题针对 iOS/Android main runtime,position: absolute 不应默认影响 desktop/web 以及 SwapSmoothReveal 的其他调用方。请用 platform-specific file / Platform.select 限制到 native,或补齐全部 shared callers 的 desktop/web/native 运行时证据后再关闭。

历史问题与契约核对

  • OK-57251 → OK-59978:方向正确。 之前移除的是 generic Order ID;本 PR 仅在存在 orderSupportUrl 时恢复 provider order entry,且优先使用 cowSwapOrderId,符合 CoW pending order 没有链上 tx hash 的场景。服务端 server-service-swap 的 provider contract 也确认是 orderSupportUrl prefix + CoW provider order id。
  • 关联 Slack 线程明确拆成两条链路:本 PR 处理 explorer link;/state-txtxId 也应使用 cowSwapOrderId,但由另一条改动负责。不要把后者算作本 PR 已交付。
  • OK-60100 相邻回归风险:“入口显示”不等于编辑链路通过。选择外部/非本账户 recipient 后再次打开编辑器,仍需有 active-account editor context;该修复不在本 diff 内,合入/回归时需要确认依赖已进入 x

评分与验证边界

维度 分数 结论
Security 9.0 未发现敏感日志、鉴权或交易校验回退
Code Quality 6.5 两个明确状态/格式边界缺口
Architecture 7.0 provider identity 分层正确,但 native 修复越过平台边界
Completeness 6.5 CI 绿;缺 ref 生命周期、缩写边界和完整跨平台运行时证明

本轮核对了 Jira 描述/附件、关联 Slack 讨论、服务端字段来源、当前 diff、review threads 与远端 CI。截图能证明 recipient entry 在 iOS/desktop 出现,但元素存在本身不算完整通过;修复后至少需要验证:作用域切换不残留、外部 recipient 可重复编辑、CoW pending order 可打开正确 explorer,以及所有 SwapSmoothReveal 调用方在 desktop/web/native 的布局。

@haicongliao

Copy link
Copy Markdown
Contributor Author

Review 基于 37f9e3dc63 复核,三个问题已在 1bb0d474ca 修复并 resolve,thread 状态可能有时间差。当前 head 为 d7516e6791

1. [P2] 收款方必填状态跨作用域残留 — 已按 swap type + source account + from/to token network & contract 组成 scope key,作用域变化即重置为 false,同作用域内保留 quote refresh 的防闪烁保持。

2. [P2] 中等长度 Order ID 缩写重复字符minLength 设为 44(= leading 24 + trailing 20),15–43 位 ID 现在整串显示;已补 14/15/43/44/45/66/114 的边界用例。

3. [P2] 原生 Yoga 修复施加到 shared component — 绝对定位测量改为仅 platformEnv.isNative 生效,desktop/web 及 SwapSmoothReveal 其他调用方(cost savings、stock alert)回到原有正常流布局。

关于"pure-function 测试覆盖不到" — 这一条成立,本轮已补。把 scope key 构造与 ref 保持一起抽成 useSettledSwapRecipientRequired,用 renderHook 覆盖真实生命周期:报价刷新周期内保持、settled 结果变化时才跳变、切 Limit / 换 token / 换账户时重置、新作用域自己 settle 后重新建立。

这组测试确实抓到了纯函数测试覆盖不到的缺陷:hook 的 ref 初始值直接取了当轮 recipientRequiredNow,绕过 isAddressInfoReady 门槛,首次挂载且目标地址尚未解析时会误判。已改为中性初始值,首帧同样需要通过 settled + address-ready 门槛。

相关测试 68 passed。tsc 仍报 MarketHomeV2/layouts/MobileLayout.native.tsx:873,为 x#12842 引入的既有问题,与本 PR 文件无关(在干净 x 上加空白改动触发检查可复现)。

🤖 Addressed by Claude Code

@zhaono1
zhaono1 enabled auto-merge (squash) August 13, 2026 00:21
zhaono1
zhaono1 previously approved these changes Aug 13, 2026
auto-merge was automatically disabled August 13, 2026 15:09

Head branch was pushed to by a user without write access

@haicongliao
haicongliao force-pushed the fix/swap-order-id-and-recipient-entry branch from d7516e6 to f4e5a7f Compare August 13, 2026 15:09
@haicongliao

Copy link
Copy Markdown
Contributor Author

已 rebase 到最新 xb6f789ec64),冲突解决,当前 head f4e5a7f40c,10 个提交保持线性。

唯一冲突点值得说明:SwapSmoothReveal.tsx

#12862 独立修复了同一个原生测量问题,做法与本 PR 早期提交一致——对测量节点无条件加绝对定位。本 PR 在 review 反馈(P1 thread)后已改为仅 platformEnv.isNative 生效,因此冲突解决时保留了平台受限版本,而非回退成无条件版本:

这意味着 diff 中会看到本 PR 调整了 #12862 刚合入的这几行,属于按 review 要求收窄平台边界,不是回退其修复。JSDoc 措辞也同步对齐为「native 才脱离文档流」。

其余 upstream 改动无重叠:#12862 触及的 actions.ts / atoms.ts / Pro 系列组件与本 PR 文件不相交,本 PR 依赖的两个前提(切 tab 清空 quote 与重置 quoteEventCompletedswapProviderSupportReceiveAddressAtom)在新 x 上均未变更,已复核。

相关测试 68 passed。tsc 仍报 MarketHomeV2/layouts/MobileLayout.native.tsx:873,在最新 x 上依旧存在且与本 PR 文件无关。

🤖 Addressed by Claude Code

Comment thread packages/kit/src/views/Swap/hooks/useSwapAccount.utils.ts Outdated
@haicongliao
haicongliao force-pushed the fix/swap-order-id-and-recipient-entry branch from a5d35b5 to 46a89f2 Compare August 15, 2026 03:15
@haicongliao

Copy link
Copy Markdown
Contributor Author

Both issues addressed in 89d052825c.

P1 — Recipient scope adopting stale quotes: confirmed and fixed. Verified the premise: selectSwapPreviousActionableQuote in the quote-progress layer does intentionally keep the previous actionable quote selected while a new round is requesting, so raw currentQuoteRes truthiness was indeed a wrong settled signal. The pair-match gate now lives inside useSettledSwapRecipientRequired itself: the hook receives the raw selection atom and only treats it as settled for the scope when its fromTokenInfo/toTokenInfo match the currently selected pair (equalTokenNoCaseSensitive), and recipientRequiredNow is gated the same way. Account/tab identity stays covered by the scope key. Moving the gate into the hook (rather than the component) keeps the whole verdict chain covered by the lifecycle tests — added a case where the previous pair's quote is still selected on the renders right after a pair switch, both when the new pair has a target address and when it doesn't. Writing those tests also flushed out a mount-render hole (the ref initialized with an ungated verdict, bypassing the address-ready gate on first render); it now starts neutral and every adoption passes the resolver.

P2 — orderSupportUrl contract: verified, and the concat is now guarded. The field has genuinely carried two shapes over time: the founding #6504 code opened swapInfo.orderSupportUrl directly as a full URL, while today's backend sends a base to append the id to — confirmed live this week on a real CoW stock order, where the server returned https://explorer.cow.fi/bnb/search/ (trailing slash, nothing else) and the appended provider uid resolved on explorer.cow.fi. (ILimitOrder.orderSupportUrl is a different field on the limit-order item and keeps its full-URL contract, untouched by this PR.) Rather than assuming the prefix shape forever, the modal now builds the link via buildSwapHistoryOrderExplorerUrl, which appends only when the URL does not already carry the order id — both historical shapes produce a working link, with unit tests for each.

79 focused tests pass; lint/tsc green in an isolated worktree install.

🤖 Addressed by Claude Code

@originalix originalix left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@haicongliao

Automated code review found blocking issues

Reviewed commit 89d052825c98.

  • P2 · Pair matching still accepts stale quote events

View the full review report

@haicongliao

Copy link
Copy Markdown
Contributor Author

P2 (stale quote events) addressed in 6bfa6f6. Confirmed the premise: pair equality cannot distinguish a same-pair account switch, where the previous account's quote stays selected and its event stays registered as current until the new request is written. Adoption now requires an active-request proof computed from the same primitives the selection layer uses — isSwapQuoteFromCurrentEvent (event-id membership) AND isSwapQuoteRequestForCurrentInput (the request lock matching the current account/pair/amount, which rejects the pre-request transition window that event membership alone cannot). The pair-equality check stays as defense-in-depth. Added the requested hook test: a same-pair previous quote retained across an account switch is not adopted, and the verdict re-establishes once the new account's own round settles. 69 focused tests pass; lint/tsc green.

🤖 Addressed by Claude Code

@originalix originalix left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@haicongliao

Automated code review found blocking issues

Reviewed commit 6bfa6f6c313a.

  • P2 · Request-starting state still proves the retained quote

View the full review report

@haicongliao

Copy link
Copy Markdown
Contributor Author

Request-starting interval addressed in fb0c993. Verified the exact sequence in quoteAction: it resets swapQuoteEventTotalCountAtom (clearing the event id) and writes the new lock with actionLock: true before the async runQuoteEvent flips any loading flag, while the previous quote stays selected — so the no-event-id fallback plus the freshly matching lock did prove a stale quote in that window. The proof is now a single composed predicate in the quote-progress layer, isSwapQuoteProvenForCurrentRequest, which treats actionLock-with-no-event-id as unproven before checking event membership and the lock match. Unit tests cover the request-starting interval with a retained same-pair quote (the reported hole), previous-event rejection after the new event reports, streamed current-event adoption while still locked, and lock mismatch. 96 focused tests pass; lint/tsc green.

🤖 Addressed by Claude Code

@sidmorizon

Copy link
Copy Markdown
Contributor

@cursoragent review

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.

4 participants