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
7 changes: 7 additions & 0 deletions .changeset/siwx-eip4361-message-format.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@reown/appkit-controllers': patch
---

Fix SIWX sign-in failing on EVM with Phantom under Reown Cloud Auth ("The app's signature request cannot be shown due to invalid formatting"). The `ReownAuthentication` SIWE message was not EIP-4361-compliant for EVM: with no statement it emitted a single blank line after the address instead of the two the spec requires (the empty statement block must stay delimited), and it used the CAIP-2 chain id (`Chain ID: eip155:1`) instead of the decimal EIP-155 id (`Chain ID: 1`). Strict wallets like Phantom rejected the message before showing the prompt; lenient wallets (MetaMask) signed it anyway.

`ReownAuthenticationMessenger` now emits the compliant blank-line structure and decimal `Chain ID` for EVM. The change is scoped to the EVM (`eip155`) namespace only β€” non-EVM messages (Solana, Bitcoin, ...) are byte-for-byte unchanged. The Reown Cloud Auth backend must accept the compliant message (it validates the message server-side).
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type CaipNetworkId, NetworkUtil } from '@reown/appkit-common'
import { type CaipNetworkId, ConstantsUtil, NetworkUtil } from '@reown/appkit-common'

import { ChainController } from '../../../controllers/ChainController.js'
import type { SIWXMessage } from '../../../utils/SIWXUtil.js'
Expand Down Expand Up @@ -35,15 +35,30 @@
}

private stringify(params: SIWXMessage.Data): string {
const [namespace, reference] = params.chainId.split(':')
const isEvm = namespace === ConstantsUtil.CHAIN.EVM
const networkName = this.getNetworkName(params.chainId)
/*
* EIP-4361 requires the decimal EIP-155 chain id for EVM (e.g. "1"), not the CAIP-2 id
* ("eip155:1"). Strict wallet parsers (e.g. Phantom) reject the CAIP form as "invalid
* formatting". EVM-only β€” non-EVM namespaces keep their CAIP-2 id.
*/
const chainId = isEvm ? reference : params.chainId

return [
`${params.domain} wants you to sign in with your ${networkName} account:`,
params.accountAddress,
params.statement ? `\n${params.statement}\n` : '',
/*
* EIP-4361 (EVM) places the optional statement between two blank lines, so a
* statement-less message must still emit the empty statement's blank line β€” otherwise
* there is a single blank line and strict parsers (e.g. Phantom) reject the message.
* This mirrors the reference `siwe` serializer. EVM-only, so non-EVM message formats
* (Solana, Bitcoin, ...) stay byte-for-byte unchanged.
*/
params.statement ? `\n${params.statement}\n` : isEvm ? '\n' : '',

Check failure on line 58 in packages/controllers/src/features/siwx/reown-authentication/ReownAuthenticationMessenger.ts

View workflow job for this annotation

GitHub Actions / code_style (lint)

Do not nest ternary expressions
`URI: ${params.uri}`,
`Version: ${params.version}`,
`Chain ID: ${params.chainId}`,
`Chain ID: ${chainId}`,
`Nonce: ${params.nonce}`,
params.issuedAt && `Issued At: ${params.issuedAt}`,
params.expirationTime && `Expiration Time: ${params.expirationTime}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,14 @@ describe.each([
`${namespace}:${id}`
)

// EVM (EIP-4361) uses the decimal chain id + two blank lines; non-EVM is unchanged
expect(message.toString())
.toBe(`mocked.com wants you to sign in with your ${networkName} account:
${address}

${namespace === 'eip155' ? '\n' : ''}
URI: http://mocked.com/
Version: 1
Chain ID: ${namespace}:${id}
Chain ID: ${namespace === 'eip155' ? id : `${namespace}:${id}`}
Nonce: mock_nonce
Issued At: 2024-12-05T16:02:32.905Z`)

Expand Down
Loading