Skip to content

Commit c237ed7

Browse files
jiexiffmcgee725
andauthored
feat: Add CAIP-294 targets to wallet_announce event data. Announce CAIP-348 target in initializeProvider (#413)
* add optional targets to CAIP-294 wallet data event * export new CAIP294Target type * announce caip-348 * update jest coverage * Update src/CAIP294.ts Co-authored-by: ffmcgee <51971598+ffmcgee725@users.noreply.github.com> --------- Co-authored-by: ffmcgee <51971598+ffmcgee725@users.noreply.github.com>
1 parent fe58406 commit c237ed7

6 files changed

Lines changed: 96 additions & 24 deletions

File tree

jest.config.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ const baseConfig = {
4545
// An object that configures minimum threshold enforcement for coverage results
4646
coverageThreshold: {
4747
global: {
48-
branches: 70.2,
49-
functions: 72.07,
50-
lines: 71.03,
51-
statements: 71.16,
48+
branches: 70.68,
49+
functions: 72.32,
50+
lines: 71.27,
51+
statements: 71.39,
5252
},
5353
},
5454

src/CAIP294.test.ts

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ const getWalletData = (): CAIP294WalletData => ({
1010
name: 'Example Wallet',
1111
icon: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"/>',
1212
rdns: 'com.example.wallet',
13-
extensionId: 'abcdefghijklmnopqrstuvwxyz',
13+
targets: [
14+
{
15+
type: 'caip-123',
16+
value: 'abcdefghijklmnopqrstuvwxyz',
17+
},
18+
],
1419
});
1520

1621
const walletDataValidationError = () =>
@@ -88,26 +93,56 @@ describe('CAIP-294', () => {
8893
});
8994
});
9095

91-
it('allows `extensionId` to be undefined or a string', () => {
96+
it('allows `targets` to be undefined', () => {
9297
const walletInfo = getWalletData();
9398
expect(() => announceWallet(walletInfo)).not.toThrow();
9499

95-
delete walletInfo.extensionId;
100+
delete walletInfo.targets;
101+
expect(() => announceWallet(walletInfo)).not.toThrow();
102+
});
96103

104+
it('allows `targets` to be empty array', () => {
105+
const walletInfo = getWalletData();
97106
expect(() => announceWallet(walletInfo)).not.toThrow();
98107

99-
walletInfo.extensionId = 'valid-string';
108+
walletInfo.targets = [];
100109
expect(() => announceWallet(walletInfo)).not.toThrow();
101110
});
102-
});
103111

