diff --git "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/api-providers-status.md" "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/api-providers-status.md" index 554daa0db..f2b7413af 100644 --- "a/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/api-providers-status.md" +++ "b/docs/white-book/04-\346\234\215\345\212\241\347\257\207/01-\346\234\215\345\212\241\346\236\266\346\236\204/api-providers-status.md" @@ -2,7 +2,7 @@ > 记录各链 API Provider 的可用性状态、已知问题和配置建议。 > -> 最后更新: 2026-01-06 (PR #174) +> 最后更新: 2026-01-07 (PR #176) ## Provider 状态概览 @@ -16,7 +16,7 @@ | **BSC** | `etherscan-v2` | `api.etherscan.io` | ❌ 需付费 | ❌ | ❌ | BSC 链在 Etherscan V2 中需要付费 Plan | | **BSC** | `bscwallet-v1` | `walletapi.bfmeta.info` | ⚠️ 部分 | ✅ | ❌ | 交易历史查询返回 `NOTOK`,后端数据问题 | | **Tron** | `tronwallet-v1` | `walletapi.bfmeta.info` | ✅ 正常 | ✅ | ✅ | PR #174 已修复 Schema 问题 | -| **BTC** | `btcwallet-v1` | `walletapi.bfmeta.info` | ❌ 故障 | ❌ | ❌ | 后端返回 404,建议使用 Mempool | +| **BTC** | `btcwallet-v1` | `walletapi.bfmeta.info` | ✅ 正常 | ✅ | ✅ | PR #176 修复 Schema 解包问题 | | **BTC** | `mempool-v1` | `mempool.space` | ✅ 正常 | ✅ | ✅ | 推荐使用的标准 BTC 数据源 | ## 已知问题与解决方案 @@ -31,9 +31,10 @@ - **配置**: 需要在环境变量中设置 `ETHERSCAN_API_KEY`。 - **注意**: BSC 链即使有 Key,普通免费 Plan 也不支持通过统一入口访问,需要使用独立的 BscScan API(V1 已弃用,需迁移到 V2)。 -### 3. BTC WalletAPI 404 -- **现象**: 所有 `/wallet/btc/*` 路径均返回 404 Not Found。 -- **建议**: 在 `default-chains.json` 中保持 `mempool-v1` 为首选,或移除 `btcwallet-v1` 配置。 +### 3. BTC WalletAPI (已修复) +- **原现象**: Schema 解析失败,数据返回空。 +- **修复**: PR #176 添加 `{ success, result }` 包装解包逻辑。 +- **状态**: 正常可用,支持余额查询和交易历史。 ## 配置建议 diff --git a/src/services/chain-adapter/providers/__tests__/btcwallet-provider.test.ts b/src/services/chain-adapter/providers/__tests__/btcwallet-provider.test.ts index cbe9aef0c..95c695a3a 100644 --- a/src/services/chain-adapter/providers/__tests__/btcwallet-provider.test.ts +++ b/src/services/chain-adapter/providers/__tests__/btcwallet-provider.test.ts @@ -39,7 +39,8 @@ describe('BtcWalletProvider', () => { const body = JSON.parse(String(init?.body ?? '{}')) expect(body.method).toBe('GET') expect(body.url).toContain(`/api/v2/address/${address}`) - return { ok: true, json: async () => ({ balance: '10', unconfirmedBalance: '-2' }) } + // API returns { success, result } wrapper + return { ok: true, json: async () => ({ success: true, result: { balance: '10', unconfirmedBalance: '-2' } }) } }) const provider = new BtcWalletProvider(entry, 'bitcoin') @@ -56,21 +57,24 @@ describe('BtcWalletProvider', () => { return { ok: true, json: async () => ({ - balance: '0', - transactions: [ - { - txid: 'tx1', - blockHeight: 100, - confirmations: 1, - blockTime: 1700000000, - vin: [{ addresses: [address], value: '5000' }], - vout: [{ addresses: ['bc1qother'], value: '3000' }, { addresses: [address], value: '1900' }], - }, - ], + success: true, + result: { + balance: '0', + transactions: [ + { + txid: 'tx1', + blockHeight: 100, + confirmations: 1, + blockTime: 1700000000, + vin: [{ addresses: [address], value: '5000' }], + vout: [{ addresses: ['bc1qother'], value: '3000' }, { addresses: [address], value: '1900' }], + }, + ], + }, }), } } - return { ok: true, json: async () => ({ balance: '0' }) } + return { ok: true, json: async () => ({ success: true, result: { balance: '0' } }) } }) const provider = new BtcWalletProvider(entry, 'bitcoin') diff --git a/src/services/chain-adapter/providers/btcwallet-provider.ts b/src/services/chain-adapter/providers/btcwallet-provider.ts index ade95255e..03c5ca36e 100644 --- a/src/services/chain-adapter/providers/btcwallet-provider.ts +++ b/src/services/chain-adapter/providers/btcwallet-provider.ts @@ -9,6 +9,12 @@ const BlockbookErrorSchema = z.looseObject({ error: z.string(), }) +const WalletApiWrapperSchema = (innerSchema: T) => + z.looseObject({ + success: z.boolean(), + result: innerSchema, + }) + const BlockbookVinSchema = z.looseObject({ addresses: z.array(z.string()), value: z.string(), @@ -179,11 +185,19 @@ export class BtcWalletProvider implements ApiProvider { throw new Error(err.data.error) } - const parsed = schema.safeParse(json) - if (!parsed.success) { - throw new Error('Invalid API response') + // Unwrap { success, result } wrapper from walletapi.bfmeta.info + const wrapped = WalletApiWrapperSchema(schema).safeParse(json) + if (wrapped.success && wrapped.data.success) { + return wrapped.data.result + } + + // Fallback: direct parse (for mempool or other non-wrapped APIs) + const direct = schema.safeParse(json) + if (direct.success) { + return direct.data } - return parsed.data + + throw new Error('Invalid API response') } }