@emblemvault/auth-sdk 2.3.5 → 2.3.6
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.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/signers/bitcoin.d.ts +181 -0
- package/dist/signers/bitcoin.d.ts.map +1 -0
- package/dist/signers/bitcoin.js +2 -0
- package/dist/signers/bitcoin.js.map +1 -0
- package/dist/signers/bitcoin.mjs +2 -0
- package/dist/signers/bitcoin.mjs.map +1 -0
- package/dist/signers/index.d.ts +1 -0
- package/dist/signers/index.d.ts.map +1 -1
- package/dist/signers/index.js +1 -1
- package/dist/signers/index.js.map +1 -1
- package/dist/signers/index.mjs +1 -1
- package/dist/signers/index.mjs.map +1 -1
- package/dist/types/signers.d.ts +16 -0
- package/dist/types/signers.d.ts.map +1 -1
- package/package.json +8 -2
package/dist/index.d.ts
CHANGED
|
@@ -4,5 +4,6 @@ export type { EmblemAuthConfig, ModalMode, AuthNetwork, AuthSession, AuthUser, S
|
|
|
4
4
|
export type { EmblemEthersWallet } from './signers/ethers';
|
|
5
5
|
export type { EmblemSolanaSigner, SolanaSignerInterface, SolanaKitSignerInterface } from './signers/solana';
|
|
6
6
|
export type { EmblemWeb3Adapter } from './signers/web3';
|
|
7
|
+
export type { EmblemBitcoinSigner, BitcoinSignerInterface, BitcoinVaultInfo, BitcoinAddressType, BitcoinNetwork, BitcoinToSignInput, BitcoinSignOptions, BitcoinSignResult, } from './signers/bitcoin';
|
|
7
8
|
export { EmblemAuthSDK as default } from './EmblemAuthSDK';
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,YAAY,EAEV,gBAAgB,EAChB,SAAS,EACT,WAAW,EAGX,WAAW,EACX,QAAQ,EACR,kBAAkB,EAGlB,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,SAAS,EAGT,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAGhB,GAAG,EACH,YAAY,EACZ,eAAe,GAChB,MAAM,SAAS,CAAC;AAGjB,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5G,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGlD,YAAY,EAEV,gBAAgB,EAChB,SAAS,EACT,WAAW,EAGX,WAAW,EACX,QAAQ,EACR,kBAAkB,EAGlB,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,SAAS,EAGT,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAGhB,GAAG,EACH,YAAY,EACZ,eAAe,GAChB,MAAM,SAAS,CAAC;AAGjB,YAAY,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5G,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import type { SignerConfig, SignerVaultInfo } from '../types/signers';
|
|
2
|
+
/**
|
|
3
|
+
* Bitcoin address types
|
|
4
|
+
*/
|
|
5
|
+
export type BitcoinAddressType = 'p2pkh' | 'p2wpkh' | 'p2tr' | 'p2sh';
|
|
6
|
+
/**
|
|
7
|
+
* Bitcoin network type
|
|
8
|
+
*/
|
|
9
|
+
export type BitcoinNetwork = 'mainnet' | 'testnet';
|
|
10
|
+
/**
|
|
11
|
+
* Input signing instruction for PSBTs
|
|
12
|
+
* Compatible with UniSat wallet-api's UserToSignInput format
|
|
13
|
+
*/
|
|
14
|
+
export interface BitcoinToSignInput {
|
|
15
|
+
/** Input index in the PSBT */
|
|
16
|
+
index: number;
|
|
17
|
+
/** Bitcoin address associated with this input */
|
|
18
|
+
address?: string;
|
|
19
|
+
/** Public key (hex) for this input */
|
|
20
|
+
publicKey?: string;
|
|
21
|
+
/** Sighash types for signing */
|
|
22
|
+
sighashTypes?: number[];
|
|
23
|
+
/** Tap leaf hash to sign (hex) for Taproot script path spending */
|
|
24
|
+
tapLeafHashToSign?: string;
|
|
25
|
+
/** Use tweaked signer for Taproot */
|
|
26
|
+
useTweakedSigner?: boolean;
|
|
27
|
+
/** Disable tweak signer for Taproot */
|
|
28
|
+
disableTweakSigner?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Bitcoin sign transaction options
|
|
32
|
+
*/
|
|
33
|
+
export interface BitcoinSignOptions {
|
|
34
|
+
/** Transaction type determines signing algorithm (ECDSA for p2pkh/p2wpkh, Schnorr for p2tr) */
|
|
35
|
+
transactionType: 'p2pkh' | 'p2wpkh' | 'p2tr';
|
|
36
|
+
/** Per-input signing instructions for complex PSBTs */
|
|
37
|
+
toSignInputs?: BitcoinToSignInput[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Response from signing a Bitcoin PSBT
|
|
41
|
+
*/
|
|
42
|
+
export interface BitcoinSignResult {
|
|
43
|
+
success: boolean;
|
|
44
|
+
/** Signed PSBT in base64 format */
|
|
45
|
+
signedPsbt: string;
|
|
46
|
+
/** Final transaction hex (ready for broadcast) */
|
|
47
|
+
signedTxHex: string;
|
|
48
|
+
/** Raw signature */
|
|
49
|
+
signature: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extended vault info including Bitcoin public key and derived addresses
|
|
53
|
+
*/
|
|
54
|
+
export interface BitcoinVaultInfo extends SignerVaultInfo {
|
|
55
|
+
/** Bitcoin public key (compressed, hex) */
|
|
56
|
+
btcPubkey: string;
|
|
57
|
+
/** Derived Bitcoin addresses */
|
|
58
|
+
btcAddresses?: {
|
|
59
|
+
/** Legacy (1...) */
|
|
60
|
+
p2pkh: string;
|
|
61
|
+
/** Native SegWit (bc1q...) */
|
|
62
|
+
p2wpkh: string;
|
|
63
|
+
/** Taproot (bc1p...) */
|
|
64
|
+
p2tr: string;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Bitcoin signer interface compatible with common Bitcoin libraries
|
|
69
|
+
*/
|
|
70
|
+
export interface BitcoinSignerInterface {
|
|
71
|
+
/** Bitcoin public key (compressed, hex) */
|
|
72
|
+
publicKey: string;
|
|
73
|
+
/** Derived Bitcoin addresses */
|
|
74
|
+
addresses: {
|
|
75
|
+
p2pkh: string;
|
|
76
|
+
p2wpkh: string;
|
|
77
|
+
p2tr: string;
|
|
78
|
+
};
|
|
79
|
+
/** Sign a PSBT */
|
|
80
|
+
signPsbt(psbtBase64: string, options: BitcoinSignOptions): Promise<BitcoinSignResult>;
|
|
81
|
+
/** Sign multiple PSBTs */
|
|
82
|
+
signAllPsbts(psbts: Array<{
|
|
83
|
+
psbt: string;
|
|
84
|
+
options: BitcoinSignOptions;
|
|
85
|
+
}>): Promise<BitcoinSignResult[]>;
|
|
86
|
+
/** Get the vault ID */
|
|
87
|
+
getVaultId(): string;
|
|
88
|
+
/** Get public key as hex */
|
|
89
|
+
getPublicKey(): string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Constants for Bitcoin calculations
|
|
93
|
+
*/
|
|
94
|
+
export declare const SATS_PER_BTC = 100000000;
|
|
95
|
+
/**
|
|
96
|
+
* Emblem Bitcoin Signer implementation
|
|
97
|
+
*/
|
|
98
|
+
export declare class EmblemBitcoinSigner implements BitcoinSignerInterface {
|
|
99
|
+
readonly publicKey: string;
|
|
100
|
+
readonly addresses: {
|
|
101
|
+
p2pkh: string;
|
|
102
|
+
p2wpkh: string;
|
|
103
|
+
p2tr: string;
|
|
104
|
+
};
|
|
105
|
+
private readonly config;
|
|
106
|
+
private readonly vaultId;
|
|
107
|
+
constructor(config: SignerConfig, vaultInfo: BitcoinVaultInfo);
|
|
108
|
+
/**
|
|
109
|
+
* Sign a Bitcoin PSBT
|
|
110
|
+
* @param psbtBase64 - PSBT in base64 format
|
|
111
|
+
* @param options - Signing options including transaction type and per-input instructions
|
|
112
|
+
*/
|
|
113
|
+
signPsbt(psbtBase64: string, options: BitcoinSignOptions): Promise<BitcoinSignResult>;
|
|
114
|
+
/**
|
|
115
|
+
* Sign multiple PSBTs
|
|
116
|
+
* @param psbts - Array of PSBTs with their signing options
|
|
117
|
+
*/
|
|
118
|
+
signAllPsbts(psbts: Array<{
|
|
119
|
+
psbt: string;
|
|
120
|
+
options: BitcoinSignOptions;
|
|
121
|
+
}>): Promise<BitcoinSignResult[]>;
|
|
122
|
+
/**
|
|
123
|
+
* Get the vault ID
|
|
124
|
+
*/
|
|
125
|
+
getVaultId(): string;
|
|
126
|
+
/**
|
|
127
|
+
* Get public key as hex
|
|
128
|
+
*/
|
|
129
|
+
getPublicKey(): string;
|
|
130
|
+
/**
|
|
131
|
+
* Check if a given address belongs to this signer
|
|
132
|
+
*/
|
|
133
|
+
ownsAddress(address: string): boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Get the appropriate address for a given type
|
|
136
|
+
*/
|
|
137
|
+
getAddress(type?: BitcoinAddressType): string;
|
|
138
|
+
/**
|
|
139
|
+
* Normalize a value to hex string (handles string, Buffer, Uint8Array)
|
|
140
|
+
*/
|
|
141
|
+
private normalizeToHex;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Create an Emblem Bitcoin signer from config
|
|
145
|
+
* @param config - Signer configuration with authentication
|
|
146
|
+
* @param infoOverride - Optional vault info to skip API call
|
|
147
|
+
*/
|
|
148
|
+
export declare function toBitcoinSigner(config: SignerConfig, infoOverride?: BitcoinVaultInfo): Promise<EmblemBitcoinSigner>;
|
|
149
|
+
/**
|
|
150
|
+
* Fetch vault info with Bitcoin-specific fields
|
|
151
|
+
*/
|
|
152
|
+
export declare function fetchBitcoinVaultInfo(config: SignerConfig): Promise<BitcoinVaultInfo>;
|
|
153
|
+
/**
|
|
154
|
+
* Utility: Detect Bitcoin address type from address string
|
|
155
|
+
*/
|
|
156
|
+
export declare function detectAddressType(address: string): BitcoinAddressType | 'unknown';
|
|
157
|
+
/**
|
|
158
|
+
* Utility: Convert satoshis to BTC
|
|
159
|
+
*/
|
|
160
|
+
export declare function satsToBTC(sats: number): number;
|
|
161
|
+
/**
|
|
162
|
+
* Utility: Convert BTC to satoshis
|
|
163
|
+
*/
|
|
164
|
+
export declare function btcToSats(btc: number): number;
|
|
165
|
+
/**
|
|
166
|
+
* Utility: Format satoshis for display
|
|
167
|
+
*/
|
|
168
|
+
export declare function formatSats(sats: number): string;
|
|
169
|
+
/**
|
|
170
|
+
* Utility: Estimate transaction size in vbytes
|
|
171
|
+
*/
|
|
172
|
+
export declare function estimateTransactionSize(inputCount: number, outputCount: number, inputType?: BitcoinAddressType): number;
|
|
173
|
+
/**
|
|
174
|
+
* Utility: Calculate estimated fee
|
|
175
|
+
*/
|
|
176
|
+
export declare function calculateFee(vsize: number, feeRate: number): number;
|
|
177
|
+
/**
|
|
178
|
+
* Utility: Check if amount is above dust threshold
|
|
179
|
+
*/
|
|
180
|
+
export declare function isDust(sats: number, addressType?: BitcoinAddressType): boolean;
|
|
181
|
+
//# sourceMappingURL=bitcoin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitcoin.d.ts","sourceRoot":"","sources":["../../src/signers/bitcoin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";async function t(t,e,s){const r=s.baseUrl??"https://api.emblemvault.ai",n=await async function(t){if("function"==typeof t.getAuthHeaders){const e=await t.getAuthHeaders();if(e&&"object"==typeof e)return e}const e=t.jwt??("function"==typeof t.getJwt?await t.getJwt():void 0)??t.sdk?.getSession()?.authToken??void 0;if(e)return{Authorization:`Bearer ${e}`};if(t.apiKey)return{"x-api-key":t.apiKey};throw new Error("No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey")}(s),i=await fetch(`${r}${t}`,{method:"POST",headers:{"content-type":"application/json",...n},body:JSON.stringify(e,(t,e)=>"bigint"==typeof e?e.toString():e)});if(!i.ok){const t=await i.text().catch(()=>"");throw new Error(function(t,e){let s=`Emblem signer error ${t}`;return t>=500?s+=": Internal server error":401===t||403===t?s+=": Authentication failed":404===t?s+=": Resource not found":405===t?s+=": Method not allowed":e&&(s+=`: ${e.substring(0,200)}`),s}(i.status,t))}return i.json()}Object.defineProperty(exports,"__esModule",{value:!0});const e=1e8;class s{constructor(t,e){if(!e.btcPubkey)throw new Error("Bitcoin public key is required in vault info");this.publicKey=e.btcPubkey,this.addresses=e.btcAddresses??{p2pkh:"",p2wpkh:"",p2tr:""},this.config=t,this.vaultId=e.vaultId}async signPsbt(e,s){const{transactionType:r,toSignInputs:n}=s,i=n?.map(t=>({...t,tapLeafHashToSign:this.normalizeToHex(t.tapLeafHashToSign)}));return await t("/sign-btc-transaction",{vaultId:this.vaultId,psbt:e,transactionType:r,toSignInputs:i},this.config)}async signAllPsbts(t){const e=[];for(const{psbt:s,options:r}of t)e.push(await this.signPsbt(s,r));return e}getVaultId(){return this.vaultId}getPublicKey(){return this.publicKey}ownsAddress(t){return t===this.addresses.p2pkh||t===this.addresses.p2wpkh||t===this.addresses.p2tr}getAddress(t="p2wpkh"){switch(t){case"p2pkh":return this.addresses.p2pkh;case"p2wpkh":case"p2sh":default:return this.addresses.p2wpkh;case"p2tr":return this.addresses.p2tr}}normalizeToHex(t){if(null==t)return;if("string"==typeof t)return t;const e=t;let s="";for(let t=0;t<e.length;t++)s+=e[t].toString(16).padStart(2,"0");return s}}async function r(e){const s=await t("/vault/info",{},e);if(!s||!s.vaultId)throw new Error("Invalid vault info response: missing vaultId");if(!s.btcPubkey)throw new Error("Invalid vault info response: missing btcPubkey (Bitcoin public key)");return{vaultId:s.vaultId,address:s.address||"",evmAddress:s.evmAddress||"0x",btcPubkey:s.btcPubkey,btcAddresses:s.btcAddresses,created_by:s.created_by}}function n(t){return t/e}exports.EmblemBitcoinSigner=s,exports.SATS_PER_BTC=e,exports.btcToSats=function(t){return Math.round(t*e)},exports.calculateFee=function(t,e){return Math.ceil(t*e)},exports.detectAddressType=function(t){return t.startsWith("bc1p")?"p2tr":t.startsWith("bc1q")?"p2wpkh":t.startsWith("1")?"p2pkh":t.startsWith("3")?"p2sh":t.startsWith("tb1p")?"p2tr":t.startsWith("tb1q")?"p2wpkh":t.startsWith("m")||t.startsWith("n")?"p2pkh":t.startsWith("2")?"p2sh":"unknown"},exports.estimateTransactionSize=function(t,e,s="p2wpkh"){let r;switch(s){case"p2pkh":r=148;break;case"p2tr":r=58;break;default:r=68}return 10+t*r+31*e},exports.fetchBitcoinVaultInfo=r,exports.formatSats=function(t){return`${t.toLocaleString()} sats (${n(t).toFixed(8)} BTC)`},exports.isDust=function(t,e="p2wpkh"){return t<("p2wpkh"===e||"p2tr"===e?294:546)},exports.satsToBTC=n,exports.toBitcoinSigner=async function(t,e){const n=e??await r(t);return new s(t,n)};
|
|
2
|
+
//# sourceMappingURL=bitcoin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitcoin.js","sources":["../../src/signers/http.ts","../../src/signers/bitcoin.ts"],"sourcesContent":["import type { SignerConfig } from '../types/signers';\n\nfunction sanitizeErrorMessage(status: number, text: string): string {\n // Sanitize error messages to avoid leaking sensitive server information\n let errorMessage = `Emblem signer error ${status}`;\n\n if (status >= 500) {\n errorMessage += ': Internal server error';\n } else if (status === 401 || status === 403) {\n errorMessage += ': Authentication failed';\n } else if (status === 404) {\n errorMessage += ': Resource not found';\n } else if (status === 405) {\n errorMessage += ': Method not allowed';\n } else if (text) {\n // For 4xx client errors, include limited error details\n errorMessage += `: ${text.substring(0, 200)}`; // Limit to 200 chars\n }\n\n return errorMessage;\n}\n\nasync function resolveAuthHeaders(config: SignerConfig): Promise<Record<string, string>> {\n // Priority: custom headers -> jwt/getJwt/sdk -> apiKey (deprecated)\n if (typeof config.getAuthHeaders === 'function') {\n const h = await config.getAuthHeaders();\n if (h && typeof h === 'object') return h;\n }\n\n const tok =\n config.jwt ??\n (typeof config.getJwt === 'function' ? await config.getJwt() : undefined) ??\n config.sdk?.getSession()?.authToken ??\n undefined;\n\n if (tok) {\n return { Authorization: `Bearer ${tok}` };\n }\n\n // apiKey is deprecated but still supported as fallback\n if (config.apiKey) {\n return { 'x-api-key': config.apiKey };\n }\n\n throw new Error(\n 'No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey'\n );\n}\n\nexport async function emblemPost<T = unknown>(\n path: string,\n body: unknown,\n config: SignerConfig\n): Promise<T> {\n const baseUrl = config.baseUrl ?? 'https://api.emblemvault.ai';\n const authHeaders = await resolveAuthHeaders(config);\n const res = await fetch(`${baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...authHeaders,\n },\n body: JSON.stringify(body, (_key: string, value: unknown) =>\n typeof value === 'bigint' ? value.toString() : value\n ),\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(sanitizeErrorMessage(res.status, text));\n }\n\n return res.json() as Promise<T>;\n}\n\nexport async function emblemGet<T = unknown>(path: string, config: SignerConfig): Promise<T> {\n const baseUrl = config.baseUrl ?? 'https://api.emblemvault.ai';\n const authHeaders = await resolveAuthHeaders(config);\n const res = await fetch(`${baseUrl}${path}`, {\n method: 'GET',\n headers: authHeaders,\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(sanitizeErrorMessage(res.status, text));\n }\n\n return res.json() as Promise<T>;\n}\n","import type { SignerConfig, SignerVaultInfo } from '../types/signers';\nimport { emblemPost } from './http';\nimport { fetchVaultInfo } from './vault';\n\n/**\n * Bitcoin address types\n */\nexport type BitcoinAddressType = 'p2pkh' | 'p2wpkh' | 'p2tr' | 'p2sh';\n\n/**\n * Bitcoin network type\n */\nexport type BitcoinNetwork = 'mainnet' | 'testnet';\n\n/**\n * Input signing instruction for PSBTs\n * Compatible with UniSat wallet-api's UserToSignInput format\n */\nexport interface BitcoinToSignInput {\n /** Input index in the PSBT */\n index: number;\n /** Bitcoin address associated with this input */\n address?: string;\n /** Public key (hex) for this input */\n publicKey?: string;\n /** Sighash types for signing */\n sighashTypes?: number[];\n /** Tap leaf hash to sign (hex) for Taproot script path spending */\n tapLeafHashToSign?: string;\n /** Use tweaked signer for Taproot */\n useTweakedSigner?: boolean;\n /** Disable tweak signer for Taproot */\n disableTweakSigner?: boolean;\n}\n\n/**\n * Bitcoin sign transaction options\n */\nexport interface BitcoinSignOptions {\n /** Transaction type determines signing algorithm (ECDSA for p2pkh/p2wpkh, Schnorr for p2tr) */\n transactionType: 'p2pkh' | 'p2wpkh' | 'p2tr';\n /** Per-input signing instructions for complex PSBTs */\n toSignInputs?: BitcoinToSignInput[];\n}\n\n/**\n * Response from signing a Bitcoin PSBT\n */\nexport interface BitcoinSignResult {\n success: boolean;\n /** Signed PSBT in base64 format */\n signedPsbt: string;\n /** Final transaction hex (ready for broadcast) */\n signedTxHex: string;\n /** Raw signature */\n signature: string;\n}\n\n/**\n * Extended vault info including Bitcoin public key and derived addresses\n */\nexport interface BitcoinVaultInfo extends SignerVaultInfo {\n /** Bitcoin public key (compressed, hex) */\n btcPubkey: string;\n /** Derived Bitcoin addresses */\n btcAddresses?: {\n /** Legacy (1...) */\n p2pkh: string;\n /** Native SegWit (bc1q...) */\n p2wpkh: string;\n /** Taproot (bc1p...) */\n p2tr: string;\n };\n}\n\n/**\n * Bitcoin signer interface compatible with common Bitcoin libraries\n */\nexport interface BitcoinSignerInterface {\n /** Bitcoin public key (compressed, hex) */\n publicKey: string;\n /** Derived Bitcoin addresses */\n addresses: {\n p2pkh: string;\n p2wpkh: string;\n p2tr: string;\n };\n /** Sign a PSBT */\n signPsbt(psbtBase64: string, options: BitcoinSignOptions): Promise<BitcoinSignResult>;\n /** Sign multiple PSBTs */\n signAllPsbts(\n psbts: Array<{ psbt: string; options: BitcoinSignOptions }>\n ): Promise<BitcoinSignResult[]>;\n /** Get the vault ID */\n getVaultId(): string;\n /** Get public key as hex */\n getPublicKey(): string;\n}\n\n/**\n * Constants for Bitcoin calculations\n */\nexport const SATS_PER_BTC = 100_000_000;\n\n/**\n * Emblem Bitcoin Signer implementation\n */\nexport class EmblemBitcoinSigner implements BitcoinSignerInterface {\n readonly publicKey: string; // Compressed public key (hex)\n readonly addresses: {\n p2pkh: string;\n p2wpkh: string;\n p2tr: string;\n };\n private readonly config: SignerConfig;\n private readonly vaultId: string;\n\n constructor(config: SignerConfig, vaultInfo: BitcoinVaultInfo) {\n if (!vaultInfo.btcPubkey) {\n throw new Error('Bitcoin public key is required in vault info');\n }\n this.publicKey = vaultInfo.btcPubkey;\n this.addresses = vaultInfo.btcAddresses ?? {\n p2pkh: '',\n p2wpkh: '',\n p2tr: '',\n };\n this.config = config;\n this.vaultId = vaultInfo.vaultId;\n }\n\n /**\n * Sign a Bitcoin PSBT\n * @param psbtBase64 - PSBT in base64 format\n * @param options - Signing options including transaction type and per-input instructions\n */\n async signPsbt(psbtBase64: string, options: BitcoinSignOptions): Promise<BitcoinSignResult> {\n const { transactionType, toSignInputs } = options;\n\n // Normalize toSignInputs for JSON transmission\n const normalizedInputs = toSignInputs?.map(input => ({\n ...input,\n // Ensure tapLeafHashToSign is a string (some libs pass Buffer/Uint8Array)\n tapLeafHashToSign: this.normalizeToHex(input.tapLeafHashToSign),\n }));\n\n const response = await emblemPost<BitcoinSignResult>(\n '/sign-btc-transaction',\n {\n vaultId: this.vaultId,\n psbt: psbtBase64,\n transactionType,\n toSignInputs: normalizedInputs,\n },\n this.config\n );\n\n return response;\n }\n\n /**\n * Sign multiple PSBTs\n * @param psbts - Array of PSBTs with their signing options\n */\n async signAllPsbts(\n psbts: Array<{ psbt: string; options: BitcoinSignOptions }>\n ): Promise<BitcoinSignResult[]> {\n const results: BitcoinSignResult[] = [];\n for (const { psbt, options } of psbts) {\n results.push(await this.signPsbt(psbt, options));\n }\n return results;\n }\n\n /**\n * Get the vault ID\n */\n getVaultId(): string {\n return this.vaultId;\n }\n\n /**\n * Get public key as hex\n */\n getPublicKey(): string {\n return this.publicKey;\n }\n\n /**\n * Check if a given address belongs to this signer\n */\n ownsAddress(address: string): boolean {\n return (\n address === this.addresses.p2pkh ||\n address === this.addresses.p2wpkh ||\n address === this.addresses.p2tr\n );\n }\n\n /**\n * Get the appropriate address for a given type\n */\n getAddress(type: BitcoinAddressType = 'p2wpkh'): string {\n switch (type) {\n case 'p2pkh':\n return this.addresses.p2pkh;\n case 'p2wpkh':\n return this.addresses.p2wpkh;\n case 'p2tr':\n return this.addresses.p2tr;\n case 'p2sh':\n // P2SH is typically wrapped SegWit, return p2wpkh as fallback\n return this.addresses.p2wpkh;\n default:\n return this.addresses.p2wpkh;\n }\n }\n\n /**\n * Normalize a value to hex string (handles string, Buffer, Uint8Array)\n */\n private normalizeToHex(value: string | ArrayLike<number> | undefined): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === 'string') return value;\n // Handle Buffer/Uint8Array\n const bytes = value as ArrayLike<number>;\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += (bytes[i] as number).toString(16).padStart(2, '0');\n }\n return hex;\n }\n}\n\n/**\n * Create an Emblem Bitcoin signer from config\n * @param config - Signer configuration with authentication\n * @param infoOverride - Optional vault info to skip API call\n */\nexport async function toBitcoinSigner(\n config: SignerConfig,\n infoOverride?: BitcoinVaultInfo\n): Promise<EmblemBitcoinSigner> {\n const info = infoOverride ?? (await fetchBitcoinVaultInfo(config));\n return new EmblemBitcoinSigner(config, info);\n}\n\n/**\n * Fetch vault info with Bitcoin-specific fields\n */\nexport async function fetchBitcoinVaultInfo(config: SignerConfig): Promise<BitcoinVaultInfo> {\n const data = await emblemPost<{\n vaultId: string;\n address?: string;\n evmAddress?: string;\n btcPubkey?: string;\n btcAddresses?: {\n p2pkh: string;\n p2wpkh: string;\n p2tr: string;\n };\n created_by?: string;\n }>('/vault/info', {}, config);\n\n if (!data || !data.vaultId) {\n throw new Error('Invalid vault info response: missing vaultId');\n }\n\n if (!data.btcPubkey) {\n throw new Error('Invalid vault info response: missing btcPubkey (Bitcoin public key)');\n }\n\n return {\n vaultId: data.vaultId,\n address: data.address || '',\n evmAddress: (data.evmAddress || '0x') as `0x${string}`,\n btcPubkey: data.btcPubkey,\n btcAddresses: data.btcAddresses,\n created_by: data.created_by,\n };\n}\n\n/**\n * Utility: Detect Bitcoin address type from address string\n */\nexport function detectAddressType(address: string): BitcoinAddressType | 'unknown' {\n if (address.startsWith('bc1p')) return 'p2tr'; // Taproot (bech32m)\n if (address.startsWith('bc1q')) return 'p2wpkh'; // Native SegWit (bech32)\n if (address.startsWith('1')) return 'p2pkh'; // Legacy\n if (address.startsWith('3')) return 'p2sh'; // Script Hash\n // Testnet addresses\n if (address.startsWith('tb1p')) return 'p2tr';\n if (address.startsWith('tb1q')) return 'p2wpkh';\n if (address.startsWith('m') || address.startsWith('n')) return 'p2pkh';\n if (address.startsWith('2')) return 'p2sh';\n return 'unknown';\n}\n\n/**\n * Utility: Convert satoshis to BTC\n */\nexport function satsToBTC(sats: number): number {\n return sats / SATS_PER_BTC;\n}\n\n/**\n * Utility: Convert BTC to satoshis\n */\nexport function btcToSats(btc: number): number {\n return Math.round(btc * SATS_PER_BTC);\n}\n\n/**\n * Utility: Format satoshis for display\n */\nexport function formatSats(sats: number): string {\n return `${sats.toLocaleString()} sats (${satsToBTC(sats).toFixed(8)} BTC)`;\n}\n\n/**\n * Utility: Estimate transaction size in vbytes\n */\nexport function estimateTransactionSize(\n inputCount: number,\n outputCount: number,\n inputType: BitcoinAddressType = 'p2wpkh'\n): number {\n let inputSize: number;\n switch (inputType) {\n case 'p2pkh':\n inputSize = 148; // Legacy (no witness discount)\n break;\n case 'p2tr':\n inputSize = 58; // Taproot (approx vbytes)\n break;\n case 'p2wpkh':\n default:\n inputSize = 68; // Native SegWit (approx vbytes)\n break;\n }\n\n const outputSize = 31; // Bech32 output (approx vbytes)\n const overhead = 10; // Version, locktime, etc.\n\n return overhead + inputCount * inputSize + outputCount * outputSize;\n}\n\n/**\n * Utility: Calculate estimated fee\n */\nexport function calculateFee(vsize: number, feeRate: number): number {\n return Math.ceil(vsize * feeRate);\n}\n\n/**\n * Utility: Check if amount is above dust threshold\n */\nexport function isDust(sats: number, addressType: BitcoinAddressType = 'p2wpkh'): boolean {\n // Dust threshold: ~546 sats for legacy, ~294 for SegWit\n const dustThreshold = addressType === 'p2wpkh' || addressType === 'p2tr' ? 294 : 546;\n return sats < dustThreshold;\n}\n"],"names":["async","emblemPost","path","body","config","baseUrl","authHeaders","getAuthHeaders","h","tok","jwt","getJwt","undefined","sdk","getSession","authToken","Authorization","apiKey","Error","resolveAuthHeaders","res","fetch","method","headers","JSON","stringify","_key","value","toString","ok","text","catch","status","errorMessage","substring","sanitizeErrorMessage","json","SATS_PER_BTC","EmblemBitcoinSigner","constructor","vaultInfo","btcPubkey","this","publicKey","addresses","btcAddresses","p2pkh","p2wpkh","p2tr","vaultId","signPsbt","psbtBase64","options","transactionType","toSignInputs","normalizedInputs","map","input","tapLeafHashToSign","normalizeToHex","psbt","signAllPsbts","psbts","results","push","getVaultId","getPublicKey","ownsAddress","address","getAddress","type","bytes","hex","i","length","padStart","fetchBitcoinVaultInfo","data","evmAddress","created_by","satsToBTC","sats","btc","Math","round","vsize","feeRate","ceil","startsWith","inputCount","outputCount","inputType","inputSize","toLocaleString","toFixed","addressType","infoOverride","info"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
async function t(t,e,s){const r=s.baseUrl??"https://api.emblemvault.ai",n=await async function(t){if("function"==typeof t.getAuthHeaders){const e=await t.getAuthHeaders();if(e&&"object"==typeof e)return e}const e=t.jwt??("function"==typeof t.getJwt?await t.getJwt():void 0)??t.sdk?.getSession()?.authToken??void 0;if(e)return{Authorization:`Bearer ${e}`};if(t.apiKey)return{"x-api-key":t.apiKey};throw new Error("No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey")}(s),i=await fetch(`${r}${t}`,{method:"POST",headers:{"content-type":"application/json",...n},body:JSON.stringify(e,(t,e)=>"bigint"==typeof e?e.toString():e)});if(!i.ok){const t=await i.text().catch(()=>"");throw new Error(function(t,e){let s=`Emblem signer error ${t}`;return t>=500?s+=": Internal server error":401===t||403===t?s+=": Authentication failed":404===t?s+=": Resource not found":405===t?s+=": Method not allowed":e&&(s+=`: ${e.substring(0,200)}`),s}(i.status,t))}return i.json()}const e=1e8;class s{constructor(t,e){if(!e.btcPubkey)throw new Error("Bitcoin public key is required in vault info");this.publicKey=e.btcPubkey,this.addresses=e.btcAddresses??{p2pkh:"",p2wpkh:"",p2tr:""},this.config=t,this.vaultId=e.vaultId}async signPsbt(e,s){const{transactionType:r,toSignInputs:n}=s,i=n?.map(t=>({...t,tapLeafHashToSign:this.normalizeToHex(t.tapLeafHashToSign)}));return await t("/sign-btc-transaction",{vaultId:this.vaultId,psbt:e,transactionType:r,toSignInputs:i},this.config)}async signAllPsbts(t){const e=[];for(const{psbt:s,options:r}of t)e.push(await this.signPsbt(s,r));return e}getVaultId(){return this.vaultId}getPublicKey(){return this.publicKey}ownsAddress(t){return t===this.addresses.p2pkh||t===this.addresses.p2wpkh||t===this.addresses.p2tr}getAddress(t="p2wpkh"){switch(t){case"p2pkh":return this.addresses.p2pkh;case"p2wpkh":case"p2sh":default:return this.addresses.p2wpkh;case"p2tr":return this.addresses.p2tr}}normalizeToHex(t){if(null==t)return;if("string"==typeof t)return t;const e=t;let s="";for(let t=0;t<e.length;t++)s+=e[t].toString(16).padStart(2,"0");return s}}async function r(t,e){const r=e??await n(t);return new s(t,r)}async function n(e){const s=await t("/vault/info",{},e);if(!s||!s.vaultId)throw new Error("Invalid vault info response: missing vaultId");if(!s.btcPubkey)throw new Error("Invalid vault info response: missing btcPubkey (Bitcoin public key)");return{vaultId:s.vaultId,address:s.address||"",evmAddress:s.evmAddress||"0x",btcPubkey:s.btcPubkey,btcAddresses:s.btcAddresses,created_by:s.created_by}}function i(t){return t.startsWith("bc1p")?"p2tr":t.startsWith("bc1q")?"p2wpkh":t.startsWith("1")?"p2pkh":t.startsWith("3")?"p2sh":t.startsWith("tb1p")?"p2tr":t.startsWith("tb1q")?"p2wpkh":t.startsWith("m")||t.startsWith("n")?"p2pkh":t.startsWith("2")?"p2sh":"unknown"}function a(t){return t/e}function o(t){return Math.round(t*e)}function u(t){return`${t.toLocaleString()} sats (${a(t).toFixed(8)} BTC)`}function p(t,e,s="p2wpkh"){let r;switch(s){case"p2pkh":r=148;break;case"p2tr":r=58;break;default:r=68}return 10+t*r+31*e}function c(t,e){return Math.ceil(t*e)}function h(t,e="p2wpkh"){return t<("p2wpkh"===e||"p2tr"===e?294:546)}export{s as EmblemBitcoinSigner,e as SATS_PER_BTC,o as btcToSats,c as calculateFee,i as detectAddressType,p as estimateTransactionSize,n as fetchBitcoinVaultInfo,u as formatSats,h as isDust,a as satsToBTC,r as toBitcoinSigner};
|
|
2
|
+
//# sourceMappingURL=bitcoin.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bitcoin.mjs","sources":["../../src/signers/http.ts","../../src/signers/bitcoin.ts"],"sourcesContent":["import type { SignerConfig } from '../types/signers';\n\nfunction sanitizeErrorMessage(status: number, text: string): string {\n // Sanitize error messages to avoid leaking sensitive server information\n let errorMessage = `Emblem signer error ${status}`;\n\n if (status >= 500) {\n errorMessage += ': Internal server error';\n } else if (status === 401 || status === 403) {\n errorMessage += ': Authentication failed';\n } else if (status === 404) {\n errorMessage += ': Resource not found';\n } else if (status === 405) {\n errorMessage += ': Method not allowed';\n } else if (text) {\n // For 4xx client errors, include limited error details\n errorMessage += `: ${text.substring(0, 200)}`; // Limit to 200 chars\n }\n\n return errorMessage;\n}\n\nasync function resolveAuthHeaders(config: SignerConfig): Promise<Record<string, string>> {\n // Priority: custom headers -> jwt/getJwt/sdk -> apiKey (deprecated)\n if (typeof config.getAuthHeaders === 'function') {\n const h = await config.getAuthHeaders();\n if (h && typeof h === 'object') return h;\n }\n\n const tok =\n config.jwt ??\n (typeof config.getJwt === 'function' ? await config.getJwt() : undefined) ??\n config.sdk?.getSession()?.authToken ??\n undefined;\n\n if (tok) {\n return { Authorization: `Bearer ${tok}` };\n }\n\n // apiKey is deprecated but still supported as fallback\n if (config.apiKey) {\n return { 'x-api-key': config.apiKey };\n }\n\n throw new Error(\n 'No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey'\n );\n}\n\nexport async function emblemPost<T = unknown>(\n path: string,\n body: unknown,\n config: SignerConfig\n): Promise<T> {\n const baseUrl = config.baseUrl ?? 'https://api.emblemvault.ai';\n const authHeaders = await resolveAuthHeaders(config);\n const res = await fetch(`${baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n ...authHeaders,\n },\n body: JSON.stringify(body, (_key: string, value: unknown) =>\n typeof value === 'bigint' ? value.toString() : value\n ),\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(sanitizeErrorMessage(res.status, text));\n }\n\n return res.json() as Promise<T>;\n}\n\nexport async function emblemGet<T = unknown>(path: string, config: SignerConfig): Promise<T> {\n const baseUrl = config.baseUrl ?? 'https://api.emblemvault.ai';\n const authHeaders = await resolveAuthHeaders(config);\n const res = await fetch(`${baseUrl}${path}`, {\n method: 'GET',\n headers: authHeaders,\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(sanitizeErrorMessage(res.status, text));\n }\n\n return res.json() as Promise<T>;\n}\n","import type { SignerConfig, SignerVaultInfo } from '../types/signers';\nimport { emblemPost } from './http';\nimport { fetchVaultInfo } from './vault';\n\n/**\n * Bitcoin address types\n */\nexport type BitcoinAddressType = 'p2pkh' | 'p2wpkh' | 'p2tr' | 'p2sh';\n\n/**\n * Bitcoin network type\n */\nexport type BitcoinNetwork = 'mainnet' | 'testnet';\n\n/**\n * Input signing instruction for PSBTs\n * Compatible with UniSat wallet-api's UserToSignInput format\n */\nexport interface BitcoinToSignInput {\n /** Input index in the PSBT */\n index: number;\n /** Bitcoin address associated with this input */\n address?: string;\n /** Public key (hex) for this input */\n publicKey?: string;\n /** Sighash types for signing */\n sighashTypes?: number[];\n /** Tap leaf hash to sign (hex) for Taproot script path spending */\n tapLeafHashToSign?: string;\n /** Use tweaked signer for Taproot */\n useTweakedSigner?: boolean;\n /** Disable tweak signer for Taproot */\n disableTweakSigner?: boolean;\n}\n\n/**\n * Bitcoin sign transaction options\n */\nexport interface BitcoinSignOptions {\n /** Transaction type determines signing algorithm (ECDSA for p2pkh/p2wpkh, Schnorr for p2tr) */\n transactionType: 'p2pkh' | 'p2wpkh' | 'p2tr';\n /** Per-input signing instructions for complex PSBTs */\n toSignInputs?: BitcoinToSignInput[];\n}\n\n/**\n * Response from signing a Bitcoin PSBT\n */\nexport interface BitcoinSignResult {\n success: boolean;\n /** Signed PSBT in base64 format */\n signedPsbt: string;\n /** Final transaction hex (ready for broadcast) */\n signedTxHex: string;\n /** Raw signature */\n signature: string;\n}\n\n/**\n * Extended vault info including Bitcoin public key and derived addresses\n */\nexport interface BitcoinVaultInfo extends SignerVaultInfo {\n /** Bitcoin public key (compressed, hex) */\n btcPubkey: string;\n /** Derived Bitcoin addresses */\n btcAddresses?: {\n /** Legacy (1...) */\n p2pkh: string;\n /** Native SegWit (bc1q...) */\n p2wpkh: string;\n /** Taproot (bc1p...) */\n p2tr: string;\n };\n}\n\n/**\n * Bitcoin signer interface compatible with common Bitcoin libraries\n */\nexport interface BitcoinSignerInterface {\n /** Bitcoin public key (compressed, hex) */\n publicKey: string;\n /** Derived Bitcoin addresses */\n addresses: {\n p2pkh: string;\n p2wpkh: string;\n p2tr: string;\n };\n /** Sign a PSBT */\n signPsbt(psbtBase64: string, options: BitcoinSignOptions): Promise<BitcoinSignResult>;\n /** Sign multiple PSBTs */\n signAllPsbts(\n psbts: Array<{ psbt: string; options: BitcoinSignOptions }>\n ): Promise<BitcoinSignResult[]>;\n /** Get the vault ID */\n getVaultId(): string;\n /** Get public key as hex */\n getPublicKey(): string;\n}\n\n/**\n * Constants for Bitcoin calculations\n */\nexport const SATS_PER_BTC = 100_000_000;\n\n/**\n * Emblem Bitcoin Signer implementation\n */\nexport class EmblemBitcoinSigner implements BitcoinSignerInterface {\n readonly publicKey: string; // Compressed public key (hex)\n readonly addresses: {\n p2pkh: string;\n p2wpkh: string;\n p2tr: string;\n };\n private readonly config: SignerConfig;\n private readonly vaultId: string;\n\n constructor(config: SignerConfig, vaultInfo: BitcoinVaultInfo) {\n if (!vaultInfo.btcPubkey) {\n throw new Error('Bitcoin public key is required in vault info');\n }\n this.publicKey = vaultInfo.btcPubkey;\n this.addresses = vaultInfo.btcAddresses ?? {\n p2pkh: '',\n p2wpkh: '',\n p2tr: '',\n };\n this.config = config;\n this.vaultId = vaultInfo.vaultId;\n }\n\n /**\n * Sign a Bitcoin PSBT\n * @param psbtBase64 - PSBT in base64 format\n * @param options - Signing options including transaction type and per-input instructions\n */\n async signPsbt(psbtBase64: string, options: BitcoinSignOptions): Promise<BitcoinSignResult> {\n const { transactionType, toSignInputs } = options;\n\n // Normalize toSignInputs for JSON transmission\n const normalizedInputs = toSignInputs?.map(input => ({\n ...input,\n // Ensure tapLeafHashToSign is a string (some libs pass Buffer/Uint8Array)\n tapLeafHashToSign: this.normalizeToHex(input.tapLeafHashToSign),\n }));\n\n const response = await emblemPost<BitcoinSignResult>(\n '/sign-btc-transaction',\n {\n vaultId: this.vaultId,\n psbt: psbtBase64,\n transactionType,\n toSignInputs: normalizedInputs,\n },\n this.config\n );\n\n return response;\n }\n\n /**\n * Sign multiple PSBTs\n * @param psbts - Array of PSBTs with their signing options\n */\n async signAllPsbts(\n psbts: Array<{ psbt: string; options: BitcoinSignOptions }>\n ): Promise<BitcoinSignResult[]> {\n const results: BitcoinSignResult[] = [];\n for (const { psbt, options } of psbts) {\n results.push(await this.signPsbt(psbt, options));\n }\n return results;\n }\n\n /**\n * Get the vault ID\n */\n getVaultId(): string {\n return this.vaultId;\n }\n\n /**\n * Get public key as hex\n */\n getPublicKey(): string {\n return this.publicKey;\n }\n\n /**\n * Check if a given address belongs to this signer\n */\n ownsAddress(address: string): boolean {\n return (\n address === this.addresses.p2pkh ||\n address === this.addresses.p2wpkh ||\n address === this.addresses.p2tr\n );\n }\n\n /**\n * Get the appropriate address for a given type\n */\n getAddress(type: BitcoinAddressType = 'p2wpkh'): string {\n switch (type) {\n case 'p2pkh':\n return this.addresses.p2pkh;\n case 'p2wpkh':\n return this.addresses.p2wpkh;\n case 'p2tr':\n return this.addresses.p2tr;\n case 'p2sh':\n // P2SH is typically wrapped SegWit, return p2wpkh as fallback\n return this.addresses.p2wpkh;\n default:\n return this.addresses.p2wpkh;\n }\n }\n\n /**\n * Normalize a value to hex string (handles string, Buffer, Uint8Array)\n */\n private normalizeToHex(value: string | ArrayLike<number> | undefined): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === 'string') return value;\n // Handle Buffer/Uint8Array\n const bytes = value as ArrayLike<number>;\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += (bytes[i] as number).toString(16).padStart(2, '0');\n }\n return hex;\n }\n}\n\n/**\n * Create an Emblem Bitcoin signer from config\n * @param config - Signer configuration with authentication\n * @param infoOverride - Optional vault info to skip API call\n */\nexport async function toBitcoinSigner(\n config: SignerConfig,\n infoOverride?: BitcoinVaultInfo\n): Promise<EmblemBitcoinSigner> {\n const info = infoOverride ?? (await fetchBitcoinVaultInfo(config));\n return new EmblemBitcoinSigner(config, info);\n}\n\n/**\n * Fetch vault info with Bitcoin-specific fields\n */\nexport async function fetchBitcoinVaultInfo(config: SignerConfig): Promise<BitcoinVaultInfo> {\n const data = await emblemPost<{\n vaultId: string;\n address?: string;\n evmAddress?: string;\n btcPubkey?: string;\n btcAddresses?: {\n p2pkh: string;\n p2wpkh: string;\n p2tr: string;\n };\n created_by?: string;\n }>('/vault/info', {}, config);\n\n if (!data || !data.vaultId) {\n throw new Error('Invalid vault info response: missing vaultId');\n }\n\n if (!data.btcPubkey) {\n throw new Error('Invalid vault info response: missing btcPubkey (Bitcoin public key)');\n }\n\n return {\n vaultId: data.vaultId,\n address: data.address || '',\n evmAddress: (data.evmAddress || '0x') as `0x${string}`,\n btcPubkey: data.btcPubkey,\n btcAddresses: data.btcAddresses,\n created_by: data.created_by,\n };\n}\n\n/**\n * Utility: Detect Bitcoin address type from address string\n */\nexport function detectAddressType(address: string): BitcoinAddressType | 'unknown' {\n if (address.startsWith('bc1p')) return 'p2tr'; // Taproot (bech32m)\n if (address.startsWith('bc1q')) return 'p2wpkh'; // Native SegWit (bech32)\n if (address.startsWith('1')) return 'p2pkh'; // Legacy\n if (address.startsWith('3')) return 'p2sh'; // Script Hash\n // Testnet addresses\n if (address.startsWith('tb1p')) return 'p2tr';\n if (address.startsWith('tb1q')) return 'p2wpkh';\n if (address.startsWith('m') || address.startsWith('n')) return 'p2pkh';\n if (address.startsWith('2')) return 'p2sh';\n return 'unknown';\n}\n\n/**\n * Utility: Convert satoshis to BTC\n */\nexport function satsToBTC(sats: number): number {\n return sats / SATS_PER_BTC;\n}\n\n/**\n * Utility: Convert BTC to satoshis\n */\nexport function btcToSats(btc: number): number {\n return Math.round(btc * SATS_PER_BTC);\n}\n\n/**\n * Utility: Format satoshis for display\n */\nexport function formatSats(sats: number): string {\n return `${sats.toLocaleString()} sats (${satsToBTC(sats).toFixed(8)} BTC)`;\n}\n\n/**\n * Utility: Estimate transaction size in vbytes\n */\nexport function estimateTransactionSize(\n inputCount: number,\n outputCount: number,\n inputType: BitcoinAddressType = 'p2wpkh'\n): number {\n let inputSize: number;\n switch (inputType) {\n case 'p2pkh':\n inputSize = 148; // Legacy (no witness discount)\n break;\n case 'p2tr':\n inputSize = 58; // Taproot (approx vbytes)\n break;\n case 'p2wpkh':\n default:\n inputSize = 68; // Native SegWit (approx vbytes)\n break;\n }\n\n const outputSize = 31; // Bech32 output (approx vbytes)\n const overhead = 10; // Version, locktime, etc.\n\n return overhead + inputCount * inputSize + outputCount * outputSize;\n}\n\n/**\n * Utility: Calculate estimated fee\n */\nexport function calculateFee(vsize: number, feeRate: number): number {\n return Math.ceil(vsize * feeRate);\n}\n\n/**\n * Utility: Check if amount is above dust threshold\n */\nexport function isDust(sats: number, addressType: BitcoinAddressType = 'p2wpkh'): boolean {\n // Dust threshold: ~546 sats for legacy, ~294 for SegWit\n const dustThreshold = addressType === 'p2wpkh' || addressType === 'p2tr' ? 294 : 546;\n return sats < dustThreshold;\n}\n"],"names":["async","emblemPost","path","body","config","baseUrl","authHeaders","getAuthHeaders","h","tok","jwt","getJwt","undefined","sdk","getSession","authToken","Authorization","apiKey","Error","resolveAuthHeaders","res","fetch","method","headers","JSON","stringify","_key","value","toString","ok","text","catch","status","errorMessage","substring","sanitizeErrorMessage","json","SATS_PER_BTC","EmblemBitcoinSigner","constructor","vaultInfo","btcPubkey","this","publicKey","addresses","btcAddresses","p2pkh","p2wpkh","p2tr","vaultId","signPsbt","psbtBase64","options","transactionType","toSignInputs","normalizedInputs","map","input","tapLeafHashToSign","normalizeToHex","psbt","signAllPsbts","psbts","results","push","getVaultId","getPublicKey","ownsAddress","address","getAddress","type","bytes","hex","i","length","padStart","toBitcoinSigner","infoOverride","info","fetchBitcoinVaultInfo","data","evmAddress","created_by","detectAddressType","startsWith","satsToBTC","sats","btcToSats","btc","Math","round","formatSats","toLocaleString","toFixed","estimateTransactionSize","inputCount","outputCount","inputType","inputSize","calculateFee","vsize","feeRate","ceil","isDust","addressType"],"mappings":"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"}
|
package/dist/signers/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { toViemAccount } from './viem';
|
|
|
2
2
|
export { type EmblemEthersWallet, toEthersWallet } from './ethers';
|
|
3
3
|
export { EmblemSolanaSigner, toSolanaWeb3Signer, toSolanaKitSigner, type SolanaSignerInterface, type SolanaKitSignerInterface, } from './solana';
|
|
4
4
|
export { EmblemWeb3Adapter, toWeb3Adapter } from './web3';
|
|
5
|
+
export { EmblemBitcoinSigner, toBitcoinSigner, fetchBitcoinVaultInfo, detectAddressType, satsToBTC, btcToSats, formatSats, estimateTransactionSize, calculateFee, isDust, SATS_PER_BTC, type BitcoinAddressType, type BitcoinNetwork, type BitcoinToSignInput, type BitcoinSignOptions, type BitcoinSignResult, type BitcoinVaultInfo, type BitcoinSignerInterface, } from './bitcoin';
|
|
5
6
|
export { emblemPost, emblemGet } from './http';
|
|
6
7
|
export { fetchVaultInfo } from './vault';
|
|
7
8
|
export { isBrowserEnvironment, isNodeEnvironment, validateBaseUrl, validateEthereumAddress, validateVaultId, toSafeNumber, validateSignerConfig, type SignerSecurityConfig, } from './validation';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,KAAK,kBAAkB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,GAC9B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,KAAK,kBAAkB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,GAC9B,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,SAAS,EACT,SAAS,EACT,UAAU,EACV,uBAAuB,EACvB,YAAY,EACZ,MAAM,EACN,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,GAC5B,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,UAAU,GACX,MAAM,SAAS,CAAC"}
|
package/dist/signers/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";function t(t){if(t&&t.__esModule)return t;var e=Object.create(null);return t&&Object.keys(t).forEach(function(r){if("default"!==r){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:!0,get:function(){return t[r]}})}}),e.default=t,Object.freeze(e)}function e(t,e){let r=`Emblem signer error ${t}`;return t>=500?r+=": Internal server error":401===t||403===t?r+=": Authentication failed":404===t?r+=": Resource not found":405===t?r+=": Method not allowed":e&&(r+=`: ${e.substring(0,200)}`),r}async function r(t){if("function"==typeof t.getAuthHeaders){const e=await t.getAuthHeaders();if(e&&"object"==typeof e)return e}const e=t.jwt??("function"==typeof t.getJwt?await t.getJwt():void 0)??t.sdk?.getSession()?.authToken??void 0;if(e)return{Authorization:`Bearer ${e}`};if(t.apiKey)return{"x-api-key":t.apiKey};throw new Error("No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey")}async function i(t,i,n){const a=n.baseUrl??"https://api.emblemvault.ai",s=await r(n),o=await fetch(`${a}${t}`,{method:"POST",headers:{"content-type":"application/json",...s},body:JSON.stringify(i,(t,e)=>"bigint"==typeof e?e.toString():e)});if(!o.ok){const t=await o.text().catch(()=>"");throw new Error(e(o.status,t))}return o.json()}function n(){return"undefined"!=typeof window&&"undefined"!=typeof document}function a(t){if(t){if(!t.startsWith("http://")&&!t.startsWith("https://"))throw new Error("baseUrl must be a valid HTTP(S) URL");!t.startsWith("http://")||t.includes("localhost")||t.includes("127.0.0.1")||console.warn("[Emblem Security Warning] baseUrl uses HTTP instead of HTTPS. This is insecure for production use.")}}function s(t,e){const r=Number(t);if(!Number.isSafeInteger(r))throw new Error(`${e} value ${t} exceeds safe integer range (max: ${Number.MAX_SAFE_INTEGER})`);return r}function o(t){return"bigint"==typeof t?"0x"+t.toString(16):t}function d(t){const e={...t};return void 0!==e.value&&(e.value=o(e.value)),void 0!==e.gas&&(e.gasLimit=o(e.gas),delete e.gas),void 0!==e.gasLimit&&(e.gasLimit=o(e.gasLimit)),void 0!==e.gasPrice&&(e.gasPrice=o(e.gasPrice)),void 0!==e.maxFeePerGas&&(e.maxFeePerGas=o(e.maxFeePerGas)),void 0!==e.maxPriorityFeePerGas&&(e.maxPriorityFeePerGas=o(e.maxPriorityFeePerGas)),void 0!==e.nonce&&(e.nonce=s(e.nonce,"nonce")),void 0!==e.chainId&&(e.chainId=s(e.chainId,"chainId")),void 0===e.maxFeePerGas&&void 0===e.maxPriorityFeePerGas||(void 0===e.gasPrice&&void 0!==e.maxFeePerGas&&(e.gasPrice=e.maxFeePerGas),delete e.maxFeePerGas,delete e.maxPriorityFeePerGas),delete e.type,delete e.accessList,delete e.account,delete e.chain,delete e.from,e}function c(t){return"string"==typeof t&&/^0x[0-9a-fA-F]*$/.test(t)}function u(t){let e="0x";for(let r=0;r<t.length;r++)e+=t[r].toString(16).padStart(2,"0");return e}async function l(t){const e=await i("/vault/info",{},t);if(!e||!e.vaultId||!e.evmAddress)throw new Error("Invalid vault info response: missing required fields");if(!String(e.evmAddress).startsWith("0x"))throw new Error("Invalid evmAddress format in response");return{vaultId:e.vaultId,tokenId:e.vaultId,address:e.address||"",evmAddress:e.evmAddress,created_by:e.created_by}}Object.defineProperty(exports,"__esModule",{value:!0});class h{constructor(t,e){this.publicKey=e.address,this.config=t,this.vaultId=e.vaultId}async signMessage(t){const e="string"==typeof t?(new TextEncoder).encode(t):t,r=btoa(String.fromCharCode(...e)),n=await i("/sign-solana-message",{vaultId:this.vaultId,message:r},this.config);try{const t="undefined"!=typeof window?window:void 0;if(t?.bs58)return t.bs58.decode(n.signature);return Uint8Array.from(atob(n.signature),t=>t.charCodeAt(0))}catch(t){if(n.signature.startsWith("0x")){const t=n.signature.slice(2),e=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)e[r/2]=parseInt(t.substr(r,2),16);return e}throw new Error(`Unable to decode signature format: ${t}`)}}async signTransaction(t){const e=this.serializeTransaction(t),r=await i("/sign-solana-transaction",{vaultId:this.vaultId,transactionToSign:e,broadcast:!1,versionedTransaction:!0},this.config),n=r.serializedSignedTransaction||r.signedTransaction;if(!n)throw new Error("No signed transaction data received from server");return this.deserializeTransaction(n)}serializeTransaction(t){if(t&&"object"==typeof t){if(t.serialize){const e=t.serialize();return btoa(String.fromCharCode(...e))}if(t.instructions||t.recentBlockhash)throw new Error("Cannot serialize unsigned transaction objects. Please use VersionedTransaction.")}return t}deserializeTransaction(t){if("object"==typeof t&&t&&t.serializedSignedTransaction&&(t=t.serializedSignedTransaction),"string"==typeof t)try{const e=atob(t);return new Uint8Array(e.split("").map(t=>t.charCodeAt(0)))}catch(t){throw console.error("Failed to decode transaction:",t),new Error(`Unable to deserialize transaction response: ${t}`)}return t}getVaultId(){return this.vaultId}async signAllTransactions(t){const e=[];for(const r of t)e.push(await this.signTransaction(r));return e}canSign(t){return t===this.publicKey}async signAndBroadcast(t,e=!0){const r=this.serializeTransaction(t),n=await i("/sign-solana-transaction",{vaultId:this.vaultId,transactionToSign:r,broadcast:e,versionedTransaction:!0},this.config);if(e){if(!n.transactionSignature)throw new Error("No transaction signature received from broadcast");return n.transactionSignature}if(!n.serializedSignedTransaction)throw new Error("No signed transaction data received from server");return n.serializedSignedTransaction}}function g(t,e,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(t):i?i.value:e.get(t)}function f(t,e,r,i,n){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!n)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?n.call(t,r):n?n.value=r:e.set(t,r),r}var w,v;"function"==typeof SuppressedError&&SuppressedError;class m{constructor(t,e,r){w.set(this,void 0),v.set(this,void 0),this.address=t,f(this,w,e,"f"),f(this,v,r,"f")}async signMessage(t){const e="string"==typeof t?t:u(t);return(await i("/sign-eth-message",{vaultId:g(this,w,"f"),message:e},g(this,v,"f"))).signature}async signTypedData(t,e,r){return(await i("/sign-typed-message",{vaultId:g(this,w,"f"),domain:t,types:e,message:r},g(this,v,"f"))).signature}async signTransaction(t){const e=d(t);return{rawTransaction:(await i("/sign-eth-tx",{vaultId:g(this,w,"f"),transaction:e},g(this,v,"f"))).signedTransaction}}}w=new WeakMap,v=new WeakMap,exports.EmblemSolanaSigner=h,exports.EmblemWeb3Adapter=m,exports.bytesToHex=u,exports.emblemGet=async function(t,i){const n=i.baseUrl??"https://api.emblemvault.ai",a=await r(i),s=await fetch(`${n}${t}`,{method:"GET",headers:a});if(!s.ok){const t=await s.text().catch(()=>"");throw new Error(e(s.status,t))}return s.json()},exports.emblemPost=i,exports.fetchVaultInfo=l,exports.isBrowserEnvironment=n,exports.isHexString=c,exports.isNodeEnvironment=function(){return"undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node},exports.normalizeTxForEmblem=d,exports.toEthersWallet=async function(e,r,n){let a;try{const e="ethers";a=await(s=e,Promise.resolve().then(function(){return t(require(s))}))}catch{throw new Error("ethers is required for toEthersWallet(). Install it with: npm install ethers")}var s;const{AbstractSigner:o,resolveAddress:c}=a,h=n??await l(e);class g extends o{constructor(t,e,r){super(e??null),this._address=null,this._vaultId=null,this._chainId=1,this._config=t,r?.address&&(this._address=r.address),r?.vaultId&&(this._vaultId=r.vaultId),r?.chainId&&(this._chainId=r.chainId)}async initialize(){return this._initPromise||(this._initPromise=l(this._config).then(t=>{this._address=t.evmAddress,this._vaultId=t.vaultId}).catch(t=>{throw this._initPromise=void 0,t})),this._initPromise}async getAddress(){return this._address||await this.initialize(),this._address}getVaultId(){if(!this._vaultId)throw new Error("Wallet not initialized. Call initialize() first.");return this._vaultId}setChainId(t){this._chainId=t}getChainId(){return this._chainId}connect(t){if(!t)throw new Error("Provider cannot be null");return new g(this._config,t,{address:this._address??void 0,vaultId:this._vaultId??void 0,chainId:this._chainId})}async signMessage(t){this._vaultId||await this.initialize();const e="string"==typeof t?t:u(t);return(await i("/sign-eth-message",{vaultId:this._vaultId,message:e},this._config)).signature}async signTypedData(t,e,r){this._vaultId||await this.initialize();const n={...e};n&&n.EIP712Domain&&delete n.EIP712Domain;return(await i("/sign-typed-message",{vaultId:this._vaultId,domain:t,types:n,message:r},this._config)).signature}async _signTypedData(t,e,r){return this.signTypedData(t,e,r)}async signTransaction(t){this._vaultId||await this.initialize();const e=t.from,r=await this.getAddress();if(e&&e.toLowerCase()!==r.toLowerCase())throw new Error("transaction from does not match signer address");const n=this.provider?{...await this.populateTransaction(t)}:{...t};if(n.from&&delete n.from,!("to"in n)||!n.to)throw new Error("Transaction must have a 'to' address");if(void 0===n.nonce||null===n.nonce)throw new Error("Transaction must have a nonce");const a=d(n);return(await i("/sign-eth-tx",{vaultId:this._vaultId,transaction:a,options:{chainId:this._chainId}},this._config)).signedTransaction}async sendTransaction(t){if(!this.provider)throw new Error("Provider required to send transaction");const e=await this.signTransaction(t);return await this.provider.broadcastTransaction(e)}async populateTransaction(t){const e={...t};if(!this.provider)throw new Error("Provider required to populate transaction");const r=e.from?await c(e.from,this.provider):await this.getAddress();let i;if(e.chainId)i=BigInt(e.chainId),this._chainId=Number(e.chainId);else{const t=await this.provider.getNetwork();i=t.chainId,this._chainId=Number(t.chainId)}const n=null!=e.nonce?Number(e.nonce):await this.provider.getTransactionCount(r,"pending"),a=e.to?await c(e.to,this.provider):null,s=e.value?BigInt(e.value.toString()):0n;let o;if(e.gasLimit)o=BigInt(e.gasLimit.toString());else try{o=await this.provider.estimateGas({...e,from:r})}catch{o=21000n}let d=null;if(e.gasPrice||2===e.type)e.gasPrice&&(d=BigInt(e.gasPrice.toString()));else{d=(await this.provider.getFeeData()).gasPrice??null}const u={from:r,to:a,value:s,nonce:n,gasLimit:o,data:e.data,chainId:i,type:e.type||void 0};return null!==d&&(u.gasPrice=d),e.maxFeePerGas&&(u.maxFeePerGas=BigInt(e.maxFeePerGas.toString())),e.maxPriorityFeePerGas&&(u.maxPriorityFeePerGas=BigInt(e.maxPriorityFeePerGas.toString())),u}async signAndBroadcast(t,e=!1){if(!this.provider)throw new Error("Provider required to send transaction");const r=await this.signTransaction(t),i=(await this.provider.broadcastTransaction(r)).hash;return e&&await this.provider.waitForTransaction(i),i}}return new g(e,r,{address:h.evmAddress,vaultId:h.vaultId})},exports.toHexIfBigInt=o,exports.toSafeNumber=s,exports.toSolanaKitSigner=async function(t,e){const r=e??await l(t);return new h(t,r)},exports.toSolanaWeb3Signer=async function(t,e){const r=e??await l(t);return new h(t,r)},exports.toViemAccount=async function(e,r){const n=r??await l(e),{evmAddress:a,vaultId:s}=n,{toAccount:o}=await(h="viem/accounts",Promise.resolve().then(function(){return t(require(h))}));var h;return o({address:a,async signMessage({message:t}){let r,n=!1;if("string"==typeof t)r=t,n=!1;else if(t&&void 0!==t.raw){const e=t.raw;r="string"==typeof e?e:u(e),n=!0}else if(t instanceof Uint8Array)r=u(t),n=!1;else{if(!c(t))throw new Error(`Unsupported message type: ${typeof t}. Expected string, Uint8Array, or hex string.`);r=t,n=!1}return(await i("/sign-eth-message",{vaultId:s,message:r,raw:n},e)).signature},async signTypedData(t){const{domain:r,types:n,message:a}=t;return(await i("/sign-typed-message",{vaultId:s,domain:r,types:n,message:a},e)).signature},async signTransaction(t,r){const n=d(t);return(await i("/sign-eth-tx",{vaultId:s,transaction:n},e)).signedTransaction}})},exports.toWeb3Adapter=async function(t,e){const r=e??await l(t);return new m(r.evmAddress,r.vaultId,t)},exports.validateBaseUrl=a,exports.validateEthereumAddress=function(t){if(!t||"string"!=typeof t)throw new Error("Address is required");if(!t.startsWith("0x"))throw new Error("Address must start with 0x");if(!/^0x[0-9a-fA-F]{40}$/.test(t))throw new Error("Invalid Ethereum address format")},exports.validateSignerConfig=function(t){const e=!!t.jwt,r="function"==typeof t.getJwt,i="function"==typeof t.getAuthHeaders;if(!e&&!r&&!i)throw new Error("Authentication required: provide jwt, getJwt(), or getAuthHeaders()");t.baseUrl&&a(t.baseUrl),t.debugSecurity&&console.log("[Emblem Security Debug]",{environment:n()?"browser":"node",hasBaseUrl:!!t.baseUrl,timestamp:(new Date).toISOString()})},exports.validateVaultId=function(t){if(!t||"string"!=typeof t)throw new Error("vaultId is required");if(""===t.trim())throw new Error("vaultId cannot be empty")};
|
|
1
|
+
"use strict";function t(t){if(t&&t.__esModule)return t;var e=Object.create(null);return t&&Object.keys(t).forEach(function(r){if("default"!==r){var s=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,s.get?s:{enumerable:!0,get:function(){return t[r]}})}}),e.default=t,Object.freeze(e)}function e(t,e){let r=`Emblem signer error ${t}`;return t>=500?r+=": Internal server error":401===t||403===t?r+=": Authentication failed":404===t?r+=": Resource not found":405===t?r+=": Method not allowed":e&&(r+=`: ${e.substring(0,200)}`),r}async function r(t){if("function"==typeof t.getAuthHeaders){const e=await t.getAuthHeaders();if(e&&"object"==typeof e)return e}const e=t.jwt??("function"==typeof t.getJwt?await t.getJwt():void 0)??t.sdk?.getSession()?.authToken??void 0;if(e)return{Authorization:`Bearer ${e}`};if(t.apiKey)return{"x-api-key":t.apiKey};throw new Error("No authentication available: provide jwt, getJwt(), getAuthHeaders(), sdk, or apiKey")}async function s(t,s,i){const n=i.baseUrl??"https://api.emblemvault.ai",a=await r(i),o=await fetch(`${n}${t}`,{method:"POST",headers:{"content-type":"application/json",...a},body:JSON.stringify(s,(t,e)=>"bigint"==typeof e?e.toString():e)});if(!o.ok){const t=await o.text().catch(()=>"");throw new Error(e(o.status,t))}return o.json()}function i(){return"undefined"!=typeof window&&"undefined"!=typeof document}function n(t){if(t){if(!t.startsWith("http://")&&!t.startsWith("https://"))throw new Error("baseUrl must be a valid HTTP(S) URL");!t.startsWith("http://")||t.includes("localhost")||t.includes("127.0.0.1")||console.warn("[Emblem Security Warning] baseUrl uses HTTP instead of HTTPS. This is insecure for production use.")}}function a(t,e){const r=Number(t);if(!Number.isSafeInteger(r))throw new Error(`${e} value ${t} exceeds safe integer range (max: ${Number.MAX_SAFE_INTEGER})`);return r}function o(t){return"bigint"==typeof t?"0x"+t.toString(16):t}function d(t){const e={...t};return void 0!==e.value&&(e.value=o(e.value)),void 0!==e.gas&&(e.gasLimit=o(e.gas),delete e.gas),void 0!==e.gasLimit&&(e.gasLimit=o(e.gasLimit)),void 0!==e.gasPrice&&(e.gasPrice=o(e.gasPrice)),void 0!==e.maxFeePerGas&&(e.maxFeePerGas=o(e.maxFeePerGas)),void 0!==e.maxPriorityFeePerGas&&(e.maxPriorityFeePerGas=o(e.maxPriorityFeePerGas)),void 0!==e.nonce&&(e.nonce=a(e.nonce,"nonce")),void 0!==e.chainId&&(e.chainId=a(e.chainId,"chainId")),void 0===e.maxFeePerGas&&void 0===e.maxPriorityFeePerGas||(void 0===e.gasPrice&&void 0!==e.maxFeePerGas&&(e.gasPrice=e.maxFeePerGas),delete e.maxFeePerGas,delete e.maxPriorityFeePerGas),delete e.type,delete e.accessList,delete e.account,delete e.chain,delete e.from,e}function c(t){return"string"==typeof t&&/^0x[0-9a-fA-F]*$/.test(t)}function u(t){let e="0x";for(let r=0;r<t.length;r++)e+=t[r].toString(16).padStart(2,"0");return e}async function h(t){const e=await s("/vault/info",{},t);if(!e||!e.vaultId||!e.evmAddress)throw new Error("Invalid vault info response: missing required fields");if(!String(e.evmAddress).startsWith("0x"))throw new Error("Invalid evmAddress format in response");return{vaultId:e.vaultId,tokenId:e.vaultId,address:e.address||"",evmAddress:e.evmAddress,created_by:e.created_by}}Object.defineProperty(exports,"__esModule",{value:!0});class l{constructor(t,e){this.publicKey=e.address,this.config=t,this.vaultId=e.vaultId}async signMessage(t){const e="string"==typeof t?(new TextEncoder).encode(t):t,r=btoa(String.fromCharCode(...e)),i=await s("/sign-solana-message",{vaultId:this.vaultId,message:r},this.config);try{const t="undefined"!=typeof window?window:void 0;if(t?.bs58)return t.bs58.decode(i.signature);return Uint8Array.from(atob(i.signature),t=>t.charCodeAt(0))}catch(t){if(i.signature.startsWith("0x")){const t=i.signature.slice(2),e=new Uint8Array(t.length/2);for(let r=0;r<t.length;r+=2)e[r/2]=parseInt(t.substr(r,2),16);return e}throw new Error(`Unable to decode signature format: ${t}`)}}async signTransaction(t){const e=this.serializeTransaction(t),r=await s("/sign-solana-transaction",{vaultId:this.vaultId,transactionToSign:e,broadcast:!1,versionedTransaction:!0},this.config),i=r.serializedSignedTransaction||r.signedTransaction;if(!i)throw new Error("No signed transaction data received from server");return this.deserializeTransaction(i)}serializeTransaction(t){if(t&&"object"==typeof t){if(t.serialize){const e=t.serialize();return btoa(String.fromCharCode(...e))}if(t.instructions||t.recentBlockhash)throw new Error("Cannot serialize unsigned transaction objects. Please use VersionedTransaction.")}return t}deserializeTransaction(t){if("object"==typeof t&&t&&t.serializedSignedTransaction&&(t=t.serializedSignedTransaction),"string"==typeof t)try{const e=atob(t);return new Uint8Array(e.split("").map(t=>t.charCodeAt(0)))}catch(t){throw console.error("Failed to decode transaction:",t),new Error(`Unable to deserialize transaction response: ${t}`)}return t}getVaultId(){return this.vaultId}async signAllTransactions(t){const e=[];for(const r of t)e.push(await this.signTransaction(r));return e}canSign(t){return t===this.publicKey}async signAndBroadcast(t,e=!0){const r=this.serializeTransaction(t),i=await s("/sign-solana-transaction",{vaultId:this.vaultId,transactionToSign:r,broadcast:e,versionedTransaction:!0},this.config);if(e){if(!i.transactionSignature)throw new Error("No transaction signature received from broadcast");return i.transactionSignature}if(!i.serializedSignedTransaction)throw new Error("No signed transaction data received from server");return i.serializedSignedTransaction}}function p(t,e,r,s){if("a"===r&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?s:"a"===r?s.call(t):s?s.value:e.get(t)}function f(t,e,r,s,i){if("m"===s)throw new TypeError("Private method is not writable");if("a"===s&&!i)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?t!==e||!i:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===s?i.call(t,r):i?i.value=r:e.set(t,r),r}var g,w;"function"==typeof SuppressedError&&SuppressedError;class v{constructor(t,e,r){g.set(this,void 0),w.set(this,void 0),this.address=t,f(this,g,e,"f"),f(this,w,r,"f")}async signMessage(t){const e="string"==typeof t?t:u(t);return(await s("/sign-eth-message",{vaultId:p(this,g,"f"),message:e},p(this,w,"f"))).signature}async signTypedData(t,e,r){return(await s("/sign-typed-message",{vaultId:p(this,g,"f"),domain:t,types:e,message:r},p(this,w,"f"))).signature}async signTransaction(t){const e=d(t);return{rawTransaction:(await s("/sign-eth-tx",{vaultId:p(this,g,"f"),transaction:e},p(this,w,"f"))).signedTransaction}}}g=new WeakMap,w=new WeakMap;const m=1e8;class y{constructor(t,e){if(!e.btcPubkey)throw new Error("Bitcoin public key is required in vault info");this.publicKey=e.btcPubkey,this.addresses=e.btcAddresses??{p2pkh:"",p2wpkh:"",p2tr:""},this.config=t,this.vaultId=e.vaultId}async signPsbt(t,e){const{transactionType:r,toSignInputs:i}=e,n=i?.map(t=>({...t,tapLeafHashToSign:this.normalizeToHex(t.tapLeafHashToSign)}));return await s("/sign-btc-transaction",{vaultId:this.vaultId,psbt:t,transactionType:r,toSignInputs:n},this.config)}async signAllPsbts(t){const e=[];for(const{psbt:r,options:s}of t)e.push(await this.signPsbt(r,s));return e}getVaultId(){return this.vaultId}getPublicKey(){return this.publicKey}ownsAddress(t){return t===this.addresses.p2pkh||t===this.addresses.p2wpkh||t===this.addresses.p2tr}getAddress(t="p2wpkh"){switch(t){case"p2pkh":return this.addresses.p2pkh;case"p2wpkh":case"p2sh":default:return this.addresses.p2wpkh;case"p2tr":return this.addresses.p2tr}}normalizeToHex(t){if(null==t)return;if("string"==typeof t)return t;const e=t;let r="";for(let t=0;t<e.length;t++)r+=e[t].toString(16).padStart(2,"0");return r}}async function b(t){const e=await s("/vault/info",{},t);if(!e||!e.vaultId)throw new Error("Invalid vault info response: missing vaultId");if(!e.btcPubkey)throw new Error("Invalid vault info response: missing btcPubkey (Bitcoin public key)");return{vaultId:e.vaultId,address:e.address||"",evmAddress:e.evmAddress||"0x",btcPubkey:e.btcPubkey,btcAddresses:e.btcAddresses,created_by:e.created_by}}function I(t){return t/m}exports.EmblemBitcoinSigner=y,exports.EmblemSolanaSigner=l,exports.EmblemWeb3Adapter=v,exports.SATS_PER_BTC=m,exports.btcToSats=function(t){return Math.round(t*m)},exports.bytesToHex=u,exports.calculateFee=function(t,e){return Math.ceil(t*e)},exports.detectAddressType=function(t){return t.startsWith("bc1p")?"p2tr":t.startsWith("bc1q")?"p2wpkh":t.startsWith("1")?"p2pkh":t.startsWith("3")?"p2sh":t.startsWith("tb1p")?"p2tr":t.startsWith("tb1q")?"p2wpkh":t.startsWith("m")||t.startsWith("n")?"p2pkh":t.startsWith("2")?"p2sh":"unknown"},exports.emblemGet=async function(t,s){const i=s.baseUrl??"https://api.emblemvault.ai",n=await r(s),a=await fetch(`${i}${t}`,{method:"GET",headers:n});if(!a.ok){const t=await a.text().catch(()=>"");throw new Error(e(a.status,t))}return a.json()},exports.emblemPost=s,exports.estimateTransactionSize=function(t,e,r="p2wpkh"){let s;switch(r){case"p2pkh":s=148;break;case"p2tr":s=58;break;default:s=68}return 10+t*s+31*e},exports.fetchBitcoinVaultInfo=b,exports.fetchVaultInfo=h,exports.formatSats=function(t){return`${t.toLocaleString()} sats (${I(t).toFixed(8)} BTC)`},exports.isBrowserEnvironment=i,exports.isDust=function(t,e="p2wpkh"){return t<("p2wpkh"===e||"p2tr"===e?294:546)},exports.isHexString=c,exports.isNodeEnvironment=function(){return"undefined"!=typeof process&&null!=process.versions&&null!=process.versions.node},exports.normalizeTxForEmblem=d,exports.satsToBTC=I,exports.toBitcoinSigner=async function(t,e){const r=e??await b(t);return new y(t,r)},exports.toEthersWallet=async function(e,r,i){let n;try{const e="ethers";n=await(a=e,Promise.resolve().then(function(){return t(require(a))}))}catch{throw new Error("ethers is required for toEthersWallet(). Install it with: npm install ethers")}var a;const{AbstractSigner:o,resolveAddress:c}=n,l=i??await h(e);class p extends o{constructor(t,e,r){super(e??null),this._address=null,this._vaultId=null,this._chainId=1,this._config=t,r?.address&&(this._address=r.address),r?.vaultId&&(this._vaultId=r.vaultId),r?.chainId&&(this._chainId=r.chainId)}async initialize(){return this._initPromise||(this._initPromise=h(this._config).then(t=>{this._address=t.evmAddress,this._vaultId=t.vaultId}).catch(t=>{throw this._initPromise=void 0,t})),this._initPromise}async getAddress(){return this._address||await this.initialize(),this._address}getVaultId(){if(!this._vaultId)throw new Error("Wallet not initialized. Call initialize() first.");return this._vaultId}setChainId(t){this._chainId=t}getChainId(){return this._chainId}connect(t){if(!t)throw new Error("Provider cannot be null");return new p(this._config,t,{address:this._address??void 0,vaultId:this._vaultId??void 0,chainId:this._chainId})}async signMessage(t){this._vaultId||await this.initialize();const e="string"==typeof t?t:u(t);return(await s("/sign-eth-message",{vaultId:this._vaultId,message:e},this._config)).signature}async signTypedData(t,e,r){this._vaultId||await this.initialize();const i={...e};i&&i.EIP712Domain&&delete i.EIP712Domain;return(await s("/sign-typed-message",{vaultId:this._vaultId,domain:t,types:i,message:r},this._config)).signature}async _signTypedData(t,e,r){return this.signTypedData(t,e,r)}async signTransaction(t){this._vaultId||await this.initialize();const e=t.from,r=await this.getAddress();if(e&&e.toLowerCase()!==r.toLowerCase())throw new Error("transaction from does not match signer address");const i=this.provider?{...await this.populateTransaction(t)}:{...t};if(i.from&&delete i.from,!("to"in i)||!i.to)throw new Error("Transaction must have a 'to' address");if(void 0===i.nonce||null===i.nonce)throw new Error("Transaction must have a nonce");const n=d(i);return(await s("/sign-eth-tx",{vaultId:this._vaultId,transaction:n,options:{chainId:this._chainId}},this._config)).signedTransaction}async sendTransaction(t){if(!this.provider)throw new Error("Provider required to send transaction");const e=await this.signTransaction(t);return await this.provider.broadcastTransaction(e)}async populateTransaction(t){const e={...t};if(!this.provider)throw new Error("Provider required to populate transaction");const r=e.from?await c(e.from,this.provider):await this.getAddress();let s;if(e.chainId)s=BigInt(e.chainId),this._chainId=Number(e.chainId);else{const t=await this.provider.getNetwork();s=t.chainId,this._chainId=Number(t.chainId)}const i=null!=e.nonce?Number(e.nonce):await this.provider.getTransactionCount(r,"pending"),n=e.to?await c(e.to,this.provider):null,a=e.value?BigInt(e.value.toString()):0n;let o;if(e.gasLimit)o=BigInt(e.gasLimit.toString());else try{o=await this.provider.estimateGas({...e,from:r})}catch{o=21000n}let d=null;if(e.gasPrice||2===e.type)e.gasPrice&&(d=BigInt(e.gasPrice.toString()));else{d=(await this.provider.getFeeData()).gasPrice??null}const u={from:r,to:n,value:a,nonce:i,gasLimit:o,data:e.data,chainId:s,type:e.type||void 0};return null!==d&&(u.gasPrice=d),e.maxFeePerGas&&(u.maxFeePerGas=BigInt(e.maxFeePerGas.toString())),e.maxPriorityFeePerGas&&(u.maxPriorityFeePerGas=BigInt(e.maxPriorityFeePerGas.toString())),u}async signAndBroadcast(t,e=!1){if(!this.provider)throw new Error("Provider required to send transaction");const r=await this.signTransaction(t),s=(await this.provider.broadcastTransaction(r)).hash;return e&&await this.provider.waitForTransaction(s),s}}return new p(e,r,{address:l.evmAddress,vaultId:l.vaultId})},exports.toHexIfBigInt=o,exports.toSafeNumber=a,exports.toSolanaKitSigner=async function(t,e){const r=e??await h(t);return new l(t,r)},exports.toSolanaWeb3Signer=async function(t,e){const r=e??await h(t);return new l(t,r)},exports.toViemAccount=async function(e,r){const i=r??await h(e),{evmAddress:n,vaultId:a}=i,{toAccount:o}=await(l="viem/accounts",Promise.resolve().then(function(){return t(require(l))}));var l;return o({address:n,async signMessage({message:t}){let r,i=!1;if("string"==typeof t)r=t,i=!1;else if(t&&void 0!==t.raw){const e=t.raw;r="string"==typeof e?e:u(e),i=!0}else if(t instanceof Uint8Array)r=u(t),i=!1;else{if(!c(t))throw new Error(`Unsupported message type: ${typeof t}. Expected string, Uint8Array, or hex string.`);r=t,i=!1}return(await s("/sign-eth-message",{vaultId:a,message:r,raw:i},e)).signature},async signTypedData(t){const{domain:r,types:i,message:n}=t;return(await s("/sign-typed-message",{vaultId:a,domain:r,types:i,message:n},e)).signature},async signTransaction(t,r){const i=d(t);return(await s("/sign-eth-tx",{vaultId:a,transaction:i},e)).signedTransaction}})},exports.toWeb3Adapter=async function(t,e){const r=e??await h(t);return new v(r.evmAddress,r.vaultId,t)},exports.validateBaseUrl=n,exports.validateEthereumAddress=function(t){if(!t||"string"!=typeof t)throw new Error("Address is required");if(!t.startsWith("0x"))throw new Error("Address must start with 0x");if(!/^0x[0-9a-fA-F]{40}$/.test(t))throw new Error("Invalid Ethereum address format")},exports.validateSignerConfig=function(t){const e=!!t.jwt,r="function"==typeof t.getJwt,s="function"==typeof t.getAuthHeaders;if(!e&&!r&&!s)throw new Error("Authentication required: provide jwt, getJwt(), or getAuthHeaders()");t.baseUrl&&n(t.baseUrl),t.debugSecurity&&console.log("[Emblem Security Debug]",{environment:i()?"browser":"node",hasBaseUrl:!!t.baseUrl,timestamp:(new Date).toISOString()})},exports.validateVaultId=function(t){if(!t||"string"!=typeof t)throw new Error("vaultId is required");if(""===t.trim())throw new Error("vaultId cannot be empty")};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|