FRE-600: Fix code review blockers

- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-04-25 00:08:01 -04:00
parent 65b552bb08
commit 7c684a42cc
48450 changed files with 5679671 additions and 383 deletions

178
node_modules/viem/utils/siwe/createSiweMessage.ts generated vendored Normal file
View File

@@ -0,0 +1,178 @@
import {
SiweInvalidMessageFieldError,
type SiweInvalidMessageFieldErrorType,
} from '../../errors/siwe.js'
import type { ErrorType } from '../../errors/utils.js'
import { type GetAddressErrorType, getAddress } from '../address/getAddress.js'
import type { SiweMessage } from './types.js'
import { isUri } from './utils.js'
export type CreateSiweMessageParameters = SiweMessage
export type CreateSiweMessageReturnType = string
export type CreateSiweMessageErrorType =
| GetAddressErrorType
| SiweInvalidMessageFieldErrorType
| ErrorType
/**
* @description Creates EIP-4361 formatted message.
*
* @example
* const message = createMessage({
* address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
* chainId: 1,
* domain: 'example.com',
* nonce: 'foobarbaz',
* uri: 'https://example.com/path',
* version: '1',
* })
*
* @see https://eips.ethereum.org/EIPS/eip-4361
*/
export function createSiweMessage(
parameters: CreateSiweMessageParameters,
): CreateSiweMessageReturnType {
const {
chainId,
domain,
expirationTime,
issuedAt = new Date(),
nonce,
notBefore,
requestId,
resources,
scheme,
uri,
version,
} = parameters
// Validate fields
{
// Required fields
if (chainId !== Math.floor(chainId))
throw new SiweInvalidMessageFieldError({
field: 'chainId',
metaMessages: [
'- Chain ID must be a EIP-155 chain ID.',
'- See https://eips.ethereum.org/EIPS/eip-155',
'',
`Provided value: ${chainId}`,
],
})
if (
!(
domainRegex.test(domain) ||
ipRegex.test(domain) ||
localhostRegex.test(domain)
)
)
throw new SiweInvalidMessageFieldError({
field: 'domain',
metaMessages: [
'- Domain must be an RFC 3986 authority.',
'- See https://www.rfc-editor.org/rfc/rfc3986',
'',
`Provided value: ${domain}`,
],
})
if (!nonceRegex.test(nonce))
throw new SiweInvalidMessageFieldError({
field: 'nonce',
metaMessages: [
'- Nonce must be at least 8 characters.',
'- Nonce must be alphanumeric.',
'',
`Provided value: ${nonce}`,
],
})
if (!isUri(uri))
throw new SiweInvalidMessageFieldError({
field: 'uri',
metaMessages: [
'- URI must be a RFC 3986 URI referring to the resource that is the subject of the signing.',
'- See https://www.rfc-editor.org/rfc/rfc3986',
'',
`Provided value: ${uri}`,
],
})
if (version !== '1')
throw new SiweInvalidMessageFieldError({
field: 'version',
metaMessages: [
"- Version must be '1'.",
'',
`Provided value: ${version}`,
],
})
// Optional fields
if (scheme && !schemeRegex.test(scheme))
throw new SiweInvalidMessageFieldError({
field: 'scheme',
metaMessages: [
'- Scheme must be an RFC 3986 URI scheme.',
'- See https://www.rfc-editor.org/rfc/rfc3986#section-3.1',
'',
`Provided value: ${scheme}`,
],
})
const statement = parameters.statement
if (statement?.includes('\n'))
throw new SiweInvalidMessageFieldError({
field: 'statement',
metaMessages: [
"- Statement must not include '\\n'.",
'',
`Provided value: ${statement}`,
],
})
}
// Construct message
const address = getAddress(parameters.address)
const origin = (() => {
if (scheme) return `${scheme}://${domain}`
return domain
})()
const statement = (() => {
if (!parameters.statement) return ''
return `${parameters.statement}\n`
})()
const prefix = `${origin} wants you to sign in with your Ethereum account:\n${address}\n\n${statement}`
let suffix = `URI: ${uri}\nVersion: ${version}\nChain ID: ${chainId}\nNonce: ${nonce}\nIssued At: ${issuedAt.toISOString()}`
if (expirationTime)
suffix += `\nExpiration Time: ${expirationTime.toISOString()}`
if (notBefore) suffix += `\nNot Before: ${notBefore.toISOString()}`
if (requestId) suffix += `\nRequest ID: ${requestId}`
if (resources) {
let content = '\nResources:'
for (const resource of resources) {
if (!isUri(resource))
throw new SiweInvalidMessageFieldError({
field: 'resources',
metaMessages: [
'- Every resource must be a RFC 3986 URI.',
'- See https://www.rfc-editor.org/rfc/rfc3986',
'',
`Provided value: ${resource}`,
],
})
content += `\n- ${resource}`
}
suffix += content
}
return `${prefix}\n${suffix}`
}
const domainRegex =
/^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(:[0-9]{1,5})?$/
const ipRegex =
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(:[0-9]{1,5})?$/
const localhostRegex = /^localhost(:[0-9]{1,5})?$/
const nonceRegex = /^[a-zA-Z0-9]{8,}$/
const schemeRegex = /^([a-zA-Z][a-zA-Z0-9+-.]*)$/

