@lombard.finance/sdk-common 3.3.1 → 3.4.0-canary.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var s=Object.create;var p=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var l=Object.getPrototypeOf,S=Object.prototype.hasOwnProperty;var b=(t,i,o,n)=>{if(i&&typeof i=="object"||typeof i=="function")for(let r of d(i))!S.call(t,r)&&r!==o&&p(t,r,{get:()=>i[r],enumerable:!(n=a(i,r))||n.enumerable});return t};var u=(t,i,o)=>(o=t!=null?s(l(t)):{},b(i||!t||!t.__esModule?p(o,"default",{value:t,enumerable:!0}):o,t));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e={prod:"prod",testnet:"testnet",stage:"stage",dev:"dev",ibc:"ibc"},g=e.prod;let c;const E=async()=>{const t=await import("bitcoinjs-lib"),i=await import("@bitcoinerlab/secp256k1");try{t.initEccLib(i)}catch(o){if(!/already initialized/i.test(String(o)))throw o}c=t};E().catch(t=>{console.error("Failed to initialize bitcoinjs-lib ECC library",t)});function f(t,i=e.prod){return console.info(`Getting output script for ${t} (${i})`),`0x${c.address.toOutputScript(t,i===e.prod?c.networks.bitcoin:c.networks.testnet).toString("hex")}`}exports.DEFAULT_ENV=g;exports.Env=e;exports.getOutputScript=f;
1
+ "use strict";var u=Object.create;var s=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var d=Object.getOwnPropertyNames;var l=Object.getPrototypeOf,b=Object.prototype.hasOwnProperty;var S=(t,i,e,c)=>{if(i&&typeof i=="object"||typeof i=="function")for(let n of d(i))!b.call(t,n)&&n!==e&&s(t,n,{get:()=>i[n],enumerable:!(c=p(i,n))||c.enumerable});return t};var a=(t,i,e)=>(e=t!=null?u(l(t)):{},S(i||!t||!t.__esModule?s(e,"default",{value:t,enumerable:!0}):e,t));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o={prod:"prod",testnet:"testnet",stage:"stage",dev:"dev",ibc:"ibc"},g=o.prod,y=["evm","bitcoin","solana","sui","starknet"];let r=null;const E=async()=>{try{const t=await import("bitcoinjs-lib"),i=await import("@bitcoinerlab/secp256k1");try{t.initEccLib(i)}catch(e){if(!/already initialized/i.test(String(e)))throw e}return t}catch(t){throw r=null,new Error(`Failed to initialize bitcoinjs-lib: ${t instanceof Error?t.message:String(t)}. Ensure bitcoinjs-lib and @bitcoinerlab/secp256k1 peer dependencies are installed.`)}},v=async()=>(r||(r=E()),r);async function w(t,i=o.prod){const e=await v();return`0x${e.address.toOutputScript(t,i===o.prod?e.networks.bitcoin:e.networks.testnet).toString("hex")}`}exports.DEFAULT_ENV=g;exports.Env=o;exports.ProviderKeys=y;exports.getOutputScript=w;
package/dist/index.js CHANGED
@@ -4,29 +4,40 @@ const r = {
4
4
  stage: "stage",
5
5
  dev: "dev",
6
6
  ibc: "ibc"
7
- }, p = r.prod;
8
- let o;
9
- const n = async () => {
10
- const t = await import("bitcoinjs-lib"), i = await import("@bitcoinerlab/secp256k1");
7
+ }, p = r.prod, u = [
8
+ "evm",
9
+ "bitcoin",
10
+ "solana",
11
+ "sui",
12
+ "starknet"
13
+ ];
14
+ let n = null;
15
+ const o = async () => {
11
16
  try {
12
- t.initEccLib(i);
13
- } catch (c) {
14
- if (!/already initialized/i.test(String(c)))
15
- throw c;
17
+ const t = await import("bitcoinjs-lib"), e = await import("@bitcoinerlab/secp256k1");
18
+ try {
19
+ t.initEccLib(e);
20
+ } catch (i) {
21
+ if (!/already initialized/i.test(String(i)))
22
+ throw i;
23
+ }
24
+ return t;
25
+ } catch (t) {
26
+ throw n = null, new Error(
27
+ `Failed to initialize bitcoinjs-lib: ${t instanceof Error ? t.message : String(t)}. Ensure bitcoinjs-lib and @bitcoinerlab/secp256k1 peer dependencies are installed.`
28
+ );
16
29
  }
17
- o = t;
18
- };
19
- n().catch((t) => {
20
- console.error("Failed to initialize bitcoinjs-lib ECC library", t);
21
- });
22
- function s(t, i = r.prod) {
23
- return console.info(`Getting output script for ${t} (${i})`), `0x${o.address.toOutputScript(
30
+ }, c = async () => (n || (n = o()), n);
31
+ async function d(t, e = r.prod) {
32
+ const i = await c();
33
+ return `0x${i.address.toOutputScript(
24
34
  t,
25
- i === r.prod ? o.networks.bitcoin : o.networks.testnet
35
+ e === r.prod ? i.networks.bitcoin : i.networks.testnet
26
36
  ).toString("hex")}`;
27
37
  }
28
38
  export {
29
39
  p as DEFAULT_ENV,
30
40
  r as Env,
31
- s as getOutputScript
41
+ u as ProviderKeys,
42
+ d as getOutputScript
32
43
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lombard.finance/sdk-common",
3
- "version": "3.3.1",
3
+ "version": "3.4.0-canary.2",
4
4
  "exports": {
5
5
  ".": {
6
6
  "types": "./src/index.ts",
@@ -17,27 +17,26 @@
17
17
  "src"
18
18
  ],
19
19
  "engines": {
20
- "node": ">= 22.14.0"
20
+ "node": ">= 18.0.0"
21
21
  },
22
22
  "scripts": {
23
23
  "build-docs": "rimraf ./sdk-docs && typedoc --out sdk-docs",
24
24
  "build": "yarn types && vite build",
25
- "lint": "biome lint --diagnostic-level=error",
25
+ "lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings=0",
26
+ "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
27
+ "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"",
26
28
  "types": "tsc --noEmit"
27
29
  },
28
30
  "peerDependencies": {
29
- "@bitcoinerlab/secp256k1": "1.2.0",
30
- "bitcoinjs-lib": "6.1.5"
31
+ "@bitcoinerlab/secp256k1": "^1.2.0",
32
+ "bitcoinjs-lib": "^6.1.5"
31
33
  },
32
34
  "devDependencies": {
33
- "@biomejs/biome": "^1.9.4",
34
35
  "@bitcoinerlab/secp256k1": "1.2.0",
35
36
  "bitcoinjs-lib": "6.1.5",
36
37
  "rimraf": "^5",
37
38
  "typedoc": "^0.26.6",
38
- "typescript": "^5.4.5"
39
- },
40
- "dependencies": {
39
+ "typescript": "^5.4.5",
41
40
  "vite": "^6.3.5"
42
41
  }
43
42
  }
package/src/index.ts CHANGED
@@ -1,2 +1,7 @@
1
1
  export * from './env';
2
+ export * from './modules';
3
+ export * from './providers';
2
4
  export * from './utils/get-output-script';
5
+
6
+ // Chain services (what modules provide)
7
+ export * from './services';
package/src/modules.ts ADDED
@@ -0,0 +1,108 @@
1
+ import type { Env } from './env';
2
+ import type { ProviderFor, ProviderKey } from './providers';
3
+
4
+ /**
5
+ * Shared token for cross-module state
6
+ */
7
+ export type SharedToken<T> = symbol & { __sharedToken?: T };
8
+
9
+ /**
10
+ * Context provided to modules during registration
11
+ */
12
+ export interface RegisterContext {
13
+ env: Env;
14
+ getProvider<TKey extends ProviderKey>(key: TKey): Promise<ProviderFor<TKey>>;
15
+ getShared<T>(token: SharedToken<T>): T | undefined;
16
+ setShared<T>(token: SharedToken<T>, value: T): void;
17
+ }
18
+
19
+ /**
20
+ * SDK Module - Generic module interface
21
+ *
22
+ * Base interface for all SDK modules. Modules are thin factories that
23
+ * instantiate services and register them with the SDK.
24
+ *
25
+ * Use this for non-chain-specific modules (e.g., apiModule).
26
+ *
27
+ * @template TId - Unique module identifier
28
+ * @template TService - Service interface the module provides
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * function apiModule(): SdkModule<'api', ApiService> {
33
+ * return {
34
+ * id: 'api',
35
+ * register(ctx) {
36
+ * return new ApiService(ctx.env);
37
+ * },
38
+ * };
39
+ * }
40
+ * ```
41
+ */
42
+ export interface SdkModule<TId extends string = string, TService = unknown> {
43
+ /** Unique module identifier */
44
+ id: TId;
45
+ /** Provider keys required by this module */
46
+ requiresProviders?: ProviderKey[];
47
+ /** Factory function that creates the service */
48
+ register(ctx: RegisterContext): TService;
49
+ }
50
+
51
+ /**
52
+ * Chain Module - Module interface for blockchain-specific services
53
+ *
54
+ * Extension of SdkModule for chain-specific modules. Adds a `chain` field
55
+ * to identify the blockchain type.
56
+ *
57
+ * Use this for chain-specific modules (e.g., btcModule, evmModule, solanaModule).
58
+ *
59
+ * @template TChain - Chain type identifier (e.g., 'btc', 'evm', 'solana')
60
+ * @template TService - Service interface the module provides
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * function evmModule(): ChainModule<'evm', EvmService> {
65
+ * return {
66
+ * id: 'evm',
67
+ * chain: 'evm',
68
+ * register(ctx) {
69
+ * return new EvmService(ctx.env);
70
+ * },
71
+ * };
72
+ * }
73
+ * ```
74
+ */
75
+ export interface ChainModule<TChain extends string = string, TService = unknown>
76
+ extends SdkModule<TChain, TService> {
77
+ /** Chain type this module provides services for */
78
+ chain: TChain;
79
+ }
80
+
81
+ /**
82
+ * Any module type (either SdkModule or ChainModule)
83
+ */
84
+ export type AnyModule =
85
+ | SdkModule<string, unknown>
86
+ | ChainModule<string, unknown>;
87
+
88
+ /**
89
+ * Extract module ID type from a module
90
+ */
91
+ export type ModuleId<TModule> =
92
+ TModule extends SdkModule<infer TId, unknown> ? TId : never;
93
+
94
+ /**
95
+ * Extract service type from a module by ID
96
+ */
97
+ export type ServiceOf<TModule extends SdkModule<string, unknown>, TId extends string> =
98
+ TModule extends SdkModule<TId, infer TService> ? TService : never;
99
+
100
+ /**
101
+ * @deprecated Use ServiceOf instead
102
+ */
103
+ export type CapabilitiesOf<
104
+ TModule extends ChainModule<string, unknown>,
105
+ TId extends string,
106
+ > = TModule extends ChainModule<string, infer TCapabilities> & { id: TId }
107
+ ? TCapabilities
108
+ : never;
@@ -0,0 +1,53 @@
1
+ import type { EIP1193Provider } from 'viem';
2
+
3
+ /**
4
+ * Provider interfaces shared between the core SDK and optional chain modules.
5
+ */
6
+ export interface EvmProvider extends EIP1193Provider {}
7
+
8
+ export interface BtcProvider {
9
+ getAddresses(): Promise<string[]>;
10
+ signMessage(message: string): Promise<string>;
11
+ getNetwork?(): Promise<string>;
12
+ }
13
+
14
+ export interface SolanaProvider {
15
+ signMessage(message: Uint8Array | string): Promise<{ signature: Uint8Array }>;
16
+ publicKey?: { toString(): string };
17
+ network?: string;
18
+ }
19
+
20
+ export interface SuiProvider {
21
+ getWallet(): unknown;
22
+ getWalletAccount(): unknown;
23
+ }
24
+
25
+ export interface StarknetProvider {
26
+ getProvider(): unknown;
27
+ }
28
+
29
+ export type AnyProvider =
30
+ | EvmProvider
31
+ | BtcProvider
32
+ | SolanaProvider
33
+ | SuiProvider
34
+ | StarknetProvider;
35
+
36
+ export const ProviderKeys = [
37
+ 'evm',
38
+ 'bitcoin',
39
+ 'solana',
40
+ 'sui',
41
+ 'starknet',
42
+ ] as const;
43
+ export type ProviderKey = (typeof ProviderKeys)[number];
44
+
45
+ export interface ProviderMap {
46
+ evm: EvmProvider;
47
+ bitcoin: BtcProvider;
48
+ solana: SolanaProvider;
49
+ sui: SuiProvider;
50
+ starknet: StarknetProvider;
51
+ }
52
+
53
+ export type ProviderFor<TKey extends ProviderKey> = ProviderMap[TKey];
@@ -0,0 +1,140 @@
1
+ /**
2
+ * API Service
3
+ *
4
+ * Operations for Lombard backend API provided by apiModule() or built-in.
5
+ * Used by actions for deposit address generation and tracking.
6
+ */
7
+
8
+ /**
9
+ * Destination chain ID (EVM numeric, Solana string, etc.)
10
+ */
11
+ export type DestinationChainId = number | string;
12
+
13
+ /**
14
+ * Deposit information returned from API
15
+ */
16
+ export interface DepositInfo {
17
+ depositAddress: string;
18
+ blockHeight?: number;
19
+ isClaimed: boolean;
20
+ txid?: string;
21
+ amount?: string;
22
+ }
23
+
24
+ /**
25
+ * Parameters for generating a deposit address
26
+ */
27
+ export interface GenerateDepositAddressParams {
28
+ /** Recipient address on destination chain */
29
+ address: string;
30
+ /** Destination chain ID */
31
+ chainId: DestinationChainId;
32
+ /** Authorization signature */
33
+ signature: string;
34
+ /**
35
+ * Token to mint (LBTC, BTCb, etc.)
36
+ * REQUIRED - determines which token contract address is used
37
+ */
38
+ token: string;
39
+ /** EIP-712 typed data (for EVM) */
40
+ eip712Data?: string;
41
+ /** Signature data (for stake and bake) */
42
+ signatureData?: string;
43
+ /** Public key (for non-EVM chains) */
44
+ pubKey?: string;
45
+ /** Partner ID for attribution */
46
+ partnerId?: string;
47
+ /** Referral code */
48
+ referrerCode?: string;
49
+ }
50
+
51
+ /**
52
+ * Parameters for getting an existing deposit address
53
+ */
54
+ export interface GetDepositAddressParams {
55
+ address: string;
56
+ chainId: DestinationChainId;
57
+ /** Token to look up (LBTC, BTCb, etc.) - REQUIRED */
58
+ token: string;
59
+ partnerId?: string;
60
+ }
61
+
62
+ /**
63
+ * Fee signature storage parameters
64
+ */
65
+ export interface StoreFeeSignatureParams {
66
+ address: string;
67
+ signature: string;
68
+ typedData: string;
69
+ /** Token address to distinguish LBTC vs BTC.b signatures */
70
+ tokenAddress?: string;
71
+ }
72
+
73
+ /**
74
+ * Fee signature retrieval parameters
75
+ */
76
+ export interface GetFeeSignatureParams {
77
+ address: string;
78
+ chainId: number;
79
+ /** Token address to distinguish LBTC vs BTC.b signatures */
80
+ tokenAddress?: string;
81
+ }
82
+
83
+ /**
84
+ * Fee signature result
85
+ */
86
+ export interface FeeSignatureResult {
87
+ hasSignature: boolean;
88
+ signature?: string;
89
+ typedData?: string;
90
+ expirationDate?: string;
91
+ }
92
+
93
+ /**
94
+ * Stake and bake signature storage parameters
95
+ */
96
+ export interface StoreStakeAndBakeParams {
97
+ signature: string;
98
+ typedData: string;
99
+ }
100
+
101
+ /**
102
+ * API Service Interface
103
+ *
104
+ * Provides all Lombard backend API operations.
105
+ * Injected into contexts as `ctx.api`.
106
+ */
107
+ export interface ApiService {
108
+ /**
109
+ * Generate a new BTC deposit address
110
+ */
111
+ generateDepositAddress(params: GenerateDepositAddressParams): Promise<string>;
112
+
113
+ /**
114
+ * Get existing deposit address for a recipient
115
+ * Returns undefined if no address exists
116
+ */
117
+ getDepositAddress(
118
+ params: GetDepositAddressParams,
119
+ ): Promise<string | undefined>;
120
+
121
+ /**
122
+ * Get deposits for an address
123
+ */
124
+ getDeposits(address: string): Promise<DepositInfo[]>;
125
+
126
+ /**
127
+ * Store network fee signature
128
+ */
129
+ storeFeeSignature(params: StoreFeeSignatureParams): Promise<void>;
130
+
131
+ /**
132
+ * Get stored network fee signature
133
+ */
134
+ getFeeSignature(params: GetFeeSignatureParams): Promise<FeeSignatureResult>;
135
+
136
+ /**
137
+ * Store stake and bake signature
138
+ */
139
+ storeStakeAndBakeSignature(params: StoreStakeAndBakeParams): Promise<void>;
140
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * BTC Chain Service
3
+ *
4
+ * Operations for Bitcoin blockchain provided by btcModule().
5
+ * Used by strategies for BTC deposit monitoring and address operations.
6
+ */
7
+
8
+ /**
9
+ * Network mode for Bitcoin operations
10
+ */
11
+ export type BtcNetworkMode = 'mainnet' | 'testnet';
12
+
13
+ /**
14
+ * BTC Service Interface
15
+ *
16
+ * Provides Bitcoin-specific operations.
17
+ * Injected into BtcCoreContext as `ctx.btc`.
18
+ */
19
+ export interface BtcService {
20
+ /**
21
+ * Get current block height from mempool
22
+ */
23
+ getCurrentBlockHeight(network: BtcNetworkMode): Promise<number>;
24
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * EVM Chain Service
3
+ *
4
+ * Operations for EVM-compatible blockchains provided by evmModule().
5
+ * Used by strategies for contract interactions and fee authorization.
6
+ */
7
+
8
+ import type { EvmProvider } from '../providers';
9
+
10
+ /**
11
+ * Chain ID type (numeric EVM chain identifier)
12
+ */
13
+ export type EvmChainId = number;
14
+
15
+ /**
16
+ * Fee authorization result
17
+ */
18
+ export interface FeeAuthorizationResult {
19
+ signature: string;
20
+ typedData?: string;
21
+ }
22
+
23
+ /**
24
+ * Stored fee signature result (from resume flow)
25
+ */
26
+ export interface StoredFeeSignature {
27
+ hasSignature: boolean;
28
+ signature?: string;
29
+ typedData?: string;
30
+ expirationDate?: string;
31
+ isDelayed?: boolean;
32
+ }
33
+
34
+ /**
35
+ * Network fee signing parameters
36
+ */
37
+ export interface SignNetworkFeeParams {
38
+ fee: string;
39
+ account: string;
40
+ chainId: EvmChainId;
41
+ provider: EvmProvider;
42
+ /** Token to sign for (LBTC or BTCb). Defaults to LBTC for backwards compatibility. */
43
+ token?: string;
44
+ }
45
+
46
+ /**
47
+ * Network fee signing result
48
+ */
49
+ export interface SignNetworkFeeResult {
50
+ signature: string;
51
+ typedData: string;
52
+ }
53
+
54
+ /**
55
+ * Stake and bake signing parameters
56
+ */
57
+ export interface SignStakeAndBakeParams {
58
+ value: string;
59
+ account: string;
60
+ chainId: EvmChainId;
61
+ provider: EvmProvider;
62
+ vaultKey: string;
63
+ token: string;
64
+ }
65
+
66
+ /**
67
+ * EVM Service Interface
68
+ *
69
+ * Provides all EVM-specific operations.
70
+ * Injected into contexts as `ctx.evm`.
71
+ */
72
+ export interface EvmService {
73
+ /**
74
+ * Get minting fee for a chain
75
+ * @param chainId - The chain ID
76
+ * @param token - Optional token (defaults to LBTC). Use 'BTCb' for BTC.b deposits.
77
+ */
78
+ getMintingFee(chainId: EvmChainId, token?: string): Promise<string>;
79
+
80
+ /**
81
+ * Sign network fee authorization (EIP-712)
82
+ */
83
+ signNetworkFee(params: SignNetworkFeeParams): Promise<SignNetworkFeeResult>;
84
+
85
+ /**
86
+ * Get stake and bake fee for a vault
87
+ */
88
+ getStakeAndBakeFee(chainId: EvmChainId, vaultKey: string): Promise<string>;
89
+
90
+ /**
91
+ * Sign stake and bake authorization
92
+ */
93
+ signStakeAndBake(
94
+ params: SignStakeAndBakeParams,
95
+ ): Promise<SignNetworkFeeResult>;
96
+
97
+ /**
98
+ * Sign LBTC destination address (for non-Ethereum EVM chains)
99
+ */
100
+ signLbtcDestination(params: {
101
+ chainId: EvmChainId;
102
+ address: string;
103
+ provider: EvmProvider;
104
+ }): Promise<{ signature: string }>;
105
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Chain Services
3
+ *
4
+ * Interfaces for what modules provide to actions.
5
+ * Each chain module (btcModule, evmModule, solanaModule, etc.)
6
+ * returns a service implementing one of these interfaces.
7
+ *
8
+ * Service-First Pattern:
9
+ * - Service interfaces define the contract
10
+ * - Service implementation classes contain the logic
11
+ * - Modules are thin factories that instantiate services
12
+ */
13
+
14
+ // Core services (provided by sdk)
15
+ export * from './api';
16
+ export * from './btc';
17
+ export * from './evm';
18
+
19
+ // External chain services (provided by sdk-solana, sdk-sui, etc.)
20
+ export * from './solana';
21
+ export * from './starknet';
22
+ export * from './sui';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Solana Chain Service
3
+ *
4
+ * Operations for Solana blockchain provided by solanaModule().
5
+ * Used by strategies that mint/send to Solana.
6
+ */
7
+ export interface SolanaService {
8
+ /**
9
+ * Sign LBTC destination address for Solana minting
10
+ * Required to generate a BTC deposit address for Solana destination
11
+ */
12
+ signLbtcDestination(args: {
13
+ network: string;
14
+ }): Promise<{ signature: string }>;
15
+
16
+ /**
17
+ * Unstake LBTC on Solana to receive BTC
18
+ *
19
+ * Burns LBTC on Solana and releases BTC to the provided Bitcoin address.
20
+ *
21
+ * @param args.amount - Amount of LBTC to unstake in base units (satoshis)
22
+ * @param args.btcAddress - Bitcoin address to receive BTC
23
+ * @param args.network - Solana network ('mainnet-beta', 'devnet', 'testnet')
24
+ * @returns Transaction signature
25
+ */
26
+ unstake(args: {
27
+ amount: string;
28
+ btcAddress: string;
29
+ network: string;
30
+ }): Promise<{ txHash: string }>;
31
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Starknet Chain Service
3
+ *
4
+ * Operations for Starknet blockchain provided by starknetModule().
5
+ * Used by strategies that mint/send to Starknet.
6
+ */
7
+ export interface StarknetService {
8
+ /**
9
+ * Sign LBTC destination address for Starknet minting
10
+ * Required to generate a BTC deposit address for Starknet destination
11
+ */
12
+ signLbtcDestination(args: {
13
+ chainId: string;
14
+ }): Promise<{ signature: string; pubKey: string }>;
15
+
16
+ /**
17
+ * Unstake LBTC on Starknet to receive BTC
18
+ *
19
+ * Burns LBTC on Starknet and releases BTC to the provided Bitcoin address.
20
+ *
21
+ * @param args.amount - Amount of LBTC to unstake (BTC decimal, e.g., "0.001")
22
+ * @param args.btcAddress - Bitcoin address to receive BTC
23
+ * @param args.env - Environment (prod, testnet, stage, dev)
24
+ * @returns Transaction hash
25
+ */
26
+ unstake(args: {
27
+ amount: string;
28
+ btcAddress: string;
29
+ env: string;
30
+ }): Promise<{ txHash: string }>;
31
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Sui Chain Service
3
+ *
4
+ * Operations for Sui blockchain provided by suiModule().
5
+ * Used by strategies that mint/send to Sui.
6
+ */
7
+ export interface SuiService {
8
+ /**
9
+ * Sign LBTC destination address for Sui minting
10
+ * Required to generate a BTC deposit address for Sui destination
11
+ */
12
+ signLbtcDestination(args: {
13
+ chainId: string;
14
+ }): Promise<{ signature: string }>;
15
+
16
+ /**
17
+ * Unstake LBTC on Sui to receive BTC
18
+ *
19
+ * Burns LBTC on Sui and releases BTC to the provided Bitcoin address.
20
+ *
21
+ * @param args.amount - Amount of LBTC to unstake (BTC decimal, e.g., "0.001")
22
+ * @param args.btcAddress - Bitcoin address to receive BTC
23
+ * @param args.chainId - Sui chain ID ('sui:mainnet', 'sui:testnet')
24
+ * @param args.env - Environment (prod, testnet, stage, dev)
25
+ * @returns Transaction digest (hash)
26
+ */
27
+ unstake(args: {
28
+ amount: string;
29
+ btcAddress: string;
30
+ chainId: string;
31
+ env: string;
32
+ }): Promise<{ txHash: string }>;
33
+ }
@@ -1,21 +1,30 @@
1
- let bitcoin: typeof import('bitcoinjs-lib');
1
+ let bitcoinPromise: Promise<typeof import('bitcoinjs-lib')> | null = null;
2
2
 
3
- const initBitcoin = async () => {
4
- const module = await import('bitcoinjs-lib');
5
- const ecc = await import('@bitcoinerlab/secp256k1');
3
+ const initBitcoin = async (): Promise<typeof import('bitcoinjs-lib')> => {
6
4
  try {
7
- module.initEccLib(ecc);
8
- } catch (err) {
9
- if (!/already initialized/i.test(String(err))) {
10
- throw err;
5
+ const module = await import('bitcoinjs-lib');
6
+ const ecc = await import('@bitcoinerlab/secp256k1');
7
+ try {
8
+ module.initEccLib(ecc);
9
+ } catch (err) {
10
+ if (!/already initialized/i.test(String(err))) {
11
+ throw err;
12
+ }
11
13
  }
14
+ return module;
15
+ } catch (err) {
16
+ // Reset promise so subsequent calls can retry
17
+ bitcoinPromise = null;
18
+ throw new Error(
19
+ `Failed to initialize bitcoinjs-lib: ${err instanceof Error ? err.message : String(err)}. ` +
20
+ 'Ensure bitcoinjs-lib and @bitcoinerlab/secp256k1 peer dependencies are installed.',
21
+ );
12
22
  }
13
- bitcoin = module;
14
23
  };
15
24
 
16
- void initBitcoin().catch(err => {
17
- // Surface initialization failures during development but swallow to avoid unhandled rejections.
18
- console.error('Failed to initialize bitcoinjs-lib ECC library', err);
19
- });
20
-
21
- export { bitcoin };
25
+ export const getBitcoin = async (): Promise<typeof import('bitcoinjs-lib')> => {
26
+ if (!bitcoinPromise) {
27
+ bitcoinPromise = initBitcoin();
28
+ }
29
+ return bitcoinPromise;
30
+ };
package/src/utils/err.ts CHANGED
@@ -11,10 +11,16 @@ export function extractErrorMessage(error: unknown): string {
11
11
  return error;
12
12
  }
13
13
 
14
- // Check for data.message pattern
15
- // biome-ignore lint/suspicious/noExplicitAny: unknown err
16
- const hasDataMessage = (err: any): err is { data: { message: string } } =>
17
- err?.data?.message && typeof err.data.message === 'string';
14
+ // Check for data.message pattern (common in API error responses)
15
+ const hasDataMessage = (
16
+ err: unknown,
17
+ ): err is { data: { message: string } } => {
18
+ if (err === null || typeof err !== 'object') return false;
19
+ if (!('data' in err) || err.data === null || typeof err.data !== 'object')
20
+ return false;
21
+ if (!('message' in err.data)) return false;
22
+ return typeof err.data.message === 'string';
23
+ };
18
24
 
19
25
  if (hasDataMessage(error)) {
20
26
  return error.data.message;
@@ -1,5 +1,5 @@
1
- import { bitcoin } from './bitcoin';
2
1
  import { Env } from '../env';
2
+ import { getBitcoin } from './bitcoin';
3
3
 
4
4
  /**
5
5
  * Get output script from address.
@@ -9,12 +9,11 @@ import { Env } from '../env';
9
9
  *
10
10
  * @returns The output script.
11
11
  */
12
- export function getOutputScript(
12
+ export async function getOutputScript(
13
13
  address: string,
14
14
  env: Env = Env.prod,
15
- ): `0x${string}` {
16
- console.info(`Getting output script for ${address} (${env})`);
17
-
15
+ ): Promise<`0x${string}`> {
16
+ const bitcoin = await getBitcoin();
18
17
  const outputScriptBuf = bitcoin.address.toOutputScript(
19
18
  address,
20
19
  env === Env.prod ? bitcoin.networks.bitcoin : bitcoin.networks.testnet,