Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/plugins/batch/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export const batchPluginManifest: PluginManifest = {
'token-create-ft-batch-state',
'token-create-ft-from-file-batch-state',
'token-create-nft-batch-state',
'token-create-nft-from-file-batch-state',
],
options: [
{
Expand Down
133 changes: 133 additions & 0 deletions src/plugins/token/hooks/batch-create-nft-from-file/handler.ts
Original file line number Diff line number Diff line change
@@ -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_NFT_FROM_FILE_COMMAND_NAME } from '@/plugins/token/commands/create-nft-from-file';
import { processTokenAssociations } from '@/plugins/token/utils/token-associations';
import { buildNftTokenDataFromFile } from '@/plugins/token/utils/token-data-builders';
import { ZustandTokenStateHelper } from '@/plugins/token/zustand-state-helper';

import { CreateNftFromFileNormalizedParamsSchema } from './types';

export class TokenCreateNftFromFileBatchStateHook extends AbstractHook {
override async preOutputPreparationHook(
args: CommandHandlerArgs,
params: PreOutputPreparationParams<
unknown,
unknown,
unknown,
BatchExecuteTransactionResult
>,
): Promise<HookResult> {
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_NFT_FROM_FILE_COMMAND_NAME,
)) {
await this.saveNft(api, logger, batchDataItem);
}
return Promise.resolve({
breakFlow: false,
result: {
message: 'success',
},
});
}

private async saveNft(
api: CoreApi,
logger: Logger,
batchDataItem: BatchDataItem,
): Promise<void> {
const parseResult = CreateNftFromFileNormalizedParamsSchema.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 = buildNftTokenDataFromFile(
innerTransactionResult,
normalisedParams.tokenDefinition,
normalisedParams.treasury.accountId,
normalisedParams.adminKey.publicKey,
normalisedParams.supplyKey.publicKey,
normalisedParams.network,
{
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(' Non-fungible 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}`);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { TokenCreateNftFromFileBatchStateHook } from './handler';
75 changes: 75 additions & 0 deletions src/plugins/token/hooks/batch-create-nft-from-file/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { z } from 'zod';

import {
KeySchema,
MemoSchema,
NetworkSchema,
NonNegativeNumberOrBigintSchema,
PrivateKeySchema,
PrivateKeyWithAccountIdSchema,
ResolvedAccountCredentialSchema,
ResolvedPublicKeySchema,
TokenNameSchema,
TokenSymbolSchema,
} from '@/core/schemas/common-schemas';
import { keyManagerNameSchema } from '@/core/services/kms/kms-types.interface';

function validateFileSupplyTypeAndMaxSupply<
Args extends {
maxSupply?: number | bigint;
supplyType?: 'finite' | 'infinite';
},
>(args: Args, ctx: z.RefinementCtx) {
const isFinite = args.supplyType === 'finite';

if (isFinite && !args.maxSupply) {
ctx.addIssue({
message: 'maxSupply is required when supplyType is finite',
code: z.ZodIssueCode.custom,
path: ['maxSupply'],
});
}

if (!isFinite && args.maxSupply) {
ctx.addIssue({
message:
'maxSupply should not be provided when supplyType is infinite, set supplyType to finite to specify maxSupply',
code: z.ZodIssueCode.custom,
path: ['maxSupply'],
});
}
}

export const NonFungibleTokenFileSchema = z
.object({
name: TokenNameSchema,
symbol: TokenSymbolSchema,
supplyType: z.union([z.literal('finite'), z.literal('infinite')]),
maxSupply: NonNegativeNumberOrBigintSchema.optional(),
treasuryKey: PrivateKeyWithAccountIdSchema,
adminKey: PrivateKeySchema,
supplyKey: KeySchema,
wipeKey: KeySchema.optional(),
kycKey: KeySchema.optional(),
freezeKey: KeySchema.optional(),
pauseKey: KeySchema.optional(),
feeScheduleKey: KeySchema.optional(),
associations: z.array(KeySchema).default([]),
memo: MemoSchema.default(''),
})
.superRefine(validateFileSupplyTypeAndMaxSupply);

export const CreateNftFromFileNormalizedParamsSchema = z.object({
filename: z.string(),
keyManager: keyManagerNameSchema,
tokenDefinition: NonFungibleTokenFileSchema,
network: NetworkSchema,
treasury: ResolvedAccountCredentialSchema,
adminKey: ResolvedPublicKeySchema,
supplyKey: ResolvedPublicKeySchema,
wipeKey: ResolvedPublicKeySchema.optional(),
kycKey: ResolvedPublicKeySchema.optional(),
freezeKey: ResolvedPublicKeySchema.optional(),
pauseKey: ResolvedPublicKeySchema.optional(),
feeScheduleKey: ResolvedPublicKeySchema.optional(),
});
6 changes: 6 additions & 0 deletions src/plugins/token/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import {
import { TokenCreateFtBatchStateHook } from './hooks/batch-create-ft';
import { TokenCreateFtFromFileBatchStateHook } from './hooks/batch-create-ft-from-file';
import { TokenCreateNftBatchStateHook } from './hooks/batch-create-nft';
import { TokenCreateNftFromFileBatchStateHook } from './hooks/batch-create-nft-from-file';

export const tokenPluginManifest: PluginManifest = {
name: 'token',
Expand All @@ -97,6 +98,11 @@ export const tokenPluginManifest: PluginManifest = {
hook: new TokenCreateNftBatchStateHook(),
options: [],
},
{
name: 'token-create-nft-from-file-batch-state',
hook: new TokenCreateNftFromFileBatchStateHook(),
options: [],
},
],
commands: [
{
Expand Down
Loading