From 7d0611c9e8bc4d1ee2d585faa624477741adc051 Mon Sep 17 00:00:00 2001 From: Charly Chevalier Date: Wed, 24 Apr 2024 12:54:00 +0200 Subject: [PATCH] feat(caip): add support of caip-19 --- src/caip-types.test.ts | 87 ++++++++++++++++++++++++++++++++++++++++++ src/caip-types.ts | 38 ++++++++++++++++++ src/index.test.ts | 6 +++ src/node.test.ts | 6 +++ 4 files changed, 137 insertions(+) diff --git a/src/caip-types.test.ts b/src/caip-types.test.ts index 16d4958e..ed9e9093 100644 --- a/src/caip-types.test.ts +++ b/src/caip-types.test.ts @@ -11,6 +11,8 @@ import { isCaipChainId, isCaipNamespace, isCaipReference, + isCaipAssetType, + isCaipAssetId, parseCaipAccountId, parseCaipChainId, toCaipChainId, @@ -149,6 +151,91 @@ describe('isCaipAccountAddress', () => { }); }); +describe('isCaipAssetType', () => { + // Imported from: https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-19.md#test-cases + it.each([ + 'eip155:1/slip44:60', + 'bip122:000000000019d6689c085ae165831e93/slip44:0', + 'cosmos:cosmoshub-3/slip44:118', + 'bip122:12a765e31ffd4059bada1e25190f6e98/slip44:2', + 'cosmos:Binance-Chain-Tigris/slip44:714', + 'cosmos:iov-mainnet/slip44:234', + 'lip9:9ee11e9df416b18b/slip44:134', + 'eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f', + 'eip155:1/erc721:0x06012c8cf97BEaD5deAe237070F9587f8E7A266d', + ])('returns true for a valid asset type %s', (id) => { + expect(isCaipAssetType(id)).toBe(true); + }); + + it.each([ + true, + false, + null, + undefined, + 1, + {}, + [], + '', + '!@#$%^&*()', + 'foo', + 'eip155', + 'eip155:', + 'eip155:1', + 'eip155:1:', + 'eip155:1:0x0000000000000000000000000000000000000000:2', + 'bip122', + 'bip122:', + 'bip122:000000000019d6689c085ae165831e93', + 'bip122:000000000019d6689c085ae165831e93/', + 'bip122:000000000019d6689c085ae165831e93/tooooooolong', + 'bip122:000000000019d6689c085ae165831e93/tooooooolong:asset', + 'eip155:1/erc721', + 'eip155:1/erc721:', + 'eip155:1/erc721:0x06012c8cf97BEaD5deAe237070F9587f8E7A266d/', + ])('returns false for an invalid asset type %s', (id) => { + expect(isCaipAssetType(id)).toBe(false); + }); +}); + +describe('isCaipAssetId', () => { + // Imported from: https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-19.md#test-cases + it.each([ + 'eip155:1/erc721:0x06012c8cf97BEaD5deAe237070F9587f8E7A266d/771769', + 'hedera:mainnet/nft:0.0.55492/12', + ])('returns true for a valid asset id %s', (id) => { + expect(isCaipAssetId(id)).toBe(true); + }); + + it.each([ + true, + false, + null, + undefined, + 1, + {}, + [], + '', + '!@#$%^&*()', + 'foo', + 'eip155', + 'eip155:', + 'eip155:1', + 'eip155:1:', + 'eip155:1:0x0000000000000000000000000000000000000000:2', + 'bip122', + 'bip122:', + 'bip122:000000000019d6689c085ae165831e93', + 'bip122:000000000019d6689c085ae165831e93/', + 'bip122:000000000019d6689c085ae165831e93/tooooooolong', + 'bip122:000000000019d6689c085ae165831e93/tooooooolong:asset', + 'eip155:1/erc721', + 'eip155:1/erc721:', + 'eip155:1/erc721:0x06012c8cf97BEaD5deAe237070F9587f8E7A266d/', + ])('returns false for an invalid asset id %s', (id) => { + expect(isCaipAssetType(id)).toBe(false); + }); +}); + describe('parseCaipChainId', () => { it('parses valid chain ids', () => { expect(parseCaipChainId('eip155:1')).toMatchInlineSnapshot(` diff --git a/src/caip-types.ts b/src/caip-types.ts index 037179ac..e7e6ee89 100644 --- a/src/caip-types.ts +++ b/src/caip-types.ts @@ -13,6 +13,12 @@ export const CAIP_ACCOUNT_ID_REGEX = export const CAIP_ACCOUNT_ADDRESS_REGEX = /^[-.%a-zA-Z0-9]{1,128}$/u; +export const CAIP_ASSET_TYPE_REGEX = + /^(?(?[-a-z0-9]{3,8}):(?[-_a-zA-Z0-9]{1,32}))\/(?[-a-z0-9]{3,8}):(?[-.%a-zA-Z0-9]{1,128})$/u; + +export const CAIP_ASSET_ID_REGEX = + /^(?(?[-a-z0-9]{3,8}):(?[-_a-zA-Z0-9]{1,32}))\/(?[-a-z0-9]{3,8}):(?[-.%a-zA-Z0-9]{1,128})\/(?[-.%a-zA-Z0-9]{1,78})$/u; + /** * A CAIP-2 chain ID, i.e., a human-readable namespace and reference. */ @@ -52,6 +58,18 @@ export const CaipAccountAddressStruct = pattern( ); export type CaipAccountAddress = Infer; +/** + * A CAIP-19 asset type identifier, i.e., a human-readable type of asset identifier. + */ +export const CaipAssetTypeStruct = pattern(string(), CAIP_ASSET_TYPE_REGEX); +export type CaipAssetType = Infer; + +/** + * A CAIP-19 asset ID identifier, i.e., a human-readable type of asset ID. + */ +export const CaipAssetIdStruct = pattern(string(), CAIP_ASSET_ID_REGEX); +export type CaipAssetId = Infer; + /** Known CAIP namespaces. */ export enum KnownCaipNamespace { /** BIP-122 (Bitcoin) compatible chains. */ @@ -113,6 +131,26 @@ export function isCaipAccountAddress( return is(value, CaipAccountAddressStruct); } +/** + * Check if the given value is a {@link CaipAssetType}. + * + * @param value - The value to check. + * @returns Whether the value is a {@link CaipAssetType}. + */ +export function isCaipAssetType(value: unknown): value is CaipAssetType { + return is(value, CaipAssetTypeStruct); +} + +/** + * Check if the given value is a {@link CaipAssetId}. + * + * @param value - The value to check. + * @returns Whether the value is a {@link CaipAssetId}. + */ +export function isCaipAssetId(value: unknown): value is CaipAssetId { + return is(value, CaipAssetIdStruct); +} + /** * Parse a CAIP-2 chain ID to an object containing the namespace and reference. * This validates the CAIP-2 chain ID before parsing it. diff --git a/src/index.test.ts b/src/index.test.ts index e9a0ad8f..9e36ce70 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -7,11 +7,15 @@ describe('index', () => { "AssertionError", "CAIP_ACCOUNT_ADDRESS_REGEX", "CAIP_ACCOUNT_ID_REGEX", + "CAIP_ASSET_ID_REGEX", + "CAIP_ASSET_TYPE_REGEX", "CAIP_CHAIN_ID_REGEX", "CAIP_NAMESPACE_REGEX", "CAIP_REFERENCE_REGEX", "CaipAccountAddressStruct", "CaipAccountIdStruct", + "CaipAssetIdStruct", + "CaipAssetTypeStruct", "CaipChainIdStruct", "CaipNamespaceStruct", "CaipReferenceStruct", @@ -95,6 +99,8 @@ describe('index', () => { "isBytes", "isCaipAccountAddress", "isCaipAccountId", + "isCaipAssetId", + "isCaipAssetType", "isCaipChainId", "isCaipNamespace", "isCaipReference", diff --git a/src/node.test.ts b/src/node.test.ts index 7fd17a33..5f7d5ed4 100644 --- a/src/node.test.ts +++ b/src/node.test.ts @@ -7,11 +7,15 @@ describe('node', () => { "AssertionError", "CAIP_ACCOUNT_ADDRESS_REGEX", "CAIP_ACCOUNT_ID_REGEX", + "CAIP_ASSET_ID_REGEX", + "CAIP_ASSET_TYPE_REGEX", "CAIP_CHAIN_ID_REGEX", "CAIP_NAMESPACE_REGEX", "CAIP_REFERENCE_REGEX", "CaipAccountAddressStruct", "CaipAccountIdStruct", + "CaipAssetIdStruct", + "CaipAssetTypeStruct", "CaipChainIdStruct", "CaipNamespaceStruct", "CaipReferenceStruct", @@ -100,6 +104,8 @@ describe('node', () => { "isBytes", "isCaipAccountAddress", "isCaipAccountId", + "isCaipAssetId", + "isCaipAssetType", "isCaipChainId", "isCaipNamespace", "isCaipReference",