15
node_modules/viem/utils/siwe/generateSiweNonce.ts generated vendored Normal file
View File

@@ -0,0 +1,15 @@
import { uid } from '../../utils/uid.js'
/**
* @description Generates random EIP-4361 nonce.
*
* @example
* const nonce = generateNonce()
*
* @see https://eips.ethereum.org/EIPS/eip-4361
*
* @returns A randomly generated EIP-4361 nonce.
*/
export function generateSiweNonce(): string {
return uid(96)
}

55
node_modules/viem/utils/siwe/parseSiweMessage.ts generated vendored Normal file
View File

@@ -0,0 +1,55 @@
import type { Address } from 'abitype'
import type { ExactPartial, Prettify } from '../../types/utils.js'
import type { SiweMessage } from './types.js'
/**
* @description Parses EIP-4361 formatted message into message fields object.
*
* @see https://eips.ethereum.org/EIPS/eip-4361
*
* @returns EIP-4361 fields object
*/
export function parseSiweMessage(
message: string,
): Prettify<ExactPartial<SiweMessage>> {
const { scheme, statement, ...prefix } = (message.match(prefixRegex)
?.groups ?? {}) as {
address: Address
domain: string
scheme?: string
statement?: string
}
const { chainId, expirationTime, issuedAt, notBefore, requestId, ...suffix } =
(message.match(suffixRegex)?.groups ?? {}) as {
chainId: string
expirationTime?: string
issuedAt?: string
nonce: string
notBefore?: string
requestId?: string
uri: string
version: '1'
}
const resources = message.split('Resources:')[1]?.split('\n- ').slice(1)
return {
...prefix,
...suffix,
...(chainId ? { chainId: Number(chainId) } : {}),
...(expirationTime ? { expirationTime: new Date(expirationTime) } : {}),
...(issuedAt ? { issuedAt: new Date(issuedAt) } : {}),
...(notBefore ? { notBefore: new Date(notBefore) } : {}),
...(requestId ? { requestId } : {}),
...(resources ? { resources } : {}),
...(scheme ? { scheme } : {}),
...(statement ? { statement } : {}),
}
}
// https://regexr.com/80gdj
const prefixRegex =
/^(?:(?<scheme>[a-zA-Z][a-zA-Z0-9+-.]*):\/\/)?(?<domain>[a-zA-Z0-9+-.]*(?::[0-9]{1,5})?) (?:wants you to sign in with your Ethereum account:\n)(?<address>0x[a-fA-F0-9]{40})\n\n(?:(?<statement>.*)\n\n)?/
// https://regexr.com/80gf9
const suffixRegex =
/(?:URI: (?<uri>.+))\n(?:Version: (?<version>.+))\n(?:Chain ID: (?<chainId>\d+))\n(?:Nonce: (?<nonce>[a-zA-Z0-9]+))\n(?:Issued At: (?<issuedAt>.+))(?:\nExpiration Time: (?<expirationTime>.+))?(?:\nNot Before: (?<notBefore>.+))?(?:\nRequest ID: (?<requestId>.+))?/

61
node_modules/viem/utils/siwe/types.ts generated vendored Normal file
View File

