UNPKG

@brian-ai/ee-portals-solver

Version:

> ⚠️ README: work in progress.

339 lines (331 loc) 9.22 kB
// src/index.ts import { Solver } from "@brian-ai/ee-core"; // src/actions/deposit.ts import ky from "ky"; import { createPublicClient, http, encodeFunctionData, erc20Abi } from "viem"; // src/constants.ts var NATIVE_ADDRESS = "0x0000000000000000000000000000000000000000"; // src/utils.ts var ADJUSTED_CHAINS_NAME_PORTALS = { "arbitrum one": "arbitrum", "op mainnet": "optimism", avalanche: "avalanche", "bnb smart chain": "bsc", gnosis: "gnosis", polygon: "polygon", base: "base", ethereum: "ethereum", fantom: "fantom" }; // src/actions/deposit.ts import { getAmountToApprove } from "@brian-ai/ee-core"; var portalsDeposit = async ({ apiUrl: inputApiUrl, apiKey, feeReceiver, fee, slippage, chain, address, inputToken, outputToken, amount }) => { const adjustedChainName = ADJUSTED_CHAINS_NAME_PORTALS[chain.name.toLowerCase()]; const publicClient = createPublicClient({ chain, transport: http() }); const blockNumber = await publicClient.getBlockNumber(); const tokens = `${adjustedChainName.toLowerCase()}:${outputToken}`; const tokenInfoUrl = `${inputApiUrl}/tokens?ids=${tokens}&networks=${adjustedChainName.toLowerCase()}&sortBy=liquidity&sortDirection=desc`; const tokenOut = await fetch(tokenInfoUrl, { headers: { Authorization: `Bearer ${apiKey}` } }); const tokenOutJson = await tokenOut.json(); const tokenOutAddress = tokenOutJson.tokens[0].address; if (!tokenOut) { return Promise.reject("Portals: token not supported"); } const depositUrl = `${inputApiUrl}/portal?sender=${address}&inputToken=${adjustedChainName.toLowerCase()}%3A${inputToken}&inputAmount=${amount}&outputToken=${adjustedChainName.toLowerCase()}%3A${tokenOutAddress}&slippageTolerancePercentage=${slippage}&partner=${feeReceiver}&feePercentage=${fee}&validate=false`; const data = await ky.get(depositUrl, { headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` } }).json(); const steps = [ { chainId: chain.id, blockNumber: Number(blockNumber), from: data.tx.from, to: data.tx.to, gasLimit: "100000", gasPrice: void 0, data: data.tx.data, value: data.tx.value } ]; if (inputToken !== NATIVE_ADDRESS) { const approveAmount = await getAmountToApprove( inputToken, address, data.tx.to, amount, chain ); if (approveAmount > 0n) { steps.unshift({ chainId: chain.id, blockNumber: Number(blockNumber), from: address, to: inputToken, gasLimit: "80000", gasPrice: void 0, data: encodeFunctionData({ abi: erc20Abi, functionName: "approve", args: [data.tx.to, approveAmount] }), value: "0" }); } } return steps; }; // src/actions/withdraw.ts import ky2 from "ky"; import { createPublicClient as createPublicClient2, http as http2, encodeFunctionData as encodeFunctionData2, erc20Abi as erc20Abi2 } from "viem"; import { getAmountToApprove as getAmountToApprove2 } from "@brian-ai/ee-core"; var portalsWithdraw = async ({ apiUrl: inputApiUrl, apiKey, feeReceiver, fee, slippage, chain, address, inputToken, outputToken, amount }) => { const adjustedChainName = ADJUSTED_CHAINS_NAME_PORTALS[chain.name.toLowerCase()]; const publicClient = createPublicClient2({ chain, transport: http2() }); const blockNumber = await publicClient.getBlockNumber(); const tokens = `${adjustedChainName.toLowerCase()}:${outputToken}`; const tokenInfoUrl = `${inputApiUrl}/tokens?ids=${tokens}&networks=${adjustedChainName.toLowerCase()}&sortBy=liquidity&sortDirection=desc`; const tokenOut = await fetch(tokenInfoUrl, { headers: { Authorization: `Bearer ${apiKey}` } }); const tokenOutJson = await tokenOut.json(); const tokenOutAddress = tokenOutJson.tokens[0].address; if (!tokenOut) { return Promise.reject("Portals: token not supported"); } const withdrawUrl = `${inputApiUrl}/portal?sender=${address}&inputToken=${adjustedChainName.toLowerCase()}%3A${inputToken}&inputAmount=${amount}&outputToken=${adjustedChainName.toLowerCase()}%3A${tokenOutAddress}&slippageTolerancePercentage=${slippage}&partner=${feeReceiver}&feePercentage=${fee}&validate=false`; const data = await ky2.get(withdrawUrl, { headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` } }).json(); const steps = [ { chainId: chain.id, blockNumber: Number(blockNumber), from: data.tx.from, to: data.tx.to, gasLimit: "100000", gasPrice: void 0, data: data.tx.data, value: data.tx.value } ]; if (inputToken !== NATIVE_ADDRESS) { const approveAmount = await getAmountToApprove2( inputToken, address, data.tx.to, amount, chain ); if (approveAmount > 0n) { steps.unshift({ chainId: chain.id, blockNumber: Number(blockNumber), from: address, to: inputToken, gasLimit: "80000", gasPrice: void 0, data: encodeFunctionData2({ abi: erc20Abi2, functionName: "approve", args: [data.tx.to, approveAmount] }), value: "0" }); } } return steps; }; // src/actions/swap.ts import ky3 from "ky"; import { createPublicClient as createPublicClient3, http as http3, encodeFunctionData as encodeFunctionData3, erc20Abi as erc20Abi3 } from "viem"; import { getAmountToApprove as getAmountToApprove3 } from "@brian-ai/ee-core"; var portalsSwap = async ({ apiUrl: inputApiUrl, apiKey, feeReceiver, fee, slippage, chain, address, inputToken, outputToken, amount }) => { const adjustedChainName = ADJUSTED_CHAINS_NAME_PORTALS[chain.name.toLowerCase()]; const publicClient = createPublicClient3({ chain, transport: http3() }); const blockNumber = await publicClient.getBlockNumber(); const swapUrl = `${inputApiUrl}/portal?sender=${address}&inputToken=${adjustedChainName.toLowerCase()}%3A${inputToken}&inputAmount=${amount}&outputToken=${adjustedChainName.toLowerCase()}%3A${outputToken}&slippageTolerancePercentage=${slippage}&partner=${feeReceiver}&feePercentage=${fee}&validate=false`; const data = await ky3.get(swapUrl, { headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` } }).json(); const steps = [ { chainId: chain.id, blockNumber: Number(blockNumber), from: data.tx.from, to: data.tx.to, gasLimit: "100000", gasPrice: void 0, data: data.tx.data, value: data.tx.value } ]; if (inputToken !== NATIVE_ADDRESS) { const approveAmount = await getAmountToApprove3( inputToken, address, data.tx.to, amount, chain ); if (approveAmount > 0n) { steps.unshift({ chainId: chain.id, blockNumber: Number(blockNumber), from: address, to: inputToken, gasLimit: "80000", gasPrice: void 0, data: encodeFunctionData3({ abi: erc20Abi3, functionName: "approve", args: [data.tx.to, approveAmount] }), value: "0" }); } } return steps; }; // src/index.ts var PortalsSolver = class extends Solver { apiKey; apiUrl; feeReceiver; fee = 0.1; slippage = 0.5; constructor(allowedChains, apiUrl, apiKey, feeReceiver, fee = 0.1, slippage = 0.5) { super("portals", allowedChains); this.apiUrl = apiUrl; this.apiKey = apiKey; this.feeReceiver = feeReceiver; this.fee = fee; this.slippage = slippage; } async execute(action, data) { if (data.chain && !this.allowedChains.includes(data.chain.id)) { return Promise.reject( `[${this.name} solver] chain ${data.chain.id} not supported.` ); } switch (action) { case "swap": return { type: "write", solver: this.name, data: await portalsSwap({ ...data, apiUrl: this.apiUrl, apiKey: this.apiKey, feeReceiver: this.feeReceiver, fee: this.fee, slippage: this.slippage }) }; case "deposit": return { type: "write", solver: this.name, data: await portalsDeposit({ ...data, apiUrl: this.apiUrl, apiKey: this.apiKey, feeReceiver: this.feeReceiver, fee: this.fee, slippage: this.slippage }) }; case "withdraw": return { type: "write", solver: this.name, data: await portalsWithdraw({ ...data, apiUrl: this.apiUrl, apiKey: this.apiKey, feeReceiver: this.feeReceiver, fee: this.fee, slippage: this.slippage }) }; default: return Promise.reject( `[${this.name} solver] action "${action}" not supported.` ); } } }; export { PortalsSolver };