You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
844 lines
24 KiB
844 lines
24 KiB
import { secp256k1 } from '@noble/curves/secp256k1'
|
|
import * as Bytes from './Bytes.js'
|
|
import * as Errors from './Errors.js'
|
|
import * as Hex from './Hex.js'
|
|
import type { Compute, ExactPartial, OneOf } from './internal/types.js'
|
|
import * as Json from './Json.js'
|
|
import * as Solidity from './Solidity.js'
|
|
|
|
/** Root type for an ECDSA signature. */
|
|
export type Signature<
|
|
recovered extends boolean = true,
|
|
bigintType = bigint,
|
|
numberType = number,
|
|
> = Compute<
|
|
recovered extends true
|
|
? {
|
|
r: bigintType
|
|
s: bigintType
|
|
yParity: numberType
|
|
}
|
|
: {
|
|
r: bigintType
|
|
s: bigintType
|
|
yParity?: numberType | undefined
|
|
}
|
|
>
|
|
|
|
/** RPC-formatted ECDSA signature. */
|
|
export type Rpc<recovered extends boolean = true> = Signature<
|
|
recovered,
|
|
Hex.Hex,
|
|
Hex.Hex
|
|
>
|
|
|
|
/** (Legacy) ECDSA signature. */
|
|
export type Legacy<bigintType = bigint, numberType = number> = {
|
|
r: bigintType
|
|
s: bigintType
|
|
v: numberType
|
|
}
|
|
|
|
/** RPC-formatted (Legacy) ECDSA signature. */
|
|
export type LegacyRpc = Legacy<Hex.Hex, Hex.Hex>
|
|
|
|
export type Tuple = readonly [yParity: Hex.Hex, r: Hex.Hex, s: Hex.Hex]
|
|
|
|
/**
|
|
* Asserts that a Signature is valid.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* Signature.assert({
|
|
* r: -49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* yParity: 1,
|
|
* })
|
|
* // @error: InvalidSignatureRError:
|
|
* // @error: Value `-549...n` is an invalid r value.
|
|
* // @error: r must be a positive integer less than 2^256.
|
|
* ```
|
|
*
|
|
* @param signature - The signature object to assert.
|
|
*/
|
|
export function assert(
|
|
signature: ExactPartial<Signature>,
|
|
options: assert.Options = {},
|
|
): asserts signature is Signature {
|
|
const { recovered } = options
|
|
if (typeof signature.r === 'undefined')
|
|
throw new MissingPropertiesError({ signature })
|
|
if (typeof signature.s === 'undefined')
|
|
throw new MissingPropertiesError({ signature })
|
|
if (recovered && typeof signature.yParity === 'undefined')
|
|
throw new MissingPropertiesError({ signature })
|
|
if (signature.r < 0n || signature.r > Solidity.maxUint256)
|
|
throw new InvalidRError({ value: signature.r })
|
|
if (signature.s < 0n || signature.s > Solidity.maxUint256)
|
|
throw new InvalidSError({ value: signature.s })
|
|
if (
|
|
typeof signature.yParity === 'number' &&
|
|
signature.yParity !== 0 &&
|
|
signature.yParity !== 1
|
|
)
|
|
throw new InvalidYParityError({ value: signature.yParity })
|
|
}
|
|
|
|
export declare namespace assert {
|
|
type Options = {
|
|
/** Whether or not the signature should be recovered (contain `yParity`). */
|
|
recovered?: boolean
|
|
}
|
|
|
|
type ErrorType =
|
|
| MissingPropertiesError
|
|
| InvalidRError
|
|
| InvalidSError
|
|
| InvalidYParityError
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Deserializes a {@link ox#Bytes.Bytes} signature into a structured {@link ox#Signature.Signature}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* Signature.fromBytes(new Uint8Array([128, 3, 131, ...]))
|
|
* // @log: { r: 5231...n, s: 3522...n, yParity: 0 }
|
|
* ```
|
|
*
|
|
* @param signature - The serialized signature.
|
|
* @returns The deserialized {@link ox#Signature.Signature}.
|
|
*/
|
|
export function fromBytes(signature: Bytes.Bytes): Signature {
|
|
return fromHex(Hex.fromBytes(signature))
|
|
}
|
|
|
|
export declare namespace fromBytes {
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Deserializes a {@link ox#Hex.Hex} signature into a structured {@link ox#Signature.Signature}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* Signature.fromHex('0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c')
|
|
* // @log: { r: 5231...n, s: 3522...n, yParity: 0 }
|
|
* ```
|
|
*
|
|
* @param serialized - The serialized signature.
|
|
* @returns The deserialized {@link ox#Signature.Signature}.
|
|
*/
|
|
export function fromHex(signature: Hex.Hex): Signature {
|
|
if (signature.length !== 130 && signature.length !== 132)
|
|
throw new InvalidSerializedSizeError({ signature })
|
|
|
|
const r = BigInt(Hex.slice(signature, 0, 32))
|
|
const s = BigInt(Hex.slice(signature, 32, 64))
|
|
|
|
const yParity = (() => {
|
|
const yParity = Number(`0x${signature.slice(130)}`)
|
|
if (Number.isNaN(yParity)) return undefined
|
|
try {
|
|
return vToYParity(yParity)
|
|
} catch {
|
|
throw new InvalidYParityError({ value: yParity })
|
|
}
|
|
})()
|
|
|
|
if (typeof yParity === 'undefined')
|
|
return {
|
|
r,
|
|
s,
|
|
} as never
|
|
return {
|
|
r,
|
|
s,
|
|
yParity,
|
|
} as never
|
|
}
|
|
|
|
export declare namespace fromHex {
|
|
type ErrorType =
|
|
| Hex.from.ErrorType
|
|
| InvalidSerializedSizeError
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Extracts a {@link ox#Signature.Signature} from an arbitrary object that may include signature properties.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* Signature.extract({
|
|
* baz: 'barry',
|
|
* foo: 'bar',
|
|
* r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* yParity: 1,
|
|
* zebra: 'stripes',
|
|
* })
|
|
* // @log: {
|
|
* // @log: r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* // @log: s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* // @log: yParity: 1
|
|
* // @log: }
|
|
* ```
|
|
*
|
|
* @param value - The arbitrary object to extract the signature from.
|
|
* @returns The extracted {@link ox#Signature.Signature}.
|
|
*/
|
|
export function extract(value: extract.Value): Signature | undefined {
|
|
if (typeof value.r === 'undefined') return undefined
|
|
if (typeof value.s === 'undefined') return undefined
|
|
return from(value as any)
|
|
}
|
|
|
|
export declare namespace extract {
|
|
type Value = {
|
|
r?: bigint | Hex.Hex | undefined
|
|
s?: bigint | Hex.Hex | undefined
|
|
yParity?: number | Hex.Hex | undefined
|
|
v?: number | Hex.Hex | undefined
|
|
}
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Instantiates a typed {@link ox#Signature.Signature} object from a {@link ox#Signature.Signature}, {@link ox#Signature.Legacy}, {@link ox#Bytes.Bytes}, or {@link ox#Hex.Hex}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* Signature.from({
|
|
* r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* yParity: 1,
|
|
* })
|
|
* // @log: {
|
|
* // @log: r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* // @log: s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* // @log: yParity: 1
|
|
* // @log: }
|
|
* ```
|
|
*
|
|
* @example
|
|
* ### From Serialized
|
|
*
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* Signature.from('0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db801')
|
|
* // @log: {
|
|
* // @log: r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* // @log: s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* // @log: yParity: 1,
|
|
* // @log: }
|
|
* ```
|
|
*
|
|
* @example
|
|
* ### From Legacy
|
|
*
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* Signature.from({
|
|
* r: 47323457007453657207889730243826965761922296599680473886588287015755652701072n,
|
|
* s: 57228803202727131502949358313456071280488184270258293674242124340113824882788n,
|
|
* v: 27,
|
|
* })
|
|
* // @log: {
|
|
* // @log: r: 47323457007453657207889730243826965761922296599680473886588287015755652701072n,
|
|
* // @log: s: 57228803202727131502949358313456071280488184270258293674242124340113824882788n,
|
|
* // @log: yParity: 0
|
|
* // @log: }
|
|
* ```
|
|
*
|
|
* @param signature - The signature value to instantiate.
|
|
* @returns The instantiated {@link ox#Signature.Signature}.
|
|
*/
|
|
export function from<
|
|
const signature extends
|
|
| OneOf<Signature<boolean> | Rpc<boolean> | Legacy | LegacyRpc>
|
|
| Hex.Hex
|
|
| Bytes.Bytes,
|
|
>(
|
|
signature:
|
|
| signature
|
|
| OneOf<Signature<boolean> | Rpc<boolean> | Legacy | LegacyRpc>
|
|
| Hex.Hex
|
|
| Bytes.Bytes,
|
|
): from.ReturnType<signature> {
|
|
const signature_ = (() => {
|
|
if (typeof signature === 'string') return fromHex(signature)
|
|
if (signature instanceof Uint8Array) return fromBytes(signature)
|
|
if (typeof signature.r === 'string') return fromRpc(signature)
|
|
if (signature.v) return fromLegacy(signature)
|
|
return {
|
|
r: signature.r,
|
|
s: signature.s,
|
|
...(typeof signature.yParity !== 'undefined'
|
|
? { yParity: signature.yParity }
|
|
: {}),
|
|
}
|
|
})()
|
|
assert(signature_)
|
|
return signature_ as never
|
|
}
|
|
|
|
export declare namespace from {
|
|
type ReturnType<
|
|
signature extends
|
|
| OneOf<Signature<boolean> | Rpc<boolean> | Legacy | LegacyRpc>
|
|
| Hex.Hex
|
|
| Bytes.Bytes,
|
|
> = signature extends Signature<boolean> & { v?: undefined }
|
|
? signature
|
|
: Signature
|
|
|
|
type ErrorType =
|
|
| assert.ErrorType
|
|
| fromBytes.ErrorType
|
|
| fromHex.ErrorType
|
|
| vToYParity.ErrorType
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a DER-encoded signature to a {@link ox#Signature.Signature}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* // @noErrors
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.fromDerBytes(new Uint8Array([132, 51, 23, ...]))
|
|
* // @log: {
|
|
* // @log: r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* // @log: s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* // @log: }
|
|
* ```
|
|
*
|
|
* @param signature - The DER-encoded signature to convert.
|
|
* @returns The {@link ox#Signature.Signature}.
|
|
*/
|
|
export function fromDerBytes(signature: Bytes.Bytes): Signature<false> {
|
|
return fromDerHex(Hex.fromBytes(signature))
|
|
}
|
|
|
|
export declare namespace fromDerBytes {
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a DER-encoded signature to a {@link ox#Signature.Signature}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.fromDerHex('0x304402206e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf02204a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8')
|
|
* // @log: {
|
|
* // @log: r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* // @log: s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* // @log: }
|
|
* ```
|
|
*
|
|
* @param signature - The DER-encoded signature to convert.
|
|
* @returns The {@link ox#Signature.Signature}.
|
|
*/
|
|
export function fromDerHex(signature: Hex.Hex): Signature<false> {
|
|
const { r, s } = secp256k1.Signature.fromDER(Hex.from(signature).slice(2))
|
|
return { r, s }
|
|
}
|
|
|
|
export declare namespace fromDerHex {
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a {@link ox#Signature.Legacy} into a {@link ox#Signature.Signature}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const legacy = Signature.fromLegacy({ r: 1n, s: 2n, v: 28 })
|
|
* // @log: { r: 1n, s: 2n, yParity: 1 }
|
|
* ```
|
|
*
|
|
* @param signature - The {@link ox#Signature.Legacy} to convert.
|
|
* @returns The converted {@link ox#Signature.Signature}.
|
|
*/
|
|
export function fromLegacy(signature: Legacy): Signature {
|
|
return {
|
|
r: signature.r,
|
|
s: signature.s,
|
|
yParity: vToYParity(signature.v),
|
|
}
|
|
}
|
|
|
|
export declare namespace fromLegacy {
|
|
type ErrorType = vToYParity.ErrorType | Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a {@link ox#Signature.Rpc} into a {@link ox#Signature.Signature}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.fromRpc({
|
|
* r: '0x635dc2033e60185bb36709c29c75d64ea51dfbd91c32ef4be198e4ceb169fb4d',
|
|
* s: '0x50c2667ac4c771072746acfdcf1f1483336dcca8bd2df47cd83175dbe60f0540',
|
|
* yParity: '0x0',
|
|
* })
|
|
* ```
|
|
*
|
|
* @param signature - The {@link ox#Signature.Rpc} to convert.
|
|
* @returns The converted {@link ox#Signature.Signature}.
|
|
*/
|
|
export function fromRpc(signature: {
|
|
r: Hex.Hex
|
|
s: Hex.Hex
|
|
yParity?: Hex.Hex | undefined
|
|
v?: Hex.Hex | undefined
|
|
}): Signature {
|
|
const yParity = (() => {
|
|
const v = signature.v ? Number(signature.v) : undefined
|
|
let yParity = signature.yParity ? Number(signature.yParity) : undefined
|
|
if (typeof v === 'number' && typeof yParity !== 'number')
|
|
yParity = vToYParity(v)
|
|
if (typeof yParity !== 'number')
|
|
throw new InvalidYParityError({ value: signature.yParity })
|
|
return yParity
|
|
})()
|
|
|
|
return {
|
|
r: BigInt(signature.r),
|
|
s: BigInt(signature.s),
|
|
yParity,
|
|
}
|
|
}
|
|
|
|
export declare namespace fromRpc {
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a {@link ox#Signature.Tuple} to a {@link ox#Signature.Signature}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.fromTuple(['0x01', '0x7b', '0x1c8'])
|
|
* // @log: {
|
|
* // @log: r: 123n,
|
|
* // @log: s: 456n,
|
|
* // @log: yParity: 1,
|
|
* // @log: }
|
|
* ```
|
|
*
|
|
* @param tuple - The {@link ox#Signature.Tuple} to convert.
|
|
* @returns The {@link ox#Signature.Signature}.
|
|
*/
|
|
export function fromTuple(tuple: Tuple): Signature {
|
|
const [yParity, r, s] = tuple
|
|
return from({
|
|
r: r === '0x' ? 0n : BigInt(r),
|
|
s: s === '0x' ? 0n : BigInt(s),
|
|
yParity: yParity === '0x' ? 0 : Number(yParity),
|
|
})
|
|
}
|
|
|
|
export declare namespace fromTuple {
|
|
type ErrorType = from.ErrorType | Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Serializes a {@link ox#Signature.Signature} to {@link ox#Bytes.Bytes}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.toBytes({
|
|
* r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* yParity: 1
|
|
* })
|
|
* // @log: Uint8Array [102, 16, 10, ...]
|
|
* ```
|
|
*
|
|
* @param signature - The signature to serialize.
|
|
* @returns The serialized signature.
|
|
*/
|
|
export function toBytes(signature: Signature<boolean>): Bytes.Bytes {
|
|
return Bytes.fromHex(toHex(signature))
|
|
}
|
|
|
|
export declare namespace toBytes {
|
|
type ErrorType =
|
|
| toHex.ErrorType
|
|
| Bytes.fromHex.ErrorType
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Serializes a {@link ox#Signature.Signature} to {@link ox#Hex.Hex}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.toHex({
|
|
* r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* yParity: 1
|
|
* })
|
|
* // @log: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c'
|
|
* ```
|
|
*
|
|
* @param signature - The signature to serialize.
|
|
* @returns The serialized signature.
|
|
*/
|
|
export function toHex(signature: Signature<boolean>): Hex.Hex {
|
|
assert(signature)
|
|
|
|
const r = signature.r
|
|
const s = signature.s
|
|
|
|
const signature_ = Hex.concat(
|
|
Hex.fromNumber(r, { size: 32 }),
|
|
Hex.fromNumber(s, { size: 32 }),
|
|
// If the signature is recovered, add the recovery byte to the signature.
|
|
typeof signature.yParity === 'number'
|
|
? Hex.fromNumber(yParityToV(signature.yParity), { size: 1 })
|
|
: '0x',
|
|
)
|
|
|
|
return signature_
|
|
}
|
|
|
|
export declare namespace toHex {
|
|
type ErrorType =
|
|
| Hex.concat.ErrorType
|
|
| Hex.fromNumber.ErrorType
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a {@link ox#Signature.Signature} to DER-encoded format.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.from({
|
|
* r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* })
|
|
*
|
|
* const signature_der = Signature.toDerBytes(signature)
|
|
* // @log: Uint8Array [132, 51, 23, ...]
|
|
* ```
|
|
*
|
|
* @param signature - The signature to convert.
|
|
* @returns The DER-encoded signature.
|
|
*/
|
|
export function toDerBytes(signature: Signature<boolean>): Bytes.Bytes {
|
|
const sig = new secp256k1.Signature(signature.r, signature.s)
|
|
return sig.toDERRawBytes()
|
|
}
|
|
|
|
export declare namespace toDerBytes {
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a {@link ox#Signature.Signature} to DER-encoded format.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.from({
|
|
* r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* })
|
|
*
|
|
* const signature_der = Signature.toDerHex(signature)
|
|
* // @log: '0x304402206e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf02204a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8'
|
|
* ```
|
|
*
|
|
* @param signature - The signature to convert.
|
|
* @returns The DER-encoded signature.
|
|
*/
|
|
export function toDerHex(signature: Signature<boolean>): Hex.Hex {
|
|
const sig = new secp256k1.Signature(signature.r, signature.s)
|
|
return `0x${sig.toDERHex()}`
|
|
}
|
|
|
|
export declare namespace toDerHex {
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a {@link ox#Signature.Signature} into a {@link ox#Signature.Legacy}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const legacy = Signature.toLegacy({ r: 1n, s: 2n, yParity: 1 })
|
|
* // @log: { r: 1n, s: 2n, v: 28 }
|
|
* ```
|
|
*
|
|
* @param signature - The {@link ox#Signature.Signature} to convert.
|
|
* @returns The converted {@link ox#Signature.Legacy}.
|
|
*/
|
|
export function toLegacy(signature: Signature): Legacy {
|
|
return {
|
|
r: signature.r,
|
|
s: signature.s,
|
|
v: yParityToV(signature.yParity),
|
|
}
|
|
}
|
|
|
|
export declare namespace toLegacy {
|
|
type ErrorType = yParityToV.ErrorType | Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a {@link ox#Signature.Signature} into a {@link ox#Signature.Rpc}.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signature = Signature.toRpc({
|
|
* r: 49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* yParity: 1
|
|
* })
|
|
* ```
|
|
*
|
|
* @param signature - The {@link ox#Signature.Signature} to convert.
|
|
* @returns The converted {@link ox#Signature.Rpc}.
|
|
*/
|
|
export function toRpc(signature: Signature): Rpc {
|
|
const { r, s, yParity } = signature
|
|
return {
|
|
r: Hex.fromNumber(r, { size: 32 }),
|
|
s: Hex.fromNumber(s, { size: 32 }),
|
|
yParity: yParity === 0 ? '0x0' : '0x1',
|
|
}
|
|
}
|
|
|
|
export declare namespace toRpc {
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a {@link ox#Signature.Signature} to a serialized {@link ox#Signature.Tuple} to be used for signatures in Transaction Envelopes, EIP-7702 Authorization Lists, etc.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const signatureTuple = Signature.toTuple({
|
|
* r: 123n,
|
|
* s: 456n,
|
|
* yParity: 1,
|
|
* })
|
|
* // @log: [yParity: '0x01', r: '0x7b', s: '0x1c8']
|
|
* ```
|
|
*
|
|
* @param signature - The {@link ox#Signature.Signature} to convert.
|
|
* @returns The {@link ox#Signature.Tuple}.
|
|
*/
|
|
export function toTuple(signature: Signature): Tuple {
|
|
const { r, s, yParity } = signature
|
|
|
|
return [
|
|
yParity ? '0x01' : '0x',
|
|
r === 0n ? '0x' : Hex.trimLeft(Hex.fromNumber(r!)),
|
|
s === 0n ? '0x' : Hex.trimLeft(Hex.fromNumber(s!)),
|
|
] as const
|
|
}
|
|
|
|
export declare namespace toTuple {
|
|
type ErrorType =
|
|
| Hex.trimLeft.ErrorType
|
|
| Hex.fromNumber.ErrorType
|
|
| Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Validates a Signature. Returns `true` if the signature is valid, `false` otherwise.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const valid = Signature.validate({
|
|
* r: -49782753348462494199823712700004552394425719014458918871452329774910450607807n,
|
|
* s: 33726695977844476214676913201140481102225469284307016937915595756355928419768n,
|
|
* yParity: 1,
|
|
* })
|
|
* // @log: false
|
|
* ```
|
|
*
|
|
* @param signature - The signature object to assert.
|
|
*/
|
|
export function validate(
|
|
signature: ExactPartial<Signature>,
|
|
options: validate.Options = {},
|
|
): boolean {
|
|
try {
|
|
assert(signature, options)
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
export declare namespace validate {
|
|
type Options = {
|
|
/** Whether or not the signature should be recovered (contain `yParity`). */
|
|
recovered?: boolean
|
|
}
|
|
|
|
type ErrorType = Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a ECDSA `v` value to a `yParity` value.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const yParity = Signature.vToYParity(28)
|
|
* // @log: 1
|
|
* ```
|
|
*
|
|
* @param v - The ECDSA `v` value to convert.
|
|
* @returns The `yParity` value.
|
|
*/
|
|
export function vToYParity(v: number): Signature['yParity'] {
|
|
if (v === 0 || v === 27) return 0
|
|
if (v === 1 || v === 28) return 1
|
|
if (v >= 35) return v % 2 === 0 ? 1 : 0
|
|
throw new InvalidVError({ value: v })
|
|
}
|
|
|
|
export declare namespace vToYParity {
|
|
type ErrorType = InvalidVError | Errors.GlobalErrorType
|
|
}
|
|
|
|
/**
|
|
* Converts a ECDSA `v` value to a `yParity` value.
|
|
*
|
|
* @example
|
|
* ```ts twoslash
|
|
* import { Signature } from 'ox'
|
|
*
|
|
* const v = Signature.yParityToV(1)
|
|
* // @log: 28
|
|
* ```
|
|
*
|
|
* @param yParity - The ECDSA `yParity` value to convert.
|
|
* @returns The `v` value.
|
|
*/
|
|
export function yParityToV(yParity: number): number {
|
|
if (yParity === 0) return 27
|
|
if (yParity === 1) return 28
|
|
throw new InvalidYParityError({ value: yParity })
|
|
}
|
|
|
|
export declare namespace yParityToV {
|
|
type ErrorType = InvalidVError | Errors.GlobalErrorType
|
|
}
|
|
|
|
/** Thrown when the serialized signature is of an invalid size. */
|
|
export class InvalidSerializedSizeError extends Errors.BaseError {
|
|
override readonly name = 'Signature.InvalidSerializedSizeError'
|
|
|
|
constructor({ signature }: { signature: Hex.Hex | Bytes.Bytes }) {
|
|
super(`Value \`${signature}\` is an invalid signature size.`, {
|
|
metaMessages: [
|
|
'Expected: 64 bytes or 65 bytes.',
|
|
`Received ${Hex.size(Hex.from(signature))} bytes.`,
|
|
],
|
|
})
|
|
}
|
|
}
|
|
|
|
/** Thrown when the signature is missing either an `r`, `s`, or `yParity` property. */
|
|
export class MissingPropertiesError extends Errors.BaseError {
|
|
override readonly name = 'Signature.MissingPropertiesError'
|
|
|
|
constructor({ signature }: { signature: unknown }) {
|
|
super(
|
|
`Signature \`${Json.stringify(signature)}\` is missing either an \`r\`, \`s\`, or \`yParity\` property.`,
|
|
)
|
|
}
|
|
}
|
|
|
|
/** Thrown when the signature has an invalid `r` value. */
|
|
export class InvalidRError extends Errors.BaseError {
|
|
override readonly name = 'Signature.InvalidRError'
|
|
|
|
constructor({ value }: { value: unknown }) {
|
|
super(
|
|
`Value \`${value}\` is an invalid r value. r must be a positive integer less than 2^256.`,
|
|
)
|
|
}
|
|
}
|
|
|
|
/** Thrown when the signature has an invalid `s` value. */
|
|
export class InvalidSError extends Errors.BaseError {
|
|
override readonly name = 'Signature.InvalidSError'
|
|
|
|
constructor({ value }: { value: unknown }) {
|
|
super(
|
|
`Value \`${value}\` is an invalid s value. s must be a positive integer less than 2^256.`,
|
|
)
|
|
}
|
|
}
|
|
|
|
/** Thrown when the signature has an invalid `yParity` value. */
|
|
export class InvalidYParityError extends Errors.BaseError {
|
|
override readonly name = 'Signature.InvalidYParityError'
|
|
|
|
constructor({ value }: { value: unknown }) {
|
|
super(
|
|
`Value \`${value}\` is an invalid y-parity value. Y-parity must be 0 or 1.`,
|
|
)
|
|
}
|
|
}
|
|
|
|
/** Thrown when the signature has an invalid `v` value. */
|
|
export class InvalidVError extends Errors.BaseError {
|
|
override readonly name = 'Signature.InvalidVError'
|
|
|
|
constructor({ value }: { value: number }) {
|
|
super(`Value \`${value}\` is an invalid v value. v must be 27, 28 or >=35.`)
|
|
}
|
|
}
|