From 76a92bb97de9adede6d6d60c3aad2961d493045e Mon Sep 17 00:00:00 2001 From: wilsoncusack Date: Mon, 6 May 2024 03:30:53 -0400 Subject: [PATCH] feat: ERC-6492 sig utils (#2209) * ERC-6492 sig utils * update name to parseERC6492Signature * fmt * tweak: refactor + docs --------- Co-authored-by: moxey.eth --- .changeset/funny-cars-allow.md | 9 +++ .changeset/three-windows-collect.md | 10 +++ .../docs/utilities/isErc6492Signature.md | 35 ++++++++++ ...tSignature.md => parseCompactSignature.md} | 8 +-- .../docs/utilities/parseErc6492Signature.md | 48 ++++++++++++++ .../{hexToSignature.md => parseSignature.md} | 8 +-- ...eToHex.md => serializeCompactSignature.md} | 12 ++-- .../utilities/serializeErc6492Signature.md | 52 +++++++++++++++ ...ignatureToHex.md => serializeSignature.md} | 12 ++-- site/sidebar.ts | 36 ++++++---- site/vercel.json | 16 +++++ src/accounts/index.ts | 10 ++- src/accounts/utils/signMessage.ts | 10 +-- src/accounts/utils/signTypedData.ts | 10 +-- src/actions/public/verifyHash.test.ts | 6 +- src/actions/public/verifyHash.ts | 4 +- src/constants/bytes.ts | 3 + .../eip3074/invokers/getInvoker.ts | 10 +-- .../eip3074/utils/signAuthMessage.ts | 10 +-- src/index.ts | 62 ++++++++++++++---- .../signature/compactSignatureToSignature.ts | 2 +- .../signature/isERC6492Signature.test.ts | 21 ++++++ src/utils/signature/isERC6492Signature.ts | 15 +++++ ....test.ts => parseCompactSignature.test.ts} | 6 +- ...tSignature.ts => parseCompactSignature.ts} | 11 ++-- .../signature/parseERC6492Signature.test.ts | 27 ++++++++ src/utils/signature/parseERC6492Signature.ts | 65 +++++++++++++++++++ ...gnature.test.ts => parseSignature.test.ts} | 18 ++--- .../{hexToSignature.ts => parseSignature.ts} | 6 +- src/utils/signature/recoverAddress.test.ts | 10 +-- src/utils/signature/recoverPublicKey.test.ts | 14 ++-- .../recoverTransactionAddress.test.ts | 6 +- .../signature/recoverTransactionAddress.ts | 4 +- ...t.ts => serializeCompactSignature.test.ts} | 6 +- ...eToHex.ts => serializeCompactSignature.ts} | 8 ++- .../serializeErc6492Signature.test.ts | 21 ++++++ .../signature/serializeErc6492Signature.ts | 42 ++++++++++++ ...Hex.test.ts => serializeSignature.test.ts} | 14 ++-- ...ignatureToHex.ts => serializeSignature.ts} | 13 ++-- test/src/ccip.ts | 4 +- 40 files changed, 547 insertions(+), 137 deletions(-) create mode 100644 .changeset/funny-cars-allow.md create mode 100644 .changeset/three-windows-collect.md create mode 100644 site/pages/docs/utilities/isErc6492Signature.md rename site/pages/docs/utilities/{hexToCompactSignature.md => parseCompactSignature.md} (81%) create mode 100644 site/pages/docs/utilities/parseErc6492Signature.md rename site/pages/docs/utilities/{hexToSignature.md => parseSignature.md} (81%) rename site/pages/docs/utilities/{compactSignatureToHex.md => serializeCompactSignature.md} (62%) create mode 100644 site/pages/docs/utilities/serializeErc6492Signature.md rename site/pages/docs/utilities/{signatureToHex.md => serializeSignature.md} (68%) create mode 100644 src/utils/signature/isERC6492Signature.test.ts create mode 100644 src/utils/signature/isERC6492Signature.ts rename src/utils/signature/{hexToCompactSignature.test.ts => parseCompactSignature.test.ts} (87%) rename src/utils/signature/{hexToCompactSignature.ts => parseCompactSignature.ts} (71%) create mode 100644 src/utils/signature/parseERC6492Signature.test.ts create mode 100644 src/utils/signature/parseERC6492Signature.ts rename src/utils/signature/{hexToSignature.test.ts => parseSignature.test.ts} (92%) rename src/utils/signature/{hexToSignature.ts => parseSignature.ts} (87%) rename src/utils/signature/{compactSignatureToHex.test.ts => serializeCompactSignature.test.ts} (85%) rename src/utils/signature/{compactSignatureToHex.ts => serializeCompactSignature.ts} (85%) create mode 100644 src/utils/signature/serializeErc6492Signature.test.ts create mode 100644 src/utils/signature/serializeErc6492Signature.ts rename src/utils/signature/{signatureToHex.test.ts => serializeSignature.test.ts} (90%) rename src/utils/signature/{signatureToHex.ts => serializeSignature.ts} (78%) diff --git a/.changeset/funny-cars-allow.md b/.changeset/funny-cars-allow.md new file mode 100644 index 0000000000..3249d295ae --- /dev/null +++ b/.changeset/funny-cars-allow.md @@ -0,0 +1,9 @@ +--- +"viem": minor +--- + +Added [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) signature utilities: + +- `isErc6492Signature` +- `parseErc6492Signature` +- `serializeErc6492Signature` diff --git a/.changeset/three-windows-collect.md b/.changeset/three-windows-collect.md new file mode 100644 index 0000000000..b4f343652a --- /dev/null +++ b/.changeset/three-windows-collect.md @@ -0,0 +1,10 @@ +--- +"viem": minor +--- + +Deprecated utilities (will be removed in v3): + +- `hexToSignature` – use `parseSignature` instead. +- `hexToCompactSignature` – use `parseCompactSignature` instead. +- `compactSignatureToHex` – use `serializeCompactSignature` instead. +- `signatureToHex` – use `serializeSignature` instead. diff --git a/site/pages/docs/utilities/isErc6492Signature.md b/site/pages/docs/utilities/isErc6492Signature.md new file mode 100644 index 0000000000..32b915d0d2 --- /dev/null +++ b/site/pages/docs/utilities/isErc6492Signature.md @@ -0,0 +1,35 @@ +--- +description: Checks whether the signature is in ERC-6492 format. +--- + +# isErc6492Signature + +Checks whether the signature is in [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) format. + +## Import + +```ts +import { isErc6492Signature } from 'viem' +``` + +## Usage + +```ts twoslash +import { isErc6492Signature } from 'viem' + +const result = isErc6492Signature('0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492') +``` + +## Returns + +`boolean` + +Whether the signature is in ERC-6492 format. + +## Parameters + +### signature + +- **Type:** [`Hex`](/docs/glossary/types#hex) + +The signature to check. \ No newline at end of file diff --git a/site/pages/docs/utilities/hexToCompactSignature.md b/site/pages/docs/utilities/parseCompactSignature.md similarity index 81% rename from site/pages/docs/utilities/hexToCompactSignature.md rename to site/pages/docs/utilities/parseCompactSignature.md index e4895a0f7f..37d4fd70d1 100644 --- a/site/pages/docs/utilities/hexToCompactSignature.md +++ b/site/pages/docs/utilities/parseCompactSignature.md @@ -2,22 +2,22 @@ description: Parses a hex formatted compact signature into a structured compact signature. --- -# hexToCompactSignature +# parseCompactSignature Parses a hex formatted compact signature into a structured ("split") compact signature. ## Import ```ts -import { hexToCompactSignature } from 'viem' +import { parseCompactSignature } from 'viem' ``` ## Usage ```ts -import { hexToCompactSignature } from 'viem' +import { parseCompactSignature } from 'viem' -hexToCompactSignature('0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793') // [!code focus:7] +parseCompactSignature('0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793') // [!code focus:7] /** * { * r: '0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76', diff --git a/site/pages/docs/utilities/parseErc6492Signature.md b/site/pages/docs/utilities/parseErc6492Signature.md new file mode 100644 index 0000000000..9439c83f3a --- /dev/null +++ b/site/pages/docs/utilities/parseErc6492Signature.md @@ -0,0 +1,48 @@ +--- +description: Parses a hex-formatted ERC-6492 flavoured signature. +--- + +# parseErc6492Signature + +Parses a hex-formatted [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) flavoured signature. + +If the signature is not in ERC-6492 format, then the underlying (original) signature is returned. + +## Import + +```ts +import { parseErc6492Signature } from 'viem' +``` + +## Usage + +```ts twoslash +import { parseErc6492Signature } from 'viem' + +const { // [!code focus:99] + factoryAddress, + factoryData, + signature, +} = parseErc6492Signature('0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492') +/** + * { + * factoryAddress: '0xCafEBAbECAFEbAbEcaFEbabECAfebAbEcAFEBaBe', + * factoryData: '0xdeadbeef', + * signature: '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b' + * } + */ +``` + +## Returns + +`ParseErc6492SignatureReturnType` + +The ERC-6492 signature components. + +## Parameters + +### signature + +- **Type:** [`Hex`](/docs/glossary/types#hex) + +The ERC-6492 signature in hex format. \ No newline at end of file diff --git a/site/pages/docs/utilities/hexToSignature.md b/site/pages/docs/utilities/parseSignature.md similarity index 81% rename from site/pages/docs/utilities/hexToSignature.md rename to site/pages/docs/utilities/parseSignature.md index 9f1faecd1c..c91b9abcb8 100644 --- a/site/pages/docs/utilities/hexToSignature.md +++ b/site/pages/docs/utilities/parseSignature.md @@ -2,22 +2,22 @@ description: Parses a hex formatted signature into a structured signature. --- -# hexToSignature +# parseSignature Parses a hex formatted signature into a structured ("split") signature. ## Import ```ts -import { hexToSignature } from 'viem' +import { parseSignature } from 'viem' ``` ## Usage ```ts -import { hexToSignature } from 'viem' +import { parseSignature } from 'viem' -hexToSignature('0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c') // [!code focus:8] +parseSignature('0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c') // [!code focus:8] /** * { * r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf', diff --git a/site/pages/docs/utilities/compactSignatureToHex.md b/site/pages/docs/utilities/serializeCompactSignature.md similarity index 62% rename from site/pages/docs/utilities/compactSignatureToHex.md rename to site/pages/docs/utilities/serializeCompactSignature.md index dab7f42500..5c982785f6 100644 --- a/site/pages/docs/utilities/compactSignatureToHex.md +++ b/site/pages/docs/utilities/serializeCompactSignature.md @@ -1,23 +1,23 @@ --- -description: Parses a compact signature into hex format. +description: Serializes a compact signature into hex format. --- -# compactSignatureToHex +# serializeCompactSignature -Parses a [EIP-2098](https://eips.ethereum.org/EIPS/eip-2098) compact signature into hex format. +Serializes a [EIP-2098](https://eips.ethereum.org/EIPS/eip-2098) compact signature into hex format. ## Import ```ts -import { compactSignatureToHex } from 'viem' +import { serializeCompactSignature } from 'viem' ``` ## Usage ```ts -import { compactSignatureToHex } from 'viem' +import { serializeCompactSignature } from 'viem' -compactSignatureToHex({ // [!code focus:8] +serializeCompactSignature({ // [!code focus:8] r: '0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90', yParityAndS: '0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064', diff --git a/site/pages/docs/utilities/serializeErc6492Signature.md b/site/pages/docs/utilities/serializeErc6492Signature.md new file mode 100644 index 0000000000..454852989d --- /dev/null +++ b/site/pages/docs/utilities/serializeErc6492Signature.md @@ -0,0 +1,52 @@ +--- +description: Serializes a ERC-6492 flavoured signature into hex format. +--- + +# serializeErc6492Signature + +Serializes a [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) flavoured signature into hex format. + +## Import + +```ts +import { serializeErc6492Signature } from 'viem' +``` + +## Usage + +```ts twoslash +import { serializeErc6492Signature } from 'viem' + +serializeErc6492Signature({ // [!code focus:99] + factoryAddress: '0xcafebabecafebabecafebabecafebabecafebabe', + factoryData: '0xdeadbeef', + signature: '0x41a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b', +}) +// "0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492" +``` + +## Returns + +[`Hex`](/docs/glossary/types#hex) + +The hex formatted signature. + +## Parameters + +### factoryAddress + +- **Type:** `Address` + +The ERC-4337 Account Factory address to use for counterfactual verification. + +### factoryData + +- **Type:** `Hex` + +Calldata to pass to deploy the ERC-4337 Account (if not deployed) for counterfactual verification. + +### signature + +- **Type:** `Hex` + +The original signature. \ No newline at end of file diff --git a/site/pages/docs/utilities/signatureToHex.md b/site/pages/docs/utilities/serializeSignature.md similarity index 68% rename from site/pages/docs/utilities/signatureToHex.md rename to site/pages/docs/utilities/serializeSignature.md index 0b3e619b2f..558dcff43b 100644 --- a/site/pages/docs/utilities/signatureToHex.md +++ b/site/pages/docs/utilities/serializeSignature.md @@ -1,23 +1,23 @@ --- -description: Parses a hex formatted signature into a structured signature. +description: Serializes a structured signature into hex format. --- -# signatureToHex +# serializeSignature -Parses a hex formatted signature into a structured ("split") signature. +Serializes a structured signature into hex format. ## Import ```ts -import { signatureToHex } from 'viem' +import { serializeSignature } from 'viem' ``` ## Usage ```ts -import { signatureToHex } from 'viem' +import { serializeSignature } from 'viem' -signatureToHex({ +serializeSignature({ r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf', s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8', yParity: 1 diff --git a/site/sidebar.ts b/site/sidebar.ts index cf7637b19a..843335e789 100644 --- a/site/sidebar.ts +++ b/site/sidebar.ts @@ -866,10 +866,6 @@ export const sidebar = { { text: 'Signature', items: [ - { - text: 'compactSignatureToHex', - link: '/docs/utilities/compactSignatureToHex', - }, { text: 'compactSignatureToSignature', link: '/docs/utilities/compactSignatureToSignature', @@ -883,12 +879,20 @@ export const sidebar = { link: '/docs/utilities/hashTypedData', }, { - text: 'hexToCompactSignature', - link: '/docs/utilities/hexToCompactSignature', + text: 'isErc6492Signature', + link: '/docs/utilities/isErc6492Signature', + }, + { + text: 'parseCompactSignature', + link: '/docs/utilities/parseCompactSignature', }, { - text: 'hexToSignature', - link: '/docs/utilities/hexToSignature', + text: 'parseErc6492Signature', + link: '/docs/utilities/parseErc6492Signature', + }, + { + text: 'parseSignature', + link: '/docs/utilities/parseSignature', }, { text: 'recoverAddress', @@ -911,12 +915,20 @@ export const sidebar = { link: '/docs/utilities/recoverTypedDataAddress', }, { - text: 'signatureToCompactSignature', - link: '/docs/utilities/signatureToCompactSignature', + text: 'serializeCompactSignature', + link: '/docs/utilities/serializeCompactSignature', + }, + { + text: 'serializeErc6492Signature', + link: '/docs/utilities/serializeErc6492Signature', }, { - text: 'signatureToHex', - link: '/docs/utilities/signatureToHex', + text: 'serializeSignature', + link: '/docs/utilities/serializeSignature', + }, + { + text: 'signatureToCompactSignature', + link: '/docs/utilities/signatureToCompactSignature', }, { text: 'verifyMessage', diff --git a/site/vercel.json b/site/vercel.json index 3c092d8c9a..84d4516ae3 100644 --- a/site/vercel.json +++ b/site/vercel.json @@ -3,6 +3,22 @@ { "source": "/:match*.html", "destination": "/:match" + }, + { + "source": "/:match/hexToSignature", + "destination": "/:match/parseSignature" + }, + { + "source": "/:match/hexToCompactSignature", + "destination": "/:match/parseCompactSignature" + }, + { + "source": "/:match/signatureToHex", + "destination": "/:match/serializeSignature" + }, + { + "source": "/:match/compactSignatureToHex", + "destination": "/:match/serializeCompactSignature" } ] } diff --git a/src/accounts/index.ts b/src/accounts/index.ts index faa18c8aea..7fba79b580 100644 --- a/src/accounts/index.ts +++ b/src/accounts/index.ts @@ -48,9 +48,13 @@ export { sign, } from './utils/sign.js' export { - type SignatureToHexErrorType, - signatureToHex, -} from '../utils/signature/signatureToHex.js' + /** @deprecated Use `SignatureToHexErrorType` instead. */ + type SerializeSignatureErrorType as SignatureToHexErrorType, + /** @deprecated Use `serializeSignature` instead. */ + serializeSignature as signatureToHex, + type SerializeSignatureErrorType, + serializeSignature, +} from '../utils/signature/serializeSignature.js' export { type SignMessageErrorType, type SignMessageParameters, diff --git a/src/accounts/utils/signMessage.ts b/src/accounts/utils/signMessage.ts index 67b5986d8e..ed07c9481a 100644 --- a/src/accounts/utils/signMessage.ts +++ b/src/accounts/utils/signMessage.ts @@ -5,9 +5,9 @@ import { hashMessage, } from '../../utils/signature/hashMessage.js' import { - type SignatureToHexErrorType, - signatureToHex, -} from '../../utils/signature/signatureToHex.js' + type SerializeSignatureErrorType, + serializeSignature, +} from '../../utils/signature/serializeSignature.js' import { type SignErrorType, sign } from './sign.js' @@ -23,7 +23,7 @@ export type SignMessageReturnType = Hex export type SignMessageErrorType = | SignErrorType | HashMessageErrorType - | SignatureToHexErrorType + | SerializeSignatureErrorType | ErrorType /** @@ -37,5 +37,5 @@ export async function signMessage({ privateKey, }: SignMessageParameters): Promise { const signature = await sign({ hash: hashMessage(message), privateKey }) - return signatureToHex(signature) + return serializeSignature(signature) } diff --git a/src/accounts/utils/signTypedData.ts b/src/accounts/utils/signTypedData.ts index 2c646c0403..ddd0e41fb3 100644 --- a/src/accounts/utils/signTypedData.ts +++ b/src/accounts/utils/signTypedData.ts @@ -7,9 +7,9 @@ import { hashTypedData, } from '../../utils/signature/hashTypedData.js' import { - type SignatureToHexErrorType, - signatureToHex, -} from '../../utils/signature/signatureToHex.js' + type SerializeSignatureErrorType, + serializeSignature, +} from '../../utils/signature/serializeSignature.js' import type { ErrorType } from '../../errors/utils.js' import { type SignErrorType, sign } from './sign.js' @@ -27,7 +27,7 @@ export type SignTypedDataReturnType = Hex export type SignTypedDataErrorType = | HashTypedDataErrorType | SignErrorType - | SignatureToHexErrorType + | SerializeSignatureErrorType | ErrorType /** @@ -48,5 +48,5 @@ export async function signTypedData< hash: hashTypedData(typedData), privateKey, }) - return signatureToHex(signature) + return serializeSignature(signature) } diff --git a/src/actions/public/verifyHash.test.ts b/src/actions/public/verifyHash.test.ts index 7b21773227..1a9b26ebcd 100644 --- a/src/actions/public/verifyHash.test.ts +++ b/src/actions/public/verifyHash.test.ts @@ -6,7 +6,7 @@ import { anvilMainnet } from '../../../test/src/anvil.js' import type { Hex } from '../../types/misc.js' import { hashMessage, toBytes } from '../../utils/index.js' -import { hexToSignature } from '../../utils/signature/hexToSignature.js' +import { parseSignature } from '../../utils/signature/parseSignature.js' import { verifyHash } from './verifyHash.js' const client = anvilMainnet.getClient() @@ -25,7 +25,7 @@ describe('verifyHash', async () => { _name: 'deployed, supports ERC1271, valid signature, plaintext', address: smartAccountConfig.address, hash: hashMessage('This is a test message for viem!'), - signature: hexToSignature( + signature: parseSignature( '0xefd5fb29a274ea6682673d8b3caa9263e936d48d486e5df68893003e0a76496439594d12245008c6fba1c8e3ef28241cffe1bef27ff6bca487b167f261f329251c', ), expectedResult: true, @@ -56,7 +56,7 @@ describe('verifyHash', async () => { _name: 'undeployed, with correct signature', address: accounts[0].address, hash: hashMessage('hello world'), - signature: hexToSignature( + signature: parseSignature( '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b', ), expectedResult: true, diff --git a/src/actions/public/verifyHash.ts b/src/actions/public/verifyHash.ts index d6a1bfb073..ff2e866df0 100644 --- a/src/actions/public/verifyHash.ts +++ b/src/actions/public/verifyHash.ts @@ -19,7 +19,7 @@ import { import { type IsHexErrorType, isHex } from '../../utils/data/isHex.js' import { type ToHexErrorType, bytesToHex } from '../../utils/encoding/toHex.js' import { getAction } from '../../utils/getAction.js' -import { signatureToHex } from '../../utils/signature/signatureToHex.js' +import { serializeSignature } from '../../utils/signature/serializeSignature.js' import { type CallErrorType, type CallParameters, call } from './call.js' export type VerifyHashParameters = Pick< @@ -58,7 +58,7 @@ export async function verifyHash( const signatureHex = (() => { if (isHex(signature)) return signature if (typeof signature === 'object' && 'r' in signature && 's' in signature) - return signatureToHex(signature) + return serializeSignature(signature) return bytesToHex(signature) })() diff --git a/src/constants/bytes.ts b/src/constants/bytes.ts index 83656662f2..f8c00dd484 100644 --- a/src/constants/bytes.ts +++ b/src/constants/bytes.ts @@ -1,2 +1,5 @@ +export const erc6492MagicBytes = + '0x6492649264926492649264926492649264926492649264926492649264926492' as const + export const zeroHash = '0x0000000000000000000000000000000000000000000000000000000000000000' as const diff --git a/src/experimental/eip3074/invokers/getInvoker.ts b/src/experimental/eip3074/invokers/getInvoker.ts index 51af6b612b..fa323d5673 100644 --- a/src/experimental/eip3074/invokers/getInvoker.ts +++ b/src/experimental/eip3074/invokers/getInvoker.ts @@ -25,9 +25,9 @@ import { keccak256, } from '../../../utils/hash/keccak256.js' import { - type HexToSignatureErrorType, - hexToSignature, -} from '../../../utils/signature/hexToSignature.js' + type ParseSignatureErrorType, + parseSignature, +} from '../../../utils/signature/parseSignature.js' import { type SignAuthMessageErrorType, signAuthMessage, @@ -59,7 +59,7 @@ export type InvokerExecuteParameters< export type InvokerExecuteErrorType = | WriteContractErrorType - | HexToSignatureErrorType + | ParseSignatureErrorType | ErrorType export type InvokerSignParameters< @@ -136,7 +136,7 @@ export function getInvoker< invokerAddress: address, }) - const { r, s, yParity } = hexToSignature(signature) + const { r, s, yParity } = parseSignature(signature) return getAction( client, writeContract, diff --git a/src/experimental/eip3074/utils/signAuthMessage.ts b/src/experimental/eip3074/utils/signAuthMessage.ts index 5561860278..42c0f0c6a0 100644 --- a/src/experimental/eip3074/utils/signAuthMessage.ts +++ b/src/experimental/eip3074/utils/signAuthMessage.ts @@ -3,9 +3,9 @@ import type { ErrorType } from '../../../errors/utils.js' import type { Hex } from '../../../types/misc.js' import { keccak256 } from '../../../utils/hash/keccak256.js' import { - type SignatureToHexErrorType, - signatureToHex, -} from '../../../utils/signature/signatureToHex.js' + type SerializeSignatureErrorType, + serializeSignature, +} from '../../../utils/signature/serializeSignature.js' import { type ToAuthMessageErrorType, type ToAuthMessageParameters, @@ -22,7 +22,7 @@ export type SignAuthMessageReturnType = Hex export type SignAuthMessageErrorType = | SignErrorType | ToAuthMessageErrorType - | SignatureToHexErrorType + | SerializeSignatureErrorType | ErrorType /** @@ -36,5 +36,5 @@ export async function signAuthMessage( hash: keccak256(toAuthMessage(parameters)), privateKey, }) - return signatureToHex(signature) + return serializeSignature(signature) } diff --git a/src/index.ts b/src/index.ts index 58ee026e21..18cbb8e909 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1209,13 +1209,39 @@ export { compactSignatureToSignature, } from './utils/signature/compactSignatureToSignature.js' export { - type HexToCompactSignatureErrorType, - hexToCompactSignature, -} from './utils/signature/hexToCompactSignature.js' -export { - type HexToSignatureErrorType, - hexToSignature, -} from './utils/signature/hexToSignature.js' + /** @deprecated Use `ParseCompactSignatureErrorType`. */ + type ParseCompactSignatureErrorType as HexToCompactSignatureErrorType, + /** @deprecated Use `parseCompactSignature`. */ + parseCompactSignature as hexToCompactSignature, + type ParseCompactSignatureErrorType, + parseCompactSignature, +} from './utils/signature/parseCompactSignature.js' +export { + type ParseErc6492SignatureErrorType, + type ParseErc6492SignatureParameters, + type ParseErc6492SignatureReturnType, + parseErc6492Signature, +} from './utils/signature/parseErc6492Signature.js' +export { + type IsErc6492SignatureErrorType, + type IsErc6492SignatureParameters, + type IsErc6492SignatureReturnType, + isErc6492Signature, +} from './utils/signature/isErc6492Signature.js' +export { + type SerializeErc6492SignatureErrorType, + type SerializeErc6492SignatureParameters, + type SerializeErc6492SignatureReturnType, + serializeErc6492Signature, +} from './utils/signature/serializeErc6492Signature.js' +export { + /** @deprecated Use `ParseSignatureErrorType`. */ + type ParseSignatureErrorType as HexToSignatureErrorType, + /** @deprecated Use `parseSignature`. */ + parseSignature as hexToSignature, + type ParseSignatureErrorType, + parseSignature, +} from './utils/signature/parseSignature.js' export { type RecoverAddressErrorType, type RecoverAddressParameters, @@ -1251,13 +1277,21 @@ export { signatureToCompactSignature, } from './utils/signature/signatureToCompactSignature.js' export { - type CompactSignatureToHexErrorType, - compactSignatureToHex, -} from './utils/signature/compactSignatureToHex.js' -export { - type SignatureToHexErrorType, - signatureToHex, -} from './utils/signature/signatureToHex.js' + /** @deprecated Use `SignatureToHexErrorType` instead. */ + type SerializeCompactSignatureErrorType as CompactSignatureToHexErrorType, + /** @deprecated Use `serializeCompactSignature` instead. */ + serializeCompactSignature as compactSignatureToHex, + type SerializeCompactSignatureErrorType, + serializeCompactSignature, +} from './utils/signature/serializeCompactSignature.js' +export { + /** @deprecated Use `SignatureToHexErrorType` instead. */ + type SerializeSignatureErrorType as SignatureToHexErrorType, + /** @deprecated Use `serializeSignature` instead. */ + serializeSignature as signatureToHex, + type SerializeSignatureErrorType, + serializeSignature, +} from './utils/signature/serializeSignature.js' export { bytesToRlp, type BytesToRlpErrorType, diff --git a/src/utils/signature/compactSignatureToSignature.ts b/src/utils/signature/compactSignatureToSignature.ts index 315982785d..9dc73ca856 100644 --- a/src/utils/signature/compactSignatureToSignature.ts +++ b/src/utils/signature/compactSignatureToSignature.ts @@ -15,7 +15,7 @@ export type CompactSignatureToSignatureErrorType = * @returns The compact signature in signature format. * * @example - * compactSignatureToHex({ + * compactSignatureToSignature({ * r: '0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90', * yParityAndS: '0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064', * }) diff --git a/src/utils/signature/isERC6492Signature.test.ts b/src/utils/signature/isERC6492Signature.test.ts new file mode 100644 index 0000000000..5f52cb434f --- /dev/null +++ b/src/utils/signature/isERC6492Signature.test.ts @@ -0,0 +1,21 @@ +import { expect, test } from 'vitest' +import { accounts } from '../../../test/src/constants.js' +import { signMessage } from '../../accounts/index.js' +import { isErc6492Signature } from './isErc6492Signature.js' +import { serializeErc6492Signature } from './serializeErc6492Signature.js' + +test('default', async () => { + const signature = await signMessage({ + message: 'hello world', + privateKey: accounts[0].privateKey, + }) + + expect(isErc6492Signature(signature)).toBe(false) + + const erc6492Signature = serializeErc6492Signature({ + factoryAddress: '0xcafebabecafebabecafebabecafebabecafebabe', + factoryData: '0xdeadbeef', + signature, + }) + expect(isErc6492Signature(erc6492Signature)).toBe(true) +}) diff --git a/src/utils/signature/isERC6492Signature.ts b/src/utils/signature/isERC6492Signature.ts new file mode 100644 index 0000000000..1e646efff9 --- /dev/null +++ b/src/utils/signature/isERC6492Signature.ts @@ -0,0 +1,15 @@ +import { erc6492MagicBytes } from '../../constants/bytes.js' +import type { ErrorType } from '../../errors/utils.js' +import type { Hex } from '../../types/misc.js' +import { type SliceHexErrorType, sliceHex } from '../data/slice.js' + +export type IsErc6492SignatureParameters = Hex +export type IsErc6492SignatureReturnType = boolean +export type IsErc6492SignatureErrorType = SliceHexErrorType | ErrorType + +/** Whether or not the signature is an ERC-6492 formatted signature. */ +export function isErc6492Signature( + signature: IsErc6492SignatureParameters, +): IsErc6492SignatureReturnType { + return sliceHex(signature, -32) === erc6492MagicBytes +} diff --git a/src/utils/signature/hexToCompactSignature.test.ts b/src/utils/signature/parseCompactSignature.test.ts similarity index 87% rename from src/utils/signature/hexToCompactSignature.test.ts rename to src/utils/signature/parseCompactSignature.test.ts index b9c0c3c20b..6157b2a449 100644 --- a/src/utils/signature/hexToCompactSignature.test.ts +++ b/src/utils/signature/parseCompactSignature.test.ts @@ -1,10 +1,10 @@ import { expect, test } from 'vitest' -import { hexToCompactSignature } from './hexToCompactSignature.js' +import { parseCompactSignature } from './parseCompactSignature.js' test('default', () => { expect( - hexToCompactSignature( + parseCompactSignature( '0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b907e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064', ), ).toMatchInlineSnapshot( @@ -17,7 +17,7 @@ test('default', () => { ) expect( - hexToCompactSignature( + parseCompactSignature( '0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793', ), ).toMatchInlineSnapshot( diff --git a/src/utils/signature/hexToCompactSignature.ts b/src/utils/signature/parseCompactSignature.ts similarity index 71% rename from src/utils/signature/hexToCompactSignature.ts rename to src/utils/signature/parseCompactSignature.ts index 13f4eb7579..9cdc583440 100644 --- a/src/utils/signature/hexToCompactSignature.ts +++ b/src/utils/signature/parseCompactSignature.ts @@ -2,12 +2,9 @@ import { secp256k1 } from '@noble/curves/secp256k1' import type { ErrorType } from '../../errors/utils.js' import type { CompactSignature, Hex } from '../../types/misc.js' -import { - type NumberToHexErrorType, - numberToHex, -} from '../../utils/encoding/toHex.js' +import { type NumberToHexErrorType, numberToHex } from '../encoding/toHex.js' -export type HexToCompactSignatureErrorType = NumberToHexErrorType | ErrorType +export type ParseCompactSignatureErrorType = NumberToHexErrorType | ErrorType /** * @description Parses a hex formatted compact signature into a structured compact signature. @@ -16,10 +13,10 @@ export type HexToCompactSignatureErrorType = NumberToHexErrorType | ErrorType * @returns The structured signature. * * @example - * hexToCompactSignature('0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c') + * parseCompactSignature('0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c') * // { r: '0x...', yParityAndS: '0x...' } */ -export function hexToCompactSignature(signatureHex: Hex): CompactSignature { +export function parseCompactSignature(signatureHex: Hex): CompactSignature { const { r, s } = secp256k1.Signature.fromCompact(signatureHex.slice(2, 130)) return { r: numberToHex(r, { size: 32 }), diff --git a/src/utils/signature/parseERC6492Signature.test.ts b/src/utils/signature/parseERC6492Signature.test.ts new file mode 100644 index 0000000000..c9facfccb8 --- /dev/null +++ b/src/utils/signature/parseERC6492Signature.test.ts @@ -0,0 +1,27 @@ +import { expect, test } from 'vitest' +import { accounts } from '../../../test/src/constants.js' +import { signMessage } from '../../accounts/index.js' +import { parseErc6492Signature } from './parseErc6492Signature.js' + +test('default', async () => { + const signature = await signMessage({ + message: 'hello world', + privateKey: accounts[0].privateKey, + }) + + expect(parseErc6492Signature(signature)).toEqual({ + signature, + }) + + expect( + parseErc6492Signature( + '0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492', + ), + ).toMatchInlineSnapshot(` + { + "address": "0xCafEBAbECAFEbAbEcaFEbabECAfebAbEcAFEBaBe", + "data": "0xdeadbeef", + "signature": "0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b", + } + `) +}) diff --git a/src/utils/signature/parseERC6492Signature.ts b/src/utils/signature/parseERC6492Signature.ts new file mode 100644 index 0000000000..0f087d623d --- /dev/null +++ b/src/utils/signature/parseERC6492Signature.ts @@ -0,0 +1,65 @@ +import type { Address } from 'abitype' +import type { ErrorType } from '../../errors/utils.js' +import type { Hex } from '../../types/misc.js' +import type { OneOf, Prettify } from '../../types/utils.js' +import { + type DecodeAbiParametersErrorType, + decodeAbiParameters, +} from '../abi/decodeAbiParameters.js' +import { + type IsErc6492SignatureErrorType, + isErc6492Signature, +} from './isErc6492Signature.js' + +export type ParseErc6492SignatureParameters = Hex + +export type ParseErc6492SignatureReturnType = Prettify< + OneOf< + | { + /** + * The ERC-4337 Account Factory address to use for counterfactual verification. + * `undefined` if the signature is not in ERC-6492 format. + */ + factoryAddress: Address + /** + * Calldata to pass to deploy account (if not deployed) for counterfactual verification. + * `undefined` if the signature is not in ERC-6492 format. + */ + factoryData: Hex + /** The original signature. */ + signature: Hex + } + | { + /** The original signature. */ + signature: Hex + } + > +> + +export type ParseErc6492SignatureErrorType = + | IsErc6492SignatureErrorType + | DecodeAbiParametersErrorType + | ErrorType + +/** + * @description Parses a hex-formatted ERC-6492 flavoured signature. + * If the signature is not in ERC-6492 format, then the underlying (original) signature is returned. + * + * @param signature ERC-6492 signature in hex format. + * @returns The parsed ERC-6492 signature. + * + * @example + * parseSignature('0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492') + * // { address: '0x...', data: '0x...', signature: '0x...' } + */ +export function parseErc6492Signature( + signature: ParseErc6492SignatureParameters, +): ParseErc6492SignatureReturnType { + if (!isErc6492Signature(signature)) return { signature } + + const [factoryAddress, factoryData, signature_] = decodeAbiParameters( + [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], + signature, + ) + return { factoryAddress, factoryData, signature: signature_ } +} diff --git a/src/utils/signature/hexToSignature.test.ts b/src/utils/signature/parseSignature.test.ts similarity index 92% rename from src/utils/signature/hexToSignature.test.ts rename to src/utils/signature/parseSignature.test.ts index 6e8cdc85fa..49117ce3a3 100644 --- a/src/utils/signature/hexToSignature.test.ts +++ b/src/utils/signature/parseSignature.test.ts @@ -2,11 +2,11 @@ import { expect, test } from 'vitest' import { toHex } from '../../utils/encoding/toHex.js' -import { hexToSignature } from './hexToSignature.js' +import { parseSignature } from './parseSignature.js' test('default', () => { expect( - hexToSignature( + parseSignature( '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c', ), ).toEqual({ @@ -21,7 +21,7 @@ test('default', () => { }) expect( - hexToSignature( + parseSignature( '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81b', ), ).toEqual({ @@ -36,7 +36,7 @@ test('default', () => { }) expect( - hexToSignature( + parseSignature( '0x602381e57b70f1ada20bd56a806291cfc5cb5088f00f0e791510fd8b8cf05cc40dea7b983e0c7d204f3dc511b1f19a2787a5c82cd72f3bd38da58f10969907841b', ), ).toEqual({ @@ -47,7 +47,7 @@ test('default', () => { }) expect( - hexToSignature( + parseSignature( '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b00', ), ).toEqual({ @@ -57,7 +57,7 @@ test('default', () => { }) expect( - hexToSignature( + parseSignature( '0xc4d8bcda762d35ea79d9542b23200f46c2c1899db15bf929bbacaf609581db0831538374a01206517edd934e474212a0f1e2d62e9a01cd64f1cf94ea2e09884901', ), ).toEqual({ @@ -69,17 +69,17 @@ test('default', () => { test('invalid yParityOrV value', async () => { expect(() => - hexToSignature( + parseSignature( '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81d', ), ).toThrowErrorMatchingInlineSnapshot('[Error: Invalid yParityOrV value]') expect(() => - hexToSignature( + parseSignature( '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db802', ), ).toThrowErrorMatchingInlineSnapshot('[Error: Invalid yParityOrV value]') expect(() => - hexToSignature( + parseSignature( '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81a', ), ).toThrowErrorMatchingInlineSnapshot('[Error: Invalid yParityOrV value]') diff --git a/src/utils/signature/hexToSignature.ts b/src/utils/signature/parseSignature.ts similarity index 87% rename from src/utils/signature/hexToSignature.ts rename to src/utils/signature/parseSignature.ts index 5252cf5ff8..e342065aed 100644 --- a/src/utils/signature/hexToSignature.ts +++ b/src/utils/signature/parseSignature.ts @@ -7,7 +7,7 @@ import { numberToHex, } from '../../utils/encoding/toHex.js' -export type HexToSignatureErrorType = NumberToHexErrorType | ErrorType +export type ParseSignatureErrorType = NumberToHexErrorType | ErrorType /** * @description Parses a hex formatted signature into a structured signature. @@ -16,10 +16,10 @@ export type HexToSignatureErrorType = NumberToHexErrorType | ErrorType * @returns The structured signature. * * @example - * hexToSignature('0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c') + * parseSignature('0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c') * // { r: '0x...', s: '0x...', v: 28n } */ -export function hexToSignature(signatureHex: Hex) { +export function parseSignature(signatureHex: Hex) { const { r, s } = secp256k1.Signature.fromCompact(signatureHex.slice(2, 130)) const yParityOrV = Number(`0x${signatureHex.slice(130)}`) const [v, yParity] = (() => { diff --git a/src/utils/signature/recoverAddress.test.ts b/src/utils/signature/recoverAddress.test.ts index 11176c550c..a98d3f57dd 100644 --- a/src/utils/signature/recoverAddress.test.ts +++ b/src/utils/signature/recoverAddress.test.ts @@ -5,7 +5,7 @@ import { getAddress } from '../address/getAddress.js' import { toBytes } from '../encoding/toBytes.js' import { hashMessage } from './hashMessage.js' -import { hexToSignature } from './hexToSignature.js' +import { parseSignature } from './parseSignature.js' import { recoverAddress } from './recoverAddress.js' test('default', async () => { @@ -20,7 +20,7 @@ test('default', async () => { expect( await recoverAddress({ hash: hashMessage('hello world'), - signature: hexToSignature( + signature: parseSignature( '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b', ), }), @@ -37,7 +37,7 @@ test('default', async () => { expect( await recoverAddress({ hash: hashMessage('🥵'), - signature: hexToSignature( + signature: parseSignature( '0x05c99bbbe9fac3ad61721a815d19d6771ad39f3e8dffa7ae7561358f20431d8e7f9e1d487c77355790c79c6eb0b0d63690f690615ef99ee3e4f25eef0317d0701b', ), }), @@ -54,7 +54,7 @@ test('default', async () => { expect( await recoverAddress({ hash: hashMessage('hello world', 'bytes'), - signature: hexToSignature( + signature: parseSignature( '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b', ), }), @@ -71,7 +71,7 @@ test('default', async () => { expect( await recoverAddress({ hash: hashMessage('🥵', 'bytes'), - signature: hexToSignature( + signature: parseSignature( '0x05c99bbbe9fac3ad61721a815d19d6771ad39f3e8dffa7ae7561358f20431d8e7f9e1d487c77355790c79c6eb0b0d63690f690615ef99ee3e4f25eef0317d0701b', ), }), diff --git a/src/utils/signature/recoverPublicKey.test.ts b/src/utils/signature/recoverPublicKey.test.ts index 3462544467..378abb5c90 100644 --- a/src/utils/signature/recoverPublicKey.test.ts +++ b/src/utils/signature/recoverPublicKey.test.ts @@ -5,7 +5,7 @@ import { privateKeyToAccount } from '../../accounts/privateKeyToAccount.js' import { toBytes } from '../encoding/toBytes.js' import { hashMessage } from './hashMessage.js' -import { hexToSignature } from './hexToSignature.js' +import { parseSignature } from './parseSignature.js' import { recoverPublicKey } from './recoverPublicKey.js' test('default', async () => { @@ -20,7 +20,7 @@ test('default', async () => { expect( await recoverPublicKey({ hash: hashMessage('hello world'), - signature: hexToSignature( + signature: parseSignature( '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b', ), }), @@ -37,7 +37,7 @@ test('default', async () => { expect( await recoverPublicKey({ hash: hashMessage('🥵'), - signature: hexToSignature( + signature: parseSignature( '0x05c99bbbe9fac3ad61721a815d19d6771ad39f3e8dffa7ae7561358f20431d8e7f9e1d487c77355790c79c6eb0b0d63690f690615ef99ee3e4f25eef0317d0701b', ), }), @@ -54,7 +54,7 @@ test('default', async () => { expect( await recoverPublicKey({ hash: hashMessage('hello world', 'bytes'), - signature: hexToSignature( + signature: parseSignature( '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b', ), }), @@ -71,7 +71,7 @@ test('default', async () => { expect( await recoverPublicKey({ hash: hashMessage('🥵', 'bytes'), - signature: hexToSignature( + signature: parseSignature( '0x05c99bbbe9fac3ad61721a815d19d6771ad39f3e8dffa7ae7561358f20431d8e7f9e1d487c77355790c79c6eb0b0d63690f690615ef99ee3e4f25eef0317d0701b', ), }), @@ -97,7 +97,7 @@ test('default', async () => { expect( await recoverPublicKey({ hash: hashMessage('hello world'), - signature: hexToSignature( + signature: parseSignature( '0xa461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b00', ), }), @@ -114,7 +114,7 @@ test('default', async () => { expect( await recoverPublicKey({ hash: '0x9a74cb859ad30835ffb2da406423233c212cf6dd78e6c2c98b0c9289568954ae', - signature: hexToSignature( + signature: parseSignature( '0xc4d8bcda762d35ea79d9542b23200f46c2c1899db15bf929bbacaf609581db0831538374a01206517edd934e474212a0f1e2d62e9a01cd64f1cf94ea2e09884901', ), }), diff --git a/src/utils/signature/recoverTransactionAddress.test.ts b/src/utils/signature/recoverTransactionAddress.test.ts index 890a29952d..90281ca843 100644 --- a/src/utils/signature/recoverTransactionAddress.test.ts +++ b/src/utils/signature/recoverTransactionAddress.test.ts @@ -3,9 +3,9 @@ import { anvilMainnet } from '../../../test/src/anvil.js' import { accounts } from '../../../test/src/constants.js' import { privateKeyToAccount, + serializeSignature, sign, signTransaction, - signatureToHex, } from '../../accounts/index.js' import { getTransaction } from '../../actions/index.js' import { walletActions } from '../../clients/decorators/wallet.js' @@ -54,7 +54,7 @@ test('signature (hex)', async () => { }) const address = await recoverTransactionAddress({ serializedTransaction, - signature: signatureToHex(signature), + signature: serializeSignature(signature), }) expect(address.toLowerCase()).toBe(accounts[0].address) }) @@ -67,7 +67,7 @@ test('signature (bytes)', async () => { }) const address = await recoverTransactionAddress({ serializedTransaction, - signature: hexToBytes(signatureToHex(signature)), + signature: hexToBytes(serializeSignature(signature)), }) expect(address.toLowerCase()).toBe(accounts[0].address) }) diff --git a/src/utils/signature/recoverTransactionAddress.ts b/src/utils/signature/recoverTransactionAddress.ts index 3c01d65cd4..d64d60b679 100644 --- a/src/utils/signature/recoverTransactionAddress.ts +++ b/src/utils/signature/recoverTransactionAddress.ts @@ -12,7 +12,7 @@ import { type RecoverAddressErrorType, recoverAddress, } from './recoverAddress.js' -import type { SignatureToHexErrorType } from './signatureToHex.js' +import type { SerializeSignatureErrorType } from './serializeSignature.js' export type RecoverTransactionAddressParameters = { serializedTransaction: TransactionSerialized @@ -25,7 +25,7 @@ export type RecoverTransactionAddressErrorType = | SerializeTransactionErrorType | RecoverAddressErrorType | Keccak256ErrorType - | SignatureToHexErrorType + | SerializeSignatureErrorType | ErrorType export async function recoverTransactionAddress( diff --git a/src/utils/signature/compactSignatureToHex.test.ts b/src/utils/signature/serializeCompactSignature.test.ts similarity index 85% rename from src/utils/signature/compactSignatureToHex.test.ts rename to src/utils/signature/serializeCompactSignature.test.ts index 8eb639486d..f65552e150 100644 --- a/src/utils/signature/compactSignatureToHex.test.ts +++ b/src/utils/signature/serializeCompactSignature.test.ts @@ -1,10 +1,10 @@ import { expect, test } from 'vitest' -import { compactSignatureToHex } from './compactSignatureToHex.js' +import { serializeCompactSignature } from './serializeCompactSignature.js' test('default', () => { expect( - compactSignatureToHex({ + serializeCompactSignature({ r: '0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90', yParityAndS: '0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064', @@ -14,7 +14,7 @@ test('default', () => { ) expect( - compactSignatureToHex({ + serializeCompactSignature({ r: '0x9328da16089fcba9bececa81663203989f2df5fe1faa6291a45381c81bd17f76', yParityAndS: '0x939c6d6b623b42da56557e5e734a43dc83345ddfadec52cbe24d0cc64f550793', diff --git a/src/utils/signature/compactSignatureToHex.ts b/src/utils/signature/serializeCompactSignature.ts similarity index 85% rename from src/utils/signature/compactSignatureToHex.ts rename to src/utils/signature/serializeCompactSignature.ts index 37dcb3056d..12c9a03c9b 100644 --- a/src/utils/signature/compactSignatureToHex.ts +++ b/src/utils/signature/serializeCompactSignature.ts @@ -3,7 +3,9 @@ import type { ErrorType } from '../../errors/utils.js' import type { CompactSignature, Hex } from '../../types/misc.js' import { type HexToBigIntErrorType, hexToBigInt } from '../encoding/fromHex.js' -export type CompactSignatureToHexErrorType = HexToBigIntErrorType | ErrorType +export type SerializeCompactSignatureErrorType = + | HexToBigIntErrorType + | ErrorType /** * @description Converts an [EIP-2098 compact signature](https://eips.ethereum.org/EIPS/eip-2098) into hex format. @@ -12,13 +14,13 @@ export type CompactSignatureToHexErrorType = HexToBigIntErrorType | ErrorType * @returns The compact signature in hex format. * * @example - * compactSignatureToHex({ + * serializeCompactSignature({ * r: '0x68a020a209d3d56c46f38cc50a33f704f4a9a10a59377f8dd762ac66910e9b90', * yParityAndS: '0x7e865ad05c4035ab5792787d4a0297a43617ae897930a6fe4d822b8faea52064', * }) * // "0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c" */ -export function compactSignatureToHex({ +export function serializeCompactSignature({ r, yParityAndS, }: CompactSignature): Hex { diff --git a/src/utils/signature/serializeErc6492Signature.test.ts b/src/utils/signature/serializeErc6492Signature.test.ts new file mode 100644 index 0000000000..77c76e74ba --- /dev/null +++ b/src/utils/signature/serializeErc6492Signature.test.ts @@ -0,0 +1,21 @@ +import { expect, test } from 'vitest' +import { accounts } from '../../../test/src/constants.js' +import { signMessage } from '../../accounts/index.js' +import { serializeErc6492Signature } from './serializeErc6492Signature.js' + +test('default', async () => { + const signature = await signMessage({ + message: 'hello world', + privateKey: accounts[0].privateKey, + }) + + expect( + serializeErc6492Signature({ + factoryAddress: '0xcafebabecafebabecafebabecafebabecafebabe', + factoryData: '0xdeadbeef', + signature, + }), + ).toMatchInlineSnapshot( + `"0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492"`, + ) +}) diff --git a/src/utils/signature/serializeErc6492Signature.ts b/src/utils/signature/serializeErc6492Signature.ts new file mode 100644 index 0000000000..026e75837b --- /dev/null +++ b/src/utils/signature/serializeErc6492Signature.ts @@ -0,0 +1,42 @@ +import type { Address } from 'abitype' +import { erc6492MagicBytes } from '../../constants/bytes.js' +import type { ErrorType } from '../../errors/utils.js' +import type { Hex } from '../../types/misc.js' +import { encodeAbiParameters } from '../abi/encodeAbiParameters.js' +import { concatHex } from '../data/concat.js' + +export type SerializeErc6492SignatureParameters = { + /** The ERC-4337 Account Factory address to use for counterfactual verification. */ + factoryAddress: Address + /** Calldata to pass to deploy account (if not deployed) for counterfactual verification. */ + factoryData: Hex + /** The original signature. */ + signature: Hex +} + +export type SerializeErc6492SignatureReturnType = Hex + +export type SerializeErc6492SignatureErrorType = ErrorType + +/** + * @description Serializes a ERC-6492 flavoured signature into hex format. + * + * @param signature ERC-6492 signature in object format. + * @returns ERC-6492 signature in hex format. + * + * @example + * serializeSignature({ address: '0x...', data: '0x...', signature: '0x...' }) + * // '0x000000000000000000000000cafebabecafebabecafebabecafebabecafebabe000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004deadbeef000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041a461f509887bd19e312c0c58467ce8ff8e300d3c1a90b608a760c5b80318eaf15fe57c96f9175d6cd4daad4663763baa7e78836e067d0163e9a2ccf2ff753f5b1b000000000000000000000000000000000000000000000000000000000000006492649264926492649264926492649264926492649264926492649264926492' + */ +export function serializeErc6492Signature( + parameters: SerializeErc6492SignatureParameters, +): SerializeErc6492SignatureReturnType { + const { factoryAddress, factoryData, signature } = parameters + return concatHex([ + encodeAbiParameters( + [{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }], + [factoryAddress, factoryData, signature], + ), + erc6492MagicBytes, + ]) +} diff --git a/src/utils/signature/signatureToHex.test.ts b/src/utils/signature/serializeSignature.test.ts similarity index 90% rename from src/utils/signature/signatureToHex.test.ts rename to src/utils/signature/serializeSignature.test.ts index ad1641d6a9..5e10cbb490 100644 --- a/src/utils/signature/signatureToHex.test.ts +++ b/src/utils/signature/serializeSignature.test.ts @@ -1,12 +1,12 @@ import { expect, test } from 'vitest' -import { toHex } from '../../utils/encoding/toHex.js' +import { toHex } from '../encoding/toHex.js' -import { signatureToHex } from './signatureToHex.js' +import { serializeSignature } from './serializeSignature.js' test('default', () => { expect( - signatureToHex({ + serializeSignature({ r: toHex( 49782753348462494199823712700004552394425719014458918871452329774910450607807n, ), @@ -20,7 +20,7 @@ test('default', () => { ) expect( - signatureToHex({ + serializeSignature({ r: toHex( 49782753348462494199823712700004552394425719014458918871452329774910450607807n, ), @@ -34,7 +34,7 @@ test('default', () => { ) expect( - signatureToHex({ + serializeSignature({ r: toHex( 49782753348462494199823712700004552394425719014458918871452329774910450607807n, ), @@ -48,7 +48,7 @@ test('default', () => { ) expect( - signatureToHex({ + serializeSignature({ r: toHex( 49782753348462494199823712700004552394425719014458918871452329774910450607807n, ), @@ -62,7 +62,7 @@ test('default', () => { ) expect( - signatureToHex({ + serializeSignature({ r: toHex( 49782753348462494199823712700004552394425719014458918871452329774910450607807n, ), diff --git a/src/utils/signature/signatureToHex.ts b/src/utils/signature/serializeSignature.ts similarity index 78% rename from src/utils/signature/signatureToHex.ts rename to src/utils/signature/serializeSignature.ts index 25103a54b8..4f0145244f 100644 --- a/src/utils/signature/signatureToHex.ts +++ b/src/utils/signature/serializeSignature.ts @@ -2,13 +2,10 @@ import { secp256k1 } from '@noble/curves/secp256k1' import type { ErrorType } from '../../errors/utils.js' import type { Hex, Signature } from '../../types/misc.js' -import { - type HexToBigIntErrorType, - hexToBigInt, -} from '../../utils/encoding/fromHex.js' -import type { ToHexErrorType } from '../../utils/encoding/toHex.js' +import { type HexToBigIntErrorType, hexToBigInt } from '../encoding/fromHex.js' +import type { ToHexErrorType } from '../encoding/toHex.js' -export type SignatureToHexErrorType = +export type SerializeSignatureErrorType = | HexToBigIntErrorType | ToHexErrorType | ErrorType @@ -20,14 +17,14 @@ export type SignatureToHexErrorType = * @returns The signature in hex format. * * @example - * signatureToHex({ + * serializeSignature({ * r: '0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf', * s: '0x4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8', * yParity: 1 * }) * // "0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db81c" */ -export function signatureToHex({ r, s, v, yParity }: Signature): Hex { +export function serializeSignature({ r, s, v, yParity }: Signature): Hex { const yParity_ = (() => { if (yParity === 0 || yParity === 1) return yParity if (v && (v === 27n || v === 28n || v >= 35n)) return v % 2n === 0n ? 1 : 0 diff --git a/test/src/ccip.ts b/test/src/ccip.ts index 04501a5fa8..b7f65962d1 100644 --- a/test/src/ccip.ts +++ b/test/src/ccip.ts @@ -6,7 +6,7 @@ import { decodeAbiParameters } from '~viem/utils/abi/decodeAbiParameters.js' import { encodeAbiParameters } from '~viem/utils/abi/encodeAbiParameters.js' import { stringToHex } from '~viem/utils/encoding/toHex.js' import { keccak256 } from '~viem/utils/hash/keccak256.js' -import { signatureToHex } from '~viem/utils/signature/signatureToHex.js' +import { serializeSignature } from '~viem/utils/signature/serializeSignature.js' import { accounts } from './constants.js' import { createHttpServer } from './utils.js' @@ -16,7 +16,7 @@ export function createCcipServer() { res.writeHead(200, { 'Content-Type': 'application/json', }) - const signature = signatureToHex( + const signature = serializeSignature( await sign({ hash: keccak256(stringToHex('jxom.viem')), privateKey: accounts[0].privateKey,