@sidhujag/sysweb3-keyring 1.0.626 → 1.0.628

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 (40) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/cjs/keyring-core.js +2512 -0
  3. package/cjs/keyring-core.js.map +1 -0
  4. package/cjs/keyring-manager.js +23 -2437
  5. package/cjs/keyring-manager.js.map +1 -1
  6. package/cjs/software.js +58 -0
  7. package/cjs/software.js.map +1 -0
  8. package/cjs/storage-core.js +227 -0
  9. package/cjs/storage-core.js.map +1 -0
  10. package/cjs/storage.js +15 -210
  11. package/cjs/storage.js.map +1 -1
  12. package/cjs/transactions/ethereum.js +121 -29
  13. package/cjs/transactions/ethereum.js.map +1 -1
  14. package/cjs/transactions/evm-local-signer.js +11 -1
  15. package/cjs/transactions/evm-local-signer.js.map +1 -1
  16. package/cjs/transactions/hardware-psbt.js +167 -0
  17. package/cjs/transactions/hardware-psbt.js.map +1 -0
  18. package/cjs/transactions/syscoin-core.js +651 -0
  19. package/cjs/transactions/syscoin-core.js.map +1 -0
  20. package/cjs/transactions/syscoin.js +8 -768
  21. package/cjs/transactions/syscoin.js.map +1 -1
  22. package/cjs/types.js.map +1 -1
  23. package/package.json +38 -4
  24. package/types/keyring-core.d.ts +257 -0
  25. package/types/keyring-manager.d.ts +5 -223
  26. package/types/software.d.ts +23 -0
  27. package/types/storage-core.d.ts +22 -0
  28. package/types/storage.d.ts +1 -4
  29. package/types/transactions/ethereum.d.ts +16 -6
  30. package/types/transactions/evm-local-signer.d.ts +3 -1
  31. package/types/transactions/hardware-psbt.d.ts +7 -0
  32. package/types/transactions/syscoin-core.d.ts +92 -0
  33. package/types/transactions/syscoin.d.ts +8 -67
  34. package/types/types.d.ts +10 -0
  35. package/cjs/transactions/__tests__/integration.test.js +0 -237
  36. package/cjs/transactions/__tests__/integration.test.js.map +0 -1
  37. package/cjs/transactions/__tests__/syscoin.test.js +0 -361
  38. package/cjs/transactions/__tests__/syscoin.test.js.map +0 -1
  39. package/types/transactions/__tests__/integration.test.d.ts +0 -1
  40. package/types/transactions/__tests__/syscoin.test.d.ts +0 -1
@@ -0,0 +1,22 @@
1
+ /** A host-owned namespace containing one wallet authority's logical records. */
2
+ export interface VaultStoragePort {
3
+ get(key: string): Promise<unknown>;
4
+ set(key: string, value: unknown): Promise<void>;
5
+ /** Atomically creates the entire batch only when every key is absent. */
6
+ createManyIfAbsent(items: Record<string, unknown>): Promise<boolean>;
7
+ }
8
+ export interface VaultPersistence {
9
+ setEncryptedVault(decryptedVault: any, pwd: string, freshVaultKeys?: {
10
+ salt: string;
11
+ }): Promise<void>;
12
+ getDecryptedVault(pwd: string, legacyPassword?: string): Promise<any>;
13
+ }
14
+ export declare class VaultAuthenticationError extends Error {
15
+ readonly code = "INVALID_PASSWORD";
16
+ constructor();
17
+ }
18
+ /**
19
+ * Create once per wallet authority and share across its network keyrings.
20
+ * The host owns storage durability and cross-context atomic creation.
21
+ */
22
+ export declare const createVaultPersistence: (storage: VaultStoragePort) => VaultPersistence;
@@ -1,8 +1,5 @@
1
- export declare class VaultAuthenticationError extends Error {
2
- readonly code = "INVALID_PASSWORD";
3
- constructor();
4
- }
5
1
  export declare const setEncryptedVault: (decryptedVault: any, pwd: string, freshVaultKeys?: {
6
2
  salt: string;
7
3
  }) => Promise<void>;
8
4
  export declare const getDecryptedVault: (pwd: string, legacyPassword?: string) => Promise<any>;
5
+ export { VaultAuthenticationError } from './storage-core';
@@ -1,15 +1,21 @@
1
1
  import { TypedMessage, SignTypedDataVersion, TypedDataV1 } from '@metamask/eth-sig-util';
