@lombard.finance/sdk-common 3.1.0 → 3.4.0

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/README.md CHANGED
File without changes
package/dist/index.cjs CHANGED
@@ -1 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("@bitcoin-js/tiny-secp256k1-asmjs"),r=require("bitcoinjs-lib");function i(t){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const o in t)if(o!=="default"){const c=Object.getOwnPropertyDescriptor(t,o);Object.defineProperty(e,o,c.get?c:{enumerable:!0,get:()=>t[o]})}}return e.default=t,Object.freeze(e)}const p=i(u),n={prod:"prod",testnet:"testnet",stage:"stage",dev:"dev"},s=n.prod;r.initEccLib(p);function a(t,e=n.prod){return console.info(`Getting output script for ${t} (${e})`),`0x${r.address.toOutputScript(t,e===n.prod?r.networks.bitcoin:r.networks.testnet).toString("hex")}`}exports.DEFAULT_ENV=s;exports.Env=n;exports.getOutputScript=a;
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;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/env.ts","../src/providers.ts","../src/utils/bitcoin.ts","../src/utils/get-output-script.ts"],"sourcesContent":["export const Env = {\n prod: 'prod',\n testnet: 'testnet',\n stage: 'stage',\n dev: 'dev',\n ibc: 'ibc',\n} as const;\n\nexport type Env = (typeof Env)[keyof typeof Env];\n\nexport const DEFAULT_ENV: Env = Env.prod;\n","import type { EIP1193Provider } from 'viem';\n\n/**\n * Provider interfaces shared between the core SDK and optional chain modules.\n */\nexport interface EvmProvider extends EIP1193Provider {}\n\nexport interface BtcProvider {\n getAddresses(): Promise<string[]>;\n signMessage(message: string): Promise<string>;\n getNetwork?(): Promise<string>;\n}\n\nexport interface SolanaProvider {\n signMessage(message: Uint8Array | string): Promise<{ signature: Uint8Array }>;\n publicKey?: { toString(): string };\n network?: string;\n}\n\nexport interface SuiProvider {\n getWallet(): unknown;\n getWalletAccount(): unknown;\n}\n\nexport interface StarknetProvider {\n getProvider(): unknown;\n}\n\nexport type AnyProvider =\n | EvmProvider\n | BtcProvider\n | SolanaProvider\n | SuiProvider\n | StarknetProvider;\n\nexport const ProviderKeys = [\n 'evm',\n 'bitcoin',\n 'solana',\n 'sui',\n 'starknet',\n] as const;\nexport type ProviderKey = (typeof ProviderKeys)[number];\n\nexport interface ProviderMap {\n evm: EvmProvider;\n bitcoin: BtcProvider;\n solana: SolanaProvider;\n sui: SuiProvider;\n starknet: StarknetProvider;\n}\n\nexport type ProviderFor<TKey extends ProviderKey> = ProviderMap[TKey];\n","let bitcoinPromise: Promise<typeof import('bitcoinjs-lib')> | null = null;\n\nconst initBitcoin = async (): Promise<typeof import('bitcoinjs-lib')> => {\n try {\n const module = await import('bitcoinjs-lib');\n const ecc = await import('@bitcoinerlab/secp256k1');\n try {\n module.initEccLib(ecc);\n } catch (err) {\n if (!/already initialized/i.test(String(err))) {\n throw err;\n }\n }\n return module;\n } catch (err) {\n // Reset promise so subsequent calls can retry\n bitcoinPromise = null;\n throw new Error(\n `Failed to initialize bitcoinjs-lib: ${err instanceof Error ? err.message : String(err)}. ` +\n 'Ensure bitcoinjs-lib and @bitcoinerlab/secp256k1 peer dependencies are installed.',\n );\n }\n};\n\nexport const getBitcoin = async (): Promise<typeof import('bitcoinjs-lib')> => {\n if (!bitcoinPromise) {\n bitcoinPromise = initBitcoin();\n }\n return bitcoinPromise;\n};\n","import { Env } from '../env';\nimport { getBitcoin } from './bitcoin';\n\n/**\n * Get output script from address.\n *\n * @param address - The address.\n * @param env\n *\n * @returns The output script.\n */\nexport async function getOutputScript(\n address: string,\n env: Env = Env.prod,\n): Promise<`0x${string}`> {\n const bitcoin = await getBitcoin();\n const outputScriptBuf = bitcoin.address.toOutputScript(\n address,\n env === Env.prod ? bitcoin.networks.bitcoin : bitcoin.networks.testnet,\n );\n const outputScript = outputScriptBuf.toString('hex');\n return `0x${outputScript}`;\n}\n"],"names":["Env","DEFAULT_ENV","ProviderKeys","bitcoinPromise","initBitcoin","module","ecc","err","getBitcoin","getOutputScript","address","env","bitcoin"],"mappings":"2hBAAO,MAAMA,EAAM,CACjB,KAAM,OACN,QAAS,UACT,MAAO,QACP,IAAK,MACL,IAAK,KACP,EAIaC,EAAmBD,EAAI,KCyBvBE,EAAe,CAC1B,MACA,UACA,SACA,MACA,UACF,ECzCA,IAAIC,EAAiE,KAErE,MAAMC,EAAc,SAAqD,CACvE,GAAI,CACF,MAAMC,EAAS,KAAM,QAAO,eAAe,EACrCC,EAAM,KAAM,QAAO,yBAAyB,EAClD,GAAI,CACFD,EAAO,WAAWC,CAAG,CACvB,OAASC,EAAK,CACZ,GAAI,CAAC,uBAAuB,KAAK,OAAOA,CAAG,CAAC,EAC1C,MAAMA,CAEV,CACA,OAAOF,CACT,OAASE,EAAK,CAEZ,MAAAJ,EAAiB,KACX,IAAI,MACR,uCAAuCI,aAAe,MAAQA,EAAI,QAAU,OAAOA,CAAG,CAAC,qFAAA,CAG3F,CACF,EAEaC,EAAa,UACnBL,IACHA,EAAiBC,EAAA,GAEZD,GCjBT,eAAsBM,EACpBC,EACAC,EAAWX,EAAI,KACS,CACxB,MAAMY,EAAU,MAAMJ,EAAA,EAMtB,MAAO,KALiBI,EAAQ,QAAQ,eACtCF,EACAC,IAAQX,EAAI,KAAOY,EAAQ,SAAS,QAAUA,EAAQ,SAAS,OAAA,EAE5B,SAAS,KAAK,CAC3B,EAC1B"}
package/dist/index.js CHANGED
@@ -1,20 +1,44 @@
1
- import * as c from "@bitcoin-js/tiny-secp256k1-asmjs";
2
- import { initEccLib as i, address as u, networks as r } from "bitcoinjs-lib";
3
- const t = {
1
+ const r = {
4
2
  prod: "prod",
5
3
  testnet: "testnet",
6
4
  stage: "stage",
7
- dev: "dev"
8
- }, d = t.prod;
9
- i(c);
10
- function f(o, p = t.prod) {
11
- return console.info(`Getting output script for ${o} (${p})`), `0x${u.toOutputScript(
12
- o,
13
- p === t.prod ? r.bitcoin : r.testnet
5
+ dev: "dev",
6
+ ibc: "ibc"
7
+ }, p = r.prod, u = [
8
+ "evm",
9
+ "bitcoin",
10
+ "solana",
11
+ "sui",
12
+ "starknet"
13
+ ];
14
+ let n = null;
15
+ const o = async () => {
16
+ try {
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
+ );
29
+ }
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(
34
+ t,
35
+ e === r.prod ? i.networks.bitcoin : i.networks.testnet
14
36
  ).toString("hex")}`;
15
37
  }
16
38
  export {
17
- d as DEFAULT_ENV,
18
- t as Env,
19
- f as getOutputScript
39
+ p as DEFAULT_ENV,
40
+ r as Env,
41
+ u as ProviderKeys,
42
+ d as getOutputScript
20
43
  };
44
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/env.ts","../src/providers.ts","../src/utils/bitcoin.ts","../src/utils/get-output-script.ts"],"sourcesContent":["export const Env = {\n prod: 'prod',\n testnet: 'testnet',\n stage: 'stage',\n dev: 'dev',\n ibc: 'ibc',\n} as const;\n\nexport type Env = (typeof Env)[keyof typeof Env];\n\nexport const DEFAULT_ENV: Env = Env.prod;\n","import type { EIP1193Provider } from 'viem';\n\n/**\n * Provider interfaces shared between the core SDK and optional chain modules.\n */\nexport interface EvmProvider extends EIP1193Provider {}\n\nexport interface BtcProvider {\n getAddresses(): Promise<string[]>;\n signMessage(message: string): Promise<string>;\n getNetwork?(): Promise<string>;\n}\n\nexport interface SolanaProvider {\n signMessage(message: Uint8Array | string): Promise<{ signature: Uint8Array }>;\n publicKey?: { toString(): string };\n network?: string;\n}\n\nexport interface SuiProvider {\n getWallet(): unknown;\n getWalletAccount(): unknown;\n}\n\nexport interface StarknetProvider {\n getProvider(): unknown;\n}\n\nexport type AnyProvider =\n | EvmProvider\n | BtcProvider\n | SolanaProvider\n | SuiProvider\n | StarknetProvider;\n\nexport const ProviderKeys = [\n 'evm',\n 'bitcoin',\n 'solana',\n 'sui',\n 'starknet',\n] as const;\nexport type ProviderKey = (typeof ProviderKeys)[number];\n\nexport interface ProviderMap {\n evm: EvmProvider;\n bitcoin: BtcProvider;\n solana: SolanaProvider;\n sui: SuiProvider;\n starknet: StarknetProvider;\n}\n\nexport type ProviderFor<TKey extends ProviderKey> = ProviderMap[TKey];\n","let bitcoinPromise: Promise<typeof import('bitcoinjs-lib')> | null = null;\n\nconst initBitcoin = async (): Promise<typeof import('bitcoinjs-lib')> => {\n try {\n const module = await import('bitcoinjs-lib');\n const ecc = await import('@bitcoinerlab/secp256k1');\n try {\n module.initEccLib(ecc);\n } catch (err) {\n if (!/already initialized/i.test(String(err))) {\n throw err;\n }\n }\n return module;\n } catch (err) {\n // Reset promise so subsequent calls can retry\n bitcoinPromise = null;\n throw new Error(\n `Failed to initialize bitcoinjs-lib: ${err instanceof Error ? err.message : String(err)}. ` +\n 'Ensure bitcoinjs-lib and @bitcoinerlab/secp256k1 peer dependencies are installed.',\n );\n }\n};\n\nexport const getBitcoin = async (): Promise<typeof import('bitcoinjs-lib')> => {\n if (!bitcoinPromise) {\n bitcoinPromise = initBitcoin();\n }\n return bitcoinPromise;\n};\n","import { Env } from '../env';\nimport { getBitcoin } from './bitcoin';\n\n/**\n * Get output script from address.\n *\n * @param address - The address.\n * @param env\n *\n * @returns The output script.\n */\nexport async function getOutputScript(\n address: string,\n env: Env = Env.prod,\n): Promise<`0x${string}`> {\n const bitcoin = await getBitcoin();\n const outputScriptBuf = bitcoin.address.toOutputScript(\n address,\n env === Env.prod ? bitcoin.networks.bitcoin : bitcoin.networks.testnet,\n );\n const outputScript = outputScriptBuf.toString('hex');\n return `0x${outputScript}`;\n}\n"],"names":["Env","DEFAULT_ENV","ProviderKeys","bitcoinPromise","initBitcoin","module","ecc","err","getBitcoin","getOutputScript","address","env","bitcoin"],"mappings":"AAAO,MAAMA,IAAM;AAAA,EACjB,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,KAAK;AAAA,EACL,KAAK;AACP,GAIaC,IAAmBD,EAAI,MCyBvBE,IAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;ACzCA,IAAIC,IAAiE;AAErE,MAAMC,IAAc,YAAqD;AACvE,MAAI;AACF,UAAMC,IAAS,MAAM,OAAO,eAAe,GACrCC,IAAM,MAAM,OAAO,yBAAyB;AAClD,QAAI;AACF,MAAAD,EAAO,WAAWC,CAAG;AAAA,IACvB,SAASC,GAAK;AACZ,UAAI,CAAC,uBAAuB,KAAK,OAAOA,CAAG,CAAC;AAC1C,cAAMA;AAAA,IAEV;AACA,WAAOF;AAAA,EACT,SAASE,GAAK;AAEZ,UAAAJ,IAAiB,MACX,IAAI;AAAA,MACR,uCAAuCI,aAAe,QAAQA,EAAI,UAAU,OAAOA,CAAG,CAAC;AAAA,IAAA;AAAA,EAG3F;AACF,GAEaC,IAAa,aACnBL,MACHA,IAAiBC,EAAA,IAEZD;ACjBT,eAAsBM,EACpBC,GACAC,IAAWX,EAAI,MACS;AACxB,QAAMY,IAAU,MAAMJ,EAAA;AAMtB,SAAO,KALiBI,EAAQ,QAAQ;AAAA,IACtCF;AAAA,IACAC,MAAQX,EAAI,OAAOY,EAAQ,SAAS,UAAUA,EAAQ,SAAS;AAAA,EAAA,EAE5B,SAAS,KAAK,CAC3B;AAC1B;"}
package/package.json CHANGED
@@ -1,35 +1,42 @@
1
1
  {
2
2
  "name": "@lombard.finance/sdk-common",
3
- "version": "3.1.0",
3
+ "version": "3.4.0",
4
4
  "exports": {
5
5
  ".": {
6
6
  "types": "./src/index.ts",
7
7
  "import": "./dist/index.js",
8
8
  "require": "./dist/index.cjs"
9
- }
9
+ },
10
+ "./utils/*": "./src/utils/*.ts"
10
11
  },
11
12
  "license": "MIT",
12
13
  "type": "module",
13
14
  "types": "./src/index.ts",
14
- "files": ["dist", "src"],
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
15
19
  "engines": {
16
- "node": ">= 22.14.0"
20
+ "node": ">= 18.0.0"
17
21
  },
18
22
  "scripts": {
19
23
  "build-docs": "rimraf ./sdk-docs && typedoc --out sdk-docs",
20
24
  "build": "yarn types && vite build",
21
- "lint": "biome lint",
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}\"",
22
28
  "types": "tsc --noEmit"
23
29
  },
24
30
  "peerDependencies": {
25
- "@bitcoin-js/tiny-secp256k1-asmjs": "2.2.3",
26
- "bitcoinjs-lib": "6.1.5"
31
+ "@bitcoinerlab/secp256k1": "^1.2.0",
32
+ "bitcoinjs-lib": "^6.1.5"
27
33
  },
28
34
  "devDependencies": {
29
- "@bitcoin-js/tiny-secp256k1-asmjs": "2.2.3",
35
+ "@bitcoinerlab/secp256k1": "1.2.0",
36
+ "bitcoinjs-lib": "6.1.5",
30
37
  "rimraf": "^5",
31
38
  "typedoc": "^0.26.6",
32
39
  "typescript": "^5.4.5",
33
- "bitcoinjs-lib": "6.1.5"
40
+ "vite": "^6.3.5"
34
41
  }
35
42
  }
package/src/env.ts CHANGED
@@ -3,6 +3,7 @@ export const Env = {
3
3
  testnet: 'testnet',
4
4
  stage: 'stage',
5
5
  dev: 'dev',
6
+ ibc: 'ibc',
6
7
  } as const;
7
8
 
8
9
  export type Env = (typeof Env)[keyof typeof Env];
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
+ }
@@ -0,0 +1,30 @@
1
+ let bitcoinPromise: Promise<typeof import('bitcoinjs-lib')> | null = null;
2
+
3
+ const initBitcoin = async (): Promise<typeof import('bitcoinjs-lib')> => {
4
+ try {
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
+ }
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
+ );
22
+ }
23
+ };
24
+
25
+ export const getBitcoin = async (): Promise<typeof import('bitcoinjs-lib')> => {
26
+ if (!bitcoinPromise) {
27
+ bitcoinPromise = initBitcoin();
28
+ }
29
+ return bitcoinPromise;
30
+ };
@@ -0,0 +1,22 @@
1
+ import { address } from 'bitcoinjs-lib';
2
+
3
+ export enum BtcAddressType {
4
+ p2tr = 'p2tr',
5
+ p2wpkh = 'p2wpkh',
6
+ p2wsh = 'p2wsh',
7
+ }
8
+
9
+ export function getBtcAddressType(btcAddress: string): BtcAddressType {
10
+ const { data, version } = address.fromBech32(btcAddress);
11
+
12
+ if (version === 0) {
13
+ if (data.length === 20) return BtcAddressType.p2wpkh;
14
+ if (data.length === 32) return BtcAddressType.p2wsh;
15
+ }
16
+
17
+ if (version === 1) {
18
+ if (data.length === 32) return BtcAddressType.p2tr;
19
+ }
20
+
21
+ throw new Error('Invalid BTC address');
22
+ }
@@ -0,0 +1,85 @@
1
+ import { AxiosError } from 'axios';
2
+
3
+ /**
4
+ * Extract a readable error message from any error object
5
+ * @param error Error object to extract message from
6
+ * @returns A readable error message string
7
+ */
8
+ export function extractErrorMessage(error: unknown): string {
9
+ // If error is already a string, return it
10
+ if (typeof error === 'string') {
11
+ return error;
12
+ }
13
+
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
+ };
24
+
25
+ if (hasDataMessage(error)) {
26
+ return error.data.message;
27
+ }
28
+
29
+ // Handle standard Error objects and Axios errors
30
+ if (error instanceof Error) {
31
+ if ('isAxiosError' in error && error.isAxiosError) {
32
+ return extractAxiosErrorMessage(error as unknown as AxiosError);
33
+ }
34
+ return error.message;
35
+ }
36
+
37
+ // Handle generic objects with a message property
38
+ if (error !== null && typeof error === 'object') {
39
+ if ('message' in error && typeof error.message === 'string') {
40
+ return error.message;
41
+ }
42
+
43
+ // Try to convert object to a readable string
44
+ try {
45
+ return JSON.stringify(error);
46
+ } catch {
47
+ // If JSON.stringify fails, return a generic message
48
+ return 'Unknown error object';
49
+ }
50
+ }
51
+
52
+ return 'Unknown error';
53
+ }
54
+
55
+ /**
56
+ * Extract error message from an Axios error
57
+ * @param error Axios error object
58
+ * @returns Error message from the Axios error
59
+ */
60
+ function extractAxiosErrorMessage(error: AxiosError): string {
61
+ if (error.response) {
62
+ const responseData = error.response.data;
63
+ if (responseData && typeof responseData === 'object') {
64
+ if (
65
+ 'message' in responseData &&
66
+ responseData.message &&
67
+ typeof responseData.message === 'string'
68
+ ) {
69
+ return responseData.message;
70
+ }
71
+ try {
72
+ return JSON.stringify(responseData);
73
+ } catch {
74
+ return 'Error in API response';
75
+ }
76
+ }
77
+ return `HTTP error ${error.response.status}: ${error.response.statusText}`;
78
+ }
79
+
80
+ if (error.message) {
81
+ return error.message;
82
+ }
83
+
84
+ return 'Network error';
85
+ }
@@ -1,8 +1,5 @@
1
- import * as ecc from '@bitcoin-js/tiny-secp256k1-asmjs';
2
- import { address as addressUtils, initEccLib, networks } from 'bitcoinjs-lib';
3
1
  import { Env } from '../env';
