Table of Contents

Class PreimageProvisioning

Namespace
NArk.ArkadeIntents.Lightning
Assembly
NArk.ArkadeIntents.dll

Deterministic claim preimages for the RFQ corridors: a preimage derived from the swap's own claim key, so the seed re-derives what storage may lose.

public static class PreimageProvisioning
Inheritance
PreimageProvisioning
Inherited Members

Remarks

The scheme is the reference client's, byte for byte: the message is TAG ‖ xonly(32) ‖ u32le(index) (or TAG ‖ xonly(32) ‖ salt(32) when the descriptor repeats across swaps), the preimage is sha256(sign_det(sha256(message))), and the signature is BIP-340 with aux_rand = 0 — the convention every local signing source in this SDK already honours, and the one Arkade-Boltz-Preimage-v1 uses on the Boltz corridor. Same (key, salt-or-index) → same signature → same preimage, so a restored wallet re-derives the claim secret from the contract's own descriptor.

The tag is protocol-scoped and versioned, and deliberately distinct from the Boltz one: a shared tag would let one wallet key derive one preimage for both corridors. Any Arkade SDK implementing the same scheme produces the same preimage, so artifacts are recoverable across implementations.

Fields

PreimageIndex

The derivation index. Unsalted derivation is only safe when the key belongs to one swap, so the index stays pinned; kept a constant for cross-SDK vectors.

public const uint PreimageIndex = 0

Field Value

uint

PreimageTag

Domain separator for the per-artifact derivation — exactly as the reference writes it.

public const string PreimageTag = "Arkade-RFQ-Preimage-v1"

Field Value

string

SaltedPreimageTag

Domain separator for the salted derivation, used when the descriptor repeats.

public const string SaltedPreimageTag = "Arkade-Contract-Preimage-Salted-v1"

Field Value

string

Methods

BuildPreimageMessage(byte[], uint)

TAG ‖ xonly(32) ‖ u32le(index) — the message that gets BIP-340 signed.

public static byte[] BuildPreimageMessage(byte[] xOnlyPubKey, uint index)

Parameters

xOnlyPubKey byte[]
index uint

Returns

byte[]

Remarks

Anchored on the canonical x-only key rather than the descriptor string: a restore reconstructs a bare descriptor that serialises differently from the signing descriptor used at creation, and only the key agrees across both.

BuildSaltedPreimageMessage(byte[], byte[])

TAG ‖ xonly(32) ‖ salt(32) — the salted message that gets BIP-340 signed.

public static byte[] BuildSaltedPreimageMessage(byte[] xOnlyPubKey, byte[] salt)

Parameters

xOnlyPubKey byte[]
salt byte[]

Returns

byte[]

Remarks

The salt replaces the pinned index as the source of per-swap uniqueness, which is what lets a key that repeats across swaps still derive a distinct preimage for each. It is public: knowing it yields nothing without the seed.

DerivePreimageAsync(IArkadeWalletSigner, OutputDescriptor, byte[]?, CancellationToken)

sha256(sign_det(sha256(TAG ‖ xonly ‖ index))), or its salted variant when salt is given — where the signing key and the message key are the same descriptor.

public static Task<byte[]> DerivePreimageAsync(IArkadeWalletSigner signer, OutputDescriptor descriptor, byte[]? salt, CancellationToken cancellationToken = default)

Parameters

signer IArkadeWalletSigner

The wallet's signer; determinism is the aux_rand = 0 convention.

descriptor OutputDescriptor

The swap's claim descriptor — the key that signs and the key in the message.

salt byte[]

Per-swap uniqueness for a repeating descriptor; null for an HD child.

cancellationToken CancellationToken

Cancels the signing round trip.

Returns

Task<byte[]>

The 32-byte preimage.

IsPerArtifactDescriptor(OutputDescriptor)

True when the descriptor names one swap — an HD child, which carries a derivation path. A bare tr(pubkey) is the same key every time it is handed out, so anything deriving per-swap secrets must branch on this, never on the wallet's type.

public static bool IsPerArtifactDescriptor(OutputDescriptor descriptor)

Parameters

descriptor OutputDescriptor

Returns

bool