viem
Version:
119 lines • 4.55 kB
JavaScript
import { BytesSizeMismatchError } from '../errors/abi.js';
import { InvalidAddressError } from '../errors/address.js';
import { InvalidDomainError, InvalidPrimaryTypeError, InvalidStructTypeError, } from '../errors/typedData.js';
import { isAddress } from './address/isAddress.js';
import { size } from './data/size.js';
import { numberToHex } from './encoding/toHex.js';
import { bytesRegex, integerRegex } from './regex.js';
import { hashDomain, } from './signature/hashTypedData.js';
import { stringify } from './stringify.js';
export function serializeTypedData(parameters) {
const { domain: domain_, message: message_, primaryType, types, } = parameters;
const normalizeData = (struct, data_) => {
const data = { ...data_ };
for (const param of struct) {
const { name, type } = param;
if (type === 'address')
data[name] = data[name].toLowerCase();
}
return data;
};
const domain = (() => {
if (!types.EIP712Domain)
return {};
if (!domain_)
return {};
return normalizeData(types.EIP712Domain, domain_);
})();
const message = (() => {
if (primaryType === 'EIP712Domain')
return undefined;
return normalizeData(types[primaryType], message_);
})();
return stringify({ domain, message, primaryType, types });
}
export function validateTypedData(parameters) {
const { domain, message, primaryType, types } = parameters;
const validateData = (struct, data) => {
for (const param of struct) {
const { name, type } = param;
const value = data[name];
const integerMatch = type.match(integerRegex);
if (integerMatch &&
(typeof value === 'number' || typeof value === 'bigint')) {
const [_type, base, size_] = integerMatch;
// If number cannot be cast to a sized hex value, it is out of range
// and will throw.
numberToHex(value, {
signed: base === 'int',
size: Number.parseInt(size_) / 8,
});
}
if (type === 'address' && typeof value === 'string' && !isAddress(value))
throw new InvalidAddressError({ address: value });
const bytesMatch = type.match(bytesRegex);
if (bytesMatch) {
const [_type, size_] = bytesMatch;
if (size_ && size(value) !== Number.parseInt(size_))
throw new BytesSizeMismatchError({
expectedSize: Number.parseInt(size_),
givenSize: size(value),
});
}
const struct = types[type];
if (struct) {
validateReference(type);
validateData(struct, value);
}
}
};
// Validate domain types.
if (types.EIP712Domain && domain) {
if (typeof domain !== 'object')
throw new InvalidDomainError({ domain });
validateData(types.EIP712Domain, domain);
}
// Validate message types.
if (primaryType !== 'EIP712Domain') {
if (types[primaryType])
validateData(types[primaryType], message);
else
throw new InvalidPrimaryTypeError({ primaryType, types });
}
}
export function getTypesForEIP712Domain({ domain, }) {
return [
typeof domain?.name === 'string' && { name: 'name', type: 'string' },
domain?.version && { name: 'version', type: 'string' },
(typeof domain?.chainId === 'number' ||
typeof domain?.chainId === 'bigint') && {
name: 'chainId',
type: 'uint256',
},
domain?.verifyingContract && {
name: 'verifyingContract',
type: 'address',
},
domain?.salt && { name: 'salt', type: 'bytes32' },
].filter(Boolean);
}
export function domainSeparator({ domain }) {
return hashDomain({
domain,
types: {
EIP712Domain: getTypesForEIP712Domain({ domain }),
},
});
}
/** @internal */
function validateReference(type) {
// Struct type must not be a Solidity type.
if (type === 'address' ||
type === 'bool' ||
type === 'string' ||
type.startsWith('bytes') ||
type.startsWith('uint') ||
type.startsWith('int'))
throw new InvalidStructTypeError({ type });
}
//# sourceMappingURL=typedData.js.map