Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
32ce099
feat: asset picker v2 — card grid + 2-step chain→asset flow
BitHighlander May 20, 2026
0b2102b
fix: asset picker — real logos + correct provider color keys
BitHighlander May 20, 2026
934f068
feat: asset picker v2 — square tiles, 64px icons, full CAIP, network-…
BitHighlander May 20, 2026
5efdd60
feat: sidebar chain click drills inline instead of full-screen AssetPage
BitHighlander May 20, 2026
0a74e9d
feat: token asset page, swap pre-selection, NEAR Intents ERC-20 fixes
BitHighlander May 20, 2026
50dc82d
fix: NEAR Intents pending swap registration, balance check, picker TS…
BitHighlander May 21, 2026
dbf4d20
fix: EIP-1559 signing broken on Base/Arbitrum/Avalanche (chainId >= 256)
BitHighlander May 21, 2026
7d5ee57
chore(deps): bump hdwallet submodule to include HID PID 0x0002 fix
BitHighlander May 21, 2026
3716cf0
fix: filter swap assets and cached balances by firmware version
BitHighlander May 22, 2026
e6f4ea6
fix: address PR review findings (tx:confirmed refresh, private mode l…
BitHighlander May 22, 2026
fee3f93
feat(hive): wire Hive RPC into vault + add integration handoff doc
BitHighlander May 22, 2026
4f63087
fix(event-stream): exponential backoff + log throttle to prevent serv…
BitHighlander May 22, 2026
219a715
chore(deps): bump hdwallet to master (TRON/TON/Zcash/Hive/BIP85)
BitHighlander May 22, 2026
e3a0704
feat(ui): colorful action buttons + Zcash privacy shortcut on dashboard
BitHighlander May 22, 2026
062c36e
fix(ui): clip orbital overflow to unblock action button clicks
BitHighlander May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 215 additions & 0 deletions docs/handoff-hive-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# Handoff: Hive Integration

## Status Summary

| Layer | Status | Notes |
|---|---|---|
| Firmware (`alpha`) | ✅ Done | `hive.c`, `fsm_msg_hive.h`, wired into `messagemap.def` + CMakeLists |
| device-protocol | ✅ Done | `messages-hive.proto`, wire IDs 1600–1603 in `alpha` |
| hdwallet | ❌ Not started | No Hive in `modules/hdwallet/` |
| Vault RPC | ❌ Not started | `feature/hive` branch only has submodule bumps, no TS code |
| Pioneer | ❌ Not started | No Hive in pioneer-caip, pioneer-coins, pioneer-balance, or pioneer-network |

---

## Chain Facts

| Field | Value |
|---|---|
| Symbol | `HIVE` |
| SLIP44 | `1275` |
| Curve | `secp256k1` |
| Derivation path | `m/44'/1275'/0'/0/0` |
| Address format | `STM`-prefixed base58 (Graphene/EOS encoding, RIPEMD checksum) |
| Chain ID (mainnet) | `beeab0de00000000000000000000000000000000000000000000000000000000` (32 bytes) |
| CAIP networkId | `hive:beeab0de` (first 8 hex chars of chain_id) |
| CAIP assetId | `hive:beeab0de/slip44:1275` |
| Decimals | `3` (milliHIVE) |
| Also supports | HBD (Hive Backed Dollars), same path/curve |
| Broadcast endpoint | Hive RPC nodes: `https://api.hive.blog`, `https://anyx.io` |

---

## What the Firmware Does

`hive.c` implements:
- `hive_getPublicKey()` — returns `STM`-prefixed base58 public key
- `hive_signTx()` — signs Graphene binary-serialized transfer transactions

`HiveSignTx` proto fields: `chain_id`, `ref_block_num`, `ref_block_prefix`, `expiration`, `from`, `to`, `amount`, `decimals`, `asset_symbol`, `memo`

Response is a 65-byte recoverable secp256k1 signature + full serialized tx bytes.

---

## Work Remaining

### 1. hdwallet — Add Hive wallet adapter
**Repo**: `modules/hdwallet/`
**Reference impl**: look at `packages/hdwallet-keepkey/src/solana.ts` or `tron.ts`

Files to add/edit:
- `packages/hdwallet-core/src/hive.ts` — types: `HiveGetPublicKey`, `HiveSignTx`, `HiveSignedTx`, wallet interface
- `packages/hdwallet-core/src/index.ts` — re-export Hive types
- `packages/hdwallet-keepkey/src/hive.ts` — wire `hiveGetPublicKey()` and `hiveSignTx()` through to firmware via USB transport
- `packages/hdwallet-keepkey/src/keepkey.ts` — add Hive capability flags + register handlers

The firmware proto is already in `deps/device-protocol`. The keepkey transport just needs to call `HiveGetPublicKey` (MessageType 1600) and `HiveSignTx` (MessageType 1602).

### 2. Vault RPC — Wire hdwallet calls into Electrobun RPC
**File**: `projects/keepkey-vault/src/bun/index.ts`
**Branch**: `feat/hive` (current main tree)

Pattern — copy from `tonGetAddress` / `tonSignTx` block (~line 1376):
```typescript
hiveGetPublicKey: async (params) => {
const addr = await engine.wallet.hiveGetPublicKey(params)
if (addr) cacheAddress('hive', JSON.stringify(params.addressNList || []), addr)
return addr
},
hiveSignTx: async (params) => {
return await engine.wallet.hiveSignTx(params)
},
```

Also add to `src/shared/rpc-schema.ts` (request/response types).

### 3. Pioneer — Add Hive chain support
**Repo**: `/Users/highlander/WebstormProjects/keepkey-stack/projects/pioneer`
**Reference**: `docs/coin-addition/coin-addition-guide.md` (UTXO-style but use Hive-specific values)

Files to edit (in order):

#### pioneer-caip `modules/pioneer/pioneer-caip/src/data.ts`
```typescript
// Chain enum
Hive = 'HIVE',

// BaseDecimal
HIVE: 3,

// ChainToCaip
'HIVE': 'hive:beeab0de/slip44:1275',

// ChainToNetworkId
'HIVE': 'hive:beeab0de',

// shortListSymbolToCaip
HIVE: 'hive:beeab0de/slip44:1275',

// shortListNameToCaip
hive: 'hive:beeab0de/slip44:1275',
```

#### pioneer-coins `modules/pioneer/pioneer-coins/src/paths.ts`
```typescript
// blockchains array
'hive:beeab0de',

// getPaths()
if (blockchains.indexOf('hive:beeab0de') >= 0) {
output.push({
note: "Hive Default",
type: "address",
networks: ['hive:beeab0de'],
script_type: "p2pkh",
available_scripts_types: ['p2pkh'],
addressNList: [0x80000000 + 44, 0x80000000 + 1275, 0x80000000 + 0],
addressNListMaster: [0x80000000 + 44, 0x80000000 + 1275, 0x80000000 + 0, 0, 0],
curve: 'secp256k1',
showDisplay: false,
})
}
```

#### pioneer-nodes `modules/pioneer/pioneer-nodes/src/seeds.ts`
Hive uses its own RPC API (not Blockbook). Add a custom node entry pointing to `https://api.hive.blog` for balance/broadcast.

#### pioneer-discovery `modules/pioneer/pioneer-discovery/src/generatedAssetData.json`
```json
"hive:beeab0de/slip44:1275": {
"symbol": "HIVE",
"name": "Hive",
"chainId": "hive:beeab0de",
"assetId": "hive:beeab0de/slip44:1275",
"decimals": 3,
"isNative": true,
"type": "native"
}
```

#### pioneer-types `modules/pioneer/pioneer-types/src/pioneer.ts`
```typescript
// availableChainsByWallet[WalletOption.KEEPKEY]
Chain.Hive,
```

#### pioneer-network / pioneer-balance / pioneer-signer
Hive has its own JSON-RPC API (`condenser_api.get_accounts` for balance, `condenser_api.broadcast_transaction` for broadcast). There is no existing pioneer-network module for Hive — a new `@pioneer-platform/hive-network` package is needed, OR balance/broadcast can be handled inline in pioneer-balance and broadcast.controller.ts with direct HTTP calls to `https://api.hive.blog`.

**Simplest path**: inline HTTP calls rather than a new package, since Hive's API is simple:
```typescript
// Balance
const res = await fetch('https://api.hive.blog', {
method: 'POST',
body: JSON.stringify({ jsonrpc:'2.0', method:'condenser_api.get_accounts', params:[['USERNAME']], id:1 })
})
const [acct] = (await res.json()).result
const balance = parseFloat(acct.balance) // "1.234 HIVE"

// Broadcast
const res = await fetch('https://api.hive.blog', {
method: 'POST',
body: JSON.stringify({ jsonrpc:'2.0', method:'condenser_api.broadcast_transaction', params:[signedTx], id:1 })
})
```

#### broadcast.controller.ts `services/pioneer-server/src/controllers/broadcast.controller.ts`
Add a `HIVE_MAP`:
```typescript
const HIVE_MAP: { [key: string]: string } = {
'hive:beeab0de': 'hive',
}
```

---

## Transaction Construction (Vault → Pioneer → Broadcast)

Hive transactions use Graphene binary serialization. The firmware already handles this — `HiveSignedTx.serialized_tx` is the complete signed transaction bytes ready to broadcast.

To broadcast: POST `serialized_tx` (hex-encoded) to `condenser_api.broadcast_transaction` on a Hive RPC node.

The signed tx format is: `[ref_block_num(2)] [ref_block_prefix(4)] [expiration(4)] [op_count(1)] [op_type(2)] [transfer_body] [extensions(1=0)] [sig_count(1)] [sig(65)]`

---

## Suggested Work Order

1. **hdwallet** — add Hive types + keepkey transport wiring (1–2 hours)
2. **Vault RPC** — add `hiveGetPublicKey` + `hiveSignTx` RPC handlers (30 min)
3. **Pioneer caip/coins/discovery** — CAIP data, paths, asset metadata (1 hour)
4. **Pioneer balance** — inline Hive API call for `get_accounts` (1 hour)
5. **Pioneer broadcast** — inline `broadcast_transaction` call (30 min)
6. **End-to-end test** — get address → sign transfer → broadcast on mainnet

---

## Reference Implementations

| Coin | Pattern to follow | Why |
|---|---|---|
| TON | `hive.ts` in hdwallet-keepkey | Non-EVM, secp256k1, custom serialization |
| Cosmos | address derivation, secp256k1 | Similar BIP44 path structure |
| TRON | broadcast inline in pioneer | Simple HTTP broadcast without dedicated network module |

---

## Worktree Locations

| Repo | Branch | Path |
|---|---|---|
| keepkey-vault-v11 | `feat/hive` | `/Users/highlander/WebstormProjects/keepkey-stack/projects/keepkey-vault-v11` (main tree) |
| keepkey-vault-v11 | `feature/hive` | `/Users/highlander/WebstormProjects/keepkey-stack/projects/keepkey-vault-v11-hive` |
| keepkey-firmware | `alpha` (Hive included) | `/Users/highlander/WebstormProjects/keepkey-stack/projects/keepkey-firmware` |
| pioneer | `release/v1.3.73` | `/Users/highlander/WebstormProjects/keepkey-stack/projects/pioneer` |
2 changes: 1 addition & 1 deletion modules/device-protocol
2 changes: 1 addition & 1 deletion modules/hdwallet
2 changes: 1 addition & 1 deletion modules/keepkey-firmware
Submodule keepkey-firmware updated 113 files
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Pioneer Bug: NEAR Intents ERC-20 — double `0x` prefix on `txParams.to`

**Date**: 2026-05-20
**Severity**: P1 — breaks all ERC-20 → * swaps via NEAR Intents (e.g. USDC on Base)
**Status**: Patched client-side in vault; Pioneer server needs a permanent fix.

---

## Symptom

User swaps USDC (Base) → anything via NEAR Intents. Vault shows:

```
Approval needed: 61.27821 USDC
Current allowance: 0 USDC · spender 0x0x833589…a02913
```

Then after clicking Confirm: `invalid hexadecimal string`.

## Root Cause

Pioneer's quote endpoint for NEAR Intents ERC-20 sources returns:

```json
{
"txParams": {
"to": "0x0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
...
}
}
```

The `to` field has a double `0x` prefix. The correct value is
`0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` (the USDC contract on Base).

## How NEAR Intents ERC-20 works (correct flow)

For ERC-20 sources, NEAR Intents encodes a direct `transfer(solverAddress, amount)`
call on the token contract — **not** `transferFrom()` via a router. Therefore:

- `txParams.to` = token contract address (the USDC contract)
- `txParams.data` = `transfer(solverAddress, amount)` calldata
- `txParams.value` = `"0"` (no ETH)
- **No ERC-20 approval needed** — the user signs one tx, not two

The vault was misidentifying this as a `transferFrom()` pattern and generating a
spurious `approve(USDC_contract, amount)` tx with the token contract as its own
spender — which is semantically invalid and would revert.

## Vault-side workaround (already merged)

Two patches in `src/bun/`:

### 1. `swap-parsing.ts` — strip duplicate `0x`

```typescript
const normalizeAddr = (addr: string | undefined): string | undefined =>
addr ? addr.replace(/^(0x)+/i, '0x') : addr

relayTx = {
to: normalizeAddr(txParams.to) as string,
...
}
```

### 2. `swap.ts` `buildRelaySwapTx()` — skip approval when `relay.to === tokenContract`

```typescript
const isDirectTransfer = relay.to.toLowerCase() === tokenContract
if (isDirectTransfer) {
console.log(`[swap] Relay ERC-20: relay.to === token contract — direct transfer(), no approval needed`)
}
if (tokenContract && tokenContract.startsWith('0x') && !isDirectTransfer) {
// ... allowance check + approveTx generation
}
```

## Pioneer fix needed

In pioneer-server's quote controller, wherever `txParams.to` is constructed for
NEAR Intents routes: ensure the address is emitted with exactly one `0x` prefix.

Search path: `pioneer-server/src/` — look for the NEAR Intents integration handler
that builds `txParams`. The double prefix likely comes from concatenating a
pre-existing `"0x"` string with an already-prefixed address (e.g.
`"0x" + "0x833589..."` or similar).

**Repro**: request a quote for `eip155:8453/erc20:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913`
→ any asset via NEAR Intents and inspect `quote.txs[0].txParams.to` in the raw
response.

## Validation after Pioneer fix

1. `txParams.to` has exactly one `0x` prefix in the raw Pioneer response
2. Vault swap flow for USDC (Base) → ETH (or any chain) via NEAR Intents shows:
- **One** device confirmation (the transfer tx) — no spurious approval dialog
- Tx broadcasts successfully with `to = 0x833589...` (correct USDC contract)
3. Vault client-side `normalizeAddr` workaround stays in place as a defensive guard
(cheap and safe to keep even after the server fix)
Loading