diff --git a/CHANGELOG.md b/CHANGELOG.md index 55c9b72e1c0..b3b3a31194a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - changed: WalletConnect Connect button pinned to the bottom of Confirm Connection - fixed: A send whose funds are not spendable yet says so instead of reporting a network error - fixed: A swap retried after a failure fetches a fresh quote instead of failing with a "closed proxy" error +- fixed: WalletConnect Bitcoin message signing returns a BIP-322 signature when the dapp requests `bip322` - fixed: Auto-login starting two competing accounts when both `YOLO_PASSWORD` and `YOLO_PIN` are set, and attempting a login when either is set to an empty string. - fixed: Auto-login never running on a device with no accounts, since the welcome carousel took priority over the login scene that owns it. - fixed: USDC.e shown as USDC in the Optimism Tarot staking pools diff --git a/src/components/modals/WcSignMessageModal.tsx b/src/components/modals/WcSignMessageModal.tsx index 5ced15f4341..d1a8f429233 100644 --- a/src/components/modals/WcSignMessageModal.tsx +++ b/src/components/modals/WcSignMessageModal.tsx @@ -26,6 +26,8 @@ interface Props { dAppIcon: string dAppName: string message: string + /** The bip122 signing protocol the dapp asked for. */ + protocol: 'ecdsa' | 'bip322' /** The address the session advertised, which is the one the dapp verifies * the signature against. */ publicAddress: string @@ -47,6 +49,7 @@ export const WcSignMessageModal: React.FC = props => { dAppIcon, dAppName, message, + protocol, publicAddress, requestId, topic, @@ -70,12 +73,14 @@ export const WcSignMessageModal: React.FC = props => { try { // `signMessage` signs the literal UTF-8 message, which is what the dapp // verifies. `signBytes` would base64-re-encode first and sign the wrong - // data. BIP137 encodes the signing address' script type in the header - // byte, which SegWit verifiers require and which collapses to the legacy - // encoding for non-SegWit addresses. + // data. For `ecdsa`, BIP137 encodes the signing address' script type in + // the header byte, which SegWit verifiers require and which collapses to + // the legacy encoding for non-SegWit addresses. `bip322` is the BIP-322 + // simple signature, a witness stack rather than a recoverable signature. + const signatureFormat = protocol === 'bip322' ? 'bip322' : 'bip137' // eslint-disable-next-line @typescript-eslint/no-deprecated const signatureBase64 = await wallet.signMessage(message, { - otherParams: { publicAddress, signatureFormat: 'bip137' } + otherParams: { publicAddress, signatureFormat } }) await walletConnect.approveRequest(topic, requestId, { address: publicAddress, diff --git a/src/components/services/WalletConnectService.tsx b/src/components/services/WalletConnectService.tsx index beb74c40c23..23f261efefe 100644 --- a/src/components/services/WalletConnectService.tsx +++ b/src/components/services/WalletConnectService.tsx @@ -3,7 +3,14 @@ import '@walletconnect/react-native-compat' import { Core } from '@walletconnect/core' import type { SessionTypes } from '@walletconnect/types' import Web3Wallet, { type Web3WalletTypes } from '@walletconnect/web3wallet' -import { asNumber, asObject, asOptional, asString, asUnknown } from 'cleaners' +import { + asNumber, + asObject, + asOptional, + asString, + asUnknown, + asValue +} from 'cleaners' import type { EdgeAccount, EdgeCurrencyWallet } from 'edge-core-js' import * as React from 'react' @@ -17,7 +24,7 @@ import { walletConnectClient } from '../../hooks/useWalletConnect' import { globalKeys } from '../../keys' -import { asLegacyTokenId } from '../../types/types' +import { asEdgeTokenId } from '../../types/types' import { snooze } from '../../util/utils' import { readActiveSessionWallets } from '../../util/walletConnectSessionStore' import { WcSignMessageModal } from '../modals/WcSignMessageModal' @@ -69,16 +76,20 @@ export const WalletConnectService: React.FC = props => { return } case 'signMessage': { - const { account: requestedAccount, message } = - asBip122SignMessageParams(payload.params) + const { + account: requestedAccount, + address: requestedAddress, + message, + protocol + } = asBip122SignMessageParams(payload.params) // A dapp may name the account it wants signed for, as the bare address // or as the CAIP-10 account the session gave it. Edge holds one // address per session, so anything else is unservable. - if ( - requestedAccount != null && - requestedAccount !== publicAddress && - requestedAccount !== sessionAccount - ) { + const isServable = (requested: string | undefined): boolean => + requested == null || + requested === publicAddress || + requested === sessionAccount + if (!isServable(requestedAccount) || !isServable(requestedAddress)) { await walletConnect.rejectRequest(topic, requestId) return } @@ -93,6 +104,7 @@ export const WalletConnectService: React.FC = props => { dAppIcon={dAppIcon} dAppName={session.peer.metadata.name} message={message} + protocol={protocol} publicAddress={publicAddress} requestId={requestId} topic={topic} @@ -251,7 +263,8 @@ export const WalletConnectService: React.FC = props => { const payloadAmounts = asObject({ nativeAmount: asString, networkFee: asString, - tokenId: asLegacyTokenId + // Some chains' payload parsers omit tokenId for the native asset. + tokenId: asOptional(asEdgeTokenId, null) }) const asSessionRequest = asObject({ id: asNumber, @@ -267,5 +280,8 @@ const asBip122Payload = asObject({ }) const asBip122SignMessageParams = asObject({ message: asString, - account: asOptional(asString) + account: asOptional(asString), + address: asOptional(asString), + // The bip122 spec defaults to ECDSA when the dapp names no protocol. + protocol: asOptional(asValue<['ecdsa', 'bip322']>('ecdsa', 'bip322'), 'ecdsa') })