2
2
  import { INetwork } from '@sidhujag/sysweb3-network';
3
3
  import { BigNumber, TransactionResponse } from '../ethers-v6';
4
- import { LedgerKeyring } from '../ledger';
5
4
  import { CustomJsonRpcProvider } from '../providers';
6
- import { TrezorKeyring } from '../trezor';
7
5
  import { IResponseFromSendErcSignedTransaction, ISendSignedErcTransactionProps, IEthereumTransactions, IEvmCancellationOptions, IEvmReplacementOptions, IEvmSendOptions, SimpleTransactionRequest, KeyringAccountType, accountType, IGasParams } from '../types';
8
- export declare class EthereumTransactions implements IEthereumTransactions {
6
+ import type { LedgerKeyring } from '../ledger';
7
+ import type { TrezorKeyring } from '../trezor';
8
+ /** Minimal trusted device capabilities used by EVM transaction routing. */
9
+ export interface EthereumLedgerSigner {
10
+ evm: Pick<LedgerKeyring['evm'], 'signTypedData' | 'signPersonalMessage' | 'signEVMTransaction'>;
11
+ verifyUtxoAddress: LedgerKeyring['verifyUtxoAddress'];
12
+ }
13
+ export type EthereumTrezorSigner = Pick<TrezorKeyring, 'signTypedData' | 'signMessage' | 'signEthTransaction' | 'verifyUtxoAddress'>;
14
+ export declare class EthereumTransactions<TLedger extends EthereumLedgerSigner | undefined = LedgerKeyring, TTrezor extends EthereumTrezorSigner | undefined = TrezorKeyring> implements IEthereumTransactions {
9
15
  private _web3Provider;
10
16
  private _web3ProviderKey?;
11
- trezorSigner: TrezorKeyring;
12
- ledgerSigner: LedgerKeyring;
17
+ trezorSigner: TTrezor;
18
+ ledgerSigner: TLedger;
13
19
  private getNetwork;
14
20
  private captureSigningContext?;
15
21
  private abortController;
@@ -29,7 +35,10 @@ export declare class EthereumTransactions implements IEthereumTransactions {
29
35
  activeAccountId: number;
30
36
  activeAccountType: KeyringAccountType;
31
37
  activeNetwork: INetwork;
32
- }, ledgerSigner: LedgerKeyring, trezorSigner: TrezorKeyring, captureSigningContext?: () => () => void);
38
+ }, ledgerSigner: TLedger, trezorSigner: TTrezor, captureSigningContext?: () => () => void);
39
+ private requireLedgerSigner;
40
+ private requireTrezorSigner;
41
+ private assertHardwareSignerAvailable;
33
42
  private captureEvmSigningContext;
34
43
  get web3Provider(): CustomJsonRpcProvider;
35
44
  private ensureProvidersInitialized;
