Skip to content

Commit

Permalink
Merge pull request #413 from FrancoAguzzi/feat/propeller-heads
Browse files Browse the repository at this point in the history
fix: replace PropellerHeads quote API by solve API
  • Loading branch information
kvhnuke authored Mar 21, 2024
2 parents 1e1ca94 + 954a377 commit f27a7af
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 60 deletions.
2 changes: 1 addition & 1 deletion packages/swap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
"lint": "prettier --write .",
"test": "ts-mocha -p tsconfig.json tests/**/prope*.test.ts"
"test": "ts-mocha -p tsconfig.json tests/**/*.test.ts"
},
"engines": {
"node": ">=14.15.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/swap/src/common/estimateGasList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const useStandardEstimate = (
})
.catch(() => null);

const estimateGasList = (
const estimateGasList = async (
transactions: EVMTransaction[],
network: SupportedNetworkName
): Promise<{
Expand Down
114 changes: 65 additions & 49 deletions packages/swap/src/providers/propeller-heads/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Web3Eth from "web3-eth";
import { numberToHex, stringToHex, toBN } from "web3-utils";
import { numberToHex, toBN } from "web3-utils";
import estimateGasList from "../../common/estimateGasList";
import {
EVMTransaction,
getQuoteOptions,
Expand All @@ -15,7 +16,6 @@ import {
StatusOptionsResponse,
SupportedNetworkName,
SwapQuote,
SwapTransaction,
TokenType,
TransactionStatus,
TransactionType,
Expand Down Expand Up @@ -122,7 +122,8 @@ class PropellerHeads extends ProviderClass {

private getPropellerHeadsSwap(
options: getQuoteOptions,
meta: QuoteMetaOptions
meta: QuoteMetaOptions,
accurateEstimate: boolean
): Promise<PropellerHeadsSwapResponse | null> {
if (
!PropellerHeads.isSupported(
Expand Down Expand Up @@ -151,13 +152,13 @@ class PropellerHeads extends ProviderClass {
NetworkNamesToSupportedProppellerHeadsBlockchains[this.network],
});

return fetch(`${BASE_URL}/solver/quote?${params.toString()}`, {
return fetch(`${BASE_URL}/solver/solve?${params.toString()}`, {
method: "POST",
body: JSON.stringify(body),
})
.then((res) => res.json())
.then(async (response: PropellerHeadsResponseType) => {
const transactions: SwapTransaction[] = [];
const transactions: EVMTransaction[] = [];
if (options.fromToken.address !== NATIVE_TOKEN_ADDRESS) {
const approvalTxs = await getAllowanceTransactions({
infinityApproval: meta.infiniteApproval,
Expand All @@ -169,19 +170,30 @@ class PropellerHeads extends ProviderClass {
});
transactions.push(...approvalTxs);
}
console.log(response);
transactions.push({
from: options.fromAddress,
gasLimit: GAS_LIMITS.swap,
to: options.fromAddress,
value: numberToHex(options.amount),
data: stringToHex(JSON.stringify(response)),
data: response.solutions[0].call_data,
type: TransactionType.evm,
});
if (accurateEstimate) {
const accurateGasEstimate = await estimateGasList(
transactions,
this.network
);
if (accurateGasEstimate) {
if (accurateGasEstimate.isError) return null;
transactions.forEach((tx, idx) => {
tx.gasLimit = accurateGasEstimate.result[idx];
});
}
}
return {
transactions,
toTokenAmount: toBN(response.quotes[0].buy_amount),
fromTokenAmount: toBN(response.quotes[0].sell_amount),
toTokenAmount: toBN(response.solutions[0].orders[0].buy_amount),
fromTokenAmount: toBN(response.solutions[0].orders[0].sell_amount),
};
})
.catch((e) => {
Expand All @@ -194,51 +206,55 @@ class PropellerHeads extends ProviderClass {
options: getQuoteOptions,
meta: QuoteMetaOptions
): Promise<ProviderQuoteResponse | null> {
return this.getPropellerHeadsSwap(options, meta).then(async (res) => {
if (!res) return null;
const response: ProviderQuoteResponse = {
fromTokenAmount: res.fromTokenAmount,
additionalNativeFees: toBN(0),
toTokenAmount: res.toTokenAmount,
provider: this.name,
quote: {
meta,
options,
return this.getPropellerHeadsSwap(options, meta, false).then(
async (res) => {
if (!res) return null;
const response: ProviderQuoteResponse = {
fromTokenAmount: res.fromTokenAmount,
additionalNativeFees: toBN(0),
toTokenAmount: res.toTokenAmount,
provider: this.name,
},
totalGaslimit: res.transactions.reduce(
(total: number, curVal: EVMTransaction) =>
total + toBN(curVal.gasLimit).toNumber(),
0
),
minMax: await this.getMinMaxAmount(),
};
return response;
});
quote: {
meta,
options,
provider: this.name,
},
totalGaslimit: res.transactions.reduce(
(total: number, curVal: EVMTransaction) =>
total + toBN(curVal.gasLimit).toNumber(),
0
),
minMax: await this.getMinMaxAmount(),
};
return response;
}
);
}

getSwap(quote: SwapQuote): Promise<ProviderSwapResponse | null> {
return this.getPropellerHeadsSwap(quote.options, quote.meta).then((res) => {
if (!res) return null;
const feeConfig =
FEE_CONFIGS[this.name][quote.meta.walletIdentifier].fee || 0;
const response: ProviderSwapResponse = {
fromTokenAmount: res.fromTokenAmount,
additionalNativeFees: toBN(0),
provider: this.name,
toTokenAmount: res.toTokenAmount,
transactions: res.transactions,
slippage: quote.meta.slippage || DEFAULT_SLIPPAGE,
fee: feeConfig * 100,
getStatusObject: async (
options: StatusOptions
): Promise<StatusOptionsResponse> => ({
options,
return this.getPropellerHeadsSwap(quote.options, quote.meta, true).then(
(res) => {
if (!res) return null;
const feeConfig =
FEE_CONFIGS[this.name][quote.meta.walletIdentifier].fee || 0;
const response: ProviderSwapResponse = {
fromTokenAmount: res.fromTokenAmount,
additionalNativeFees: toBN(0),
provider: this.name,
}),
};
return response;
});
toTokenAmount: res.toTokenAmount,
transactions: res.transactions,
slippage: quote.meta.slippage || DEFAULT_SLIPPAGE,
fee: feeConfig * 100,
getStatusObject: async (
options: StatusOptions
): Promise<StatusOptionsResponse> => ({
options,
provider: this.name,
}),
};
return response;
}
);
}

getStatus(options: StatusOptions): Promise<TransactionStatus> {
Expand Down
24 changes: 17 additions & 7 deletions packages/swap/src/providers/propeller-heads/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@ import { BN, EVMTransaction } from "../../types";

export interface PropellerHeadsResponseType {
request_id: string;
quotes: [
solutions: [
{
sell_token: string;
buy_token: string;
sell_amount: string;
buy_amount: string;
external_id: string;
orders: [
{
origin_address: string;
sell_token: string;
buy_token: string;
sell_amount: string;
executed_sell_amount: string;
buy_amount: string;
executed_buy_amount: string;
external_id: string;
receiver: string;
}
];
call_data: string;
gas: string;
target_address: string;
}
];
gas: number;
buy_tokens: [
{
symbol: string;
Expand Down
1 change: 0 additions & 1 deletion packages/swap/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ export enum SupportedNetworkName {
Klaytn = NetworkNames.Klaytn,
Aurora = NetworkNames.Aurora,
Zksync = NetworkNames.ZkSync,
Starknet = NetworkNames.Starknet,
}

// eslint-disable-next-line no-shadow
Expand Down
12 changes: 11 additions & 1 deletion packages/swap/tests/swap.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { toBN } from "web3-utils";
import { expect } from "chai";
import Web3Eth from "web3-eth";
import Swap from "../src";
Expand All @@ -8,9 +9,9 @@ import {
WalletIdentifier,
} from "../src/types";
import {
amount,
fromToken,
toToken,
amount,
fromAddress,
toAddress,
nodeURL,
Expand Down Expand Up @@ -72,14 +73,19 @@ describe("Swap", () => {
(q) => q.provider === ProviderName.changelly
);
const zeroxQuote = quotes.find((q) => q.provider === ProviderName.zerox);
const propellerHeadsQuote = quotes.find(
(q) => q.provider === ProviderName.propellerHeads
);
if (quotes?.length > 3) {
const rangoQuote = quotes.find((q) => q.provider === ProviderName.rango);
expect(rangoQuote!.provider).to.be.eq(ProviderName.rango);
}

expect(zeroxQuote).to.be.eq(undefined);
expect(changellyQuote!.provider).to.be.eq(ProviderName.changelly);
expect(oneInceQuote!.provider).to.be.eq(ProviderName.oneInch);
expect(paraswapQuote!.provider).to.be.eq(ProviderName.paraswap);
expect(propellerHeadsQuote!.provider).to.be.eq(ProviderName.propellerHeads);
const swapOneInch = await enkryptSwap.getSwap(oneInceQuote!.quote);
expect(swapOneInch?.fromTokenAmount.toString()).to.be.eq(amount.toString());
expect(swapOneInch?.transactions.length).to.be.eq(2);
Expand Down Expand Up @@ -107,11 +113,15 @@ describe("Swap", () => {
(q) => q.provider === ProviderName.changelly
);
const zeroxQuote = quotes.find((q) => q.provider === ProviderName.zerox);
const propellerHeadsQuote = quotes.find(
(q) => q.provider === ProviderName.propellerHeads
);
// const rangoQuote = quotes.find((q) => q.provider === ProviderName.rango);
expect(zeroxQuote!.provider).to.be.eq(ProviderName.zerox);
expect(changellyQuote!.provider).to.be.eq(ProviderName.changelly);
expect(oneInceQuote!.provider).to.be.eq(ProviderName.oneInch);
expect(paraswapQuote!.provider).to.be.eq(ProviderName.paraswap);
expect(propellerHeadsQuote!.provider).to.be.eq(ProviderName.propellerHeads);
// expect(rangoQuote!.provider).to.be.eq(ProviderName.rango);
}).timeout(10000);
});

1 comment on commit f27a7af

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.