Skip to content

Add boarding contract type#533

Open
pietro909 wants to merge 4 commits into
masterfrom
feat/boarding-contract-type
Open

Add boarding contract type#533
pietro909 wants to merge 4 commits into
masterfrom
feat/boarding-contract-type

Conversation

@pietro909
Copy link
Copy Markdown
Contributor

@pietro909 pietro909 commented May 29, 2026

Adds a registered boarding contract handler and derives the on-chain boarding address from it, replacing the ad-hoc boardingTapscript plumbing. The boarding address and the getBoardingAddress() API are unchanged.

What

  • BoardingContractHandler (type boarding), reusing the default DefaultVtxo.Script shape and spend paths; sources its CSV timelock from ArkInfo.boardingExitDelay. Not discoverable (HD index selection stays wallet-owned).
  • Exported from the package root (@arkade-os/sdk) alongside the other built-in handlers.
  • Wallet setup sources boardingTapscript from the handler; contract-manager init persists a matching active boarding contract so its Arkade address is watched.
  • script is the unique contract identity. When boardingExitDelay coincides with the offchain unilateral-exit delay, the boarding script is byte-identical to the default baseline; ensureWalletContract accepts the existing row in either direction instead of creating a duplicate or throwing. default/boarding are the only compatible pair; delegate stays strict.

Notes

  • Backward compatible: the contract-derived boarding script produces a byte-identical pkScript/address.
  • In the collision case the single shared row may carry either type — consumers must not rely on type === "boarding" to identify boarding purpose; resolve via getBoardingAddress() / boardingTapscript.

Tests

  • New unit coverage for the handler (registration, script/address byte-identity, serde, not-discoverable, root export) and wallet integration (persistence, watcher registration, idempotency, server-delay change, both collision directions).
  • Full non-e2e unit suite, typecheck, lint, and build pass.

Manually tested on mutinynet.

Summary by CodeRabbit

  • New Features

    • Added boarding contract handler to the TypeScript SDK, now available through the public API alongside existing contract handlers.
    • Enhanced wallet with idempotent contract initialization logic for improved baseline contract collision handling.
    • Boarding contracts are now fully integrated into the wallet's contract lifecycle and management.
  • Tests

    • Added comprehensive test suites for boarding contract functionality and wallet integration scenarios.

Review Change Stack

pietro909 added 2 commits May 29, 2026 14:59
Register a `boarding` contract handler and derive the on-chain boarding
address from it. Wallet setup sources boardingTapscript from the handler;
contract-manager init persists a matching active boarding contract
(create-if-missing), so VTXOs on the boarding script are watched.
Route default and boarding baseline registration through ensureWalletContract:
a same-script collision (coinciding exit delays) accepts the existing row in
either direction instead of throwing. Boarding stays a distinct row when its
script differs.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Walkthrough

This PR introduces a new "boarding" contract handler to the TS SDK with wallet-level idempotent initialization. The boarding handler delegates to the existing default handler while maintaining a distinct type identifier and non-discoverable semantics. Wallet initialization is updated to derive boarding scripts from the handler and use collision-aware helpers to avoid duplicate contracts when default and boarding types produce identical scripts.

Changes

Boarding Contract Handler and Wallet Integration

Layer / File(s) Summary
Boarding Handler Definition
packages/ts-sdk/src/contracts/handlers/boarding.ts
Introduces BoardingContractParams as an alias of DefaultContractParams and exports BoardingContractHandler with type: "boarding". Handler delegates script creation, serialization, deserialization, and spending-path accessors to DefaultContractHandler, and explicitly notes non-discoverability.
Handler Registration and SDK Surface
packages/ts-sdk/src/contracts/handlers/index.ts, packages/ts-sdk/src/contracts/index.ts, packages/ts-sdk/src/index.ts
Registers BoardingContractHandler in the contractHandlers registry and re-exports it alongside BoardingContractParams through handlers, contracts, and root module public surfaces.
Wallet-Level Contract Initialization
packages/ts-sdk/src/wallet/wallet.ts
Derives boardingTapscript from BoardingContractHandler.createScript instead of inline construction. Adds areSameScriptBaselineTypesCompatible to treat default/boarding as compatible type collisions and ensureWalletContract for idempotent get-or-create contract logic. Updates contract manager initialization to use ensureWalletContract for both default and boarding contracts, allowing graceful handling when script-identical collisions occur.
BoardingContractHandler Test Suite
packages/ts-sdk/test/contracts/handlers/boarding.test.ts
Tests handler registration, script creation output and structure, correct CSV timelock sourcing, address derivation (on-chain and Arkade), parameter round-tripping, discoverability assertions (non-discoverable), and spend-path selection logic.
Wallet Integration and Collision Handling Tests
packages/ts-sdk/test/wallet/boarding-contract.test.ts
End-to-end tests covering: backward-compatible getBoardingAddress derivation, handler-based boardingTapscript creation, contract manager persistence and idempotent re-initialization, VTXO annotation with spendable paths, handler non-discoverability, multi-boot delay-change scenarios with contract promotion, HD key resolution, areSameScriptBaselineTypesCompatible compatibility matrix, ensureWalletContract collision resolution in both directions without duplicates, and the edge case where boardingExitDelay === unilateralExitDelay results in a single shared script.
Existing Test Clarifications
packages/ts-sdk/test/wallet.test.ts
Adds explanatory comments clarifying that boarding and default scripts are identical when exit delays match, and adds explicit boardingExitDelay to the mocked Ark server response.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • Kukks
  • altafan
  • arkanaai
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: introducing a new boarding contract type to the SDK with full handler implementation and wallet integration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/boarding-contract-type

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@arkanaai arkanaai Bot mentioned this pull request May 29, 2026
3 tasks
Copy link
Copy Markdown
Contributor