@@ -66,6 +75,7 @@ export declare class EthereumTransactions implements IEthereumTransactions {
66
75
  cancelSentTransaction: (txHash: string, isLegacy: boolean | undefined, fallbackNonce: number | undefined, options: IEvmCancellationOptions) => Promise<{
67
76
  error?: boolean;
68
77
  isCanceled: boolean;
78
+ maximumFee?: string;
69
79
  transaction?: TransactionResponse;
70
80
  }>;
71
81
  sendFormattedTransaction: (params: SimpleTransactionRequest, isLegacy?: boolean, options?: IEvmSendOptions) => Promise<any>;
@@ -20,5 +20,7 @@ export declare const hashPersonalMessage: (message: Uint8Array) => string;
20
20
  export declare const signPersonalMessage: (message: string | Uint8Array, privateKey: string | Uint8Array) => string;
21
21
  export declare const parsePersonalMessage: (hexMessage: string) => string;
22
22
  export declare const signTransaction: (transaction: TransactionRequest, privateKey: string | Uint8Array) => string;
23
- export declare const sendLocalEvmTransaction: (provider: CustomJsonRpcProvider, privateKey: string, transaction: Deferrable<Record<string, any>>, beforeBroadcast?: () => void, beforeSign?: () => void) => Promise<any>;
23
+ export declare const sendLocalEvmTransaction: (provider: CustomJsonRpcProvider, privateKey: string, transaction: Deferrable<Record<string, any>>, beforeBroadcast?: () => void, beforeSign?: () => void,
24
+ /** Synchronous final-envelope check; throw to reject. Promise results are rejected. */
25
+ validateBeforeSign?: (transaction: TransactionRequest) => void) => Promise<any>;
24
26
  export declare const sameAddress: (left: string, right: string) => boolean;
@@ -0,0 +1,7 @@
1
+ import { type ISyscoinHardwareSigner } from './syscoin-core';
2
+ import type { LedgerKeyring } from '../ledger';
3
+ import type { TrezorKeyring } from '../trezor';
4
+ /** Legacy device adapter. The software transaction engine never imports it. */
5
+ export declare function createLedgerPsbtSigner(ledger: LedgerKeyring): ISyscoinHardwareSigner;
6
+ /** Keep Trezor conversion and signing outside the software-only import graph. */
7
+ export declare function createTrezorPsbtSigner(trezor: TrezorKeyring): ISyscoinHardwareSigner;
@@ -0,0 +1,92 @@
1
+ import { ITxid, txUtils } from '@sidhujag/sysweb3-utils';
2
+ import { Psbt } from 'bitcoinjs-lib';
3
+ import { ISyscoinTransactions, KeyringAccountType, accountType } from '../types';
4
+ import { ExcludedOutpoint } from '../utils/excluded-utxos';
5
+ import { type PsbtAccountScope } from '../utils/psbt-account-scope';
6
+ import type { SyscoinHDSigner } from '../signers';
7
+ import type { INetwork } from '@sidhujag/sysweb3-network';
8
+ /** A hardware operation receives only its selected public account/context. */
9
+ export interface ISyscoinHardwareSigningRequest {
10
+ readonly account: Readonly<{
11
+ id: number;
12
+ xpub: string;
13
+ }>;
14
+ assertCurrent: () => void;
15
+ getRawTransaction?: ReturnType<typeof txUtils>['getRawTransaction'];
16
+ readonly network: Readonly<INetwork>;
17
+ psbt: Psbt;
18
+ scope: PsbtAccountScope;
19
+ }
20
+ /**
21
+ * Trusted wallet-authority adapter, not an untrusted plugin entry point.
22
+ * Implementations must preserve the reviewed unsigned transaction and enforce
23
+ * device signature/account-scope validation before returning the signed PSBT.
24
+ */
25
+ export type ISyscoinHardwareSigner = (request: ISyscoinHardwareSigningRequest) => Promise<Psbt>;
26
+ export interface ISyscoinHardwareSigners {
27
+ ledger?: ISyscoinHardwareSigner;
28
+ trezor?: ISyscoinHardwareSigner;
29
+ }
30
+ export declare const createHardwareSignerUnavailableError: (hardware: string) => Error & {
31
+ code: string;
32
+ };
33
+ export declare class SyscoinTransactionsCore implements ISyscoinTransactions {
34
+ private getSigner;
35
+ private getReadOnlySigner;
36
+ private hardwareSigners;
37
+ private getState;
38
+ private getAddress;
39
+ private captureSigningContext?;
40
+ constructor(getSyscoinSigner: () => {
41
+ hd: SyscoinHDSigner;
42
+ main: any;
43
+ }, getReadOnlySigner: () => {
44
+ main: any;
45
+ }, getState: () => {
46
+ accounts: {
47
+ HDAccount: accountType;
48
+ Imported: accountType;
49
+ Ledger: accountType;
50
+ Trezor: accountType;
51
+ };
52
+ activeAccountId: number;
53
+ activeAccountType: KeyringAccountType;
54
+ activeNetwork: INetwork;
55
+ }, getAddress: (xpub: string, isChangeAddress: boolean) => Promise<string>, hardwareSigners?: ISyscoinHardwareSigners, captureSigningContext?: () => () => void);
56
+ private getTransactionPSBT;
57
+ decodeRawTransaction: (psbtOrHex: any, isRawHex?: boolean) => any;
58
+ private feeCache;
59
+ getRecommendedFee: (explorerUrl: string) => Promise<number>;
60
+ txUtilsFunctions: () => {
61
+ getRawTransaction: (explorerUrl: string, txid: string) => Promise<any>;
62
+ };
63
+ private signPSBTWithSigner;
64
+ private createNativeSweepPSBT;
65
+ private createUnsignedPSBT;
66
+ private signPSBTWithMethod;
67
+ getEstimateSysTransactionFee: ({ txOptions, isMax, nativeSweep, amount, receivingAddress, feeRate, token, excludedOutpoints, }: {
68
+ amount: number | string;
69
+ feeRate?: number;
70
+ receivingAddress: string;
71
+ token?: {
72
+ guid: string;
73
+ symbol?: string;
74
+ } | null;
75
+ txOptions?: any;
76
+ isMax?: boolean | false;
77
+ nativeSweep?: boolean;
78
+ excludedOutpoints?: readonly ExcludedOutpoint[];
79
+ }) => Promise<{
80
+ grossAmount?: string | undefined;
81
+ recipientAmount?: string | undefined;
82
+ fee: number;
83
+ psbt: any;
84
+ }>;
85
+ private sendSignedTransaction;
86
+ signPSBT: ({ psbt, isTrezor, isLedger, }: {
87
+ psbt: any;
88
+ isTrezor?: boolean;
89
+ isLedger?: boolean;
90
+ }) => Promise<any>;
91
+ sendTransaction: (psbt: any) => Promise<ITxid>;
92
+ }
@@ -1,68 +1,9 @@
1
- import { INetwork } from '@sidhujag/sysweb3-network';
2
- import { ITxid } from '@sidhujag/sysweb3-utils';
3
- import { LedgerKeyring } from '../ledger';
4
- import { SyscoinHDSigner } from '../signers';
5
- import { TrezorKeyring } from '../trezor';
6
- import { ISyscoinTransactions, KeyringAccountType, accountType } from '../types';
7
- import { ExcludedOutpoint } from '../utils/excluded-utxos';
8
- export declare class SyscoinTransactions implements ISyscoinTransactions {
9
- private getSigner;
10
- private getReadOnlySigner;
11
- private trezor;
12
- private ledger;
13
- private getState;
14
- private getAddress;
15
- private captureSigningContext?;
16
- constructor(getSyscoinSigner: () => {
17
- hd: SyscoinHDSigner;
18
- main: any;
19
- }, getReadOnlySigner: () => {
20
- main: any;
21
- }, getState: () => {
22
- accounts: {
23
- HDAccount: accountType;
24
- Imported: accountType;
25
- Ledger: accountType;
26
- Trezor: accountType;
27
- };
28
- activeAccountId: number;
29
- activeAccountType: KeyringAccountType;
30
- activeNetwork: INetwork;
31
- }, getAddress: (xpub: string, isChangeAddress: boolean) => Promise<string>, ledgerSigner: LedgerKeyring, trezorSigner: TrezorKeyring, captureSigningContext?: () => () => void);
32
- private getTransactionPSBT;
33
- decodeRawTransaction: (psbtOrHex: any, isRawHex?: boolean) => any;
34
- private feeCache;
35
- getRecommendedFee: (explorerUrl: string) => Promise<number>;
36
- txUtilsFunctions: () => {
37
- getRawTransaction: (explorerUrl: string, txid: string) => Promise<any>;
38
- };
39
- private signPSBTWithSigner;
40
- private createNativeSweepPSBT;
41
- private createUnsignedPSBT;
42
- private signPSBTWithMethod;
43
- getEstimateSysTransactionFee: ({ txOptions, isMax, nativeSweep, amount, receivingAddress, feeRate, token, excludedOutpoints, }: {
44
- amount: number | string;
45
- feeRate?: number;
46
- receivingAddress: string;
47
- token?: {
48
- guid: string;
49
- symbol?: string;
50
- } | null;
51
- txOptions?: any;
52
- isMax?: boolean | false;
53
- nativeSweep?: boolean;
54
- excludedOutpoints?: readonly ExcludedOutpoint[];
55
- }) => Promise<{
56
- grossAmount?: string | undefined;
57
- recipientAmount?: string | undefined;
58
- fee: number;
59
- psbt: any;
60
- }>;
61
- private sendSignedTransaction;
62
- signPSBT: ({ psbt, isTrezor, isLedger, }: {
63
- psbt: any;
64
- isTrezor?: boolean;
65
- isLedger?: boolean;
66
- }) => Promise<any>;
67
- sendTransaction: (psbt: any) => Promise<ITxid>;
1
+ import { SyscoinTransactionsCore } from './syscoin-core';
2
+ import type { LedgerKeyring } from '../ledger';
3
+ import type { TrezorKeyring } from '../trezor';
4
+ type CoreArguments = ConstructorParameters<typeof SyscoinTransactionsCore>;
5
+ /** Legacy constructor: preserve device arguments while composing shared logic. */
6
+ export declare class SyscoinTransactions extends SyscoinTransactionsCore {
7
+ constructor(getSyscoinSigner: CoreArguments[0], getReadOnlySigner: CoreArguments[1], getState: CoreArguments[2], getAddress: CoreArguments[3], ledgerSigner: LedgerKeyring, trezorSigner: TrezorKeyring, captureSigningContext?: CoreArguments[5]);
68
8
  }
9
+ export {};
package/types/types.d.ts CHANGED
@@ -56,6 +56,15 @@ export interface IEvmReplacementOptions {
56
56
  beforeBroadcast?: () => void;
57
57
  }
58
58
  export interface IEvmCancellationOptions {
59
+ /** Select the locally controlled outer gas payer for a smart-account transaction. */
60
+ targetAccount?: {
61
+ id: number;
62
+ type: KeyringAccountType;
63
+ };
64
+ /** Read-only preview; never requests credentials, a device signature or broadcast. */
65
+ previewOnly?: boolean;
66
+ /** Required for sending: maximum execution gas fee in decimal wei approved by the user. */
67
+ approvedMaximumFee?: string;
59
68
  /** Synchronous host guard for selection or session state outside the keyring. */
60
69
  assertCurrentContext: () => void;
61
70
  /**
@@ -72,6 +81,7 @@ export interface IEthereumTransactions {
72
81
  cancelSentTransaction: (txHash: string, isLegacy: boolean | undefined, fallbackNonce: number | undefined, options: IEvmCancellationOptions) => Promise<{
73
82
  error?: boolean;
74
83
  isCanceled: boolean;
84
+ maximumFee?: string;
75
85
  transaction?: TransactionResponse;
76
86
  }>;
77
87
  decryptMessage: (msgParams: string[]) => string;
@@ -1,237 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const types_1 = require("../../types");
4
- const syscoin_1 = require("../syscoin");
5
- // Integration tests for the complete transaction flow
6
- describe('PSBT Transaction Flow Integration', () => {
7
- let syscoinTransactions;
8
- let mockSigner;
9
- let mockState;
10
- let mockLedger;
11
- beforeEach(() => {
12
- // Setup comprehensive mocks
13
- mockSigner = {
14
- hd: {
15
- Signer: { network: 'mainnet' },
16
- sign: jest.fn().mockResolvedValue('signed-psbt'),
17
- },
18
- main: {
19
- blockbookURL: 'https://blockbook.test',
20
- createPSBTFromRes: jest.fn().mockResolvedValue('unsigned-psbt'),
21
- assetAllocationSend: jest.fn().mockResolvedValue({
22
- psbt: 'unsigned-token-psbt',
23
- }),
24
- send: jest.fn().mockResolvedValue({
25
- extractTransaction: () => ({ getId: () => 'final-txid' }),
26
- }),
27
- },
28
- };
29
- mockState = {
30
- activeAccountId: 0,
31
- activeAccountType: types_1.KeyringAccountType.HDAccount,
32
- accounts: {
33
- [types_1.KeyringAccountType.HDAccount]: {
34
- 0: { xpub: 'xpub123', id: 0 },
35
- },
36
- },
37
- activeNetwork: { currency: 'sys', chainId: 57 },
38
- };
39
- mockLedger = {
40
- ledgerTransport: true,
41
- ledgerUtxoClient: {
42
- getMasterFingerprint: jest.fn().mockResolvedValue('fingerprint'),
43
- signPsbt: jest.fn().mockResolvedValue([[0, { signature: 'sig' }]]),
44
- },
45
- };
46
- const mockGetAddress = jest.fn().mockResolvedValue('change-address');
47
- const mockTrezor = {
48
- init: jest.fn(),
49
- convertToTrezorFormat: jest.fn(),
50
- signUtxoTransaction: jest.fn(),
51
- };
52
- syscoinTransactions = new syscoin_1.SyscoinTransactions(() => mockSigner, () => mockSigner, // getReadOnlySigner - same mock for testing
53
- () => mockState, mockGetAddress, mockLedger, mockTrezor);
54
- });
55
- describe('Complete Native Transaction Flow', () => {
56
- it('should handle native transaction from creation to broadcast', async () => {
57
- // Step 1: Create unsigned PSBT and get fee
58
- const { fee, psbt: unsignedPsbt } = await syscoinTransactions.getEstimateSysTransactionFee({
59
- amount: 1,
60
- receivingAddress: 'sys1qtest',
61
- feeRate: 0.00001,
62
- });
63
- expect(fee).toBeGreaterThan(0);
64
- expect(unsignedPsbt).toBeDefined();
65
- // Step 2: Sign the PSBT
66
- const signedPsbt = await syscoinTransactions.signPSBT({
67
- psbt: unsignedPsbt,
68
- isTrezor: false,
69
- isLedger: false,
70
- });
71
- // Step 3: Send the signed PSBT
72
- const result = await syscoinTransactions.sendTransaction(signedPsbt);
73
- expect(result.txid).toBe('final-txid');
74
- expect(mockSigner.main.send).toHaveBeenCalledWith(signedPsbt);
75
- });
76
- });
77
- describe('Complete Token Transaction Flow', () => {
78
- it('should handle token transaction from creation to broadcast', async () => {
79
- // Mock token lookup
80
- jest.doMock('@sidhujag/sysweb3-utils', () => ({
81
- getAsset: jest.fn().mockResolvedValue({
82
- assetGuid: 'token123',
83
- decimals: 8,
84
- }),
85
- }));
86
- // Step 1: Create unsigned token PSBT and get fee
87
- const { fee, psbt: unsignedPsbt } = await syscoinTransactions.getEstimateSysTransactionFee({
88
- amount: 100,
89
- receivingAddress: 'sys1qtest',
90
- token: { guid: 'token123', symbol: 'TEST' },
91
- });
92
- expect(fee).toBeGreaterThan(0);
93
- expect(unsignedPsbt).toBeDefined();
94
- expect(mockSigner.main.assetAllocationSend).toHaveBeenCalledWith(expect.any(Object), expect.any(Map), 'change-address', expect.any(Object), 'xpub123');
95
- // Step 2: Sign the token PSBT
96
- const signedPsbt = await syscoinTransactions.signPSBT({
97
- psbt: unsignedPsbt,
98
- isTrezor: false,
99
- isLedger: false,
100
- });
101
- // Step 3: Send the signed PSBT
102
- const result = await syscoinTransactions.sendTransaction(signedPsbt);
103
- expect(result.txid).toBe('final-txid');
104
- });
105
- });
106
- describe('Hardware Wallet Integration', () => {
107
- it('should handle Trezor transaction flow', async () => {
108
- const mockTrezor = {
109
- init: jest.fn(),
110
- convertToTrezorFormat: jest.fn().mockReturnValue('trezor-format'),
111
- signUtxoTransaction: jest.fn().mockResolvedValue('trezor-signed-psbt'),
112
- };
113
- syscoinTransactions.trezor = mockTrezor;
114
- // Create unsigned PSBT
115
- const { fee, psbt: unsignedPsbt } = await syscoinTransactions.getEstimateSysTransactionFee({
116
- amount: 1,
117
- receivingAddress: 'sys1qtest',
118
- });
119
- expect(fee).toBeGreaterThan(0);
120
- expect(unsignedPsbt).toBeDefined();
121
- // Sign with Trezor
122
- const signedPsbt = await syscoinTransactions.signPSBT({
123
- psbt: unsignedPsbt,
124
- isTrezor: true,
125
- });
126
- expect(mockTrezor.init).toHaveBeenCalled();
127
- expect(mockTrezor.signUtxoTransaction).toHaveBeenCalled();
128
- expect(signedPsbt).toBe('trezor-signed-psbt');
129
- // Send
130
- const result = await syscoinTransactions.sendTransaction(signedPsbt);
131
- expect(result.txid).toBe('final-txid');
132
- });
133
- it('should handle Ledger transaction flow', async () => {
134
- mockState.activeAccountType = types_1.KeyringAccountType.Ledger;
135
- mockState.accounts[types_1.KeyringAccountType.Ledger] = {
136
- 0: { xpub: 'ledger-xpub', id: 0, isLedgerWallet: true },
137
- };
138
- // Mock Psbt for Ledger
139
- jest.doMock('syscoinjs-lib', () => ({
140
- Psbt: {
141
- fromBase64: jest.fn().mockReturnValue({
142
- updateInput: jest.fn(),
143
- finalizeAllInputs: jest.fn(),
144
- toBase64: jest.fn().mockReturnValue('ledger-signed-psbt'),
145
- }),
146
- },
147
- }));
148
- // Create unsigned PSBT
149
- const { fee, psbt: unsignedPsbt } = await syscoinTransactions.getEstimateSysTransactionFee({
150
- amount: 1,
151
- receivingAddress: 'sys1qtest',
152
- });
153
- expect(fee).toBeGreaterThan(0);
154
- expect(unsignedPsbt).toBeDefined();
155
- // Sign with Ledger
156
- const signedPsbt = await syscoinTransactions.signPSBT({
157
- psbt: unsignedPsbt,
158
- isLedger: true,
159
- });
160
- expect(mockLedger.ledgerUtxoClient.signPsbt).toHaveBeenCalled();
161
- expect(signedPsbt).toBeDefined();
162
- // Send
163
- const result = await syscoinTransactions.sendTransaction(signedPsbt);
164
- expect(result.txid).toBe('final-txid');
165
- });
166
- });
167
- describe('Error Scenarios', () => {
168
- it('should handle signing errors separately from sending errors', async () => {
169
- // Test signing error
170
- mockSigner.hd.sign = jest
171
- .fn()
172
- .mockRejectedValue(new Error('Device disconnected'));
173
- await expect(syscoinTransactions.getEstimateSysTransactionFee({
174
- amount: 1,
175
- receivingAddress: 'sys1qtest',
176
- })).rejects.toThrow('Failed to sign transaction: Device disconnected');
177
- // Test sending error
178
- mockSigner.main.send = jest
179
- .fn()
180
- .mockRejectedValue(new Error('Network error'));
181
- await expect(syscoinTransactions.sendTransaction('valid-psbt')).rejects.toThrow('Failed to send transaction');
182
- });
183
- it('should require PSBT for sendTransaction', async () => {
184
- await expect(syscoinTransactions.sendTransaction('')).rejects.toThrow('Pre-signed PSBT is required');
185
- });
186
- });
187
- describe('Fee Calculation', () => {
188
- it('should calculate correct fees for multi-input transactions', async () => {
189
- // Mock multiple UTXOs
190
- const mockUtxos = [
191
- { txid: 'tx1', vout: 0, value: '50000000' },
192
- { txid: 'tx2', vout: 1, value: '30000000' },
193
- { txid: 'tx3', vout: 0, value: '20000000' },
194
- ];
195
- jest.doMock('syscoinjs-lib', () => ({
196
- utils: {
197
- fetchBackendUTXOS: jest.fn().mockResolvedValue(mockUtxos),
198
- sanitizeBlockbookUTXOs: jest.fn().mockReturnValue(mockUtxos),
199
- },
200
- }));
201
- jest.doMock('coinselectsyscoin', () => ({
202
- utils: {
203
- transactionBytes: jest.fn().mockReturnValue(500), // Larger tx with multiple inputs
204
- },
205
- }));
206
- const { fee } = await syscoinTransactions.getEstimateSysTransactionFee({
207
- amount: 0.8, // Amount that requires multiple UTXOs
208
- receivingAddress: 'sys1qtest',
209
- feeRate: 0.00001,
210
- });
211
- // Fee should be proportional to transaction size
212
- expect(fee).toBeGreaterThan(0.00001); // More than minimum
213
- });
214
- });
215
- describe('RBF Support', () => {
216
- it('should create RBF-enabled transactions by default', async () => {
217
- await syscoinTransactions.getEstimateSysTransactionFee({
218
- amount: 1,
219
- receivingAddress: 'sys1qtest',
220
- });
221
- // Check that RBF was enabled in the transaction options
222
- expect(mockSigner.main.createPSBTFromRes).toHaveBeenCalled();
223
- const callArgs = mockSigner.main.createPSBTFromRes.mock.calls[0];
224
- expect(callArgs[0]).toMatchObject({ rbf: true });
225
- });
226
- it('should disable RBF when requested', async () => {
227
- await syscoinTransactions.getEstimateSysTransactionFee({
228
- amount: 1,
229
- receivingAddress: 'sys1qtest',
230
- txOptions: { rbf: false },
231
- });
232
- const callArgs = mockSigner.main.createPSBTFromRes.mock.calls[0];
233
- expect(callArgs[0]).toMatchObject({ rbf: false });
234
- });
235
- });
236
- });
237
- //# sourceMappingURL=integration.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"integration.test.js","sourceRoot":"","sources":["../../../../src/transactions/__tests__/integration.test.ts"],"names":[],"mappings":"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"}