Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: cleanup + align swap response formats #1355

Merged
merged 3 commits into from
Jan 7, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
test: add unified swap endpoint script
dohaki committed Jan 7, 2025
commit 117236fa2a0819e549fca8a63345386f47bbc143
8 changes: 4 additions & 4 deletions api/swap/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { VercelResponse } from "@vercel/node";
import axios from "axios";

import { TypedVercelRequest } from "../_types";
import {
@@ -9,13 +10,12 @@ import {
import { handleBaseSwapQueryParams, BaseSwapQueryParams } from "./_utils";
import { getPermitArgsFromContract } from "../_permit";
import { getReceiveWithAuthArgsFromContract } from "../_transfer-with-auth";
import axios from "axios";

type SwapFlowType = "permit" | "transfer-with-auth" | "approval";

function makeSwapHandler(path: string) {
return (params: unknown) =>
axios.get(`${resolveVercelEndpoint()}/${path}`, { params });
axios.get(`${resolveVercelEndpoint(true)}/api/swap/${path}`, { params });
}
const swapFlowTypeToHandler = {
permit: makeSwapHandler("permit"),
@@ -62,9 +62,9 @@ export default async function handler(
}

const handler = swapFlowTypeToHandler[swapFlowType];
const responseJson = await handler(request.query);
const { data } = await handler(request.query);
const enrichedResponseJson = {
...responseJson,
...data,
swapFlowType,
};

96 changes: 92 additions & 4 deletions scripts/tests/_swap-utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "@across-protocol/constants";
import { ethers } from "ethers";
import { ethers, Wallet } from "ethers";
import dotenv from "dotenv";
import axios from "axios";

@@ -183,10 +183,98 @@ export async function fetchSwapQuote(slug?: "approval" | "permit" | "auth") {
for (const testCase of filteredTestCases) {
console.log("\nTest case:", testCase.labels.join(" "));
console.log("Params:", testCase.params);
const response = await axios.get(`${SWAP_API_BASE_URL}/api/swap/${slug}`, {
params: testCase.params,
});
const response = await axios.get(
`${SWAP_API_BASE_URL}/api/swap${slug ? `/${slug}` : ""}`,
{
params: testCase.params,
}
);
console.log(JSON.stringify(response.data, null, 2));
return response.data as BaseSwapResponse;
}
}

export async function signAndWaitPermitFlow(params: {
wallet: Wallet;
swapResponse: BaseSwapResponse;
}) {
if (!params.swapResponse.eip712) {
throw new Error("No EIP712 data for permit");
}

// sign permit + deposit
const permitSig = await params.wallet._signTypedData(
params.swapResponse.eip712.permit.domain,
params.swapResponse.eip712.permit.types,
params.swapResponse.eip712.permit.message
);
console.log("Signed permit:", permitSig);

const depositSig = await params.wallet._signTypedData(
params.swapResponse.eip712.deposit.domain,
params.swapResponse.eip712.deposit.types,
params.swapResponse.eip712.deposit.message
);
console.log("Signed deposit:", depositSig);

// relay
const relayResponse = await axios.post(`${SWAP_API_BASE_URL}/api/relay`, {
...params.swapResponse.swapTx,
signatures: { permit: permitSig, deposit: depositSig },
});
console.log("Relay response:", relayResponse.data);

// track relay
while (true) {
const relayStatusResponse = await axios.get(
`${SWAP_API_BASE_URL}/api/relay/status?requestHash=${relayResponse.data.requestHash}`
);
console.log("Relay status response:", relayStatusResponse.data);

if (relayStatusResponse.data.status === "success") {
break;
}

await new Promise((resolve) => setTimeout(resolve, 1_000));
}
}

export async function signAndWaitAllowanceFlow(params: {
wallet: Wallet;
swapResponse: BaseSwapResponse;
}) {
if (!params.swapResponse.swapTx || !("data" in params.swapResponse.swapTx)) {
throw new Error("No swap tx for allowance flow");
}

if (params.swapResponse.approvalTxns) {
console.log("Approval needed...");
let step = 1;
for (const approvalTxn of params.swapResponse.approvalTxns) {
const stepLabel = `(${step}/${params.swapResponse.approvalTxns.length})`;
const tx = await params.wallet.sendTransaction({
to: approvalTxn.to,
data: approvalTxn.data,
});
console.log(`${stepLabel} Approval tx hash:`, tx.hash);
await tx.wait();
console.log(`${stepLabel} Approval tx mined`);
step++;
}
}

try {
const tx = await params.wallet.sendTransaction({
to: params.swapResponse.swapTx.to,
data: params.swapResponse.swapTx.data,
value: params.swapResponse.swapTx.value,
gasLimit: params.swapResponse.swapTx.gas,
gasPrice: params.swapResponse.swapTx.gasPrice,
});
console.log("Tx hash: ", tx.hash);
await tx.wait();
console.log("Tx mined");
} catch (e) {
console.error("Tx reverted", e);
}
}
33 changes: 2 additions & 31 deletions scripts/tests/swap-allowance.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Wallet } from "ethers";

import { getProvider } from "../../api/_utils";
import { fetchSwapQuote } from "./_swap-utils";
import { fetchSwapQuote, signAndWaitAllowanceFlow } from "./_swap-utils";

async function swapWithAllowance() {
console.log("Swapping with allowance...");
@@ -17,36 +17,7 @@ async function swapWithAllowance() {
getProvider(swapQuote.swapTx.chainId)
);

if (swapQuote.approvalTxns) {
console.log("Approval needed...");
let step = 1;
for (const approvalTxn of swapQuote.approvalTxns) {
const stepLabel = `(${step}/${swapQuote.approvalTxns.length})`;
const tx = await wallet.sendTransaction({
to: approvalTxn.to,
data: approvalTxn.data,
});
console.log(`${stepLabel} Approval tx hash:`, tx.hash);
await tx.wait();
console.log(`${stepLabel} Approval tx mined`);
step++;
}
}

try {
const tx = await wallet.sendTransaction({
to: swapQuote.swapTx.to,
data: swapQuote.swapTx.data,
value: swapQuote.swapTx.value,
gasLimit: swapQuote.swapTx.gas,
gasPrice: swapQuote.swapTx.gasPrice,
});
console.log("Tx hash: ", tx.hash);
await tx.wait();
console.log("Tx mined");
} catch (e) {
console.error("Tx reverted", e);
}
await signAndWaitAllowanceFlow({ wallet, swapResponse: swapQuote });
}
}

39 changes: 2 additions & 37 deletions scripts/tests/swap-auth.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Wallet } from "ethers";
import axios from "axios";

