Documentation Index Fetch the complete documentation index at: https://docs.arkadeos.com/llms.txt
Use this file to discover all available pages before exploring further.
Install the SDK If you haven’t already, install the SDK and set up your environment
The Dryja-Poon construction enables bidirectional payment channels with revocation-based state updates.
Path Condition When to use Funding (collaborative) Alice + Bob + server signatures Open channel instantly Funding (unilateral) Alice + Bob signatures (after exit delay) Fallback if server unresponsive To-local (revocation) Counterparty revocation signature Punish old state broadcast To-local (normal) Self signature (after CSV delay) Claim own balance To-remote Counterparty signature Immediate claim
Build the Tapscript
import { Script } from '@scure/btc-signer' ;
import { hex } from '@scure/base' ;
import {
RestArkProvider ,
MnemonicIdentity ,
VtxoScript ,
networks
} from '@arkade-os/sdk' ;
// Setup
const arkProvider = new RestArkProvider ( 'https://arkade.computer' );
const info = await arkProvider . getInfo ();
const serverPubkey = hex . decode ( info . signerPubkey ). slice ( 1 ); // x-only
const exitDelay = BigInt ( info . exitDelay );
// Channel participants
const alice = MnemonicIdentity . fromMnemonic ( "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" );
const bob = MnemonicIdentity . fromMnemonic ( "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong" );
const alicePubkey = await alice . xOnlyPublicKey ();
const bobPubkey = await bob . xOnlyPublicKey ();
// Standard Lightning CSV delay for to_local
const toLocalDelay = 144 n ; // ~1 day in blocks
Funding output
2-of-2 multisig between Alice and Bob, with Ark timeout path:
Collaborative : < alicePubkey > OP_CHECKSIGVERIFY < bobPubkey > OP_CHECKSIGVERIFY < serverPubkey > OP_CHECKSIG
Unilateral: < exitDelay > OP_CSV OP_DROP < alicePubkey > OP_CHECKSIGVERIFY < bobPubkey > OP_CHECKSIG
// Collaborative: aliceSig + bobSig + serverSig
const fundingCollaborative = Script . encode ([
alicePubkey ,
'CHECKSIGVERIFY' ,
bobPubkey ,
'CHECKSIGVERIFY' ,
serverPubkey ,
'CHECKSIG'
]);
// Unilateral: after exitDelay, aliceSig + bobSig
const fundingUnilateral = Script . encode ([
exitDelay ,
'CHECKSEQUENCEVERIFY' ,
'DROP' ,
alicePubkey ,
'CHECKSIGVERIFY' ,
bobPubkey ,
'CHECKSIG'
]);
const fundingScript = new VtxoScript ([ fundingCollaborative , fundingUnilateral ]);
const fundingAddress = fundingScript . address ( networks . bitcoin . hrp , serverPubkey ). encode ();
console . log ( 'Funding address:' , fundingAddress );
To-local output (commitment transaction)
Funds belonging to the broadcaster, with revocation for punishment:
Revocation : < revocationPubkey > OP_CHECKSIG
Normal: < toLocalDelay > OP_CSV OP_DROP < localPubkey > OP_CHECKSIG
// Revocation keys are derived per commitment (simplified here)
function buildToLocalScript (
localPubkey : Uint8Array ,
revocationPubkey : Uint8Array ,
delay : bigint
) {
// Revocation path: counterparty can punish old state
const revocationPath = Script . encode ([
revocationPubkey ,
'CHECKSIG'
]);
// Normal path: broadcaster claims after delay
const normalPath = Script . encode ([
delay ,
'CHECKSEQUENCEVERIFY' ,
'DROP' ,
localPubkey ,
'CHECKSIG'
]);
// Ark unilateral exit variant
const unilateralPath = Script . encode ([
exitDelay ,
'CHECKSEQUENCEVERIFY' ,
'DROP' ,
delay ,
'CHECKSEQUENCEVERIFY' ,
'DROP' ,
localPubkey ,
'CHECKSIG'
]);
return new VtxoScript ([ revocationPath , normalPath , unilateralPath ]);
}
// Alice's to_local in her commitment
const aliceRevocationPubkey = /* derived from Bob's revocation basepoint */ ;
const aliceToLocal = buildToLocalScript ( alicePubkey , aliceRevocationPubkey , toLocalDelay );
To-remote output (commitment transaction)
Funds belonging to the counterparty, immediately spendable:
Normal : < remotePubkey > OP_CHECKSIG
Unilateral: < exitDelay > OP_CSV OP_DROP < remotePubkey > OP_CHECKSIG
function buildToRemoteScript ( remotePubkey : Uint8Array ) {
// Normal: counterparty claims immediately
const normalPath = Script . encode ([
remotePubkey ,
'CHECKSIG'
]);
// Ark unilateral exit
const unilateralPath = Script . encode ([
exitDelay ,
'CHECKSEQUENCEVERIFY' ,
'DROP' ,
remotePubkey ,
'CHECKSIG'
]);
return new VtxoScript ([ normalPath , unilateralPath ]);
}
// Bob's to_remote in Alice's commitment
const bobToRemote = buildToRemoteScript ( bobPubkey );
The dual-path pattern
Every Lightning script gets two Taproot leaves:
Taproot tree:
├── <standard lightning script>
└── <exitDelay> OP_CSV OP_DROP <standard lightning script>
The first leaf is vanilla Lightning. The second adds a CSV delay matching the Batch expiry for Ark unilateral exit. The server’s key never appears in commitment or HTLC scripts.
Script breakdown
Opcode Effect CHECKSIGVerify signature, return result CHECKSIGVERIFYVerify signature, continue if valid CHECKSEQUENCEVERIFYEnforce relative timelock (CSV) DROPRemove top stack element
Next steps
Lightning Channels Full channel lifecycle on Arkade
Hashlock contract Add hash conditions for HTLCs
Spilman channel Simpler unidirectional channels
Deep dive Learn about VTXOs and timelocks