Skip to content

Commit

Permalink
899: Added createLastLookUnsignedOrder
Browse files Browse the repository at this point in the history
  • Loading branch information
piersss committed Apr 21, 2024
1 parent 6844634 commit b85f1ce
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 19 deletions.
1 change: 1 addition & 0 deletions src/entities/ExtendedPricing/ExtendedPricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import { Pricing } from "@airswap/utils";
export type ExtendedPricing = {
isLastLook?: boolean;
locator: string;
serverWallet: string | null;
} & Pricing;
13 changes: 11 additions & 2 deletions src/entities/ExtendedPricing/ExtendedPricingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ export const subscribeExtendedPricingERC20 = async (
]);

return pricings.map((pricing) =>
transformToExtendedPricing(pricing, server.locator, true)
transformToExtendedPricing(
pricing,
server.locator,
server.getSenderWallet(),
true
)
);
};

Expand All @@ -25,6 +30,10 @@ export const getExtendedPricingERC20 = async (
const pricings = await server.getPricingERC20([{ baseToken, quoteToken }]);

return pricings.map((pricing) =>
transformToExtendedPricing(pricing, server.locator)
transformToExtendedPricing(
pricing,
server.locator,
server.getSenderWallet()
)
);
};
2 changes: 2 additions & 0 deletions src/entities/ExtendedPricing/ExtendedPricingTransformers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { ExtendedPricing } from "./ExtendedPricing";
export const transformToExtendedPricing = (
pricing: Pricing,
locator: string,
serverWallet: string | null,
isLastLook?: boolean
): ExtendedPricing => ({
...pricing,
...(isLastLook && { isLastLook }),
serverWallet,
locator,
});
52 changes: 48 additions & 4 deletions src/entities/OrderERC20/OrderERC20Service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import { Server } from "@airswap/libraries";
import { OrderERC20, toAtomicString } from "@airswap/utils";
import { Server, SwapERC20 } from "@airswap/libraries";
import {
createOrderERC20,
OrderERC20,
Pricing,
toAtomicString,
TokenInfo,
} from "@airswap/utils";
import { Web3Provider } from "@ethersproject/providers";

import { BigNumber } from "ethers";
import { BigNumber } from "bignumber.js";

import { LAST_LOOK_ORDER_EXPIRY_SEC } from "../../constants/configParams";
import { AppError, isAppError } from "../../errors/appError";
import { createOrderERC20Signature } from "../../helpers/createSwapSignature";
import { isPromiseFulfilledResult } from "../../helpers/promise";
import { ExtendedPricing } from "../ExtendedPricing/ExtendedPricing";
import { getPricingQuoteAmount } from "../ExtendedPricing/ExtendedPricingHelpers";
import { isOrderERC20 } from "./OrderERC20Helpers";
import { transformUnsignedOrderERC20ToOrderERC20 } from "./OrderERC20Transformers";

const REQUEST_ORDER_TIMEOUT_MS = 5000;

Expand Down Expand Up @@ -46,5 +59,36 @@ export async function requestRfqOrders(
)
.map((result) => result.value)
.filter(isOrderERC20)
.filter((o) => BigNumber.from(o.signerAmount).gt("0"));
.filter((o) => new BigNumber(o.signerAmount).gt("0"));
}

export const requestLastLookOrder = async (
servers: Server[],
quoteToken: string,
baseToken: string,
baseTokenAmount: string,
baseTokenDecimals: number,
senderWallet: string,
proxyingFor?: string
): Promise<OrderERC20[] | undefined> => {
if (!servers.length) {
console.error("[requestOrders] No counterparties");

return [];
}

const promises = servers.map(async (server) =>
Promise.race([
server.getSignerSideOrderERC20(
toAtomicString(baseTokenAmount, baseTokenDecimals),
quoteToken,
baseToken,
senderWallet,
proxyingFor
),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), REQUEST_ORDER_TIMEOUT_MS)
),
])
);
};
18 changes: 17 additions & 1 deletion src/entities/OrderERC20/OrderERC20Transformers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { OrderERC20, Signature } from "@airswap/utils";
import { OrderERC20, Signature, UnsignedOrderERC20 } from "@airswap/utils";
import { FullSwapERC20 } from "@airswap/utils/build/src/swap-erc20";

