Skip to content

Commit

Permalink
add creator to the event
Browse files Browse the repository at this point in the history
wip on preminter test

wip on scanning for the event; currently we cannot deploy the contract cause its too large :(

added test that shows how to recover the signer
  • Loading branch information
oveddan committed Sep 11, 2023
1 parent 01b9ec0 commit 100222d
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 100222d

Please sign in to comment.