@guveno/wallet-sdk 1.0.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.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +211 -0
  3. package/dist/api/client.d.ts +48 -0
  4. package/dist/api/client.d.ts.map +1 -0
  5. package/dist/api/client.js +563 -0
  6. package/dist/api/client.js.map +1 -0
  7. package/dist/api/types.d.ts +237 -0
  8. package/dist/api/types.d.ts.map +1 -0
  9. package/dist/api/types.js +9 -0
  10. package/dist/api/types.js.map +1 -0
  11. package/dist/chains.d.ts +25 -0
  12. package/dist/chains.d.ts.map +1 -0
  13. package/dist/chains.js +67 -0
  14. package/dist/chains.js.map +1 -0
  15. package/dist/constants.d.ts +17 -0
  16. package/dist/constants.d.ts.map +1 -0
  17. package/dist/constants.js +19 -0
  18. package/dist/constants.js.map +1 -0
  19. package/dist/crypto/sealed-box.d.ts +8 -0
  20. package/dist/crypto/sealed-box.d.ts.map +1 -0
  21. package/dist/crypto/sealed-box.js +66 -0
  22. package/dist/crypto/sealed-box.js.map +1 -0
  23. package/dist/derivation.d.ts +26 -0
  24. package/dist/derivation.d.ts.map +1 -0
  25. package/dist/derivation.js +121 -0
  26. package/dist/derivation.js.map +1 -0
  27. package/dist/errors.d.ts +81 -0
  28. package/dist/errors.d.ts.map +1 -0
  29. package/dist/errors.js +95 -0
  30. package/dist/errors.js.map +1 -0
  31. package/dist/hot-wallet.d.ts +57 -0
  32. package/dist/hot-wallet.d.ts.map +1 -0
  33. package/dist/hot-wallet.js +139 -0
  34. package/dist/hot-wallet.js.map +1 -0
  35. package/dist/index.d.ts +29 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +21 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/keyprovider/file-provider.d.ts +19 -0
  40. package/dist/keyprovider/file-provider.d.ts.map +1 -0
  41. package/dist/keyprovider/file-provider.js +48 -0
  42. package/dist/keyprovider/file-provider.js.map +1 -0
  43. package/dist/keyprovider/index.d.ts +13 -0
  44. package/dist/keyprovider/index.d.ts.map +1 -0
  45. package/dist/keyprovider/index.js +3 -0
  46. package/dist/keyprovider/index.js.map +1 -0
  47. package/dist/keyprovider/kms-provider.d.ts +26 -0
  48. package/dist/keyprovider/kms-provider.d.ts.map +1 -0
  49. package/dist/keyprovider/kms-provider.js +32 -0
  50. package/dist/keyprovider/kms-provider.js.map +1 -0
  51. package/dist/manager.d.ts +28 -0
  52. package/dist/manager.d.ts.map +1 -0
  53. package/dist/manager.js +499 -0
  54. package/dist/manager.js.map +1 -0
  55. package/dist/mnemonic.d.ts +13 -0
  56. package/dist/mnemonic.d.ts.map +1 -0
  57. package/dist/mnemonic.js +44 -0
  58. package/dist/mnemonic.js.map +1 -0
  59. package/dist/session/file-store.d.ts +10 -0
  60. package/dist/session/file-store.d.ts.map +1 -0
  61. package/dist/session/file-store.js +88 -0
  62. package/dist/session/file-store.js.map +1 -0
  63. package/dist/signing/sign-bitcoin.d.ts +14 -0
  64. package/dist/signing/sign-bitcoin.d.ts.map +1 -0
  65. package/dist/signing/sign-bitcoin.js +133 -0
  66. package/dist/signing/sign-bitcoin.js.map +1 -0
  67. package/dist/signing/sign-ethereum.d.ts +13 -0
  68. package/dist/signing/sign-ethereum.d.ts.map +1 -0
  69. package/dist/signing/sign-ethereum.js +49 -0
  70. package/dist/signing/sign-ethereum.js.map +1 -0
  71. package/dist/signing/sign-polkadot.d.ts +9 -0
  72. package/dist/signing/sign-polkadot.d.ts.map +1 -0
  73. package/dist/signing/sign-polkadot.js +79 -0
  74. package/dist/signing/sign-polkadot.js.map +1 -0
  75. package/dist/signing/sign-withdrawal.d.ts +21 -0
  76. package/dist/signing/sign-withdrawal.d.ts.map +1 -0
  77. package/dist/signing/sign-withdrawal.js +35 -0
  78. package/dist/signing/sign-withdrawal.js.map +1 -0
  79. package/dist/signing/sign-xrp.d.ts +7 -0
  80. package/dist/signing/sign-xrp.d.ts.map +1 -0
  81. package/dist/signing/sign-xrp.js +27 -0
  82. package/dist/signing/sign-xrp.js.map +1 -0
  83. package/dist/signing/types.d.ts +84 -0
  84. package/dist/signing/types.d.ts.map +1 -0
  85. package/dist/signing/types.js +15 -0
  86. package/dist/signing/types.js.map +1 -0
  87. package/dist/storage/crypto.d.ts +7 -0
  88. package/dist/storage/crypto.d.ts.map +1 -0
  89. package/dist/storage/crypto.js +80 -0
  90. package/dist/storage/crypto.js.map +1 -0
  91. package/dist/storage/file-store.d.ts +21 -0
  92. package/dist/storage/file-store.d.ts.map +1 -0
  93. package/dist/storage/file-store.js +219 -0
  94. package/dist/storage/file-store.js.map +1 -0
  95. package/dist/types.d.ts +187 -0
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +2 -0
  98. package/dist/types.js.map +1 -0
  99. package/docs/security.md +119 -0
  100. package/package.json +76 -0