export const transformFullSwapERC20ToOrderERC20 = (
Expand All @@ -17,3 +17,19 @@ export const transformFullSwapERC20ToOrderERC20 = (
...signature,
};
};

export const transformUnsignedOrderERC20ToOrderERC20 = (
unsignedOrder: UnsignedOrderERC20,
signature: Signature
): OrderERC20 => {
return {
expiry: unsignedOrder.expiry,
nonce: unsignedOrder.nonce,
senderToken: unsignedOrder.senderToken,
senderAmount: unsignedOrder.senderAmount,
signerWallet: unsignedOrder.signerWallet,
signerToken: unsignedOrder.signerToken,
signerAmount: unsignedOrder.signerAmount,
...signature,
};
};
1 change: 1 addition & 0 deletions src/errors/pricingError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export enum PricingErrorType {
formulaicPricingNotSupported = "formulaicPricingNotSupported",
noOrdersFound = "noOrdersFound",
noPricingFound = "noPricingFound",
noServerWalletFound = "noServerWalletFound",
ordersExpired = "ordersExpired",
}
55 changes: 55 additions & 0 deletions src/features/quotes/quotesActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { createOrderERC20, TokenInfo } from "@airswap/utils";

import { BigNumber } from "bignumber.js";

import { AppDispatch } from "../../app/store";
import { LAST_LOOK_ORDER_EXPIRY_SEC } from "../../constants/configParams";
import { ExtendedPricing } from "../../entities/ExtendedPricing/ExtendedPricing";
import { getPricingQuoteAmount } from "../../entities/ExtendedPricing/ExtendedPricingHelpers";
import { setBestOrder } from "./quotesSlice";

interface CreateLastLookUnsignedOrder {
account: string;
baseToken: TokenInfo;
baseAmount: string;
pricing: ExtendedPricing;
protocolFee: number;
quoteToken: TokenInfo;
}

export const createLastLookUnsignedOrder =
(props: CreateLastLookUnsignedOrder) =>
async (dispatch: AppDispatch): Promise<void> => {
const { account, baseToken, baseAmount, pricing, protocolFee, quoteToken } =
props;

const quoteAmount = getPricingQuoteAmount(pricing, baseAmount, protocolFee);
const senderWallet = pricing.serverWallet;

const baseAmountAtomic = new BigNumber(baseAmount)
.multipliedBy(10 ** baseToken.decimals)
// Note that we remove the signer fee from the amount that we send.
// This was already done to determine quoteAmount.
.dividedBy(1 + protocolFee / 10000)
.integerValue(BigNumber.ROUND_CEIL)
.toString();

const quoteAmountAtomic = new BigNumber(quoteAmount)
.multipliedBy(10 ** quoteToken.decimals)
.integerValue(BigNumber.ROUND_FLOOR)
.toString();

const unsignedOrder = createOrderERC20({
expiry: Math.floor(Date.now() / 1000 + LAST_LOOK_ORDER_EXPIRY_SEC),
nonce: Date.now().toString(),
senderWallet,
signerWallet: account,
signerToken: baseToken.address,
senderToken: quoteToken.address,
protocolFee: protocolFee.toString(),
signerAmount: baseAmountAtomic,
senderAmount: quoteAmountAtomic,
});

dispatch(setBestOrder(unsignedOrder));
};
7 changes: 7 additions & 0 deletions src/features/quotes/quotesApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export const fetchBestPricing = createAsyncThunk<
),
]);

console.log(rfqServers, lastLookServers);

const timeout = 2000;
const lastLookPromises = lastLookServers.map((server) =>
Promise.race([
Expand All @@ -79,13 +81,18 @@ export const fetchBestPricing = createAsyncThunk<
])
);

console.log(rfqPromises);

const pricingPromises = [...lastLookPromises, ...rfqPromises];

if (!pricingPromises.length) {
return PricingErrorType.noPricingFound;
}

const responses = await Promise.allSettled(pricingPromises);

console.log(responses);