import { getProvider } from "../../api/_utils";
import { fetchSwapQuote, SWAP_API_BASE_URL } from "./_swap-utils";
import { fetchSwapQuote, signAndWaitPermitFlow } from "./_swap-utils";

async function swapWithAuth() {
console.log("Swapping with auth...");
@@ -18,41 +17,7 @@ async function swapWithAuth() {
getProvider(swapQuote.swapTx.chainId)
);

// sign permit + deposit
const permitSig = await wallet._signTypedData(
swapQuote.eip712.permit.domain,
swapQuote.eip712.permit.types,
swapQuote.eip712.permit.message
);
console.log("Signed permit:", permitSig);

const depositSig = await wallet._signTypedData(
swapQuote.eip712.deposit.domain,
swapQuote.eip712.deposit.types,
swapQuote.eip712.deposit.message
);
console.log("Signed deposit:", depositSig);

// relay
const relayResponse = await axios.post(`${SWAP_API_BASE_URL}/api/relay`, {
...swapQuote.swapTx,
signatures: { permit: permitSig, deposit: depositSig },
});
console.log("Relay response:", relayResponse.data);

// track relay
while (true) {
const relayStatusResponse = await axios.get(
`${SWAP_API_BASE_URL}/api/relay/status?requestHash=${relayResponse.data.requestHash}`
);
console.log("Relay status response:", relayStatusResponse.data);

if (relayStatusResponse.data.status === "success") {
break;
}

await new Promise((resolve) => setTimeout(resolve, 1_000));
}
await signAndWaitPermitFlow({ wallet, swapResponse: swapQuote });
}
}

39 changes: 2 additions & 37 deletions scripts/tests/swap-permit.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Wallet } from "ethers";

import { getProvider } from "../../api/_utils";
import { fetchSwapQuote, SWAP_API_BASE_URL } from "./_swap-utils";
import axios from "axios";
import { fetchSwapQuote, signAndWaitPermitFlow } from "./_swap-utils";

