Skip to content

Commit d0ffbcc

Browse files
authored
feat: logger, config, deprecate legacy tx (#1302)
* fix: global dir with logger and config, moved constants to global dir * feat: config and logger instances * feat: implement logger, warn old tx usage
1 parent 482682d commit d0ffbcc

39 files changed

+620
-45
lines changed

__tests__/rpcProvider.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
stark,
1717
waitForTransactionOptions,
1818
} from '../src';
19-
import { StarknetChainId } from '../src/constants';
19+
import { StarknetChainId } from '../src/global/constants';
2020
import { felt, uint256 } from '../src/utils/calldata/cairo';
2121
import { toBigInt, toHexString } from '../src/utils/num';
2222
import {

__tests__/utils/config.test.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { constants, config } from '../../src';
2+
3+
describe('Configuration', () => {
4+
// Reset the configuration before each test to avoid side effects
5+
beforeEach(() => {
6+
config.reset();
7+
});
8+
9+
describe('Initial Configuration', () => {
10+
it('should initialize with default values', () => {
11+
expect(config.get('legacyMode')).toBe(constants.DEFAULT_GLOBAL_CONFIG.legacyMode);
12+
expect(config.get('logLevel')).toBe(constants.DEFAULT_GLOBAL_CONFIG.logLevel);
13+
});
14+
});
15+
16+
describe('get()', () => {
17+
it('should retrieve the value of an existing key', () => {
18+
expect(config.get('logLevel')).toBe(constants.DEFAULT_GLOBAL_CONFIG.logLevel);
19+
});
20+
21+
it('should return the default value for a non-existent key', () => {
22+
expect(config.get('nonExistentKey', 'default')).toBe('default');
23+
});
24+
25+
it('should return undefined for a non-existent key without a default', () => {
26+
expect(config.get('nonExistentKey')).toBeUndefined();
27+
});
28+
});
29+
30+
describe('set()', () => {
31+
it('should update the value of an existing key', () => {
32+
config.set('logLevel', 'DEBUG');
33+
expect(config.get('logLevel')).toBe('DEBUG');
34+
});
35+
36+
it('should add a new key-value pair', () => {
37+
config.set('newKey', 'value');
38+
expect(config.get('newKey')).toBe('value');
39+
});
40+
});
41+
42+
describe('update()', () => {
43+
it('should merge provided configuration with existing values', () => {
44+
config.update({ legacyMode: true, newKey: 'value' });
45+
expect(config.get('legacyMode')).toBe(true);
46+
expect(config.get('newKey')).toBe('value');
47+
expect(config.get('logLevel')).toBe('INFO'); // Existing key remains unchanged
48+
});
49+
});
50+
51+
describe('getAll()', () => {
52+
it('should return a copy of the configuration', () => {
53+
const all = config.getAll();
54+
all.legacyMode = true; // Modify the copy
55+
expect(config.get('legacyMode')).toBe(false); // Original remains unaffected
56+
});
57+
});
58+
59+
describe('reset()', () => {
60+
it('should restore the configuration to initial defaults', () => {
61+
config.set('logLevel', 'ERROR');
62+
config.reset();
63+
expect(config.get('logLevel')).toBe('INFO');
64+
});
65+
});
66+
67+
describe('delete()', () => {
68+
it('should remove a key from the configuration', () => {
69+
config.set('newKey', 'value');
70+
config.delete('newKey');
71+
expect(config.hasKey('newKey')).toBe(false);
72+
});
73+
74+
it('should do nothing if the key does not exist', () => {
75+
config.delete('nonExistentKey');
76+
expect(config.hasKey('nonExistentKey')).toBe(false);
77+
});
78+
});
79+
80+
describe('hasKey()', () => {
81+
it('should return true for existing keys', () => {
82+
expect(config.hasKey('logLevel')).toBe(true);
83+
});
84+
85+
it('should return false for non-existent keys', () => {
86+
expect(config.hasKey('nonExistentKey')).toBe(false);
87+
});
88+
});
89+
90+
describe('Edge Cases', () => {
91+
it('should handle undefined values with default in get()', () => {
92+
config.set('someKey', undefined);
93+
expect(config.get('someKey', 'DEFAULT')).toBe('DEFAULT');
94+
});
95+
96+
it('should treat keys as case-sensitive', () => {
97+
config.set('LogLevel', 'DEBUG');
98+
expect(config.hasKey('LogLevel')).toBe(true);
99+
expect(config.hasKey('logLevel')).toBe(true); // Original key still exists
100+
});
101+
});
102+
});

__tests__/utils/ellipticalCurve.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { constants, ec } from '../../src';
2-
import { StarknetChainId } from '../../src/constants';
2+
import { StarknetChainId } from '../../src/global/constants';
33
import { computeHashOnElements } from '../../src/utils/hash';
44
import { calculateTransactionHash } from '../../src/utils/hash/transactionHash/v2';
55
import { fromCallsToExecuteCalldataWithNonce } from '../../src/utils/transaction';

__tests__/utils/logger.test.ts

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/* eslint-disable no-console */
2+
import { logger, LogLevel } from '../../src';
3+
4+
// Mock the config module
5+
const mockConfigStore: { logLevel: LogLevel; [key: string]: any } = {
6+
logLevel: 'INFO',
7+
};
8+
9+
jest.mock('../../src/global/config', () => ({
10+
config: {
11+
get: jest.fn().mockImplementation((key: string, defaultValue?: any) => {
12+
return mockConfigStore[key] ?? defaultValue;
13+
}),
14+
set: jest.fn().mockImplementation((key: string, value: any) => {
15+
mockConfigStore[key] = value;
16+
}),
17+
},
18+
}));
19+
20+
// Mock console methods
21+
const mockConsole = {
22+
debug: jest.fn(),
23+
info: jest.fn(),
24+
warn: jest.fn(),
25+
error: jest.fn(),
26+
log: jest.fn(),
27+
};
28+
29+
global.console = mockConsole as any;
30+
31+
describe('Logger', () => {
32+
// const gLog = jest.spyOn(global.console, 'log');
33+
const gInfo = jest.spyOn(global.console, 'info');
34+
const gDebug = jest.spyOn(global.console, 'debug');
35+
const gWarn = jest.spyOn(global.console, 'warn');
36+
const gError = jest.spyOn(global.console, 'error');
37+
38+
beforeEach(() => {
39+
// Reset mock config and console calls
40+
mockConfigStore.logLevel = 'INFO';
41+
jest.clearAllMocks();
42+
jest.useFakeTimers();
43+
jest.setSystemTime(new Date('2024-01-01T00:00:00Z'));
44+
});
45+
46+
afterEach(() => {
47+
jest.useRealTimers();
48+
});
49+
50+
describe('Log Level Configuration', () => {
51+
it('should have config log level', () => {
52+
expect(logger.getLogLevel()).toBe('INFO');
53+
});
54+
55+
it('should set and get log level OFF', () => {
56+
logger.setLogLevel('OFF');
57+
expect(logger.getLogLevel()).toBe('OFF');
58+
expect(logger.getEnabledLogLevels()).toStrictEqual([]);
59+
});
60+
61+
it('should set and get log level FATAL', () => {
62+
logger.setLogLevel('FATAL');
63+
expect(logger.getLogLevel()).toBe('FATAL');
64+
expect(logger.getEnabledLogLevels()).toStrictEqual(['FATAL']);
65+
});
66+
67+
it('should set and get log level ERROR', () => {
68+
logger.setLogLevel('ERROR');
69+
expect(logger.getLogLevel()).toBe('ERROR');
70+
expect(logger.getEnabledLogLevels()).toStrictEqual(['ERROR', 'FATAL']);
71+
});
72+
73+
it('should set and get log level WARN', () => {
74+
logger.setLogLevel('WARN');
75+
expect(logger.getLogLevel()).toBe('WARN');
76+
expect(logger.getEnabledLogLevels()).toStrictEqual(['WARN', 'ERROR', 'FATAL']);
77+
});
78+
79+
it('should set and get log level INFO', () => {
80+
logger.setLogLevel('INFO');
81+
expect(logger.getLogLevel()).toBe('INFO');
82+
expect(logger.getEnabledLogLevels()).toStrictEqual(['INFO', 'WARN', 'ERROR', 'FATAL']);
83+
});
84+
85+
it('should set and get log level DEBUG', () => {
86+
logger.setLogLevel('DEBUG');
87+
expect(logger.getLogLevel()).toBe('DEBUG');
88+
expect(logger.getEnabledLogLevels()).toStrictEqual([
89+
'DEBUG',
90+
'INFO',
91+
'WARN',
92+
'ERROR',
93+
'FATAL',
94+
]);
95+
});
96+
});
97+
98+
describe('Log Filtering', () => {
99+
it('should log messages at or above current level', () => {
100+
logger.setLogLevel('WARN');
101+
102+
logger.debug('Debug message');
103+
logger.warn('Warning message');
104+
105+
expect(gDebug).not.toHaveBeenCalled();
106+
expect(gWarn).toHaveBeenCalled();
107+
});
108+
109+
it('should not log when level is OFF', () => {
110+
logger.setLogLevel('OFF');
111+
112+
logger.error('Error message');
113+
logger.fatal('Fatal message');
114+
115+
expect(gError).not.toHaveBeenCalled();
116+
expect(gError).not.toHaveBeenCalled();
117+
});
118+
});
119+
120+
describe('Log Methods', () => {
121+
it('should format messages correctly', () => {
122+
logger.info('Test message', { key: 'value' });
123+
124+
const expectedMessage = `[2024-01-01T00:00:00.000Z] INFO: Test message\n${JSON.stringify({ key: 'value' }, null, 2)}`;
125+
expect(gInfo).toHaveBeenCalledWith(expectedMessage);
126+
});
127+
128+
it('should use appropriate console methods', () => {
129+
logger.setLogLevel('DEBUG');
130+
logger.debug('Debug');
131+
logger.info('Info');
132+
logger.warn('Warn');
133+
logger.error('Error');
134+
logger.fatal('Fatal');
135+
136+
expect(gDebug).toHaveBeenCalled();
137+
expect(gInfo).toHaveBeenCalled();
138+
expect(gWarn).toHaveBeenCalled();
139+
expect(gError).toHaveBeenCalledTimes(2);
140+
});
141+
});
142+
143+
describe('Edge Cases', () => {
144+
it('should handle empty data', () => {
145+
logger.info('Message without data');
146+
const expectedMessage = '[2024-01-01T00:00:00.000Z] INFO: Message without data';
147+
expect(gInfo).toHaveBeenCalledWith(expectedMessage);
148+
});
149+
150+
it('should handle circular data structures', () => {
151+
logger.setLogLevel('DEBUG');
152+
const circularObj: any = { a: 'test' };
153+
circularObj.myself = circularObj;
154+
155+
logger.error('Circular error', circularObj);
156+
157+
// Should handle circular references in stringification
158+
expect(gError).toHaveBeenCalled();
159+
});
160+
});
161+
});

__tests__/utils/stark.browser.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44
import { TextEncoder } from 'util';
55

6-
import * as constants from '../../src/constants';
6+
import * as constants from '../../src/global/constants';
77
import * as json from '../../src/utils/json';
88

99
const { IS_BROWSER } = constants;

__tests__/utils/starknetId.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { StarknetChainId } from '../../src/constants';
1+
import { StarknetChainId } from '../../src/global/constants';
22
import { getStarknetIdContract, useDecoded, useEncoded } from '../../src/utils/starknetId';
33

44
function randomWithSeed(seed: number) {

__tests__/utils/typedData.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
type ArraySignatureType,
1818
type Signature,
1919
} from '../../src';
20-
import { PRIME } from '../../src/constants';
20+
import { PRIME } from '../../src/global/constants';
2121
import { getSelectorFromName } from '../../src/utils/hash';
2222
import { MerkleTree } from '../../src/utils/merkle';
2323
import {

src/account/default.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
SNIP9_V2_INTERFACE_ID,
55
UDC,
66
ZERO,
7-
} from '../constants';
7+
} from '../global/constants';
88
import { Provider, ProviderInterface } from '../provider';
99
import { Signer, SignerInterface } from '../signer';
1010
import {

src/channel/rpc_0_6.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { NetworkName, StarknetChainId } from '../constants';
1+
import { NetworkName, StarknetChainId, SYSTEM_MESSAGES } from '../global/constants';
22
import { LibraryError, RpcError } from '../utils/errors';
33
import {
44
AccountInvocationItem,
@@ -30,6 +30,7 @@ import { getHexStringArray, toHex, toStorageKey } from '../utils/num';
3030
import { Block, getDefaultNodeUrl, isV3Tx, isVersion, wait } from '../utils/provider';
3131
import { decompressProgram, signatureToHexArray } from '../utils/stark';
3232
import { getVersionsByType } from '../utils/transaction';
33+
import { logger } from '../global/logger';
3334

3435
const defaultOptions = {
3536
headers: { 'Content-Type': 'application/json' },
@@ -448,6 +449,11 @@ export class RpcChannel {
448449
nonce: toHex(details.nonce),
449450
},
450451
});
452+
453+
logger.warn(SYSTEM_MESSAGES.legacyTxWarningMessage, {
454+
version: RPC.ETransactionVersion.V1,
455+
type: RPC.ETransactionType.INVOKE,
456+
});
451457
} else {
452458
// V3
453459
promise = this.fetchEndpoint('starknet_addInvokeTransaction', {
@@ -493,6 +499,11 @@ export class RpcChannel {
493499
nonce: toHex(details.nonce),
494500
},
495501
});
502+
503+
logger.warn(SYSTEM_MESSAGES.legacyTxWarningMessage, {
504+
version: RPC.ETransactionVersion.V1,
505+
type: RPC.ETransactionType.DECLARE,
506+
});
496507
} else if (isSierra(contract) && !isV3Tx(details)) {
497508
// V2 Cairo1
498509
promise = this.fetchEndpoint('starknet_addDeclareTransaction', {
@@ -512,6 +523,11 @@ export class RpcChannel {
512523
nonce: toHex(details.nonce),
513524
},
514525
});
526+
527+
logger.warn(SYSTEM_MESSAGES.legacyTxWarningMessage, {
528+
version: RPC.ETransactionVersion.V2,
529+
type: RPC.ETransactionType.DECLARE,
530+
});
515531
} else if (isSierra(contract) && isV3Tx(details)) {
516532
// V3 Cairo1
517533
promise = this.fetchEndpoint('starknet_addDeclareTransaction', {
@@ -562,6 +578,11 @@ export class RpcChannel {
562578
nonce: toHex(details.nonce),
563579
},
564580
});
581+
582+
logger.warn(SYSTEM_MESSAGES.legacyTxWarningMessage, {
583+
version: RPC.ETransactionVersion.V1,
584+
type: RPC.ETransactionType.DEPLOY_ACCOUNT,
585+
});
565586
} else {
566587
// v3
567588
promise = this.fetchEndpoint('starknet_addDeployAccountTransaction', {
@@ -650,6 +671,11 @@ export class RpcChannel {
650671
nonce: toHex(invocation.nonce),
651672
max_fee: toHex(invocation.maxFee || 0),
652673
};
674+
675+
logger.warn(SYSTEM_MESSAGES.legacyTxWarningMessage, {
676+
version: invocation.version,
677+
type: invocation.type,
678+
});
653679
} else {
654680
// V3
655681
details = {

0 commit comments

Comments
 (0)