fix: use session-approved chainId for WC signing requests

WC SignClient validates that the request chainId is in the session's
approved namespaces. When the DApp browser creates a session, it may
only approve the relay chain. Signing requests for Asset Hub or People
Chain then fail with "Missing or invalid chainId".

Fix: always use a chainId from the session's approved chains for the
WC request. The wallet determines the actual signing chain from the
transaction payload's genesisHash, not from the WC request chainId.
This commit is contained in:
2026-02-24 20:36:56 +03:00
parent 8b906f0b84
commit 1bedda8006
+28 -8
View File
@@ -23,6 +23,28 @@ let initPromise: Promise<SignClient> | null = null;
let currentSession: SessionTypes.Struct | null = null;
let requestId = 0;
/**
* Get a chainId that is approved in the current WC session.
* The wallet determines actual signing chain from the payload's genesisHash,
* so the WC request chainId is only for WC protocol compliance.
*/
function getApprovedChainId(fallback: string): string {
if (!currentSession) return fallback;
const ns = currentSession.namespaces['polkadot'];
if (!ns) return fallback;
// Try chains array first
if (ns.chains && ns.chains.length > 0) return ns.chains[0];
// Derive from accounts (format: polkadot:<chain_id>:<address>)
if (ns.accounts && ns.accounts.length > 0) {
const parts = ns.accounts[0].split(':');
if (parts.length >= 2) return `${parts[0]}:${parts[1]}`;
}
return fallback;
}
/**
* Initialize the WalletConnect SignClient (singleton with race protection)
*/
@@ -152,10 +174,12 @@ export function getSessionAccounts(): string[] {
if (!polkadotNamespace?.accounts) return [];
// Account format: polkadot:<chain_id>:<ss58_address>
return polkadotNamespace.accounts.map((account) => {
// Same address appears once per chain, deduplicate
const addresses = polkadotNamespace.accounts.map((account) => {
const parts = account.split(':');
return parts[parts.length - 1]; // Last part is the SS58 address
});
return [...new Set(addresses)];
}
/**
@@ -215,15 +239,11 @@ export function createWCSigner(defaultGenesisHash: string, address: string) {
throw new Error('WalletConnect session expired. Please reconnect your wallet.');
}
// Derive chainId from the payload's own genesisHash (set by the TX's API)
// This ensures Asset Hub TXs use Asset Hub chainId, relay TXs use relay chainId, etc.
const chainId = payload.genesisHash ? buildChainId(payload.genesisHash) : defaultChainId;
// Always use a chainId approved in the WC session for protocol compliance.
// The wallet determines actual signing chain from the payload's genesisHash.
const chainId = getApprovedChainId(defaultChainId);
const wcAccount = `polkadot:${chainId.split(':')[1]}:${address}`;
if (import.meta.env.DEV) {
console.log('[WC] signPayload chainId:', chainId, 'from payload genesis:', payload.genesisHash);
}
const id = ++requestId;
const result = await signClient.request<{ signature: string }>({