Skip to content

Commit 350fbee

Browse files
committed
feat: implement signature request controller
Without this patch the user has no way of canceling signature requests. This patch introduces a status field. It has 3 possible values: PENDING, CANCELED and EXECUTED. The controller allows canceling the signature request only when it's in status PENDING and the caller authenticates as one of the owners of the Safe.
1 parent b18ef34 commit 350fbee

File tree

4 files changed

+264
-0
lines changed

4 files changed

+264
-0
lines changed

src/__generated__/routes/routes.ts

Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/__generated__/swagger.json

Lines changed: 88 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import {
2+
Body,
3+
Controller,
4+
Post,
5+
Path,
6+
Response,
7+
Route,
8+
SuccessResponse,
9+
Tags,
10+
} from "tsoa";
11+
12+
import { SupabaseDataService } from "../services/SupabaseDataService.js";
13+
import { verifyAuthSignedData } from "../utils/verifyAuthSignedData.js";
14+
15+
interface CancelSignatureRequest {
16+
signature: string;
17+
owner_address: string;
18+
chain_id: number;
19+
}
20+
21+
@Route("v1/signature-requests")
22+
@Tags("SignatureRequests")
23+
export class SignatureRequestController extends Controller {
24+
private readonly dataService: SupabaseDataService;
25+
26+
constructor() {
27+
super();
28+
this.dataService = new SupabaseDataService();
29+
}
30+
31+
@Post("{safe_address}-{message_hash}/cancel")
32+
@SuccessResponse(200, "Signature request canceled successfully")
33+
@Response(401, "Unauthorized")
34+
@Response(404, "Signature request not found")
35+
public async cancelSignatureRequest(
36+
@Path() safe_address: string,
37+
@Path() message_hash: string,
38+
@Body() requestBody: CancelSignatureRequest,
39+
): Promise<{ success: boolean; message: string }> {
40+
if (
41+
!(await this.isValidSignature(safe_address, message_hash, requestBody))
42+
) {
43+
return this.errorResponse("Unauthorized", 401);
44+
}
45+
46+
const signatureRequest = await this.dataService.getSignatureRequest(
47+
safe_address,
48+
message_hash,
49+
);
50+
if (!signatureRequest) {
51+
return this.errorResponse("Signature request not found", 404);
52+
}
53+
54+
switch (signatureRequest.status) {
55+
case "executed":
56+
return this.errorResponse(
57+
"Signature request has already been executed",
58+
);
59+
60+
case "canceled":
61+
return this.successResponse("Signature request canceled successfully");
62+
63+
case "pending":
64+
await this.dataService.updateSignatureRequestStatus(
65+
safe_address,
66+
message_hash,
67+
"canceled",
68+
);
69+
return this.successResponse("Signature request canceled successfully");
70+
71+
default:
72+
return this.errorResponse(
73+
`Invalid signature request status: ${signatureRequest.status}`,
74+
);
75+
}
76+
}
77+
78+
async isValidSignature(
79+
safeAddress: string,
80+
messageHash: string,
81+
requestBody: CancelSignatureRequest,
82+
): Promise<boolean> {
83+
const { signature, owner_address, chain_id } = requestBody;
84+
const id = `${safeAddress}-${messageHash}`;
85+
86+
return verifyAuthSignedData({
87+
address: owner_address as `0x${string}`,
88+
types: {
89+
SignatureRequest: [
90+
{ name: "cancelSignatureRequestId", type: "string" },
91+
],
92+
},
93+
primaryType: "SignatureRequest",
94+
signature: signature as `0x${string}`,
95+
message: {
96+
cancelSignatureRequestId: id,
97+
},
98+
requiredChainId: chain_id,
99+
});
100+
}
101+
102+
successResponse(message: string) {
103+
return { success: true, message };
104+
}
105+
106+
errorResponse(message: string, status = 400) {
107+
this.setStatus(status);
108+
return { success: false, message };
109+
}
110+
}

src/services/SupabaseDataService.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,28 @@ export class SupabaseDataService extends BaseSupabaseService<KyselyDataDatabase>
673673
.execute();
674674
}
675675

676+
async getSignatureRequest(safe_address: string, message_hash: string) {
677+
return this.db
678+
.selectFrom("signature_requests")
679+
.selectAll()
680+
.where("safe_address", "=", safe_address)
681+
.where("message_hash", "=", message_hash)
682+
.executeTakeFirst();
683+
}
684+
685+
async updateSignatureRequestStatus(
686+
safe_address: string,
687+
message_hash: string,
688+
status: DataDatabase["public"]["Enums"]["signature_request_status_enum"],
689+
) {
690+
return this.db
691+
.updateTable("signature_requests")
692+
.set({ status })
693+
.where("safe_address", "=", safe_address)
694+
.where("message_hash", "=", message_hash)
695+
.execute();
696+
}
697+
676698
getSignatureRequests(args: GetSignatureRequestArgs) {
677699
return {
678700
data: this.handleGetData("signature_requests", args),

0 commit comments

Comments
 (0)