Skip to content

Commit

Permalink
feat(caip): add .toCaipChainId + KnownCaipNamespace (#175)
Browse files Browse the repository at this point in the history
This adds some new helpers regarding CAIP-2 chain IDs.

This is in regard to the on-going work of adding those chain-agnostics
IDs into our Snap keyring implementations.

Initially those helpers were living on
https://github.com/MetaMask/eth-snap-keyring repository, but it feels
more natural to have them here. Moreover, we might use them elsewhere.

## Related

- MetaMask/eth-snap-keyring#231

---------

Co-authored-by: Elliot Winkler <[email protected]>
  • Loading branch information
ccharly and mcmire authored Mar 14, 2024
1 parent e58a8d8 commit d532b07
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
57 changes: 57 additions & 0 deletions src/caip-types.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import {
isCaipReference,
parseCaipAccountId,
parseCaipChainId,
toCaipChainId,
KnownCaipNamespace,
CAIP_NAMESPACE_REGEX,
CAIP_REFERENCE_REGEX,
} from './caip-types';

describe('isCaipChainId', () => {
Expand Down Expand Up @@ -274,3 +278,56 @@ describe('parseCaipAccountId', () => {
);
});
});

describe('toCaipChainId', () => {
// This function relies on @metamask/utils CAIP helpers. Those are being
// tested with a variety of inputs.
// Here we mainly focus on our own wrapper around those:

it('returns a valid CAIP-2 chain ID when given a valid namespace and reference', () => {
const namespace = 'abc';
const reference = '1';
expect(toCaipChainId(namespace, reference)).toBe(
`${namespace}:${reference}`,
);
});

it.each(Object.values(KnownCaipNamespace))(
'treats %s as a valid namespace',
(namespace) => {
const reference = '1';
expect(toCaipChainId(namespace, reference)).toBe(
`${namespace}:${reference}`,
);
},
);

it.each([
// Too short, must have 3 chars at least
'',
'xs',
// Not matching
'!@#$%^&*()',
// Too long
'namespacetoolong',
])('throws for invalid namespaces: %s', (namespace) => {
const reference = '1';
expect(() => toCaipChainId(namespace, reference)).toThrow(
`Invalid "namespace", must match: ${CAIP_NAMESPACE_REGEX.toString()}`,
);
});

it.each([
// Too short, must have 1 char at least
'',
// Not matching
'!@#$%^&*()',
// Too long
'012345678901234567890123456789012', // 33 chars
])('throws for invalid reference: %s', (reference) => {
const namespace = 'abc';
expect(() => toCaipChainId(namespace, reference)).toThrow(
`Invalid "reference", must match: ${CAIP_REFERENCE_REGEX.toString()}`,
);
});
});
40 changes: 40 additions & 0 deletions src/caip-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ export const CaipAccountAddressStruct = pattern(
);
export type CaipAccountAddress = Infer<typeof CaipAccountAddressStruct>;

/** Known CAIP namespaces. */
export enum KnownCaipNamespace {
/** EIP-155 compatible chains. */
Eip155 = 'eip155',
}

/**
* Check if the given value is a {@link CaipChainId}.
*
Expand Down Expand Up @@ -146,3 +152,37 @@ export function parseCaipAccountId(caipAccountId: CaipAccountId): {
},
};
}

/**
* Chain ID as defined per the CAIP-2
* {@link https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md}.
*
* It defines a way to uniquely identify any blockchain in a human-readable
* way.
*
* @param namespace - The standard (ecosystem) of similar blockchains.
* @param reference - Identify of a blockchain within a given namespace.
* @throws {@link Error}
* This exception is thrown if the inputs does not comply with the CAIP-2
* syntax specification
* {@link https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md#syntax}.
* @returns A CAIP chain ID.
*/
export function toCaipChainId(
namespace: CaipNamespace,
reference: CaipReference,
): CaipChainId {
if (!isCaipNamespace(namespace)) {
throw new Error(
`Invalid "namespace", must match: ${CAIP_NAMESPACE_REGEX.toString()}`,
);
}

if (!isCaipReference(reference)) {
throw new Error(
`Invalid "reference", must match: ${CAIP_REFERENCE_REGEX.toString()}`,
);
}

return `${namespace}:${reference}`;
}
2 changes: 2 additions & 0 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('index', () => {
"JsonRpcVersionStruct",
"JsonSize",
"JsonStruct",
"KnownCaipNamespace",
"PendingJsonRpcResponseStruct",
"StrictHexStruct",
"UnsafeJsonStruct",
Expand Down Expand Up @@ -129,6 +130,7 @@ describe('index', () => {
"signedBigIntToBytes",
"stringToBytes",
"timeSince",
"toCaipChainId",
"valueToBytes",
"wrapError",
]
Expand Down
2 changes: 2 additions & 0 deletions src/node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('node', () => {
"JsonRpcVersionStruct",
"JsonSize",
"JsonStruct",
"KnownCaipNamespace",
"PendingJsonRpcResponseStruct",
"StrictHexStruct",
"UnsafeJsonStruct",
Expand Down Expand Up @@ -136,6 +137,7 @@ describe('node', () => {
"signedBigIntToBytes",
"stringToBytes",
"timeSince",
"toCaipChainId",
"valueToBytes",
"wrapError",
"writeFile",
Expand Down

0 comments on commit d532b07

Please sign in to comment.