diff --git a/providers/universal-provider/src/utils/caip25.ts b/providers/universal-provider/src/utils/caip25.ts index c59267bee..ce3849c42 100644 --- a/providers/universal-provider/src/utils/caip25.ts +++ b/providers/universal-provider/src/utils/caip25.ts @@ -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"; @@ -44,7 +44,7 @@ export const extractCapabilitiesFromSession = ( address: string, chainIds: string[], ) => { - const { sessionProperties = {}, scopedProperties = {} } = session; + const { sessionProperties = {}, scopedProperties = {}, namespaces = {} } = session; const result: Record = {}; if (!isValidObject(scopedProperties) && !isValidObject(sessionProperties)) { @@ -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(); + 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 diff --git a/providers/universal-provider/test/utils.spec.ts b/providers/universal-provider/test/utils.spec.ts index fb2185252..1bd557225 100644 --- a/providers/universal-provider/test/utils.spec.ts +++ b/providers/universal-provider/test/utils.spec.ts @@ -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"], + 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", + }, + }, + "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, + }, + }, + }); + }); });