@@ -0,0 +1,44 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { generateMnemonic, validateMnemonic } from '@scure/bip39';
3
+ import { wordlist as englishWordlist } from '@scure/bip39/wordlists/english.js';
4
+ import { SUPPORTED_MNEMONIC_WORD_COUNTS } from './constants.js';
5
+ import { ValidationError } from './errors.js';
6
+ export function normalizeMnemonic(mnemonic) {
7
+ return mnemonic
8
+ .trim()
9
+ .toLowerCase()
10
+ .split(/\s+/)
11
+ .filter(Boolean)
12
+ .join(' ');
13
+ }
14
+ export function getMnemonicWordCount(mnemonic) {
15
+ const normalized = normalizeMnemonic(mnemonic);
16
+ return normalized.length === 0 ? 0 : normalized.split(' ').length;
17
+ }
18
+ export function isSupportedMnemonicWordCount(count) {
19
+ return SUPPORTED_MNEMONIC_WORD_COUNTS.includes(count);
20
+ }
21
+ export function assertValidMnemonic(mnemonic) {
22
+ const normalized = normalizeMnemonic(mnemonic);
23
+ const wordCount = getMnemonicWordCount(normalized);
24
+ if (!isSupportedMnemonicWordCount(wordCount)) {
25
+ throw new ValidationError('Mnemonic must contain exactly 12 or 24 words.');
26
+ }
27
+ if (!validateMnemonic(normalized, englishWordlist)) {
28
+ throw new ValidationError('Mnemonic is not a valid BIP39 English mnemonic.');
29
+ }
30
+ return normalized;
31
+ }
32
+ export function generateRandomMnemonic(wordCount = 12) {
33
+ const strength = wordCount === 12 ? 128 : 256;
34
+ return generateMnemonic(englishWordlist, strength);
35
+ }
36
+ /**
37
+ * SHA-256 of the trimmed mnemonic, hex-encoded — the `keyFingerprint` a wallet is
38
+ * registered under. Used to confirm a key provider returned the mnemonic that
39
+ * actually belongs to a given wallet before signing.
40
+ */
41
+ export function fingerprintMnemonic(mnemonic) {
42
+ return createHash('sha256').update(mnemonic.trim(), 'utf8').digest('hex');
43
+ }
44
+ //# sourceMappingURL=mnemonic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mnemonic.js","sourceRoot":"","sources":["../src/mnemonic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,QAAQ,IAAI,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAChF,OAAO,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,QAAQ;SACZ,IAAI,EAAE;SACN,WAAW,EAAE;SACb,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,OAAO,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,KAAa;IACxD,OAAO,8BAA8B,CAAC,QAAQ,CAAC,KAA0B,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,oBAAoB,CAAC,UAAU,CAAC,CAAC;IAEnD,IAAI,CAAC,4BAA4B,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,eAAe,CAAC,+CAA+C,CAAC,CAAC;IAC7E,CAAC;IAED,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,eAAe,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,eAAe,CAAC,iDAAiD,CAAC,CAAC;IAC/E,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,YAA+B,EAAE;IACtE,MAAM,QAAQ,GAAG,SAAS,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9C,OAAO,gBAAgB,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ApiSession, ApiSessionStore } from '../api/types.js';
2
+ export declare class FileSystemApiSessionStore implements ApiSessionStore {
3
+ readonly rootDir: string;
4
+ private readonly sessionPath;
5
+ constructor(storageDir?: string);
6
+ read(): Promise<ApiSession | null>;
7
+ write(session: ApiSession): Promise<void>;
8
+ clear(): Promise<void>;
9
+ }
10
+ //# sourceMappingURL=file-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../../src/session/file-store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AA6CnE,qBAAa,yBAA0B,YAAW,eAAe;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,UAAU,CAAC,EAAE,MAAM;IAKzB,IAAI,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAmBlC,KAAK,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAUzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAa7B"}
@@ -0,0 +1,88 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ import path from 'node:path';
4
+ import { z } from 'zod';
5
+ import { StorageError } from '../errors.js';
6
+ import { resolveStorageDir } from '../storage/file-store.js';
7
+ const jsonObjectSchema = z.record(z.string(), z.unknown());
8
+ const companyMembershipSchema = z.object({
9
+ id: z.number().int().positive(),
10
+ name: z.string(),
11
+ role: z.enum(['owner', 'admin', 'manager', 'signer', 'viewer'])
12
+ });
13
+ const userEncryptionKeySchema = z.object({
14
+ id: z.uuid(),
15
+ publicKey: z.string(),
16
+ encryptedPrivateKeyJson: jsonObjectSchema,
17
+ createdAt: z.iso.datetime(),
18
+ updatedAt: z.iso.datetime()
19
+ });
20
+ const apiUserSchema = z.object({
21
+ id: z.number().int().positive(),
22
+ email: z.email(),
23
+ company: companyMembershipSchema.nullable(),
24
+ encryptionKey: userEncryptionKeySchema.nullable(),
25
+ createdAt: z.iso.datetime(),
26
+ updatedAt: z.iso.datetime()
27
+ });
28
+ const sessionSchema = z.object({
29
+ baseUrl: z.url(),
30
+ accessToken: z.string().min(1),
31
+ tokenType: z.literal('Bearer'),
32
+ expiresIn: z.string().min(1),
33
+ user: apiUserSchema,
34
+ authenticatedAt: z.iso.datetime()
35
+ });
36
+ async function writeJsonAtomic(filePath, value) {
37
+ const dir = path.dirname(filePath);
38
+ const tempPath = path.join(dir, `${path.basename(filePath)}.${randomUUID()}.tmp`);
39
+ const payload = `${JSON.stringify(value, null, 2)}\n`;
40
+ await fs.writeFile(tempPath, payload, { encoding: 'utf8', mode: 0o600 });
41
+ await fs.rename(tempPath, filePath);
42
+ }
43
+ export class FileSystemApiSessionStore {
44
+ rootDir;
45
+ sessionPath;
46
+ constructor(storageDir) {
47
+ this.rootDir = resolveStorageDir(storageDir);
48
+ this.sessionPath = path.join(this.rootDir, 'auth-session.json');
49
+ }
50
+ async read() {
51
+ await fs.mkdir(this.rootDir, { recursive: true, mode: 0o700 });
52
+ try {
53
+ const raw = await fs.readFile(this.sessionPath, 'utf8');
54
+ return sessionSchema.parse(JSON.parse(raw));
55
+ }
56
+ catch (error) {
57
+ if (error.code === 'ENOENT') {
58
+ return null;
59
+ }
60
+ if (error instanceof z.ZodError) {
61
+ throw new StorageError(`Session file ${this.sessionPath} is malformed.`, { cause: error });
62
+ }
63
+ throw new StorageError('Failed to read the API session store.', { cause: error });
64
+ }
65
+ }
66
+ async write(session) {
67
+ await fs.mkdir(this.rootDir, { recursive: true, mode: 0o700 });
68
+ try {
69
+ await writeJsonAtomic(this.sessionPath, session);
70
+ }
71
+ catch (error) {
72
+ throw new StorageError('Failed to persist the API session store.', { cause: error });
73
+ }
74
+ }
75
+ async clear() {
76
+ try {
77
+ await fs.unlink(this.sessionPath);
78
+ }
79
+ catch (error) {
80
+ const code = error.code;
81
+ if (code === 'ENOENT') {
82
+ return;
83
+ }
84
+ throw new StorageError('Failed to clear the API session store.', { cause: error });
85
+ }
86
+ }
87
+ }
88
+ //# sourceMappingURL=file-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store.js","sourceRoot":"","sources":["../../src/session/file-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { SigningPayload } from './types.js';
2
+ /**
3
+ * Builds and signs a P2WPKH (native SegWit) Bitcoin withdrawal for a prepared
4
+ * payload, returning the signed raw transaction hex the server broadcasts.
5
+ *
6
+ * The server selects the source address's UTXOs and supplies the fee rate and
7
+ * change address; this signer spends all of them, pays the requested output(s),
8
+ * computes the fee from the fee rate and the transaction's virtual size, and
9
+ * returns the remainder as change to the source address (dropping it into the
10
+ * fee when it would be dust). The private key is derived in-process from the
11
+ * mnemonic and never leaves the caller.
12
+ */
13
+ export declare function signBitcoinWithdrawal(payload: SigningPayload, mnemonic: string, derivationPath: string): string;
14
+ //# sourceMappingURL=sign-bitcoin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-bitcoin.d.ts","sourceRoot":"","sources":["../../src/signing/sign-bitcoin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAoC,cAAc,EAAE,MAAM,YAAY,CAAC;AA6CnF;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CA8F/G"}
@@ -0,0 +1,133 @@
1
+ import { mnemonicToSeedSync } from '@scure/bip39';
2
+ import { BIP32Factory } from 'bip32';
3
+ import { address as btcAddress, initEccLib, networks, payments, Psbt } from 'bitcoinjs-lib';
4
+ import * as ecc from 'tiny-secp256k1';
5
+ initEccLib(ecc);
6
+ const bip32 = BIP32Factory(ecc);
7
+ // Outputs below this many sats cost more to spend than they're worth, so we fold
8
+ // a sub-dust change amount into the fee rather than create an unspendable output.
9
+ // 294 sats is the standard relay dust limit for a P2WPKH output.
10
+ const DUST_THRESHOLD = 294n;
11
+ // Conservative virtual-size constants (vbytes) for fee estimation. We round up so
12
+ // we never underpay and risk the transaction being rejected by the mempool.
13
+ // - overhead: nVersion(4) + segwit marker+flag(0.5) + in/out var-ints + locktime(4)
14
+ // - P2WPKH input: 36 outpoint + 4 sequence + 1 empty scriptSig + ~27 witness
15
+ const TX_OVERHEAD_VBYTES = 11;
16
+ const P2WPKH_INPUT_VBYTES = 68;
17
+ /** Serialized-output virtual size: 8-byte value + var-int script length + script. */
18
+ function outputVbytes(script) {
19
+ const length = script.length;
20
+ const varint = length < 0xfd ? 1 : 3;
21
+ return 8 + varint + length;
22
+ }
23
+ /**
24
+ * Converts a decimal display amount (e.g. "0.25") to integer base units (sats)
25
+ * without floating point. Rejects amounts with more precision than the asset allows.
26
+ */
27
+ function toBaseUnits(amount, decimals) {
28
+ const trimmed = amount.trim();
29
+ const parts = trimmed.split('.');
30
+ if (parts.length > 2) {
31
+ throw new Error(`Invalid Bitcoin amount "${amount}".`);
32
+ }
33
+ const whole = parts[0] ?? '';
34
+ const fraction = parts[1] ?? '';
35
+ if (!/^\d+$/.test(whole) || (fraction !== '' && !/^\d+$/.test(fraction))) {
36
+ throw new Error(`Invalid Bitcoin amount "${amount}".`);
37
+ }
38
+ if (fraction.length > decimals) {
39
+ throw new Error(`Amount "${amount}" has more than ${decimals} decimal places.`);
40
+ }
41
+ return BigInt(whole + fraction.padEnd(decimals, '0'));
42
+ }
43
+ /**
44
+ * Builds and signs a P2WPKH (native SegWit) Bitcoin withdrawal for a prepared
45
+ * payload, returning the signed raw transaction hex the server broadcasts.
46
+ *
47
+ * The server selects the source address's UTXOs and supplies the fee rate and
48
+ * change address; this signer spends all of them, pays the requested output(s),
49
+ * computes the fee from the fee rate and the transaction's virtual size, and
50
+ * returns the remainder as change to the source address (dropping it into the
51
+ * fee when it would be dust). The private key is derived in-process from the
52
+ * mnemonic and never leaves the caller.
53
+ */
54
+ export function signBitcoinWithdrawal(payload, mnemonic, derivationPath) {
55
+ const state = payload.chainState;
56
+ if (state == null) {
57
+ throw new Error('Missing Bitcoin chain state from the server.');
58
+ }
59
+ if (!Array.isArray(state.utxos) || state.utxos.length === 0) {
60
+ throw new Error('No spendable UTXOs are available for this address.');
61
+ }
62
+ const network = payload.network === 'testnet' ? networks.testnet : networks.bitcoin;
63
+ const seed = mnemonicToSeedSync(mnemonic);
64
+ const node = bip32.fromSeed(Buffer.from(seed), network).derivePath(derivationPath);
65
+ const source = payments.p2wpkh({ pubkey: Buffer.from(node.publicKey), network });
66
+ if (source.address == null || source.output == null) {
67
+ throw new Error('Unable to derive the Bitcoin signing key.');
68
+ }
69
+ // Safety net: the derived key must own the source address (and therefore the
70
+ // UTXOs). A mismatch means a wrong path/mnemonic — fail before signing.
71
+ if (source.address !== payload.sourceAddress) {
72
+ throw new Error(`Derived address ${source.address} does not match the withdrawal's source ${payload.sourceAddress}.`);
73
+ }
74
+ const sourceScript = Buffer.from(source.output);
75
+ const decimals = payload.asset.decimals;
76
+ const recipients = payload.outputs != null && payload.outputs.length > 0
77
+ ? payload.outputs
78
+ : payload.toAddress != null
79
+ ? [{ toAddress: payload.toAddress, amount: payload.amount }]
80
+ : [];
81
+ if (recipients.length === 0) {
82
+ throw new Error('Bitcoin withdrawals require at least one output.');
83
+ }
84
+ // Resolve each recipient to a value + output script (also validates the address
85
+ // belongs to this network).
86
+ const outputs = recipients.map((recipient) => {
87
+ const value = toBaseUnits(recipient.amount, decimals);
88
+ if (value <= 0n) {
89
+ throw new Error('Each Bitcoin output amount must be greater than zero.');
90
+ }
91
+ return { value, script: btcAddress.toOutputScript(recipient.toAddress, network) };
92
+ });
93
+ const totalIn = state.utxos.reduce((sum, utxo) => sum + BigInt(utxo.value), 0n);
94
+ const totalOut = outputs.reduce((sum, output) => sum + output.value, 0n);
95
+ const feeRate = BigInt(Math.max(1, Math.ceil(state.feeRate)));
96
+ const changeScript = btcAddress.toOutputScript(state.changeAddress ?? payload.sourceAddress, network);
97
+ const baseVbytes = TX_OVERHEAD_VBYTES +
98
+ state.utxos.length * P2WPKH_INPUT_VBYTES +
99
+ outputs.reduce((sum, output) => sum + outputVbytes(output.script), 0);
100
+ // First assume a change output exists; if the leftover is dust, drop it and let
101
+ // the whole remainder be the fee.
102
+ const feeWithChange = BigInt(baseVbytes + outputVbytes(changeScript)) * feeRate;
103
+ const change = totalIn - totalOut - feeWithChange;
104
+ const finalOutputs = outputs.map((output) => ({ script: output.script, value: output.value }));
105
+ if (change >= DUST_THRESHOLD) {
106
+ finalOutputs.push({ script: changeScript, value: change });
107
+ }
108
+ else {
109
+ const feeWithoutChange = BigInt(baseVbytes) * feeRate;
110
+ if (totalIn < totalOut + feeWithoutChange) {
111
+ throw new Error('Insufficient funds to cover the withdrawal and network fee.');
112
+ }
113
+ }
114
+ const psbt = new Psbt({ network });
115
+ for (const utxo of state.utxos) {
116
+ psbt.addInput({
117
+ hash: utxo.txid,
118
+ index: utxo.vout,
119
+ witnessUtxo: { script: sourceScript, value: BigInt(utxo.value) },
120
+ });
121
+ }
122
+ for (const output of finalOutputs) {
123
+ psbt.addOutput({ script: output.script, value: output.value });
124
+ }
125
+ const signer = {
126
+ publicKey: Buffer.from(node.publicKey),
127
+ sign: (hash) => Buffer.from(node.sign(hash)),
128
+ };
129
+ psbt.signAllInputs(signer);
130
+ psbt.finalizeAllInputs();
131
+ return psbt.extractTransaction().toHex();
132
+ }
133
+ //# sourceMappingURL=sign-bitcoin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-bitcoin.js","sourceRoot":"","sources":["../../src/signing/sign-bitcoin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { SigningPayload } from './types.js';
2
+ /** Optional caller-set gas, in wei (fees) and gas units (limit). Each falls back to the server suggestion. */
3
+ export interface EthereumGasOverrides {
4
+ maxFeePerGas?: string;
5
+ maxPriorityFeePerGas?: string;
6
+ gasLimit?: string;
7
+ }
8
+ /**
9
+ * Builds and signs an EIP-1559 Ethereum transaction for a prepared withdrawal.
10
+ * Returns a serialized signed tx the server relays via `provider.broadcastTransaction`.
11
+ */
12
+ export declare function signEthereumWithdrawal(payload: SigningPayload, mnemonic: string, derivationPath: string, overrides?: EthereumGasOverrides): Promise<string>;
13
+ //# sourceMappingURL=sign-ethereum.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-ethereum.d.ts","sourceRoot":"","sources":["../../src/signing/sign-ethereum.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,YAAY,CAAC;AAIrE,8GAA8G;AAC9G,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,SAAS,CAAC,EAAE,oBAAoB,GAC/B,OAAO,CAAC,MAAM,CAAC,CA6CjB"}
@@ -0,0 +1,49 @@
1
+ import { HDNodeWallet, Interface, parseUnits } from 'ethers';
2
+ const ERC20_INTERFACE = new Interface(['function transfer(address to,uint256 amount)']);
3
+ /**
4
+ * Builds and signs an EIP-1559 Ethereum transaction for a prepared withdrawal.
5
+ * Returns a serialized signed tx the server relays via `provider.broadcastTransaction`.
6
+ */
7
+ export async function signEthereumWithdrawal(payload, mnemonic, derivationPath, overrides) {
8
+ const state = payload.chainState;
9
+ if (!state) {
10
+ throw new Error('Missing Ethereum chain state from the server.');
11
+ }
12
+ if (!payload.toAddress) {
13
+ throw new Error('Ethereum withdrawals require a destination address.');
14
+ }
15
+ const wallet = HDNodeWallet.fromPhrase(mnemonic, undefined, derivationPath);
16
+ const value = parseUnits(payload.amount, payload.asset.decimals);
17
+ // Nonce and chainId are correctness-critical and never overridable; gas is.
18
+ const maxFeePerGas = BigInt(overrides?.maxFeePerGas ?? state.maxFeePerGas);
19
+ const maxPriorityFeePerGas = BigInt(overrides?.maxPriorityFeePerGas ?? state.maxPriorityFeePerGas);
20
+ const gasLimit = BigInt(overrides?.gasLimit ?? state.gasLimit);
21
+ if (maxPriorityFeePerGas > maxFeePerGas) {
22
+ throw new Error('Max priority fee cannot exceed max fee.');
23
+ }
24
+ const base = {
25
+ nonce: state.nonce,
26
+ chainId: BigInt(state.chainId),
27
+ maxFeePerGas,
28
+ maxPriorityFeePerGas,
29
+ gasLimit,
30
+ type: 2
31
+ };
32
+ let request;
33
+ if (payload.asset.type === 'erc20') {
34
+ if (!payload.asset.contractAddress) {
35
+ throw new Error('ERC20 asset is missing its contract address.');
36
+ }
37
+ request = {
38
+ ...base,
39
+ to: payload.asset.contractAddress,
40
+ value: 0n,
41
+ data: ERC20_INTERFACE.encodeFunctionData('transfer', [payload.toAddress, value])
42
+ };
43
+ }
44
+ else {
45
+ request = { ...base, to: payload.toAddress, value };
46
+ }
47
+ return wallet.signTransaction(request);
48
+ }
49
+ //# sourceMappingURL=sign-ethereum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-ethereum.js","sourceRoot":"","sources":["../../src/signing/sign-ethereum.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAA2B,MAAM,QAAQ,CAAC;AAGtF,MAAM,eAAe,GAAG,IAAI,SAAS,CAAC,CAAC,8CAA8C,CAAC,CAAC,CAAC;AASxF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAuB,EACvB,QAAgB,EAChB,cAAsB,EACtB,SAAgC;IAEhC,MAAM,KAAK,GAAG,OAAO,CAAC,UAA4C,CAAC;IACnE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,QAAQ,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IAC5E,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAEjE,4EAA4E;IAC5E,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,EAAE,YAAY,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3E,MAAM,oBAAoB,GAAG,MAAM,CAAC,SAAS,EAAE,oBAAoB,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACnG,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,EAAE,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC/D,IAAI,oBAAoB,GAAG,YAAY,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,IAAI,GAAuB;QAC/B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;QAC9B,YAAY;QACZ,oBAAoB;QACpB,QAAQ;QACR,IAAI,EAAE,CAAC;KACR,CAAC;IAEF,IAAI,OAA2B,CAAC;IAChC,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACnC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,GAAG;YACR,GAAG,IAAI;YACP,EAAE,EAAE,OAAO,CAAC,KAAK,CAAC,eAAe;YACjC,KAAK,EAAE,EAAE;YACT,IAAI,EAAE,eAAe,CAAC,kBAAkB,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;SACjF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC;IACtD,CAAC;IAED,OAAO,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { SigningPayload } from './types.js';
2
+ /**
3
+ * Builds and signs a `balances.transferKeepAlive` extrinsic for a prepared
4
+ * Polkadot withdrawal. Fully offline: the server-supplied {@link PolkadotChainState}
5
+ * carries the runtime metadata so the call is SCALE-encoded here without any RPC.
6
+ * Returns the signed extrinsic hex the server relays via `author.submitExtrinsic`.
7
+ */
8
+ export declare function signPolkadotWithdrawal(payload: SigningPayload, mnemonic: string, derivationPath: string): Promise<string>;
9
+ //# sourceMappingURL=sign-polkadot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-polkadot.d.ts","sourceRoot":"","sources":["../../src/signing/sign-polkadot.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAsB,cAAc,EAAE,MAAM,YAAY,CAAC;AAoBrE;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CA2DjB"}
@@ -0,0 +1,79 @@
1
+ import { Keyring } from '@polkadot/keyring';
2
+ import { u8aEq } from '@polkadot/util';
3
+ import { cryptoWaitReady, decodeAddress } from '@polkadot/util-crypto';
4
+ import { construct, getRegistryBase, methods } from '@substrate/txwrapper-polkadot';
5
+ import { POLKADOT_SS58_FORMAT } from '../chains.js';
6
+ /**
7
+ * Converts a decimal display amount (e.g. "1.5") to integer base units (plancks)
8
+ * using the asset's decimals, without floating point. Rejects more precision than
9
+ * the asset supports so we never silently truncate a user's amount.
10
+ */
11
+ function toBaseUnits(amount, decimals) {
12
+ const trimmed = amount.trim();
13
+ if (!/^\d+(\.\d+)?$/.test(trimmed)) {
14
+ throw new Error(`Invalid Polkadot amount "${amount}".`);
15
+ }
16
+ const [whole, fraction = ''] = trimmed.split('.');
17
+ if (fraction.length > decimals) {
18
+ throw new Error(`Amount "${amount}" exceeds the asset's ${decimals} decimals of precision.`);
19
+ }
20
+ const combined = `${whole}${fraction.padEnd(decimals, '0')}`.replace(/^0+(?=\d)/, '');
21
+ return BigInt(combined).toString();
22
+ }
23
+ /**
24
+ * Builds and signs a `balances.transferKeepAlive` extrinsic for a prepared
25
+ * Polkadot withdrawal. Fully offline: the server-supplied {@link PolkadotChainState}
26
+ * carries the runtime metadata so the call is SCALE-encoded here without any RPC.
27
+ * Returns the signed extrinsic hex the server relays via `author.submitExtrinsic`.
28
+ */
29
+ export async function signPolkadotWithdrawal(payload, mnemonic, derivationPath) {
30
+ const state = payload.chainState;
31
+ if (!state) {
32
+ throw new Error('Missing Polkadot chain state from the server.');
33
+ }
34
+ if (!payload.toAddress) {
35
+ throw new Error('Polkadot withdrawals require a destination address.');
36
+ }
37
+ await cryptoWaitReady();
38
+ const ss58Format = POLKADOT_SS58_FORMAT[payload.network] ?? 0;
39
+ const keyring = new Keyring({ type: 'sr25519', ss58Format });
40
+ const pair = keyring.createFromUri(`${mnemonic}${derivationPath}`);
41
+ // The 32-byte public key is the account's true identity regardless of SS58
42
+ // encoding; refuse to sign if the derived key doesn't own the source address.
43
+ if (!u8aEq(pair.publicKey, decodeAddress(payload.sourceAddress))) {
44
+ throw new Error('The derived Polkadot key does not match the withdrawal source address.');
45
+ }
46
+ const metadataRpc = state.metadataRpc;
47
+ const registry = getRegistryBase({
48
+ chainProperties: {
49
+ ss58Format,
50
+ tokenDecimals: payload.asset.decimals,
51
+ tokenSymbol: payload.asset.symbol
52
+ },
53
+ // Polkadot and Paseo encode a balances transfer with base Substrate types, so
54
+ // no chain-specific type overrides are needed to decode the metadata.
55
+ specTypes: {},
56
+ metadataRpc
57
+ });
58
+ const unsigned = methods.balances.transferKeepAlive({
59
+ dest: { id: payload.toAddress },
60
+ value: toBaseUnits(payload.amount, payload.asset.decimals)
61
+ }, {
62
+ address: payload.sourceAddress,
63
+ blockHash: state.blockHash,
64
+ blockNumber: state.blockNumber,
65
+ eraPeriod: state.eraPeriod,
66
+ genesisHash: state.genesisHash,
67
+ metadataRpc,
68
+ nonce: state.nonce,
69
+ specVersion: state.specVersion,
70
+ tip: Number(state.tip),
71
+ transactionVersion: state.transactionVersion
72
+ }, { metadataRpc, registry });
73
+ const signingPayload = construct.signingPayload(unsigned, { registry });
74
+ const { signature } = registry
75
+ .createType('ExtrinsicPayload', signingPayload, { version: unsigned.version })
76
+ .sign(pair);
77
+ return construct.signedTx(unsigned, signature, { metadataRpc, registry });
78
+ }
79
+ //# sourceMappingURL=sign-polkadot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-polkadot.js","sourceRoot":"","sources":["../../src/signing/sign-polkadot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type EthereumGasOverrides } from './sign-ethereum.js';
2
+ import { type SigningPayload } from './types.js';
3
+ export interface SignWithdrawalInput {
4
+ payload: SigningPayload;
5
+ /** Decrypted mnemonic for the wallet's key. Held only in caller scope; never persisted. */
6
+ mnemonic: string;
7
+ /** Derivation path of the source address (from its Address record). */
8
+ derivationPath: string;
9
+ /** Caller-customized Ethereum gas (wei / units); falls back to server suggestions. */
10
+ ethereumGas?: EthereumGasOverrides;
11
+ }
12
+ /**
13
+ * Produces a fully-signed raw transaction (hex) for a prepared withdrawal. Pure — no
14
+ * network access. New chains are added here as their per-chain signer + server chain
15
+ * state land.
16
+ */
17
+ export declare function signWithdrawal({ payload, mnemonic, derivationPath, ethereumGas }: SignWithdrawalInput): Promise<string>;
18
+ /** Chains the SDK signer currently supports. */
19
+ export declare const SIGNABLE_CHAINS: readonly ["ethereum", "xrp", "bitcoin", "polkadot"];
20
+ export declare function canSignChain(chain: string): boolean;
21
+ //# sourceMappingURL=sign-withdrawal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-withdrawal.d.ts","sourceRoot":"","sources":["../../src/signing/sign-withdrawal.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAGvF,OAAO,EAAmD,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAElG,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,cAAc,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,cAAc,EAAE,MAAM,CAAC;IACvB,sFAAsF;IACtF,WAAW,CAAC,EAAE,oBAAoB,CAAC;CACpC;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,EACnC,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAAW,EACZ,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBvC;AAED,gDAAgD;AAChD,eAAO,MAAM,eAAe,qDAAsD,CAAC;AAEnF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD"}
@@ -0,0 +1,35 @@
1
+ import { assertValidMnemonic } from '../mnemonic.js';
2
+ import { signBitcoinWithdrawal } from './sign-bitcoin.js';
3
+ import { signEthereumWithdrawal } from './sign-ethereum.js';
4
+ import { signPolkadotWithdrawal } from './sign-polkadot.js';
5
+ import { signXrpWithdrawal } from './sign-xrp.js';
6
+ import { UnsupportedSigningError, WithdrawalExpiredError } from './types.js';
7
+ /**
8
+ * Produces a fully-signed raw transaction (hex) for a prepared withdrawal. Pure — no
9
+ * network access. New chains are added here as their per-chain signer + server chain
10
+ * state land.
11
+ */
12
+ export async function signWithdrawal({ payload, mnemonic, derivationPath, ethereumGas }) {
13
+ assertValidMnemonic(mnemonic);
14
+ if (Date.now() >= new Date(payload.expiresAt).getTime()) {
15
+ throw new WithdrawalExpiredError();
16
+ }
17
+ switch (payload.chain) {
18
+ case 'ethereum':
19
+ return signEthereumWithdrawal(payload, mnemonic, derivationPath, ethereumGas);
20
+ case 'xrp':
21
+ return signXrpWithdrawal(payload, mnemonic, derivationPath);
22
+ case 'bitcoin':
23
+ return signBitcoinWithdrawal(payload, mnemonic, derivationPath);
24
+ case 'polkadot':
25
+ return signPolkadotWithdrawal(payload, mnemonic, derivationPath);
26
+ default:
27
+ throw new UnsupportedSigningError(payload.chain);
28
+ }
29
+ }
30
+ /** Chains the SDK signer currently supports. */
31
+ export const SIGNABLE_CHAINS = ['ethereum', 'xrp', 'bitcoin', 'polkadot'];
32
+ export function canSignChain(chain) {
33
+ return SIGNABLE_CHAINS.includes(chain);
34
+ }
35
+ //# sourceMappingURL=sign-withdrawal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-withdrawal.js","sourceRoot":"","sources":["../../src/signing/sign-withdrawal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAA6B,MAAM,oBAAoB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAuB,MAAM,YAAY,CAAC;AAYlG;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EACnC,OAAO,EACP,QAAQ,EACR,cAAc,EACd,WAAW,EACS;IACpB,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAE9B,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACxD,MAAM,IAAI,sBAAsB,EAAE,CAAC;IACrC,CAAC;IAED,QAAQ,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,KAAK,UAAU;YACb,OAAO,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;QAChF,KAAK,KAAK;YACR,OAAO,iBAAiB,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QAC9D,KAAK,SAAS;YACZ,OAAO,qBAAqB,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QAClE,KAAK,UAAU;YACb,OAAO,sBAAsB,CAAC,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QACnE;YACE,MAAM,IAAI,uBAAuB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,gDAAgD;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,CAAU,CAAC;AAEnF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAQ,eAAqC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { SigningPayload } from './types.js';
2
+ /**
3
+ * Builds and signs an XRP Payment for a prepared withdrawal. Returns the signed
4
+ * tx_blob hex the server relays via `client.submit`.
5
+ */
6
+ export declare function signXrpWithdrawal(payload: SigningPayload, mnemonic: string, derivationPath: string): string;
7
+ //# sourceMappingURL=sign-xrp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-xrp.d.ts","sourceRoot":"","sources":["../../src/signing/sign-xrp.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,YAAY,CAAC;AAEhE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAuB3G"}
@@ -0,0 +1,27 @@
1
+ import { Wallet as XrplWallet, xrpToDrops } from 'xrpl';
2
+ /**
3
+ * Builds and signs an XRP Payment for a prepared withdrawal. Returns the signed
4
+ * tx_blob hex the server relays via `client.submit`.
5
+ */
6
+ export function signXrpWithdrawal(payload, mnemonic, derivationPath) {
7
+ const state = payload.chainState;
8
+ if (!state) {
9
+ throw new Error('Missing XRP chain state from the server.');
10
+ }
11
+ if (!payload.toAddress) {
12
+ throw new Error('XRP withdrawals require a destination address.');
13
+ }
14
+ const wallet = XrplWallet.fromMnemonic(mnemonic, { derivationPath });
15
+ const payment = {
16
+ TransactionType: 'Payment',
17
+ Account: payload.sourceAddress,
18
+ Destination: payload.toAddress,
19
+ Amount: xrpToDrops(payload.amount),
20
+ Sequence: state.sequence,
21
+ Fee: state.fee,
22
+ LastLedgerSequence: state.lastLedgerSequence,
23
+ ...(payload.destinationTag != null ? { DestinationTag: payload.destinationTag } : {})
24
+ };
25
+ return wallet.sign(payment).tx_blob;
26
+ }
27
+ //# sourceMappingURL=sign-xrp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-xrp.js","sourceRoot":"","sources":["../../src/signing/sign-xrp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,UAAU,EAAgB,MAAM,MAAM,CAAC;AAGtE;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAuB,EAAE,QAAgB,EAAE,cAAsB;IACjG,MAAM,KAAK,GAAG,OAAO,CAAC,UAAuC,CAAC;IAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;IAErE,MAAM,OAAO,GAAY;QACvB,eAAe,EAAE,SAAS;QAC1B,OAAO,EAAE,OAAO,CAAC,aAAa;QAC9B,WAAW,EAAE,OAAO,CAAC,SAAS;QAC9B,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC;QAClC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,kBAAkB,EAAE,KAAK,CAAC,kBAAkB;QAC5C,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtF,CAAC;IAEF,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;AACtC,CAAC"}