feat: SHEEP CHOICE routing, cross-chain deposits, and any-token ZAP

One commit because the pieces do not compile apart: the shared copy, the tab
shell and the data layer all changed together, and splitting them further would
mean inventing intermediate states that never existed.

SHEEP CHOICE — the terminal's own swap. Quotes come from a solver that splits
one trade across several pools instead of forcing it down a single path, and
returns a ready-to-sign Settler transaction; the UI draws the split leg by leg
and scores every venue against one shared fee-free baseline, so the card that
says it pays most actually does. The Kyber transaction path is gone — Kyber is
read-only USD valuation now, and there is no Kyber calldata to sign.

BRIDGE — deposits from other chains over Relay, Across and the native portal,
priced side by side and sorted by what actually reaches you. No fee on any of
them. In-flight transfers get a countdown and survive a reload.

ZAP — add liquidity holding neither side of the pair; whatever needs swapping
is done in the same flow, with an optional stake-after step. Uniswap V2
liquidity now shows up under POSITIONS.

Swaps in flight are persisted, so a refresh mid-swap no longer loses the
transaction. Pair labels copy their token and pool addresses, and jump to
DexScreener.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
labrinyang
2026-07-23 00:01:52 +08:00
co-authored by Claude Opus 4.8
parent d73eaf873a
commit bca538e7e3
100 changed files with 10658 additions and 1638 deletions
+165 -51
View File
@@ -10,26 +10,39 @@ export const zh: typeof en = {
exceedsBalance: '超出余额',
bal: '余额',
max: 'MAX',
maxGasTip: 'MAX 会保留 {{amt}} {{sym}} 作源链 gas — 25/50/75% 按原始余额计算',
slipCustom: '自定义',
slipInvalid: '需大于 0',
close: '× 收起',
noMatch: '无匹配',
gasToken: '(gas)',
tokenSearch: '符号或地址…',
tokenResolving: '读取代币中…',
tokenNotErc20: '不是本链的 ERC-20',
},
hdr: {
pools: '池子',
positions: '仓位',
swap: '兑换',
epoch: 'epoch',
flip: '翻转',
bridge: '跨链',
blk: '块',
history: '历史',
historyTip: '活动与交易日志',
connect: '[ 连接 ]',
wrongChain: '[ 链错误 ]',
},
news: {
label: '更新',
title: '最近更新',
tip: '更新内容 — 本终端的最近改动',
freshTip: '最近两天有 {{n}} 条更新',
empty: '暂无更新',
},
app: {
wrongNetwork: '!! 网络错误 — 本终端只在 Robinhood Chain (4663) 上写入',
switch: '切换',
tagline: 'LP TERMINAL v0.2 · 精确授权 · 链上实时读取',
keys: '快捷键: [1] 池子 [2] 仓位 [3] 兑换 [4] 限价',
keys: '快捷键: [1] 池子 [2] 仓位 [3] 兑换 [4] 限价 [5] 跨链',
blockscout: 'blockscout↗',
reloaded: '已刷新 — 此标签页打开期间部署了新版本',
},
@@ -52,7 +65,8 @@ export const zh: typeof en = {
},
lang: { label: 'lang:' },
rbar: {
inRange: '区间内 · 已穿过 {{pct}}% · 带宽 ±{{band}}%',
inRange: '区间内',
inRangeTip: '已穿过 {{pct}}% · 带宽 ±{{band}}%',
outRise: '超出区间 · 价格需上涨 {{pct}}% 才能回到区间',
outFall: '超出区间 · 价格需下跌 {{pct}}% 才能回到区间',
orderFilled: '订单已完全成交 · 100% 持有 {{sym}} — 提取以锁定',
@@ -61,6 +75,7 @@ export const zh: typeof en = {
orderWaitFall: '订单等待中 · 价格需下跌 {{pct}}% 开始成交',
toLow: '→ 下界',
fromHigh: '上界 ←',
allOf: '100% {{sym}}',
flipTip: '翻转价格方向',
ticksTip: 'tick [{{lo}}, {{hi}}] · 当前 {{tick}}',
},
@@ -72,18 +87,13 @@ export const zh: typeof en = {
approve: '授权 {{sym}}',
received: '到账 {{amt}} UP',
swapToEth: '兑换 → ETH',
hintKyberMinOut: '触发 kyber 最小到账保护 — 报价到发送之间价格变了;调高滑点或重试',
hintSwapMinOut: '触发最小到账保护 — 报价到发送之间价格变了;调高滑点或重试',
hintPS: '价格滑点 — 池价超出容差,重试即可',
hintV2Ratio: 'v2 池比例在联动后变了 — 重新输入金额再试',
hintSTF: '代币转账失败 — 检查授权/余额',
hintNP: '未获此 token id 的授权',
hintDeadline: '超过截止时间 — 重试',
},
kyber: {
routerMismatch: '路由地址不符 {{addr}} — 已中止',
badValue: '交易 value 异常 {{got}}(应为 {{want}})— 已中止',
buildDeviates: '构建结果偏离报价(in {{in}}, out {{out}})— 已中止',
},
pools: {
searchPlaceholder: '搜索交易对 / 符号 / 代币 0x… / 池 0x… ( / )',
protoAll: '全部',
@@ -114,6 +124,14 @@ export const zh: typeof en = {
thFeeApr: '费率 APR',
thRewards: '排放奖励',
sortTip: '点击排序',
addrTip: '地址 — 点击复制',
addrHint: '点击复制',
addrPool: '池子',
addrCopyTip: '复制完整地址',
addrCopied: '已复制',
addrFailed: '复制失败',
addrExplorerTip: '在浏览器中打开',
dsTip: '在 dexscreener 打开该交易对(新标签页)',
mineDotTip: '你在此池有仓位',
gauge: 'gauge',
killed: '已停用',
@@ -140,6 +158,8 @@ export const zh: typeof en = {
fundZapTip: '只持有一边?zap 先把合适的份额换成另一种代币,再存入 — 逐步执行',
range: '区间',
full: '全区间',
more: '更多',
moreTip: '单边区间、自定义 %、精确价格或原始 tick',
pctCustom: '± % …',
pctCustomTip: '对称区间,自定义百分比',
above: '↑ 上方',
@@ -180,15 +200,19 @@ export const zh: typeof en = {
stepAddV2: '添加 v2 {{pair}} 流动性',
stepAdd: '添加 {{pair}} 流动性',
stepMint: '铸造 {{kind}} {{pair}} [{{lo}},{{hi}}]',
stakeAfter: '添加后质押',
stakeAfterTip: '添加后直接继续质押新仓位以赚取 UP —— 该选择会被记住,直到你更改',
},
zap: {
zapIn: 'ZAP 输入',
ethTip: '原生 ETH — 第 1 步先包装成 WETH',
slip: '滑点',
slipHint: '兑换腿最小到账 · 存入最小值按 1% 带边计算',
solving: '> 正在通过 kyber 求解拆分',
solving: '> 正在比较 Uniswap / UP33 直连报价',
cantPlan: '> 无法规划: {{err}}',
planTitle: 'ZAP 计划',
refreshPlan: '刷新计划',
refreshTip: '计划每 {{s}} 秒自动刷新,{{n}} 次后暂停 — 点击立即刷新',
split: '拆分',
keepAll: '全部保留 {{amt}} {{sym}}',
keepSwap: '保留 {{keep}} + 兑换 {{swap}} {{sym}}',
@@ -197,16 +221,29 @@ export const zh: typeof en = {
swapRow: '兑换',
swapMin: '最少 {{amt}} @ {{slip}}%',
impact: '价格影响 {{pct}}%',
impactOff: '价格影响不可用(美元标价不可靠 — 以最小到账为准)',
impactOff: '价格影响不可用 — 请选择滑点',
chooseSlippage: '执行前请选择滑点',
// 不用「手续费」:本 catalog 里它 15 处全指池子手续费,同面板下方就是
// 「费率 APR」——裸写「手续费」会被读成池子费,等于没披露
terminalFee: '终端费 {{pct}}%',
via: '经 {{route}}',
pickOther: '其他',
swapN: '兑换 {{n}}',
outsideSplit: '兑换 {{a0}} {{sym}} → {{sym0}} + {{a1}} {{sym}} → {{sym1}}',
outsideOne: '全部 {{amt}} {{sym}} 兑换为 {{outSym}}',
outsideSd: '输入币不在交易对内 — 两侧均按存入比例买入',
depositRow: '存入',
dustSd: '预计零头 {{dust}} 留在钱包(不会发送)',
actualSd: '兑换侧按实际到账存入,不按报价',
bandWarn: '兑换影响 {{impact}}% 相对 ±{{band}}% 区间过大 — 会产生零头;建议放宽区间或减少数量',
run: '⚡ 执行 ZAP',
runTx: '⚡ 执行 ZAP{{n}} 笔交易',
runSteps: '⚡ 执行 ZAP{{n}} ',
runHint: '任一步失败即停 — 资金都在你钱包里,不会悬空',
halted: '在第 {{n}} 步停止 — 已完成的步骤都是普通钱包余额。解决后重新执行(会按当前余额重新规划),或用双币模式收尾。',
haltedSwap:
'在第 {{n}} 步(兑换)停止:最新报价已低于 {{slip}}% 滑点下限 — 价格动了。请提高滑点以增加执行成功率(AUTO 已上调为 {{next}}%),然后重新执行 — 会按当前余额重新规划。',
haltedDeposit:
'在第 {{n}} 步(存入)停止:交易在途时池价越过了 {{band}}% 存入容差。直接重试即可 — 最小值按实时价格重算;已兑换的代币安全在钱包。',
done: '✓ zap 完成 — 流动性已生效。',
replanFailed: 'zap 重新规划失败: {{err}}',
orderGrows: '这会加大未平的区间订单 — 同区间、同均价、更大规模。',
@@ -214,11 +251,11 @@ export const zh: typeof en = {
errNotInPool: '该代币不在此池中',
errNoReserves: '池子无储备 — 用双币模式初始注入',
errNoPrice: '池价格不可用',
errZeroQuote: 'kyber 报价为零 — 池/路由太薄',
errNoRoute: '对手代币没有 kyber 路由',
errZeroQuote: '直连报价为零 — 池/路由太薄',
errNoRoute: '对手代币没有直连路由',
errQuoteFailed: '{{protocols}} 直连报价比较失败',
errTooSmall: '数量对该区间来说太小',
stWrap: '包装 {{amt}} ETH → WETH',
stApproveKyber: '授权 {{sym}} → kyber 路由',
stApproveSpender: '授权 {{sym}} → {{spender}}',
spenderRouter: '路由',
spenderNpm: '仓位管理器',
@@ -226,11 +263,9 @@ export const zh: typeof en = {
stIncrease: '加仓 #{{id}}',
stMint: '铸造仓位',
stAddLiquidity: '添加流动性',
stStake: '质押仓位 → 赚取 UP',
halt: 'zap 已停止 — {{msg}}',
haltRequote: '重新报价失败: {{err}}',
haltPriceMoved: '价格自预览后变动(现 {{now}},预览 {{prev}})— 请核对后重新执行',
haltTokenOut: '路由 tokenOut {{addr}} 不是池子对手代币 — 已中止',
haltNoTransfer: '兑换成功但未找到对手代币转账 — 请先排查再重试',
haltNothing: '没有可存入的数量',
haltDepositSmall: '当前价格下存入量对该区间太小(已换的代币在你钱包里)',
stakeHint: 'zap 完成 — 质押仓位开始赚 UP',
@@ -247,7 +282,7 @@ export const zh: typeof en = {
lpValueSub: '{{cl}} CL · {{v2}} v2 · {{staked}} 已质押',
lpValueFees: '未领手续费 ≈{{usd}}',
lpValueUnpriced: '{{n}} 个未定价',
pendingUp: '待领 UP',
pendingRewards: '待领奖励',
upPerDay: '+{{n}} UP/天',
upPerDayUsd: '(≈{{usd}}/天)',
claimAll: '全部领取({{n}} 笔)',
@@ -286,20 +321,19 @@ export const zh: typeof en = {
withdrawTip: '移除 100% 流动性并领取全部(双击确认)',
value: '价值',
noAnchor: '— 无价格锚',
holds: '持有',
pendingUpK: '待领 UP',
fees: '未领手续费',
levyNote: '10% 未质押抽成已扣除)',
earning: '收益中',
earnEmit: '{{n}} UP/天',
earnUsdDay: '({{usd}}/天)',
earnApr: 'APR {{apr}}',
earnEmit: '{{n}} UP/天',
earnUsdDay: '{{usd}}/天',
earnApr: 'APR {{apr}}',
earnShareStaked: '占质押流动性 {{share}}',
earnShareGauge: '占 gauge {{share}}',
earnOutStaked: '0 — 超出区间',
earnOutStakedTip: '质押 CL 只在价格处于区间内时发 UP',
earnEnded: '0 — 本 epoch 无 UP 排放流入',
earnFeeApr: '费率 APR',
earnFeeApr: '费率 APR',
earnShareActive: '占活跃流动性 {{share}}',
earnSharePool: '占池子 {{share}}',
earnWhileInRange: '区间内有效',
@@ -308,6 +342,11 @@ export const zh: typeof en = {
earnEmpty: '空仓 — 仅剩未领手续费',
earningStaked: '收益中(质押)',
earningWallet: '收益中(钱包)',
// same-pool aggregation header (ClPoolGroup)
groupPositions: '{{n}} 个仓位',
groupStakedN: '{{n}} 个质押',
groupStakedShare: '质押占比',
groupIdle: '当前无收益',
orderRow: '区间订单 {{sell}} → {{buy}}:',
orderLockIn: '提取 → 锁定 {{sym}}',
orderClose: '立即平仓(部分成交)',
@@ -336,9 +375,9 @@ export const zh: typeof en = {
v2StakeAll: '全部质押',
v2UnstakeAll: '全部解押',
v2ClaimFees: '领取手续费',
v2Total: '合计',
v2WalletLp: '钱包 LP',
v2StakedLp: '质押 LP',
v2Lp: 'LP',
v2Staked: '{{n}} 已质押',
v2Wallet: '{{n}} 在钱包',
v2Claimable: '可领手续费',
v2RemoveOf: '按钱包 LP 计',
v2RemoveStakedNote: '— 质押的 LP 需先解押',
@@ -363,42 +402,67 @@ export const zh: typeof en = {
market: '市价',
limit: '限价 · 挂 LP 卖出',
limitTip: '用单边 LPCL 区间订单)卖出代币',
chainTip: '网络标识 — 本终端的所有报价与交易均在 Robinhood Chainid {{id}})上执行',
loadingTokens: '> 加载代币列表…',
from: '卖出',
to: '买入',
sell: '卖出',
buy: '买入',
balTip: '钱包余额 — 点击填入最大可用',
flipTip: '互换方向',
rate: '汇率: 1 {{a}} = {{n}} {{b}}',
rateV: '1 {{a}} = {{n}} {{b}}',
rateTip: '点击反转',
quotes: '报价',
enterAmount: '输入数量以获取两路报价。',
kyberAgg: 'KYBER 聚合',
up33Native: 'UP33 原生',
route: '路由',
details: '明细',
kRate: '汇率',
kMinReceived: '最少到账',
// 「价格影响」仍是桥接那一行的标签,那里是到账/发送的带符号差值。兑换这边叫
// 「成交成本」:price impact 惯例上指不含费的规模冲击,而这个数是刻意把费用
// 折进去的,沿用旧词只会继续误导。
kImpact: '价格影响',
kCost: '成交成本',
costTip: '本笔交易的全部成本 — 价格推动、池子手续费、代币转账税与终端手续费合计,相对于「不受规模与费用影响时能拿到的价格」',
kTerminalFee: '终端手续费',
terminalFeeOfWhich: '我们收取的 {{pct}}% — 已包含在上面的成交成本里,不是额外加收',
kFee: '费用',
ctaEnterAmount: '输入数量',
ctaQuoting: '询价中…',
ctaRefreshQuote: '↻ 刷新报价',
ctaPending: '交易确认中',
pendingSaveFailed: '交易已发送,但待确认状态无法保存 — 请保持此页面开启',
pendPending: '待确认',
pendConfirmed: '已确认',
pendFailed: '失败',
pendStale: '未确认',
viewTx: '查看',
pendingDismiss: '清除此行(不影响链上交易)',
refreshTip: '报价每 {{s}} 秒自动刷新 — 点击立即刷新',
solverRefreshTip: '报价每 {{s}} 秒刷新;SHEEP CHOICE 自动刷新 3 次后暂停 — 点击立即刷新',
usdDeltaTip: '买入侧与卖出侧的美元价值差(按场馆现价)— 已包含价格影响与费用',
solverRoute: 'SHEEP CHOICE',
solverDown: 'SHEEP CHOICE 离线 — 仅直连场所',
stSwapSolver: '兑换 {{amt}} {{a}} → {{b}} [sheep choice]',
uniswapDirect: 'UNISWAP 直连',
up33Direct: 'UP33 直连',
quoteImpact: '影响 {{pct}}%', // 桥接卡片
quoteCost: '成本 {{pct}}%',
noDirectPool: '该交易对没有直连池',
quoteFailed: '直连报价失败',
tokenListFallback: 'Uniswap 索引不可用 — 代币列表仅显示已验证的发现结果。',
tokenListFailed: 'Uniswap 代币列表失败: {{err}}',
up33TokenListFailed: 'UP33 池注册表失败: {{err}}',
unavailable: '不可用: {{err}}',
gas: 'gas {{usd}}',
best: '最优',
viaV2: '经 v2 {{kind}} {{fee}}%',
stable: '稳定',
volatile: '波动',
viaCl: '经 CL {{fee}}% ts{{ts}}',
bpsVsKyber: '对比 kyber {{bps}} bps',
noNativePool: '该交易对没有直接的 UP33 池',
needsWeth: '需要 WETH — 先包装',
slippage: '滑点',
refresh: '↻ 刷新',
execVia: '通过 {{route}} 执行 →',
noRoute: '无路由',
minReceived: '最少到账: {{amt}} {{sym}} @ {{slip}}%',
nativeWethNote: '原生路由到账的是 WETH(不是 ETH)',
chooseSlippage: '请选择滑点',
wrapBtn: '包装 {{amt}} ETH',
unwrapBtn: '解包 {{amt}} WETH',
wrapNote: '1:1,无费用 — WETH9 {{addr}}',
stWrap: '包装 {{amt}} ETH → WETH',
stUnwrap: '解包 {{amt}} WETH → ETH',
stSwapKyber: '兑换 {{amt}} {{a}} → {{b}} [KYBER]',
stSwapV2: '兑换 {{amt}} {{a}} → {{b}} [UP33 v2]',
stSwapCl: '兑换 {{amt}} {{a}} → {{b}} [UP33 CL ts{{ts}}]',
errNoQuote: 'kyber 报价不可用',
errNeedsWeth: 'UP33 原生路由需要 WETH — 先把 ETH 包装成 WETH(选择 ETH→WETH',
stSwapDirect: '兑换 {{amt}} {{a}} → {{b}} [{{route}}]',
errQuoteMoved: '最新报价低于页面显示的最小到账 — 请核对后重试',
receivedBelowMinimum: '确认到账 {{got}} 低于最小值 {{min}} — 请先排查再重试',
},
limit: {
intro: '> 做挂单方,不做吃单方:在市场价之外挂单边 LP — 不付交易费,成交过程还赚手续费',
@@ -470,4 +534,54 @@ export const zh: typeof en = {
orderLive: '区间订单 #{{id}} 已挂出 — {{sym}} 升值时逐步成交',
track: '在仓位页跟踪',
},
bridge: {
chainTip: '选择链 — Robinhood Chain (id {{id}}) 永远是路径的一端',
homeTag: '主链',
balFailed: '余额读取失败 — 点击重试({{err}})',
from: '从',
to: '到',
flipTip: '反转方向',
route: '路由',
etaS: '约{{s}}秒',
etaMin: '约{{m}}分钟',
minShort: '最少 {{amt}} {{sym}}',
quoteFailed: '报价失败',
kEta: '预计时间',
kProviderCost: '桥成本',
impactTip: '到账 vs 发送的全部损耗 — 桥成本 + 终端费。点击展开',
terminalFeeTip: '终端费 {{pct}}% — 在报价内收取,不产生额外转账',
impactWarn: '总损耗 {{pct}}% 偏高 — 当前路由流动性薄。可对比各家报价、减小金额,或改用 USDG 出入金',
noRoute: '无可用路由',
execVia: '通过 {{provider}} 跨链 →',
stDeposit: '跨链 {{amt}} {{sym}}: {{from}} → {{to}}',
stgApprove: '授权',
stgDeposit: '发送',
stgSent: '已发送',
sentNote: '入金已确认 — 到账进度见上方 PENDING',
filled: '跨链完成 — 资产已到目标链',
refunded: '未成交 — 已在源链退款(扣除 gas)',
fillFailed: '目标链成交失败 — 请用入金交易哈希到桥方浏览器核查',
quoteExpired: '报价已过期 — 刷新报价后重试',
switchFailed: '切换网络被拒绝: {{err}}',
tokenTip: '选择代币 — 仅列出各桥实际支持的同代币路由',
nRoutes: '{{n}} 条路由',
nRoute1: '1 条路由',
discovering: '正在发现路由…',
noTokens: '该链对当前无可桥代币',
discoverFailed: '路由发现失败',
retry: '↻ 重试',
canonical: 'CANONICAL',
canonicalTip: '官方 Arbitrum 桥 — 1:1 无损,仅付 L1 gas;实测到账约 10 分钟。价格最优即排第一,不论速度',
pendingTitle: 'PENDING',
pendingTracked: '入金已发送 — 预计 {{eta}} 到账,进度见 BRIDGE PENDING',
etaLeft: '剩 {{eta}}',
verifying: '核对中',
pFilled: '已到账',
pRefunded: '已退款',
pFailed: '失败',
pStale: '仍在等待',
stale: '等待超过 1 小时 — 可手动重查,或用入金交易哈希到浏览器核实',
recheckTip: '立即查询状态',
dismissTip: '从列表移除(不影响转账本身)',
},
}