UNPKG

@eco-foundation/routes-sdk

Version:
405 lines (395 loc) 12.6 kB
import { encodeFunctionData, erc20Abi, isAddress } from "./chunk-FR6EIUC4.js"; import { __require } from "./chunk-4VNS5WPM.js"; // src/constants.ts var chainIds = [ 1, // ETH Mainnet 10, // Optimism 130, // Unichain 137, // Polygon // 2741,// Abstract // 5000,// Mantle 8453, // Base 42161, // Arbitrum 42220, // Celo 57073 // Ink ]; var stables = ["USDC", "USDbC", "USDCe", "USDT", "oUSDT", "USDT0"]; var stableAddresses = { 1: { USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", USDT: "0xdac17f958d2ee523a2206206994597c13d831ec7", oUSDT: "0x1217bfe6c773eec6cc4a38b5dc45b92292b6e189" }, 10: { USDC: "0x0b2c639c533813f4aa9d7837caf62653d097ff85", USDCe: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", USDT: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", oUSDT: "0x1217bfe6c773eec6cc4a38b5dc45b92292b6e189" }, 130: { USDC: "0x078D782b760474a361dDA0AF3839290b0EF57AD6", USDT0: "0x9151434b16b9763660705744891fa906f660ecc5" }, 137: { USDC: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", USDCe: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", USDT: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f" }, // 2741: { // USDT: "0x0709f39376deee2a2dfc94a58edeb2eb9df012bd", // USDCe: "0x84a71ccd554cc1b02749b35d22f684cc8ec987e1", // }, // 5000: { // USDC: "0x09bc4e0d864854c6afb6eb9a9cdf58ac190d0df9", // USDT: "0x201EBa5CC46D216Ce6DC03F6a759e8E766e956aE", // }, 8453: { USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", USDbC: "0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA", oUSDT: "0x1217bfe6c773eec6cc4a38b5dc45b92292b6e189" }, 42161: { USDC: "0xaf88d065e77c8cc2239327c5edb3a432268e5831", USDCe: "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8", USDT0: "0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9" }, 42220: { USDC: "0xceba9300f2b948710d2653dd7b07f33a8b32118c", USDT: "0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e" }, 57073: { USDCe: "0xF1815bd50389c46847f0Bda824eC8da914045D14", USDT0: "0x0200C29006150606B650577BBE7B6248F58470c1" } }; // src/utils.ts import { randomBytes } from "crypto"; function getSecondsFromNow(seconds) { return new Date(Date.now() + 1e3 * seconds); } function dateToTimestamp(date) { return BigInt(Math.floor(date.getTime() / 1e3)); } function isAmountInvalid(amount) { return amount < BigInt(0); } function sum(items) { return items.reduce((acc, amount) => acc + BigInt(amount), BigInt(0)); } function generateRandomHex(length = 32) { return `0x${randomBytes(length).toString("hex")}`; } // src/routes/RoutesService.ts import { EcoProtocolAddresses } from "@eco-foundation/routes-ts"; // src/config.ts var ECO_SDK_CONFIG = { openQuotingBaseUrl: "https://quotes.eco.com", isPreprod: false }; // src/routes/RoutesService.ts var RoutesService = class { constructor({ isPreprod } = {}) { this.isPreprod = isPreprod || ECO_SDK_CONFIG.isPreprod || false; } /** * Creates a simple intent. * * @param {CreateSimpleIntentParams} params - The parameters for creating the simple intent. * * @returns {IntentType} The created intent. * * @throws {Error} If the creator address is invalid, the origin and destination chain are the same, the amount is invalid, or the expiry time is in the past. Or if there is no prover for the specified configuration. */ createSimpleIntent({ creator, originChainID, destinationChainID, receivingToken, spendingToken, spendingTokenLimit, amount, recipient = creator, prover = "HyperProver", expiryTime = getSecondsFromNow(90 * 60) // 90 minutes from now }) { if (!isAddress(creator, { strict: false })) { throw new Error("Invalid creator address"); } if (!isAddress(recipient, { strict: false })) { throw new Error("Invalid recipient address"); } if (originChainID === destinationChainID) { throw new Error("originChainID and destinationChainID cannot be the same"); } if (isAmountInvalid(amount)) { throw new Error("Invalid amount"); } if (spendingTokenLimit < BigInt(amount)) { throw new Error("Insufficient spendingTokenLimit"); } if (expiryTime < getSecondsFromNow(60)) { throw new Error("Expiry time must be 60 seconds or more in the future"); } const data = encodeFunctionData({ abi: erc20Abi, functionName: "transfer", args: [recipient, amount] }); return { route: { salt: generateRandomHex(), source: BigInt(originChainID), destination: BigInt(destinationChainID), inbox: EcoProtocolAddresses[this.getEcoChainId(destinationChainID)].Inbox, tokens: [ { token: receivingToken, amount } ], calls: [ { target: receivingToken, data, value: BigInt(0) } ] }, reward: { creator, prover: this.getProverContract(prover, originChainID), deadline: dateToTimestamp(expiryTime), nativeValue: BigInt(0), tokens: [ { token: spendingToken, amount: spendingTokenLimit } ] } }; } /** * Creates an intent. * * @param {CreateRouteParams} params - The parameters for creating the intent. * * @returns {IntentType} The created intent. * * @throws {Error} If the creator address is invalid, the origin and destination chain are the same, the calls or tokens are invalid, or the expiry time is in the past. */ createIntent({ creator, originChainID, destinationChainID, calls, callTokens, tokens, prover = "HyperProver", expiryTime = getSecondsFromNow(90 * 60) // 90 minutes from now }) { if (!isAddress(creator, { strict: false })) { throw new Error("Invalid creator address"); } if (originChainID === destinationChainID) { throw new Error("originChainID and destinationChainID cannot be the same"); } if (!calls.length || calls.some((call) => !isAddress(call.target, { strict: false }) || isAmountInvalid(call.value))) { throw new Error("Invalid calls"); } if (!callTokens.length || callTokens.some((token) => !isAddress(token.token, { strict: false }) || isAmountInvalid(token.amount))) { throw new Error("Invalid callTokens"); } if (!tokens.length || tokens.some((token) => !isAddress(token.token, { strict: false }) || isAmountInvalid(token.amount))) { throw new Error("Invalid tokens"); } if (expiryTime < getSecondsFromNow(60)) { throw new Error("Expiry time must be 60 seconds or more in the future"); } return { route: { salt: generateRandomHex(), source: BigInt(originChainID), destination: BigInt(destinationChainID), inbox: EcoProtocolAddresses[this.getEcoChainId(destinationChainID)].Inbox, tokens: callTokens, calls }, reward: { creator, prover: this.getProverContract(prover, originChainID), deadline: dateToTimestamp(expiryTime), nativeValue: BigInt(0), tokens } }; } /** * Applies a quote to an intent, modifying the reward tokens. * * @param {ApplyQuoteToIntentParams} params - The parameters for applying the quote to the intent. * * @returns {IntentType} The intent with the quote applied. * * @throws {Error} If the quote is invalid. */ applyQuoteToIntent({ intent, quote }) { if (!quote.quoteData.tokens.length) { throw new Error("Invalid quoteData: tokens array must have length greater than 0"); } intent.reward.tokens = quote.quoteData.tokens.map(({ token, amount }) => ({ token, amount: BigInt(amount) })); return intent; } /** * Returns the EcoChainId for a given chainId, appending "-pre" if the environment is pre-production. * * @param chainId - The chain ID to be converted to an EcoChainId. * @returns The EcoChainId, with "-pre" appended if the environment is pre-production. */ getEcoChainId(chainId) { return `${chainId}${this.isPreprod ? "-pre" : ""}`; } getProverContract(prover, chainID) { let proverContract; const ecoChainID = this.getEcoChainId(chainID); switch (prover) { case "HyperProver": { proverContract = EcoProtocolAddresses[ecoChainID].HyperProver; break; } case "StorageProver": { const defaultProver = EcoProtocolAddresses[ecoChainID].Prover; if (!defaultProver) { throw new Error("No default prover found for this chain"); } proverContract = defaultProver; break; } default: { proverContract = prover; } } return proverContract; } static getStableAddress(chainID, stable) { const stableAddress = stableAddresses[chainID][stable]; if (!stableAddress) { throw new Error(`Stable ${stable} not found on chain ${chainID}`); } return stableAddress; } static getStableFromAddress(chainID, address) { for (const stable in stableAddresses[chainID]) { if (stableAddresses[chainID][stable]?.toLowerCase() === address.toLowerCase()) { return stable; } } throw new Error(`Stable not found for address ${address} on chain ${chainID}`); } }; // src/quotes/OpenQuotingClient.ts import axios from "axios"; // src/quotes/types.ts var OpenQuotingAPI; ((OpenQuotingAPI2) => { let Endpoints; ((Endpoints2) => { Endpoints2["Quotes"] = "/api/v1/quotes"; })(Endpoints = OpenQuotingAPI2.Endpoints || (OpenQuotingAPI2.Endpoints = {})); })(OpenQuotingAPI || (OpenQuotingAPI = {})); // src/quotes/OpenQuotingClient.ts var OpenQuotingClient = class { constructor({ dAppID, customBaseUrl }) { this.MAX_RETRIES = 5; this.dAppID = dAppID; this.axiosInstance = axios.create({ baseURL: customBaseUrl || ECO_SDK_CONFIG.openQuotingBaseUrl }); import("axios-retry").then(({ default: axiosRetry }) => { axiosRetry(this.axiosInstance, { retries: this.MAX_RETRIES, retryDelay: axiosRetry.linearDelay(1e3) }); }).catch(() => { const axiosRetry = __require("axios-retry"); axiosRetry(this.axiosInstance, { retries: this.MAX_RETRIES, retryDelay: axiosRetry.linearDelay(1e3) }); }).catch(() => { console.warn("Failed to import axios-retry, OpenQuotingClient requests will only be attempted once"); }); } /** * Requests quotes for a given intent. * * @param intent - The intent for which quotes are being requested. * @returns A promise that resolves to an `OpenQuotingClient_ApiResponse_Quotes` object containing the quotes. * @throws An error if multiple requests fail. * * @remarks * This method sends a POST request to the `/api/v1/quotes` endpoint with the provided intent information. */ async requestQuotesForIntent(intent) { const payload = { dAppID: this.dAppID, intentData: { routeData: { originChainID: intent.route.source.toString(), destinationChainID: intent.route.destination.toString(), inboxContract: intent.route.inbox, tokens: intent.route.tokens.map((token) => ({ token: token.token, amount: token.amount.toString() })), calls: intent.route.calls.map((call) => ({ target: call.target, data: call.data, value: call.value.toString() })) }, rewardData: { creator: intent.reward.creator, proverContract: intent.reward.prover, deadline: intent.reward.deadline.toString(), nativeValue: intent.reward.nativeValue.toString(), tokens: intent.reward.tokens.map((token) => ({ token: token.token, amount: token.amount.toString() })) } } }; const response = await this.axiosInstance.post(OpenQuotingAPI.Endpoints.Quotes, payload); return response.data.data; } }; // src/quotes/quoteSelectors.ts function selectCheapestQuote(quotes) { return quotes.reduce((cheapest, quote) => { const cheapestSum = cheapest ? sum(cheapest.quoteData.tokens.map(({ amount }) => amount)) : BigInt(Infinity); const quoteSum = sum(quote.quoteData.tokens.map(({ amount }) => amount)); return quoteSum < cheapestSum ? quote : cheapest; }); } export { OpenQuotingClient, RoutesService, chainIds, selectCheapestQuote, stableAddresses, stables };