Conversation
Deprecate StorageAdapter (new)
* Contract Manager * Contract Manager * simplify * Refactor contract system to use URLSearchParams and improve initialization handling - Replace manual URL encoding/decoding in arkcontract with URLSearchParams - Add concurrent initialization guard for ContractManager to prevent race conditions - Make notifyIncomingFunds stop function async to properly handle cleanup - Update contract tests to use new contractHandlers registry instead of deprecated SpendingStrategyRegistry - Remove obsolete comments and simplify type definitions in ContractWatcher * Remove CSV generalization from DefaultContractHandler Contracts may have multiple unilateral exit paths with different CSV values, so this cannot be generalized at the handler level. Export the timelock utility functions for use by other handlers. * ContractManager: auto-watch on initialize, support multiple event callbacks - initialize() now automatically starts watching contracts - onContractEvent() added for registering event callbacks (returns unsubscribe) - startWatching() deprecated (still works but just registers callback) - Support multiple concurrent event callbacks - Add vtxo_spendable event type for sweeper notifications * Wallet: include pubKey and serverPubKey in default contract params The default contract registered with ContractManager now includes the actual pubKey and serverPubKey parameters instead of empty params. * ContractManager: validate params via handler on createContract Adds validation in createContract() that: 1. Verifies a handler exists for the contract type 2. Attempts to create the script from params (catches invalid/missing params) 3. Verifies the derived script matches the provided script Also updates contract tests to use valid params and scripts. * ContractWatcher: watch contracts with VTXOs regardless of state - Add getScriptsToWatch() that returns scripts for active contracts AND contracts with known VTXOs (regardless of state) - Update updateSubscription() to use getScriptsToWatch() - Update addContract() to poll first to discover VTXOs, then update subscription This ensures we continue monitoring contracts even after they're deactivated, as long as they have unspent VTXOs. * ContractSweeper: handler-defined sweep destinations - Add optional getSweepDestination method to ContractHandler interface - Update ContractSweeper to use handler-defined destinations - Destination priority: handler > contract.sweepDestination > default - Handler receives defaultDestination to use as fallback * Add delegation types and architecture hooks for future VTXO refresh Prepares the contract system for future delegation/refresh support: - Add DelegationConfig interface for contract delegation settings - Add DelegatedForfeit interface for pre-signed forfeit data - Add DelegationResult interface for delegation creation results - Add delegation field to Contract interface - Add supportsDelegation() method to ContractHandler interface - Implement supportsDelegation() on DefaultContractHandler (returns true) Delegation enables server-side VTXO refresh using pre-signed forfeit transactions (SIGHASH_ALL|ANYONECANPAY), eliminating the need for complex multi-party coordination in contracts like VHTLC. * ServiceWorker: add contract operation request/response types Adds request and response types for contract operations: - GET_CONTRACTS: List contracts with optional filter - GET_CONTRACT: Get single contract by ID - CREATE_CONTRACT: Create new contract - UPDATE_CONTRACT_STATE: Change contract state - GET_CONTRACT_VTXOS: Get VTXOs by contract - GET_CONTRACT_BALANCE: Get balance for a contract - CONTRACT_EVENT: Broadcast contract events This prepares the service worker API for ContractManager integration. Handler implementation can be added when the wallet's contract manager is properly initialized in the service worker context. * Add README documentation for contract system Documents the contract system architecture including: - Architecture overview with ASCII diagram - Core concepts (Contract, ContractHandler, ArkContract strings) - Usage examples (setup, creating, querying, lifecycle) - Event types reference - Watching and sweeping behavior - Delegation future support - Custom handler registration - File reference * Add comprehensive contract system tests Adds 23 new tests covering: - ArkContract encoding/decoding (7 tests) - Handler param validation (3 tests) - VTXO-based watching / getScriptsToWatch (3 tests) - Multiple event callbacks (2 tests) - DefaultContractHandler path selection (6 tests) - getSweepDestination (2 tests) Total contract tests: 44 (up from 21) * Address PR review comments - Add .claude/ to .gitignore and remove tracked settings file - Make ParsedArkContract generic with T extends Record<string, string> - Add ids filter to ContractFilter for bulk lookups - Add command injection warning in example fundAddress function * Remove sweeper and delegation code for separate PR - Delete ContractSweeper class and README - Remove SweeperConfig, SweepResult, and delegation types - Remove autoSweep/sweepDestination from Contract interface - Remove sweeper integration from ContractManager - Remove vtxo_swept event type from ContractWatcher - Remove supportsDelegation from ContractHandler - Update wallet to remove sweeper config and methods - Update service worker request types - Update tests to remove sweeper assertions This code will be reintroduced in a separate PR to keep the initial contract system PR focused and smaller. * Fix merge from next and merge ContractManagerRepository into ContractManager * Handle fresh DB in migration tool * Update example, modify signature of * Add test for VHTLC script * Move ContractManager to ReadonlyWallet * Fix docs, remove forcePoll to use config instead, fix test * Ensure an existing contract has same type when created * Refactor ContractManager and ContractWatcher, extract ContractCache * Continute refactoring, update tests * Implment contracts methods in SW * Example updated * Add tests for ContractVtxoCache * Address sequence comment * Remove/update comments * DRY - refactor handlers * Fix filters * Add deprecation notice for bolt-swap specific methods * remove Contract.data field and modify behavior of updateContract * Remove VTXO cache * Remove plurality from filter fields --------- Co-authored-by: Pietro Grandi <dev@pietro.uno>
…d methods (arkade-os#268) * Migrate commitmentTxid under WalletRepository, fixes first point of arkade-os#267 * Introduce new schema for contracts arkade-os#267 * Remove deprecated methods that are unused arkade-os#267 * Update tests * Explicit object names role (v1 - v2) and remove unused import * Remove deprecated methods entirely and delegate contracts migration to the package which created them * Remove last 'any' from handlers * Treat commitmentTx unique per txid * Remove ID from Contract * Use VTXO to determine spendable paths and introduce new method to calculate all paths * Add dedicated tests for CSV locks * Remove unnecessary deprecated method from IWallet - it's not released yet * Remove support for commitmentTxs * Uniform naming in repository classes
…commitment tx modification - Pass Extension output to buildForfeitTx as additionalOutputs so the IntrospectorPacket is part of the transaction from construction, preventing txid mismatch with the introspector's expected value. - Remove addIntrospectorPacketToTx for boarding commitment txs. The introspector already knows arkade scripts from the intent proof submission, so modifying the server-built commitment tx (which changes its txid) is unnecessary. - Add optional additionalOutputs parameter to buildForfeitTx and buildForfeitTxWithOutput for Extension OP_RETURN support.
arkd reconstructs forfeit txs with exactly 2 outputs (forfeit + anchor) and compares txids. Adding an Extension output changes the txid, causing INVALID_FORFEIT_TXS errors. The introspector already has arkade script info from the intent proof, so forfeits don't need Extension outputs. Also reverts the additionalOutputs parameter from buildForfeitTx since it's no longer needed.
|
[Arkana Review — PR #319: Arkade script support] Reviewed at: 2026-03-26 11:34 UTC | Head: SummaryMassive PR (~3.3k additions) adding full client-side support for ArkadeScript (introspector co-signing). Key components:
Security analysis1. Changed from 2. Batch handler — connector output validation (batch.ts:204–206) if (!connectorOutput?.amount || !connectorOutput?.script) {
throw new Error(`Invalid connector output...`);
}This now throws instead of silently continuing — addresses the coderabbit concern about partial finalization. Correct. 3. Timelock unit detection (batch.ts:83–85) type: event.batchExpiry >= 512n ? "seconds" : "blocks"This uses the BIP-68 threshold (values ≥ 500,000,000 are seconds in BIP-65/CLTV, but for CSV the bit flag is different). However, this is the batch expiry from the server event, not raw nLockTime/nSequence. The comment says "BIP-65: values >= 512 are interpreted as seconds, below as blocks" which is incorrect — BIP-65 uses 500,000,000 as the threshold, and BIP-68 uses a bit flag. This threshold of 512 seems protocol-specific to Ark's RelativeLocktime encoding (matching the Go code where 4. Introspector provider — no request timeouts
5. Tweak computation ( const scalar = bytesToBigInt(hash) % secp256k1.Point.CURVE().n || 1n;The 6. E2E test flakiness (test/e2e/arkade.test.ts:427–435) The UTXO esplora fetch is one-shot without retry. Coderabbit flagged this — still not addressed. Will be flaky in CI when the faucet tx hasn't confirmed yet. Cross-repo impact
Refactoring notes
Outstanding items
None are blockers. The core cryptographic and protocol logic is correct. — Arkana |
ReviewArkade script support — comprehensive implementation ✓Scope: Full client-side Arkade scripting (opcodes, introspector, banking) Key observations:
Minor items:
Ship it — protocol-sound, well-tested. |
Banco Partial Fill SpecOverviewExtend the banco swap contract so a taker can fill part of an offer. The script has two hardcoded constants — an exchange ratio (expressed as a fraction Ratio as Integer ArithmeticThe stack has no floating-point support. The ratio is encoded as two integers — a numerator and denominator:
The consumed BTC for a given fill is computed entirely with 64-bit integer ops: Using Example ratios:
Worked ExampleSetup. Alice (maker) locks 1 BTC (100 000 000 sats) wanting 5 units of Asset X. Fill 1 — Bob delivers 2 X (partial)Script enforces:
Fill 2 — Carol delivers 3 X (full fill)Script enforces:
Output Layout
Script:
|
| Value | Size | Description |
|---|---|---|
makerWitnessProgram |
32 B | Maker's x-only taproot key (makerPkScript[2:]) |
ratioNum |
8 B (LE64) | Numerator of exchange ratio |
ratioDen |
8 B (LE64) | Denominator of exchange ratio |
assetTxid |
32 B | Asset ID txid |
assetGroupIndex |
scriptNum | Asset group index |
Pseudocode with stack trace
# ═══════════════════════════════════════════════════
# STEP 1 — Verify output 1 pays the maker
# ═══════════════════════════════════════════════════
OP_1 # [1]
OP_INSPECTOUTPUTSCRIPTPUBKEY # [program, version]
OP_1 OP_EQUALVERIFY # [program] ← taproot v1
<makerWitnessProgram> OP_EQUALVERIFY # [] ← correct key
# ═══════════════════════════════════════════════════
# STEP 2 — Read asset X amount from output 1
# ═══════════════════════════════════════════════════
OP_1 <assetTxid> <groupIdx> # [1, txid, gidx]
OP_INSPECTOUTASSETLOOKUP # [amount_le64] (or -1 if absent)
OP_DUP OP_1NEGATE OP_EQUAL
OP_NOT OP_VERIFY # [amount_le64] ← verified found
# ═══════════════════════════════════════════════════
# STEP 3 — Compute consumed BTC = amount × ratioNum / ratioDen
# ═══════════════════════════════════════════════════
<ratioNum_le64> OP_MUL64 OP_VERIFY # [product_le64]
<ratioDen_le64> OP_DIV64 OP_VERIFY # [remainder, quotient]
OP_NIP # [consumed_le64] ← drop remainder
# ═══════════════════════════════════════════════════
# STEP 4 — Get input value (BTC locked in this VTXO)
# ═══════════════════════════════════════════════════
OP_PUSHCURRENTINPUTINDEX # [consumed, idx]
OP_INSPECTINPUTVALUE # [consumed, input_val]
# ═══════════════════════════════════════════════════
# STEP 5 — Full fill or partial fill?
# ═══════════════════════════════════════════════════
OP_2DUP # [consumed, input_val, consumed, input_val]
OP_SWAP # [consumed, input_val, input_val, consumed]
OP_LESSTHANOREQUAL64 # [consumed, input_val, flag]
# flag=1 → input_val ≤ consumed (full fill)
# flag=0 → input_val > consumed (partial fill)
OP_IF
# ── FULL FILL ──────────────────────
OP_2DROP # []
OP_1 # [1] ← success, no output constraints
OP_ELSE
# ── PARTIAL FILL ───────────────────
# change = input_value − consumed
OP_SWAP # [input_val, consumed]
OP_SUB64 OP_VERIFY # [change_le64]
# 5a. output 0 value == change
OP_0 OP_INSPECTOUTPUTVALUE # [change, out0_val]
OP_EQUALVERIFY # []
# 5b. output 0 script == current input's script
OP_PUSHCURRENTINPUTINDEX
OP_INSPECTINPUTSCRIPTPUBKEY # [in_prog, in_ver]
OP_0
OP_INSPECTOUTPUTSCRIPTPUBKEY # [in_prog, in_ver, out_prog, out_ver]
OP_ROT # [in_prog, out_prog, out_ver, in_ver]
OP_EQUALVERIFY # [in_prog, out_prog]
OP_EQUAL # [match] ← success
OP_ENDIF
Review: Arkade script support✅ Major feature — comprehensive implementation This PR adds introspector-based smart contract support to the SDK. 46 files is large, so I'll focus on protocol correctness: Script & Opcode Safety:
Introspector Integration:
Contract Lifecycle:
Docker Setup:
Tests:
Cross-repo coordination:
✅ Mergeable — structure is solid, but verify introspector signature validation in code review |
🔍 Review:
|
|
Notes on the partial fill script from @msinkec: GeneralisationThe contract does not necessarily need to always be funded with BTC, it could be selling ArkadeAssets for BTC or ArkadeAsset for ArkadeAsset. The script builder looks at the type SwapAsset = "btc" | asset.AssetId
export interface BancoSwapParams {
offerAmount: bigint,
offerAsset: SwapAsset,
quoteAmount: bigint,
quoteAsset: SwapAsset,
// ...
}
export class BancoSwap {
constructor(
readonly params: BancoSwapParams,
// ...
) {}
partialFillScript(): Uint8Array {
const verifyAssetLookup = [
"OP_DUP", "OP_1NEGATE", "OP_EQUAL"
"OP_NOT", "OP_VERIFY"
];
// ...
// Construct Step 2:
const readReceivedQuoteAssetAmount = () => {
if (params.quoteAsset === "btc") {
return [
"OP_1",
"OP_INSPECTOUTPUTVALUE"
]
}
return [
"OP_1",
params.quoteAsset.txid,
params.quoteAsset.groupIndex,
"OP_INSPECTOUTASSETLOOKUP",
...verifyAssetLookup
]
}
// ...
// Construct Step 4:
const readAvailableOfferAssetAmount = () => {
if (params.quoteAsset === "btc") {
return [
"OP_PUSHCURRENTINPUTINDEX",
"OP_INSPECTINPUTVALUE"
]
}
return [
"OP_PUSHCURRENTINPUTINDEX",
params.quoteAsset.txid,
params.quoteAsset.groupIndex,
"OP_INSPECTINASSETLOOKUP",
...verifyAssetLookup
]
}
}
}The hardcoded ratio becomes:
Product OverflowIn order to compute Example: Mitigations:
Ratios less than 1If the maker wants more atomic units of the This works fine but introduces a The taker should always be expecting: Example: This is an unavoidable UX challenge due to the atomic nature of the units. Subdust outputs
Scenario 1:
|
For dust related issues I think we can lean on arkds limitations. |
|
@louisinger Opened a PR for partial swaps: louisinger#5 |
Add ratio-based partial fill covenant scripts that allow a taker to fill all or part of a banco swap offer. Three swap directions are supported via dedicated scripts matching the expert-reviewed reference: - BTC → asset (maker offers BTC, wants asset) - asset → BTC (maker offers asset, wants BTC) - asset → asset (maker offers asset X, wants asset Y) Each script enforces: input-0 restriction, maker payment on output 1, ratio-based consumed amount via MUL64/DIV64, and correct change output (value + scriptPubKey match) for partial fills. Asset-offer scripts additionally preserve BTC dust value and verify maker payment on full fill output 0. Wire format extended with ratioNum (0x09), ratioDen (0x0a), and offerAsset (0x0b) TLV fields. Maker applies GCD reduction before encoding. Taker supports fillAmount option for BTC→asset partial fills. Made-with: Cursor
🔍 Review —
|
🔍 Arkana PR Review — #319Arkade script support — a major feature PR (6219+/205-, 51 files) by @louisinger adding client-side Arkade Script support, introspector integration, Banco swap contracts, and comprehensive tests. Architecture OverviewThe PR adds five major subsystems:
Security Observations✅ Good: PSBT field key matching fix (
|
🔍 Incremental Review — #319 (new commit:
|
Adds full client-side support for https://github.com/ArkLabsHQ/introspector and ArkadeScript
What's included
Tests
@Kukks @tiero please review
Summary by CodeRabbit
New Features
Repositories / Storage
Tests