feat: adding relayer fee commitment with signed message

This commit is contained in:
Francisco Bezzecchi
2025-04-09 18:56:23 -03:00
parent b856d407c9
commit ff46f68ab0
5 changed files with 74 additions and 9 deletions

View File

@@ -1,9 +1,12 @@
import { NextFunction, Request, Response } from "express";
import { getAddress } from "viem";
import { getAssetConfig } from "../../config/index.js";
import { getAssetConfig, getFeeReceiverAddress } from "../../config/index.js";
import { QuoterError } from "../../exceptions/base.exception.js";
import { quoteProvider, web3Provider } from "../../providers/index.js";
import { QuoteMarshall } from "../../types.js";
import { encodeWithdrawalData } from "../../utils.js";
const TIME_20_SECS = 20 * 1000;
export async function relayQuoteHandler(
req: Request,
@@ -25,11 +28,28 @@ export async function relayQuoteHandler(
const quote = await quoteProvider.quoteNativeTokenInERC20(chainId, tokenAddress, amountIn);
const feeBPS = await quoteProvider.quoteFeeBPSNative(config.fee_bps, amountIn, quote, gasPrice, value);
const recipient = req.body.recipient ? getAddress(req.body.recipient.toString()) : undefined
const quoteResponse = new QuoteMarshall({
baseFeeBPS: config.fee_bps,
feeBPS,
});
if (recipient) {
const feeReceiverAddress = getFeeReceiverAddress(chainId);
const withdrawalData = encodeWithdrawalData({
feeRecipient: getAddress(feeReceiverAddress),
recipient,
relayFeeBPS: feeBPS
})
const expiration = Number(new Date()) + TIME_20_SECS
const relayerCommitment = { withdrawalData, expiration };
const signedRelayerCommitment = await web3Provider.signRelayerCommitment(chainId, relayerCommitment);
quoteResponse.addFeeCommitment({ expiration, withdrawalData, signedRelayerCommitment })
}
res
.status(200)
.json(res.locals.marshalResponse(new QuoteMarshall({
baseFeeBPS: config.fee_bps,
feeBPS,
})));
.json(res.locals.marshalResponse(quoteResponse));
}

View File

@@ -8,10 +8,15 @@ export interface QuotetBody {
/** Asset address */
asset: string;
/** Asset address */
address?: string;
recipient?: string;
}
export interface QuoteResponse {
baseFeeBPS: bigint,
feeBPS: bigint,
feeCommitment?: {
expiration: number,
withdrawalData: string
signedRelayerCommitment: string
}
}

View File

@@ -1,14 +1,29 @@
import { Chain, createPublicClient, http, PublicClient } from "viem";
import { Chain, createPublicClient, Hex, http, PublicClient } from "viem";
import {
CONFIG
CONFIG,
getSignerPrivateKey
} from "../config/index.js";
import { createChainObject } from "../utils.js";
import { privateKeyToAccount } from "viem/accounts";
interface IWeb3Provider {
client(chainId: number): PublicClient;
getGasPrice(chainId: number): Promise<bigint>;
}
const domain = (chainId: number) => ({
name: "Privacy Pools Relayer",
version: "1",
chainId,
} as const)
const RelayerCommitmentTypes = {
RelayerCommitment: [
{ name: "withdrawalData", type: "bytes" },
{ name: "expiration", type: "uint256" },
]
} as const;
/**
* Class representing the provider for interacting with several chains
*/
@@ -42,4 +57,19 @@ export class Web3Provider implements IWeb3Provider {
return await this.client(chainId).getGasPrice()
}
async signRelayerCommitment(chainId: number, commitment: { withdrawalData: `0x${string}`, expiration: number }) {
const pk = getSignerPrivateKey(chainId) as Hex;
const signer = privateKeyToAccount(pk);
const {withdrawalData, expiration} = commitment;
return signer.signTypedData({
domain: domain(chainId),
types: RelayerCommitmentTypes,
primaryType: 'RelayerCommitment',
message: {
withdrawalData,
expiration: BigInt(expiration)
}
})
}
}

View File

@@ -10,7 +10,7 @@ const quoteSchema: JSONSchemaType<QuotetBody> = {
chainId: { type: ["string", "number"] },
amount: { type: ["string"] },
asset: { type: ["string"] },
address: { type: ["string"], nullable: true },
recipient: { type: ["string"], nullable: true },
},
required: ["chainId", "amount", "asset"],
} as const;

View File

@@ -41,10 +41,20 @@ export class QuoteMarshall extends RelayerMarshall {
constructor(readonly response: QuoteResponse) {
super();
}
addFeeCommitment(feeCommitment: {
expiration: number
withdrawalData: string,
signedRelayerCommitment: string
}) {
this.response.feeCommitment = feeCommitment;
}
override toJSON(): object {
return {
baseFeeBPS: this.response.baseFeeBPS.toString(),
feeBPS: this.response.feeBPS.toString(),
feeCommitment: this.response.feeCommitment
}
}
}