Skip to content

Commit

Permalink
edits for relay flow
Browse files Browse the repository at this point in the history
  • Loading branch information
gsteenkamp89 committed Dec 30, 2024
1 parent bb178e6 commit 75fbc44
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 62 deletions.
46 changes: 46 additions & 0 deletions api/_spoke-pool-periphery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,49 @@ export function encodeSwapAndBridgeWithPermitCalldata(args: {
]
);
}

export function encodeDepositWithAuthCalldata(args: {
signatureOwner: string;
depositData: SpokePoolV3PeripheryInterface.DepositDataStruct;
validAfter: number;
validBefore: number;
nonce: string;
receiveWithAuthSignature: string;
depositDataSignature: string;
}) {
return SpokePoolV3Periphery__factory.createInterface().encodeFunctionData(
"depositWithAuthorization",
[
args.signatureOwner,
args.depositData,
args.validAfter,
args.validBefore,
args.nonce,
args.receiveWithAuthSignature,
args.depositDataSignature,
]
);
}

export function encodeSwapAndBridgeWithAuthCalldata(args: {
signatureOwner: string;
swapAndDepositData: SpokePoolV3PeripheryInterface.SwapAndDepositDataStruct;
validAfter: number;
validBefore: number;
nonce: string;
receiveWithAuthSignature: string;
depositDataSignature: string;
}) {
return SpokePoolV3Periphery__factory.createInterface().encodeFunctionData(
"swapAndBridgeWithAuthorization",
[
args.signatureOwner,
args.swapAndDepositData,
args.validAfter,
args.validBefore,
args.nonce,
args.receiveWithAuthSignature,
args.depositDataSignature,
]
);
}
2 changes: 1 addition & 1 deletion api/relay/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type RelayRequest = {
to: string;
methodNameAndArgs: ReturnType<typeof validateMethodArgs>;
signatures: {
permit: string;
permit: string; // use this for all auth signatures
deposit: string;
};
};
Expand Down
74 changes: 64 additions & 10 deletions api/relay/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { hexString, positiveIntStr, validAddress } from "../_utils";
import { getPermitTypedData } from "../_permit";
import { InvalidParamError } from "../_errors";
import {
encodeDepositWithAuthCalldata,
encodeDepositWithPermitCalldata,
encodeSwapAndBridgeWithAuthCalldata,
encodeSwapAndBridgeWithPermitCalldata,
getDepositTypedData,
getSwapAndDepositTypedData,
Expand Down Expand Up @@ -47,13 +49,15 @@ const SwapAndDepositDataSchema = type({
routerCalldata: hexString(),
});

const DepositDataSchema = type({
submissionFees: SubmissionFeesSchema,
baseDepositData: BaseDepositDataSchema,
inputAmount: positiveIntStr(),
});

export const DepositWithPermitArgsSchema = type({
signatureOwner: validAddress(),
depositData: type({
submissionFees: SubmissionFeesSchema,
baseDepositData: BaseDepositDataSchema,
inputAmount: positiveIntStr(),
}),
depositData: DepositDataSchema,
deadline: positiveIntStr(),
});

Expand All @@ -63,12 +67,33 @@ export const SwapAndDepositWithPermitArgsSchema = type({
deadline: positiveIntStr(),
});

export const DepositWithAuthArgsSchema = type({
signatureOwner: validAddress(),
depositData: DepositDataSchema,
validAfter: positiveIntStr(),
validBefore: positiveIntStr(),
nonce: hexString(),
});

export const SwapAndDepositWithAuthArgsSchema = type({
signatureOwner: validAddress(),
swapAndDepositData: SwapAndDepositDataSchema,
validAfter: positiveIntStr(),
validBefore: positiveIntStr(),
nonce: hexString(),
});

export const allowedMethodNames = [
"depositWithPermit",
"swapAndBridgeWithPermit",
];

export function validateMethodArgs(methodName: string, args: any) {
"depositWithAuth",
"swapAndBridgeWithAuth",
] as const;

export function validateMethodArgs(
methodName: (typeof allowedMethodNames)[number],
args: any
) {
if (methodName === "depositWithPermit") {
assert(args, DepositWithPermitArgsSchema);
return {
Expand All @@ -81,6 +106,18 @@ export function validateMethodArgs(methodName: string, args: any) {
args: args as Infer<typeof SwapAndDepositWithPermitArgsSchema>,
methodName,
} as const;
} else if (methodName === "depositWithAuth") {
assert(args, DepositWithAuthArgsSchema);
return {
args: args as Infer<typeof DepositWithAuthArgsSchema>,
methodName,
} as const;
} else if (methodName === "swapAndBridgeWithAuth") {
assert(args, SwapAndDepositWithAuthArgsSchema);
return {
args: args as Infer<typeof SwapAndDepositWithAuthArgsSchema>,
methodName,
} as const;
}
throw new Error(`Invalid method name: ${methodName}`);
}
Expand Down Expand Up @@ -190,12 +227,29 @@ export function encodeCalldataForRelayRequest(request: RelayRequest) {
swapAndDepositDataSignature: request.signatures.deposit,
permitSignature: request.signatures.permit,
});
} else if (request.methodNameAndArgs.methodName === "depositWithAuth") {
encodedCalldata = encodeDepositWithAuthCalldata({
...request.methodNameAndArgs.args,
validAfter: Number(request.methodNameAndArgs.args.validAfter),
validBefore: Number(request.methodNameAndArgs.args.validAfter),
nonce: request.methodNameAndArgs.args.nonce,
receiveWithAuthSignature: request.signatures.deposit,
depositDataSignature: request.signatures.permit,
});
} else if (request.methodNameAndArgs.methodName === "swapAndBridgeWithAuth") {
encodedCalldata = encodeSwapAndBridgeWithAuthCalldata({
...request.methodNameAndArgs.args,
validAfter: Number(request.methodNameAndArgs.args.validAfter),
validBefore: Number(request.methodNameAndArgs.args.validAfter),
nonce: request.methodNameAndArgs.args.nonce,
receiveWithAuthSignature: request.signatures.deposit,
depositDataSignature: request.signatures.permit,
});
}
// TODO: Add cases for `withAuth` and `withPermit2`
// TODO: Add cases for `withPermit2`
else {
throw new Error(`Can not encode calldata for relay request`);
}

