Skip to content
This repository was archived by the owner on May 16, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Don't hesitate to reach out or submit pull requests with missing aggregators ada
- [Li.Fi](https://li.fi/) `stable` `tested`
- [Squid Router](https://www.squidrouter.com/) `stable` `tested`
- [Socket](https://socket.tech/) `stable` `tested`
- [Sifi](https://sifi.org/) `untested`

### Liquidity
- [1inch](https://1inch.io/) `stable` `tested`
Expand Down Expand Up @@ -101,6 +102,7 @@ async function getTransactionRequests(): TransactionRequest[] {
- [0x](https://github.com/AstrolabFinance/swapper/blob/main/src/ZeroX/index.ts)
- [KyberSwap](https://github.com/AstrolabFinance/swapper/blob/main/src/KyberSwap/index.ts)
- [ParaSwap](https://github.com/AstrolabFinance/swapper/blob/main/src/ParaSwap/index.ts)
- [Sifi](https://github.com/AstrolabFinance/swapper/blob/main/src/Sifi/index.ts)

### Swapper Contract

Expand Down
87 changes: 87 additions & 0 deletions src/Sifi/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { ITransactionRequestWithEstimate } from "../types";
import qs from "qs";
import { ISwapperParams, validateQuoteParams } from "../types";

// Sifi specific types
interface IQuote {
// NOTE: No fields have been added since these are never accessed
}

interface IQuoteParams {
fromChain: number;
fromToken: string;
toChain?: number;
toToken: string;
fromAmount: string;
disablePermit?: number;
}

const ROUTER_ADDRESS = "0x65c49E9996A877d062085B71E1460fFBe3C4c5Aa";
const apiRoot = "https://api.sifi.org/v1/";

export const routerByChainId: { [id: number]: string } = {
1: ROUTER_ADDRESS,
10: ROUTER_ADDRESS,
56: ROUTER_ADDRESS,
137: ROUTER_ADDRESS,
8453: ROUTER_ADDRESS,
42161: ROUTER_ADDRESS,
43114: ROUTER_ADDRESS,
};

const convertParams = (o: ISwapperParams): IQuoteParams => ({
fromChain: o.inputChainId,
fromToken: o.input,
toChain: o.outputChainId,
toToken: o.output,
fromAmount: o.amountWei.toString(),
disablePermit: 1,
});

export async function getQuote(o: ISwapperParams): Promise<IQuote|undefined> {
if (!routerByChainId[o.inputChainId]) return undefined;
// NOTE: Required for validateQuoteParams
o.payer = o.payer || o.testPayer!;
if (!validateQuoteParams(o)) throw new Error("invalid input");
const params = convertParams(o);
try {
const url = `${apiRoot}quote?${qs.stringify(params)}`;
const res = await fetch(url);
if (res.status >= 400)
throw new Error(`${res.status}: ${res.statusText} - ${await res.text?.()}`);
return await res.json();
} catch (e) {
console.error(`getQuote failed: ${e}`);
}
}

// NOTE: Expects referrer to be an EVM address
export async function getTransactionRequest(o: ISwapperParams): Promise<ITransactionRequestWithEstimate|undefined> {
const quote = await getQuote(o);
if (!quote) return undefined;
const params = {
quote,
toAddress: o.receiver ?? o.payer ?? o.testPayer,
fromAddress: o.payer ?? o.testPayer,
partner: o.referrer
};
try {
const res = await fetch(
`${apiRoot}swap`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(params),
},
);
if (res.status >= 400)
throw new Error(`${res.status}: ${res.statusText} - ${await res.text?.()}`);
const swap = await res.json();
return {
...swap.tx,
estimatedGas: swap.tx.gasLimit,
};
} catch (e) {
console.error(`getTransactionRequest failed: ${e}`);
}
}
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as KyberSwap from "./KyberSwap";
import * as Squid from "./Squid";
import * as LiFi from "./LiFi";
import * as Socket from "./Socket";
import * as Sifi from "./Sifi";

import { Aggregator, AggregatorId, ISwapperParams, ITransactionRequestWithEstimate } from "./types";

Expand All @@ -19,6 +20,7 @@ export const aggregatorById: { [key: string]: Aggregator } = {
[AggregatorId.SQUID]: <Aggregator>Squid,
[AggregatorId.LIFI]: <Aggregator>LiFi,
[AggregatorId.SOCKET]: <Aggregator>Socket,
[AggregatorId.SIFI]: <Aggregator>Sifi,
};

/**
Expand Down Expand Up @@ -125,5 +127,6 @@ export {
KyberSwap,
Squid,
LiFi,
Socket
Socket,
Sifi
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export enum AggregatorId {
ONE_INCH = "ONE_INCH",
ZERO_X = "ZERO_X",
PARASWAP = "PARASWAP",
SIFI = "SIFI",
}

export interface ISwapperParams {
Expand Down
4 changes: 4 additions & 0 deletions test/unit/swapper.client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ describe("swapper.client.test", function () {
for (const tr of (await getTransactionRequestByAggregatorCases(AggregatorId.ZERO_X)))
expect(tr?.data).to.be.a("string");
});
it("Sifi", async function () {
for (const tr of (await getTransactionRequestByAggregatorCases(AggregatorId.SIFI)))
expect(tr?.data).to.be.a("string");
});
})
});

9 changes: 9 additions & 0 deletions test/utils/cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ export const cases = (): ISwapperParams[] => {
testPayer: addresses[250].accounts!.impersonate,
payer: "",
},
{
aggregatorId,
inputChainId: 42161, // Arbitrum One
input: addresses[42161].tokens.DAI,
output: addresses[42161].tokens.USDCE,
amountWei: 100 * 1e18, // string to prevent overflow
testPayer: addresses[42161].accounts!.impersonate,
payer: "",
},
],
);
}
Expand Down