Skip to content
Merged
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
42 changes: 42 additions & 0 deletions packages/sdk/src/plt/Cbor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { HexString } from '../types.js';
import { cborDecode, cborEncode } from '../types/cbor.js';
import { TokenHolder, TokenMetadataUrl } from './index.js';
import {
TokenInitializationParameters,
TokenListUpdateEventDetails,
TokenModuleAccountState,
TokenModuleState,
Expand Down Expand Up @@ -196,11 +197,50 @@ function decodeTokenPauseEventDetails(value: Cbor): TokenPauseEventDetails {
return decoded as TokenPauseEventDetails;
}

function decodeTokenInitializationParameters(value: Cbor): TokenInitializationParameters {
const decoded = cborDecode(value.bytes);
if (typeof decoded !== 'object' || decoded === null) {
throw new Error('Invalid CBOR data for TokenInitializationParameters');
}

// Validate required fields
if (!('governanceAccount' in decoded && TokenHolder.instanceOf(decoded.governanceAccount))) {
throw new Error('Invalid TokenInitializationParameters: missing or invalid governanceAccount');
}
if (!('metadata' in decoded)) {
throw new Error('Invalid TokenInitializationParameters: missing metadataUrl');
}
let metadata = TokenMetadataUrl.fromCBORValue(decoded.metadata);
if (!('name' in decoded && typeof decoded.name === 'string')) {
throw new Error('Invalid TokenInitializationParameters: missing or invalid name');
}

// Validate optional fields
if ('allowList' in decoded && typeof decoded.allowList !== 'boolean') {
throw new Error('Invalid TokenInitializationParameters: allowList must be a boolean');
}
if ('denyList' in decoded && typeof decoded.denyList !== 'boolean') {
throw Error('Invalid TokenInitializationParameters: denyList must be a boolean');
}
if ('mintable' in decoded && typeof decoded.mintable !== 'boolean') {
throw new Error('Invalid TokenInitializationParameters: mintable must be a boolean');
}
if ('burnable' in decoded && typeof decoded.burnable !== 'boolean') {
throw new Error('Invalid TokenInitializationParameters: burnable must be a boolean');
}
if ('paused' in decoded && typeof decoded.paused !== 'boolean') {
throw new Error('Invalid TokenInitializationParameters: paused must be a boolean');
}

return { ...decoded, metadata } as TokenInitializationParameters;
}

type DecodeTypeMap = {
TokenModuleState: TokenModuleState;
TokenModuleAccountState: TokenModuleAccountState;
TokenListUpdateEventDetails: TokenListUpdateEventDetails;
TokenPauseEventDetails: TokenPauseEventDetails;
TokenInitializationParameters: TokenInitializationParameters;
};

export function decode<T extends keyof DecodeTypeMap>(cbor: Cbor, type: T): DecodeTypeMap[T];
Expand All @@ -222,6 +262,8 @@ export function decode<T extends keyof DecodeTypeMap | undefined>(cbor: Cbor, ty
return decodeTokenListUpdateEventDetails(cbor);
case 'TokenPauseEventDetails':
return decodeTokenPauseEventDetails(cbor);
case 'TokenInitializationParameters':
return decodeTokenInitializationParameters(cbor);
default:
return cborDecode(cbor.bytes);
}
Expand Down
78 changes: 78 additions & 0 deletions packages/sdk/test/ci/plt/Cbor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,84 @@ describe('Cbor', () => {
});
});

describe('TokenInitializationParameters', () => {
test('should encode and decode TokenInitializationParameters correctly', () => {
const accountAddress = AccountAddress.fromBase58('3XSLuJcXg6xEua6iBPnWacc3iWh93yEDMCqX8FbE3RDSbEnT9P');
const tokenHolder = TokenHolder.fromAccountAddress(accountAddress);
const metadataUrl = TokenMetadataUrl.fromString('https://example.com/metadata.json');

const params = {
name: 'Test Token',
metadata: metadataUrl,
governanceAccount: tokenHolder,
allowList: true,
denyList: false,
mintable: true,
burnable: true,
customField: 'custom value',
};

const encoded = Cbor.encode(params);
const decoded = Cbor.decode(encoded, 'TokenInitializationParameters');

expect(decoded.name).toBe(params.name);
expect(decoded.metadata).toEqual(params.metadata);
expect(decoded.governanceAccount).toEqual(params.governanceAccount);
expect(decoded.allowList).toBe(params.allowList);
expect(decoded.denyList).toBe(params.denyList);
expect(decoded.mintable).toBe(params.mintable);
expect(decoded.burnable).toBe(params.burnable);
});

test('should throw error if TokenInitializationParameters is missing required fields', () => {
// Missing governanceAccount
const invalidParams1 = {
name: 'Test Token',
metadata: TokenMetadataUrl.fromString('https://example.com/metadata.json'),
// governanceAccount is missing
};
const encoded1 = Cbor.encode(invalidParams1);
expect(() => Cbor.decode(encoded1, 'TokenInitializationParameters')).toThrow(
/missing or invalid governanceAccount/
);

// Missing name
const accountAddress = AccountAddress.fromBase58('3XSLuJcXg6xEua6iBPnWacc3iWh93yEDMCqX8FbE3RDSbEnT9P');
const invalidParams2 = {
// name is missing
metadata: TokenMetadataUrl.fromString('https://example.com/metadata.json'),
governanceAccount: TokenHolder.fromAccountAddress(accountAddress),
};
const encoded2 = Cbor.encode(invalidParams2);
expect(() => Cbor.decode(encoded2, 'TokenInitializationParameters')).toThrow(/missing or invalid name/);

// Missing metadata
const invalidParams3 = {
name: 'Test Token',
// metadata is missing
governanceAccount: TokenHolder.fromAccountAddress(accountAddress),
};
const encoded3 = Cbor.encode(invalidParams3);
expect(() => Cbor.decode(encoded3, 'TokenInitializationParameters')).toThrow(/missing metadataUrl/);
});

test('should throw error if TokenInitializationParameters has invalid field types', () => {
const accountAddress = AccountAddress.fromBase58('3XSLuJcXg6xEua6iBPnWacc3iWh93yEDMCqX8FbE3RDSbEnT9P');
const tokenHolder = TokenHolder.fromAccountAddress(accountAddress);
const metadataUrl = TokenMetadataUrl.fromString('https://example.com/metadata.json');

// Invalid allowList type
const invalidParams = {
name: 'Test Token',
metadata: metadataUrl,
governanceAccount: tokenHolder,
allowList: 'yes', // Should be boolean
};
const encoded = Cbor.encode(invalidParams);
expect(() => Cbor.decode(encoded, 'TokenInitializationParameters')).toThrow(/allowList must be a boolean/);
});
});

describe('TokenModuleAccountState', () => {
test('should encode and decode TokenModuleAccountState correctly', () => {
const state = {
Expand Down