From 92f9ed1cafb6d6946cbef4356679e3b48b3dfb86 Mon Sep 17 00:00:00 2001 From: TechGeorgii Date: Fri, 16 Feb 2024 16:47:01 +0700 Subject: [PATCH] Can specify domain separator, not only domain object. --- src.ts/hash/typed-data.ts | 52 +++++++++++++++++++++--------------- src.ts/wallet/base-wallet.ts | 7 +++-- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src.ts/hash/typed-data.ts b/src.ts/hash/typed-data.ts index a21710782..ea67c216f 100644 --- a/src.ts/hash/typed-data.ts +++ b/src.ts/hash/typed-data.ts @@ -4,7 +4,8 @@ import { keccak256 } from "../crypto/index.js"; import { recoverAddress } from "../transaction/index.js"; import { concat, defineProperties, getBigInt, getBytes, hexlify, isHexString, mask, toBeHex, toQuantity, toTwos, zeroPadValue, - assertArgument + assertArgument, + isBytesLike } from "../utils/index.js"; import { id } from "./id.js"; @@ -517,10 +518,11 @@ export class TypedDataEncoder { /** * Return the fully encoded [[link-eip-712]] %%value%% for %%types%% with %%domain%%. */ - static encode(domain: TypedDataDomain, types: Record>, value: Record): string { + static encode(domain: TypedDataDomain | BytesLike, types: Record>, value: Record): string { return concat([ "0x1901", - TypedDataEncoder.hashDomain(domain), + // if domainSeparator is passed (array of bytes), then just use it. + isBytesLike(domain) ? domain : TypedDataEncoder.hashDomain(domain), TypedDataEncoder.from(types).hash(value) ]); } @@ -528,7 +530,7 @@ export class TypedDataEncoder { /** * Return the hash of the fully encoded [[link-eip-712]] %%value%% for %%types%% with %%domain%%. */ - static hash(domain: TypedDataDomain, types: Record>, value: Record): string { + static hash(domain: TypedDataDomain | BytesLike, types: Record>, value: Record): string { return keccak256(TypedDataEncoder.encode(domain, types, value)); } @@ -537,23 +539,26 @@ export class TypedDataEncoder { * Resolves to the value from resolving all addresses in %%value%% for * %%types%% and the %%domain%%. */ - static async resolveNames(domain: TypedDataDomain, types: Record>, value: Record, resolveName: (name: string) => Promise): Promise<{ domain: TypedDataDomain, value: any }> { - // Make a copy to isolate it from the object passed in - domain = Object.assign({ }, domain); - - // Allow passing null to ignore value - for (const key in domain) { - if ((>domain)[key] == null) { - delete (>domain)[key]; - } - } - + static async resolveNames(domain: T, types: Record>, value: Record, resolveName: (name: string) => Promise): Promise<{ domain: T, value: any }> { // Look up all ENS names const ensCache: Record = { }; - // Do we need to look up the domain's verifyingContract? - if (domain.verifyingContract && !isHexString(domain.verifyingContract, 20)) { - ensCache[domain.verifyingContract] = "0x"; + if (!isBytesLike(domain)) { + // Make a copy to isolate it from the object passed in + domain = Object.assign({}, domain); + } + if (!isBytesLike(domain)) { + // Allow passing null to ignore value + for (const key in domain) { + if ((>domain)[key] == null) { + delete (>domain)[key]; + } + } + + // Do we need to look up the domain's verifyingContract? + if (domain.verifyingContract && !isHexString(domain.verifyingContract, 20)) { + ensCache[domain.verifyingContract] = "0x"; + } } // We are going to use the encoder to visit all the base values @@ -572,9 +577,11 @@ export class TypedDataEncoder { ensCache[name] = await resolveName(name); } - // Replace the domain verifyingContract if needed - if (domain.verifyingContract && ensCache[domain.verifyingContract]) { - domain.verifyingContract = ensCache[domain.verifyingContract]; + if (!isBytesLike(domain)) { + // Replace the domain verifyingContract if needed + if (domain.verifyingContract && ensCache[domain.verifyingContract]) { + domain.verifyingContract = ensCache[domain.verifyingContract]; + } } // Replace all ENS names with their address @@ -652,7 +659,8 @@ export class TypedDataEncoder { /** * Compute the address used to sign the typed data for the %%signature%%. + * DOMAIN_SEPARATOR can be passed into %%domain%% directly. */ -export function verifyTypedData(domain: TypedDataDomain, types: Record>, value: Record, signature: SignatureLike): string { +export function verifyTypedData(domain: TypedDataDomain | BytesLike, types: Record>, value: Record, signature: SignatureLike): string { return recoverAddress(TypedDataEncoder.hash(domain, types, value), signature); } diff --git a/src.ts/wallet/base-wallet.ts b/src.ts/wallet/base-wallet.ts index 4ef6bd7b8..e0b99586b 100644 --- a/src.ts/wallet/base-wallet.ts +++ b/src.ts/wallet/base-wallet.ts @@ -3,7 +3,7 @@ import { hashMessage, TypedDataEncoder } from "../hash/index.js"; import { AbstractSigner } from "../providers/index.js"; import { computeAddress, Transaction } from "../transaction/index.js"; import { - defineProperties, resolveProperties, assert, assertArgument + defineProperties, resolveProperties, assert, assertArgument, BytesLike } from "../utils/index.js"; import type { SigningKey } from "../crypto/index.js"; @@ -105,7 +105,10 @@ export class BaseWallet extends AbstractSigner { return this.signingKey.sign(hashMessage(message)).serialized; } - async signTypedData(domain: TypedDataDomain, types: Record>, value: Record): Promise { + /** + * DOMAIN_SEPARATOR can be passed into %%domain%% directly. + */ + async signTypedData(domain: TypedDataDomain | BytesLike, types: Record>, value: Record): Promise { // Populate any ENS names const populated = await TypedDataEncoder.resolveNames(domain, types, value, async (name: string) => {