diff --git a/src/core/schemas/common-schemas.ts b/src/core/schemas/common-schemas.ts index fa0174055..c3f972e78 100644 --- a/src/core/schemas/common-schemas.ts +++ b/src/core/schemas/common-schemas.ts @@ -930,3 +930,14 @@ export const HexEncodedDataSchema = z 'Data must be a hex encoded string starting in format "0x12abfe456123"', ) .describe('HEX encoded data format'); + +export const ResolvedAccountCredentialSchema = z.object({ + keyRefId: z.string(), + accountId: z.string(), + publicKey: z.string(), +}); + +export const ResolvedPublicKeySchema = z.object({ + keyRefId: z.string(), + publicKey: z.string(), +}); diff --git a/src/plugins/batch/manifest.ts b/src/plugins/batch/manifest.ts index 520187d21..65e659f63 100644 --- a/src/plugins/batch/manifest.ts +++ b/src/plugins/batch/manifest.ts @@ -96,6 +96,7 @@ export const batchPluginManifest: PluginManifest = { 'account-create-batch-state', 'topic-create-batch-state', 'token-create-ft-batch-state', + 'token-create-ft-from-file-batch-state', ], options: [ { diff --git a/src/plugins/token/hooks/batch-create-ft-from-file/handler.ts b/src/plugins/token/hooks/batch-create-ft-from-file/handler.ts new file mode 100644 index 000000000..25b54af47 --- /dev/null +++ b/src/plugins/token/hooks/batch-create-ft-from-file/handler.ts @@ -0,0 +1,133 @@ +import type { CommandHandlerArgs, CoreApi, Logger } from '@/core'; +import type { + HookResult, + PreOutputPreparationParams, +} from '@/core/hooks/types'; +import type { Credential } from '@/core/services/kms/kms-types.interface'; +import type { + BatchDataItem, + BatchExecuteTransactionResult, + TransactionResult, +} from '@/core/types/shared.types'; + +import { StateError } from '@/core'; +import { AbstractHook } from '@/core/hooks/abstract-hook'; +import { AliasType } from '@/core/services/alias/alias-service.interface'; +import { composeKey } from '@/core/utils/key-composer'; +import { TOKEN_CREATE_FT_FROM_FILE_COMMAND_NAME } from '@/plugins/token/commands/create-ft-from-file'; +import { processTokenAssociations } from '@/plugins/token/utils/token-associations'; +import { buildTokenDataFromFile } from '@/plugins/token/utils/token-data-builders'; +import { ZustandTokenStateHelper } from '@/plugins/token/zustand-state-helper'; + +import { CreateFtFromFileNormalizedParamsSchema } from './types'; + +export class TokenCreateFtFromFileBatchStateHook extends AbstractHook { + override async preOutputPreparationHook( + args: CommandHandlerArgs, + params: PreOutputPreparationParams< + unknown, + unknown, + unknown, + BatchExecuteTransactionResult + >, + ): Promise { + const { api, logger } = args; + const batchData = params.executeTransactionResult.updatedBatchData; + if (!batchData.success) { + return Promise.resolve({ + breakFlow: false, + result: { + message: 'Batch transaction status failure', + }, + }); + } + for (const batchDataItem of [...batchData.transactions].filter( + (item) => item.command === TOKEN_CREATE_FT_FROM_FILE_COMMAND_NAME, + )) { + await this.saveToken(api, logger, batchDataItem); + } + return Promise.resolve({ + breakFlow: false, + result: { + message: 'success', + }, + }); + } + + private async saveToken( + api: CoreApi, + logger: Logger, + batchDataItem: BatchDataItem, + ): Promise { + const parseResult = CreateFtFromFileNormalizedParamsSchema.safeParse( + batchDataItem.normalizedParams, + ); + if (!parseResult.success) { + logger.warn( + `There was a problem with parsing data schema. The saving will not be done`, + ); + return; + } + const normalisedParams = parseResult.data; + const innerTransactionId = batchDataItem.transactionId; + if (!innerTransactionId) { + logger.warn( + `No transaction ID found for batch transaction ${batchDataItem.order}`, + ); + return; + } + + const innerTransactionResult: TransactionResult = + await api.receipt.getReceipt({ + transactionId: innerTransactionId, + }); + + if (!innerTransactionResult.tokenId) { + throw new StateError( + 'Transaction completed but did not return a token ID', + { context: { transactionId: innerTransactionResult.transactionId } }, + ); + } + + const tokenData = buildTokenDataFromFile( + innerTransactionResult, + normalisedParams.tokenDefinition, + normalisedParams.treasury.accountId, + normalisedParams.adminKey.publicKey, + normalisedParams.network, + { + supplyPublicKey: normalisedParams.supplyKey?.publicKey, + wipePublicKey: normalisedParams.wipeKey?.publicKey, + kycPublicKey: normalisedParams.kycKey?.publicKey, + freezePublicKey: normalisedParams.freezeKey?.publicKey, + pausePublicKey: normalisedParams.pauseKey?.publicKey, + feeSchedulePublicKey: normalisedParams.feeScheduleKey?.publicKey, + }, + ); + + tokenData.associations = await processTokenAssociations( + innerTransactionResult.tokenId, + normalisedParams.tokenDefinition.associations as Credential[], + api, + logger, + normalisedParams.keyManager, + ); + + const key = composeKey( + normalisedParams.network, + innerTransactionResult.tokenId, + ); + const tokenState = new ZustandTokenStateHelper(api.state, logger); + tokenState.saveToken(key, tokenData); + logger.info(' Token data saved to state'); + + api.alias.register({ + alias: normalisedParams.tokenDefinition.name, + type: AliasType.Token, + network: normalisedParams.network, + entityId: innerTransactionResult.tokenId, + createdAt: innerTransactionResult.consensusTimestamp, + }); + logger.info(` Name registered: ${normalisedParams.tokenDefinition.name}`); + } +} diff --git a/src/plugins/token/hooks/batch-create-ft-from-file/index.ts b/src/plugins/token/hooks/batch-create-ft-from-file/index.ts new file mode 100644 index 000000000..e899c6934 --- /dev/null +++ b/src/plugins/token/hooks/batch-create-ft-from-file/index.ts @@ -0,0 +1 @@ +export { TokenCreateFtFromFileBatchStateHook } from './handler'; diff --git a/src/plugins/token/hooks/batch-create-ft-from-file/types.ts b/src/plugins/token/hooks/batch-create-ft-from-file/types.ts new file mode 100644 index 000000000..ab4780d35 --- /dev/null +++ b/src/plugins/token/hooks/batch-create-ft-from-file/types.ts @@ -0,0 +1,56 @@ +import { z } from 'zod'; + +import { + HtsDecimalsSchema, + KeySchema, + MemoSchema, + NetworkSchema, + PrivateKeySchema, + PrivateKeyWithAccountIdSchema, + ResolvedAccountCredentialSchema, + ResolvedPublicKeySchema, + TinybarSchema, + TokenNameSchema, + TokenSymbolSchema, + TokenTypeSchema, +} from '@/core/schemas/common-schemas'; +import { keyManagerNameSchema } from '@/core/services/kms/kms-types.interface'; +import { TokenFileCustomFeeSchema } from '@/plugins/token/schema'; + +export const FungibleTokenFileSchema = z.object({ + name: TokenNameSchema, + symbol: TokenSymbolSchema, + decimals: HtsDecimalsSchema, + supplyType: z.union([z.literal('finite'), z.literal('infinite')]), + initialSupply: TinybarSchema, + maxSupply: TinybarSchema, + treasuryKey: PrivateKeyWithAccountIdSchema, + adminKey: PrivateKeySchema, + supplyKey: KeySchema.optional(), + wipeKey: KeySchema.optional(), + kycKey: KeySchema.optional(), + freezeKey: KeySchema.optional(), + pauseKey: KeySchema.optional(), + feeScheduleKey: KeySchema.optional(), + associations: z.array(PrivateKeyWithAccountIdSchema).default([]), + customFees: z + .array(TokenFileCustomFeeSchema) + .max(10, 'Maximum 10 custom fees allowed per token') + .default([]), + memo: MemoSchema.default(''), + tokenType: TokenTypeSchema, +}); + +export const CreateFtFromFileNormalizedParamsSchema = z.object({ + keyManager: keyManagerNameSchema, + tokenDefinition: FungibleTokenFileSchema, + network: NetworkSchema, + treasury: ResolvedAccountCredentialSchema, + adminKey: ResolvedPublicKeySchema, + supplyKey: ResolvedPublicKeySchema.optional(), + wipeKey: ResolvedPublicKeySchema.optional(), + kycKey: ResolvedPublicKeySchema.optional(), + freezeKey: ResolvedPublicKeySchema.optional(), + pauseKey: ResolvedPublicKeySchema.optional(), + feeScheduleKey: ResolvedPublicKeySchema.optional(), +}); diff --git a/src/plugins/token/hooks/batch-create-ft/types.ts b/src/plugins/token/hooks/batch-create-ft/types.ts index fb64bfb00..b4274d069 100644 --- a/src/plugins/token/hooks/batch-create-ft/types.ts +++ b/src/plugins/token/hooks/batch-create-ft/types.ts @@ -2,23 +2,14 @@ import { z } from 'zod'; import { NetworkSchema, + ResolvedAccountCredentialSchema, + ResolvedPublicKeySchema, SupplyTypeSchema, TinybarSchema, } from '@/core/schemas/common-schemas'; import { keyManagerNameSchema } from '@/core/services/kms/kms-types.interface'; import { HederaTokenType } from '@/core/shared/constants'; -const ResolvedAccountCredentialSchema = z.object({ - keyRefId: z.string(), - accountId: z.string(), - publicKey: z.string(), -}); - -const ResolvedPublicKeySchema = z.object({ - keyRefId: z.string(), - publicKey: z.string(), -}); - export const CreateFtNormalizedParamsSchema = z.object({ name: z.string(), symbol: z.string(), diff --git a/src/plugins/token/manifest.ts b/src/plugins/token/manifest.ts index 10d2e2a77..fe3bae043 100644 --- a/src/plugins/token/manifest.ts +++ b/src/plugins/token/manifest.ts @@ -73,6 +73,7 @@ import { TokenViewOutputSchema, } from './commands/view'; import { TokenCreateFtBatchStateHook } from './hooks/batch-create-ft'; +import { TokenCreateFtFromFileBatchStateHook } from './hooks/batch-create-ft-from-file'; export const tokenPluginManifest: PluginManifest = { name: 'token', @@ -85,6 +86,11 @@ export const tokenPluginManifest: PluginManifest = { hook: new TokenCreateFtBatchStateHook(), options: [], }, + { + name: 'token-create-ft-from-file-batch-state', + hook: new TokenCreateFtFromFileBatchStateHook(), + options: [], + }, ], commands: [ {