Skip to content

Commit 919c120

Browse files
committed
temp: wasm-ton scaffold
1 parent 397599a commit 919c120

36 files changed

Lines changed: 4661 additions & 0 deletions

packages/wasm-ton/.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
target/
2+
node_modules/
3+
# we actually only track the .ts files
4+
dist/
5+
test/*.js
6+
test/*.d.ts
7+
js/*.js
8+
js/*.d.ts
9+
js/wasm
10+
.vscode
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"coin": "ton",
3+
"existing": true,
4+
"ticket": null,
5+
"paths": {
6+
"bitgowasm": "/Users/luiscovarrubias/BitGoWasm",
7+
"bitgojs": "/Users/luiscovarrubias/BitGoJS",
8+
"walletPlatform": "/Users/luiscovarrubias/bitgo-microservices"
9+
},
10+
"worktrees": {
11+
"bitgowasm": "/Users/luiscovarrubias/BitGoWasm-ton",
12+
"bitgojs": "/Users/luiscovarrubias/BitGoJS-ton",
13+
"walletPlatform": "/Users/luiscovarrubias/bitgo-microservices-ton"
14+
},
15+
"ghAvailable": true,
16+
"guideFile": "/Users/luiscovarrubias/claude-plugins/plugins/wasm-onboard-coin/skills/references/onboarding-guide.md",
17+
"patternsFile": "/Users/luiscovarrubias/claude-plugins/plugins/wasm-onboard-coin/skills/references/patterns.md"
18+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Implementation Plan: BitGoJS sdk-coin-ton WASM Integration
2+
3+
## Reference PRs
4+
- SOL: BitGo/BitGoJS#8074
5+
- DOT: BitGo/BitGoJS#8322
6+
7+
## Files to Create/Modify
8+
9+
### New file: `modules/sdk-coin-ton/src/lib/explainTransactionWasm.ts`
10+
- Import `Transaction`, `parseTransaction`, `ParsedTransaction` from `@bitgo/wasm-ton`
11+
- Export `explainTonTransaction(rawTransaction: string): TransactionExplanation`
12+
- Map `ParsedTransaction` fields to BitGoJS `TransactionExplanation` shape:
13+
- `id` from parsed tx
14+
- `outputs` = `[{ address: recipient, amount: amount.toString() }]`
15+
- `outputAmount` = amount string
16+
- `changeOutputs` = `[]`, `changeAmount` = `'0'`
17+
- `fee` = `{ fee: 'UNKNOWN' }` (TON fees determined on-chain)
18+
- `withdrawAmount` for staking withdrawals
19+
- Map `TonTransactionType` to BitGoJS `TransactionType` enum
20+
21+
### Modify: `modules/sdk-coin-ton/package.json`
22+
- Add dependency: `"@bitgo/wasm-ton": "0.1.0"`
23+
24+
### Modify: `modules/sdk-coin-ton/src/lib/transaction.ts`
25+
- Add WASM-backed `explainTransaction()` path
26+
- Use `explainTonTransaction()` from new file when WASM available
27+
28+
### Modify: `modules/sdk-coin-ton/src/lib/index.ts`
29+
- Export `explainTonTransaction` from new file
30+
31+
### Modify: `modules/sdk-coin-ton/src/ton.ts` (main coin class)
32+
- Wire `explainTransaction()` to use WASM path
33+
- Pattern: check WASM availability, fallback to existing JS path
34+
35+
## Testing
36+
- Existing tests must pass with WASM backend
37+
- Compare WASM explain output vs JS explain output for all fixtures
38+
- All 7 transaction types must produce identical explanations
39+
40+
## Key Patterns
41+
- `bigint` internally, `String()` at serialization boundary
42+
- Single integration file (not scattered imports)
43+
- WASM explain is called from both coin class and transaction class
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Implementation Plan: wallet-platform TON WASM Integration
2+
3+
## Reference PRs
4+
- SOL WASM: #55513
5+
- DOT WASM: #56374
6+
7+
## Files to Create
8+
9+
### New directory: `coins/ton/wasm/`
10+
11+
### `coins/ton/wasm/index.ts`
12+
- Export: `shouldUseWasm`, `canUseWasmPath`, `buildTransactionWithIntentWasm`, `wasmBuildSignedTransaction`
13+
- `shouldUseWasm(coin: string)` — check feature flag `shouldUseWasm('tton')`
14+
- `canUseWasmPath(intent)` — validate intent has required fields for WASM path
15+
16+
### `coins/ton/wasm/buildTransaction.ts`
17+
- `buildTransactionWithIntentWasm(intent, context)` — map WP intent to WASM intent format
18+
- Intent mapping (WP → WASM):
19+
- `payment``{ intentType: "Payment", recipient, amount, memo, bounceable }`
20+
- `payment` (token) → `{ intentType: "JettonTransfer", senderJettonAddress, recipient, tokenAmount, tonAmount, forwardTonAmount, memo }`
21+
- `delegate` (TON_WHALES) → `{ intentType: "TonWhalesDeposit", validatorAddress, amount, queryId }`
22+
- `delegate` (SINGLE_NOMINATOR) → `{ intentType: "Payment", recipient: validatorAddress, amount, bounceable: true }`
23+
- `delegate` (MULTI_NOMINATOR) → `{ intentType: "Payment", recipient: validatorAddress, amount, bounceable: true, memo: "d" }`
24+
- `undelegate` (TON_WHALES) → `{ intentType: "TonWhalesWithdrawal", validatorAddress, amount, withdrawalAmount, queryId }`
25+
- `undelegate` (SINGLE_NOMINATOR) → `{ intentType: "SingleNominatorWithdraw", validatorAddress, amount: 1e9, withdrawAmount }`
26+
- `undelegate` (MULTI_NOMINATOR) → `{ intentType: "Payment", recipient: validatorAddress, amount: 1e9, bounceable: true, memo: "w" }`
27+
- Vesting deposit/withdrawal → respective intent types with wallet_id=268
28+
- Build context: `{ sender, publicKey, seqno, expireTime, walletId, bounceable }`
29+
30+
### `coins/ton/wasm/signTransaction.ts`
31+
- `wasmBuildSignedTransaction(serializedTxHex, commonPub, signatureHex)`:
32+
- Convert hex to base64 BOC
33+
- `Transaction.fromBoc(boc)``tx.addSignature(pubkey, sig)``tx.toBroadcastFormat()`
34+
- Return `{ id, tx: broadcastFormat }`
35+
36+
### `coins/ton/wasm/parseTransaction.ts`
37+
- `wasmParseTransaction(serializedTxHex)`:
38+
- Convert hex to base64 BOC
39+
- `parseTransaction(boc)` → map to WP's `TonParsedTransaction` shape
40+
41+
## Files to Modify
42+
43+
### `coins/ton/ton.ts`
44+
- Import WASM module from `./wasm/`
45+
- In `buildTransactionWithIntent`: add WASM path before existing JS path
46+
- `if (shouldUseWasm(this.coin)) { return buildTransactionWithIntentWasm(...) }`
47+
- In `getSignedTx`: add WASM path
48+
- `if (shouldUseWasm(this.coin)) { return wasmBuildSignedTransaction(...) }`
49+
- In `internalParseTransaction`: add WASM path
50+
51+
## Staging Gate
52+
- All WASM paths gated behind `shouldUseWasm('tton')` feature flag
53+
- Only enabled on testnet initially (staging)
54+
- Existing JS path remains as fallback
55+
56+
## Testing
57+
- Unit tests for intent mapping
58+
- Compare WASM build output vs JS build output for each intent type
59+
- Signing round-trip: build → sign → parse → verify
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# Implementation Plan: @bitgo/wasm-ton
2+
3+
## Reference Package
4+
`wasm-solana` (general structure) + `wasm-dot` (for comparison)
5+
6+
## Rust Crates
7+
```toml
8+
tlb = { version = "0.7", default-features = false, features = ["sha2"] }
9+
tlbits = "0.7"
10+
tlb-ton = { version = "0.7", default-features = false, features = ["sha2"] }
11+
ton-contracts = { version = "0.7", features = ["wallet"] }
12+
```
13+
14+
## Prior Art
15+
Experiment code at `~/BitGoWasm/.cursor/ton-wasm-experiments/` has working Rust code for all transaction types. Signable payloads verified byte-identical with BitGoJS.
16+
17+
---
18+
19+
## Phase 1: Scaffold + Address Creation
20+
21+
### Package scaffold
22+
Copy from `wasm-solana` and adapt:
23+
- `Cargo.toml` — name=wasm-ton, add toner crates, remove solana crates
24+
- `Makefile` — copy from wasm-solana, change package name
25+
- `package.json` — name=@bitgo/wasm-ton, `"private": true`, version=0.1.0
26+
- `tsconfig.json`, `tsconfig.cjs.json` — copy as-is
27+
- `.gitignore` — copy as-is
28+
29+
### Rust source files
30+
- `src/lib.rs` — module declarations, re-exports
31+
- `src/error.rs``WasmTonError` enum (thiserror)
32+
- `src/address.rs` — Core address logic:
33+
- `encode(pubkey: &[u8; 32], bounceable: bool, wallet_id: u32) -> String` using toner's `MsgAddress` + `WalletV4R2`
34+
- `decode(address: &str) -> AddressInfo { workchain, hash, bounceable }`
35+
- `validate(address: &str) -> bool`
36+
- Address derivation: pubkey → v4r2 state init → cell hash → MsgAddress
37+
- `src/wasm/mod.rs` — re-export WASM types
38+
- `src/wasm/address.rs``AddressNamespace` with `#[wasm_bindgen]`:
39+
- `encode(pubkey: &[u8], bounceable: bool) -> String`
40+
- `decode(address: &str) -> JsValue` (AddressInfo)
41+
- `validate(address: &str) -> bool`
42+
- `is_bounceable(address: &str) -> bool`
43+
- `set_bounceable(address: &str, bounceable: bool) -> String`
44+
45+
### TypeScript wrappers
46+
- `js/address.ts``Address` namespace: `encode()`, `decode()`, `validate()`, `isBounceable()`, `setBounceable()`
47+
- `js/index.ts` — barrel exports
48+
49+
### Tests
50+
- Address round-trip: encode → decode → re-encode
51+
- Bounceable/non-bounceable conversion
52+
- Known addresses from BitGoJS fixtures
53+
- Invalid address rejection
54+
55+
### Verification
56+
- `npm run build` passes
57+
- `npm test` passes
58+
59+
---
60+
61+
## Phase 2: Transaction Parsing + Signing
62+
63+
### Core Rust
64+
- `src/transaction.rs``TonTransaction` struct:
65+
- `from_boc(boc_base64: &str) -> Result<Self>` — deserialize BOC to typed message
66+
- Internal fields: `ext_msg: Message`, `sign_body: WalletV4R2SignBody`, `signature: [u8; 64]`
67+
- `signable_payload(&self) -> [u8; 32]` — SHA-256 hash of sign body Cell
68+
- `add_signature(&mut self, pubkey: &[u8; 32], signature: &[u8; 64])` — place sig
69+
- `to_broadcast_format(&self) -> String` — serialize to base64 BOC
70+
- `id(&self) -> String` — base64 cell hash with `/``_`, `+``-`
71+
- `to_bytes(&self) -> Vec<u8>` — raw BOC bytes
72+
- Public key extraction from StateInit (when seqno=0)
73+
74+
- `src/parser.rs``parse_transaction(tx: &TonTransaction) -> ParsedTransaction`:
75+
- Standalone function (NOT a method on TonTransaction)
76+
- Returns `ParsedTransaction` with: sender, recipient, amount, seqno, expire_time, wallet_id, bounceable, memo, tx_type
77+
- Opcode dispatch for tx_type detection:
78+
- `0x00000000` → text comment (check "Deposit"/"Withdraw" for vesting)
79+
- `0x0f8a7ea5` → Jetton transfer (use toner's `JettonTransfer`)
80+
- `0x00001000` → SingleNominatorWithdraw
81+
- `2077040623` → TonWhalesDeposit
82+
- `3665837821` → TonWhalesWithdraw
83+
- empty body → plain transfer
84+
85+
- `src/types.rs` — shared types:
86+
- `TonTransactionType` enum: Send, SendToken, SingleNominatorWithdraw, TonWhalesDeposit, TonWhalesWithdrawal, TonWhalesVestingDeposit, TonWhalesVestingWithdrawal
87+
- `ParsedTransaction` struct
88+
- `ParsedPayload` enum (per-opcode data)
89+
90+
- `src/staking.rs` — custom CellSerialize/CellDeserialize impls:
91+
- `NominatorWithdraw { query_id: u64, amount: BigUint }`
92+
- `WhalesDeposit { query_id: u64 }`
93+
- `WhalesWithdraw { query_id: u64, unstake_amount: BigUint }`
94+
95+
### WASM bindings
96+
- `src/wasm/transaction.rs``WasmTransaction`:
97+
- `from_boc(boc: &str) -> WasmTransaction`
98+
- `signable_payload(&self) -> Vec<u8>`
99+
- `add_signature(&mut self, pubkey: &[u8], signature: &[u8])`
100+
- `to_broadcast_format(&self) -> String`
101+
- `id(&self) -> String`
102+
103+
- `src/wasm/parser.rs``ParserNamespace`:
104+
- `parse_transaction(boc: &str) -> JsValue` (ParsedTransaction via serde_wasm_bindgen)
105+
106+
- `src/wasm/try_into_js_value.rs` — BigInt conversion for u64 amounts
107+
108+
### TypeScript wrappers
109+
- `js/transaction.ts``Transaction` class wrapping WasmTransaction
110+
- `js/parser.ts``parseTransaction()` function + `ParsedTransaction` type
111+
112+
### Tests
113+
- Round-trip: parse fixture BOC → verify fields → add signature → serialize → re-parse
114+
- Use real fixtures from `~/BitGoJS/modules/sdk-coin-ton/test/resources/ton.ts`
115+
- All 7 transaction types
116+
- Signable payload matches BitGoJS output (verified in experiments)
117+
118+
---
119+
120+
## Phase 3: Transaction Building (Intents)
121+
122+
### Core Rust
123+
- `src/builder/mod.rs` — module declarations
124+
- `src/builder/types.rs` — intent types:
125+
```rust
126+
#[derive(Deserialize)]
127+
#[serde(tag = "intentType")]
128+
pub enum TonIntent {
129+
Payment(PaymentIntent),
130+
JettonTransfer(JettonTransferIntent),
131+
SingleNominatorWithdraw(NominatorWithdrawIntent),
132+
TonWhalesDeposit(WhalesDepositIntent),
133+
TonWhalesWithdrawal(WhalesWithdrawalIntent),
134+
TonWhalesVestingDeposit(VestingDepositIntent),
135+
TonWhalesVestingWithdrawal(VestingWithdrawalIntent),
136+
}
137+
```
138+
Each intent struct has the params from wallet-platform research.
139+
140+
- `src/builder/context.rs` — build context:
141+
```rust
142+
pub struct TonBuildContext {
143+
pub sender: String,
144+
pub public_key: String, // hex
145+
pub seqno: u32,
146+
pub expire_time: u32,
147+
pub wallet_id: Option<u32>, // default 698983191
148+
pub bounceable: Option<bool>,
149+
}
150+
```
151+
152+
- `src/builder/build.rs``build_transaction(intent: &TonIntent, context: &TonBuildContext) -> Result<TonTransaction>`:
153+
- Shared: `V4R2::create_sign_body(wallet_id, expire_at, seqno, actions)` + `SendMsgAction` + `Message::transfer`
154+
- Payment: `TextComment(memo)` body (or empty)
155+
- JettonTransfer: `JettonTransfer` body (toner built-in)
156+
- SingleNominatorWithdraw: custom `NominatorWithdraw` body
157+
- TonWhalesDeposit: custom `WhalesDeposit` body
158+
- TonWhalesWithdrawal: custom `WhalesWithdraw` body
159+
- VestingDeposit: `TextComment("Deposit")` + wallet_id=268
160+
- VestingWithdrawal: `TextComment("Withdraw")` + wallet_id=268
161+
- StateInit inclusion when seqno=0
162+
163+
### WASM bindings
164+
- `src/wasm/builder.rs``BuilderNamespace`:
165+
- `build_transaction(intent: JsValue, context: JsValue) -> WasmTransaction`
166+
167+
### TypeScript wrappers
168+
- `js/builder.ts``buildTransaction(intent, context)` + intent type interfaces
169+
170+
### Tests per intent
171+
- Build → serialize → parse → verify decoded fields match intent params
172+
- Build with seqno=0 → verify StateInit present
173+
- Compare signable payloads with experiment results where available
174+
175+
---
176+
177+
## Phase 4: Scratch Validation (Testnet)
178+
179+
- Create `scratch/ton-wallet.ts`
180+
- Commands: setup, balance, send, jetton-send, stake (whales), unstake
181+
- Testnet: TON testnet
182+
- Signing: tweetnacl (Ed25519)
183+
- Validate all intents broadcast successfully

0 commit comments

Comments
 (0)