diff --git a/docs/output-schemas-guide.md b/docs/output-schemas-guide.md index 53c2a99c7..2de54a5a5 100644 --- a/docs/output-schemas-guide.md +++ b/docs/output-schemas-guide.md @@ -368,12 +368,22 @@ interface CommandOutputSpec { "supplyType": "FINITE", "transactionId": "0.0.123@1700000000.123456789", "adminAccountId": "0.0.12345", + "adminPublicKey": "302a300506032b6570032100...", "supplyAccountId": "0.0.12345", + "supplyPublicKey": "302a300506032b6570032100...", + "freezePublicKey": "302a300506032b6570032100...", + "wipePublicKey": "302a300506032b6570032100...", + "pausePublicKey": "302a300506032b6570032100...", + "kycPublicKey": "302a300506032b6570032100...", + "feeSchedulePublicKey": "302a300506032b6570032100...", + "metadataPublicKey": "302a300506032b6570032100...", "alias": "my-nft", "network": "testnet" } ``` +All key fields (`adminPublicKey`, `supplyPublicKey`, `freezePublicKey`, `wipePublicKey`, `pausePublicKey`, `kycPublicKey`, `feeSchedulePublicKey`, `metadataPublicKey`) are optional and only appear when the corresponding key was provided. + #### `token mint-ft` **Output**: diff --git a/src/core/services/token/token-service.ts b/src/core/services/token/token-service.ts index 53408b0de..b4df04f77 100644 --- a/src/core/services/token/token-service.ts +++ b/src/core/services/token/token-service.ts @@ -95,10 +95,10 @@ export class TokenServiceImpl implements TokenService { wipePublicKey, kycPublicKey, freezePublicKey, - freezeDefault, pausePublicKey, feeSchedulePublicKey, metadataPublicKey, + freezeDefault, customFees, memo, autoRenewPeriodSeconds, @@ -189,6 +189,11 @@ export class TokenServiceImpl implements TokenService { this.logger.debug(`[TOKEN SERVICE] Set metadata key`); } + if (freezeDefault !== undefined) { + tokenCreateTx.setFreezeDefault(freezeDefault); + this.logger.debug(`[TOKEN SERVICE] Set freeze default: ${freezeDefault}`); + } + if (autoRenewPeriodSeconds && autoRenewAccountId) { tokenCreateTx .setAutoRenewAccountId(AccountId.fromString(autoRenewAccountId)) diff --git a/src/core/types/token.types.ts b/src/core/types/token.types.ts index b36211524..575003193 100644 --- a/src/core/types/token.types.ts +++ b/src/core/types/token.types.ts @@ -72,11 +72,12 @@ export interface TokenCreateParams { pausePublicKey?: PublicKey; feeSchedulePublicKey?: PublicKey; metadataPublicKey?: PublicKey; + autoRenewPeriod?: number; + autoRenewAccountId?: string; + expirationTime?: Date; customFees?: CustomFee[]; memo?: string; autoRenewPeriodSeconds?: number; - autoRenewAccountId?: string; - expirationTime?: Date; } /** diff --git a/src/plugins/token/README.md b/src/plugins/token/README.md index c9e75bd2a..c1252edb0 100644 --- a/src/plugins/token/README.md +++ b/src/plugins/token/README.md @@ -182,6 +182,103 @@ hcli token create-ft \ **Batch support:** Pass `--batch ` to add token creation to a batch instead of executing immediately. See the [Batch Support](#-batch-support) section. +### Token Create NFT + +Create a new non-fungible token (NFT) collection with specified properties. + +```bash +# Using account alias +hcli token create-nft \ + --token-name "My NFT Collection" \ + --symbol "MNFT" \ + --treasury alice \ + --supply-type FINITE \ + --max-supply 1000 \ + --admin-key alice \ + --supply-key alice \ + --freeze-key alice \ + --wipe-key alice \ + --name my-nft-collection + +# With additional optional keys and settings +hcli token create-nft \ + --token-name "My Collection" \ + --symbol "MC" \ + --treasury 0.0.123456:302e020100300506032b657004220420... \ + --supply-type INFINITE \ + --admin-key alice \ + --supply-key alice \ + --kyc-key alice \ + --pause-key alice \ + --fee-schedule-key alice \ + --metadata-key alice \ + --auto-renew-period 7776000 \ + --auto-renew-account-id 0.0.123456 \ + --freeze-default false \ + --name my-collection +``` + +**Parameters:** + +- `--token-name` / `-T`: Token name - **Required** +- `--symbol` / `-s`: Token symbol/ticker - **Required** +- `--treasury`: Treasury account for the NFT collection - **Optional** (defaults to operator) + - Account alias: `alice` + - Account with key: `0.0.123456:private-key` +- `--supply-type`: Supply type - **Optional** (defaults to `INFINITE`) + - `INFINITE` - Unlimited supply + - `FINITE` - Fixed maximum supply (requires `--max-supply`) +- `--max-supply`: Maximum number of NFTs in collection (required for FINITE) - **Optional** +- `--admin-key`: Admin key for administrative operations - **Optional** +- `--supply-key`: Supply key for minting NFTs - **Optional** +- `--freeze-key`: Freeze key to freeze token transfers for accounts - **Optional** +- `--wipe-key`: Wipe key to wipe token balances - **Optional** +- `--kyc-key`: KYC key to grant/revoke KYC status - **Optional** +- `--pause-key`: Pause key to pause all token transfers - **Optional** +- `--fee-schedule-key`: Fee schedule key to modify custom fees - **Optional** +- `--metadata-key`: Metadata key to update token metadata - **Optional** +- `--freeze-default`: Default freeze status for new associations (requires `--freeze-key`) - **Optional** (defaults to false) +- `--auto-renew-period`: Token auto-renewal period in seconds (e.g., 7776000 for 90 days) - **Optional** +- `--auto-renew-account-id`: Account ID that pays for token auto-renewal fees - **Optional** +- `--expiration-time`: Token expiration time in ISO 8601 format (e.g., 2027-01-01T00:00:00Z) - **Optional** +- `--name`: Token alias to register - **Optional** +- `--key-manager`: Key manager type - **Optional** (defaults to config setting) + - `local` or `local_encrypted` +- `--memo`: Optional memo for the token (max 100 characters) - **Optional** +- `--batch`: Add to batch instead of executing immediately - **Optional** + +**Output:** + +```json +{ + "tokenId": "0.0.123456", + "name": "My NFT Collection", + "symbol": "MNFT", + "treasuryId": "0.0.111", + "supplyType": "FINITE", + "transactionId": "0.0.123@1700000000.123456789", + "adminPublicKey": "302e020100300506032b657004220420...", + "supplyPublicKey": "302e020100300506032b657004220420...", + "freezePublicKey": "302e020100300506032b657004220420...", + "wipePublicKey": "302e020100300506032b657004220420...", + "kycPublicKey": "302e020100300506032b657004220420...", + "pausePublicKey": "302e020100300506032b657004220420...", + "feeSchedulePublicKey": "302e020100300506032b657004220420...", + "metadataPublicKey": "302e020100300506032b657004220420...", + "network": "testnet" +} +``` + +**Notes:** + +- NFTs are non-fungible, meaning each NFT is unique and tracked by serial number +- No decimals field applies to NFTs +- Use `mint-nft` command to mint individual NFTs to the collection +- Token name is automatically registered as an alias after successful creation +- Freeze default requires freeze key to be set + +**Batch support:** Pass `--batch ` to add NFT collection creation to a batch instead of executing immediately. See the [Batch Support](#-batch-support) section. + ### Token Mint FT Mint additional fungible tokens to increase supply. Tokens are minted to the token's treasury account. @@ -600,6 +697,11 @@ The token file supports aliases and raw keys with optional key type prefixes: "freezeKey": "", "pauseKey": "", "feeScheduleKey": "", + "metadataKey": "", + "freezeDefault": false, + "autoRenewPeriod": 7776000, + "autoRenewAccountId": "", + "expirationTime": "2027-01-01T00:00:00Z", "memo": "Optional token memo", "autoRenewPeriod": "86400", "autoRenewAccount": "", @@ -667,6 +769,11 @@ The NFT file supports aliases and raw keys with optional key type prefixes: "freezeKey": "", "pauseKey": "", "feeScheduleKey": "", + "metadataKey": "", + "freezeDefault": false, + "autoRenewPeriod": 7776000, + "autoRenewAccountId": "", + "expirationTime": "2027-01-01T00:00:00Z", "memo": "Optional NFT collection memo", "associations": ["", "..."] } @@ -794,6 +901,14 @@ interface TokenData { supplyType: SupplyType; maxSupply: number; memo?: string; + adminPublicKey?: string; + supplyPublicKey?: string; + wipePublicKey?: string; + kycPublicKey?: string; + freezePublicKey?: string; + pausePublicKey?: string; + feeSchedulePublicKey?: string; + metadataPublicKey?: string; keys: TokenKeys; network: 'mainnet' | 'testnet' | 'previewnet' | 'localnet'; associations: TokenAssociation[]; @@ -801,7 +916,21 @@ interface TokenData { } ``` -The `tokenType` field discriminates fungible tokens (`FUNGIBLE_COMMON`) from NFT collections (`NON_FUNGIBLE_UNIQUE`). NFT tokens use zero for `decimals` and `initialSupply`; minted NFTs are tracked by serial number on the ledger. The schema is validated using Zod (`TokenDataSchema`) and stored as JSON Schema in the plugin manifest for runtime validation. +**Field Descriptions:** + +- `tokenType`: Discriminates fungible tokens (`FUNGIBLE_COMMON`) from NFT collections (`NON_FUNGIBLE_UNIQUE`) +- `decimals`: Number of decimal places for fungible tokens; zero for NFTs +- `initialSupply`: Initial supply amount; zero for NFTs +- `adminPublicKey`: Public key with admin privileges for token operations +- `supplyPublicKey`: Public key authorized to mint/burn tokens +- `wipePublicKey`: Public key authorized to wipe token balances +- `kycPublicKey`: Public key authorized to grant/revoke KYC status +- `freezePublicKey`: Public key authorized to freeze token transfers +- `pausePublicKey`: Public key authorized to pause all token transfers +- `feeSchedulePublicKey`: Public key authorized to update custom fees +- `metadataPublicKey`: Public key authorized to update token metadata + +NFT tokens use zero for `decimals` and `initialSupply`; minted NFTs are tracked by serial number on the ledger. The schema is validated using Zod (`TokenDataSchema`) and stored as JSON Schema in the plugin manifest for runtime validation. ## ๐Ÿงช Testing @@ -840,7 +969,7 @@ All commands support multiple output formats: ### Human-Readable (Default) -**Token Create:** +**Fungible Token Create:** ``` โœ… Token created successfully: 0.0.12345 @@ -853,6 +982,17 @@ All commands support multiple output formats: Transaction ID: 0.0.123@1700000000.123456789 ``` +**Non-Fungible Token Create:** + +``` +โœ… NFT created successfully: 0.0.123456 + Name: My NFT Collection (MNFT) + Treasury: 0.0.111 + Supply Type: FINITE + Network: testnet + Transaction ID: 0.0.123@1700000000.123456789 +``` + **FT Mint:** ``` @@ -889,6 +1029,28 @@ All commands support multiple output formats: } ``` +**Non-Fungible Token Create:** + +```json +{ + "tokenId": "0.0.123456", + "name": "My NFT Collection", + "symbol": "MNFT", + "treasuryId": "0.0.111", + "supplyType": "FINITE", + "transactionId": "0.0.123@1700000000.123456789", + "adminPublicKey": "302e020100300506032b657004220420...", + "supplyPublicKey": "302e020100300506032b657004220420...", + "freezePublicKey": "302e020100300506032b657004220420...", + "wipePublicKey": "302e020100300506032b657004220420...", + "kycPublicKey": "302e020100300506032b657004220420...", + "pausePublicKey": "302e020100300506032b657004220420...", + "feeSchedulePublicKey": "302e020100300506032b657004220420...", + "metadataPublicKey": "302e020100300506032b657004220420...", + "network": "testnet" +} +``` + **FT Mint:** ```json diff --git a/src/plugins/token/__tests__/unit/create-nft.test.ts b/src/plugins/token/__tests__/unit/create-nft.test.ts index 5b1a0bdf6..dcec8c1af 100644 --- a/src/plugins/token/__tests__/unit/create-nft.test.ts +++ b/src/plugins/token/__tests__/unit/create-nft.test.ts @@ -1,4 +1,5 @@ import type { CommandHandlerArgs } from '@/core/plugins/plugin.interface'; +import type { TokenCreateNftOutput } from '@/plugins/token/commands/create-nft/output'; import { assertOutput } from '@/__tests__/utils/assert-output'; import { AliasType } from '@/core/services/alias/alias-service.interface'; @@ -14,6 +15,7 @@ import { expectedNftTransactionParams, makeNftCreateCommandArgs, mockAccountIds, + mockAccountKeyPairs, mockTransactions, } from './helpers/fixtures'; import { @@ -173,7 +175,167 @@ describe('tokenCreateNftHandler', () => { }); }); + describe('optional key scenarios', () => { + test('should create NFT with all optional keys', async () => { + const mockSaveToken = jest.fn(); + const mockSignResult = makeTransactionResult({ + tokenId: mockAccountIds.treasury, + }); + + MockedHelper.mockImplementation(() => ({ + saveToken: mockSaveToken, + })); + + const { api, tokenTransactions } = makeApiMocks({ + tokenTransactions: { + createTokenTransaction: jest + .fn() + .mockReturnValue(mockTransactions.token), + }, + txExecute: { + execute: jest.fn().mockResolvedValue(mockSignResult), + }, + alias: { + resolve: jest.fn().mockImplementation((alias, type) => { + if (type === AliasType.Account && alias === 'treasury-account') { + return { + entityId: '0.0.123456', + publicKey: '302a300506032b6570032100' + '1'.repeat(64), + keyRefId: 'treasury-key-ref-id', + }; + } + return null; + }), + }, + }); + + const logger = makeLogger(); + const args: CommandHandlerArgs = { + args: { + tokenName: 'TestNFT', + symbol: 'TNFT', + supplyType: SupplyType.INFINITE, + treasury: 'treasury-account', + freezeKey: mockAccountKeyPairs.freeze, + wipeKey: mockAccountKeyPairs.wipe, + pauseKey: mockAccountKeyPairs.pause, + kycKey: mockAccountKeyPairs.kyc, + feeScheduleKey: mockAccountKeyPairs.feeSchedule, + metadataKey: mockAccountKeyPairs.supply, + }, + api, + state: api.state, + config: api.config, + logger, + }; + + const result = await tokenCreateNft(args); + + expect(tokenTransactions.createTokenTransaction).toHaveBeenCalledWith( + expect.objectContaining({ + freezePublicKey: expect.any(Object), + wipePublicKey: expect.any(Object), + pausePublicKey: expect.any(Object), + kycPublicKey: expect.any(Object), + feeSchedulePublicKey: expect.any(Object), + metadataPublicKey: expect.any(Object), + }), + ); + assertOutput(result.result, TokenCreateNftOutputSchema); + const output = result.result as TokenCreateNftOutput; + expect(output.freezePublicKey).toBeDefined(); + expect(output.wipePublicKey).toBeDefined(); + expect(output.pausePublicKey).toBeDefined(); + expect(output.kycPublicKey).toBeDefined(); + expect(output.feeSchedulePublicKey).toBeDefined(); + expect(output.metadataPublicKey).toBeDefined(); + }); + + test('should create NFT with lifecycle params', async () => { + const mockSaveToken = jest.fn(); + const mockSignResult = makeTransactionResult({ + tokenId: mockAccountIds.treasury, + }); + + MockedHelper.mockImplementation(() => ({ + saveToken: mockSaveToken, + })); + + const { api, tokenTransactions } = makeApiMocks({ + tokenTransactions: { + createTokenTransaction: jest + .fn() + .mockReturnValue(mockTransactions.token), + }, + txExecute: { + execute: jest.fn().mockResolvedValue(mockSignResult), + }, + alias: { + resolve: jest.fn().mockImplementation((alias, type) => { + if (type === AliasType.Account && alias === 'treasury-account') { + return { + entityId: '0.0.123456', + publicKey: '302a300506032b6570032100' + '1'.repeat(64), + keyRefId: 'treasury-key-ref-id', + }; + } + return null; + }), + }, + }); + + const logger = makeLogger(); + const args: CommandHandlerArgs = { + args: { + tokenName: 'TestNFT', + symbol: 'TNFT', + supplyType: SupplyType.INFINITE, + treasury: 'treasury-account', + autoRenewPeriod: 7776000, + autoRenewAccountId: '0.0.100000', + expirationTime: '2027-01-01T00:00:00Z', + }, + api, + state: api.state, + config: api.config, + logger, + }; + + const result = await tokenCreateNft(args); + + expect(tokenTransactions.createTokenTransaction).toHaveBeenCalledWith( + expect.objectContaining({ + autoRenewPeriod: 7776000, + autoRenewAccountId: '0.0.100000', + expirationTime: expect.any(Date), + }), + ); + assertOutput(result.result, TokenCreateNftOutputSchema); + }); + }); + describe('validation scenarios', () => { + test('should reject freezeDefault without freezeKey', async () => { + const { api } = makeApiMocks(); + const logger = makeLogger(); + const args: CommandHandlerArgs = { + args: { + tokenName: 'TestNFT', + symbol: 'TNFT', + supplyType: SupplyType.INFINITE, + freezeDefault: true, + }, + api, + state: api.state, + config: api.config, + logger, + }; + + await expect(tokenCreateNft(args)).rejects.toThrow( + /freezeDefault requires freezeKey/, + ); + }); + test('should exit with error when no credentials found', async () => { // Arrange const { api, keyResolver } = makeApiMocks(); diff --git a/src/plugins/token/commands/create-nft/handler.ts b/src/plugins/token/commands/create-nft/handler.ts index 51e69f539..c54c9e860 100644 --- a/src/plugins/token/commands/create-nft/handler.ts +++ b/src/plugins/token/commands/create-nft/handler.ts @@ -73,6 +73,45 @@ export class TokenCreateNftCommand extends BaseTransactionCommand< api.keyResolver, 'token:supply', ); + const freeze = await resolveOptionalKey( + validArgs.freezeKey, + keyManager, + api.keyResolver, + 'token:freeze', + ); + const wipe = await resolveOptionalKey( + validArgs.wipeKey, + keyManager, + api.keyResolver, + 'token:wipe', + ); + const pause = await resolveOptionalKey( + validArgs.pauseKey, + keyManager, + api.keyResolver, + 'token:pause', + ); + const kyc = await resolveOptionalKey( + validArgs.kycKey, + keyManager, + api.keyResolver, + 'token:kyc', + ); + const feeSchedule = await resolveOptionalKey( + validArgs.feeScheduleKey, + keyManager, + api.keyResolver, + 'token:feeSchedule', + ); + const metadata = await resolveOptionalKey( + validArgs.metadataKey, + keyManager, + api.keyResolver, + 'token:metadata', + ); + const expirationTime = validArgs.expirationTime + ? new Date(validArgs.expirationTime) + : undefined; let finalMaxSupply: bigint | undefined; if (validArgs.supplyType === SupplyType.FINITE) { @@ -109,7 +148,17 @@ export class TokenCreateNftCommand extends BaseTransactionCommand< treasury, admin, supply, + freeze, + wipe, + pause, + kyc, + feeSchedule, + metadata, finalMaxSupply, + freezeDefault: validArgs.freezeDefault, + autoRenewPeriod: validArgs.autoRenewPeriod, + autoRenewAccountId: validArgs.autoRenewAccountId, + expirationTime, keyRefIds: [treasury.keyRefId, ...adminKeyRefIds, ...supplyKeyRefIds], }; } @@ -132,6 +181,16 @@ export class TokenCreateNftCommand extends BaseTransactionCommand< ? PublicKey.fromString(normalisedParams.admin.publicKey) : undefined, supplyPublicKey: toPublicKey(normalisedParams.supply), + freezePublicKey: toPublicKey(normalisedParams.freeze), + wipePublicKey: toPublicKey(normalisedParams.wipe), + pausePublicKey: toPublicKey(normalisedParams.pause), + kycPublicKey: toPublicKey(normalisedParams.kyc), + feeSchedulePublicKey: toPublicKey(normalisedParams.feeSchedule), + metadataPublicKey: toPublicKey(normalisedParams.metadata), + freezeDefault: normalisedParams.freezeDefault, + autoRenewPeriod: normalisedParams.autoRenewPeriod, + autoRenewAccountId: normalisedParams.autoRenewAccountId, + expirationTime: normalisedParams.expirationTime, memo: normalisedParams.memo, }); return { transaction }; @@ -200,6 +259,12 @@ export class TokenCreateNftCommand extends BaseTransactionCommand< supplyType: normalisedParams.supplyType, adminPublicKey: normalisedParams.admin?.publicKey, supplyPublicKey: normalisedParams.supply?.publicKey, + freezePublicKey: normalisedParams.freeze?.publicKey, + wipePublicKey: normalisedParams.wipe?.publicKey, + pausePublicKey: normalisedParams.pause?.publicKey, + kycPublicKey: normalisedParams.kyc?.publicKey, + feeSchedulePublicKey: normalisedParams.feeSchedule?.publicKey, + metadataPublicKey: normalisedParams.metadata?.publicKey, network: normalisedParams.network, }); @@ -227,6 +292,12 @@ export class TokenCreateNftCommand extends BaseTransactionCommand< transactionId: result.transactionId, adminPublicKey: normalisedParams.admin?.publicKey, supplyPublicKey: normalisedParams.supply?.publicKey, + freezePublicKey: normalisedParams.freeze?.publicKey, + wipePublicKey: normalisedParams.wipe?.publicKey, + pausePublicKey: normalisedParams.pause?.publicKey, + kycPublicKey: normalisedParams.kyc?.publicKey, + feeSchedulePublicKey: normalisedParams.feeSchedule?.publicKey, + metadataPublicKey: normalisedParams.metadata?.publicKey, alias: normalisedParams.alias, network: normalisedParams.network, }; diff --git a/src/plugins/token/commands/create-nft/input.ts b/src/plugins/token/commands/create-nft/input.ts index 3f2f26f04..4be2ca7a1 100644 --- a/src/plugins/token/commands/create-nft/input.ts +++ b/src/plugins/token/commands/create-nft/input.ts @@ -2,6 +2,7 @@ import { z } from 'zod'; import { AmountInputSchema, + EntityIdSchema, KeyManagerTypeSchema, KeySchema, MemoSchema, @@ -13,10 +14,6 @@ import { import { validateSupplyTypeAndMaxSupply } from '@/core/shared/validation/validate-supply.zod'; import { SupplyType } from '@/core/types/shared.types'; -/** - * Input schema for token create command - * Validates arguments for creating a new fungible token - */ export const TokenCreateNftInputSchema = z .object({ tokenName: TokenNameSchema.describe('Token name'), @@ -36,6 +33,45 @@ export const TokenCreateNftInputSchema = z supplyKey: KeySchema.optional().describe( 'Supply key. Accepts any key format.', ), + freezeKey: KeySchema.optional().describe( + 'Freeze key. Allows freezing token transfers for specific accounts.', + ), + wipeKey: KeySchema.optional().describe( + 'Wipe key. Allows wiping token balance from specific accounts.', + ), + pauseKey: KeySchema.optional().describe( + 'Pause key. Allows pausing all token transfers.', + ), + kycKey: KeySchema.optional().describe( + 'KYC key. Allows granting/revoking KYC status.', + ), + feeScheduleKey: KeySchema.optional().describe( + 'Fee schedule key. Allows modifying custom fees.', + ), + metadataKey: KeySchema.optional().describe( + 'Metadata key. Allows updating token metadata.', + ), + freezeDefault: z + .boolean() + .optional() + .describe( + 'Default freeze status for new token associations. Requires freezeKey.', + ), + autoRenewPeriod: z + .number() + .int() + .positive() + .optional() + .describe('Auto-renew period in seconds (e.g. 7776000 for 90 days).'), + autoRenewAccountId: EntityIdSchema.optional().describe( + 'Account ID that pays for token auto-renewal fees (e.g. 0.0.12345).', + ), + expirationTime: z.iso + .datetime() + .optional() + .describe( + 'Token expiration time in ISO 8601 format (e.g. 2027-01-01T00:00:00Z).', + ), name: TokenAliasNameSchema.optional().describe( 'Optional alias to register for the token', ), @@ -46,6 +82,15 @@ export const TokenCreateNftInputSchema = z 'Optional memo for the token (max 100 characters)', ), }) - .superRefine(validateSupplyTypeAndMaxSupply); + .superRefine(validateSupplyTypeAndMaxSupply) + .superRefine((data, ctx) => { + if (data.freezeDefault !== undefined && !data.freezeKey) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'freezeDefault requires freezeKey to be set', + path: ['freezeDefault'], + }); + } + }); export type TokenCreateNftInput = z.infer; diff --git a/src/plugins/token/commands/create-nft/output.ts b/src/plugins/token/commands/create-nft/output.ts index 11feeb762..f7aa014b4 100644 --- a/src/plugins/token/commands/create-nft/output.ts +++ b/src/plugins/token/commands/create-nft/output.ts @@ -33,6 +33,19 @@ export const TokenCreateNftOutputSchema = z.object({ supplyAccountId: EntityIdSchema.optional().describe('Supply account ID'), supplyPublicKey: PublicKeyDefinitionSchema.optional().describe('Supply public key'), + freezePublicKey: + PublicKeyDefinitionSchema.optional().describe('Freeze public key'), + wipePublicKey: + PublicKeyDefinitionSchema.optional().describe('Wipe public key'), + pausePublicKey: + PublicKeyDefinitionSchema.optional().describe('Pause public key'), + kycPublicKey: PublicKeyDefinitionSchema.optional().describe('KYC public key'), + feeSchedulePublicKey: PublicKeyDefinitionSchema.optional().describe( + 'Fee schedule public key', + ), + metadataPublicKey: PublicKeyDefinitionSchema.optional().describe( + 'Metadata public key', + ), alias: z.string().describe('Token alias').optional(), network: NetworkSchema.describe('Network on which token exists'), }); @@ -50,13 +63,33 @@ export const TOKEN_CREATE_NFT_TEMPLATE = ` {{#if adminAccountId}} Admin account: {{hashscanLink adminAccountId "account" network}} {{/if}} +{{#if adminPublicKey}} Admin public key: {{adminPublicKey}} +{{/if}} {{#if supplyAccountId}} Supply account: {{hashscanLink supplyAccountId "account" network}} {{/if}} {{#if supplyPublicKey}} Supply public key: {{supplyPublicKey}} {{/if}} +{{#if freezePublicKey}} + Freeze public key: {{freezePublicKey}} +{{/if}} +{{#if wipePublicKey}} + Wipe public key: {{wipePublicKey}} +{{/if}} +{{#if pausePublicKey}} + Pause public key: {{pausePublicKey}} +{{/if}} +{{#if kycPublicKey}} + KYC public key: {{kycPublicKey}} +{{/if}} +{{#if feeSchedulePublicKey}} + Fee schedule public key: {{feeSchedulePublicKey}} +{{/if}} +{{#if metadataPublicKey}} + Metadata public key: {{metadataPublicKey}} +{{/if}} {{#if alias}} Alias: {{alias}} {{/if}} diff --git a/src/plugins/token/commands/create-nft/types.ts b/src/plugins/token/commands/create-nft/types.ts index 088d9d004..52b70fc85 100644 --- a/src/plugins/token/commands/create-nft/types.ts +++ b/src/plugins/token/commands/create-nft/types.ts @@ -26,7 +26,17 @@ export interface TokenCreateNftNormalizedParams extends BaseNormalizedParams { treasury: ResolvedAccountCredential; admin?: ResolvedPublicKey; supply?: ResolvedPublicKey; + freeze?: ResolvedPublicKey; + wipe?: ResolvedPublicKey; + pause?: ResolvedPublicKey; + kyc?: ResolvedPublicKey; + feeSchedule?: ResolvedPublicKey; + metadata?: ResolvedPublicKey; finalMaxSupply?: bigint; + freezeDefault?: boolean; + autoRenewPeriod?: number; + autoRenewAccountId?: string; + expirationTime?: Date; } export interface TokenCreateNftBuildTransactionResult extends BaseBuildTransactionResult {} diff --git a/src/plugins/token/manifest.ts b/src/plugins/token/manifest.ts index 83006ed20..d8dd76514 100644 --- a/src/plugins/token/manifest.ts +++ b/src/plugins/token/manifest.ts @@ -559,6 +559,82 @@ export const tokenPluginManifest: PluginManifest = { description: 'Supply key of token. Can be {accountId}:{privateKey} pair, account ID, account public key in {ed25519|ecdsa}:public:{public-key} format, account private key in {ed25519|ecdsa}:private:{private-key} format, key reference or account alias.', }, + { + name: 'freeze-key', + short: 'f', + type: OptionType.STRING, + required: false, + description: + 'Freeze key. Allows freezing token transfers for specific accounts.', + }, + { + name: 'wipe-key', + short: 'w', + type: OptionType.STRING, + required: false, + description: + 'Wipe key. Allows wiping token balance from specific accounts.', + }, + { + name: 'pause-key', + short: 'p', + type: OptionType.STRING, + required: false, + description: 'Pause key. Allows pausing all token transfers.', + }, + { + name: 'kyc-key', + short: 'y', + type: OptionType.STRING, + required: false, + description: 'KYC key. Allows granting/revoking KYC status.', + }, + { + name: 'fee-schedule-key', + short: 'e', + type: OptionType.STRING, + required: false, + description: 'Fee schedule key. Allows modifying custom fees.', + }, + { + name: 'metadata-key', + short: 'D', + type: OptionType.STRING, + required: false, + description: 'Metadata key. Allows updating token metadata.', + }, + { + name: 'freeze-default', + short: 'F', + type: OptionType.BOOLEAN, + required: false, + description: + 'Default freeze status for new token associations. Requires freeze-key.', + }, + { + name: 'auto-renew-period', + short: 'R', + type: OptionType.NUMBER, + required: false, + description: + 'Auto-renew period in seconds (e.g. 7776000 for 90 days).', + }, + { + name: 'auto-renew-account-id', + short: 'r', + type: OptionType.STRING, + required: false, + description: + 'Account ID that pays for token auto-renewal fees (e.g. 0.0.12345).', + }, + { + name: 'expiration-time', + short: 'x', + type: OptionType.STRING, + required: false, + description: + 'Token expiration time in ISO 8601 format (e.g. 2027-01-01T00:00:00Z).', + }, { name: 'name', short: 'n', diff --git a/src/plugins/token/utils/token-data-builders.ts b/src/plugins/token/utils/token-data-builders.ts index 9b6fbb297..2e8d0a3b3 100644 --- a/src/plugins/token/utils/token-data-builders.ts +++ b/src/plugins/token/utils/token-data-builders.ts @@ -21,10 +21,10 @@ export function buildTokenData( supplyType: string; adminPublicKey?: string; supplyPublicKey?: string; - wipePublicKey?: string; - kycPublicKey?: string; freezePublicKey?: string; + wipePublicKey?: string; pausePublicKey?: string; + kycPublicKey?: string; feeSchedulePublicKey?: string; metadataPublicKey?: string; network: SupportedNetwork; @@ -45,10 +45,10 @@ export function buildTokenData( : 0n, adminPublicKey: params.adminPublicKey, supplyPublicKey: params.supplyPublicKey, - wipePublicKey: params.wipePublicKey, - kycPublicKey: params.kycPublicKey, freezePublicKey: params.freezePublicKey, + wipePublicKey: params.wipePublicKey, pausePublicKey: params.pausePublicKey, + kycPublicKey: params.kycPublicKey, feeSchedulePublicKey: params.feeSchedulePublicKey, metadataPublicKey: params.metadataPublicKey, network: params.network,