Skip to content

Commit d532b07

Browse files
ccharlymcmire
andauthored
feat(caip): add .toCaipChainId + KnownCaipNamespace (#175)
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 <elliot.winkler@gmail.com>
1 parent e58a8d8 commit d532b07

4 files changed

Lines changed: 101 additions & 0 deletions

File tree

src/caip-types.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import {
1313
isCaipReference,
1414
parseCaipAccountId,
1515
parseCaipChainId,
16+
toCaipChainId,
17+
KnownCaipNamespace,
18+
CAIP_NAMESPACE_REGEX,
19+
CAIP_REFERENCE_REGEX,
1620
} from './caip-types';
1721

1822
describe('isCaipChainId', () => {
@@ -274,3 +278,56 @@ describe('parseCaipAccountId', () => {
274278
);
275279
});
276280
});
281+
282+
describe('toCaipChainId', () => {
283+
// This function relies on @metamask/utils CAIP helpers. Those are being
284+
// tested with a variety of inputs.
285+
// Here we mainly focus on our own wrapper around those:
286+
287+
it('returns a valid CAIP-2 chain ID when given a valid namespace and reference', () => {
288+
const namespace = 'abc';
289+
const reference = '1';
290+
expect(toCaipChainId(namespace, reference)).toBe(
291+
`${namespace}:${reference}`,
292+
);
293+
});
294+
295+
it.each(Object.values(KnownCaipNamespace))(
296+
'treats %s as a valid namespace',
297+
(namespace) => {
298+
const reference = '1';
299+
expect(toCaipChainId(namespace, reference)).toBe(
300+
`${namespace}:${reference}`,
301+
);
302+
},
303+
);
304+
305+
it.each([
306+
// Too short, must have 3 chars at least
307+
'',
308+
'xs',
309+
// Not matching
310+
'!@#$%^&*()',
311+
// Too long
312+
'namespacetoolong',
313+
])('throws for invalid namespaces: %s', (namespace) => {
314+
const reference = '1';
315+
expect(() => toCaipChainId(namespace, reference)).toThrow(
316+
`Invalid "namespace", must match: ${CAIP_NAMESPACE_REGEX.toString()}`,
317+
);
318+
});
319+
320+
it.each([
321+
// Too short, must have 1 char at least
322+
'',
323+
// Not matching
324+
'!@#$%^&*()',
325+
// Too long
326+
'012345678901234567890123456789012', // 33 chars
327+
])('throws for invalid reference: %s', (reference) => {
328+
const namespace = 'abc';
329+
expect(() => toCaipChainId(namespace, reference)).toThrow(
330+
`Invalid "reference", must match: ${CAIP_REFERENCE_REGEX.toString()}`,
331+
);
332+
});
333+
});

src/caip-types.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ export const CaipAccountAddressStruct = pattern(
4646
);
4747
export type CaipAccountAddress = Infer<typeof CaipAccountAddressStruct>;
4848

49+
/** Known CAIP namespaces. */
50+
export enum KnownCaipNamespace {
51+
/** EIP-155 compatible chains. */
52+
Eip155 = 'eip155',
53+
}
54+
4955
/**
5056
* Check if the given value is a {@link CaipChainId}.
5157
*
@@ -146,3 +152,37 @@ export function parseCaipAccountId(caipAccountId: CaipAccountId): {
146152
},
147153
};
148154
}
155+
156+
/**
157+
* Chain ID as defined per the CAIP-2
158+
* {@link https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md}.
159+
*
160+
* It defines a way to uniquely identify any blockchain in a human-readable
161+
* way.
162+
*
163+
* @param namespace - The standard (ecosystem) of similar blockchains.
164+
* @param reference - Identify of a blockchain within a given namespace.
165+
* @throws {@link Error}
166+
* This exception is thrown if the inputs does not comply with the CAIP-2
167+
* syntax specification
168+
* {@link https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md#syntax}.
169+
* @returns A CAIP chain ID.
170+
*/
171+
export function toCaipChainId(
172+
namespace: CaipNamespace,
173+
reference: CaipReference,
174+
): CaipChainId {
175+
if (!isCaipNamespace(namespace)) {
176+
throw new Error(
177+
`Invalid "namespace", must match: ${CAIP_NAMESPACE_REGEX.toString()}`,
178+
);
179+
}
180+
181+
if (!isCaipReference(reference)) {
182+
throw new Error(
183+
`Invalid "reference", must match: ${CAIP_REFERENCE_REGEX.toString()}`,
184+
);
185+
}
186+
187+
return `${namespace}:${reference}`;
188+
}

src/index.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe('index', () => {
3434
"JsonRpcVersionStruct",
3535
"JsonSize",
3636
"JsonStruct",
37+
"KnownCaipNamespace",
3738
"PendingJsonRpcResponseStruct",
3839
"StrictHexStruct",
3940
"UnsafeJsonStruct",
@@ -129,6 +130,7 @@ describe('index', () => {
129130
"signedBigIntToBytes",
130131
"stringToBytes",
131132
"timeSince",
133+
"toCaipChainId",
132134
"valueToBytes",
133135
"wrapError",
134136
]

src/node.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ describe('node', () => {
3434
"JsonRpcVersionStruct",
3535
"JsonSize",
3636
"JsonStruct",
37+
"KnownCaipNamespace",
3738
"PendingJsonRpcResponseStruct",
3839
"StrictHexStruct",
3940
"UnsafeJsonStruct",
@@ -136,6 +137,7 @@ describe('node', () => {
136137
"signedBigIntToBytes",
137138
"stringToBytes",
138139
"timeSince",
140+
"toCaipChainId",
139141
"valueToBytes",
140142
"wrapError",
141143
"writeFile",

0 commit comments

Comments
 (0)