Skip to content

Commit

Permalink
feat: fixes and tests
Browse files Browse the repository at this point in the history
Signed-off-by: Pablo Maldonado <[email protected]>
  • Loading branch information
md0x committed Dec 11, 2024
1 parent 5f174ec commit 78d297b
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 47 deletions.
15 changes: 8 additions & 7 deletions api/_exclusivity/utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ethers } from "ethers";

export const MAX_MESSAGE_AGE_SECONDS = 300;

// TODO: get this from gh
export const whiteListedRelayers = [
"0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", // dev wallet
];
export const getWhiteListedRelayers = () => {
return [
"0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", // dev wallet
];
};

export const getRelayerFromSignature = async (
signature: string,
message: string
) => {
export const getRelayerFromSignature = (signature: string, message: string) => {
return ethers.utils.verifyMessage(message, signature);
};

Expand Down
4 changes: 4 additions & 0 deletions api/_types/generic.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ import { VercelRequest } from "@vercel/node";

export type TypedVercelRequest<T> = VercelRequest & {
query: Partial<T>;
body: T;
headers: {
signature: string;
};
};
51 changes: 11 additions & 40 deletions api/relayer-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,25 @@ import { VercelResponse } from "@vercel/node";
import {
getRelayerFromSignature,
isTimestampValid,
whiteListedRelayers,
getWhiteListedRelayers,
MAX_MESSAGE_AGE_SECONDS,
} from "./_exclusivity/utils";
import { TypedVercelRequest } from "./_types";

export const MAX_MESSAGE_AGE_SECONDS = 300;

const handleGetRequest = async (
request: TypedVercelRequest<any>,
response: VercelResponse
) => {
const { signature, message } = request.query;

const { timestamp, ...restOfMessage } = JSON.parse(message);
if (!isTimestampValid(timestamp, MAX_MESSAGE_AGE_SECONDS)) {
return response.status(400).json({ message: "Message too old" });
}

const relayer = await getRelayerFromSignature(
signature,
JSON.stringify(restOfMessage)
);

if (!whiteListedRelayers.includes(relayer)) {
return response.status(401).json({ message: "Unauthorized" });
}

// Handle authenticated GET request
response.status(200).json({ message: "Authenticated GET request received" });
};

const handlePostRequest = async (
request: TypedVercelRequest<any>,
response: VercelResponse
) => {
const { signature, message } = request.body;

const { timestamp, ...restOfMessage } = message;
const { message } = request.body;
const { signature } = request.headers;
const { timestamp } = JSON.parse(message);
if (!isTimestampValid(timestamp, MAX_MESSAGE_AGE_SECONDS)) {
return response.status(400).json({ message: "Message too old" });
}

const relayer = await getRelayerFromSignature(
signature,
JSON.stringify(restOfMessage)
);
const relayer = getRelayerFromSignature(signature, message);

if (!whiteListedRelayers.includes(relayer)) {
if (!getWhiteListedRelayers().includes(relayer)) {
return response.status(401).json({ message: "Unauthorized" });
}

Expand All @@ -60,12 +32,11 @@ const handler = async (
request: TypedVercelRequest<any>,
response: VercelResponse
) => {
if (request.method && ["GET", "POST"].includes(request.method)) {
return request.method === "GET"
? handleGetRequest(request, response)
: handlePostRequest(request, response);
if (request.method === "POST") {
return handlePostRequest(request, response);
} else {
response.setHeader("Allow", ["GET", "POST"]);
// TODO: Add support for GET requests in the future
response.setHeader("Allow", ["POST"]);
response.status(405).end(`Method ${request.method} Not Allowed`);
}
};
Expand Down
95 changes: 95 additions & 0 deletions test/api/relayer-config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import handler from "../../api/relayer-config";
import { TypedVercelRequest } from "../../api/_types";
import { VercelResponse } from "@vercel/node";
import { ethers } from "ethers";
import * as utils from "../../api/_exclusivity/utils";
const { MAX_MESSAGE_AGE_SECONDS } = utils;

const getMockedResponse = () => {
const response: any = {};
response.status = jest.fn().mockReturnValue(response);
response.json = jest.fn();
response.setHeader = jest.fn();
return response as VercelResponse;
};

describe("Relayer Config API", () => {
let response: VercelResponse;
const whitelistedRelayer = ethers.Wallet.createRandom();
const unauthorizedRelayer = ethers.Wallet.createRandom();

beforeEach(() => {
response = getMockedResponse();
jest
.spyOn(utils, "getWhiteListedRelayers")
.mockReturnValue([whitelistedRelayer.address]);
});

test("POST request with valid timestamp", async () => {
const message = {
timestamp: Date.now() / 1000,
};
const messageString = JSON.stringify(message);
const signature = await whitelistedRelayer.signMessage(messageString);

const request = {
method: "POST",
headers: {
signature: signature,
},
body: {
message: messageString,
} as any,
} as TypedVercelRequest<any>;

await handler(request, response);

expect(response.status).toHaveBeenCalledWith(200);
});

test("POST request with invalid timestamp", async () => {
const message = {
timestamp: Date.now() / 1000 - MAX_MESSAGE_AGE_SECONDS - 1,
};
const signature = await whitelistedRelayer.signMessage(
JSON.stringify(message)
);

const request = {
method: "POST",
headers: {
signature: signature,
},
body: {
message: JSON.stringify(message),
} as any,
} as TypedVercelRequest<any>;

await handler(request, response);

expect(response.status).toHaveBeenCalledWith(400);
expect(response.json).toHaveBeenCalledWith({ message: "Message too old" });
});

test("POST request with invalid signature", async () => {
const message = {
timestamp: Date.now() / 1000,
};
const signature = await unauthorizedRelayer.signMessage(
JSON.stringify(message)
);

const request = {
method: "POST",
headers: {
signature: signature,
},
body: { message: JSON.stringify(message) } as any,
} as TypedVercelRequest<any>;

await handler(request, response);

expect(response.status).toHaveBeenCalledWith(401);
expect(response.json).toHaveBeenCalledWith({ message: "Unauthorized" });
});
});

0 comments on commit 78d297b

Please sign in to comment.