Skip to content

Commit

Permalink
feat(keyring-utils): add isScopeEqual + isScopeEqualToAny
Browse files Browse the repository at this point in the history
  • Loading branch information
ccharly committed Feb 19, 2025
1 parent 5ea149d commit 7360e49
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 0 deletions.
58 changes: 58 additions & 0 deletions packages/keyring-utils/src/scopes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { isScopeEqual, isScopeEqualToAny } from './scopes';

const ETH_EOA = 'eip155:0';
const ETH_MAINNET = 'eip155:1';
const ETH_TESTNET = 'eip155:11155111';
const BTC_MAINNET = 'bip122:000000000019d6689c085ae165831e93';
const BTC_TESTNET = 'bip122:000000000933ea01ad0ee984209779ba';

describe('isScopeEqual', () => {
it('returns true when both scopes are equal', () => {
expect(isScopeEqual(ETH_MAINNET, ETH_MAINNET)).toBe(true);
expect(isScopeEqual(ETH_TESTNET, ETH_TESTNET)).toBe(true);
expect(isScopeEqual(BTC_MAINNET, BTC_MAINNET)).toBe(true);
expect(isScopeEqual(BTC_TESTNET, BTC_TESTNET)).toBe(true);
});

it('returns false when both scopes are not equal', () => {
expect(isScopeEqual(ETH_MAINNET, ETH_TESTNET)).toBe(false);
expect(isScopeEqual(ETH_TESTNET, ETH_MAINNET)).toBe(false);
expect(isScopeEqual(BTC_MAINNET, BTC_TESTNET)).toBe(false);
expect(isScopeEqual(BTC_TESTNET, BTC_MAINNET)).toBe(false);
});

it('supports EVM EOA scopes', () => {
expect(isScopeEqual(ETH_MAINNET, ETH_EOA)).toBe(true);
expect(isScopeEqual(ETH_TESTNET, ETH_EOA)).toBe(true);
expect(isScopeEqual(ETH_EOA, ETH_MAINNET)).toBe(true);
expect(isScopeEqual(ETH_EOA, ETH_TESTNET)).toBe(true);

expect(isScopeEqual(ETH_EOA, BTC_MAINNET)).toBe(false);
expect(isScopeEqual(BTC_MAINNET, ETH_EOA)).toBe(false);
});
});

describe('isScopeEqualToAny', () => {
it('returns true when both scopes are equal', () => {
expect(isScopeEqualToAny(ETH_MAINNET, [ETH_TESTNET, ETH_MAINNET])).toBe(
true,
);
expect(isScopeEqualToAny(BTC_MAINNET, [BTC_MAINNET, BTC_TESTNET])).toBe(
true,
);
});

it('returns false when both scopes are not equal', () => {
expect(isScopeEqualToAny(ETH_MAINNET, [ETH_TESTNET])).toBe(false);
expect(isScopeEqualToAny(ETH_MAINNET, [])).toBe(false);
expect(isScopeEqualToAny(BTC_MAINNET, [ETH_MAINNET])).toBe(false);
});

it('supports EVM EOA scopes', () => {
expect(isScopeEqualToAny(ETH_EOA, [ETH_TESTNET, ETH_MAINNET])).toBe(true);
expect(isScopeEqualToAny(ETH_MAINNET, [ETH_TESTNET, ETH_EOA])).toBe(true);

expect(isScopeEqualToAny(BTC_MAINNET, [ETH_TESTNET, ETH_EOA])).toBe(false);
expect(isScopeEqualToAny(ETH_EOA, [BTC_MAINNET, BTC_TESTNET])).toBe(false);
});
});
50 changes: 50 additions & 0 deletions packages/keyring-utils/src/scopes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { CaipChainId } from '@metamask/utils';
import { KnownCaipNamespace } from '@metamask/utils';

// We do not uses the `EthScope` from `@metamask/keyring-api` here, to avoid pulling it
// as a dependency.
export const ETH_SCOPE_EOA = `${KnownCaipNamespace.Eip155}:0`;

// We use a string prefix when comparing EOAs to avoid unecessary splits.
const ETH_SCOPE_PREFIX = `${KnownCaipNamespace.Eip155}:`;

/**
* Check if scope is compatible with a list of scopes. It also supports the special
* case of `eip155:0` for EVM EOA chain ID which is compatible with any EVM chain
* ID (`eip155:*`).
*
* @param scope - The scope (CAIP-2 chain ID) to check.
* @param other - Another scope to compare to.
* @returns True if both scope are compatible, false otherwise.
*/
export function isScopeEqual(scope: CaipChainId, other: CaipChainId): boolean {
const isScopeEoa = scope === ETH_SCOPE_EOA;
const isOtherEoa = other === ETH_SCOPE_EOA;

// Special case for EOA scopes (we check on both sides);
if (isScopeEoa) {
return other.startsWith(ETH_SCOPE_PREFIX);
}
if (isOtherEoa) {
return scope.startsWith(ETH_SCOPE_PREFIX);
}

// Normal case, if both scope stricly matches, then they are compatible.
return scope === other;
}

/**
* Check if scope is compatible with a list of scopes. It also supports the special
* case of `eip155:0` for EVM EOA chain ID which is compatible with any EVM chain
* ID (`eip155:*`).
*
* @param scope - The scope (CAIP-2 chain ID) to check.
* @param scopes - The list of scopes to check against.
* @returns True if the scope is compatible within the list of scopes.
*/
export function isScopeEqualToAny(
scope: CaipChainId,
scopes: CaipChainId[],
): boolean {
return scopes.some((other) => isScopeEqual(scope, other));
}

0 comments on commit 7360e49

Please sign in to comment.