async function swapWithPermit() {
console.log("Swapping with permit...");
@@ -18,41 +17,7 @@ async function swapWithPermit() {
getProvider(swapQuote.swapTx.chainId)
);

// sign permit + deposit
const permitSig = await wallet._signTypedData(
swapQuote.eip712.permit.domain,
swapQuote.eip712.permit.types,
swapQuote.eip712.permit.message
);
console.log("Signed permit:", permitSig);

const depositSig = await wallet._signTypedData(
swapQuote.eip712.deposit.domain,
swapQuote.eip712.deposit.types,
swapQuote.eip712.deposit.message
);
console.log("Signed deposit:", depositSig);

// relay
const relayResponse = await axios.post(`${SWAP_API_BASE_URL}/api/relay`, {
...swapQuote.swapTx,
signatures: { permit: permitSig, deposit: depositSig },
});
console.log("Relay response:", relayResponse.data);

// track relay
while (true) {
const relayStatusResponse = await axios.get(
`${SWAP_API_BASE_URL}/api/relay/status?requestHash=${relayResponse.data.requestHash}`
);
console.log("Relay status response:", relayStatusResponse.data);

if (relayStatusResponse.data.status === "success") {
break;
}

await new Promise((resolve) => setTimeout(resolve, 1_000));
}
await signAndWaitPermitFlow({ wallet, swapResponse: swapQuote });
}
}

45 changes: 45 additions & 0 deletions scripts/tests/swap-unified.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Wallet } from "ethers";

import { getProvider } from "../../api/_utils";
import {
fetchSwapQuote,
signAndWaitAllowanceFlow,
signAndWaitPermitFlow,
} from "./_swap-utils";

async function swapUnified() {
console.log("Swapping with unified endpoint...");
const swapQuote = await fetchSwapQuote();

if (!swapQuote || !swapQuote.swapTx) {
console.log("No swap quote");
return;
}

if (process.env.DEV_WALLET_PK) {
const wallet = new Wallet(process.env.DEV_WALLET_PK!).connect(
getProvider(swapQuote.swapTx.chainId)
);

// if a permit-based flow is available, the unified endpoint will prefer that over an
// allowance-based flow and return the relevant EIP712 data.
if (swapQuote.eip712) {
// sign permit + relay + track
await signAndWaitPermitFlow({ wallet, swapResponse: swapQuote });
}
// if no permit-based flow is available, we can use the allowance-based flow
else {
// sign and send approval txns + swap txn
await signAndWaitAllowanceFlow({ wallet, swapResponse: swapQuote });
}
}
}

swapUnified()
.then(() => console.log("Done"))
.catch((e) => {
console.error(e);
if (e.response?.data) {
console.log("Tx for debug sim:", e.response.data.transaction);
}
});

Unchanged files with check annotations Beta

}
// @TODO: Implement the following function
export async function getCrossSwapQuotesForExactInput(crossSwap: CrossSwap) {

Check warning on line 82 in api/_dexes/cross-swap-service.ts

GitHub Actions / format-and-lint

'crossSwap' is defined but never used. Allowed unused args must match /^_/u
throw new Error("Not implemented yet");
}
isNative,
referrer,
fillDeadline,
inputTokenAddress,

Check warning on line 287 in src/utils/bridge.ts

GitHub Actions / format-and-lint

'inputTokenAddress' is defined but never used. Allowed unused args must match /^_/u
outputTokenAddress,
swapTokenAddress,

Check warning on line 289 in src/utils/bridge.ts

GitHub Actions / format-and-lint

'swapTokenAddress' is defined but never used. Allowed unused args must match /^_/u
exclusiveRelayer = ethers.constants.AddressZero,
exclusivityDeadline = 0,
swapQuote,
interchangeableTokensMap,
nonEthChains,
GetBridgeFeesResult,
isDefined,

Check warning on line 15 in src/views/Bridge/utils.ts

GitHub Actions / format-and-lint

'isDefined' is defined but never used
parseUnits,

Check warning on line 16 in src/views/Bridge/utils.ts

GitHub Actions / format-and-lint

'parseUnits' is defined but never used
} from "utils";
import { SwapQuoteApiResponse } from "utils/serverless-api/prod/swap-quote";