emblem-vault-sdk 2.3.6 → 3.0.0-experimental
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/.traces/blockchain-provider-abstraction-plan.md +302 -0
- package/.traces/notes.md +43 -0
- package/.traces/readme-verification-trace.md +988 -0
- package/.traces/test-migration-status.md +227 -0
- package/.traces/tests-consolidated-trace.md +667 -0
- package/dist/abi/abi.json +53 -0
- package/dist/bundle.js +21602 -17419
- package/dist/clients/emblemVaultSolanaWalletClient.d.ts +74 -0
- package/dist/clients/emblemVaultSolanaWalletClient.js +149 -0
- package/dist/clients/emblemVaultSolanaWalletClient.js.map +1 -0
- package/dist/clients/emblemVaultWalletClient.d.ts +52 -0
- package/dist/clients/emblemVaultWalletClient.js +110 -0
- package/dist/clients/emblemVaultWalletClient.js.map +1 -0
- package/dist/derive.js +7 -0
- package/dist/derive.js.map +1 -0
- package/dist/index.js +319 -114
- package/dist/index.js.map +1 -0
- package/dist/providers/ProviderManager.d.ts +57 -0
- package/dist/providers/ProviderManager.js +283 -0
- package/dist/providers/ProviderManager.js.map +1 -0
- package/dist/providers.d.ts +101 -0
- package/dist/providers.js +96 -0
- package/dist/providers.js.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.js +61 -58
- package/dist/utils.js.map +1 -0
- package/docs/bundle.js +21602 -17419
- package/docs/index.html +150 -13
- package/docs/local.html +385 -0
- package/docs/steps.html +420 -53
- package/package.json +22 -13
- package/readme.md +795 -21
- package/src/abi/abi.json +54 -1
- package/src/clients/emblemVaultSolanaWalletClient.ts +257 -0
- package/src/clients/emblemVaultWalletClient.ts +159 -0
- package/src/derive.ts +5 -5
- package/src/index.ts +320 -102
- package/src/providers/ProviderManager.ts +254 -0
- package/src/providers.ts +163 -0
- package/src/types.ts +50 -0
- package/src/utils.ts +63 -62
- package/test/.mocharc.js +8 -0
- package/test/README.md +212 -0
- package/test/fixtures/ethscriptions/balance-with-native-first.json +24 -0
- package/test/fixtures/ethscriptions/balance-with-native.json +23 -0
- package/test/fixtures/ethscriptions/balance.json +18 -0
- package/test/helpers/sdkLoader.js +88 -0
- package/test/helpers/setup.js +55 -0
- package/test/integration/ApiOperations.spec.js +102 -0
- package/test/integration/VaultAI.spec.js +57 -0
- package/test/integration/VaultCreation.spec.js +95 -0
- package/test/mocks.js +127 -0
- package/test/tsconfig.json +25 -0
- package/test/unit/AllowedBells.spec.js +53 -0
- package/test/unit/AllowedBitcoinDeGods.spec.js +53 -0
- package/test/unit/AllowedBitcoinOrdinals.spec.js +46 -0
- package/test/unit/AllowedCounterparty.spec.js +66 -0
- package/test/unit/AllowedCursedOrdinals.spec.js +68 -0
- package/test/unit/AllowedEmbells.spec.js +11 -0
- package/test/unit/AllowedEmbels.spec.js +11 -0
- package/test/unit/AllowedEmblemOpen.spec.js +20 -0
- package/test/unit/AllowedEthscriptions.spec.js +53 -0
- package/test/unit/AllowedHardcodedNfts.spec.js +77 -0
- package/test/unit/AllowedNamecoin.spec.js +33 -0
- package/test/unit/AllowedOrdi.spec.js +46 -0
- package/test/unit/AllowedOxbt.spec.js +46 -0
- package/test/unit/AllowedProjectCollection.spec.js +51 -0
- package/test/unit/AllowedProtocolCollection.spec.js +44 -0
- package/test/unit/AllowedStamps.spec.js +78 -0
- package/test/unit/DarkfarmsMetadata.spec.js +10 -0
- package/test/unit/EmblemVaultSdk.spec.js +188 -0
- package/test/unit/Providers.spec.js +290 -0
- package/test/unit/ResourceMonitor.spec.js +111 -0
- package/test/unit/SDKMemoryLeaks.spec.js +176 -0
- package/test/unit/Signing.spec.js +321 -0
- package/test/unit/VaultAI.spec.js +140 -0
- package/test/unit/clients/EmblemVaultSolanaWalletClient.spec.js +296 -0
- package/test/unit/clients/EmblemVaultWalletClient.spec.js +246 -0
- package/test/utils/ResourceMonitor.js +169 -0
- package/test/windsurf-rules.md +68 -0
- package/tsconfig.json +7 -3
- package/types/derive.d.ts +0 -5
- package/types/index.d.ts +152 -32
- package/types/types.d.ts +43 -0
- package/types/utils.d.ts +7 -1
- package/dist/abi/quote.js +0 -29
- package/dist/abi/quote.json +0 -26
- package/jest.config.js +0 -6
- package/tests/AllowedBells.test.ts +0 -53
- package/tests/AllowedBitcoinDeGods.test.ts +0 -53
- package/tests/AllowedBitcoinOrdinals.test.ts +0 -46
- package/tests/AllowedCounterparty.test.ts +0 -45
- package/tests/AllowedCursedOrdinals.test.ts +0 -68
- package/tests/AllowedEmbells.test.ts +0 -12
- package/tests/AllowedEmbels.test.ts +0 -16
- package/tests/AllowedEmblemOpen.test.ts +0 -20
- package/tests/AllowedEthscriptions.test.ts +0 -39
- package/tests/AllowedHardcodedNfts.test.ts +0 -78
- package/tests/AllowedNamecoin.test.ts +0 -33
- package/tests/AllowedOrdi.test.ts +0 -47
- package/tests/AllowedOxbt.test.ts +0 -47
- package/tests/AllowedProjectlCollection.test.ts +0 -51
- package/tests/AllowedProtocolCollection.test.ts +0 -44
- package/tests/AllowedStamps.test.ts +0 -73
- package/tests/DarkfarmsMetadata.test.ts +0 -10
- package/tests/EmblemVaultSdk.test.ts +0 -124
- package/tests/fixtures/embels/balance.json +0 -10
- package/tests/fixtures/ethscriptions/balance-with-native-first.json +0 -69
- package/tests/fixtures/ethscriptions/balance-with-native.json +0 -37
- package/tests/fixtures/ethscriptions/balance.json +0 -64
- /package/{tests → test}/fixtures/bells/balance-with-non-bell-first.json +0 -0
- /package/{tests → test}/fixtures/bells/balance-with-non-bell.json +0 -0
- /package/{tests → test}/fixtures/bells/balance.json +0 -0
- /package/{tests → test}/fixtures/bitcoin-degods/balance-with-non-degods-first.json +0 -0
- /package/{tests → test}/fixtures/bitcoin-degods/balance-with-non-degods.json +0 -0
- /package/{tests → test}/fixtures/bitcoin-degods/balance.json +0 -0
- /package/{tests → test}/fixtures/bitcoin-ordinals/balance-with-native-first.json +0 -0
- /package/{tests → test}/fixtures/bitcoin-ordinals/balance-with-only-native.json +0 -0
- /package/{tests → test}/fixtures/bitcoin-ordinals/balance.json +0 -0
- /package/{tests → test}/fixtures/counterparty/balance-with-multiple.json +0 -0
- /package/{tests → test}/fixtures/counterparty/balance.json +0 -0
- /package/{tests → test}/fixtures/cursed-ordinals/balance-with-native-first.json +0 -0
- /package/{tests → test}/fixtures/cursed-ordinals/balance-with-native.json +0 -0
- /package/{tests → test}/fixtures/cursed-ordinals/balance.json +0 -0
- /package/{tests → test}/fixtures/dogeparty/balance-with-non-dogeparty-first.json +0 -0
- /package/{tests → test}/fixtures/dogeparty/balance-with-non-dogeparty.json +0 -0
- /package/{tests → test}/fixtures/dogeparty/balance.json +0 -0
- /package/{tests → test}/fixtures/emblem-open/balance.json +0 -0
- /package/{tests → test}/fixtures/hardcoded-nfts/balance-witn-non-hardcoded-first.json +0 -0
- /package/{tests → test}/fixtures/hardcoded-nfts/balance-witn-non-hardcoded.json +0 -0
- /package/{tests → test}/fixtures/hardcoded-nfts/balance.json +0 -0
- /package/{tests → test}/fixtures/megapunks/balance-with-non-megapunk-first.json +0 -0
- /package/{tests → test}/fixtures/megapunks/balance-with-non-megapunk.json +0 -0
- /package/{tests → test}/fixtures/megapunks/balance.json +0 -0
- /package/{tests → test}/fixtures/namecoin/balance-with-native-first.json +0 -0
- /package/{tests → test}/fixtures/namecoin/balance-with-native.json +0 -0
- /package/{tests → test}/fixtures/namecoin/balance.json +0 -0
- /package/{tests → test}/fixtures/ordi/balance-with-native-first.json +0 -0
- /package/{tests → test}/fixtures/ordi/balance-with-native.json +0 -0
- /package/{tests → test}/fixtures/ordi/balance.json +0 -0
- /package/{tests → test}/fixtures/oxbt/balance-with-native-first.json +0 -0
- /package/{tests → test}/fixtures/oxbt/balance-with-native.json +0 -0
- /package/{tests → test}/fixtures/oxbt/balance.json +0 -0
- /package/{tests → test}/fixtures/stamps/balance-with-non-stamps-first.json +0 -0
- /package/{tests → test}/fixtures/stamps/balance-with-non-stamps.json +0 -0
- /package/{tests → test}/fixtures/stamps/balance.json +0 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
const { expect } = require('chai');
|
|
2
|
+
const sinon = require('sinon'); // Using sinon for mocking/stubbing
|
|
3
|
+
const { EmblemVaultSDK } = require('../../dist/index.js'); // Assuming built files are in dist
|
|
4
|
+
const {
|
|
5
|
+
detectProviderType,
|
|
6
|
+
isProviderType,
|
|
7
|
+
Web3ProviderAdapter
|
|
8
|
+
} = require('../../dist/providers.js');
|
|
9
|
+
const { Connection, PublicKey } = require('@solana/web3.js');
|
|
10
|
+
|
|
11
|
+
// Define blockchain type constants to match the implementation
|
|
12
|
+
const EthereumProvider = 'ethereum';
|
|
13
|
+
const SolanaProvider = 'solana';
|
|
14
|
+
const BitcoinProvider = 'bitcoin';
|
|
15
|
+
const OtherProvider = 'other';
|
|
16
|
+
|
|
17
|
+
describe('Blockchain Provider Abstraction', () => {
|
|
18
|
+
let sdk;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
sdk = new EmblemVaultSDK('mock-api-key');
|
|
22
|
+
// Reset window mocks if needed for detection tests in the future
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
sinon.restore(); // Restore any sinon mocks/stubs
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('Utility Functions (providers.ts)', () => {
|
|
30
|
+
it('detectProviderType should correctly identify providers', () => {
|
|
31
|
+
// Mock Ethereum provider
|
|
32
|
+
const ethProvider = {
|
|
33
|
+
request: () => {},
|
|
34
|
+
isMetaMask: true
|
|
35
|
+
};
|
|
36
|
+
expect(detectProviderType(ethProvider)).to.equal(EthereumProvider);
|
|
37
|
+
|
|
38
|
+
// Mock Solana provider (like Phantom)
|
|
39
|
+
const solanaProvider = {
|
|
40
|
+
isPhantom: true,
|
|
41
|
+
connect: () => {},
|
|
42
|
+
disconnect: () => {},
|
|
43
|
+
signTransaction: () => {},
|
|
44
|
+
signAllTransactions: () => {},
|
|
45
|
+
signMessage: () => {}
|
|
46
|
+
};
|
|
47
|
+
const solanaDetected = detectProviderType(solanaProvider);
|
|
48
|
+
expect(solanaDetected).to.equal(SolanaProvider);
|
|
49
|
+
|
|
50
|
+
// // Real Solana Connection object
|
|
51
|
+
// const solanaConnection = {
|
|
52
|
+
// _commitment: 'confirmed',
|
|
53
|
+
// _rpcEndpoint: 'http://localhost:8899',
|
|
54
|
+
// getAccountInfo: () => {},
|
|
55
|
+
// getRecentBlockhash: () => {}
|
|
56
|
+
// };
|
|
57
|
+
// expect(detectProviderType(solanaConnection)).to.equal(SolanaProvider);
|
|
58
|
+
|
|
59
|
+
// Unknown provider
|
|
60
|
+
const unknownProvider = { someRandomProperty: true };
|
|
61
|
+
expect(detectProviderType(unknownProvider)).to.equal(OtherProvider);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('isProviderType should correctly check provider types', () => {
|
|
65
|
+
const mockEthProvider = { request: () => {}, eth: {} };
|
|
66
|
+
// Use a more explicit mock, ensuring properties are clearly defined
|
|
67
|
+
const mockSolProvider = {
|
|
68
|
+
publicKey: { toString: () => 'somePublicKey' }, // Ensure publicKey is truthy and somewhat realistic
|
|
69
|
+
signTransaction: () => Promise.resolve({}),
|
|
70
|
+
isConnected: () => Promise.resolve(true), // Add potentially missing base properties if needed
|
|
71
|
+
type: 'solana' // Explicitly add type to mock if needed, though detect shouldn't rely on it
|
|
72
|
+
};
|
|
73
|
+
const mockOtherProvider = { someProp: true };
|
|
74
|
+
|
|
75
|
+
const detectedSolType = detectProviderType(mockSolProvider);
|
|
76
|
+
|
|
77
|
+
expect(isProviderType(mockEthProvider, EthereumProvider)).to.be.true; // Keep generic type for JS
|
|
78
|
+
expect(isProviderType(mockEthProvider, SolanaProvider)).to.be.false;
|
|
79
|
+
expect(isProviderType(mockSolProvider, SolanaProvider)).to.be.true; // This is the line that likely failed
|
|
80
|
+
expect(isProviderType(mockSolProvider, EthereumProvider)).to.be.false;
|
|
81
|
+
expect(isProviderType(mockOtherProvider, EthereumProvider)).to.be.false;
|
|
82
|
+
expect(isProviderType(mockOtherProvider, SolanaProvider)).to.be.false;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('Web3ProviderAdapter', () => {
|
|
87
|
+
let mockWeb3;
|
|
88
|
+
let adapter;
|
|
89
|
+
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
// Create a more detailed mock Web3 instance
|
|
92
|
+
mockWeb3 = {
|
|
93
|
+
eth: {
|
|
94
|
+
getAccounts: sinon.stub(),
|
|
95
|
+
getChainId: sinon.stub(),
|
|
96
|
+
getBalance: sinon.stub(),
|
|
97
|
+
personal: {
|
|
98
|
+
sign: sinon.stub(),
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
// Add other web3 properties if needed by the adapter
|
|
102
|
+
};
|
|
103
|
+
adapter = new Web3ProviderAdapter(mockWeb3);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should initialize correctly', () => {
|
|
107
|
+
expect(adapter.type).to.equal(EthereumProvider);
|
|
108
|
+
expect(adapter.getRawWeb3()).to.equal(mockWeb3);
|
|
109
|
+
expect(adapter.eth).to.equal(mockWeb3.eth);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('request should call correct web3.eth methods', async () => {
|
|
113
|
+
mockWeb3.eth.getAccounts.resolves(['0x123']);
|
|
114
|
+
await adapter.request({ method: 'eth_accounts' });
|
|
115
|
+
expect(mockWeb3.eth.getAccounts.calledOnce).to.be.true;
|
|
116
|
+
|
|
117
|
+
mockWeb3.eth.getChainId.resolves('1');
|
|
118
|
+
await adapter.request({ method: 'eth_chainId' });
|
|
119
|
+
expect(mockWeb3.eth.getChainId.calledOnce).to.be.true;
|
|
120
|
+
|
|
121
|
+
mockWeb3.eth.getBalance.resolves('1000');
|
|
122
|
+
await adapter.request({ method: 'eth_getBalance', params: ['0x123'] });
|
|
123
|
+
expect(mockWeb3.eth.getBalance.calledOnceWith('0x123')).to.be.true;
|
|
124
|
+
|
|
125
|
+
mockWeb3.eth.personal.sign.resolves('0xSignature');
|
|
126
|
+
await adapter.request({ method: 'personal_sign', params: ['message', '0x123', 'password'] });
|
|
127
|
+
expect(mockWeb3.eth.personal.sign.calledOnceWith('message', '0x123', 'password')).to.be.true;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('request should throw for unimplemented methods', async () => {
|
|
131
|
+
try {
|
|
132
|
+
await adapter.request({ method: 'unimplemented_method' });
|
|
133
|
+
throw new Error('Should have thrown');
|
|
134
|
+
} catch (error) {
|
|
135
|
+
expect(error.message).to.contain('not implemented');
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('isConnected should return true if accounts exist', async () => {
|
|
140
|
+
mockWeb3.eth.getAccounts.resolves(['0x123']);
|
|
141
|
+
expect(await adapter.isConnected()).to.be.true;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('isConnected should return false if no accounts exist', async () => {
|
|
145
|
+
mockWeb3.eth.getAccounts.resolves([]);
|
|
146
|
+
expect(await adapter.isConnected()).to.be.false;
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('isConnected should return false on error', async () => {
|
|
150
|
+
mockWeb3.eth.getAccounts.rejects(new Error('Connection failed'));
|
|
151
|
+
expect(await adapter.isConnected()).to.be.false;
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('SDK Provider Management (index.ts)', () => {
|
|
156
|
+
const mockEthProvider = { type: EthereumProvider, request: async () => {}, isConnected: async () => true };
|
|
157
|
+
const mockSolProvider = { type: SolanaProvider, connect: async () => ({publicKey: 'solKey'}), disconnect: async () => {}, isConnected: async () => true };
|
|
158
|
+
const mockBtcProvider = { type: BitcoinProvider, isConnected: async () => true };
|
|
159
|
+
|
|
160
|
+
it('registerProvider should store providers', () => {
|
|
161
|
+
sdk.registerProvider(EthereumProvider, mockEthProvider);
|
|
162
|
+
sdk.registerProvider(SolanaProvider, mockSolProvider);
|
|
163
|
+
expect(sdk.hasProvider(EthereumProvider)).to.be.true;
|
|
164
|
+
expect(sdk.hasProvider(SolanaProvider)).to.be.true;
|
|
165
|
+
expect(sdk.hasProvider(BitcoinProvider)).to.be.false;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('getProvider should retrieve registered providers', () => {
|
|
169
|
+
sdk.registerProvider(EthereumProvider, mockEthProvider);
|
|
170
|
+
expect(sdk.getProvider(EthereumProvider)).to.equal(mockEthProvider);
|
|
171
|
+
expect(sdk.getProvider(SolanaProvider)).to.be.undefined;
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('getOrDetectProvider should return registered provider first', async () => {
|
|
175
|
+
sdk.registerProvider(EthereumProvider, mockEthProvider);
|
|
176
|
+
const provider = await sdk.getOrDetectProvider(EthereumProvider);
|
|
177
|
+
expect(provider).to.equal(mockEthProvider);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Detection tests would require mocking 'window' and are more complex for unit tests
|
|
181
|
+
// it('getOrDetectProvider should detect window.ethereum if not registered', async () => { ... });
|
|
182
|
+
|
|
183
|
+
it('getOrDetectProvider should throw if no provider is registered or detected', async () => {
|
|
184
|
+
// Ensure no provider is registered and window is clean (or mocked as empty)
|
|
185
|
+
try {
|
|
186
|
+
await sdk.getOrDetectProvider(EthereumProvider);
|
|
187
|
+
throw new Error('Should have thrown');
|
|
188
|
+
} catch (error) {
|
|
189
|
+
expect(error.message).to.contain('No provider available');
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('loadWeb3 should return raw web3 from Web3ProviderAdapter', async () => {
|
|
194
|
+
const mockWeb3Instance = { eth: { /* ... */ } }; // Simple mock
|
|
195
|
+
const adapter = new Web3ProviderAdapter(mockWeb3Instance);
|
|
196
|
+
sdk.registerProvider(EthereumProvider, adapter);
|
|
197
|
+
|
|
198
|
+
const web3 = await sdk.loadWeb3();
|
|
199
|
+
expect(web3).to.equal(mockWeb3Instance);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('loadWeb3 should return the provider object if not an adapter', async () => {
|
|
203
|
+
const consoleWarnStub = sinon.stub(console, 'warn');
|
|
204
|
+
sdk.registerProvider(EthereumProvider, mockEthProvider); // Register a non-adapter provider
|
|
205
|
+
|
|
206
|
+
const provider = await sdk.loadWeb3();
|
|
207
|
+
expect(provider).to.equal(mockEthProvider);
|
|
208
|
+
expect(consoleWarnStub.calledOnce).to.be.true;
|
|
209
|
+
expect(consoleWarnStub.firstCall.args[0]).to.contain('not a Web3 instance');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('loadWeb3 should return undefined and log error if no provider found', async () => {
|
|
213
|
+
const consoleErrorStub = sinon.stub(console, 'error');
|
|
214
|
+
// Ensure no provider is registered
|
|
215
|
+
const web3 = await sdk.loadWeb3();
|
|
216
|
+
expect(web3).to.be.undefined;
|
|
217
|
+
expect(consoleErrorStub.called).to.be.true; // Could be called by getOrDetectProvider throwing too
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe('Solana Provider Integration', () => {
|
|
222
|
+
it('should detect Solana providers correctly', () => {
|
|
223
|
+
// Test with Phantom-like provider
|
|
224
|
+
const phantomProvider = {
|
|
225
|
+
isPhantom: true,
|
|
226
|
+
publicKey: new PublicKey('11111111111111111111111111111111'),
|
|
227
|
+
connect: () => {},
|
|
228
|
+
disconnect: () => {},
|
|
229
|
+
signTransaction: () => {},
|
|
230
|
+
signAllTransactions: () => {},
|
|
231
|
+
signMessage: () => {}
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
expect(detectProviderType(phantomProvider)).to.equal(SolanaProvider);
|
|
235
|
+
expect(isProviderType(phantomProvider, SolanaProvider)).to.be.true;
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should integrate with EmblemVaultSolanaWalletClient', () => {
|
|
239
|
+
// Create a Solana wallet client
|
|
240
|
+
const solanaWalletClient = sdk.createSolanaWalletClient({
|
|
241
|
+
walletId: 'test-solana-wallet-001'
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Our client should have the type property set
|
|
245
|
+
solanaWalletClient.type = 'emblemVaultSolanaWalletClient';
|
|
246
|
+
|
|
247
|
+
// Register it as a provider
|
|
248
|
+
sdk.registerProvider(SolanaProvider, solanaWalletClient);
|
|
249
|
+
|
|
250
|
+
// Verify it's registered correctly
|
|
251
|
+
expect(sdk.getProvider(SolanaProvider)).to.equal(solanaWalletClient);
|
|
252
|
+
|
|
253
|
+
// Verify it's detected as a Solana provider
|
|
254
|
+
expect(isProviderType(solanaWalletClient, SolanaProvider)).to.be.true;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('should detect Solana Connection objects', () => {
|
|
258
|
+
// Use a mock Connection with the properties our detection logic is looking for
|
|
259
|
+
const mockConnection = {
|
|
260
|
+
_commitment: 'confirmed',
|
|
261
|
+
_rpcEndpoint: 'http://localhost:8899',
|
|
262
|
+
getAccountInfo: () => {},
|
|
263
|
+
getRecentBlockhash: () => {},
|
|
264
|
+
isPhantom: true
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// Verify it's detected as a Solana provider
|
|
268
|
+
expect(detectProviderType(mockConnection)).to.equal(SolanaProvider);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should prioritize registered providers over detection', async () => {
|
|
272
|
+
// Create and register a Solana wallet client
|
|
273
|
+
const solanaWalletClient = sdk.createSolanaWalletClient({
|
|
274
|
+
walletId: 'test-solana-wallet-002'
|
|
275
|
+
});
|
|
276
|
+
sdk.registerProvider(SolanaProvider, solanaWalletClient);
|
|
277
|
+
|
|
278
|
+
// Create a Phantom-like provider that would be detected
|
|
279
|
+
const phantomProvider = {
|
|
280
|
+
isPhantom: true,
|
|
281
|
+
publicKey: new PublicKey('11111111111111111111111111111111')
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// getOrDetectProvider should return the registered provider
|
|
285
|
+
const provider = await sdk.getOrDetectProvider(SolanaProvider);
|
|
286
|
+
expect(provider).to.equal(solanaWalletClient);
|
|
287
|
+
expect(provider).to.not.equal(phantomProvider);
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const { expect } = require('chai');
|
|
2
|
+
const { ResourceMonitor } = require('../utils/ResourceMonitor');
|
|
3
|
+
|
|
4
|
+
describe('ResourceMonitor', () => {
|
|
5
|
+
let monitor;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
// Create a new monitor for each test
|
|
9
|
+
monitor = new ResourceMonitor(10, false);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should correctly measure memory usage', () => {
|
|
13
|
+
const memoryUsage = monitor.getMemoryUsage();
|
|
14
|
+
expect(memoryUsage).to.be.a('number');
|
|
15
|
+
expect(memoryUsage).to.be.greaterThan(0);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should take snapshots and store them', () => {
|
|
19
|
+
const label = 'initial';
|
|
20
|
+
const memoryUsage = monitor.takeSnapshot(label);
|
|
21
|
+
|
|
22
|
+
expect(memoryUsage).to.be.a('number');
|
|
23
|
+
expect(monitor.getSnapshots()).to.have.lengthOf(1);
|
|
24
|
+
expect(monitor.getSnapshots()[0].label).to.equal(label);
|
|
25
|
+
expect(monitor.getSnapshots()[0].memoryUsage).to.equal(memoryUsage);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should calculate differences between snapshots', () => {
|
|
29
|
+
// Create a large array to cause memory allocation
|
|
30
|
+
monitor.takeSnapshot('before-allocation');
|
|
31
|
+
|
|
32
|
+
// Allocate memory
|
|
33
|
+
const largeArray = new Array(1000000).fill('test');
|
|
34
|
+
|
|
35
|
+
monitor.takeSnapshot('after-allocation');
|
|
36
|
+
|
|
37
|
+
// Get difference
|
|
38
|
+
const diff = monitor.getDifferenceBetweenSnapshots('before-allocation', 'after-allocation');
|
|
39
|
+
|
|
40
|
+
expect(diff).to.be.a('number');
|
|
41
|
+
// Memory should have increased due to the large array allocation
|
|
42
|
+
expect(diff).to.be.greaterThan(0);
|
|
43
|
+
|
|
44
|
+
// Clean up to avoid affecting other tests
|
|
45
|
+
largeArray.length = 0;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should detect when memory usage exceeds threshold', () => {
|
|
49
|
+
// Set a very low threshold for testing
|
|
50
|
+
monitor = new ResourceMonitor(0.1, false);
|
|
51
|
+
|
|
52
|
+
monitor.takeSnapshot('baseline');
|
|
53
|
+
|
|
54
|
+
// Allocate significant memory
|
|
55
|
+
const largeArray = new Array(5000000).fill('test');
|
|
56
|
+
|
|
57
|
+
const thresholdCheck = monitor.checkThreshold('baseline');
|
|
58
|
+
|
|
59
|
+
expect(thresholdCheck.exceeded).to.be.true;
|
|
60
|
+
expect(thresholdCheck.percentageIncrease).to.be.a('number');
|
|
61
|
+
expect(thresholdCheck.percentageIncrease).to.be.greaterThan(0.1);
|
|
62
|
+
|
|
63
|
+
// Clean up
|
|
64
|
+
largeArray.length = 0;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should reset snapshots correctly', () => {
|
|
68
|
+
monitor.takeSnapshot('snapshot1');
|
|
69
|
+
monitor.takeSnapshot('snapshot2');
|
|
70
|
+
|
|
71
|
+
expect(monitor.getSnapshots()).to.have.lengthOf(2);
|
|
72
|
+
|
|
73
|
+
monitor.reset();
|
|
74
|
+
|
|
75
|
+
expect(monitor.getSnapshots()).to.have.lengthOf(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should generate a readable report', () => {
|
|
79
|
+
monitor.takeSnapshot('start');
|
|
80
|
+
|
|
81
|
+
// Allocate some memory
|
|
82
|
+
const array = new Array(100000).fill('test');
|
|
83
|
+
|
|
84
|
+
monitor.takeSnapshot('middle');
|
|
85
|
+
|
|
86
|
+
// Allocate more memory
|
|
87
|
+
const array2 = new Array(100000).fill('test');
|
|
88
|
+
|
|
89
|
+
monitor.takeSnapshot('end');
|
|
90
|
+
|
|
91
|
+
const report = monitor.generateReport();
|
|
92
|
+
|
|
93
|
+
expect(report).to.be.a('string');
|
|
94
|
+
expect(report).to.include('Memory Usage Report');
|
|
95
|
+
expect(report).to.include('start');
|
|
96
|
+
expect(report).to.include('middle');
|
|
97
|
+
expect(report).to.include('end');
|
|
98
|
+
|
|
99
|
+
// Clean up
|
|
100
|
+
array.length = 0;
|
|
101
|
+
array2.length = 0;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should attempt to trigger garbage collection', () => {
|
|
105
|
+
const result = monitor.triggerGC();
|
|
106
|
+
|
|
107
|
+
// This will be true only if the test is run with --expose-gc
|
|
108
|
+
// We don't assert on the result since it depends on how the test is run
|
|
109
|
+
expect(result).to.be.a('boolean');
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
const { expect } = require('chai');
|
|
2
|
+
const { EmblemVaultSDK } = require('../../dist');
|
|
3
|
+
const { ResourceMonitor } = require('../utils/ResourceMonitor');
|
|
4
|
+
|
|
5
|
+
describe('SDK Memory Leak Tests', () => {
|
|
6
|
+
// Configure test parameters
|
|
7
|
+
const ITERATIONS = 10; // Reduced from 20 to make tests run faster
|
|
8
|
+
const MEMORY_THRESHOLD = 25; // Increased from 15% to 25% to accommodate normal fluctuations
|
|
9
|
+
const API_KEY = process.env.API_KEY || 'DEMO_KEY';
|
|
10
|
+
|
|
11
|
+
let monitor;
|
|
12
|
+
let sdk = new EmblemVaultSDK(API_KEY);
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
// Create a new monitor for each test
|
|
16
|
+
monitor = new ResourceMonitor(MEMORY_THRESHOLD, false);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
// Generate and log memory report after each test
|
|
21
|
+
const report = monitor.generateReport();
|
|
22
|
+
// console.log(report);
|
|
23
|
+
|
|
24
|
+
// Attempt garbage collection between tests
|
|
25
|
+
monitor.triggerGC();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Helper function to run repeated operations and monitor memory
|
|
30
|
+
* @param {Function} operation - The SDK operation to test
|
|
31
|
+
* @param {string} operationName - Name of the operation for reporting
|
|
32
|
+
*/
|
|
33
|
+
async function testOperation(operation, operationName) {
|
|
34
|
+
// Take initial snapshot
|
|
35
|
+
monitor.takeSnapshot(`${operationName}-start`);
|
|
36
|
+
|
|
37
|
+
// Run multiple iterations of the operation
|
|
38
|
+
for (let i = 0; i < ITERATIONS; i++) {
|
|
39
|
+
// Take snapshot before operation
|
|
40
|
+
monitor.takeSnapshot(`${operationName}-iter-${i+1}-before`);
|
|
41
|
+
|
|
42
|
+
// Run the operation
|
|
43
|
+
await operation();
|
|
44
|
+
|
|
45
|
+
// Take snapshot after operation
|
|
46
|
+
monitor.takeSnapshot(`${operationName}-iter-${i+1}-after`);
|
|
47
|
+
|
|
48
|
+
// Force garbage collection if available
|
|
49
|
+
monitor.triggerGC();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Take final snapshot
|
|
53
|
+
monitor.takeSnapshot(`${operationName}-end`);
|
|
54
|
+
|
|
55
|
+
// Check if memory usage exceeds threshold
|
|
56
|
+
const thresholdCheck = monitor.checkThreshold(`${operationName}-start`);
|
|
57
|
+
|
|
58
|
+
// We don't want to fail the test, but we log a warning if threshold is exceeded
|
|
59
|
+
if (thresholdCheck.exceeded) {
|
|
60
|
+
console.warn(`WARNING: Memory usage for ${operationName} increased by ${thresholdCheck.percentageIncrease}%, which exceeds the threshold of ${MEMORY_THRESHOLD}%`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Calculate memory difference
|
|
64
|
+
const memoryDiff = monitor.getDifferenceBetweenSnapshots(`${operationName}-start`, `${operationName}-end`);
|
|
65
|
+
|
|
66
|
+
// Log memory difference
|
|
67
|
+
console.log(`Memory difference for ${operationName}: ${memoryDiff} MB`);
|
|
68
|
+
|
|
69
|
+
// Return the memory difference for assertions
|
|
70
|
+
return {
|
|
71
|
+
memoryDiff,
|
|
72
|
+
thresholdExceeded: thresholdCheck.exceeded,
|
|
73
|
+
percentageIncrease: thresholdCheck.percentageIncrease
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Create more complete mock data for tests
|
|
78
|
+
const mockAssetMetadata = [
|
|
79
|
+
{
|
|
80
|
+
projectName: 'Bitcoin Ordinals',
|
|
81
|
+
description: 'Test description',
|
|
82
|
+
image: 'https://example.com/image.png',
|
|
83
|
+
properties: {
|
|
84
|
+
test: 'value'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const mockCuratedContract = {
|
|
90
|
+
name: 'Ethscription',
|
|
91
|
+
mintable: true,
|
|
92
|
+
allowed: () => true,
|
|
93
|
+
template: {
|
|
94
|
+
name: 'Test Template',
|
|
95
|
+
description: 'Test Description',
|
|
96
|
+
image: 'https://example.com/image.png'
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Tests with proper mock data
|
|
101
|
+
it('should not leak memory with getAssetMetadata using override function', async function() {
|
|
102
|
+
this.timeout(30000);
|
|
103
|
+
|
|
104
|
+
const result = await testOperation(
|
|
105
|
+
async () => await sdk.getAssetMetadata('Bitcoin Ordinals', false, () => mockAssetMetadata),
|
|
106
|
+
'getAssetMetadata-override'
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// We don't assert on the exact memory difference, as it can vary,
|
|
110
|
+
// but we log if it exceeds the threshold
|
|
111
|
+
// console.log(`Memory increase percentage: ${result.percentageIncrease}%`);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should not leak memory with getAllAssetMetadata', async function() {
|
|
115
|
+
this.timeout(30000);
|
|
116
|
+
|
|
117
|
+
const result = await testOperation(
|
|
118
|
+
async () => sdk.getAllAssetMetadata(),
|
|
119
|
+
'getAllAssetMetadata'
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// console.log(`Memory increase percentage: ${result.percentageIncrease}%`);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should not leak memory with fetchCuratedContractByName using override function', async function() {
|
|
126
|
+
this.timeout(30000);
|
|
127
|
+
|
|
128
|
+
const result = await testOperation(
|
|
129
|
+
async () => await sdk.fetchCuratedContractByName('Ethscription', false, () => [mockCuratedContract]),
|
|
130
|
+
'fetchCuratedContractByName-override'
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// console.log(`Memory increase percentage: ${result.percentageIncrease}%`);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should not leak memory with getRemoteAssetMetadata using override function', async function() {
|
|
137
|
+
this.timeout(30000);
|
|
138
|
+
|
|
139
|
+
const result = await testOperation(
|
|
140
|
+
async () => await sdk.getInventoryAssetMetadata('Bitcoin Ordinals', () => mockAssetMetadata),
|
|
141
|
+
'getRemoteAssetMetadata-override'
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// console.log(`Memory increase percentage: ${result.percentageIncrease}%`);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Test with a deliberately leaky override function
|
|
148
|
+
it('should detect memory leaks with leaky override function', async function() {
|
|
149
|
+
this.timeout(30000);
|
|
150
|
+
|
|
151
|
+
// Create a closure with a leaky array that grows on each call
|
|
152
|
+
let leakyArray = [];
|
|
153
|
+
|
|
154
|
+
// This override function will cause a memory leak
|
|
155
|
+
const leakyOverride = () => {
|
|
156
|
+
// Create a larger object and add it to the leaky array
|
|
157
|
+
const largeObject = new Array(500000).fill('memory leak test data');
|
|
158
|
+
leakyArray.push(largeObject);
|
|
159
|
+
|
|
160
|
+
// Return mock data
|
|
161
|
+
return mockAssetMetadata;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const result = await testOperation(
|
|
165
|
+
async () => await sdk.getInventoryAssetMetadataProject('Bitcoin Ordinals', leakyOverride),
|
|
166
|
+
'leaky-override'
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
// console.log(`Memory increase percentage: ${result.percentageIncrease}%`);
|
|
170
|
+
|
|
171
|
+
// This test should show a significant memory increase
|
|
172
|
+
// We expect the threshold to be exceeded
|
|
173
|
+
expect(result.percentageIncrease).to.be.greaterThan(2);
|
|
174
|
+
// console.log('✓ Successfully detected memory leak in leaky override function');
|
|
175
|
+
});
|
|
176
|
+
});
|