const pricings = responses
.filter((response): response is PromiseFulfilledResult<unknown> =>
isPromiseFulfilledResult<unknown>(response)
Expand Down
37 changes: 29 additions & 8 deletions src/features/quotes/quotesHooks.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { useEffect } from "react";

import { OrderERC20 } from "@airswap/utils";
import { OrderERC20, UnsignedOrderERC20 } from "@airswap/utils";
import { useWeb3React } from "@web3-react/core";

import { useAppDispatch, useAppSelector } from "../../app/hooks";
import { ExtendedPricing } from "../../entities/ExtendedPricing/ExtendedPricing";
import { PricingErrorType } from "../../errors/pricingError";
import useTokenInfo from "../../hooks/useTokenInfo";
import { selectProtocolFee } from "../metadata/metadataSlice";
import { createLastLookUnsignedOrder } from "./quotesActions";
import { fetchBestPricing, fetchBestRfqOrder } from "./quotesApi";

interface UseQuotesReturn {
isLoading: boolean;
isFailed: boolean;
bestPricing?: ExtendedPricing;
bestOrder?: OrderERC20;
bestOrder?: OrderERC20 | UnsignedOrderERC20;
error?: PricingErrorType;
}

Expand All @@ -25,13 +26,15 @@ const useQuotes = (
isSubmitted: boolean
): UseQuotesReturn => {
const dispatch = useAppDispatch();
const { account, chainId, library } = useWeb3React();

const protocolFee = useAppSelector(selectProtocolFee);
const { isFailed, isLoading, bestPricing, bestOrder } = useAppSelector(
(state) => state.quotes
);
const { account, chainId, library } = useWeb3React();

const baseTokenInfo = useTokenInfo(baseToken);
const quoteTokenInfo = useTokenInfo(quoteToken);

useEffect(() => {
if (!chainId || !library || !isSubmitted) {
Expand All @@ -40,24 +43,29 @@ const useQuotes = (

dispatch(
fetchBestPricing({
provider: library,
baseToken,
baseTokenAmount,
quoteToken,
provider: library,
chainId: chainId,
protocolFee,
})
);
}, [isSubmitted]);

useEffect(() => {
if (!chainId || !library || !account || !bestPricing || !baseTokenInfo) {
if (
!chainId ||
!library ||
!account ||
!bestPricing ||
!baseTokenInfo ||
!quoteTokenInfo
) {
return;
}

console.log(bestPricing);

if (!bestPricing.isLastLook || bestPricing.isLastLook) {
if (!bestPricing.isLastLook) {
dispatch(
fetchBestRfqOrder({
provider: library,
Expand All @@ -68,7 +76,20 @@ const useQuotes = (
senderWallet: account,
})
);

return;
}

dispatch(
createLastLookUnsignedOrder({
account,
baseToken: baseTokenInfo,
baseAmount: baseTokenAmount,
pricing: bestPricing,
protocolFee,
quoteToken: quoteTokenInfo,
})
);
}, [bestPricing]);

useEffect(() => {
Expand Down
18 changes: 14 additions & 4 deletions src/features/quotes/quotesSlice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OrderERC20, Pricing } from "@airswap/utils";
import { createSlice } from "@reduxjs/toolkit";
import { OrderERC20, UnsignedOrderERC20 } from "@airswap/utils";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";

import { ExtendedPricing } from "../../entities/ExtendedPricing/ExtendedPricing";
import { isExtendedPricing } from "../../entities/ExtendedPricing/ExtendedPricingHelpers";
Expand All @@ -10,7 +10,7 @@ interface QuotesState {
isFailed: boolean;
isLoading: boolean;
bestPricing?: ExtendedPricing;
bestOrder?: OrderERC20;
bestOrder?: OrderERC20 | UnsignedOrderERC20;
error?: PricingErrorType;
}

Expand All @@ -22,7 +22,15 @@ const initialState: QuotesState = {
const quotesSlice = createSlice({
name: "quotes",
initialState,
reducers: {},
reducers: {
setBestOrder: (
state,
action: PayloadAction<OrderERC20 | UnsignedOrderERC20>
): QuotesState => ({
...state,
bestOrder: action.payload,
}),
},
extraReducers: (builder) => {
builder.addCase(
fetchBestPricing.pending,
Expand Down Expand Up @@ -55,4 +63,6 @@ const quotesSlice = createSlice({
},
});

export const { setBestOrder } = quotesSlice.actions;

export default quotesSlice.reducer;

0 comments on commit b85f1ce

Please sign in to comment.