Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add EIP-712 helpers
Browse files Browse the repository at this point in the history
horsefacts committed Nov 21, 2023
1 parent f026ad1 commit d939de2
Showing 10 changed files with 886 additions and 0 deletions.
46 changes: 46 additions & 0 deletions packages/core/src/eth/contracts/idGateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { HubAsyncResult, HubError } from "../../errors";
import { ResultAsync } from "neverthrow";
import { verifyTypedData, bytesToHex } from "viem";

export type IdGatewayRegisterMessage = {
to: `0x${string}`;
recovery: `0x${string}`;
nonce: bigint;
deadline: bigint;
};

export const ID_GATEWAY_ADDRESS = "0x00000000Fc25870C6eD6b6c7E41Fb078b7656f69" as const;

export const ID_GATEWAY_EIP_712_DOMAIN = {
name: "Farcaster IdGateway",
version: "1",
chainId: 10,
verifyingContract: ID_GATEWAY_ADDRESS,
} as const;

export const ID_GATEWAY_REGISTER_TYPE = [
{ name: "to", type: "address" },
{ name: "recovery", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
] as const;

export const verifyRegister = async (
message: IdGatewayRegisterMessage,
signature: Uint8Array,
address: Uint8Array,
): HubAsyncResult<boolean> => {
const valid = await ResultAsync.fromPromise(
verifyTypedData({
address: bytesToHex(address),
domain: ID_GATEWAY_EIP_712_DOMAIN,
types: { Register: ID_GATEWAY_REGISTER_TYPE },
primaryType: "Register",
message,
signature,
}),
(e) => new HubError("unknown", e as Error),
);

return valid;
};
118 changes: 118 additions & 0 deletions packages/core/src/eth/contracts/idRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { HubAsyncResult, HubError } from "../../errors";
import { ResultAsync } from "neverthrow";
import { verifyTypedData, bytesToHex } from "viem";

export type IdRegistryTransferMessage = {
fid: bigint;
to: `0x${string}`;
nonce: bigint;
deadline: bigint;
};

export type IdRegistryTransferAndChangeRecoveryMessage = {
fid: bigint;
to: `0x${string}`;
recovery: `0x${string}`;
nonce: bigint;
deadline: bigint;
};

export type IdRegistryChangeRecoveryAddressMessage = {
fid: bigint;
from: `0x${string}`;
to: `0x${string}`;
nonce: bigint;
deadline: bigint;
};

export const ID_REGISTRY_ADDRESS = "0x00000000Fc6c5F01Fc30151999387Bb99A9f489b" as const;

export const ID_REGISTRY_EIP_712_DOMAIN = {
name: "Farcaster IdRegistry",
version: "1",
chainId: 10,
verifyingContract: ID_REGISTRY_ADDRESS,
} as const;

export const ID_REGISTRY_TRANSFER_TYPE = [
{ name: "fid", type: "uint256" },
{ name: "to", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
] as const;

export const ID_REGISTRY_TRANSFER_AND_CHANGE_RECOVERY_TYPE = [
{ name: "fid", type: "uint256" },
{ name: "to", type: "address" },
{ name: "recovery", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
] as const;

export const ID_REGISTRY_CHANGE_RECOVERY_ADDRESS_TYPE = [
{ name: "fid", type: "uint256" },
{ name: "from", type: "address" },
{ name: "to", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
] as const;

export const verifyTransfer = async (
message: IdRegistryTransferMessage,
signature: Uint8Array,
address: Uint8Array,
): HubAsyncResult<boolean> => {
const valid = await ResultAsync.fromPromise(
verifyTypedData({
address: bytesToHex(address),
domain: ID_REGISTRY_EIP_712_DOMAIN,
types: { Transfer: ID_REGISTRY_TRANSFER_TYPE },
primaryType: "Transfer",
message,
signature,
}),
(e) => new HubError("unknown", e as Error),
);

return valid;
};

export const verifyTransferAndChangeRecovery = async (
message: IdRegistryTransferAndChangeRecoveryMessage,
signature: Uint8Array,
address: Uint8Array,
): HubAsyncResult<boolean> => {
const valid = await ResultAsync.fromPromise(
verifyTypedData({
address: bytesToHex(address),
domain: ID_REGISTRY_EIP_712_DOMAIN,
types: { TransferAndChangeRecovery: ID_REGISTRY_TRANSFER_AND_CHANGE_RECOVERY_TYPE },
primaryType: "TransferAndChangeRecovery",
message,
signature,
}),
(e) => new HubError("unknown", e as Error),
);

return valid;
};

export const verifyChangeRecoveryAddress = async (
message: IdRegistryChangeRecoveryAddressMessage,
signature: Uint8Array,
address: Uint8Array,
): HubAsyncResult<boolean> => {
const valid = await ResultAsync.fromPromise(
verifyTypedData({
address: bytesToHex(address),
domain: ID_REGISTRY_EIP_712_DOMAIN,
types: { ChangeRecoveryAddress: ID_REGISTRY_CHANGE_RECOVERY_ADDRESS_TYPE },
primaryType: "ChangeRecoveryAddress",
message,
signature,
}),
(e) => new HubError("unknown", e as Error),
);

return valid;
};
52 changes: 52 additions & 0 deletions packages/core/src/eth/contracts/keyGateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { HubAsyncResult, HubError } from "../../errors";
import { ResultAsync } from "neverthrow";
import { verifyTypedData, bytesToHex } from "viem";

export type KeyGatewayAddMessage = {
owner: `0x${string}`;
keyType: number;
key: Uint8Array;
metadataType: number;
metadata: `0x${string}`;
nonce: bigint;
deadline: bigint;
};

export const KEY_GATEWAY_ADDRESS = "0x00000000fC56947c7E7183f8Ca4B62398CaAdf0B" as const;

export const KEY_GATEWAY_EIP_712_DOMAIN = {
name: "Farcaster KeyGateway",
version: "1",
chainId: 10,
verifyingContract: KEY_GATEWAY_ADDRESS,
} as const;

export const KEY_GATEWAY_ADD_TYPE = [
{ name: "owner", type: "address" },
{ name: "keyType", type: "uint32" },
{ name: "key", type: "bytes" },
{ name: "metadataType", type: "uint8" },
{ name: "metadata", type: "bytes" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
] as const;

export const verifyAdd = async (
message: KeyGatewayAddMessage,
signature: Uint8Array,
address: Uint8Array,
): HubAsyncResult<boolean> => {
const valid = await ResultAsync.fromPromise(
verifyTypedData({
address: bytesToHex(address),
domain: KEY_GATEWAY_EIP_712_DOMAIN,
types: { Add: KEY_GATEWAY_ADD_TYPE },
primaryType: "Add",
message: { ...message, key: bytesToHex(message.key) },
signature,
}),
(e) => new HubError("unknown", e as Error),
);

return valid;
};
46 changes: 46 additions & 0 deletions packages/core/src/eth/contracts/keyRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { HubAsyncResult, HubError } from "../../errors";
import { ResultAsync } from "neverthrow";
import { verifyTypedData, bytesToHex } from "viem";

export type KeyRegistryRemoveMessage = {
owner: `0x${string}`;
key: Uint8Array;
nonce: bigint;
deadline: bigint;
};

export const KEY_REGISTRY_ADDRESS = "0x00000000fc1237824fb747abde0ff18990e59b7e" as const;

export const KEY_REGISTRY_EIP_712_DOMAIN = {
name: "Farcaster KeyRegistry",
version: "1",
chainId: 10,
verifyingContract: KEY_REGISTRY_ADDRESS,
} as const;

export const KEY_REGISTRY_REMOVE_TYPE = [
{ name: "owner", type: "address" },
{ name: "key", type: "bytes" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
] as const;

export const verifyRemove = async (
message: KeyRegistryRemoveMessage,
signature: Uint8Array,
address: Uint8Array,
): HubAsyncResult<boolean> => {
const valid = await ResultAsync.fromPromise(
verifyTypedData({
address: bytesToHex(address),
domain: KEY_REGISTRY_EIP_712_DOMAIN,
types: { Remove: KEY_REGISTRY_REMOVE_TYPE },
primaryType: "Remove",
message: { ...message, key: bytesToHex(message.key) },
signature,
}),
(e) => new HubError("unknown", e as Error),
);

return valid;
};
44 changes: 44 additions & 0 deletions packages/core/src/eth/contracts/signedKeyRequestValidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { HubAsyncResult, HubError } from "../../errors";
import { ResultAsync } from "neverthrow";
import { verifyTypedData, bytesToHex } from "viem";

export type SignedKeyRequestMessage = {
requestFid: bigint;
key: Uint8Array;
deadline: bigint;
};

export const SIGNED_KEY_REQUEST_VALIDATOR_ADDRESS = "0x00000000FC700472606ED4fA22623Acf62c60553" as const;

export const SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN = {
name: "Farcaster SignedKeyRequestValidator",
version: "1",
chainId: 10,
verifyingContract: SIGNED_KEY_REQUEST_VALIDATOR_ADDRESS,
} as const;

export const SIGNED_KEY_REQUEST_TYPE = [
{ name: "requestFid", type: "uint256" },
{ name: "key", type: "bytes" },
{ name: "deadline", type: "uint256" },
] as const;

export const verifyKeyRequest = async (
message: SignedKeyRequestMessage,
signature: Uint8Array,
address: Uint8Array,
): HubAsyncResult<boolean> => {
const valid = await ResultAsync.fromPromise(
verifyTypedData({
address: bytesToHex(address),
domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
types: { SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE },
primaryType: "SignedKeyRequest",
message: { ...message, key: bytesToHex(message.key) },
signature,
}),
(e) => new HubError("unknown", e as Error),
);

return valid;
};
20 changes: 20 additions & 0 deletions packages/core/src/signers/eip712Signer.ts
Original file line number Diff line number Diff line change
@@ -3,6 +3,15 @@ import { HubAsyncResult } from "../errors";
import { VerificationEthAddressClaim } from "../verifications";
import { UserNameProofClaim } from "../userNameProof";
import { Signer } from "./signer";
import { KeyGatewayAddMessage } from "../eth/contracts/keyGateway";
import { KeyRegistryRemoveMessage } from "../eth/contracts/keyRegistry";
import { IdGatewayRegisterMessage } from "../eth/contracts/idGateway";
import {
IdRegistryChangeRecoveryAddressMessage,
IdRegistryTransferAndChangeRecoveryMessage,
IdRegistryTransferMessage,
} from "../eth/contracts/idRegistry";
import { SignedKeyRequestMessage } from "../eth/contracts/signedKeyRequestValidator";

/**
* Extend this class to implement an EIP712 signer.
@@ -21,4 +30,15 @@ export abstract class Eip712Signer implements Signer {
chainId?: number,
): HubAsyncResult<Uint8Array>;
public abstract signUserNameProofClaim(claim: UserNameProofClaim): HubAsyncResult<Uint8Array>;
public abstract signRegister(message: IdGatewayRegisterMessage): HubAsyncResult<Uint8Array>;
public abstract signAdd(message: KeyGatewayAddMessage): HubAsyncResult<Uint8Array>;
public abstract signRemove(message: KeyRegistryRemoveMessage): HubAsyncResult<Uint8Array>;
public abstract signTransfer(message: IdRegistryTransferMessage): HubAsyncResult<Uint8Array>;
public abstract signTransferAndChangeRecovery(
message: IdRegistryTransferAndChangeRecoveryMessage,
): HubAsyncResult<Uint8Array>;
public abstract signChangeRecoveryAddress(
message: IdRegistryChangeRecoveryAddressMessage,
): HubAsyncResult<Uint8Array>;
public abstract signKeyRequest(message: SignedKeyRequestMessage): HubAsyncResult<Uint8Array>;
}
99 changes: 99 additions & 0 deletions packages/core/src/signers/ethersEip712Signer.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,31 @@ import {
EIP_712_USERNAME_DOMAIN,
EIP_712_USERNAME_PROOF,
} from "../crypto/eip712";
import {
ID_GATEWAY_EIP_712_DOMAIN,
ID_GATEWAY_REGISTER_TYPE,
IdGatewayRegisterMessage,
} from "../eth/contracts/idGateway";
import {
KeyRegistryRemoveMessage,
KEY_REGISTRY_EIP_712_DOMAIN,
KEY_REGISTRY_REMOVE_TYPE,
} from "../eth/contracts/keyRegistry";
import {
IdRegistryTransferMessage,
ID_REGISTRY_EIP_712_DOMAIN,
ID_REGISTRY_TRANSFER_TYPE,
ID_REGISTRY_TRANSFER_AND_CHANGE_RECOVERY_TYPE,
IdRegistryTransferAndChangeRecoveryMessage,
ID_REGISTRY_CHANGE_RECOVERY_ADDRESS_TYPE,
IdRegistryChangeRecoveryAddressMessage,
} from "../eth/contracts/idRegistry";
import { KeyGatewayAddMessage, KEY_GATEWAY_EIP_712_DOMAIN, KEY_GATEWAY_ADD_TYPE } from "../eth/contracts/keyGateway";
import {
SIGNED_KEY_REQUEST_TYPE,
SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
SignedKeyRequestMessage,
} from "../eth/contracts/signedKeyRequestValidator";

export type MinimalEthersSigner = Pick<Signer, "signTypedData" | "getAddress">;

@@ -70,4 +95,78 @@ export class EthersEip712Signer extends Eip712Signer {
// Convert hex signature to bytes
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signRegister(message: IdGatewayRegisterMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._ethersSigner.signTypedData(ID_GATEWAY_EIP_712_DOMAIN, { Register: [...ID_GATEWAY_REGISTER_TYPE] }, message),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signTransfer(message: IdRegistryTransferMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._ethersSigner.signTypedData(
ID_REGISTRY_EIP_712_DOMAIN,
{ Transfer: [...ID_REGISTRY_TRANSFER_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signTransferAndChangeRecovery(
message: IdRegistryTransferAndChangeRecoveryMessage,
): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._ethersSigner.signTypedData(
ID_REGISTRY_EIP_712_DOMAIN,
{ TransferAndChangeRecovery: [...ID_REGISTRY_TRANSFER_AND_CHANGE_RECOVERY_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signChangeRecoveryAddress(message: IdRegistryChangeRecoveryAddressMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._ethersSigner.signTypedData(
ID_REGISTRY_EIP_712_DOMAIN,
{ ChangeRecoveryAddress: [...ID_REGISTRY_CHANGE_RECOVERY_ADDRESS_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signAdd(message: KeyGatewayAddMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._ethersSigner.signTypedData(KEY_GATEWAY_EIP_712_DOMAIN, { Add: [...KEY_GATEWAY_ADD_TYPE] }, message),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signRemove(message: KeyRegistryRemoveMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._ethersSigner.signTypedData(KEY_REGISTRY_EIP_712_DOMAIN, { Remove: [...KEY_REGISTRY_REMOVE_TYPE] }, message),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signKeyRequest(message: SignedKeyRequestMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._ethersSigner.signTypedData(
SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
{ SignedKeyRequest: [...SIGNED_KEY_REQUEST_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}
}
107 changes: 107 additions & 0 deletions packages/core/src/signers/ethersV5Eip712Signer.ts
Original file line number Diff line number Diff line change
@@ -9,6 +9,31 @@ import { eip712 } from "../crypto";
import { hexStringToBytes } from "../bytes";
import { VerificationEthAddressClaim } from "../verifications";
import { UserNameProofClaim } from "../userNameProof";
import {
ID_GATEWAY_EIP_712_DOMAIN,
ID_GATEWAY_REGISTER_TYPE,
IdGatewayRegisterMessage,
} from "../eth/contracts/idGateway";
import {
KeyRegistryRemoveMessage,
KEY_REGISTRY_EIP_712_DOMAIN,
KEY_REGISTRY_REMOVE_TYPE,
} from "../eth/contracts/keyRegistry";
import {
ID_REGISTRY_CHANGE_RECOVERY_ADDRESS_TYPE,
ID_REGISTRY_EIP_712_DOMAIN,
ID_REGISTRY_TRANSFER_AND_CHANGE_RECOVERY_TYPE,
ID_REGISTRY_TRANSFER_TYPE,
IdRegistryChangeRecoveryAddressMessage,
IdRegistryTransferAndChangeRecoveryMessage,
IdRegistryTransferMessage,
} from "../eth/contracts/idRegistry";
import { KeyGatewayAddMessage, KEY_GATEWAY_EIP_712_DOMAIN, KEY_GATEWAY_ADD_TYPE } from "../eth/contracts/keyGateway";
import {
SIGNED_KEY_REQUEST_TYPE,
SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
SignedKeyRequestMessage,
} from "../eth/contracts/signedKeyRequestValidator";

export type TypedDataSigner = EthersAbstractSigner & EthersTypedDataSigner;

@@ -66,4 +91,86 @@ export class EthersV5Eip712Signer extends Eip712Signer {
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signRegister(message: IdGatewayRegisterMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._typedDataSigner._signTypedData(
ID_GATEWAY_EIP_712_DOMAIN,
{ Register: [...ID_GATEWAY_REGISTER_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signTransfer(message: IdRegistryTransferMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._typedDataSigner._signTypedData(
ID_REGISTRY_EIP_712_DOMAIN,
{ Transfer: [...ID_REGISTRY_TRANSFER_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signTransferAndChangeRecovery(
message: IdRegistryTransferAndChangeRecoveryMessage,
): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._typedDataSigner._signTypedData(
ID_REGISTRY_EIP_712_DOMAIN,
{ TransferAndChangeRecovery: [...ID_REGISTRY_TRANSFER_AND_CHANGE_RECOVERY_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signChangeRecoveryAddress(message: IdRegistryChangeRecoveryAddressMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._typedDataSigner._signTypedData(
ID_REGISTRY_EIP_712_DOMAIN,
{ ChangeRecoveryAddress: [...ID_REGISTRY_CHANGE_RECOVERY_ADDRESS_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signAdd(message: KeyGatewayAddMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._typedDataSigner._signTypedData(KEY_GATEWAY_EIP_712_DOMAIN, { Add: [...KEY_GATEWAY_ADD_TYPE] }, message),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signRemove(message: KeyRegistryRemoveMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._typedDataSigner._signTypedData(
KEY_REGISTRY_EIP_712_DOMAIN,
{ Remove: [...KEY_REGISTRY_REMOVE_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signKeyRequest(message: SignedKeyRequestMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._typedDataSigner._signTypedData(
SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
{ SignedKeyRequest: [...SIGNED_KEY_REQUEST_TYPE] },
message,
),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}
}
236 changes: 236 additions & 0 deletions packages/core/src/signers/testUtils.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,17 @@ import { makeVerificationEthAddressClaim, VerificationEthAddressClaim } from "..
import { makeUserNameProofClaim, UserNameProofClaim } from "../userNameProof";
import { Eip712Signer } from "./eip712Signer";
import { bytesToHex } from "viem";
import { IdGatewayRegisterMessage, verifyRegister } from "../eth/contracts/idGateway";
import { KeyRegistryRemoveMessage, verifyRemove } from "../eth/contracts/keyRegistry";
import {
IdRegistryChangeRecoveryAddressMessage,
IdRegistryTransferMessage,
verifyChangeRecoveryAddress,
verifyTransfer,
verifyTransferAndChangeRecovery,
} from "../eth/contracts/idRegistry";
import { KeyGatewayAddMessage, verifyAdd } from "../eth/contracts/keyGateway";
import { SignedKeyRequestMessage, verifyKeyRequest } from "../eth/contracts/signedKeyRequestValidator";

export const testEip712Signer = async (signer: Eip712Signer) => {
let signerKey: Uint8Array;
@@ -101,4 +112,229 @@ export const testEip712Signer = async (signer: Eip712Signer) => {
expect(result._unsafeUnwrapErr().errCode).toBe("bad_request.invalid_param");
});
});

describe("signRegister", () => {
let message: IdGatewayRegisterMessage;
let signature: Uint8Array;

beforeAll(async () => {
message = {
to: bytesToHex(signerKey),
recovery: bytesToHex(signerKey),
nonce: 0n,
deadline: BigInt(Math.floor(Date.now() / 1000)),
};
const signatureResult = await signer.signRegister(message);
expect(signatureResult.isOk()).toBeTruthy();
signature = signatureResult._unsafeUnwrap();
});

test("succeeds", async () => {
const valid = await verifyRegister(message, signature, signerKey);
expect(valid).toEqual(ok(true));
});

test("fails with HubError", async () => {
const result = await signer.signRegister({
...message,
deadline: -1n,
});
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr().errCode).toBe("bad_request.invalid_param");
});
});

describe("signTransfer", () => {
let message: IdRegistryTransferMessage;
let signature: Uint8Array;

beforeAll(async () => {
message = {
fid: 1n,
to: bytesToHex(signerKey),
nonce: 0n,
deadline: BigInt(Math.floor(Date.now() / 1000)),
};
const signatureResult = await signer.signTransfer(message);
expect(signatureResult.isOk()).toBeTruthy();
signature = signatureResult._unsafeUnwrap();
});

test("succeeds", async () => {
const valid = await verifyTransfer(message, signature, signerKey);
expect(valid).toEqual(ok(true));
});

test("fails with HubError", async () => {
const result = await signer.signTransfer({
...message,
deadline: -1n,
});
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr().errCode).toBe("bad_request.invalid_param");
});
});

describe("signTransferAndChangeRecovery", () => {
let message: IdRegistryTransferAndChangeRecoveryMessage;
let signature: Uint8Array;

beforeAll(async () => {
message = {
fid: 1n,
to: bytesToHex(signerKey),
recovery: bytesToHex(signerKey),
nonce: 0n,
deadline: BigInt(Math.floor(Date.now() / 1000)),
};
const signatureResult = await signer.signTransferAndChangeRecovery(message);
expect(signatureResult.isOk()).toBeTruthy();
signature = signatureResult._unsafeUnwrap();
});

test("succeeds", async () => {
const valid = await verifyTransferAndChangeRecovery(message, signature, signerKey);
expect(valid).toEqual(ok(true));
});

test("fails with HubError", async () => {
const result = await signer.signTransferAndChangeRecovery({
...message,
deadline: -1n,
});
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr().errCode).toBe("bad_request.invalid_param");
});
});

describe("signChangeRecoveryAddress", () => {
let message: IdRegistryChangeRecoveryAddressMessage;
let signature: Uint8Array;

beforeAll(async () => {
message = {
fid: 1n,
from: bytesToHex(signerKey),
to: bytesToHex(signerKey),
nonce: 0n,
deadline: BigInt(Math.floor(Date.now() / 1000)),
};
const signatureResult = await signer.signChangeRecoveryAddress(message);
expect(signatureResult.isOk()).toBeTruthy();
signature = signatureResult._unsafeUnwrap();
});

test("succeeds", async () => {
const valid = await verifyChangeRecoveryAddress(message, signature, signerKey);
expect(valid).toEqual(ok(true));
});

test("fails with HubError", async () => {
const result = await signer.signChangeRecoveryAddress({
...message,
deadline: -1n,
});
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr().errCode).toBe("bad_request.invalid_param");
});
});

describe("signAdd", () => {
let message: KeyGatewayAddMessage;
let signature: Uint8Array;

beforeAll(async () => {
const key = Factories.Bytes.build({}, { transient: { length: 65 } });
const metadata = Factories.Bytes.build({}, { transient: { length: 65 } });
message = {
owner: bytesToHex(signerKey),
keyType: 1,
key,
metadataType: 1,
metadata: bytesToHex(metadata),
nonce: 0n,
deadline: BigInt(Math.floor(Date.now() / 1000)),
};
const signatureResult = await signer.signAdd(message);
expect(signatureResult.isOk()).toBeTruthy();
signature = signatureResult._unsafeUnwrap();
});

test("succeeds", async () => {
const valid = await verifyAdd(message, signature, signerKey);
expect(valid).toEqual(ok(true));
});

test("fails with HubError", async () => {
const result = await signer.signAdd({
...message,
deadline: -1n,
});
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr().errCode).toBe("bad_request.invalid_param");
});
});

describe("signRemove", () => {
let message: KeyRegistryRemoveMessage;
let signature: Uint8Array;

beforeAll(async () => {
const key = Factories.Bytes.build({}, { transient: { length: 65 } });
message = {
owner: bytesToHex(signerKey),
key,
nonce: 0n,
deadline: BigInt(Math.floor(Date.now() / 1000)),
};
const signatureResult = await signer.signRemove(message);
expect(signatureResult.isOk()).toBeTruthy();
signature = signatureResult._unsafeUnwrap();
});

test("succeeds", async () => {
const valid = await verifyRemove(message, signature, signerKey);
expect(valid).toEqual(ok(true));
});

test("fails with HubError", async () => {
const result = await signer.signRemove({
...message,
deadline: -1n,
});
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr().errCode).toBe("bad_request.invalid_param");
});
});

describe("signKeyRequestMetadata", () => {
let message: SignedKeyRequestMessage;
let signature: Uint8Array;

beforeAll(async () => {
const key = Factories.Bytes.build({}, { transient: { length: 65 } });
message = {
requestFid: 1n,
key,
deadline: BigInt(Math.floor(Date.now() / 1000)),
};
const signatureResult = await signer.signKeyRequest(message);
expect(signatureResult.isOk()).toBeTruthy();
signature = signatureResult._unsafeUnwrap();
});

test("succeeds", async () => {
const valid = await verifyKeyRequest(message, signature, signerKey);
expect(valid).toEqual(ok(true));
});

test("fails with HubError", async () => {
const result = await signer.signKeyRequest({
...message,
deadline: -1n,
});
expect(result.isErr()).toBe(true);
expect(result._unsafeUnwrapErr().errCode).toBe("bad_request.invalid_param");
});
});
};
118 changes: 118 additions & 0 deletions packages/core/src/signers/viemLocalEip712Signer.ts
Original file line number Diff line number Diff line change
@@ -13,6 +13,31 @@ import { HubAsyncResult, HubError } from "../errors";
import { VerificationEthAddressClaim } from "../verifications";
import { UserNameProofClaim } from "../userNameProof";
import { Eip712Signer } from "./eip712Signer";
import {
ID_GATEWAY_EIP_712_DOMAIN,
ID_GATEWAY_REGISTER_TYPE,
IdGatewayRegisterMessage,
} from "../eth/contracts/idGateway";
import {
KeyRegistryRemoveMessage,
KEY_REGISTRY_EIP_712_DOMAIN,
KEY_REGISTRY_REMOVE_TYPE,
} from "../eth/contracts/keyRegistry";
import {
IdRegistryTransferMessage,
ID_REGISTRY_EIP_712_DOMAIN,
ID_REGISTRY_TRANSFER_TYPE,
ID_REGISTRY_TRANSFER_AND_CHANGE_RECOVERY_TYPE,
IdRegistryTransferAndChangeRecoveryMessage,
ID_REGISTRY_CHANGE_RECOVERY_ADDRESS_TYPE,
IdRegistryChangeRecoveryAddressMessage,
} from "../eth/contracts/idRegistry";
import { KEY_GATEWAY_ADD_TYPE, KEY_GATEWAY_EIP_712_DOMAIN, KeyGatewayAddMessage } from "../eth/contracts/keyGateway";
import {
SIGNED_KEY_REQUEST_TYPE,
SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
SignedKeyRequestMessage,
} from "../eth/contracts/signedKeyRequestValidator";

export class ViemLocalEip712Signer extends Eip712Signer {
private readonly _viemLocalAccount: LocalAccount<string>;
@@ -73,4 +98,97 @@ export class ViemLocalEip712Signer extends Eip712Signer {
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signRegister(message: IdGatewayRegisterMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._viemLocalAccount.signTypedData({
domain: ID_GATEWAY_EIP_712_DOMAIN,
types: { Register: ID_GATEWAY_REGISTER_TYPE },
primaryType: "Register",
message,
}),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signTransfer(message: IdRegistryTransferMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._viemLocalAccount.signTypedData({
domain: ID_REGISTRY_EIP_712_DOMAIN,
types: { Transfer: ID_REGISTRY_TRANSFER_TYPE },
primaryType: "Transfer",
message,
}),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signTransferAndChangeRecovery(
message: IdRegistryTransferAndChangeRecoveryMessage,
): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._viemLocalAccount.signTypedData({
domain: ID_REGISTRY_EIP_712_DOMAIN,
types: { TransferAndChangeRecovery: ID_REGISTRY_TRANSFER_AND_CHANGE_RECOVERY_TYPE },
primaryType: "TransferAndChangeRecovery",
message,
}),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signChangeRecoveryAddress(message: IdRegistryChangeRecoveryAddressMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._viemLocalAccount.signTypedData({
domain: ID_REGISTRY_EIP_712_DOMAIN,
types: { ChangeRecoveryAddress: ID_REGISTRY_CHANGE_RECOVERY_ADDRESS_TYPE },
primaryType: "ChangeRecoveryAddress",
message,
}),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signAdd(message: KeyGatewayAddMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._viemLocalAccount.signTypedData({
domain: KEY_GATEWAY_EIP_712_DOMAIN,
types: { Add: KEY_GATEWAY_ADD_TYPE },
primaryType: "Add",
message: { ...message, key: bytesToHex(message.key) },
}),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signRemove(message: KeyRegistryRemoveMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._viemLocalAccount.signTypedData({
domain: KEY_REGISTRY_EIP_712_DOMAIN,
types: { Remove: KEY_REGISTRY_REMOVE_TYPE },
primaryType: "Remove",
message: { ...message, key: bytesToHex(message.key) },
}),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}

public async signKeyRequest(message: SignedKeyRequestMessage): HubAsyncResult<Uint8Array> {
const hexSignature = await ResultAsync.fromPromise(
this._viemLocalAccount.signTypedData({
domain: SIGNED_KEY_REQUEST_VALIDATOR_EIP_712_DOMAIN,
types: { SignedKeyRequest: SIGNED_KEY_REQUEST_TYPE },
primaryType: "SignedKeyRequest",
message: { ...message, key: bytesToHex(message.key) },
}),
(e) => new HubError("bad_request.invalid_param", e as Error),
);
return hexSignature.andThen((hex) => hexStringToBytes(hex));
}
}

0 comments on commit d939de2

Please sign in to comment.