104-
it('throws if the `extensionId` field is invalid', () => {
105-
[null, '', 42, Symbol('bar')].forEach((invalidExtensionId) => {
106-
const walletInfo = getWalletData();
107-
walletInfo.extensionId = invalidExtensionId as any;
112+
it('throws if the `targets` field is invalid', () => {
113+
[null, '', 42, Symbol('bar'), {}].forEach((invalidTargets) => {
114+
const walletInfo = getWalletData();
115+
walletInfo.targets = invalidTargets as any;
116+
117+
expect(() => announceWallet(walletInfo)).toThrow(
118+
walletDataValidationError(),
119+
);
120+
});
121+
});
122+
123+
it('throws if the `targets` field contains invalid targets', () => {
124+
[undefined, null, '', 42, Symbol('bar'), [], {}].forEach(
125+
(invalidTarget) => {
126+
const walletInfo = getWalletData();
127+
walletInfo.targets = [invalidTarget as any];
128+
129+
expect(() => announceWallet(walletInfo)).toThrow(
130+
walletDataValidationError(),
131+
);
132+
},
133+
);
134+
});
135+
136+
it('throws if the `targets` field contains invalid target types', () => {
137+
[undefined, null, '', 42, Symbol('bar'), [], {}].forEach(
138+
(invalidTargetType) => {
139+
const walletInfo = getWalletData();
140+
walletInfo.targets = [{ type: invalidTargetType as any }];
108141

109-
expect(() => announceWallet(walletInfo)).toThrow(
110-
walletDataValidationError(),
142+
expect(() => announceWallet(walletInfo)).toThrow(
143+
walletDataValidationError(),
144+
);
145+
},
111146
);
112147
});
113148
});

src/CAIP294.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,25 @@ declare global {
1919
}
2020
}
2121

22+
/**
23+
* Represents the protocol/transport supported by the wallet.
24+
* @type CAIP294Target
25+
* @property type - The type of the target. SHOULD reference a CAIP number in the `caip-x` format.
26+
* @property value - The value specifying how to connect to the target as specified by the specification in the `type` property.
27+
*/
28+
export type CAIP294Target = { type: string; value?: unknown };
29+
2230
/**
2331
* Represents the assets needed to display and identify a wallet.
2432
* @type CAIP294WalletData
2533
* @property uuid - A locally unique identifier for the wallet. MUST be a v4 UUID.
2634
* @property name - The name of the wallet.
2735
* @property icon - The icon for the wallet. MUST be data URI.
2836
* @property rdns - The reverse syntax domain name identifier for the wallet.
29-
* @property extensionId - The canonical extension ID of the wallet provider for the active browser.
37+
* @property targets - The target objects specifying the protocol/transport supported by the wallet.
3038
*/
3139
export type CAIP294WalletData = BaseProviderInfo & {
32-
extensionId?: string | undefined;
40+
targets?: CAIP294Target[];
3341
};
3442

3543
/**
@@ -120,6 +128,16 @@ function isValidAnnounceWalletEvent(
120128
);
121129
}
122130

131+
/**
132+
* Validates a {@link CAIP294Target} object.
133+
*
134+
* @param data - The {@link CAIP294Target} to validate.
135+
* @returns Whether the {@link CAIP294Target} is valid.
136+
*/
137+
function isValidWalletTarget(data: unknown): data is CAIP294Target {
138+
return isObject(data) && typeof data.type === 'string' && Boolean(data.type);
139+
}
140+
123141
/**
124142
* Validates an {@link CAIP294WalletData} object.
125143
*
@@ -137,8 +155,8 @@ function isValidWalletData(data: unknown): data is CAIP294WalletData {
137155
data.icon.startsWith('data:image') &&
138156
typeof data.rdns === 'string' &&
139157
FQDN_REGEX.test(data.rdns) &&
140-
(data.extensionId === undefined ||
141-
(typeof data.extensionId === 'string' && data.extensionId.length > 0))
158+
(data.targets === undefined ||
159+
(Array.isArray(data.targets) && data.targets.every(isValidWalletTarget)))
142160
);
143161
}
144162

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { RequestArguments } from './BaseProvider';
33
import type {
44
CAIP294AnnounceWalletEvent,
55
CAIP294WalletData,
6+
CAIP294Target,
67
CAIP294RequestWalletEvent,
78
} from './CAIP294';
89
import {
@@ -41,6 +42,7 @@ export type {
4142
EIP6963RequestProviderEvent,
4243
CAIP294AnnounceWalletEvent,
4344
CAIP294WalletData as CAIP294WalletInfo,
45+
CAIP294Target,
4446
CAIP294RequestWalletEvent,
4547
};
4648

src/initializeInpageProvider.test.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('announceCaip294WalletData', () => {
4949
});
5050

5151
describe('build type is flask', () => {
52-
it('should announce wallet with extensionId for non-firefox browsers', async () => {
52+
it('should announce wallet with caip-348 target for chromium browsers', async () => {
5353
const extensionId = 'test-extension-id';
5454
(getBuildType as jest.Mock).mockReturnValue('flask');
5555
(mockProvider.request as jest.Mock).mockReturnValue({ extensionId });
@@ -59,18 +59,26 @@ describe('announceCaip294WalletData', () => {
5959
expect(getBuildType).toHaveBeenCalledWith(mockProviderInfo.rdns);
6060
expect(announceWallet).toHaveBeenCalledWith({
6161
...mockProviderInfo,
62-
extensionId,
62+
targets: [
63+
{
64+
type: 'caip-348',
65+
value: extensionId,
66+
},
67+
],
6368
});
6469
});
6570

66-
it('should announce wallet without extensionId for firefox browser', async () => {
71+
it('should announce wallet without caip-348 target for firefox browser', async () => {
6772
(getBuildType as jest.Mock).mockReturnValue('flask');
6873
(mockProvider.request as jest.Mock).mockReturnValue({});
6974

7075
await announceCaip294WalletData(mockProvider, mockProviderInfo);
7176

7277
expect(getBuildType).toHaveBeenCalledWith(mockProviderInfo.rdns);
73-
expect(announceWallet).toHaveBeenCalledWith(mockProviderInfo);
78+
expect(announceWallet).toHaveBeenCalledWith({
79+
...mockProviderInfo,
80+
targets: [],
81+
});
7482
});
7583
});
7684
});

src/initializeInpageProvider.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,20 @@ export async function announceCaip294WalletData(
122122
const providerState = await provider.request<{ extensionId?: string }>({
123123
method: 'metamask_getProviderState',
124124
});
125+
126+
const targets = [];
127+
125128
const extensionId = providerState?.extensionId;
129+
if (extensionId) {
130+
targets.push({
131+
type: 'caip-348',
132+
value: extensionId,
133+
});
134+
}
126135

127136
const walletData = {
128137
...providerInfo,
129-
extensionId,
138+
targets,
130139
};
131140

132141
announceWallet(walletData);

0 commit comments

Comments
 (0)