@@ -0,0 +1,61 @@
import type { Address } from 'abitype'
/**
* @description EIP-4361 message fields
*
* @see https://eips.ethereum.org/EIPS/eip-4361
*/
export type SiweMessage = {
/**
* The Ethereum address performing the signing.
*/
address: Address
/**
* The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) Chain ID to which the session is bound,
*/
chainId: number
/**
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) authority that is requesting the signing.
*/
domain: string
/**
* Time when the signed authentication message is no longer valid.
*/
expirationTime?: Date | undefined
/**
* Time when the message was generated, typically the current time.
*/
issuedAt?: Date | undefined
/**
* A random string typically chosen by the relying party and used to prevent replay attacks.
*/
nonce: string
/**
* Time when the signed authentication message will become valid.
*/
notBefore?: Date | undefined
/**
* A system-specific identifier that may be used to uniquely refer to the sign-in request.
*/
requestId?: string | undefined
/**
* A list of information or references to information the user wishes to have resolved as part of authentication by the relying party.
*/
resources?: string[] | undefined
/**
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) URI scheme of the origin of the request.
*/
scheme?: string | undefined
/**
* A human-readable ASCII assertion that the user will sign.
*/
statement?: string | undefined
/**
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) URI referring to the resource that is the subject of the signing (as in the subject of a claim).
*/
uri: string
/**
* The current version of the SIWE Message.
*/
version: '1'
}

50
node_modules/viem/utils/siwe/utils.ts generated vendored Normal file
View File

@@ -0,0 +1,50 @@
export function isUri(value: string) {
// based on https://github.com/ogt/valid-url
// check for illegal characters
if (/[^a-z0-9:/?#[\]@!$&'()*+,;=.\-_~%]/i.test(value)) return false
// check for hex escapes that aren't complete
if (/%[^0-9a-f]/i.test(value)) return false
if (/%[0-9a-f](:?[^0-9a-f]|$)/i.test(value)) return false
// from RFC 3986
const splitted = splitUri(value)
const scheme = splitted[1]
const authority = splitted[2]
const path = splitted[3]
const query = splitted[4]
const fragment = splitted[5]
// scheme and path are required, though the path can be empty
if (!(scheme?.length && path.length >= 0)) return false
// if authority is present, the path must be empty or begin with a /
if (authority?.length) {
if (!(path.length === 0 || /^\//.test(path))) return false
} else {
// if authority is not present, the path must not start with //
if (/^\/\//.test(path)) return false
}
// scheme must begin with a letter, then consist of letters, digits, +, ., or -
if (!/^[a-z][a-z0-9+\-.]*$/.test(scheme.toLowerCase())) return false
let out = ''
// re-assemble the URL per section 5.3 in RFC 3986
out += `${scheme}:`
if (authority?.length) out += `//${authority}`
out += path
if (query?.length) out += `?${query}`
if (fragment?.length) out += `#${fragment}`
return out
}
function splitUri(value: string) {
return value.match(
/(?:([^:/?#]+):)?(?:\/\/([^/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/,
)!
}

72
node_modules/viem/utils/siwe/validateSiweMessage.ts generated vendored Normal file
View File

@@ -0,0 +1,72 @@
import type { Address } from 'abitype'
import type { ExactPartial } from '../../types/utils.js'
import { isAddress } from '../address/isAddress.js'
import { isAddressEqual } from '../address/isAddressEqual.js'
import type { SiweMessage } from './types.js'
export type ValidateSiweMessageParameters = {
/**
* Ethereum address to check against.
*/
address?: Address | undefined
/**
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) authority to check against.
*/
domain?: string | undefined
/**
* EIP-4361 message fields.
*/
message: ExactPartial<SiweMessage>
/**
* Random string to check against.
*/
nonce?: string | undefined
/**
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) URI scheme to check against.
*/
scheme?: string | undefined
/**
* Current time to check optional `expirationTime` and `notBefore` fields.
*
* @default new Date()
*/
time?: Date | undefined
}
export type ValidateSiweMessageReturnType = boolean
/**
* @description Validates EIP-4361 message.
*
* @see https://eips.ethereum.org/EIPS/eip-4361
*/
export function validateSiweMessage(
parameters: ValidateSiweMessageParameters,
): ValidateSiweMessageReturnType {
const {
address,
domain,
message,
nonce,
scheme,
time = new Date(),
} = parameters
if (domain && message.domain !== domain) return false
if (nonce && message.nonce !== nonce) return false
if (scheme && message.scheme !== scheme) return false
if (message.expirationTime && time >= message.expirationTime) return false
if (message.notBefore && time < message.notBefore) return false
try {
if (!message.address) return false
if (!isAddress(message.address, { strict: false })) return false
if (address && !isAddressEqual(message.address, address)) return false
} catch {
return false
}
return true
}