@volt-protocol/sdk
v0.1.0-testnet.1
Published
Volt Protocol TypeScript SDK
Readme
@volt-protocol/sdk
TypeScript SDK for Volt Protocol — trigger Stacks smart contracts from Bitcoin transactions.
Installation
npm install @volt-protocol/sdkRequires Node.js ≥ 18. For React hooks, react ≥ 18 is a peer dependency.
Quick Start
import { VoltClient, registerTrigger, getPaymentUri } from '@volt-protocol/sdk';
const client = new VoltClient({
network: 'testnet',
registryContract: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.volt-registry',
verifierContract: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.volt-verifier',
relayRegistryContract: 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.volt-relay-registry',
});
const { triggerId } = await registerTrigger(client, {
targetContract: 'ST2CY5V39NHDPWSXMW9QDT3HC3GD6Q6XX4CFRK9AG.my-contract',
functionName: 'on-volt-trigger',
feePerRelay: 500,
initialEscrow: 1_000_000,
signerKey: process.env.PRIVATE_KEY!,
});
const { uri } = getPaymentUri({ triggerId, recipientBtcAddress: 'bc1q...' });
console.log('Send BTC to trigger your contract:', uri);API Reference
VoltClient
The central client object. Pass it to functions that need to interact with on-chain contracts.
new VoltClient(config: VoltClientConfig)VoltClientConfig
| Field | Type | Description |
|---|---|---|
| network | 'mainnet' \| 'testnet' \| 'devnet' | Stacks network |
| registryContract | string | SP….volt-registry principal |
| verifierContract | string | SP….volt-verifier principal |
| relayRegistryContract | string | SP….volt-relay-registry principal |
| stacksApiUrl | string? | Override Stacks API base URL |
| senderKey | string? | Default private key for read-only sender address |
Methods
// Call a read-only contract function. Returns a ClarityValue.
client.callReadOnly(contract: string, fn: string, args: ClarityValue[]): Promise<ClarityValue>
// Broadcast a contract call. Returns { txid, status }.
client.callPublic(contract: string, fn: string, args: ClarityValue[]): Promise<TxResult>registerTrigger
Register a new Volt trigger in the on-chain registry.
import { registerTrigger } from '@volt-protocol/sdk';
const result = await registerTrigger(client, options);TriggerOptions
| Field | Type | Description |
|---|---|---|
| targetContract | string | Stacks contract that implements volt-trigger-trait |
| functionName | string | Function to call (typically on-volt-trigger) |
| feePerRelay | number | Relay fee in microstacks per execution |
| initialEscrow | number | Initial STX deposit in microstacks (min 1,000,000) |
| signerKey | string | Private key hex for the registrant account |
| triggerId | string? | 32-byte hex ID — auto-generated if omitted |
| awaitConfirmation | boolean? | Wait for on-chain confirmation before resolving |
Returns { triggerId: string, txid: string, status: 'pending' | 'confirmed' }.
getPaymentUri
Build the BIP-21 payment URI and 80-byte Volt OP_RETURN for a trigger invocation.
import { getPaymentUri } from '@volt-protocol/sdk';
const { uri, opReturnHex, nonce, triggerId } = getPaymentUri(options);PaymentUriOptions
| Field | Type | Description |
|---|---|---|
| triggerId | string | 32-byte hex trigger ID |
| recipientBtcAddress | string? | Bitcoin address for the BIP-21 bitcoin: URI |
| amountSats | number? | BTC amount in satoshis |
| nonce | string? | 4-byte hex nonce — auto-generated if omitted |
| params | Array<{ type: string; value: Buffer }>? | TLV-encoded payload params |
Returns PaymentUri:
interface PaymentUri {
uri: string; // BIP-21: bitcoin:<addr>?amount=<btc>&op_return=<hex>
opReturnHex: string; // Full 80-byte Volt OP_RETURN as hex (160 chars)
nonce: string; // 4-byte nonce used (hex)
triggerId: string; // 32-byte trigger ID (hex)
}watchTrigger
Poll the Stacks API for a trigger execution lifecycle. Returns a WatchHandle with a stop() method.
import { watchTrigger } from '@volt-protocol/sdk';
const handle = watchTrigger({
triggerId,
nonce,
stacksApiUrl: 'https://api.testnet.hiro.so',
onPending: (btcTxid) => console.log('BTC tx in mempool:', btcTxid),
onConfirmed: (btcTxid, confs) => console.log('Confirmed with', confs, 'confirmations'),
onExecuted: (stacksTxid) => console.log('Stacks contract called:', stacksTxid),
onFailed: (reason) => console.error('Failed:', reason),
timeoutMs: 600_000, // 10 minutes
});
// Cancel watching:
handle.stop();WatchOptions
| Field | Type | Description |
|---|---|---|
| triggerId | string | 32-byte hex trigger ID |
| nonce | string | 4-byte hex nonce from getPaymentUri |
| stacksApiUrl | string | Stacks API base URL |
| onPending | (btcTxid: string) => void | BTC tx first seen in mempool |
| onConfirmed | (btcTxid: string, confirmations: number) => void | BTC tx confirmed |
| onExecuted | (stacksTxid: string, result: unknown) => void | Stacks contract executed |
| onFailed | (reason: string) => void | Timeout or revert |
| timeoutMs | number? | Polling timeout in ms (default: 3,600,000 — 1 hour) |
generateContractStub
Scaffold a new Clarity contract that implements volt-trigger-trait.
import { generateContractStub } from '@volt-protocol/sdk';
const source = generateContractStub({
contractName: 'my-trigger',
triggerId: 'aabb...', // 32-byte hex
verifierAddress: 'SP1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM',
params: [
{ name: 'recipient', type: 'principal' },
{ name: 'amount', type: 'uint' },
],
});
// Write to a file, then extend with your logic
fs.writeFileSync('contracts/my-trigger.clar', source);ContractStubOptions
| Field | Type | Description |
|---|---|---|
| contractName | string | Contract name (for comments) |
| triggerId | string | 32-byte hex trigger ID (for comments) |
| verifierAddress | string | Stacks address of the volt-verifier deployer |
| params | Array<{ name: string; type: string }>? | Parameters to document in the stub |
Returns the Clarity source code as a string.
Utility Functions
import {
encodeTlvPayload,
decodeTlvPayload,
generateTriggerId,
generateNonce,
isValidPrincipal,
reverseHex,
doubleSha256,
} from '@volt-protocol/sdk';| Function | Signature | Description |
|---|---|---|
| encodeTlvPayload | (params) => Buffer | Encode params into 36-byte TLV buffer; IPFS CID fallback |
| decodeTlvPayload | (payload: Buffer) => Array<{type, value}> | Decode 36-byte TLV buffer |
| generateTriggerId | () => string | Random 32-byte trigger ID as 64-char hex |
| generateNonce | () => string | Random 4-byte nonce as 8-char hex |
| isValidPrincipal | (s: string) => boolean | Validate a Stacks principal string |
| reverseHex | (hex: string) => string | Reverse bytes (Bitcoin txid convention) |
| doubleSha256 | (data: Buffer) => Buffer | SHA-256(SHA-256(data)) |
React Hooks
Import from @volt-protocol/sdk/react to avoid loading React in non-React environments.
import { VoltProvider, useVoltTrigger, useTriggerInfo } from '@volt-protocol/sdk/react';VoltProvider
Wrap your app to provide Volt config to hooks.
<VoltProvider config={{
network: 'testnet',
stacksApiUrl: 'https://api.testnet.hiro.so',
registryContract: 'ST1....volt-registry',
}}>
<App />
</VoltProvider>useVoltTrigger
const { status, btcTxid, stacksTxid, error } = useVoltTrigger({
triggerId: '...',
nonce: '...',
enabled: true, // set false to pause polling
});
// status: 'idle' | 'pending' | 'confirmed' | 'executed' | 'failed'useTriggerInfo
const { data, isLoading, error } = useTriggerInfo(triggerId);
// data: TriggerInfo | undefinedTriggerInfo
interface TriggerInfo {
registrant: string;
targetContract: string;
functionName: string;
feePerRelay: bigint;
feeEscrowBalance: bigint;
active: boolean;
callCount: bigint;
registeredAt: bigint;
lastTriggered: bigint;
}Error Handling
All SDK functions throw VoltError on failure:
import { VoltError } from '@volt-protocol/sdk';
try {
await registerTrigger(client, options);
} catch (err) {
if (err instanceof VoltError) {
console.error(err.code, err.message);
// codes: INVALID_FEE, ESCROW_TOO_LOW, INVALID_TRIGGER_ID,
// INVALID_PRINCIPAL, BROADCAST_FAILED, REGISTER_FAILED,
// TX_ABORTED, TX_TIMEOUT
}
}License
MIT
