Skip to content

Commit a1a7c0a

Browse files
authored
Add meta-transaction relayer endpoint (#289)
* Init relayer * Add meta-transaction relayer endpoint * Update to engine native flow * Add support for relayer * Update order * Add description * Skip auth check for relayer * Relayer is actually working with SDK
1 parent 3697d88 commit a1a7c0a

File tree

11 files changed

+404
-3
lines changed

11 files changed

+404
-3
lines changed

src/db/relayer/getRelayerById.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { prisma } from "../client";
2+
3+
interface GetRelayerByIdParams {
4+
id: string;
5+
}
6+
7+
export const getRelayerById = async ({ id }: GetRelayerByIdParams) => {
8+
const relayer = await prisma.relayers.findUnique({
9+
where: {
10+
id,
11+
},
12+
});
13+
14+
if (!relayer) {
15+
return null;
16+
}
17+
18+
return {
19+
...relayer,
20+
chainId: parseInt(relayer.chainId),
21+
allowedContracts: relayer.allowedContracts
22+
? (JSON.parse(relayer.allowedContracts).map((contractAddress: string) =>
23+
contractAddress.toLowerCase(),
24+
) as string[])
25+
: null,
26+
};
27+
};

src/db/transactions/queueTx.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const queueTx = async ({
4545
const txTableData = {
4646
chainId: chainId.toString(),
4747
functionName: tx.getMethod(),
48-
functionArgs: tx.getArgs().toString(),
48+
functionArgs: JSON.stringify(tx.getArgs()),
4949
extension,
5050
deployedContractAddress: deployedContractAddress,
5151
deployedContractType: deployedContractType,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- CreateTable
2+
CREATE TABLE "relayers" (
3+
"id" TEXT NOT NULL,
4+
"name" TEXT,
5+
"chainId" TEXT NOT NULL,
6+
"backendWalletAddress" TEXT NOT NULL,
7+
"allowedContracts" TEXT,
8+
9+
CONSTRAINT "relayers_pkey" PRIMARY KEY ("id")
10+
);

src/prisma/schema.prisma

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,13 @@ model Webhooks {
161161
162162
@@map("webhooks")
163163
}
164+
165+
model Relayers {
166+
id String @id @default(uuid()) @map("id")
167+
name String? @map("name")
168+
chainId String @map("chainId")
169+
backendWalletAddress String @map("backendWalletAddress")
170+
allowedContracts String? @map("allowedContracts")
171+
172+
@@map("relayers")
173+
}

src/schema/extension.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export type ContractExtension =
1010
| "deploy-prebuilt"
1111
| "deploy-published"
1212
| "account-factory"
13-
| "account";
13+
| "account"
14+
| "forwarder";

src/server/middleware/auth.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,22 @@ export const withAuth = async (server: FastifyInstance) => {
168168
req.url.includes("/auth/login") ||
169169
req.url.includes("/auth/user") ||
170170
req.url.includes("/auth/switch-account") ||
171-
req.url.includes("/auth/logout")
171+
req.url.includes("/auth/logout") ||
172+
req.url.includes("/transaction/status")
172173
) {
173174
// We skip auth check for static endpoints and auth routes
174175
return;
175176
}
176177

178+
if (
179+
req.url.includes("/relayer") &&
180+
!req.url.includes("/create") &&
181+
!req.url.includes("/revoke")
182+
) {
183+
// Relayer endpoints can handle their own authentication
184+
return;
185+
}
186+
177187
// TODO: Enable authentication check for websocket requests
178188
if (
179189
req.headers.upgrade &&

src/server/routes/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ import { getAuthConfiguration } from "./configuration/auth/get";
7777
import { updateAuthConfiguration } from "./configuration/auth/update";
7878
import { accountRoutes } from "./contract/extensions/account";
7979
import { accountFactoryRoutes } from "./contract/extensions/accountFactory";
80+
import { relayTransaction } from "./relayer";
81+
import { createRelayer } from "./relayer/create";
82+
import { getAllRelayers } from "./relayer/getAll";
83+
import { revokeRelayer } from "./relayer/revoke";
8084

8185
export const withRoutes = async (fastify: FastifyInstance) => {
8286
// Wallet
@@ -119,6 +123,12 @@ export const withRoutes = async (fastify: FastifyInstance) => {
119123
await fastify.register(getChainData);
120124
await fastify.register(getAllChainData);
121125

126+
// Relayer
127+
await fastify.register(getAllRelayers);
128+
await fastify.register(createRelayer);
129+
await fastify.register(revokeRelayer);
130+
await fastify.register(relayTransaction);
131+
122132
// Generic
123133
await fastify.register(readContract);
124134
await fastify.register(writeToContract);

src/server/routes/relayer/create.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Static, Type } from "@sinclair/typebox";
2+
import { FastifyInstance } from "fastify";
3+
import { StatusCodes } from "http-status-codes";
4+
import { prisma } from "../../../db/client";
5+
import { getChainIdFromChain } from "../../utils/chain";
6+
7+
const BodySchema = Type.Object({
8+
chain: Type.String(),
9+
backendWalletAddress: Type.String({
10+
description:
11+
"The address of the backend wallet to use for relaying transactions.",
12+
}),
13+
allowedContracts: Type.Optional(Type.Array(Type.String())),
14+
});
15+
16+
const ReplySchema = Type.Object({
17+
result: Type.Object({
18+
relayerId: Type.String(),
19+
}),
20+
});
21+
22+
export async function createRelayer(fastify: FastifyInstance) {
23+
fastify.route<{
24+
Reply: Static<typeof ReplySchema>;
25+
Body: Static<typeof BodySchema>;
26+
}>({
27+
method: "POST",
28+
url: "/relayer/create",
29+
schema: {
30+
summary: "Create a new meta-transaction relayer",
31+
description: "Create a new meta-transaction relayer",
32+
tags: ["Relayer"],
33+
operationId: "create",
34+
body: BodySchema,
35+
response: {
36+
[StatusCodes.OK]: ReplySchema,
37+
},
38+
},
39+
handler: async (req, res) => {
40+
const { chain, backendWalletAddress, allowedContracts } = req.body;
41+
42+
const relayer = await prisma.relayers.create({
43+
data: {
44+
chainId: getChainIdFromChain(chain).toString(),
45+
backendWalletAddress,
46+
allowedContracts: allowedContracts
47+
? JSON.stringify(allowedContracts)
48+
: null,
49+
},
50+
});
51+
52+
return res.status(200).send({
53+
result: {
54+
relayerId: relayer.id,
55+
},
56+
});
57+
},
58+
});
59+
}

src/server/routes/relayer/getAll.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Type } from "@sinclair/typebox";
2+
import { FastifyInstance } from "fastify";
3+
import { StatusCodes } from "http-status-codes";
4+
import { prisma } from "../../../db/client";
5+
6+
const ReplySchema = Type.Object({
7+
result: Type.Array(
8+
Type.Object({
9+
id: Type.String(),
10+
name: Type.Union([Type.String(), Type.Null()]),
11+
chainId: Type.String(),
12+
backendWalletAddress: Type.String(),
13+
allowedContracts: Type.Union([Type.Array(Type.String()), Type.Null()]),
14+
}),
15+
),
16+
});
17+
18+
export async function getAllRelayers(fastify: FastifyInstance) {
19+
fastify.route({
20+
method: "GET",
21+
url: "/relayer/get-all",
22+
schema: {
23+
summary: "Get all meta-transaction relayers",
24+
description: "Get all meta-transaction relayers",
25+
tags: ["Relayer"],
26+
operationId: "getAll",
27+
response: {
28+
[StatusCodes.OK]: ReplySchema,
29+
},
30+
},
31+
handler: async (req, res) => {
32+
const relayers = await prisma.relayers.findMany();
33+
34+
return res.status(200).send({
35+
result: relayers.map((relayer) => ({
36+
...relayer,
37+
allowedContracts: relayer.allowedContracts
38+
? JSON.parse(relayer.allowedContracts)
39+
: null,
40+
})),
41+
});
42+
},
43+
});
44+
}

0 commit comments

Comments
 (0)