4
-
5
- initEccLib(ecc);
2
+ import { getBitcoin } from './bitcoin';
6
3
 
7
4
  /**
8
5
  * Get output script from address.
@@ -12,15 +9,14 @@ initEccLib(ecc);
12
9
  *
13
10
  * @returns The output script.
14
11
  */
15
- export function getOutputScript(
12
+ export async function getOutputScript(
16
13
  address: string,
17
14
  env: Env = Env.prod,
18
- ): `0x${string}` {
19
- console.info(`Getting output script for ${address} (${env})`);
20
-
21
- const outputScriptBuf = addressUtils.toOutputScript(
15
+ ): Promise<`0x${string}`> {
16
+ const bitcoin = await getBitcoin();
17
+ const outputScriptBuf = bitcoin.address.toOutputScript(
22
18
  address,
23
- env === Env.prod ? networks.bitcoin : networks.testnet,
19
+ env === Env.prod ? bitcoin.networks.bitcoin : bitcoin.networks.testnet,
24
20
  );
25
21
  const outputScript = outputScriptBuf.toString('hex');
26
22
  return `0x${outputScript}`;
@@ -0,0 +1,17 @@
1
+ import BigNumber from 'bignumber.js';
2
+
3
+ export function toBaseDenomination(
4
+ input: BigNumber.Value,
5
+ decimalPlaces: number,
6
+ ) {
7
+ return BigNumber(input)
8
+ .multipliedBy(BigNumber(10).pow(decimalPlaces))
9
+ .decimalPlaces(0, BigNumber.ROUND_HALF_UP);
10
+ }
11
+
12
+ export function fromBaseDenomination(
13
+ input: BigNumber.Value,
14
+ decimalPlaces: number,
15
+ ) {
16
+ return BigNumber(input).dividedBy(BigNumber(10).pow(decimalPlaces));
17
+ }
package/src/vite-env.d.ts CHANGED
File without changes