Skip to content

Commit 51515aa

Browse files
committed
feat(1670): resolved merge conflicts
Signed-off-by: mmyslblocky <michal.myslinski@blockydevs.com>
2 parents 6462614 + 94cf56a commit 51515aa

File tree

51 files changed

+390
-210
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+390
-210
lines changed

docs/adr/ADR-010-batch-transaction-plugin.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ export class BatchifyHook extends AbstractHook {
360360

361361
override async preExecuteTransactionHook(
362362
args: CommandHandlerArgs,
363-
params: PreExecuteTransactionParams<..., BatchifyBuildTransactionResult, BatchifySignTransactionResult>,
363+
params: PreExecuteTransactionParams<..., BaseBuildTransactionResult, BaseSignTransactionResult>,
364364
commandName: string,
365365
): Promise<HookResult> {
366366
const batchName = BatchifyInputSchema.parse(args.args).batch;

eslint.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ module.exports = [
127127
},
128128
},
129129

130+
// Type definition files - allow empty object types (used as named aliases for base types)
131+
{
132+
files: ['**/types.ts'],
133+
rules: {
134+
'@typescript-eslint/no-empty-object-type': 'off',
135+
},
136+
},
137+
130138
// Command handlers - allow sync handlers
131139
{
132140
files: ['src/plugins/**/commands/**/handler.ts'],

src/core/errors/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ export { StateError } from './state-error';
1313
export { TransactionError } from './transaction-error';
1414
export { TransactionPrecheckError } from './transaction-precheck-error';
1515
export { ValidationError } from './validation-error';
16+
export {
17+
formatZodIssueLine,
18+
formatZodIssuesForMessage,
19+
} from '@/core/utils/format-zod-issues';

src/core/errors/validation-error.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import type { ZodError } from 'zod';
22

3+
import {
4+
formatZodIssueLine,
5+
formatZodIssuesForMessage,
6+
} from '@/core/utils/format-zod-issues';
7+
38
import { CliError } from './cli-error';
49

510
export class ValidationError extends CliError {
@@ -21,11 +26,13 @@ export class ValidationError extends CliError {
2126
}
2227

2328
static fromZod(zodError: ZodError): ValidationError {
24-
const issues = zodError.issues.map((i) => i.message);
25-
const message = `Validation failed:\n${issues.map((i) => ` - ${i}`).join('\n')}`;
29+
const issueLines = zodError.issues.map((issue) =>
30+
formatZodIssueLine(issue),
31+
);
32+
const message = `Validation failed:\n${formatZodIssuesForMessage(zodError)}`;
2633

2734
return new ValidationError(message, {
28-
context: { issues },
35+
context: { issues: issueLines },
2936
cause: zodError,
3037
});
3138
}

src/core/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ export {
6363
SupplyType,
6464
SupportedNetwork,
6565
} from './types/shared.types';
66+
export type {
67+
BaseBuildTransactionResult,
68+
BaseNormalizedParams,
69+
BaseSignTransactionResult,
70+
} from './types/transaction.types';
6671

6772
// ============================================================================
6873
// Shared Constants

src/core/schemas/__tests__/unit/common-schemas.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,6 @@ describe('ExpirationTimeSchema', () => {
142142
expect(ExpirationTimeSchema.parse(undefined)).toBeUndefined();
143143
});
144144

145-
test('accepts empty string as omitted expiration', () => {
146-
expect(ExpirationTimeSchema.parse('')).toBeUndefined();
147-
});
148-
149145
test('rejects expiration in the past', () => {
150146
expect(() =>
151147
ExpirationTimeSchema.parse('2000-01-01T00:00:00.000Z'),

src/core/services/mirrornode/__tests__/unit/hedera-mirrornode-service.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ import {
2121
createMockAccountListItemAPIResponse,
2222
createMockExchangeRateResponse,
2323
createMockGetAccountsAPIResponse,
24+
createMockMirrorNodeTokenByIdJson,
2425
createMockNftInfo,
2526
createMockTokenAirdropsResponse,
2627
createMockTokenBalancesResponse,
27-
createMockTokenInfo,
2828
createMockTopicInfo,
2929
createMockTopicMessage,
3030
createMockTopicMessagesAPIResponse,
@@ -827,10 +827,10 @@ describe('HederaMirrornodeServiceDefaultImpl', () => {
827827
describe('getTokenInfo', () => {
828828
it('should fetch token info with correct URL', async () => {
829829
const { service } = setupService();
830-
const mockTokenInfo = createMockTokenInfo();
830+
const mockJson = createMockMirrorNodeTokenByIdJson();
831831
(global.fetch as jest.Mock).mockResolvedValue({
832832
ok: true,
833-
json: jest.fn().mockResolvedValue(mockTokenInfo),
833+
json: jest.fn().mockResolvedValue(mockJson),
834834
});
835835

836836
const result = await service.getTokenInfo(TEST_TOKEN_ID);
@@ -840,6 +840,7 @@ describe('HederaMirrornodeServiceDefaultImpl', () => {
840840
);
841841
expect(result.token_id).toBe(TEST_TOKEN_ID);
842842
expect(result.symbol).toBe('TEST');
843+
expect(result.treasury_account_id).toBe('0.0.1234');
843844
});
844845

845846
it('should throw error on HTTP 404', async () => {

src/core/services/mirrornode/__tests__/unit/mocks.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ export const createMockTopicMessagesAPIResponse = (
100100
...overrides,
101101
});
102102

103+
/**
104+
* Domain `TokenInfo` after `getTokenInfo` (for mocks that bypass HTTP and return a resolved value).
105+
*/
103106
export const createMockTokenInfo = (
104107
overrides: Partial<TokenInfo> = {},
105108
): TokenInfo => ({
@@ -123,6 +126,32 @@ export const createMockTokenInfo = (
123126
...overrides,
124127
});
125128

129+
/**
130+
* Raw JSON body for GET /api/v1/tokens/{id} (Mirror Node). Use with `fetch` mocks.
131+
*/
132+
export const createMockMirrorNodeTokenByIdJson = (
133+
overrides: Record<string, unknown> = {},
134+
): Record<string, unknown> => ({
135+
token_id: '0.0.2000',
136+
symbol: 'TEST',
137+
name: 'Test Token',
138+
decimals: '6',
139+
total_supply: '1000000000',
140+
max_supply: '1000000000',
141+
type: 'NON_FUNGIBLE_UNIQUE',
142+
treasury_account_id: '0.0.1234',
143+
created_timestamp: '2024-01-01T12:00:00.000Z',
144+
deleted: false,
145+
freeze_default: false,
146+
default_kyc_status: false,
147+
auto_renew_period: 7776000,
148+
auto_renew_account: '0.0.1234',
149+
expiry_timestamp: 1893456000000000000,
150+
pause_status: 'UNPAUSED',
151+
memo: '',
152+
...overrides,
153+
});
154+
126155
export const createMockTopicInfo = (
127156
overrides: Partial<TopicInfo> = {},
128157
): TopicInfo => ({

src/core/services/mirrornode/hedera-mirrornode-service.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,17 @@ import type {
2626
TransactionDetailsResponse,
2727
} from './types';
2828

29-
import { ConfigurationError, NetworkError, NotFoundError } from '@/core/errors';
29+
import {
30+
CliError,
31+
ConfigurationError,
32+
NetworkError,
33+
NotFoundError,
34+
} from '@/core/errors';
3035
import { KeyAlgorithm } from '@/core/shared/constants';
36+
import { parseWithSchema } from '@/core/shared/validation/parse-with-schema.zod';
3137
import { handleMirrorNodeErrorResponse } from '@/core/utils/handle-mirror-node-error-response';
3238

39+
import { TokenInfoSchema } from './schema';
3340
import { NetworkToBaseUrl } from './types';
3441

3542
export class HederaMirrornodeServiceDefaultImpl implements HederaMirrornodeService {
@@ -324,10 +331,13 @@ export class HederaMirrornodeServiceDefaultImpl implements HederaMirrornodeServi
324331
);
325332
}
326333

327-
return (await response.json()) as TokenInfo;
334+
return parseWithSchema(
335+
TokenInfoSchema,
336+
await response.json(),
337+
`Mirror Node GET /tokens/${tokenId}`,
338+
);
328339
} catch (error) {
329-
if (error instanceof NotFoundError || error instanceof NetworkError)
330-
throw error;
340+
if (error instanceof CliError) throw error;
331341
throw new NetworkError(`Failed to fetch token info for ${tokenId}`, {
332342
cause: error,
333343
recoverable: true,
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type { TokenInfo } from './types';
2+
3+
import { z } from 'zod';
4+
5+
const mirrorKeyObject = z.object({
6+
_type: z.string(),
7+
key: z.string(),
8+
});
9+
10+
const optionalKeyRef = z.union([mirrorKeyObject, z.null()]).optional();
11+
12+
export const TokenInfoSchema: z.ZodType<TokenInfo> = z.object({
13+
token_id: z.string(),
14+
symbol: z.string(),
15+
name: z.string(),
16+
decimals: z.string(),
17+
total_supply: z.string(),
18+
max_supply: z.string(),
19+
type: z.string(),
20+
treasury_account_id: z.string(),
21+
admin_key: optionalKeyRef,
22+
kyc_key: optionalKeyRef,
23+
freeze_key: optionalKeyRef,
24+
wipe_key: optionalKeyRef,
25+
supply_key: optionalKeyRef,
26+
fee_schedule_key: optionalKeyRef,
27+
metadata_key: optionalKeyRef,
28+
pause_key: optionalKeyRef,
29+
created_timestamp: z.string(),
30+
deleted: z.boolean().nullable().optional(),
31+
freeze_default: z.boolean().optional(),
32+
default_kyc_status: z.boolean(),
33+
auto_renew_account: z.string(),
34+
auto_renew_period: z.number(),
35+
expiry_timestamp: z.number(),
36+
pause_status: z.string(),
37+
memo: z.string(),
38+
});

0 commit comments

Comments
 (0)