@arkanaai arkanaai Bot left a comment

Choose a reason for hiding this comment

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

🔍 Arkana Code Review — feat/boarding-contract-type

Verdict: Code looks correct. Requesting human sign-off — this is protocol-critical (boarding = on-chain funds entry into Ark).


✅ What's right

1. Handler delegation is clean and correct.
BoardingContractHandler delegates every method to DefaultContractHandler, producing byte-identical scripts. The handler only differs in type: "boarding" and non-discoverability. This is the right level of abstraction.

2. Boarding script derivation round-trips correctly.
Old path: new DefaultVtxo.Script({ pubKey, serverPubKey, csvTimelock: boardingTimelock })
New path: BoardingContractHandler.createScript({ pubKey: hex.encode(pubKey), serverPubKey: hex.encode(serverPubKey), csvTimelock: timelockToSequence(boardingTimelock).toString() })DefaultContractHandler.deserializeParamsnew DefaultVtxo.Script(typed).

The round-trip through timelockToSequencetoStringNumber()sequenceToTimelock is lossless for valid BIP68 values. The pubkey round-trip through hex.encodenormalizeToDescriptorextractPubKeyhex.decode is likewise identity-preserving. Tests at boarding.test.ts:409-447 pin byte-identity.

3. ensureWalletContract collision handling is well-reasoned.

  • wallet.ts:255-269: Queries by script, checks type compatibility, returns early for default↔boarding collision.
  • areSameScriptBaselineTypesCompatible is symmetric and restrictive (only default↔boarding).
  • Compatible collision → existing row already active and watched (from ContractManager.initialize() at contractManager.ts:397-399 which loads ALL persisted contracts on startup). No gap in monitoring.
  • Incompatible collision → falls through to createContractupsertContract → throws type mismatch. Correct.

4. Test coverage is comprehensive (747 new test lines). Covers registration, script identity, address derivation, param serde, non-discoverability, spend paths, persistence, watcher subscription, idempotency, collision in both directions, incompatible-type throw, server delay change, and the shared-script edge case.

5. Backward compatible. getBoardingAddress() derives from this.boardingTapscript.onchainAddress() — unchanged and independent of the persisted contract.


⚠️ Minor observations (non-blocking)

1. ensureWalletContract skips state update on compatible collision (wallet.ts:264-267).
If an existing compatible-type row is inactive, the function returns early without promoting it to active. The watcher won't re-register it (already loaded during ContractManager.initialize(), but if inactive, it may not be watched depending on getContracts filter semantics). In practice this is a non-issue: wallet baseline contracts are always created active, and no current code path deactivates them. But worth a defensive comment.

2. Hardcoded compatibility matrix (wallet.ts:220-228).
areSameScriptBaselineTypesCompatible is a closed-form check for defaultboarding. Any future baseline type that can collide (unlikely given distinct Taproot trees) would need to be added here. The JSDoc documents this, which is sufficient.

3. Potential merge conflict with PR #518 (require-timelock).
PR #518 modifies default.ts and wallet.ts to make csvTimelock required. The boarding test "falls back to the default timelock when csvTimelock is missing" (boarding.test.ts:513-524) may need updating if #518 merges first.

4. BoardingContractHandler is a pure delegation wrapper — changes to DefaultContractHandler silently propagate. The tests pin behavior, which is the right mitigation. No action needed.


🚨 Protocol-critical flag

This PR touches boarding — the on-chain Bitcoin deposit path into Ark. While the code is correct and the refactoring preserves byte-identical behavior:

  • Boarding funds are real on-chain BTC locked under the forfeit/exit Taproot tree
  • The collision handling logic determines which contract row "owns" the boarding script and its VTXOs
  • A bug here could cause boarding deposits to become invisible or unspendable

Requesting human review per protocol. @Kukks @altafan — please sign off.

Copy link
Copy Markdown
Contributor

@arkanaai arkanaai Bot left a comment

Choose a reason for hiding this comment

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

🔍 Arkana — Follow-up (new commit 91f2d19)

Reviewed the delta since my last pass. Only change is test/e2e/contract-params-change.test.ts:

  • contractsAfterFirst count: 2 → 3 (adds boarding contract)
  • contractsAfterSecond count: 4 → 5 (boarding persists across param change)
  • Added clarifying comments explaining the count breakdown

Correct. The boarding contract is now persisted alongside default+delegate, and the e2e test expectations needed updating to reflect that. No production code changed.

My previous review stands — code is correct, still requesting human sign-off since this is protocol-critical (boarding = on-chain funds entry). @Kukks @altafan

@pietro909 pietro909 requested a review from Kukks May 29, 2026 14:47
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.

1 participant