Can specify domain separator, not only domain object.
This commit is contained in:
parent
3a1d175c20
commit
92f9ed1caf
@ -4,7 +4,8 @@ import { keccak256 } from "../crypto/index.js";
|
|||||||
import { recoverAddress } from "../transaction/index.js";
|
import { recoverAddress } from "../transaction/index.js";
|
||||||
import {
|
import {
|
||||||
concat, defineProperties, getBigInt, getBytes, hexlify, isHexString, mask, toBeHex, toQuantity, toTwos, zeroPadValue,
|
concat, defineProperties, getBigInt, getBytes, hexlify, isHexString, mask, toBeHex, toQuantity, toTwos, zeroPadValue,
|
||||||
assertArgument
|
assertArgument,
|
||||||
|
isBytesLike
|
||||||
} from "../utils/index.js";
|
} from "../utils/index.js";
|
||||||
|
|
||||||
import { id } from "./id.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%%.
|
* Return the fully encoded [[link-eip-712]] %%value%% for %%types%% with %%domain%%.
|
||||||
*/
|
*/
|
||||||
static encode(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string {
|
static encode(domain: TypedDataDomain | BytesLike, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string {
|
||||||
return concat([
|
return concat([
|
||||||
"0x1901",
|
"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)
|
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%%.
|
* Return the hash of the fully encoded [[link-eip-712]] %%value%% for %%types%% with %%domain%%.
|
||||||
*/
|
*/
|
||||||
static hash(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string {
|
static hash(domain: TypedDataDomain | BytesLike, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string {
|
||||||
return keccak256(TypedDataEncoder.encode(domain, types, value));
|
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
|
* Resolves to the value from resolving all addresses in %%value%% for
|
||||||
* %%types%% and the %%domain%%.
|
* %%types%% and the %%domain%%.
|
||||||
*/
|
*/
|
||||||
static async resolveNames(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>, resolveName: (name: string) => Promise<string>): Promise<{ domain: TypedDataDomain, value: any }> {
|
static async resolveNames<T extends TypedDataDomain | BytesLike>(domain: T, types: Record<string, Array<TypedDataField>>, value: Record<string, any>, resolveName: (name: string) => Promise<string>): Promise<{ domain: T, 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 ((<Record<string, any>>domain)[key] == null) {
|
|
||||||
delete (<Record<string, any>>domain)[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up all ENS names
|
// Look up all ENS names
|
||||||
const ensCache: Record<string, string> = { };
|
const ensCache: Record<string, string> = { };
|
||||||
|
|
||||||
// Do we need to look up the domain's verifyingContract?
|
if (!isBytesLike(domain)) {
|
||||||
if (domain.verifyingContract && !isHexString(domain.verifyingContract, 20)) {
|
// Make a copy to isolate it from the object passed in
|
||||||
ensCache[domain.verifyingContract] = "0x";
|
domain = Object.assign({}, domain);
|
||||||
|
}
|
||||||
|
if (!isBytesLike(domain)) {
|
||||||
|
// Allow passing null to ignore value
|
||||||
|
for (const key in domain) {
|
||||||
|
if ((<Record<string, any>>domain)[key] == null) {
|
||||||
|
delete (<Record<string, any>>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
|
// 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);
|
ensCache[name] = await resolveName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the domain verifyingContract if needed
|
if (!isBytesLike(domain)) {
|
||||||
if (domain.verifyingContract && ensCache[domain.verifyingContract]) {
|
// Replace the domain verifyingContract if needed
|
||||||
domain.verifyingContract = ensCache[domain.verifyingContract];
|
if (domain.verifyingContract && ensCache[domain.verifyingContract]) {
|
||||||
|
domain.verifyingContract = ensCache[domain.verifyingContract];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace all ENS names with their address
|
// 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%%.
|
* 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<string, Array<TypedDataField>>, value: Record<string, any>, signature: SignatureLike): string {
|
export function verifyTypedData(domain: TypedDataDomain | BytesLike, types: Record<string, Array<TypedDataField>>, value: Record<string, any>, signature: SignatureLike): string {
|
||||||
return recoverAddress(TypedDataEncoder.hash(domain, types, value), signature);
|
return recoverAddress(TypedDataEncoder.hash(domain, types, value), signature);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import { hashMessage, TypedDataEncoder } from "../hash/index.js";
|
|||||||
import { AbstractSigner } from "../providers/index.js";
|
import { AbstractSigner } from "../providers/index.js";
|
||||||
import { computeAddress, Transaction } from "../transaction/index.js";
|
import { computeAddress, Transaction } from "../transaction/index.js";
|
||||||
import {
|
import {
|
||||||
defineProperties, resolveProperties, assert, assertArgument
|
defineProperties, resolveProperties, assert, assertArgument, BytesLike
|
||||||
} from "../utils/index.js";
|
} from "../utils/index.js";
|
||||||
|
|
||||||
import type { SigningKey } from "../crypto/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;
|
return this.signingKey.sign(hashMessage(message)).serialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
async signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string> {
|
/**
|
||||||
|
* DOMAIN_SEPARATOR can be passed into %%domain%% directly.
|
||||||
|
*/
|
||||||
|
async signTypedData(domain: TypedDataDomain | BytesLike, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string> {
|
||||||
|
|
||||||
// Populate any ENS names
|
// Populate any ENS names
|
||||||
const populated = await TypedDataEncoder.resolveNames(domain, types, value, async (name: string) => {
|
const populated = await TypedDataEncoder.resolveNames(domain, types, value, async (name: string) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user