Skip to content

Commit

Permalink
add missing addresses for premint as to not break the tests
Browse files Browse the repository at this point in the history
  • Loading branch information
oveddan committed Sep 11, 2023
1 parent 01b9ec0 commit 20a8648
Show file tree
Hide file tree
Showing 4 changed files with 458 additions and 42 deletions.
247 changes: 247 additions & 0 deletions package/hashTypedData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import { AbiParameter } from "abitype";
import {
Address,
HashTypedDataParameters,
Hex,
TypedData,
TypedDataDomain,
TypedDataParameter,
concat,
encodeAbiParameters,
keccak256,
toHex,
} from "viem";

export type MessageTypeProperty = {
name: string;
type: string;
};

export function hashedTypedDataV4<
const TTypedData extends TypedData | { [key: string]: unknown },
TPrimaryType extends string = string
>({
structHash,
chainId,
verifyingContract,
_types,
name,
version,
}: {
structHash: `0x${string}`;
verifyingContract: Address;
chainId: number;
_types: HashTypedDataParameters<TTypedData, TPrimaryType>["types"];
name: string;
version: string;
}) {
const domain: TypedDataDomain = {
chainId,
name,
version,
verifyingContract,
};

const types = {
EIP712Domain: getTypesForEIP712Domain({ domain }),
...(_types as TTypedData),
};

const parts: Hex[] = ["0x1901"];

// hash the eip 712 domain, verifying contract, and chain id.
parts.push(
hashDomain({
domain,
types: types as Record<string, MessageTypeProperty[]>,
})
);

// append the already hashed message
parts.push(structHash);

return keccak256(concat(parts));
}

export function hashDomain({
domain,
types,
}: {
domain: TypedDataDomain;
types: Record<string, MessageTypeProperty[]>;
}) {
return hashStruct({
data: domain,
primaryType: "EIP712Domain",
types,
});
}

function hashStruct({
data,
primaryType,
types,
}: {
data: Record<string, unknown>;
primaryType: string;
types: Record<string, MessageTypeProperty[]>;
}) {
const encoded = encodeData({
data,
primaryType,
types,
});
return keccak256(encoded);
}

function encodeData({
data,
primaryType,
types,
}: {
data: Record<string, unknown>;
primaryType: string;
types: Record<string, MessageTypeProperty[]>;
}) {
const encodedTypes: AbiParameter[] = [{ type: "bytes32" }];
const encodedValues: unknown[] = [hashType({ primaryType, types })];

for (const field of types[primaryType]!) {
const [type, value] = encodeField({
types,
name: field.name,
type: field.type,
value: data[field.name],
});
encodedTypes.push(type);
encodedValues.push(value);
}

return encodeAbiParameters(encodedTypes, encodedValues);
}

function hashType({
primaryType,
types,
}: {
primaryType: string;
types: Record<string, MessageTypeProperty[]>;
}) {
const encodedHashType = toHex(encodeType({ primaryType, types }));
return keccak256(encodedHashType);
}

function encodeType({
primaryType,
types,
}: {
primaryType: string;
types: Record<string, MessageTypeProperty[]>;
}) {
let result = "";
const unsortedDeps = findTypeDependencies({ primaryType, types });
unsortedDeps.delete(primaryType);

const deps = [primaryType, ...Array.from(unsortedDeps).sort()];
for (const type of deps) {
result += `${type}(${types[type]!.map(
({ name, type: t }) => `${t} ${name}`
).join(",")})`;
}

return result;
}

function findTypeDependencies(
{
primaryType: primaryType_,
types,
}: {
primaryType: string;
types: Record<string, MessageTypeProperty[]>;
},
results: Set<string> = new Set()
): Set<string> {
const match = primaryType_.match(/^\w*/u);
const primaryType = match?.[0]!;
if (results.has(primaryType) || types[primaryType] === undefined) {
return results;
}

results.add(primaryType);

for (const field of types[primaryType]!) {
findTypeDependencies({ primaryType: field.type, types }, results);
}
return results;
}

function encodeField({
types,
name,
type,
value,
}: {
types: Record<string, MessageTypeProperty[]>;
name: string;
type: string;
value: any;
}): [type: AbiParameter, value: any] {
if (types[type] !== undefined) {
return [
{ type: "bytes32" },
keccak256(encodeData({ data: value, primaryType: type, types })),
];
}

if (type === "bytes") {
const prepend = value.length % 2 ? "0" : "";
value = `0x${prepend + value.slice(2)}`;
return [{ type: "bytes32" }, keccak256(value)];
}

if (type === "string") return [{ type: "bytes32" }, keccak256(toHex(value))];

if (type.lastIndexOf("]") === type.length - 1) {
const parsedType = type.slice(0, type.lastIndexOf("["));
const typeValuePairs = (value as [AbiParameter, any][]).map((item) =>
encodeField({
name,
type: parsedType,
types,
value: item,
})
);
return [
{ type: "bytes32" },
keccak256(
encodeAbiParameters(
typeValuePairs.map(([t]) => t),
typeValuePairs.map(([, v]) => v)
)
),
];
}

return [{ type }, value];
}

export function getTypesForEIP712Domain({
domain,
}: {
domain?: TypedDataDomain;
}): TypedDataParameter[] {
return [
typeof domain?.name === "string" && { name: "name", type: "string" },
domain?.version && { name: "version", type: "string" },
typeof domain?.chainId === "number" && {
name: "chainId",
type: "uint256",
},
domain?.verifyingContract && {
name: "verifyingContract",
type: "address",
},
domain?.salt && { name: "salt", type: "bytes32" },
].filter(Boolean) as TypedDataParameter[];
}
Loading

0 comments on commit 20a8648

Please sign in to comment.