Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions providers/universal-provider/src/utils/caip25.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SessionTypes } from "@walletconnect/types";
import { isValidObject } from "@walletconnect/utils";
import { isValidObject, parseAccountId } from "@walletconnect/utils";

import { isValidJSONObject } from "./misc.js";

Expand Down Expand Up @@ -44,7 +44,7 @@ export const extractCapabilitiesFromSession = (
address: string,
chainIds: string[],
) => {
const { sessionProperties = {}, scopedProperties = {} } = session;
const { sessionProperties = {}, scopedProperties = {}, namespaces = {} } = session;
const result: Record<string, any> = {};

if (!isValidObject(scopedProperties) && !isValidObject(sessionProperties)) {
Expand All @@ -54,25 +54,31 @@ export const extractCapabilitiesFromSession = (
// get all capabilities from sessionProperties as they apply to all chains/addresses
const globalCapabilities = getCapabilitiesFromObject(sessionProperties);

for (const chain of chainIds) {
const namespaceChainIds = new Set<string>();
for (const account of namespaces[EIP155_PREFIX]?.accounts ?? []) {
const params = parseAccountId(account);
if (params.address === address) {
namespaceChainIds.add(decimalToHex(params.reference));
}
}

// fallback to namespace chainIds if no chainIds are provided
const targetChainIds = chainIds.length > 0 ? chainIds : Array.from(namespaceChainIds);

for (const chain of targetChainIds) {
const chainId = hexToDecimal(chain);
if (!chainId) {
continue;
}

result[decimalToHex(chainId)] = globalCapabilities;

const chainSpecific = scopedProperties?.[`${EIP155_PREFIX}:${chainId}`];
const addressSpecific = chainSpecific?.[`${EIP155_PREFIX}:${chainId}:${address}`];

if (chainSpecific) {
const addressSpecific = chainSpecific?.[`${EIP155_PREFIX}:${chainId}:${address}`];

// use the address specific capabilities if they exist, otherwise use the chain specific capabilities
result[decimalToHex(chainId)] = {
...result[decimalToHex(chainId)],
...getCapabilitiesFromObject(addressSpecific || chainSpecific),
};
}
result[decimalToHex(chainId)] = {
...globalCapabilities,
...(chainSpecific ? getCapabilitiesFromObject(chainSpecific) : {}),
...(addressSpecific ? getCapabilitiesFromObject(addressSpecific) : {}),
};
}

// remove any chains that have no capabilities
Expand Down
113 changes: 113 additions & 0 deletions providers/universal-provider/test/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,4 +389,117 @@ describe("UniversalProvider utils", function () {
},
});
});

it("should extract capabilities from session. Case 9", function () {
const session = {
namespaces: {
eip155: {
chains: ["eip155:1", "eip155:137", "eip155:84532"],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
chains: ["eip155:1", "eip155:137", "eip155:84532"],
accounts: [
"eip155:137:0x0910e12C68d02B561a34569E1367c9AAb42bd811",
"eip155:84532:0x0910e12C68d02B561a34569E1367c9AAb42bd810",
],

accounts: [
"eip155:1:0x0910e12C68d02B561a34569E1367c9AAb42bd811",
"eip155:137:0x0910e12C68d02B561a34569E1367c9AAb42bd811",
"eip155:84532:0x0910e12C68d02B561a34569E1367c9AAb42bd810",
],
},
},
scopedProperties: {
"eip155:1": {
atomic: {
status: "unsupported",
},
},
"eip155:137": {
paymasterService: {
supported: true,
},
"eip155:137:0x0910e12C68d02B561a34569E1367c9AAb42bd811": {
atomic: {
status: "supported",
},
},
},
"eip155:84532": {
"eip155:84532:0x0910e12C68d02B561a34569E1367c9AAb42bd810": {
atomic: {
status: "supported",
},
},
},
},
} as unknown as SessionTypes.Struct;

const capabilities = extractCapabilitiesFromSession(
session,
"0x0910e12C68d02B561a34569E1367c9AAb42bd811",
[],
);

expect(capabilities).toEqual({
"0x1": {
atomic: {
status: "unsupported",
},
},
Comment on lines +438 to +442
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"0x1": {
atomic: {
status: "unsupported",
},
},

with the change to loop through the accounts instead of chains, this 0x1 capabilty no longer applies

"0x89": {
paymasterService: {
supported: true,
},
atomic: {
status: "supported",
},
},
});
});

it("should extract capabilities from session. Case 10", function () {
const session = {
namespaces: {
eip155: {
chains: ["eip155:1", "eip155:137", "eip155:84532"],
accounts: [
"eip155:137:0x0910e12C68d02B561a34569E1367c9AAb42bd811",
"eip155:84532:0x0910e12C68d02B561a34569E1367c9AAb42bd810",
],
},
},
scopedProperties: {
"eip155:1": {
atomic: {
status: "unsupported",
},
},
"eip155:137": {
paymasterService: {
supported: true,
},
"eip155:137:0x0910e12C68d02B561a34569E1367c9AAb42bd810": {
atomic: {
status: "supported",
},
},
},
"eip155:84532": {
"eip155:84532:0x0910e12C68d02B561a34569E1367c9AAb42bd810": {
atomic: {
status: "supported",
},
},
},
},
} as unknown as SessionTypes.Struct;

const capabilities = extractCapabilitiesFromSession(
session,
"0x0910e12C68d02B561a34569E1367c9AAb42bd811",
[],
);

expect(capabilities).toEqual({
"0x89": {
paymasterService: {
supported: true,
},
},
});
});
});