From ada69465afff741b95b1d344566646befde49e6a Mon Sep 17 00:00:00 2001 From: Andre Meyer Date: Wed, 4 Oct 2023 17:16:50 +0200 Subject: [PATCH] wip refactor --- .eslintrc.cjs | 3 +- package.json | 2 +- playground/tsconfig.json | 26 +- src/generated/api.d.ts | 974 ------------------ tsconfig.json | 40 + use-defi/.eslintrc.cjs | 26 + use-defi/package.json | 14 +- use-defi/src/api/approve.ts | 14 +- use-defi/src/api/multichain.ts | 27 +- use-defi/src/api/positions.ts | 3 +- use-defi/src/api/route.ts | 18 +- .../src/hooks/useExecutePosition/index.ts | 17 +- use-defi/src/hooks/usePositions/index.ts | 2 +- use-defi/src/utils/fetch.ts | 35 + use-defi/src/utils/formatTransaction.ts | 20 +- use-defi/src/utils/parseApiError.ts | 2 +- use-defi/tsconfig.json | 43 +- 17 files changed, 153 insertions(+), 1113 deletions(-) delete mode 100644 src/generated/api.d.ts create mode 100644 tsconfig.json create mode 100644 use-defi/.eslintrc.cjs create mode 100644 use-defi/src/utils/fetch.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index dff88cc..163acf1 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -14,7 +14,7 @@ module.exports = { ecmaVersion: 'latest', sourceType: 'module', tsconfigRootDir: __dirname, - project: ['./tsconfig.json'], + project: ['./use-defi/tsconfig.json', './playground/tsconfig.json'], }, plugins: ['react-refresh'], rules: { @@ -22,5 +22,6 @@ module.exports = { '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-explicit-any': ['warn'], '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unnecessary-type-assertion': 'off', }, }; diff --git a/package.json b/package.json index 6becda4..0f94067 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "postinstall": "pnpm generate", "lint": "pnpm eslint src/*", "test": "pnpm vitest run --root ./test --config ./vitest.config.ts", - "generate": "npx openapi-typescript https://api.enso.finance/api-json -o src/generated/api.d.ts", + "generate": "npx openapi-typescript https://api.enso.finance/api-json -o use-defi/src/generated/api.d.ts", "build": "pnpm generate && pnpm build:setup && rollup -c", "build:setup": "pnpm gts clean && mkdir dist && jq 'del(.scripts) | del(.devDependencies) | del(.private) | del(.pnpm)' package.json > dist/package.json && cp README.md dist/README.md", "dev": "cd playground && vite dev" diff --git a/playground/tsconfig.json b/playground/tsconfig.json index 4ce646d..7801fdd 100644 --- a/playground/tsconfig.json +++ b/playground/tsconfig.json @@ -1,35 +1,15 @@ { + "extends": "../tsconfig.json", "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": [ - "ES2020", - "DOM", - "DOM.Iterable" - ], - "module": "ESNext", - "skipLibCheck": true, - /* Bundler mode */ - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - /* Linting */ - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noFallthroughCasesInSwitch": true, "paths": { "@ensofinance/use-defi": [ - "../src/index.ts" + "../use-defi/src/index.ts" ] } }, "include": [ "src", - "../src/*.ts" + "../use-defi/src" ], "references": [ { diff --git a/src/generated/api.d.ts b/src/generated/api.d.ts deleted file mode 100644 index dce5c61..0000000 --- a/src/generated/api.d.ts +++ /dev/null @@ -1,974 +0,0 @@ -/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - - -export interface paths { - "/api/v1/defiTokens": { - /** Returns defi tokens that have a yield, available to use at the /route endpoints. Includes underlying tokens, APYs, TVLs, and more. */ - get: operations["DefiTokensController_defiTokens"]; - }; - "/api/v1/baseTokens": { - /** Returns base tokens available to use at the /route endpoint */ - get: operations["BaseTokensController_baseTokens"]; - }; - "/api/v1/networks": { - /** Returns networks supported by the API */ - get: operations["NetworksController_networks"]; - }; - "/api/v1/projects": { - /** Returns projects and relevant protocols available to use in bundle shortcuts */ - get: operations["ProjectsController_projects"]; - }; - "/api/v1/prices/{chainId}/{address}": { - /** Returns price for defi token or base token */ - get: operations["PricesController_getPrice"]; - }; - "/api/v1/actions": { - /** Returns actions available to use in bundle shortcuts */ - get: operations["ActionsController_findAll"]; - }; - "/api/v1/standards": { - /** Returns standards and methods available to use in bundle shortcuts */ - get: operations["StandardsController_standards"]; - }; - "/api/v1/shortcuts/route": { - /** Best route from a token to another */ - get: operations["RouterController_routeShortcutTransaction"]; - /** Best route from a token to another */ - post: operations["RouterController_postRouteShortcutTransaction"]; - }; - "/api/v1/wallet": { - /** Returns EnsoWallet address details */ - get: operations["WalletController_wallet"]; - }; - "/api/v1/wallet/approve": { - /** Returns transaction that approves your EnsoWallet to spend tokens */ - get: operations["WalletController_createApproveTransaction"]; - }; - "/api/v1/wallet/balances": { - get: operations["WalletController_walletBalances"]; - }; - "/api/v1/shortcuts/bundle": { - /** Bundle a list of actions into a single tx */ - post: operations["BundleController_bundleShortcutTransaction"]; - }; - "/api/v1/shortcuts/builder": { - /** Build a shortcut from multiple contract calls */ - post: operations["BuilderController_builderShortcutTransaction"]; - }; - "/api/v1/shortcuts/static/ipor": { - /** Get transaction for IPOR shortcut */ - post: operations["IporController_iporShortcutTransaction"]; - }; - "/api/v1/shortcuts/quote": { - /** Quote from a token to another */ - get: operations["QuoteController_quote"]; - /** Simulate a route */ - post: operations["QuoteController_quoteMultipath"]; - }; - "/api/experimental/multichain/shortcut/route": { - post: operations["SocketController_multichainRouteShortcutTransactionWithSocket"]; - }; -} - -export type webhooks = Record; - -export interface components { - schemas: { - PoolToken: { - /** @example 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 */ - address: string; - /** @example Liquid staked Ether 2.0 */ - name: string; - /** @example stETH */ - symbol: string; - /** @example 18 */ - decimals: number; - /** @example 1 */ - chain: number; - /** @example lido */ - project: string; - /** @example lido */ - protocol: string; - /** @example 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 */ - poolAddress: string; - /** @example 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 */ - primaryAddress: string; - /** - * @example [ - * "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" - * ] - */ - underlyingTokens: string[]; - }; - Protocol: { - /** @example lido */ - name: string; - /** @example lido */ - slug: string; - /** @example https://icons.llama.fi/lido.png */ - logo: string; - /** @example https://lido.fi/ */ - url: string; - /** @example Liquidity for staked assets. Daily rewards, no lock ups. Available for Ethereum, Solana, Polygon, Terra, Kusama & Polkadot. */ - description: string; - /** @example LidoFinance */ - twitter: string; - /** @example Liquid Staking */ - category: string; - /** - * @example [ - * 1 - * ] - */ - chainIds: number[]; - }; - DefiToken: { - /** @example 3.8 */ - apy?: number; - /** @example null */ - apyBase?: number; - /** @example 0 */ - apyReward?: number; - /** @example 1 */ - chainId: number; - /** @example 0xae7ab96520de3a18e5e111b5eaab095312d7fe84:0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee */ - id: string; - /** @example Liquid staked Ether 2.0 */ - name: string; - /** @example 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 */ - poolAddress: string; - /** @example 0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84 */ - primaryAddress: string; - /** @example stETH */ - subtitle: string; - /** @example [] */ - rewardPoolTokens: string[]; - token?: components["schemas"]["PoolToken"]; - tokenAddress: string; - tvl: number; - underlyingTokens: string[]; - project: string; - protocol: components["schemas"]["Protocol"]; - }; - BaseToken: { - /** @example PieDAO BTC++ */ - name: string; - /** @example BTC++ */ - symbol: string; - /** @example 0x0327112423F3A68efdF1fcF402F6c5CB9f7C33fd */ - address: string; - /** @example 18 */ - decimals: number; - /** @example https://metadata-service-dev.herokuapp.com/api/token/1/0x0327112423f3a68efdf1fcf402f6c5cb9f7c33fd/icon */ - logoURI: string; - /** @example 1 */ - chainId: number; - }; - Network: { - /** @example 1 */ - id: number; - /** @example Ethereum */ - name: string; - }; - Project: { - /** @example Iron Bank */ - title: string; - /** @example iron-bank */ - id: string; - /** @example compound */ - protocol: string; - /** - * @example [ - * 1, - * 10 - * ] - */ - chainIds: string; - /** @example Test Description */ - description: string; - /** @example @handle */ - twitter: string; - /** @example Lending */ - category: string; - /** @example 523 */ - poolCount: number; - }; - Price: { - /** @example 8 */ - decimals: number; - /** @example 27052 */ - price: number; - /** @example 0x2260fac5e5542a773aa44fbcfedf7c193bc2c599 */ - address: string; - /** @example WBTC */ - symbol: string; - /** @example 0.99 */ - confidence: number; - /** @example 1695197412 */ - timestamp: number; - /** @example 1 */ - chainId: number; - }; - Action: { - /** @enum {string} */ - action: "approve" | "borrow" | "deposit" | "harvest" | "permittransferfrom" | "redeem" | "repay" | "apiswap" | "swap" | "transfer" | "transferfrom" | "withdraw" | "route" | "split"; - inputs: { - [key: string]: string | undefined; - }; - }; - StandardAction: { - /** @enum {string} */ - action: "approve" | "borrow" | "deposit" | "harvest" | "permittransferfrom" | "redeem" | "repay" | "apiswap" | "swap" | "transfer" | "transferfrom" | "withdraw" | "route" | "split"; - inputs: { - [key: string]: string | undefined; - }; - name: string; - functionNames: string[]; - supportedChains: components["schemas"]["Network"][]; - }; - Standard: { - protocol: components["schemas"]["Protocol"]; - forks: components["schemas"]["Protocol"][]; - actions: components["schemas"]["StandardAction"][]; - }; - Hop: { - tokenIn: string[]; - positionInId: string[]; - tokenOut: string[]; - positionOutId: string[]; - protocol: string; - action: string; - primary: string; - internalRoutes: string[]; - }; - Transaction: { - data: string; - to: string; - from: string; - value: string; - }; - RouteShortcutTransaction: { - /** @description The route the shortcut will use */ - route: components["schemas"]["Hop"][]; - gas: number; - amountOut: string[]; - /** @description Price impact in basis points, null if USD price not found */ - priceImpact: number; - /** @description Block number the transaction was created on */ - createdAt: number; - /** @description The tx object to use in `ethers` */ - tx: components["schemas"]["Transaction"]; - }; - RouteShortcutInputs: { - /** - * @description Chain ID of the network to execute the transaction on - * @default 1 - */ - chainId?: number; - /** - * @description Ethereum address of the wallet to send the transaction from - * @default 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 - */ - fromAddress: string; - /** @description Flag that indicates whether to calculate and return the price impact of the transaction */ - priceImpact?: boolean | null; - /** - * @deprecated - * @description Flag that indicates if gained tokenOut should be sent to EOA - */ - toEoa?: boolean | null; - /** - * @description Ethereum address of the receiver of the tokenOut - * @example 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 - */ - receiver?: string; - /** - * @description Amount of tokenIn to swap in wei - * @example [ - * "1000000000000000000" - * ] - */ - amountIn: string[]; - /** - * @description Minimum amount out in wei. If specified, slippage should not be specified - * @example [ - * "1000000000000000000" - * ] - */ - minAmountOut?: string[]; - /** - * @description Slippage in basis points (1/10000). If specified, minAmountOut should not be specified - * @default 300 - * @example 300 - */ - slippage?: string; - /** - * @description Ethereum address of the token to swap from. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example [ - * "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - * ] - */ - tokenIn: string[]; - /** - * @description Ethereum address of the token to swap to. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example [ - * "0x6b175474e89094c44da98b954eedeac495271d0f" - * ] - */ - tokenOut: string[]; - }; - Wallet: { - address: string; - isDeployed: boolean; - }; - WalletApproveTransaction: { - /** @description The tx object to use in `ethers` */ - tx: Record; - /** @description The gas estimate for the transaction */ - gas: string; - /** @description The token address to approve */ - token: string; - /** @description The amount of tokens to approve */ - amount: string; - /** @description The spender address to approve */ - spender: string; - }; - ActionToBundle: { - protocol: string; - /** @enum {string} */ - action: "approve" | "borrow" | "deposit" | "harvest" | "permittransferfrom" | "redeem" | "repay" | "apiswap" | "swap" | "transfer" | "transferfrom" | "withdraw" | "route" | "split"; - args: Record; - }; - CallsToBuild: { - address: string; - method: string; - value?: string; - /** - * @example [ - * 123, - * true, - * { - * "id": 123 - * } - * ] - */ - args?: unknown[][]; - abi?: string; - }; - BuilderShortcutRequestDto: { - /** - * @description List of calls to build - * @example [ - * { - * "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - * "method": "deposit", - * "value": "1000000000000000000", - * "args": [] - * }, - * { - * "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - * "method": "balanceOf", - * "args": [ - * "0x89ba58Cc0e8bcbC1108dbD6F33356a136a021C62" - * ] - * }, - * { - * "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", - * "method": "withdraw", - * "args": [ - * { - * "useOutputOfCallAt": 1 - * } - * ] - * } - * ] - */ - calls: components["schemas"]["CallsToBuild"][]; - }; - IporShortcutInput: { - /** - * @description Amount of tokenIn in wei - * @example 1000000000000000 - */ - amountIn: string; - /** - * @description Address of the tokenIn. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - */ - tokenIn: string; - /** - * @description Address of the tokenBToBuy - * @example 0x0e6c016417A0108b76E35939EE7F8F922a4Ef638 - */ - tokenBToBuy: string; - /** - * @description Percentage of tokenB to buy in basis points (1/10000) - * @example 5000 - */ - percentageForTokenB: string; - /** - * @description Slippage in basis points (1/10000). Default is 300 - * @default 300 - * @example 300 - */ - slippage?: string; - }; - IporShortcutTransaction: { - /** @description Block number the transaction was created on */ - createdAt: number; - /** @description The tx object to use in `ethers` */ - tx: components["schemas"]["Transaction"]; - }; - Step: { - tokenIn: string; - tokenOut: string; - protocol: string; - action: string; - primary: string; - }; - Path: { - /** - * @description Amount of tokenIn to swap in wei - * @example 1000000000000000000 - */ - amountIn: string; - /** @description Ordered array of steps to build */ - path: components["schemas"]["Step"][]; - }; - QuoteRouteInputs: { - /** - * @description Chain ID of the network to execute the transaction on - * @default 1 - */ - chainId?: number; - /** - * @description Ethereum address of the wallet to send the transaction from - * @example 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 - */ - fromAddress?: string; - /** @description Ordered array of paths that you want to simulate */ - route: components["schemas"]["Path"][]; - }; - Quote: { - /** @description The route the shortcut will use */ - route: components["schemas"]["Hop"][]; - gas: number; - amountOut: string[]; - /** @description Price impact in basis points, null if USD price not found */ - priceImpact: number; - }; - MultichainRouteShortcutInputsIn: { - /** - * @description Chain ID of the token to swap from - * @example 42161 - */ - sourceChainId: number; - /** - * @description Address of the token to swap from. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - */ - token: string; - }; - MultichainRouteShortcutInputsOut: { - /** - * @description Chain ID of the token to swap to - * @example 1 - */ - destinationChainId: number; - /** - * @description Address of the token to swap from. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example 0xae7ab96520de3a18e5e111b5eaab095312d7fe84 - */ - token: string; - }; - MultichainRouteShortcutInputsBase: { - /** - * @description Amount of tokenIn to swap in wei - * @example 1000000000000000000 - */ - amountIn: string; - /** - * @description Slippage in basis points (1/10000) - * @default 300 - * @example 300 - */ - slippage?: string; - /** - * @description Address and chain of the token to swap from - * @example { - * "sourceChainId": 42161, - * "token": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - * } - */ - in: components["schemas"]["MultichainRouteShortcutInputsIn"]; - /** - * @description Address and chain of the token to swap to - * @example { - * "destinationChainId": 1, - * "token": "0xae7ab96520de3a18e5e111b5eaab095312d7fe84" - * } - */ - out: components["schemas"]["MultichainRouteShortcutInputsOut"]; - /** - * @description Address of the user - * @example 0x93621DCA56fE26Cdee86e4F6B18E116e9758Ff11 - */ - fromAddress: string; - }; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} - -export type external = Record; - -export interface operations { - - /** Returns defi tokens that have a yield, available to use at the /route endpoints. Includes underlying tokens, APYs, TVLs, and more. */ - DefiTokensController_defiTokens: { - parameters: { - query?: { - /** - * @description Address of an underlying token - * @example 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 - */ - underlyingAddress?: unknown; - /** - * @description Address of the token to search for - * @example 0xc4AD29ba4B3c580e6D59105FFf484999997675Ff - */ - tokenAddress?: unknown; - /** - * @description Protocol of the token to search for - * @example curve-dex - */ - protocol?: unknown; - /** - * @description Project of the token to search for - * @example curve-dex - */ - project?: unknown; - /** - * @description Symbol of the token to search for - * @example crv3crypto - */ - symbol?: unknown; - /** - * @description Chain ID of the token to search for - * @example 1 - */ - chainId?: unknown; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["DefiToken"][]; - }; - }; - }; - }; - /** Returns base tokens available to use at the /route endpoint */ - BaseTokensController_baseTokens: { - parameters: { - query: { - /** - * @description Address of the token to search for - * @example 0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 - */ - address: unknown; - /** - * @description Symbol of the token to search for - * @example USDC - */ - symbol?: unknown; - /** - * @description Name of the token to search for - * @example USD Coin - */ - name?: unknown; - /** - * @description Chain ID of the token to search for - * @example 1 - */ - chainId?: unknown; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["BaseToken"][]; - }; - }; - }; - }; - /** Returns networks supported by the API */ - NetworksController_networks: { - parameters: { - query?: { - /** - * @description Title of the network to search for - * @example Ethereum - */ - name?: unknown; - /** - * @description Chain ID of the network to search for - * @example 1 - */ - chainId?: unknown; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["Network"][]; - }; - }; - }; - }; - /** Returns projects and relevant protocols available to use in bundle shortcuts */ - ProjectsController_projects: { - parameters: { - query?: { - /** - * @description Protocol of the project to search for - * @example aave-v3 - */ - protocol?: unknown; - /** - * @description ID of the project to search for - * @example aave-v3 - */ - id?: unknown; - /** - * @description Title of the project to search for - * @example aave-v3 - */ - title?: unknown; - /** - * @description Chain ID of the project to search for - * @example 1 - */ - chainId?: unknown; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["Project"][]; - }; - }; - }; - }; - /** Returns price for defi token or base token */ - PricesController_getPrice: { - parameters: { - path: { - /** - * @description Address of the token to search for - * @example 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 - */ - address: unknown; - /** - * @description Chain ID of the network to search for - * @example 1 - */ - chainId: unknown; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["Price"]; - }; - }; - }; - }; - /** Returns actions available to use in bundle shortcuts */ - ActionsController_findAll: { - responses: { - 200: { - content: { - "application/json": components["schemas"]["Action"][]; - }; - }; - }; - }; - /** Returns standards and methods available to use in bundle shortcuts */ - StandardsController_standards: { - responses: { - 200: { - content: { - "application/json": components["schemas"]["Standard"][]; - }; - }; - }; - }; - /** Best route from a token to another */ - RouterController_routeShortcutTransaction: { - parameters: { - query: { - /** @description Chain ID of the network to execute the transaction on */ - chainId?: number; - /** @description Ethereum address of the wallet to send the transaction from */ - fromAddress: string; - /** @description Flag that indicates whether to calculate and return the price impact of the transaction */ - priceImpact?: boolean | null; - /** - * @deprecated - * @description Flag that indicates if gained tokenOut should be sent to EOA - */ - toEoa?: boolean | null; - /** - * @description Ethereum address of the receiver of the tokenOut - * @example 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 - */ - receiver?: string; - /** - * @description Amount of tokenIn to swap in wei - * @example [ - * "1000000000000000000" - * ] - */ - amountIn: string[]; - /** - * @description Minimum amount out in wei. If specified, slippage should not be specified - * @example [ - * "1000000000000000000" - * ] - */ - minAmountOut?: string[]; - /** - * @description Slippage in basis points (1/10000). If specified, minAmountOut should not be specified - * @example 300 - */ - slippage?: string; - /** - * @description Ethereum address of the token to swap from. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example [ - * "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - * ] - */ - tokenIn: string[]; - /** - * @description Ethereum address of the token to swap to. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example [ - * "0x6b175474e89094c44da98b954eedeac495271d0f" - * ] - */ - tokenOut: string[]; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["RouteShortcutTransaction"]; - }; - }; - 400: never; - }; - }; - /** Best route from a token to another */ - RouterController_postRouteShortcutTransaction: { - requestBody: { - content: { - "application/json": components["schemas"]["RouteShortcutInputs"]; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["RouteShortcutTransaction"]; - }; - }; - 400: never; - }; - }; - /** Returns EnsoWallet address details */ - WalletController_wallet: { - parameters: { - query: { - /** @description Chain ID of the network to execute the transaction on */ - chainId?: number; - /** @description Ethereum address of the wallet to send the transaction from */ - fromAddress: string; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["Wallet"][]; - }; - }; - }; - }; - /** Returns transaction that approves your EnsoWallet to spend tokens */ - WalletController_createApproveTransaction: { - parameters: { - query: { - /** @description Chain ID of the network to execute the transaction on */ - chainId?: number; - /** @description Ethereum address of the wallet to send the transaction from */ - fromAddress: string; - /** @description ERC20 token address of the token to approve */ - tokenAddress: string; - /** @description Amount of tokens to approve in wei */ - amount: string; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["WalletApproveTransaction"]; - }; - }; - }; - }; - WalletController_walletBalances: { - parameters: { - query: { - /** @description Chain ID of the network to execute the transaction on */ - chainId?: number; - /** @description Ethereum address of the eoaAddress to check balances wallet for */ - eoaAddress: string; - tokenType: "defiTokens" | "baseTokens" | null; - }; - }; - responses: { - 200: never; - }; - }; - /** Bundle a list of actions into a single tx */ - BundleController_bundleShortcutTransaction: { - parameters: { - query: { - /** @description Chain ID of the network to execute the transaction on */ - chainId?: number; - /** @description Ethereum address of the wallet to send the transaction from */ - fromAddress: string; - }; - }; - requestBody: { - content: { - "application/json": components["schemas"]["ActionToBundle"][]; - }; - }; - responses: { - 200: never; - }; - }; - /** Build a shortcut from multiple contract calls */ - BuilderController_builderShortcutTransaction: { - parameters: { - query: { - /** @description Chain ID of the network to execute the transaction on */ - chainId?: number; - /** @description Ethereum address of the wallet to send the transaction from */ - fromAddress: string; - }; - }; - requestBody: { - content: { - "application/json": components["schemas"]["BuilderShortcutRequestDto"]; - }; - }; - responses: { - 201: never; - }; - }; - /** Get transaction for IPOR shortcut */ - IporController_iporShortcutTransaction: { - parameters: { - query: { - /** @description Chain ID of the network to execute the transaction on */ - chainId?: number; - /** @description Ethereum address of the wallet to send the transaction from */ - fromAddress: string; - }; - }; - requestBody: { - content: { - "application/json": components["schemas"]["IporShortcutInput"]; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["IporShortcutTransaction"]; - }; - }; - }; - }; - /** Quote from a token to another */ - QuoteController_quote: { - parameters: { - query: { - /** @description Chain ID of the network to execute the transaction on */ - chainId?: number; - /** - * @description Ethereum address of the wallet to send the transaction from - * @example 0xd8da6bf26964af9d7eed9e03e53415d37aa96045 - */ - fromAddress?: string; - /** - * @description Ethereum address of the token to swap from. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example [ - * "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" - * ] - */ - tokenIn: string[]; - /** - * @description Ethereum address of the token to swap from. For ETH, use 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee - * @example [ - * "0x6b175474e89094c44da98b954eedeac495271d0f" - * ] - */ - tokenOut: string[]; - /** - * @description Amount of tokenIn to swap in wei - * @example [ - * "1000000000000000000" - * ] - */ - amountIn: string[]; - /** @description Flag that indicates whether to calculate and return the price impact of the transaction */ - priceImpact?: boolean | null; - }; - }; - responses: { - 200: never; - }; - }; - /** Simulate a route */ - QuoteController_quoteMultipath: { - requestBody: { - content: { - "application/json": components["schemas"]["QuoteRouteInputs"]; - }; - }; - responses: { - 200: { - content: { - "application/json": components["schemas"]["Quote"]; - }; - }; - 400: never; - }; - }; - SocketController_multichainRouteShortcutTransactionWithSocket: { - requestBody: { - content: { - "application/json": components["schemas"]["MultichainRouteShortcutInputsBase"]; - }; - }; - responses: { - 201: never; - }; - }; -} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..585d2d0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "target": "ES2017", + "module": "ESNext", + "lib": [ + "ESNext", + "DOM", + "DOM.Iterable" + ], + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "declaration": true, + "resolveJsonModule": true, + "rootDir": "./", + "baseUrl": "./", + "jsx": "preserve", + "skipLibCheck": true, + "noUnusedLocals": true, + "emitDeclarationOnly": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "outDir": "./dist", + "types": [ + "vitest/globals" + ], + }, + "files": [], + "references": [ + { + "path": "./use-defi" + }, + { + "path": "./playground" + } + ] +} \ No newline at end of file diff --git a/use-defi/.eslintrc.cjs b/use-defi/.eslintrc.cjs new file mode 100644 index 0000000..dff88cc --- /dev/null +++ b/use-defi/.eslintrc.cjs @@ -0,0 +1,26 @@ +/* eslint-env node */ + +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'plugin:react-hooks/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-explicit-any': ['warn'], + '@typescript-eslint/no-unsafe-assignment': 'off', + }, +}; diff --git a/use-defi/package.json b/use-defi/package.json index 67027e3..19054f8 100644 --- a/use-defi/package.json +++ b/use-defi/package.json @@ -2,14 +2,14 @@ "name": "@ensofinance/use-defi", "version": "0.2.3", "description": "React hooks library to use with enso shortcuts infrastructure", - "main": "dist/index.js", - "module": "dist/index.esm.js", - "types": "dist/index.d.ts", + "main": "index.js", + "module": "index.esm.js", + "types": "index.d.ts", "exports": { ".": { - "require": "./dist/index.js", - "import": "./dist/index.esm.js", - "types": "./dist/index.d.ts" + "require": "./index.js", + "import": "./index.esm.js", + "types": "./index.d.ts" } }, "scripts": { @@ -66,4 +66,4 @@ "vite-tsconfig-paths": "^4.2.0", "vitest": "^0.33.0" } -} +} \ No newline at end of file diff --git a/use-defi/src/api/approve.ts b/use-defi/src/api/approve.ts index 7d6629a..4d8b784 100644 --- a/use-defi/src/api/approve.ts +++ b/use-defi/src/api/approve.ts @@ -1,7 +1,5 @@ -import { ENSO_API } from '../constants'; import { API_AllowancesOptions, API_AllowancesResponse, API_ApproveOptions, API_ApproveResponse } from '../types/api'; -import { API_Response } from '../types/enso'; -import { parseApiErrorOrReturn } from '../utils/parseApiError'; +import { apiFetchGet } from '../utils/fetch'; export const getEnsoApiApprove = async (options: API_ApproveOptions): Promise => { const queryParams = { @@ -11,10 +9,7 @@ export const getEnsoApiApprove = async (options: API_ApproveOptions): Promise('api/v1/wallet/approve', queryParams); }; export const getEnsoApiAllowance = async ( @@ -25,8 +20,5 @@ export const getEnsoApiAllowance = async ( fromAddress: options.fromAddress, }; - const response = await fetch(`${ENSO_API}/api/v1/wallet/approvals?${new URLSearchParams(queryParams)}`); - const data = await response.json(); - - return parseApiErrorOrReturn(data); + return apiFetchGet('api/v1/wallet/approvals', queryParams); }; diff --git a/use-defi/src/api/multichain.ts b/use-defi/src/api/multichain.ts index 532b470..d766b31 100644 --- a/use-defi/src/api/multichain.ts +++ b/use-defi/src/api/multichain.ts @@ -1,27 +1,18 @@ -import { ENSO_API } from '../constants'; import { API_CrossChainOptions, API_CrossChainResponse } from '../types/api'; -import { parseApiErrorOrReturn } from '../utils/parseApiError'; +import { apiFetchPost } from '../utils/fetch'; export const getEnsoApiCrossChainRoute = async ( options: API_CrossChainOptions, ): Promise => { - const response = await fetch( - `${ENSO_API}/api/experimental/multichain/shortcut/route/${options.sourceChainId}/${options.destinationChainId}/${options.fromAddress}`, + return apiFetchPost( + `api/experimental/multichain/shortcut/route/${options.sourceChainId}/${options.destinationChainId}/${options.fromAddress}`, { - method: 'POST', - headers: { - Authorization: `Bearer ${options.apiKey}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - tokenIn: options.tokenIn, - tokenOut: options.tokenOut, - amountIn: options.amountIn, - slippage: options.slippage, - }), + tokenIn: options.tokenIn, + tokenOut: options.tokenOut, + amountIn: options.amountIn, + slippage: options.slippage, }, + undefined, + options.apiKey, ); - const data = await response.json(); - - return parseApiErrorOrReturn(data); }; diff --git a/use-defi/src/api/positions.ts b/use-defi/src/api/positions.ts index 1dc8049..69ea080 100644 --- a/use-defi/src/api/positions.ts +++ b/use-defi/src/api/positions.ts @@ -1,11 +1,10 @@ import { USE_POSITIONS_DATA_SOURCE } from '../constants'; import { API_GetPositionsResponse } from '../types/api'; -import { API_Response } from '../types/enso'; import { parseApiErrorOrReturn } from '../utils/parseApiError'; export const getEnsoApiPositions = async (): Promise => { const response = await fetch(USE_POSITIONS_DATA_SOURCE); const data = await response.json(); - return parseApiErrorOrReturn(data); + return parseApiErrorOrReturn(data as API_GetPositionsResponse); }; diff --git a/use-defi/src/api/route.ts b/use-defi/src/api/route.ts index 2ea92d0..ff5e261 100644 --- a/use-defi/src/api/route.ts +++ b/use-defi/src/api/route.ts @@ -1,10 +1,9 @@ import { Address } from 'viem'; -import { ENSO_API } from '../constants'; import { API_RouteOptions, ExecutableRoute } from '../types/api'; -import { API_Response, BigNumberish } from '../types/enso'; +import { BigNumberish } from '../types/enso'; import { manyBigIntParseToString } from '../utils/bigint'; -import { parseApiErrorOrReturn } from '../utils/parseApiError'; +import { apiFetchPost } from '../utils/fetch'; export type QueryRouteOptions = { chainId: number; @@ -59,16 +58,5 @@ export const getEnsoApiBundleRoute = async (options: QueryRouteOptions): Promise const actions = [routeAction]; - const response = await fetch(`${ENSO_API}/api/v1/shortcuts/bundle?${new URLSearchParams(queryParams)}`, { - method: 'POST', - headers: { - Authorization: `Bearer ${options.apiKey}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify(actions), - }); - - const data = await response.json(); - - return parseApiErrorOrReturn(data); + return apiFetchPost('api/v1/shortcuts/bundle', actions, queryParams, options.apiKey); }; diff --git a/use-defi/src/hooks/useExecutePosition/index.ts b/use-defi/src/hooks/useExecutePosition/index.ts index eee2c9f..e1787ea 100644 --- a/use-defi/src/hooks/useExecutePosition/index.ts +++ b/use-defi/src/hooks/useExecutePosition/index.ts @@ -58,9 +58,10 @@ export const useExecutePosition = (args?: UseExecutePositionArgs): UseExecuteSho }); const preparedTransaction = useMemo(() => { - if (!routeQueryResponse || routeQueryResponse.status === 'error' || !routeQueryResponse.route) return undefined; - return formatTransaction(routeQueryResponse.route.tx); - }, [routeQueryResponse]); + if (!routeQueryResponse || routeQueryResponse.status === 'error' || !routeQueryResponse.route || !walletClient) + return undefined; + return formatTransaction(routeQueryResponse.route.tx, walletClient?.chain); + }, [routeQueryResponse, walletClient]); const executeRoute = useCallback(async () => { if (!preparedTransaction) throw new Error('No route execution transaction available'); @@ -69,7 +70,7 @@ export const useExecutePosition = (args?: UseExecutePositionArgs): UseExecuteSho const executeApprovalsOrTransfers = useCallback(() => { const transactionFuncs = routeQueryResponse?.approvals?.map( - (approvalData: any) => walletClient?.sendTransaction(formatTransaction(approvalData.tx)), + (approvalData) => walletClient?.sendTransaction(formatTransaction(approvalData.tx, walletClient.chain)), ); if (!transactionFuncs) return; @@ -84,19 +85,19 @@ export const useExecutePosition = (args?: UseExecutePositionArgs): UseExecuteSho ...routeQueryResponse.route, execute: executeRoute, }, - approvals: routeQueryResponse.approvals?.map((approval: any) => ({ + approvals: routeQueryResponse.approvals?.map((approval) => ({ token: approval.token as `0x${string}`, amount: approval.amount, gas: approval.gas, spender: approval.spender, - execute: async () => walletClient?.sendTransaction(formatTransaction(approval.tx)), + execute: async () => walletClient?.sendTransaction(formatTransaction(approval.tx, walletClient.chain)), })), - transfers: routeQueryResponse.transfers?.map((transfer: any) => ({ + transfers: routeQueryResponse.transfers?.map((transfer) => ({ token: transfer.token as `0x${string}`, amount: transfer.amount, gas: transfer.gas, spender: transfer.spender, - execute: async () => walletClient?.sendTransaction(formatTransaction(transfer.tx)), + execute: async () => walletClient?.sendTransaction(formatTransaction(transfer.tx, walletClient.chain)), })), }; } diff --git a/use-defi/src/hooks/usePositions/index.ts b/use-defi/src/hooks/usePositions/index.ts index 3f1cff2..f6fbfa0 100644 --- a/use-defi/src/hooks/usePositions/index.ts +++ b/use-defi/src/hooks/usePositions/index.ts @@ -41,7 +41,7 @@ export const usePositions = (args: UsePositionsArgs): UsePositionsPayload => { } if (data) { - return data.filter((metaPosition: any) => filters.every((filter) => filter(metaPosition))); + return data.filter((metaPosition: Position) => filters.every((filter) => filter(metaPosition))); } return []; diff --git a/use-defi/src/utils/fetch.ts b/use-defi/src/utils/fetch.ts new file mode 100644 index 0000000..717ea74 --- /dev/null +++ b/use-defi/src/utils/fetch.ts @@ -0,0 +1,35 @@ +import { URLSearchParams } from 'url'; + +import { ENSO_API } from '../constants'; + +import { parseApiErrorOrReturn } from './parseApiError'; + +export const apiFetchGet = async (apiPath: string, qsParams: Record): Promise => { + const response = await fetch(`${ENSO_API}/${apiPath}?${new URLSearchParams(qsParams).toString()}`); + const data = await response.json(); + + return parseApiErrorOrReturn(data as T); +}; + +export const apiFetchPost = async ( + apiPath: string, + body?: Record, + qsParams?: Record, + apiKey?: string, +): Promise => { + const headers = new Headers(); + headers.set('Content-Type', 'application/json'); + + if (apiKey) { + headers.set('Authorization', `Bearer ${apiKey}`); + } + + const response = await fetch(`${ENSO_API}/${apiPath}?${new URLSearchParams(qsParams).toString()}`, { + method: 'POST', + headers, + body: body ? JSON.stringify(body) : undefined, + }); + const data = await response.json(); + + return parseApiErrorOrReturn(data as T); +}; diff --git a/use-defi/src/utils/formatTransaction.ts b/use-defi/src/utils/formatTransaction.ts index 701e609..0672e2d 100644 --- a/use-defi/src/utils/formatTransaction.ts +++ b/use-defi/src/utils/formatTransaction.ts @@ -1,21 +1,19 @@ -import { Address, Hash, TransactionRequestBase } from 'viem'; +import { Account, Hash, SendTransactionParameters } from 'viem'; +import { Chain } from 'wagmi'; import { Transaction as API_Transaction } from '../types/api'; -type BaseTransaction = Omit & { - from?: Address; -}; - -export const formatTransaction = (txLike: API_Transaction): BaseTransaction => { - const tx: BaseTransaction = { +export const formatTransaction = ( + txLike: API_Transaction, + chain: Chain, +): SendTransactionParameters => { + const tx: SendTransactionParameters = { + chain: chain, to: txLike.to as Hash, data: txLike.data as Hash, value: txLike.value ? BigInt(txLike.value) : BigInt(0), + account: txLike.from, }; - if (txLike.from) { - tx.from = txLike.from; - } - return tx; }; diff --git a/use-defi/src/utils/parseApiError.ts b/use-defi/src/utils/parseApiError.ts index 5b2a2ea..1125335 100644 --- a/use-defi/src/utils/parseApiError.ts +++ b/use-defi/src/utils/parseApiError.ts @@ -1,6 +1,6 @@ import { API_Error, API_Response } from '../types/enso'; -export function parseApiErrorOrReturn(apiResponse: API_Response) { +export function parseApiErrorOrReturn(apiResponse: API_Response): T { const apiError = apiResponse as API_Error; if (apiError.message) { throw new Error(`Invalid Response from Enso API: ${apiError.error ?? apiError.message} (${apiError.statusCode})`); diff --git a/use-defi/tsconfig.json b/use-defi/tsconfig.json index 0aa78fe..56a6e12 100644 --- a/use-defi/tsconfig.json +++ b/use-defi/tsconfig.json @@ -1,44 +1,7 @@ { - "compilerOptions": { - "target": "ES2017", - "module": "ESNext", - "lib": [ - "ESNext", - "DOM", - "DOM.Iterable" - ], - "moduleResolution": "node", - "esModuleInterop": true, - "strict": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "declaration": true, - "resolveJsonModule": true, - "rootDir": "./", - "baseUrl": "./", - "jsx": "preserve", - "skipLibCheck": true, - "noUnusedLocals": true, - "emitDeclarationOnly": true, - "allowJs": true, - "forceConsistentCasingInFileNames": true, - "isolatedModules": true, - "outDir": "dist", - "types": [ - "vitest/globals" - ], - "paths": { - "@ensofinance/use-defi": [ - "./src" - ] - } - }, + "extends": "../tsconfig.json", + "compilerOptions": {}, "include": [ - "src", - "test" - ], - "exclude": [ - "node_modules", - "**/dist", + "**/*.ts" ] } \ No newline at end of file