return encodedCalldata;
}

Expand Down
121 changes: 76 additions & 45 deletions api/swap/auth/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,63 @@ import { getTransferWithAuthTypedData } from "../../_transfer-with-auth";
import {
getDepositTypedData,
getSwapAndDepositTypedData,
TransferType,
} from "../../_spoke-pool-periphery";
import { extractDepositDataStruct } from "../../_dexes/utils";
import { BigNumber, utils } from "ethers";
import {
extractDepositDataStruct,
extractSwapAndDepositDataStruct,
} from "../../_dexes/utils";
import { BigNumberish, BytesLike, utils } from "ethers";
import { SpokePoolV3PeripheryInterface } from "../../_typechain/SpokePoolV3Periphery";

export async function buildAuthTxPayload(
crossSwapQuotes: CrossSwapQuotes,
authDeadline: number, // maybe milliseconds
authStart = 0 // maybe milliseconds
) {
export async function buildAuthTxPayload({
crossSwapQuotes,
authDeadline,
authStart = 0,
submissionFees,
}: {
crossSwapQuotes: CrossSwapQuotes;
authDeadline: number; // maybe milliseconds
authStart?: number; // maybe milliseconds
submissionFees?: {
amount: BigNumberish;
recipient: string;
};
}) {
const { originSwapQuote, bridgeQuote, crossSwap, contracts } =
crossSwapQuotes;
const originChainId = crossSwap.inputToken.chainId;
const { originSwapEntryPoint, depositEntryPoint, originRouter } = contracts;

const baseDepositData = await extractDepositDataStruct(crossSwapQuotes);

let entryPointContract:
| DepositEntryPointContract
| OriginSwapEntryPointContract;
let getDepositTypedDataPromise:
| ReturnType<typeof getDepositTypedData>
| ReturnType<typeof getSwapAndDepositTypedData>;
let methodName: string;
let methodNameAndArgsWithoutSignatures:
| {
methodName: "depositWithAuthorization";
argsWithoutSignatures: {
signatureOwner: string;
depositData: SpokePoolV3PeripheryInterface.DepositDataStruct;
validAfter: BigNumberish;
validBefore: BigNumberish;
nonce: BytesLike;
};
}
| {
methodName: "swapAndBridgeWithAuthorization";
argsWithoutSignatures: {
signatureOwner: string;
swapAndDepositData: SpokePoolV3PeripheryInterface.SwapAndDepositDataStruct;
validAfter: BigNumberish;
validBefore: BigNumberish;
nonce: BytesLike;
};
};

// random non-sequesntial nonce
const nonce = utils.hexlify(utils.randomBytes(32));

if (originSwapQuote) {
if (!originSwapEntryPoint) {
Expand All @@ -50,29 +83,24 @@ export async function buildAuthTxPayload(
`'originRouter' needs to be defined for origin swap quotes`
);
}

const swapAndDepositData =
await extractSwapAndDepositDataStruct(crossSwapQuotes);
entryPointContract = originSwapEntryPoint;

getDepositTypedDataPromise = getSwapAndDepositTypedData({
swapAndDepositData: {
// TODO: Make this dynamic
submissionFees: {
amount: BigNumber.from(0),
recipient: crossSwapQuotes.crossSwap.depositor,
},
depositData: baseDepositData,
swapToken: originSwapQuote.tokenIn.address,
swapTokenAmount: originSwapQuote.maximumAmountIn,
minExpectedInputTokenAmount: originSwapQuote.minAmountOut,
routerCalldata: originSwapQuote.swapTx.data,
exchange: originRouter.address,
transferType:
originRouter.name === "UniswapV3UniversalRouter"
? TransferType.Transfer
: TransferType.Approval,
},
swapAndDepositData: swapAndDepositData,
chainId: originChainId,
});
methodName = "swapAndBridgeWithAuthorization";
methodNameAndArgsWithoutSignatures = {
methodName: "swapAndBridgeWithAuthorization",
argsWithoutSignatures: {
signatureOwner: crossSwap.depositor,
swapAndDepositData,
validAfter: authStart,
validBefore: authDeadline,
nonce,
},
};
} else {
if (!depositEntryPoint) {
throw new Error(
Expand All @@ -85,26 +113,27 @@ export async function buildAuthTxPayload(
`auth is not supported for deposit entry point contract '${depositEntryPoint.name}'`
);
}

const depositDataStruct = await extractDepositDataStruct(
crossSwapQuotes,
submissionFees
);
entryPointContract = depositEntryPoint;
getDepositTypedDataPromise = getDepositTypedData({
depositData: {
// TODO: Make this dynamic
submissionFees: {
amount: BigNumber.from(0),
recipient: crossSwap.depositor,
},
baseDepositData,
inputAmount: BigNumber.from(bridgeQuote.inputAmount),
},
depositData: depositDataStruct,
chainId: originChainId,
});
methodName = "depositWithAuthorization";
methodNameAndArgsWithoutSignatures = {
methodName: "depositWithAuthorization",
argsWithoutSignatures: {
signatureOwner: crossSwap.depositor,
depositData: depositDataStruct,
validAfter: authStart,
validBefore: authDeadline,
nonce,
},
};
}

// random non-sequesntial nonce
const nonce = utils.hexlify(utils.randomBytes(32));

const [authTypedData, depositTypedData] = await Promise.all([
getTransferWithAuthTypedData({
tokenAddress:
Expand All @@ -119,6 +148,7 @@ export async function buildAuthTxPayload(
}),
getDepositTypedDataPromise,
]);

return {
eip712: {
transferWithAuthorization: authTypedData.eip712,
Expand All @@ -127,7 +157,8 @@ export async function buildAuthTxPayload(
swapTx: {
chainId: originChainId,
to: entryPointContract.address,
methodName,
argsWithoutSignatures:
methodNameAndArgsWithoutSignatures.argsWithoutSignatures,
},
};
}
12 changes: 9 additions & 3 deletions api/swap/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getSwapRouter02Strategy } from "../../_dexes/uniswap/swap-router-02";
import { InvalidParamError } from "../../_errors";
import { QuoteFetchStrategies } from "../../_dexes/utils";
import { buildAuthTxPayload } from "./_utils";
import { GAS_SPONSOR_ADDRESS } from "../../relay/_utils";

export const authSwapQueryParamsSchema = type({
authDeadline: optional(positiveIntStr()),
Expand Down Expand Up @@ -90,11 +91,16 @@ const handler = async (
quoteFetchStrategies
);
// Build tx for auth
const crossSwapTxForAuth = await buildAuthTxPayload(
const crossSwapTxForAuth = await buildAuthTxPayload({
crossSwapQuotes,
authDeadline,
authStart
);
authStart,
// FIXME: Calculate proper fees
submissionFees: {
amount: "0",
recipient: GAS_SPONSOR_ADDRESS,
},
});

const responseJson = crossSwapTxForAuth;

Expand Down
Loading

0 comments on commit 75fbc44

Please sign in to comment.