├── .eslintrc.json ├── .github └── workflows │ └── build-test.yml ├── .gitignore ├── .prettierrc.json ├── README.md ├── examples ├── 1_usdc_poly_to_bsc.ts ├── 2_usdc_bsc_to_poly.ts ├── 3_matic_to_bnb.ts ├── 4_bnb_to_matic.ts ├── 5_usdc_poly_to_eth_polygon_bridge.ts ├── 6_usdc_xdai_to_poly.ts ├── 7_matic_to_xdai.ts ├── continue_active_route.ts └── exampleRunner.ts ├── jest.config.js ├── package.json ├── spec.json ├── src ├── baseSocket.ts ├── chain.test.ts ├── chain.ts ├── client │ ├── core │ │ ├── ApiError.ts │ │ ├── ApiRequestOptions.ts │ │ ├── ApiResult.ts │ │ ├── CancelablePromise.ts │ │ ├── OpenAPI.ts │ │ └── request.ts │ ├── index.ts │ ├── models │ │ ├── ActiveRouteOutputDTO.ts │ │ ├── ActiveRouteResponse.ts │ │ ├── ActiveRoutesOutputDTO.ts │ │ ├── ActiveRoutesRequest.ts │ │ ├── ApprovalData.ts │ │ ├── ApprovalOutputDTO.ts │ │ ├── ApprovalTxOutputDTO.ts │ │ ├── Balance.ts │ │ ├── BalanceResult.ts │ │ ├── BridgeDetails.ts │ │ ├── BridgeInsuranceData.ts │ │ ├── BridgeRouteErrors.ts │ │ ├── BridgeStatusResponse.ts │ │ ├── BridgeStatusResponseDTO.ts │ │ ├── ChainDetails.ts │ │ ├── ChainGasBalances.ts │ │ ├── ChainId.ts │ │ ├── Dexes.ts │ │ ├── ExtraData.ts │ │ ├── GasFee.ts │ │ ├── GasPriceResponseDTO.ts │ │ ├── GasTokenDetails.ts │ │ ├── HealthResponseDTO.ts │ │ ├── InsuranceFee.ts │ │ ├── MinGasBalances.ts │ │ ├── NextTxOutputDTO.ts │ │ ├── NextTxResponse.ts │ │ ├── OpRebateData.ts │ │ ├── QuoteOutputDTO.ts │ │ ├── QuoteRequest.ts │ │ ├── RefuelData.ts │ │ ├── RefuelStatusResponse.ts │ │ ├── RewardData.ts │ │ ├── Route.ts │ │ ├── RouteStatusOutputDTO.ts │ │ ├── SingleTxDTO.ts │ │ ├── SingleTxOutputDTO.ts │ │ ├── SingleTxResponse.ts │ │ ├── SocketPreferences.ts │ │ ├── SocketRoute.ts │ │ ├── StartActiveRouteInputDTO.ts │ │ ├── SupportedBridgesOutputDTO.ts │ │ ├── SupportedChainsOutputDTO.ts │ │ ├── Token.ts │ │ ├── TokenBalanceReponseDTO.ts │ │ ├── TokenListOutputDTO.ts │ │ ├── TokenListRequest.ts │ │ ├── TokenPriceResponseDTO.ts │ │ ├── TransactionReceiptResponseDTO.ts │ │ ├── TxStatus.ts │ │ ├── TxType.ts │ │ ├── UserTx.ts │ │ └── UserTxType.ts │ └── services │ │ ├── Approvals.ts │ │ ├── Balances.ts │ │ ├── Quote.ts │ │ ├── Routes.ts │ │ ├── Server.ts │ │ ├── Supported.ts │ │ └── TokenLists.ts ├── constants.ts ├── index.ts ├── mocks │ ├── mockActiveRoute.json │ ├── mockChainDetails.json │ ├── mockRoute.json │ └── mockTokenList.json ├── path.test.ts ├── path.ts ├── socket.test.ts ├── socket.ts ├── socketTx.test.ts ├── socketTx.ts ├── tokenList.test.ts ├── tokenList.ts ├── types.ts ├── utils.ts └── web3ConnectedSocket.ts ├── tsconfig.json └── yarn.lock /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "parser": "@typescript-eslint/parser", 7 | "parserOptions": { 8 | "project": "tsconfig.json", 9 | "sourceType": "module" 10 | }, 11 | "plugins": ["@typescript-eslint", "jest"], 12 | "extends": [ 13 | "eslint:recommended", 14 | "plugin:@typescript-eslint/eslint-recommended", 15 | "plugin:@typescript-eslint/recommended", 16 | "plugin:jest/recommended", 17 | "prettier" 18 | ], 19 | "ignorePatterns": ["lib/", "examples/"], 20 | "rules": { 21 | "@typescript-eslint/no-explicit-any": "off" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: Build Test 2 | on: push 3 | 4 | jobs: 5 | build_test: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - uses: actions/setup-node@v3 10 | with: 11 | node-version: 14 12 | - run: yarn 13 | - run: yarn lint 14 | - run: yarn build 15 | - run: yarn test 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | lib/ 3 | .vscode 4 | .npm 5 | .eslintcache 6 | .DS_Store 7 | coverage 8 | .env 9 | *.tgz -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "overrides": [ 4 | { 5 | "files": "*.ts", 6 | "options": { 7 | "parser": "typescript" 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository is deprecated and was archived 2 | # Please see [docs.bungee.exchange](https://docs.bungee.exchange/) for the up to date API 3 | 4 | 5 | # Socket v2 SDK 6 | 7 | -> [Docs](https://socketdottech.github.io/socket-v2-sdk-docs/) <- 8 | 9 | ## Install 10 | 11 | - `yarn add @socket.tech/socket-v2-sdk` 12 | 13 | or 14 | 15 | - `npm i @socket.tech/socket-v2-sdk` 16 | 17 | ## Usage 18 | 19 | In summary: 20 | 21 | - Initialise the sdk: 22 | ```ts 23 | const socket = new Socket({ apiKey: API_KEY }); 24 | ``` 25 | - Retrieve the token lists 26 | 27 | ```ts 28 | const tokenList = await socket.getTokenList({ 29 | fromChainId: 1, 30 | toChainId: 137, 31 | }); 32 | 33 | // tokenList.from has list of from tokens 34 | // tokenList.to has list of to tokens 35 | ``` 36 | 37 | - Create a path 38 | ```ts 39 | const path = new Path({ fromToken, toToken }); 40 | ``` 41 | - Get quote 42 | ```ts 43 | const quote = await socket.getBestQuote({ 44 | path, 45 | amount, 46 | address, 47 | }, { ... Any quote preferences here }) 48 | ``` 49 | - You have 2 options for executing a quote. Managing the steps yourself or connecting a web3 provider. 50 | 51 | - Connecting web3 provider: 52 | ```ts 53 | const provider = new ethers.providers.Web3Provider(window.ethereum); // Or use wallet provider like onboard, web3modal, web3react etc. 54 | const connectedSocket = socket.connect(provider); 55 | await connectedSocket.start(quote, { 56 | onTx: (tx) => { 57 | console.log('Executing transaction', tx); 58 | return (tx) => { 59 | console.log('Done transaction', tx); 60 | } 61 | } 62 | ... // Other callbacks 63 | }); 64 | ``` 65 | - Handle the steps manually 66 | 67 | ```ts 68 | const execute = await socket.start(quote); 69 | let next = await execute.next(); 70 | 71 | while (!next.done && next.value) { 72 | const tx = next.value; 73 | const approvalTxData = await tx.getApproveTransaction(); 74 | // ... if there is approval send the approve and wait 75 | 76 | const sendTxData = await tx.getSendTransaction(); 77 | // ... send the tx and execute next 78 | 79 | next = await execute.next(sendTx.hash); 80 | } 81 | ``` 82 | 83 | ### Direct api communication 84 | 85 | All api functions are available through the typescript client 86 | 87 | ```ts 88 | const socket = new Socket(API_KEY); 89 | const activeRoute = await socket.client.routes.getActiveRoute({ activeRouteId: 1234 }); 90 | ``` 91 | 92 | ## Test 93 | 94 | - USDC Polygon to BSC 95 | `PRIVATE_KEY="" npx ts-node examples/1_usdc_poly_to_bsc.ts` 96 | 97 | Other examples [here](/examples/) 98 | -------------------------------------------------------------------------------- /examples/1_usdc_poly_to_bsc.ts: -------------------------------------------------------------------------------- 1 | import { runRoute } from "./exampleRunner"; 2 | 3 | (async () => { 4 | await runRoute({ 5 | fromAmount: "15", 6 | fromChainId: 137, 7 | toChainId: 56, 8 | fromTokenAddress: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", 9 | toTokenAddress: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", 10 | }); 11 | })(); 12 | -------------------------------------------------------------------------------- /examples/2_usdc_bsc_to_poly.ts: -------------------------------------------------------------------------------- 1 | import { runRoute } from "./exampleRunner"; 2 | 3 | (async () => { 4 | await runRoute({ 5 | fromAmount: "14.1", 6 | fromChainId: 56, 7 | toChainId: 137, 8 | fromTokenAddress: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d", 9 | toTokenAddress: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", 10 | }); 11 | })(); 12 | -------------------------------------------------------------------------------- /examples/3_matic_to_bnb.ts: -------------------------------------------------------------------------------- 1 | import { NATIVE_TOKEN_ADDRESS } from "../src/constants"; 2 | import { runRoute } from "./exampleRunner"; 3 | 4 | (async () => { 5 | await runRoute({ 6 | fromAmount: "20", 7 | fromChainId: 137, 8 | toChainId: 56, 9 | fromTokenAddress: NATIVE_TOKEN_ADDRESS, 10 | toTokenAddress: NATIVE_TOKEN_ADDRESS, 11 | multiTx: true, 12 | }); 13 | })(); 14 | -------------------------------------------------------------------------------- /examples/4_bnb_to_matic.ts: -------------------------------------------------------------------------------- 1 | import { NATIVE_TOKEN_ADDRESS } from "../src/constants"; 2 | import { runRoute } from "./exampleRunner"; 3 | 4 | (async () => { 5 | await runRoute({ 6 | fromAmount: "10", 7 | fromChainId: 1, 8 | toChainId: 1313161554, 9 | fromTokenAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", 10 | toTokenAddress: NATIVE_TOKEN_ADDRESS, 11 | multiTx: true, 12 | feeTakerAddress: "0xF75aAa99e6877fA62375C37c343c51606488cd08", 13 | feePercent: "5", 14 | bridgeWithGas: true, 15 | }); 16 | })(); 17 | -------------------------------------------------------------------------------- /examples/5_usdc_poly_to_eth_polygon_bridge.ts: -------------------------------------------------------------------------------- 1 | import { BridgeName } from "../src/client/models/BridgeDetails"; 2 | import { runRoute } from "./exampleRunner"; 3 | 4 | (async () => { 5 | await runRoute({ 6 | fromAmount: "1", 7 | fromChainId: 137, 8 | toChainId: 1, 9 | fromTokenAddress: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", 10 | toTokenAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", 11 | bridge: BridgeName.PolygonBridge, 12 | multiTx: true, 13 | }); 14 | })(); 15 | -------------------------------------------------------------------------------- /examples/6_usdc_xdai_to_poly.ts: -------------------------------------------------------------------------------- 1 | import { runRoute } from "./exampleRunner"; 2 | 3 | (async () => { 4 | await runRoute({ 5 | fromAmount: "12", 6 | fromChainId: 100, 7 | toChainId: 137, 8 | fromTokenAddress: "0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83", 9 | toTokenAddress: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174", 10 | }); 11 | })(); 12 | -------------------------------------------------------------------------------- /examples/7_matic_to_xdai.ts: -------------------------------------------------------------------------------- 1 | import { NATIVE_TOKEN_ADDRESS } from "../src/constants"; 2 | import { runRoute } from "./exampleRunner"; 3 | 4 | (async () => { 5 | await runRoute({ 6 | fromAmount: "5", 7 | fromChainId: 137, 8 | toChainId: 100, 9 | fromTokenAddress: NATIVE_TOKEN_ADDRESS, 10 | toTokenAddress: NATIVE_TOKEN_ADDRESS, 11 | }); 12 | })(); 13 | -------------------------------------------------------------------------------- /examples/continue_active_route.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "ethers"; 2 | import { Socket } from "../src"; 3 | import { executeRoute } from "./exampleRunner"; 4 | 5 | const API_KEY = "645b2c8c-5825-4930-baf3-d9b997fcd88c"; // Testing key 6 | 7 | const wallet = process.env.PRIVATE_KEY 8 | ? new ethers.Wallet(process.env.PRIVATE_KEY) 9 | : ethers.Wallet.createRandom(); 10 | 11 | const socket = new Socket({ apiKey: API_KEY }); 12 | 13 | (async () => { 14 | const userAddress = await wallet.getAddress(); 15 | // Cotninue latest active route 16 | const routes = await socket.getActiveRoutes({ userAddress }); 17 | const route = routes.activeRoutes[0]; 18 | const execute = await socket.continue(route.activeRouteId); 19 | await executeRoute(execute); 20 | })(); 21 | -------------------------------------------------------------------------------- /examples/exampleRunner.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Server, Socket } from "../src"; 3 | import * as ethers from "ethers"; 4 | import { Path } from "../src/path"; 5 | // import { BridgeName } from "../src/client/models/BridgeDetails"; 6 | import { ChainId } from "../src/client/models/ChainId"; 7 | import { SocketTx } from "../src/socketTx"; 8 | // import { Bridge } from "@socket.tech/ll-core"; 9 | 10 | const API_KEY = "72a5b4b0-e727-48be-8aa1-5da9d62fe635"; // Testing key 11 | 12 | const wallet = process.env.PRIVATE_KEY 13 | ? new ethers.Wallet(process.env.PRIVATE_KEY) 14 | : ethers.Wallet.createRandom(); 15 | 16 | // Polygon ethers fee data is broken 17 | async function getPolygonFeeData() { 18 | const gas: { 19 | standard: { 20 | maxPriorityFee: number; 21 | maxFee: number; 22 | }; 23 | } = (await axios.get("https://gasstation-mainnet.matic.network/v2")).data; 24 | 25 | return { 26 | maxPriorityFeePerGas: ethers.utils.parseUnits( 27 | Math.ceil(gas.standard.maxPriorityFee).toString(), 28 | "gwei" 29 | ), 30 | maxFeePerGas: ethers.utils.parseUnits(Math.ceil(gas.standard.maxFee).toString(), "gwei"), 31 | }; 32 | } 33 | 34 | const chainProviders: { [index: number]: ethers.providers.JsonRpcProvider } = { 35 | 100: new ethers.providers.JsonRpcProvider("https://gnosis-mainnet.public.blastapi.io"), 36 | 137: new ethers.providers.JsonRpcProvider("https://polygon-rpc.com"), 37 | 56: new ethers.providers.JsonRpcProvider("https://bsc-dataseed4.binance.org"), 38 | }; 39 | 40 | export async function runRoute({ 41 | fromAmount, 42 | fromChainId, 43 | toChainId, 44 | fromTokenAddress, 45 | toTokenAddress, 46 | // bridge, 47 | multiTx = false, 48 | feeTakerAddress, 49 | feePercent, 50 | bridgeWithGas = false, 51 | }: { 52 | fromAmount: string; 53 | fromChainId: ChainId; 54 | toChainId: ChainId; 55 | fromTokenAddress: string; 56 | toTokenAddress: string; 57 | // bridge?: BridgeName; 58 | multiTx?: boolean; 59 | feeTakerAddress?: string; 60 | feePercent?: string; 61 | bridgeWithGas?: boolean 62 | }) { 63 | const socket = new Socket({ 64 | apiKey: API_KEY, 65 | defaultQuotePreferences: { 66 | singleTxOnly: !multiTx, 67 | }, 68 | }); 69 | 70 | const userAddress = await wallet.getAddress(); 71 | 72 | const tokenList = await socket.getTokenList({ 73 | fromChainId: fromChainId, 74 | toChainId: toChainId, 75 | }); 76 | 77 | const fromToken = tokenList.from.tokenByAddress(fromTokenAddress); 78 | const toToken = tokenList.to.tokenByAddress(toTokenAddress); 79 | 80 | const path = new Path({ fromToken, toToken }); 81 | if (!fromToken.decimals) { 82 | throw new Error("danger! from token has no decimals!"); 83 | } 84 | const amount = ethers.utils.parseUnits(fromAmount, fromToken.decimals).toString(); 85 | // const prefs = bridge ? { includeBridges: [bridge] } : undefined; 86 | const quote = await socket.getBestQuote( 87 | { 88 | path: path, 89 | amount, 90 | address: userAddress, 91 | }, 92 | { 93 | feePercent: feePercent, 94 | feeTakerAddress: feeTakerAddress, 95 | bridgeWithGas, 96 | singleTxOnly: true, 97 | // @ts-ignore 98 | excludeBridges: ['synapse', 'across'] 99 | } 100 | ); 101 | 102 | if (!quote) { 103 | throw new Error("no quote available"); 104 | } 105 | 106 | console.log('quote', quote); 107 | const execute = await Server.getSingleTx({requestBody: {route: quote?.route, refuel: quote?.refuel}}); 108 | console.log('execute', execute); 109 | // const execute = await socket.start(quote); 110 | // await executeRoute(execute); 111 | } 112 | 113 | export async function executeRoute(execute: AsyncGenerator) { 114 | let next = await execute.next(); 115 | 116 | while (!next.done && next.value) { 117 | const tx = next.value; 118 | console.log(`Executing step ${tx.userTxIndex} "${tx.userTxType}" on chain ${tx.chainId}`); 119 | const provider = chainProviders[tx.chainId]; 120 | const approvalTxData = await tx.getApproveTransaction(); 121 | if (approvalTxData) { 122 | const feeData = tx.chainId === 137 ? await getPolygonFeeData() : {}; 123 | const approvalTx = await wallet.connect(provider).sendTransaction({ 124 | ...approvalTxData, 125 | ...feeData, 126 | }); 127 | console.log(`Approving: ${approvalTx.hash}`); 128 | await approvalTx.wait(); 129 | } 130 | const sendTxData = await tx.getSendTransaction(); 131 | const feeData = tx.chainId === 137 ? await getPolygonFeeData() : {}; 132 | const sendTx = await wallet.connect(provider).sendTransaction({ 133 | ...sendTxData, 134 | ...feeData, 135 | }); 136 | console.log(`Sending: ${sendTx.hash}`); 137 | await sendTx.wait(); 138 | next = await execute.next(sendTx.hash); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | "^.+\\.tsx?$": "ts-jest", 4 | }, 5 | testEnvironment: "node", 6 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], 7 | testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(ts|js)x?$", 8 | testPathIgnorePatterns: ["/node_modules/", "lib"], 9 | }; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@socket.tech/socket-v2-sdk", 3 | "version": "1.24.2", 4 | "module": "lib/src/index.js", 5 | "main": "lib/src/index.js", 6 | "types": "lib/src/index.d.ts", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "directories": { 11 | "lib": "lib", 12 | "src": "src" 13 | }, 14 | "files": [ 15 | "lib", 16 | "src" 17 | ], 18 | "repository": "git@github.com:SocketDotTech/socket-v2-sdk.git", 19 | "author": "rugamoto ", 20 | "license": "MIT", 21 | "scripts": { 22 | "build": "tsc", 23 | "lint": "eslint . --ext .ts", 24 | "test": "jest", 25 | "generate": "npx openapi-typescript-codegen --input ./spec.json --output ./src/client --indent 2 --postfix \"\" --client axios --useOptions && prettier --write ./src/client-generated", 26 | "generate:docs": "typedoc --excludeProtected --excludePrivate --plugin typedoc-plugin-markdown --out docs src/index.ts" 27 | }, 28 | "devDependencies": { 29 | "@types/jest": "^28.1.1", 30 | "@typescript-eslint/eslint-plugin": "^5.22.0", 31 | "@typescript-eslint/parser": "^5.22.0", 32 | "eslint": "^8.14.0", 33 | "eslint-config-prettier": "^8.5.0", 34 | "eslint-plugin-jest": "^26.1.5", 35 | "jest": "^28.1.0", 36 | "prettier": "^2.6.2", 37 | "ts-jest": "^28.0.4", 38 | "typedoc": "^0.22.17", 39 | "typedoc-plugin-markdown": "^3.12.1", 40 | "typescript": "^4.6.4" 41 | }, 42 | "dependencies": { 43 | "@socket.tech/ll-core": "^0.1.49", 44 | "@socket.tech/ll-core-v2": "^0.0.66", 45 | "axios": "^0.27.2", 46 | "ethers": "^5.6.5", 47 | "form-data": "^4.0.0", 48 | "tslib": "^2.4.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/baseSocket.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from "./chain"; 2 | import { 3 | Approvals, 4 | Balances, 5 | BridgeRouteErrors, 6 | NextTxResponse, 7 | OpenAPI, 8 | Quotes, 9 | Routes, 10 | Server, 11 | SortOptions, 12 | Supported, 13 | TokenLists, 14 | } from "./client"; 15 | import { ActiveRouteStatus, ActiveRouteResponse } from "./client/models/ActiveRouteResponse"; 16 | import { QuotePreferences } from "./client/models/QuoteRequest"; 17 | import { SocketTx } from "./socketTx"; 18 | import { TokenList } from "./tokenList"; 19 | import { QuoteParams, SocketOptions, SocketQuote } from "./types"; 20 | import { Web3Provider } from "@ethersproject/providers"; 21 | import { ChainId } from "@socket.tech/ll-core"; 22 | import { ActiveRoutesRequest } from "./client/models/ActiveRoutesRequest"; 23 | import { SocketPreferences } from "./client/models/SocketPreferences"; 24 | import { TokenListRequest } from "./client/models/TokenListRequest"; 25 | import { SocketRoute } from "./client/models/SocketRoute"; 26 | 27 | export interface ActiveRouteGenerator extends AsyncGenerator { 28 | /** Active Route Id */ 29 | activeRouteId: number; 30 | } 31 | 32 | /** 33 | * The Socket represents the socket sdk. This is the starting point for interacting 34 | * with the socket SDK. It allows you to retrieve routes and start the execution of trades based on quotes 35 | * 36 | * It includes direct access to the socket api. 37 | */ 38 | export abstract class BaseSocket { 39 | /** 40 | * The api options 41 | */ 42 | protected _options: SocketOptions; 43 | 44 | /** 45 | * Cached instance of all chain details 46 | */ 47 | protected _chainsCache: Chain[] | undefined; 48 | 49 | /** 50 | * The provider to use for executing routes 51 | */ 52 | protected _provider: Web3Provider | undefined; 53 | 54 | /** 55 | * API client for accessing the socket api directly 56 | */ 57 | client = { 58 | routes: Routes, 59 | balances: Balances, 60 | approvals: Approvals, 61 | server: Server, 62 | quotes: Quotes, 63 | supported: Supported, 64 | tokenLists: TokenLists, 65 | }; 66 | 67 | /** 68 | * 69 | * @param options Socket sdk options 70 | */ 71 | constructor(options: SocketOptions) { 72 | this._options = options; 73 | OpenAPI.API_KEY = this._options.apiKey; 74 | OpenAPI.BASE = this._options.baseUrl ?? OpenAPI.BASE; 75 | } 76 | 77 | /** 78 | * Get all supported chains 79 | * @returns List of chains 80 | */ 81 | async getChains() { 82 | if (this._chainsCache) return this._chainsCache; 83 | 84 | const supportedChains = await Supported.getAllSupportedChains(); 85 | 86 | this._chainsCache = supportedChains.result.map((chain) => new Chain(chain)); 87 | 88 | return this._chainsCache; 89 | } 90 | 91 | /** 92 | * Get a chain by id 93 | * @param chainId The numeric id of the chain 94 | * @returns The requested chain 95 | */ 96 | async getChain(chainId: ChainId) { 97 | const chains = await this.getChains(); 98 | const chain = chains.find((c) => c.chainId === chainId); 99 | if (!chain) { 100 | throw new Error("Chain not supported"); 101 | } 102 | 103 | return chain; 104 | } 105 | 106 | /** 107 | * get Balances for a user address 108 | * @param userAddress The user address 109 | */ 110 | async getBalances({ userAddress }) { 111 | return await Balances.getBalances({ userAddress }); 112 | } 113 | 114 | /** 115 | * get Balance for a user address 116 | * @param tokenAddress The token address 117 | * @param chainId The chain id 118 | * @param userAddress The user address 119 | * @returns The balance 120 | */ 121 | 122 | async getBalance({ tokenAddress, chainId, userAddress }) { 123 | return await Balances.getBalance({ 124 | tokenAddress, 125 | chainId, 126 | userAddress, 127 | }); 128 | } 129 | 130 | /** 131 | * Get the list of tokens available for each chain for a given path 132 | * @param options 133 | * @param options.fromChainId The source chain id e.g. 0x1 134 | * @param options.toChainId The destination chain id e.g. 0x56 135 | * 136 | * @returns The `from` and `to` token lists 137 | */ 138 | async getTokenList(request: TokenListRequest) { 139 | const fromTokenListData = ( 140 | await TokenLists.getFromTokenList({ 141 | isShortList: true, 142 | ...request, 143 | }) 144 | ).result; 145 | const toTokenListData = ( 146 | await TokenLists.getToTokenList({ 147 | isShortList: true, 148 | ...request, 149 | }) 150 | ).result; 151 | 152 | const from = new TokenList(request.fromChainId, fromTokenListData); 153 | const to = new TokenList(request.toChainId, toTokenListData); 154 | 155 | return { from, to }; 156 | } 157 | 158 | /** 159 | * Checks that the preferences desired are valid 160 | * @param preferences The socket preferences 161 | */ 162 | private validatePreferences(preferences: SocketPreferences) { 163 | if (preferences.includeBridges && preferences.excludeBridges) { 164 | throw new Error("Only one of `includeBridges` or `excludeBridges` can be specified."); 165 | } 166 | 167 | if (preferences.includeDexes && preferences.excludeDexes) { 168 | throw new Error("Only one of `includeDexes` or `excludeDexes` can be specified."); 169 | } 170 | } 171 | 172 | /** 173 | * Get the best quote 174 | * @param params The parameters of the quote 175 | * @param preferences Additional route preferences for retrieving the quote from the api 176 | * @returns The best quote if found or null 177 | */ 178 | async getBestQuote(params: QuoteParams, preferences?: QuotePreferences) { 179 | const { routes } = await this.getAllQuotes(params, preferences); 180 | // API returns the 'sort by time' in descending order of service time, hence reversing the order 181 | // To be removed once API response is fixed 182 | if (preferences?.sort === SortOptions.Time) { 183 | return routes ? routes.reverse()[0] : null; 184 | } else return routes ? routes[0] : null; 185 | } 186 | 187 | /** 188 | * Get All quotes 189 | * @param params The parameters of the quote 190 | * @param preferences Additional route preferences for retrieving the quote from the api 191 | * @returns All quotes found 192 | */ 193 | async getAllQuotes( 194 | { path, address, amount }: QuoteParams, 195 | preferences?: QuotePreferences 196 | ): Promise<{ 197 | routes: SocketQuote[]; 198 | bridgeRouteErrors: BridgeRouteErrors; 199 | socketRoute: SocketRoute; 200 | }> { 201 | const finalPreferences = { 202 | ...(this._options.defaultQuotePreferences || {}), 203 | ...(preferences || {}), 204 | }; 205 | this.validatePreferences(finalPreferences); 206 | 207 | const request = { 208 | fromChainId: path.fromToken.chainId, 209 | toChainId: path.toToken.chainId, 210 | fromTokenAddress: path.fromToken.address, 211 | toTokenAddress: path.toToken.address, 212 | fromAmount: amount, 213 | userAddress: address, 214 | recipient: address, 215 | ...finalPreferences, 216 | }; 217 | 218 | const quote = (await Quotes.getQuote(request)).result; 219 | return { 220 | routes: 221 | quote.routes?.map((route) => ({ 222 | route, 223 | path, 224 | address, 225 | amount, 226 | refuel: quote.refuel, 227 | errors: quote.bridgeRouteErrors, 228 | })) || [], 229 | bridgeRouteErrors: quote.bridgeRouteErrors, 230 | socketRoute: quote.socketRoute, 231 | }; 232 | } 233 | 234 | /** 235 | * Retrieve the active routes. Active routes can be used to continue a quote 236 | * @param options Criteria for returning active routes. Commonly `address` is most useful 237 | * @returns list of active routes 238 | */ 239 | async getActiveRoutes(options: ActiveRoutesRequest) { 240 | const routes = await Routes.getActiveRoutesForUser(options); 241 | return routes.result; 242 | } 243 | 244 | /** 245 | * Asserts that the transaction object has been marked done 246 | * @param socketTx The socket transaction 247 | */ 248 | private _assertTxDone(socketTx?: SocketTx) { 249 | if (socketTx && !socketTx.done) { 250 | throw new Error( 251 | `Previous transaction ${socketTx.userTxIndex}: ${socketTx.userTxType} has not been submitted.` 252 | ); 253 | } 254 | } 255 | 256 | /** 257 | * Returns a generator that yields each transaction for a route in sequence 258 | * @param initialTx The first transaction to execute 259 | * @param activeRoute The active route object if this executor is for an active route 260 | */ 261 | private async *_executor( 262 | initialTx: NextTxResponse, 263 | activeRoute?: ActiveRouteResponse 264 | ): AsyncGenerator { 265 | let nextTx: NextTxResponse = initialTx; 266 | let prevSocketTx: SocketTx | undefined; 267 | 268 | do { 269 | if (prevSocketTx) { 270 | this._assertTxDone(prevSocketTx); 271 | nextTx = (await Routes.nextTx({ activeRouteId: initialTx.activeRouteId })).result; 272 | } 273 | const currentSocketTx = new SocketTx(nextTx, this._options.statusCheckInterval); 274 | let hash = activeRoute?.userTxs[currentSocketTx.userTxIndex].sourceTransactionHash; 275 | if (!hash) { 276 | hash = yield currentSocketTx; 277 | if (!hash) { 278 | throw new Error(`A hash must be provided to \`next\``); 279 | } 280 | } 281 | await currentSocketTx.submit(hash); 282 | prevSocketTx = currentSocketTx; 283 | } while (nextTx.userTxIndex + 1 < nextTx.totalUserTx); 284 | } 285 | 286 | /** 287 | * Start executing a socket quote/route. 288 | * @param quote 289 | * @returns An iterator that will yield each transaction required in the route 290 | */ 291 | protected async _startQuote(quote: SocketQuote): Promise { 292 | const routeStart = ( 293 | await Routes.startActiveRoute({ 294 | startRequest: { 295 | route: quote.route, 296 | refuel: quote.refuel, 297 | fromChainId: quote.path.fromToken.chainId, 298 | toChainId: quote.path.toToken.chainId, 299 | fromAssetAddress: quote.path.fromToken.address, 300 | toAssetAddress: quote.path.toToken.address, 301 | includeFirstTxDetails: true, 302 | }, 303 | }) 304 | ).result; 305 | 306 | return { activeRouteId: routeStart.activeRouteId, ...this._executor(routeStart) }; 307 | } 308 | 309 | /** 310 | * Continue an active route 311 | * @param activeRouteId The active route id of the desired route to continue 312 | * @returns An iterator that will yield each transaction required in the route 313 | */ 314 | protected async _continueRoute(activeRouteId: number): Promise { 315 | const activeRoute = (await Routes.getActiveRoute({ activeRouteId: activeRouteId })).result; 316 | if (activeRoute.routeStatus === ActiveRouteStatus.COMPLETED) { 317 | throw new Error(`Route ${activeRoute.activeRouteId} is already complete`); 318 | } 319 | 320 | const tx = (await Routes.nextTx({ activeRouteId: activeRoute.activeRouteId })).result; 321 | return { 322 | activeRouteId: activeRoute.activeRouteId, 323 | ...this._executor(tx, activeRoute), 324 | }; 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/chain.test.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from "./chain"; 2 | import MOCK_CHAIN_DETAILS from "./mocks/mockChainDetails.json"; 3 | 4 | describe("Chain", () => { 5 | it("can get native token", async () => { 6 | const chain = new Chain(MOCK_CHAIN_DETAILS); 7 | expect(chain.chainId).toBe(100); 8 | const nativeToken = chain.nativeToken; 9 | expect(nativeToken.chainId).toBe(100); 10 | expect(nativeToken.address).toBe("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); 11 | expect(nativeToken.symbol).toBe("XDAI"); 12 | expect(nativeToken.decimals).toBe(18); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/chain.ts: -------------------------------------------------------------------------------- 1 | import { ChainDetails, Token } from "."; 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-empty-interface 4 | export interface Chain extends ChainDetails {} 5 | 6 | /** 7 | * The chain object represents a supported chain 8 | */ 9 | export class Chain { 10 | constructor(chainDetails: ChainDetails) { 11 | Object.assign(this, chainDetails); 12 | } 13 | 14 | /** 15 | * The native token of the chain 16 | */ 17 | get nativeToken(): Token { 18 | return { 19 | ...this.currency, 20 | chainId: this.chainId, 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/client/core/ApiError.ts: -------------------------------------------------------------------------------- 1 | import type { ApiResult } from "./ApiResult"; 2 | 3 | export class ApiError extends Error { 4 | public readonly url: string; 5 | public readonly status: number; 6 | public readonly statusText: string; 7 | public readonly body: any; 8 | 9 | constructor(response: ApiResult, message: string) { 10 | super(message); 11 | 12 | this.name = "ApiError"; 13 | this.url = response.url; 14 | this.status = response.status; 15 | this.statusText = response.statusText; 16 | this.body = response.body; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/client/core/ApiRequestOptions.ts: -------------------------------------------------------------------------------- 1 | export type ApiRequestOptions = { 2 | readonly method: "GET" | "PUT" | "POST" | "DELETE" | "OPTIONS" | "HEAD" | "PATCH"; 3 | readonly url: string; 4 | readonly path?: Record; 5 | readonly cookies?: Record; 6 | readonly headers?: Record; 7 | readonly query?: Record; 8 | readonly formData?: Record; 9 | readonly body?: any; 10 | readonly mediaType?: string; 11 | readonly responseHeader?: string; 12 | readonly errors?: Record; 13 | }; 14 | -------------------------------------------------------------------------------- /src/client/core/ApiResult.ts: -------------------------------------------------------------------------------- 1 | export type ApiResult = { 2 | readonly url: string; 3 | readonly ok: boolean; 4 | readonly status: number; 5 | readonly statusText: string; 6 | readonly body: any; 7 | }; 8 | -------------------------------------------------------------------------------- /src/client/core/CancelablePromise.ts: -------------------------------------------------------------------------------- 1 | export class CancelError extends Error { 2 | constructor(message: string) { 3 | super(message); 4 | this.name = "CancelError"; 5 | } 6 | 7 | public get isCancelled(): boolean { 8 | return true; 9 | } 10 | } 11 | 12 | export interface OnCancel { 13 | readonly isResolved: boolean; 14 | readonly isRejected: boolean; 15 | readonly isCancelled: boolean; 16 | 17 | (cancelHandler: () => void): void; 18 | } 19 | 20 | export class CancelablePromise implements Promise { 21 | readonly [Symbol.toStringTag]: string; 22 | 23 | private _isResolved: boolean; 24 | private _isRejected: boolean; 25 | private _isCancelled: boolean; 26 | private readonly _cancelHandlers: (() => void)[]; 27 | private readonly _promise: Promise; 28 | private _resolve?: (value: T | PromiseLike) => void; 29 | private _reject?: (reason?: any) => void; 30 | 31 | constructor( 32 | executor: ( 33 | resolve: (value: T | PromiseLike) => void, 34 | reject: (reason?: any) => void, 35 | onCancel: OnCancel 36 | ) => void 37 | ) { 38 | this._isResolved = false; 39 | this._isRejected = false; 40 | this._isCancelled = false; 41 | this._cancelHandlers = []; 42 | this._promise = new Promise((resolve, reject) => { 43 | this._resolve = resolve; 44 | this._reject = reject; 45 | 46 | const onResolve = (value: T | PromiseLike): void => { 47 | if (this._isResolved || this._isRejected || this._isCancelled) { 48 | return; 49 | } 50 | this._isResolved = true; 51 | this._resolve?.(value); 52 | }; 53 | 54 | const onReject = (reason?: any): void => { 55 | if (this._isResolved || this._isRejected || this._isCancelled) { 56 | return; 57 | } 58 | this._isRejected = true; 59 | this._reject?.(reason); 60 | }; 61 | 62 | const onCancel = (cancelHandler: () => void): void => { 63 | if (this._isResolved || this._isRejected || this._isCancelled) { 64 | return; 65 | } 66 | this._cancelHandlers.push(cancelHandler); 67 | }; 68 | 69 | Object.defineProperty(onCancel, "isResolved", { 70 | get: (): boolean => this._isResolved, 71 | }); 72 | 73 | Object.defineProperty(onCancel, "isRejected", { 74 | get: (): boolean => this._isRejected, 75 | }); 76 | 77 | Object.defineProperty(onCancel, "isCancelled", { 78 | get: (): boolean => this._isCancelled, 79 | }); 80 | 81 | return executor(onResolve, onReject, onCancel as OnCancel); 82 | }); 83 | } 84 | 85 | public then( 86 | onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, 87 | onRejected?: ((reason: any) => TResult2 | PromiseLike) | null 88 | ): Promise { 89 | return this._promise.then(onFulfilled, onRejected); 90 | } 91 | 92 | public catch( 93 | onRejected?: ((reason: any) => TResult | PromiseLike) | null 94 | ): Promise { 95 | return this._promise.catch(onRejected); 96 | } 97 | 98 | public finally(onFinally?: (() => void) | null): Promise { 99 | return this._promise.finally(onFinally); 100 | } 101 | 102 | public cancel(): void { 103 | if (this._isResolved || this._isRejected || this._isCancelled) { 104 | return; 105 | } 106 | this._isCancelled = true; 107 | if (this._cancelHandlers.length) { 108 | try { 109 | for (const cancelHandler of this._cancelHandlers) { 110 | cancelHandler(); 111 | } 112 | } catch (error) { 113 | console.warn("Cancellation threw an error", error); 114 | return; 115 | } 116 | } 117 | this._cancelHandlers.length = 0; 118 | this._reject?.(new CancelError("Request aborted")); 119 | } 120 | 121 | public get isCancelled(): boolean { 122 | return this._isCancelled; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/client/core/OpenAPI.ts: -------------------------------------------------------------------------------- 1 | import type { ApiRequestOptions } from "./ApiRequestOptions"; 2 | 3 | type Resolver = (options: ApiRequestOptions) => Promise; 4 | type Headers = Record; 5 | 6 | export type OpenAPIConfig = { 7 | BASE: string; 8 | VERSION: string; 9 | WITH_CREDENTIALS: boolean; 10 | CREDENTIALS: "include" | "omit" | "same-origin"; 11 | TOKEN?: string | Resolver; 12 | API_KEY?: string | Resolver; 13 | USERNAME?: string | Resolver; 14 | PASSWORD?: string | Resolver; 15 | HEADERS?: Headers | Resolver; 16 | ENCODE_PATH?: (path: string) => string; 17 | }; 18 | 19 | export const OpenAPI: OpenAPIConfig = { 20 | BASE: "https://api.socket.tech", 21 | VERSION: "1.0", 22 | WITH_CREDENTIALS: false, 23 | CREDENTIALS: "include", 24 | API_KEY: undefined, 25 | TOKEN: undefined, 26 | USERNAME: undefined, 27 | PASSWORD: undefined, 28 | HEADERS: undefined, 29 | ENCODE_PATH: undefined, 30 | }; 31 | -------------------------------------------------------------------------------- /src/client/core/request.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios"; 2 | import FormData from "form-data"; 3 | 4 | import { ApiError } from "./ApiError"; 5 | import type { ApiRequestOptions } from "./ApiRequestOptions"; 6 | import type { ApiResult } from "./ApiResult"; 7 | import { CancelablePromise } from "./CancelablePromise"; 8 | import type { OnCancel } from "./CancelablePromise"; 9 | import type { OpenAPIConfig } from "./OpenAPI"; 10 | 11 | const isDefined = (value: T | null | undefined): value is Exclude => { 12 | return value !== undefined && value !== null; 13 | }; 14 | 15 | const isString = (value: any): value is string => { 16 | return typeof value === "string"; 17 | }; 18 | 19 | const isStringWithValue = (value: any): value is string => { 20 | return isString(value) && value !== ""; 21 | }; 22 | 23 | const isBlob = (value: any): value is Blob => { 24 | return ( 25 | typeof value === "object" && 26 | typeof value.type === "string" && 27 | typeof value.stream === "function" && 28 | typeof value.arrayBuffer === "function" && 29 | typeof value.constructor === "function" && 30 | typeof value.constructor.name === "string" && 31 | /^(Blob|File)$/.test(value.constructor.name) && 32 | /^(Blob|File)$/.test(value[Symbol.toStringTag]) 33 | ); 34 | }; 35 | 36 | const isSuccess = (status: number): boolean => { 37 | return status >= 200 && status < 300; 38 | }; 39 | 40 | const base64 = (str: string): string => { 41 | try { 42 | return btoa(str); 43 | } catch (err) { 44 | return Buffer.from(str).toString("base64"); 45 | } 46 | }; 47 | 48 | const getQueryString = (params: Record): string => { 49 | const qs: string[] = []; 50 | 51 | const append = (key: string, value: any) => { 52 | qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); 53 | }; 54 | 55 | const process = (key: string, value: any) => { 56 | if (isDefined(value)) { 57 | if (Array.isArray(value)) { 58 | value.forEach((v) => { 59 | process(key, v); 60 | }); 61 | } else if (typeof value === "object") { 62 | Object.entries(value).forEach(([k, v]) => { 63 | process(`${key}[${k}]`, v); 64 | }); 65 | } else { 66 | append(key, value); 67 | } 68 | } 69 | }; 70 | 71 | Object.entries(params).forEach(([key, value]) => { 72 | process(key, value); 73 | }); 74 | 75 | if (qs.length > 0) { 76 | return `?${qs.join("&")}`; 77 | } 78 | 79 | return ""; 80 | }; 81 | 82 | const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { 83 | const encoder = config.ENCODE_PATH || encodeURI; 84 | 85 | const path = options.url 86 | .replace("{api-version}", config.VERSION) 87 | .replace(/{(.*?)}/g, (substring: string, group: string) => { 88 | // eslint-disable-next-line no-prototype-builtins 89 | if (options.path?.hasOwnProperty(group)) { 90 | return encoder(String(options.path[group])); 91 | } 92 | return substring; 93 | }); 94 | 95 | const url = `${config.BASE}${path}`; 96 | if (options.query) { 97 | return `${url}${getQueryString(options.query)}`; 98 | } 99 | return url; 100 | }; 101 | 102 | const getFormData = (options: ApiRequestOptions): FormData | undefined => { 103 | if (options.formData) { 104 | const formData = new FormData(); 105 | 106 | const process = (key: string, value: any) => { 107 | if (isString(value) || isBlob(value)) { 108 | formData.append(key, value); 109 | } else { 110 | formData.append(key, JSON.stringify(value)); 111 | } 112 | }; 113 | 114 | Object.entries(options.formData) 115 | .filter(([, value]) => isDefined(value)) 116 | .forEach(([key, value]) => { 117 | if (Array.isArray(value)) { 118 | value.forEach((v) => process(key, v)); 119 | } else { 120 | process(key, value); 121 | } 122 | }); 123 | 124 | return formData; 125 | } 126 | return undefined; 127 | }; 128 | 129 | type Resolver = (options: ApiRequestOptions) => Promise; 130 | 131 | const resolve = async ( 132 | options: ApiRequestOptions, 133 | resolver?: T | Resolver 134 | ): Promise => { 135 | if (typeof resolver === "function") { 136 | return (resolver as Resolver)(options); 137 | } 138 | return resolver; 139 | }; 140 | 141 | const getHeaders = async ( 142 | config: OpenAPIConfig, 143 | options: ApiRequestOptions, 144 | formData?: FormData 145 | ): Promise> => { 146 | const token = await resolve(options, config.TOKEN); 147 | const username = await resolve(options, config.USERNAME); 148 | const password = await resolve(options, config.PASSWORD); 149 | const additionalHeaders = await resolve(options, config.HEADERS); 150 | const formHeaders = (typeof formData?.getHeaders === "function" && formData?.getHeaders()) || {}; 151 | 152 | const headers = Object.entries({ 153 | Accept: "application/json", 154 | ...additionalHeaders, 155 | ...options.headers, 156 | ...formHeaders, 157 | }) 158 | .filter(([, value]) => isDefined(value)) 159 | .reduce( 160 | (headers, [key, value]) => ({ 161 | ...headers, 162 | [key]: String(value), 163 | }), 164 | {} as Record 165 | ); 166 | 167 | if (isStringWithValue(token)) { 168 | headers["Authorization"] = `Bearer ${token}`; 169 | } 170 | 171 | if (isStringWithValue(username) && isStringWithValue(password)) { 172 | const credentials = base64(`${username}:${password}`); 173 | headers["Authorization"] = `Basic ${credentials}`; 174 | } 175 | 176 | return headers; 177 | }; 178 | 179 | const getRequestBody = (options: ApiRequestOptions): any => { 180 | if (options.body) { 181 | return options.body; 182 | } 183 | return undefined; 184 | }; 185 | 186 | const sendRequest = async ( 187 | config: OpenAPIConfig, 188 | options: ApiRequestOptions, 189 | url: string, 190 | body: any, 191 | formData: FormData | undefined, 192 | headers: Record, 193 | onCancel: OnCancel 194 | ): Promise> => { 195 | const source = axios.CancelToken.source(); 196 | 197 | const requestConfig: AxiosRequestConfig = { 198 | url, 199 | headers, 200 | data: body ?? formData, 201 | method: options.method, 202 | withCredentials: config.WITH_CREDENTIALS, 203 | cancelToken: source.token, 204 | }; 205 | 206 | onCancel(() => source.cancel("The user aborted a request.")); 207 | 208 | try { 209 | return await axios.request(requestConfig); 210 | } catch (error) { 211 | const axiosError = error as AxiosError; 212 | if (axiosError.response) { 213 | return axiosError.response; 214 | } 215 | throw error; 216 | } 217 | }; 218 | 219 | const getResponseHeader = ( 220 | response: AxiosResponse, 221 | responseHeader?: string 222 | ): string | undefined => { 223 | if (responseHeader) { 224 | const content = response.headers[responseHeader]; 225 | if (isString(content)) { 226 | return content; 227 | } 228 | } 229 | return undefined; 230 | }; 231 | 232 | const getResponseBody = (response: AxiosResponse): any => { 233 | if (response.status !== 204) { 234 | return response.data; 235 | } 236 | return undefined; 237 | }; 238 | 239 | const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { 240 | const errors: Record = { 241 | 400: "Bad Request", 242 | 401: "Unauthorized", 243 | 403: "Forbidden", 244 | 404: "Not Found", 245 | 500: "Internal Server Error", 246 | 502: "Bad Gateway", 247 | 503: "Service Unavailable", 248 | ...options.errors, 249 | }; 250 | 251 | const error = errors[result.status]; 252 | if (error) { 253 | console.error("API Error", JSON.stringify(options), JSON.stringify(result)); 254 | throw new ApiError(result, error); 255 | } 256 | 257 | if (!result.ok) { 258 | throw new ApiError(result, "Generic Error"); 259 | } 260 | }; 261 | 262 | /** 263 | * Request method 264 | * @param config The OpenAPI configuration object 265 | * @param options The request options from the service 266 | * @returns CancelablePromise 267 | * @throws ApiError 268 | */ 269 | export const request = ( 270 | config: OpenAPIConfig, 271 | options: ApiRequestOptions 272 | ): CancelablePromise => { 273 | return new CancelablePromise(async (resolve, reject, onCancel) => { 274 | try { 275 | const url = getUrl(config, options); 276 | const formData = getFormData(options); 277 | const body = getRequestBody(options); 278 | const headers = await getHeaders(config, options, formData); 279 | 280 | if (!onCancel.isCancelled) { 281 | const response = await sendRequest( 282 | config, 283 | options, 284 | url, 285 | body, 286 | formData, 287 | headers, 288 | onCancel 289 | ); 290 | const responseBody = getResponseBody(response); 291 | const responseHeader = getResponseHeader(response, options.responseHeader); 292 | 293 | const result: ApiResult = { 294 | url, 295 | ok: isSuccess(response.status), 296 | status: response.status, 297 | statusText: response.statusText, 298 | body: responseHeader ?? responseBody, 299 | }; 300 | 301 | catchErrorCodes(options, result); 302 | 303 | resolve(result.body); 304 | } 305 | } catch (error) { 306 | reject(error); 307 | } 308 | }); 309 | }; 310 | -------------------------------------------------------------------------------- /src/client/index.ts: -------------------------------------------------------------------------------- 1 | export { ApiError } from "./core/ApiError"; 2 | export { CancelablePromise, CancelError } from "./core/CancelablePromise"; 3 | export { OpenAPI } from "./core/OpenAPI"; 4 | export type { OpenAPIConfig } from "./core/OpenAPI"; 5 | 6 | export type { ActiveRouteResponse, ActiveRouteStatus } from "./models/ActiveRouteResponse"; 7 | export type { ActiveRoutesOutputDTO } from "./models/ActiveRoutesOutputDTO"; 8 | export type { ApprovalData } from "./models/ApprovalData"; 9 | export type { ApprovalOutputDTO } from "./models/ApprovalOutputDTO"; 10 | export type { ApprovalTxOutputDTO } from "./models/ApprovalTxOutputDTO"; 11 | export type { Balance } from "./models/Balance"; 12 | export type { BalanceResult } from "./models/BalanceResult"; 13 | export { BridgeDetails } from "./models/BridgeDetails"; 14 | export { BridgeStatusResponse } from "./models/BridgeStatusResponse"; 15 | export type { BridgeStatusResponseDTO } from "./models/BridgeStatusResponseDTO"; 16 | export type { ChainDetails } from "./models/ChainDetails"; 17 | export type { ChainGasBalances } from "./models/ChainGasBalances"; 18 | export type { GasPriceResponseDTO } from "./models/GasPriceResponseDTO"; 19 | export type { GasTokenDetails } from "./models/GasTokenDetails"; 20 | export type { MinGasBalances } from "./models/MinGasBalances"; 21 | export type { NextTxOutputDTO } from "./models/NextTxOutputDTO"; 22 | export { NextTxResponse } from "./models/NextTxResponse"; 23 | export type { QuoteOutputDTO, Quote } from "./models/QuoteOutputDTO"; 24 | export type { Route } from "./models/Route"; 25 | export { RouteStatusOutputDTO } from "./models/RouteStatusOutputDTO"; 26 | export type { SingleTxDTO } from "./models/SingleTxDTO"; 27 | export type { SingleTxOutputDTO } from "./models/SingleTxOutputDTO"; 28 | export { SingleTxResponse } from "./models/SingleTxResponse"; 29 | export { StartActiveRouteInputDTO } from "./models/StartActiveRouteInputDTO"; 30 | export type { SupportedBridgesOutputDTO } from "./models/SupportedBridgesOutputDTO"; 31 | export type { SupportedChainsOutputDTO } from "./models/SupportedChainsOutputDTO"; 32 | export { Token } from "./models/Token"; 33 | export type { TokenBalanceReponseDTO } from "./models/TokenBalanceReponseDTO"; 34 | export type { TokenListOutputDTO } from "./models/TokenListOutputDTO"; 35 | export type { TokenPriceResponseDTO } from "./models/TokenPriceResponseDTO"; 36 | export type { TransactionReceiptResponseDTO } from "./models/TransactionReceiptResponseDTO"; 37 | 38 | export * from "./models/ChainId"; 39 | export * from "./models/Dexes"; 40 | export * from "./models/BridgeDetails"; 41 | export * from "./models/UserTx"; 42 | export * from "./models/TxType"; 43 | export * from "./models/UserTxType"; 44 | export * from "./models/QuoteRequest"; 45 | export * from "./models/BridgeRouteErrors"; 46 | export * from "./models/RefuelData"; 47 | export * from "./models/ActiveRouteOutputDTO"; 48 | export * from "./models/RouteStatusOutputDTO"; 49 | export * from "./models/GasFee"; 50 | export * from "./models/TxStatus"; 51 | export * from "./models/ActiveRoutesRequest"; 52 | export * from "./models/TokenListRequest"; 53 | export * from "./models/SocketPreferences"; 54 | 55 | export { Approvals } from "./services/Approvals"; 56 | export { Balances } from "./services/Balances"; 57 | export { Quotes } from "./services/Quote"; 58 | export { Routes } from "./services/Routes"; 59 | export { Server } from "./services/Server"; 60 | export { Supported } from "./services/Supported"; 61 | export { TokenLists } from "./services/TokenLists"; 62 | -------------------------------------------------------------------------------- /src/client/models/ActiveRouteOutputDTO.ts: -------------------------------------------------------------------------------- 1 | import type { ActiveRouteResponse } from "./ActiveRouteResponse"; 2 | 3 | export type ActiveRouteOutputDTO = { 4 | /** 5 | * Status of API response. 6 | */ 7 | success: boolean; 8 | result: ActiveRouteResponse; 9 | }; 10 | -------------------------------------------------------------------------------- /src/client/models/ActiveRouteResponse.ts: -------------------------------------------------------------------------------- 1 | import type { Token } from "./Token"; 2 | import { UserTx } from "./UserTx"; 3 | 4 | export enum ActiveRouteStatus { 5 | PENDING = "PENDING", 6 | COMPLETED = "COMPLETED", 7 | } 8 | 9 | export type ActiveRouteResponse = { 10 | /** 11 | * Id of the Active Route. 12 | */ 13 | activeRouteId: number; 14 | /** 15 | * Address of user doing the Active Route. 16 | */ 17 | userAddress: string; 18 | /** 19 | * Total number of txs required in Active Route. 20 | */ 21 | totalUserTx: number; 22 | /** 23 | * Array of user txs. 24 | */ 25 | userTxs: Array; 26 | /** 27 | * Id of source chain. 28 | */ 29 | fromChainId: number; 30 | /** 31 | * Id of destination chain. 32 | */ 33 | toChainId: number; 34 | /** 35 | * Address of token on source chain. 36 | */ 37 | fromAssetAddress: string; 38 | /** 39 | * Address of token on destination chain. 40 | */ 41 | toAssetAddress: string; 42 | /** 43 | * Amount of sending tokens. 44 | */ 45 | fromAmount: string; 46 | /** 47 | * Approximate amount of receiving tokens. 48 | */ 49 | toAmount: string; 50 | /** 51 | * Status of the Active Route. 52 | */ 53 | routeStatus: ActiveRouteStatus; 54 | /** 55 | * Timestamp of Route start. 56 | */ 57 | createdAt: string; 58 | /** 59 | * Timestamp of last route update. 60 | */ 61 | updatedAt: string; 62 | /** 63 | * Index of current tx in userTxs array. 64 | */ 65 | currentUserTxIndex: number; 66 | fromAsset: Token; 67 | toAsset: Token; 68 | }; 69 | -------------------------------------------------------------------------------- /src/client/models/ActiveRoutesOutputDTO.ts: -------------------------------------------------------------------------------- 1 | import type { ActiveRouteResponse } from "./ActiveRouteResponse"; 2 | 3 | export type ActiveRoutesOutputDTO = { 4 | /** 5 | * Status of API response. 6 | */ 7 | success: boolean; 8 | result: { 9 | activeRoutes: Array; 10 | pagination: { offset: number; limit: number; totalRecords: number }; 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /src/client/models/ActiveRoutesRequest.ts: -------------------------------------------------------------------------------- 1 | export interface ActiveRoutesRequest { 2 | /** Address of user starting the route. **/ 3 | userAddress: string; 4 | /** Sort param for routes. **/ 5 | sort?: "updatedAt" | "createdAt"; 6 | /** Offset for fetching active routes. **/ 7 | offset?: string; 8 | /** Number of active routes to return in one API call. **/ 9 | limit?: string; 10 | /** Status of the route. The route will only be marked completed if all the user txs have been completed. **/ 11 | routeStatus?: "PENDING" | "COMPLETED"; 12 | /** Id of sending chain **/ 13 | fromChainId?: string; 14 | /** Id of destination chain. **/ 15 | toChainId?: string; 16 | /** Address of token on source chain. **/ 17 | fromTokenAddress?: string; 18 | /** Token address on destination chain. **/ 19 | toTokenAddress?: string; 20 | } 21 | -------------------------------------------------------------------------------- /src/client/models/ApprovalData.ts: -------------------------------------------------------------------------------- 1 | export type ApprovalData = { 2 | /** 3 | * Minimum amount of approval needed. 4 | */ 5 | minimumApprovalAmount: string; 6 | /** 7 | * Address of token for which approval is required. 8 | */ 9 | approvalTokenAddress: string; 10 | /** 11 | * Contract address that needs approval. 12 | */ 13 | allowanceTarget: string; 14 | /** 15 | * Address of owner. 16 | */ 17 | owner: string; 18 | }; 19 | -------------------------------------------------------------------------------- /src/client/models/ApprovalOutputDTO.ts: -------------------------------------------------------------------------------- 1 | export type ApprovalOutputDTO = { 2 | success: boolean; 3 | result: { 4 | value: string; 5 | tokenAddress: string; 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /src/client/models/ApprovalTxOutputDTO.ts: -------------------------------------------------------------------------------- 1 | export type ApprovalTxOutputDTO = { 2 | success: boolean; 3 | result: { 4 | data?: string; 5 | to?: string; 6 | from?: string; 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /src/client/models/Balance.ts: -------------------------------------------------------------------------------- 1 | import type { BalanceResult } from "./BalanceResult"; 2 | 3 | export type Balance = { 4 | success: boolean; 5 | result: Array; 6 | }; 7 | -------------------------------------------------------------------------------- /src/client/models/BalanceResult.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@socket.tech/ll-core"; 2 | 3 | export type BalanceResult = { 4 | chainId: ChainId; 5 | address: string; 6 | name: string; 7 | symbol: string; 8 | decimals: number; 9 | price: number; 10 | amount: number; 11 | currency: string; 12 | }; 13 | -------------------------------------------------------------------------------- /src/client/models/BridgeDetails.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "@socket.tech/ll-core"; 2 | 3 | export import BridgeName = Bridge; 4 | 5 | export type BridgeDetails = { 6 | /** 7 | * Name of bridge. 8 | */ 9 | name: BridgeName; 10 | /** 11 | * URL for icon of bridge. 12 | */ 13 | icon?: string; 14 | /** 15 | * Approx time for bridging in seconds. 16 | */ 17 | serviceTime?: number; 18 | /** 19 | * Display name of bridge. 20 | */ 21 | displayName: string; 22 | /** 23 | * Security score of bridge. 24 | */ 25 | securityScore?: number; 26 | }; 27 | -------------------------------------------------------------------------------- /src/client/models/BridgeInsuranceData.ts: -------------------------------------------------------------------------------- 1 | export type BridgeInsuranceData = { 2 | amount: string; 3 | }; 4 | -------------------------------------------------------------------------------- /src/client/models/BridgeRouteErrors.ts: -------------------------------------------------------------------------------- 1 | import { BridgeName } from ".."; 2 | 3 | export enum BridgeErrorStatus { 4 | MIN_AMOUNT_NOT_MET = "MIN_AMOUNT_NOT_MET", 5 | ASSET_NOT_SUPPORTED = "ASSET_NOT_SUPPORTED", 6 | } 7 | 8 | export type BridgeRouteErrors = { 9 | [bridge in BridgeName]?: { 10 | /** The error status of the bridge */ 11 | status: BridgeErrorStatus; 12 | /** Minimum amount for this route if status if `MIN_AMOUNT_NOT_MET`*/ 13 | minAmount?: string; 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /src/client/models/BridgeStatusResponse.ts: -------------------------------------------------------------------------------- 1 | import { RefuelStatusResponse } from "./RefuelStatusResponse"; 2 | import { Token } from "./Token"; 3 | import { TxStatus } from "./TxStatus"; 4 | 5 | export type BridgeStatusResponse = { 6 | /** 7 | * Destination Transaction hash. 8 | */ 9 | destinationTransactionHash?: string; 10 | /** 11 | * Status of source transaction while bridging. 12 | */ 13 | sourceTxStatus: TxStatus; 14 | /** 15 | * Bridge name 16 | */ 17 | bridgeName: string; 18 | /** 19 | * Indicates whether the tx is a socket transaction 20 | */ 21 | isSocketTx: boolean; 22 | /** 23 | * Source Transaction. 24 | */ 25 | sourceTransactionHash: string; 26 | /** 27 | * Status of destination transaction while bridging. 28 | */ 29 | destinationTxStatus: TxStatus; 30 | /** 31 | * Source Chain Id 32 | */ 33 | fromChainId: number; 34 | /** 35 | * Destination Chain Id. 36 | */ 37 | toChainId: number; 38 | /** 39 | * Refuel 40 | */ 41 | refuel?: RefuelStatusResponse; 42 | /** 43 | * Source Asset 44 | */ 45 | fromAsset?: Token; 46 | /** 47 | * Destination Asset (actual received token) 48 | */ 49 | toAsset?: Token; 50 | /** 51 | * Source Token Price 52 | */ 53 | srcTokenPrice?: number; 54 | /** 55 | * Destination Token Price 56 | */ 57 | destTokenPrice?: number; 58 | /** 59 | * Source Amount 60 | */ 61 | fromAmount?: string; 62 | /** 63 | * Destination Amount (actual amount received) 64 | */ 65 | toAmount?: string; 66 | /** 67 | * Address of the sender 68 | */ 69 | sender?: string; 70 | /** 71 | * Address of the recipient 72 | */ 73 | recipient?: string; 74 | }; 75 | -------------------------------------------------------------------------------- /src/client/models/BridgeStatusResponseDTO.ts: -------------------------------------------------------------------------------- 1 | import type { BridgeStatusResponse } from "./BridgeStatusResponse"; 2 | 3 | export type BridgeStatusResponseDTO = { 4 | /** 5 | * Status of API. 6 | */ 7 | success: boolean; 8 | result: BridgeStatusResponse; 9 | }; 10 | -------------------------------------------------------------------------------- /src/client/models/ChainDetails.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./ChainId"; 2 | import type { GasTokenDetails } from "./GasTokenDetails"; 3 | 4 | export type ChainDetails = { 5 | /** 6 | * Id of chain. 7 | */ 8 | chainId: ChainId; 9 | /** 10 | * Name of chain. 11 | */ 12 | name: string; 13 | /** 14 | * URL for icon of chain. 15 | */ 16 | icon: string; 17 | /** 18 | * Flag indicating whether the chain is L1. 19 | */ 20 | isL1: boolean; 21 | /** 22 | * Flag indicating whether sending of tokens is supported from chain. 23 | */ 24 | sendingEnabled: boolean; 25 | /** 26 | * Flag indicating whether receiving of tokens is supported to chain. 27 | */ 28 | receivingEnabled: boolean; 29 | currency: GasTokenDetails; 30 | rpcs: Array; 31 | explorers: Array; 32 | }; 33 | -------------------------------------------------------------------------------- /src/client/models/ChainGasBalances.ts: -------------------------------------------------------------------------------- 1 | export type ChainGasBalances = any; 2 | -------------------------------------------------------------------------------- /src/client/models/ChainId.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@socket.tech/ll-core"; 2 | 3 | export import ChainId = ChainId; 4 | -------------------------------------------------------------------------------- /src/client/models/Dexes.ts: -------------------------------------------------------------------------------- 1 | import { DexNames } from "@socket.tech/ll-core"; 2 | 3 | export type Dexes = DexNames.OneInch | DexNames.ZeroX; 4 | -------------------------------------------------------------------------------- /src/client/models/ExtraData.ts: -------------------------------------------------------------------------------- 1 | import { OpRebateData } from "./OpRebateData"; 2 | import { RewardData } from "./RewardData"; 3 | 4 | export type ExtraData = { 5 | /** 6 | * @deprecated socket no longer uses opRebateData 7 | */ 8 | opRebateData?: OpRebateData; 9 | rewards?: RewardData[]; 10 | }; 11 | -------------------------------------------------------------------------------- /src/client/models/GasFee.ts: -------------------------------------------------------------------------------- 1 | import { Token } from ".."; 2 | 3 | export type GasFee = { 4 | /** Gas token details. */ 5 | asset: Token; 6 | /** Approx Gas Limit of the transaction. */ 7 | gasLimit: number; 8 | /** USD value of gas fees at current gas price. */ 9 | feesInUsd: number; 10 | /** Estimated Amount of gas token will be used */ 11 | gasAmount: string; 12 | }; 13 | -------------------------------------------------------------------------------- /src/client/models/GasPriceResponseDTO.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./ChainId"; 2 | 3 | export type GasPriceResponseDTO = { 4 | success: boolean; 5 | result: { 6 | chainId?: ChainId; 7 | txType?: number; 8 | fast?: { 9 | gasPrice?: number; 10 | estimatedSeconds?: number; 11 | }; 12 | normal?: { 13 | gasPrice?: number; 14 | estimatedSeconds?: number; 15 | }; 16 | slow?: { 17 | gasPrice?: number; 18 | estimatedSeconds?: number; 19 | }; 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /src/client/models/GasTokenDetails.ts: -------------------------------------------------------------------------------- 1 | export type GasTokenDetails = { 2 | /** 3 | * Address of gas token. 4 | */ 5 | address: string; 6 | /** 7 | * URL for icon of gas token. 8 | */ 9 | icon: string; 10 | /** 11 | * Name of gas token. 12 | */ 13 | name: string; 14 | /** 15 | * Symbol of gas token. 16 | */ 17 | symbol: string; 18 | /** 19 | * Decimals of gas token. 20 | */ 21 | decimals: number; 22 | /** 23 | * Minimum amount to be left for gas while using max amount. 24 | */ 25 | minNativeCurrencyForGas: string; 26 | }; 27 | -------------------------------------------------------------------------------- /src/client/models/HealthResponseDTO.ts: -------------------------------------------------------------------------------- 1 | export type HealthResponseDTO = { 2 | readonly ok: boolean; 3 | }; 4 | -------------------------------------------------------------------------------- /src/client/models/InsuranceFee.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "./Token"; 2 | 3 | export type InsuranceFee = { 4 | allowanceTarget: 'string', 5 | amount: string; 6 | asset: Token; 7 | capacity: string; 8 | deadline: number; 9 | feesInUsd: number; 10 | maxCapacityPerTx: string; 11 | nativeAsset: Token; 12 | nativeFeeAmount: string; 13 | nativeFeesInUsd: number 14 | }; -------------------------------------------------------------------------------- /src/client/models/MinGasBalances.ts: -------------------------------------------------------------------------------- 1 | export type MinGasBalances = any; 2 | -------------------------------------------------------------------------------- /src/client/models/NextTxOutputDTO.ts: -------------------------------------------------------------------------------- 1 | import type { NextTxResponse } from "./NextTxResponse"; 2 | 3 | export type NextTxOutputDTO = { 4 | /** 5 | * Status of API. 6 | */ 7 | status: boolean; 8 | result: NextTxResponse; 9 | }; 10 | -------------------------------------------------------------------------------- /src/client/models/NextTxResponse.ts: -------------------------------------------------------------------------------- 1 | import type { ApprovalData } from "./ApprovalData"; 2 | import { ChainId } from "./ChainId"; 3 | import { TxType } from "./TxType"; 4 | import { UserTxType } from "./UserTxType"; 5 | 6 | export interface NextTxResponse { 7 | /** 8 | * Id of Active Route. 9 | */ 10 | activeRouteId: number; 11 | /** 12 | * Type of user transaction. 13 | */ 14 | userTxType: UserTxType; 15 | /** 16 | * Address to which transaction has to be sent. 17 | */ 18 | txTarget: string; 19 | /** 20 | * Id of chain where transaction has to be sent. 21 | */ 22 | chainId: ChainId; 23 | /** 24 | * Calldata for transaction. 25 | */ 26 | txData: string; 27 | /** 28 | * Type of transaction. 29 | */ 30 | txType: TxType; 31 | 32 | /** 33 | * Native token amount to be sent with transaction. 34 | */ 35 | value: string; 36 | /** 37 | * Index of transaction in Active Route. Index of the object in the userTxs array. 38 | */ 39 | userTxIndex: number; 40 | /** 41 | * Total number of transactions in Active Route. 42 | */ 43 | totalUserTx: number; 44 | approvalData: ApprovalData | null; 45 | } 46 | -------------------------------------------------------------------------------- /src/client/models/OpRebateData.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "./Token"; 2 | 3 | /** 4 | * @deprecated replaced by RewardData type 5 | */ 6 | export type OpRebateData = { 7 | amount: string; 8 | asset: Token; 9 | amountInUsd: number; 10 | }; 11 | -------------------------------------------------------------------------------- /src/client/models/QuoteOutputDTO.ts: -------------------------------------------------------------------------------- 1 | import { BridgeRouteErrors } from "./BridgeRouteErrors"; 2 | import { RefuelData } from "./RefuelData"; 3 | import type { Route } from "./Route"; 4 | import { SocketRoute } from "./SocketRoute"; 5 | import type { Token } from "./Token"; 6 | 7 | export type Quote = { 8 | routes?: Array; 9 | refuel?: RefuelData; 10 | fromChainId?: number; 11 | fromAsset?: Token; 12 | toChainId?: number; 13 | toAsset?: Token; 14 | bridgeRouteErrors: BridgeRouteErrors; 15 | socketRoute?: SocketRoute 16 | }; 17 | 18 | export type QuoteOutputDTO = { 19 | /** 20 | * Status of API. 21 | */ 22 | success: boolean; 23 | result: Quote; 24 | }; 25 | -------------------------------------------------------------------------------- /src/client/models/QuoteRequest.ts: -------------------------------------------------------------------------------- 1 | import { SocketPreferences } from "./SocketPreferences"; 2 | 3 | export enum SortOptions { 4 | Output = "output", 5 | Gas = "gas", 6 | Time = "time", 7 | } 8 | 9 | export interface QuotePreferences extends SocketPreferences { 10 | /** Flag to return only best route per bridge using the sort criteria **/ 11 | uniqueRoutesPerBridge?: boolean; 12 | /** Param to sort routes based on. **/ 13 | sort?: SortOptions; 14 | /** Maximum number of transactions. 15 | * This option will be ignored if singleTxOnly is marked as true. **/ 16 | maxUserTxs?: string; 17 | /** Only get quotes with that are compatible with contracts **/ 18 | isContractCall?: boolean; 19 | /** include gas transfer with bridging tx **/ 20 | bridgeWithGas?: boolean; 21 | /** Default swap slippage for the route in percent between 0 and 100. Default Slippage assumed if not passed. **/ 22 | defaultSwapSlippage?: string; 23 | /** default bridge slippage for the route in percent between 0 and 100. Default Slippage assumed if not passed. NOTE - Slippage is not present for all bridges. **/ 24 | defaultBridgeSlippage?: string; 25 | /** Address that collects the fees */ 26 | feeTakerAddress?: string; 27 | /** 28 | * percentage of fee to be cut from source token amount. 29 | * Fee percent cannot be greater than 5 30 | * Supports up to 3 decimal places 31 | */ 32 | feePercent?: string; 33 | } 34 | 35 | export interface QuoteRequest extends QuotePreferences { 36 | /** Chain id of source chain. **/ 37 | fromChainId: number; 38 | /** Token address on source chain. **/ 39 | fromTokenAddress: string; 40 | /** Chain id of destination chain. **/ 41 | toChainId: number; 42 | /** Token address on destination chain. **/ 43 | toTokenAddress: string; 44 | /** Amount of sending tokens. **/ 45 | fromAmount: string; 46 | /** Address of user. This will be used to check approvals. **/ 47 | userAddress: string; 48 | /** Address of recipient. This will be used to check approvals. **/ 49 | recipient?: string; 50 | } 51 | -------------------------------------------------------------------------------- /src/client/models/RefuelData.ts: -------------------------------------------------------------------------------- 1 | import { Token } from ".."; 2 | import { GasFee } from "./GasFee"; 3 | 4 | export interface RefuelData { 5 | fromAmount: string; 6 | toAmount: string; 7 | gasFees: GasFee; 8 | recipient: string; 9 | serviceTime: number; 10 | fromAsset: Token; 11 | toAsset: Token; 12 | fromChainId: number; 13 | toChainId: number; 14 | } 15 | -------------------------------------------------------------------------------- /src/client/models/RefuelStatusResponse.ts: -------------------------------------------------------------------------------- 1 | import { TxStatus } from "./TxStatus"; 2 | 3 | export type RefuelStatusResponse = { 4 | destinationTxStatus: TxStatus; 5 | sourceTxStatus: TxStatus; 6 | bridge: string; 7 | status: TxStatus; 8 | destinationTransactionHash?: string; 9 | } -------------------------------------------------------------------------------- /src/client/models/RewardData.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "./Token"; 2 | 3 | export type RewardData = { 4 | amount: string; 5 | asset: Token; 6 | amountInUsd: number; 7 | chainId: number; 8 | }; 9 | -------------------------------------------------------------------------------- /src/client/models/Route.ts: -------------------------------------------------------------------------------- 1 | import { BridgeName } from "./BridgeDetails"; 2 | import type { ChainGasBalances } from "./ChainGasBalances"; 3 | import { ExtraData } from "./ExtraData"; 4 | import type { MinGasBalances } from "./MinGasBalances"; 5 | import { Token } from "./Token"; 6 | import { UserTx } from "./UserTx"; 7 | 8 | export type Route = { 9 | /** 10 | * Unique id for each route. 11 | */ 12 | routeId: string; 13 | 14 | /** 15 | * Contains only on single swap. 16 | */ 17 | isOnlySwapRoute: boolean; 18 | 19 | /** 20 | * Sending token amount. 21 | */ 22 | fromAmount: string; 23 | chainGasBalances: ChainGasBalances; 24 | minimumGasBalances: MinGasBalances; 25 | /** 26 | * Approximate receiving token amount. 27 | */ 28 | toAmount: string; 29 | /** 30 | * Array of bridges used in the route 31 | */ 32 | usedBridgeNames: Array; 33 | /** 34 | * Total number of transactions for the route. 35 | */ 36 | totalUserTx: number; 37 | /** 38 | * Combined USD gas fees for all transactions in the route. 39 | */ 40 | totalGasFeesInUsd: number; 41 | /** 42 | * Address of user receiving the amount. 43 | */ 44 | recipient: string; 45 | /** 46 | * Address of user making the transactions. 47 | */ 48 | sender: string; 49 | /** 50 | * Array of user transactions. 51 | */ 52 | userTxs: Array; 53 | /** 54 | * Estimate of total time in seconds, excluding the transaction time. 55 | */ 56 | serviceTime: number; 57 | /** 58 | * Estimate of max time to exit from the chain in seconds. 59 | */ 60 | maxServiceTime: number; 61 | /** 62 | * Receive Value 63 | */ 64 | receivedValueInUsd?: number; 65 | /** 66 | * Input Value 67 | */ 68 | inputValueInUsd: number; 69 | /** 70 | * Output Value 71 | */ 72 | outputValueInUsd: number; 73 | 74 | /** 75 | * Integrator Fee. 76 | */ 77 | integratorFee: { 78 | amount: string; 79 | asset: Token; 80 | feeTakerAddress?: string; 81 | }; 82 | 83 | /** 84 | * Extra Data, includes OP Rebate details 85 | */ 86 | extraData?: ExtraData 87 | }; 88 | -------------------------------------------------------------------------------- /src/client/models/RouteStatusOutputDTO.ts: -------------------------------------------------------------------------------- 1 | // import { TxStatus } from "./TxStatus"; 2 | // TODO: Tx status is being returned inconsistently 3 | // SHOULD BE IDENTICALY TO TxStatus 4 | /** 5 | * Status of transaction while bridging. 6 | */ 7 | export enum PrepareActiveRouteStatus { 8 | PENDING = "pending", 9 | COMPLETED = "completed", 10 | } 11 | 12 | export type RouteStatusOutputDTO = { 13 | /** 14 | * Status of API. 15 | */ 16 | status: boolean; 17 | result: PrepareActiveRouteStatus; 18 | }; 19 | -------------------------------------------------------------------------------- /src/client/models/SingleTxDTO.ts: -------------------------------------------------------------------------------- 1 | import { BridgeInsuranceData } from "./BridgeInsuranceData"; 2 | import type { RefuelData } from "./RefuelData"; 3 | import type { Route } from "./Route"; 4 | 5 | export type SingleTxDTO = { 6 | route: Route; 7 | refuel?: RefuelData; 8 | bridgeInsuranceData?: BridgeInsuranceData; 9 | }; 10 | -------------------------------------------------------------------------------- /src/client/models/SingleTxOutputDTO.ts: -------------------------------------------------------------------------------- 1 | import type { SingleTxResponse } from "./SingleTxResponse"; 2 | 3 | export type SingleTxOutputDTO = { 4 | /** 5 | * Status of API. 6 | */ 7 | status: boolean; 8 | result: SingleTxResponse; 9 | }; 10 | -------------------------------------------------------------------------------- /src/client/models/SingleTxResponse.ts: -------------------------------------------------------------------------------- 1 | import type { ApprovalData } from "./ApprovalData"; 2 | import { TxType } from "./TxType"; 3 | import { UserTxType } from "./UserTxType"; 4 | import { ChainId } from "@socket.tech/ll-core"; 5 | 6 | export type SingleTxResponse = { 7 | /** 8 | * Type of user transaction. 9 | */ 10 | userTxType: UserTxType; 11 | /** 12 | * Address to which transaction has to be sent. 13 | */ 14 | txTarget: string; 15 | /** 16 | * Id of chain where transaction has to be sent. 17 | */ 18 | chainId: ChainId; 19 | /** 20 | * Calldata for transaction. 21 | */ 22 | txData: string; 23 | /** 24 | * Type of transaction. 25 | */ 26 | txType: TxType; 27 | /** 28 | * Native token amount to be sent with transaction. 29 | */ 30 | value: string; 31 | /** 32 | * Total number of transactions in Active Route. 33 | */ 34 | totalUserTx: number; 35 | approvalData: ApprovalData | null; 36 | }; 37 | -------------------------------------------------------------------------------- /src/client/models/SocketPreferences.ts: -------------------------------------------------------------------------------- 1 | import { BridgeName, Dexes } from ".."; 2 | 3 | export interface SocketPreferences { 4 | /** Flag to specify if routes that have dex swap should be ignored. **/ 5 | disableSwapping?: boolean; 6 | /** Specify Dexes that should be included in routes. **/ 7 | includeDexes?: Array; 8 | /** Specify Dexes that should be excluded in routes. 9 | * This option will be ignored if includeDexes is specified. **/ 10 | excludeDexes?: Array; 11 | /** Specify Bridges that should be included in routes. **/ 12 | includeBridges?: Array; 13 | /** Specify Bridges that should be excluded in routes. 14 | * This option will be ignored if includeBridges is specified. **/ 15 | excludeBridges?: Array; 16 | /** Only get quotes with one user transaction to bridge. **/ 17 | singleTxOnly?: boolean; 18 | } 19 | -------------------------------------------------------------------------------- /src/client/models/SocketRoute.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "./Token"; 2 | 3 | type FeeData = { 4 | [time: string]: { 5 | feesInUsd: number; 6 | amount: string; 7 | }; 8 | }; 9 | 10 | export type SocketBridgeFees = { 11 | asset: Token; 12 | feeDeductedByMins: FeeData; 13 | }; 14 | 15 | export type SocketRoute = { 16 | bridgeFees: SocketBridgeFees; 17 | bridgeSlippage: string; 18 | fromAmount: string; 19 | fromAsset: Token; 20 | fromChainId: number; 21 | inputValueInUsd: number; 22 | isOnlySwapRoute: boolean; 23 | minAmountOut: string; 24 | minAmountOutInUsd: number; 25 | outputValueInUsd: number; 26 | recipient: string; 27 | sender: string; 28 | toAmount: string; 29 | toAsset: Token; 30 | toChainId: number; 31 | fromTokenPriceInUsd: number; 32 | toTokenPriceInUsd: number; 33 | }; -------------------------------------------------------------------------------- /src/client/models/StartActiveRouteInputDTO.ts: -------------------------------------------------------------------------------- 1 | import { BridgeInsuranceData } from "./BridgeInsuranceData"; 2 | import { RefuelData } from "./RefuelData"; 3 | import type { Route } from "./Route"; 4 | 5 | export type StartActiveRouteInputDTO = { 6 | /** 7 | * Chain id of source chain. 8 | */ 9 | fromChainId: number; 10 | /** 11 | * Chain id of destination chain. 12 | */ 13 | toChainId: number; 14 | /** 15 | * Token address on source chain. 16 | */ 17 | fromAssetAddress: string; 18 | /** 19 | * Token address on destination chain. 20 | */ 21 | toAssetAddress: string; 22 | /** 23 | * Include the tx details for the first user transaction. If true it will return the txData txType etc. 24 | * If false, it will only return the active route Id of the selected route. 25 | */ 26 | includeFirstTxDetails?: boolean; 27 | /** 28 | * Selected route by the user to bridge tokens from one chain to another. 29 | */ 30 | route: Route; 31 | /** Refuel data for if the user have selected bridge with gas option */ 32 | refuel?: RefuelData; 33 | /** Bridge Insurance data if the user has opted for bridge insurance */ 34 | bridgeInsuranceData?: BridgeInsuranceData 35 | }; 36 | -------------------------------------------------------------------------------- /src/client/models/SupportedBridgesOutputDTO.ts: -------------------------------------------------------------------------------- 1 | import type { BridgeDetails } from "./BridgeDetails"; 2 | 3 | export type SupportedBridgesOutputDTO = { 4 | /** 5 | * Status of API. 6 | */ 7 | success: boolean; 8 | result: Array; 9 | }; 10 | -------------------------------------------------------------------------------- /src/client/models/SupportedChainsOutputDTO.ts: -------------------------------------------------------------------------------- 1 | import type { ChainDetails } from "./ChainDetails"; 2 | 3 | export type SupportedChainsOutputDTO = { 4 | /** 5 | * Status of API. 6 | */ 7 | success: boolean; 8 | result: Array; 9 | }; 10 | -------------------------------------------------------------------------------- /src/client/models/Token.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./ChainId"; 2 | 3 | export type Token = { 4 | /** 5 | * Name of token. 6 | */ 7 | name?: string; 8 | /** 9 | * Address of token. 10 | */ 11 | address: string; 12 | /** 13 | * URL for icon of token. 14 | */ 15 | icon?: string; 16 | /** 17 | * Decimal used for token. 18 | */ 19 | decimals?: number; 20 | /** 21 | * Symbol of token. 22 | */ 23 | symbol: string; 24 | /** 25 | * Chain id of the token 26 | */ 27 | chainId: ChainId; 28 | /** 29 | * URL for icon of token. 30 | */ 31 | logoURI?: string; 32 | /** 33 | * Unique Id over all chains 34 | */ 35 | chainAgnosticId?: string | null; 36 | }; 37 | -------------------------------------------------------------------------------- /src/client/models/TokenBalanceReponseDTO.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./ChainId"; 2 | 3 | export type TokenBalanceReponseDTO = { 4 | success: boolean; 5 | result: { 6 | chainId?: ChainId; 7 | tokenAddress?: string; 8 | userAddress?: string; 9 | balance?: string; 10 | decimals?: number; 11 | icon?: string; 12 | symbol?: string; 13 | name?: string; 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /src/client/models/TokenListOutputDTO.ts: -------------------------------------------------------------------------------- 1 | import type { Token } from "./Token"; 2 | 3 | export type TokenListOutputDTO = { 4 | /** 5 | * Status of API. 6 | */ 7 | success: boolean; 8 | result: Array; 9 | }; 10 | -------------------------------------------------------------------------------- /src/client/models/TokenListRequest.ts: -------------------------------------------------------------------------------- 1 | import { SocketPreferences } from "./SocketPreferences"; 2 | 3 | export interface TokenListRequest extends SocketPreferences { 4 | /** Id of source chain, e.g Optimism = 10 **/ 5 | fromChainId: number; 6 | /** Id of destination chain, e.g xDAI = 100 **/ 7 | toChainId: number; 8 | /** To be Marked true if you want the shorter and more efficient token list. **/ 9 | isShortList?: boolean; 10 | } 11 | -------------------------------------------------------------------------------- /src/client/models/TokenPriceResponseDTO.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "./ChainId"; 2 | 3 | export type TokenPriceResponseDTO = { 4 | success: boolean; 5 | result: { 6 | chainId?: ChainId; 7 | tokenAddress?: string; 8 | tokenPrice?: number; 9 | currency?: string; 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /src/client/models/TransactionReceiptResponseDTO.ts: -------------------------------------------------------------------------------- 1 | export type TransactionReceiptResponseDTO = { 2 | /** 3 | * Status of API. 4 | */ 5 | success: boolean; 6 | result: any; 7 | }; 8 | -------------------------------------------------------------------------------- /src/client/models/TxStatus.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Status of transaction while bridging. 3 | */ 4 | export enum TxStatus { 5 | PENDING = "PENDING", 6 | COMPLETED = "COMPLETED", 7 | FAILED = "FAILED", 8 | } 9 | -------------------------------------------------------------------------------- /src/client/models/TxType.ts: -------------------------------------------------------------------------------- 1 | export enum TxType { 2 | ETH_SEND_TRANSACTION = "eth_sendTransaction", 3 | ETH_SIGN_MESSAGE = "eth_signMessage", 4 | } 5 | -------------------------------------------------------------------------------- /src/client/models/UserTx.ts: -------------------------------------------------------------------------------- 1 | import { TransactionReceipt } from "@ethersproject/abstract-provider"; 2 | import { ApprovalData, BridgeDetails, Token } from ".."; 3 | import { ChainId } from "./ChainId"; 4 | import { GasFee } from "./GasFee"; 5 | import { InsuranceFee } from "./InsuranceFee"; 6 | import { PrepareActiveRouteStatus } from "./RouteStatusOutputDTO"; 7 | import { TxType } from "./TxType"; 8 | import { UserTxType } from "./UserTxType"; 9 | import { ExtraData } from "./ExtraData"; 10 | 11 | export type Step = { 12 | type: string; 13 | protocol: BridgeDetails; 14 | fromChainId: number; 15 | fromAsset: Token; 16 | fromAmount: string; 17 | toChainId: number; 18 | toAsset: Token; 19 | toAmount: string; 20 | gasFees: GasFee; 21 | serviceTime: number; 22 | maxServiceTime: number; 23 | protocolFees: { 24 | amount: string; 25 | feesInUsd: number; 26 | asset: Token; 27 | }; 28 | bridgeSlippage?: number; 29 | swapSlippage?: number; 30 | insuranceFee?: InsuranceFee 31 | minAmountOut?: string; 32 | extraData?: ExtraData 33 | }; 34 | 35 | export type UserTx = { 36 | userTxType: UserTxType; 37 | txType: TxType; 38 | chainId: ChainId; 39 | toAmount: string; 40 | toAsset: Token; 41 | stepCount: number; 42 | routePath: string; 43 | sender: string; 44 | approvalData?: ApprovalData; 45 | steps: Step[]; 46 | gasFees: GasFee; 47 | serviceTime: number; 48 | maxServiceTime: number; 49 | recipient: string; 50 | userTxIndex: number; 51 | userTxStatus?: PrepareActiveRouteStatus; 52 | sourceTransactionHash?: string; 53 | sourceTransactionReceipt?: TransactionReceipt; 54 | destinationTxHash?: string; 55 | destinationTxReceipt?: TransactionReceipt; 56 | bridgeSlippage?: number; 57 | swapSlippage?: number; 58 | protocol?: { 59 | name: string; 60 | displayName: string; 61 | icon: string; 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /src/client/models/UserTxType.ts: -------------------------------------------------------------------------------- 1 | export enum UserTxType { 2 | APPROVE = "approve", 3 | FUND_MOVR = "fund-movr", 4 | CLAIM = "claim", 5 | DEX_SWAP = "dex-swap", 6 | SIGN = "sign", 7 | } 8 | -------------------------------------------------------------------------------- /src/client/services/Approvals.ts: -------------------------------------------------------------------------------- 1 | import type { ApprovalOutputDTO } from "../models/ApprovalOutputDTO"; 2 | import type { ApprovalTxOutputDTO } from "../models/ApprovalTxOutputDTO"; 3 | 4 | import type { CancelablePromise } from "../core/CancelablePromise"; 5 | import { OpenAPI } from "../core/OpenAPI"; 6 | import { request as __request } from "../core/request"; 7 | import { ChainId } from "../models/ChainId"; 8 | 9 | export class Approvals { 10 | /** 11 | * @returns ApprovalOutputDTO Gives approval values of given tokens for a given owner & chainId 12 | * @throws ApiError 13 | */ 14 | public static fetchApprovals({ 15 | chainId, 16 | owner, 17 | allowanceTarget, 18 | tokenAddress, 19 | }: { 20 | /** ID of chain, e.g Ethereum Mainnet = 1 **/ 21 | chainId: ChainId; 22 | /** Wallet address of token holder **/ 23 | owner: string; 24 | /** Address whose spending allowance is to be checked **/ 25 | allowanceTarget: string; 26 | /** Contract address of token **/ 27 | tokenAddress: string; 28 | }): CancelablePromise { 29 | return __request(OpenAPI, { 30 | method: "GET", 31 | url: "/v2/approval/check-allowance", 32 | headers: { 33 | "API-KEY": OpenAPI.API_KEY, 34 | }, 35 | query: { 36 | chainID: chainId, 37 | owner: owner, 38 | allowanceTarget: allowanceTarget, 39 | tokenAddress: tokenAddress, 40 | }, 41 | }); 42 | } 43 | 44 | /** 45 | * @returns ApprovalTxOutputDTO Return the Approval Tx Data for the given params. 46 | * @throws ApiError 47 | */ 48 | public static fetchApprovalsCalldata({ 49 | chainId, 50 | owner, 51 | allowanceTarget, 52 | tokenAddress, 53 | amount, 54 | }: { 55 | /** ID of chain, e.g Ethereum Mainnet = 1 **/ 56 | chainId: ChainId; 57 | /** Wallet address of token holder **/ 58 | owner: string; 59 | /** Address whose spending allowance is to be checked **/ 60 | allowanceTarget: string; 61 | /** Contract address of token **/ 62 | tokenAddress: string; 63 | /** Amount of tokens to approve, e.g 10 USDC (6 decimals) **/ 64 | amount: string; 65 | }): CancelablePromise { 66 | return __request(OpenAPI, { 67 | method: "GET", 68 | url: "/v2/approval/build-tx", 69 | headers: { 70 | "API-KEY": OpenAPI.API_KEY, 71 | }, 72 | query: { 73 | chainID: chainId, 74 | owner: owner, 75 | allowanceTarget: allowanceTarget, 76 | tokenAddress: tokenAddress, 77 | amount: amount, 78 | }, 79 | }); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/client/services/Balances.ts: -------------------------------------------------------------------------------- 1 | import type { Balance } from "../models/Balance"; 2 | import type { TokenBalanceReponseDTO } from "../models/TokenBalanceReponseDTO"; 3 | 4 | import type { CancelablePromise } from "../core/CancelablePromise"; 5 | import { OpenAPI } from "../core/OpenAPI"; 6 | import { request as __request } from "../core/request"; 7 | import { ChainId } from "../models/ChainId"; 8 | 9 | export class Balances { 10 | /** 11 | * @returns Balance Returns the balance of all tokens for a user address on all supported chains 12 | * @throws ApiError 13 | */ 14 | public static getBalances({ userAddress }: { userAddress: string }): CancelablePromise { 15 | return __request(OpenAPI, { 16 | method: "GET", 17 | url: "/v2/balances", 18 | headers: { 19 | "API-KEY": OpenAPI.API_KEY, 20 | }, 21 | query: { 22 | userAddress: userAddress, 23 | }, 24 | }); 25 | } 26 | 27 | /** 28 | * @returns TokenBalanceReponseDTO Returns the balance of the token on any given chain 29 | * @throws ApiError 30 | */ 31 | public static getBalance({ 32 | tokenAddress, 33 | chainId, 34 | userAddress, 35 | }: { 36 | /** Token contract address on network, e.g USDC on Ethereum Mainnet **/ 37 | tokenAddress: string; 38 | /** ID of chain, e.g Ethereum Mainnet = 1 **/ 39 | chainId: ChainId; 40 | /** Address of the user **/ 41 | userAddress: string; 42 | }): CancelablePromise { 43 | return __request(OpenAPI, { 44 | method: "GET", 45 | url: "/v2/balances/token-balance", 46 | headers: { 47 | "API-KEY": OpenAPI.API_KEY, 48 | }, 49 | query: { 50 | tokenAddress: tokenAddress, 51 | chainId: chainId, 52 | userAddress: userAddress, 53 | }, 54 | }); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/client/services/Quote.ts: -------------------------------------------------------------------------------- 1 | import type { QuoteOutputDTO } from "../models/QuoteOutputDTO"; 2 | 3 | import type { CancelablePromise } from "../core/CancelablePromise"; 4 | import { OpenAPI } from "../core/OpenAPI"; 5 | import { request as __request } from "../core/request"; 6 | import { QuoteRequest } from "../models/QuoteRequest"; 7 | 8 | export class Quotes { 9 | /** 10 | * @returns QuoteOutputDTO Returns all the possible routes for bridging tokens from one chain to another. One of the routes can be selected and passed in to start the route. 11 | * @throws ApiError 12 | */ 13 | public static getQuote(quoteRequest: QuoteRequest): CancelablePromise { 14 | return __request(OpenAPI, { 15 | method: "GET", 16 | url: "/v2/quote", 17 | headers: { 18 | "API-KEY": OpenAPI.API_KEY, 19 | }, 20 | query: quoteRequest, 21 | }); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/client/services/Routes.ts: -------------------------------------------------------------------------------- 1 | import type { ActiveRoutesOutputDTO } from "../models/ActiveRoutesOutputDTO"; 2 | import type { NextTxOutputDTO } from "../models/NextTxOutputDTO"; 3 | import type { RouteStatusOutputDTO } from "../models/RouteStatusOutputDTO"; 4 | import type { StartActiveRouteInputDTO } from "../models/StartActiveRouteInputDTO"; 5 | 6 | import type { CancelablePromise } from "../core/CancelablePromise"; 7 | import { OpenAPI } from "../core/OpenAPI"; 8 | import { request as __request } from "../core/request"; 9 | import { ActiveRoutesRequest } from "../models/ActiveRoutesRequest"; 10 | import { ActiveRouteOutputDTO } from "../models/ActiveRouteOutputDTO"; 11 | 12 | export class Routes { 13 | /** 14 | * Function that takes in a route and starts the selected route. 15 | * Function is responsible for 16 | * - Saving the selected route to bridge tokens from one chain to another. 17 | * - Saving the fromChain, toChain, and fromAsssetAddress and toAssetAddress 18 | * - Returns the Active Route Id, Current Tx, Total number of txs, txType 19 | * @returns StartActiveRouteResponseDTO Starts the Active Route and gives back the data to start the route 20 | * @returns any 21 | * @throws ApiError 22 | */ 23 | public static startActiveRoute({ 24 | startRequest, 25 | }: { 26 | startRequest: StartActiveRouteInputDTO; 27 | }): CancelablePromise { 28 | return __request(OpenAPI, { 29 | method: "POST", 30 | url: "/v2/route/start", 31 | headers: { 32 | "API-KEY": OpenAPI.API_KEY, 33 | }, 34 | body: startRequest, 35 | mediaType: "application/json", 36 | }); 37 | } 38 | 39 | /** 40 | * @returns ActiveRouteDTO Get active route details using active route id 41 | * @throws ApiError 42 | */ 43 | public static getActiveRoute({ 44 | activeRouteId, 45 | }: { 46 | /** Id of the Active Route. **/ 47 | activeRouteId: number; 48 | }): CancelablePromise { 49 | return __request(OpenAPI, { 50 | method: "GET", 51 | url: "/v2/route/active-routes", 52 | headers: { 53 | "API-KEY": OpenAPI.API_KEY, 54 | }, 55 | query: { 56 | activeRouteId: activeRouteId, 57 | }, 58 | }); 59 | } 60 | 61 | /** 62 | * @returns ActiveRoutesOutputDTO Get all the active routes from a user address. Filters like fromChainId, toChainId and token addresses can be used to get back specific active routes. 63 | * @throws ApiError 64 | */ 65 | public static getActiveRoutesForUser( 66 | request: ActiveRoutesRequest 67 | ): CancelablePromise { 68 | return __request(OpenAPI, { 69 | method: "GET", 70 | url: "/v2/route/active-routes/users", 71 | headers: { 72 | "API-KEY": OpenAPI.API_KEY, 73 | }, 74 | query: request, 75 | }); 76 | } 77 | 78 | /** 79 | * @returns NextTxOutputDTO Get next tx details of an active route 80 | * @throws ApiError 81 | */ 82 | public static nextTx({ 83 | activeRouteId, 84 | swapSlippage, 85 | bridgeSlippage, 86 | }: { 87 | /** Id of Active Route. **/ 88 | activeRouteId: number; 89 | /** Swap Slippage **/ 90 | swapSlippage?: string; 91 | /** Bridge Slippage */ 92 | bridgeSlippage?: string; 93 | }): CancelablePromise { 94 | return __request(OpenAPI, { 95 | method: "GET", 96 | url: "/v2/route/build-next-tx", 97 | headers: { 98 | "API-KEY": OpenAPI.API_KEY, 99 | }, 100 | query: { 101 | activeRouteId: activeRouteId, 102 | swapSlippage: swapSlippage, 103 | bridgeSlippage: bridgeSlippage, 104 | }, 105 | }); 106 | } 107 | 108 | /** 109 | * @returns RouteStatusOutputDTO Get status of an active route while submitting transaction 110 | * @throws ApiError 111 | */ 112 | public static updateActiveRoute({ 113 | activeRouteId, 114 | userTxIndex, 115 | txHash, 116 | signature, 117 | }: { 118 | /** Id of Active Route. **/ 119 | activeRouteId: number; 120 | /** Index of the userTxs in the Active Route. Every active route will have a userTxs array. userTxIndex is the index of the object in the userTxs array. **/ 121 | userTxIndex: number; 122 | /** Transaction hash that relates to the userTxIndex. Each object in the userTxs is a transaction that has to be done by the user to progress in the route. If all the transactions are completed in the route, it will be marked complete. **/ 123 | txHash?: string; 124 | /** Signature to be sent in case the next transaction is dependant on the signature. **/ 125 | signature?: string; 126 | }): CancelablePromise { 127 | return __request(OpenAPI, { 128 | method: "GET", 129 | url: "/v2/route/prepare", 130 | headers: { 131 | "API-KEY": OpenAPI.API_KEY, 132 | }, 133 | query: { 134 | activeRouteId: activeRouteId, 135 | userTxIndex: userTxIndex, 136 | txHash: txHash, 137 | signature: signature, 138 | }, 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/client/services/Server.ts: -------------------------------------------------------------------------------- 1 | import type { BridgeStatusResponseDTO } from "../models/BridgeStatusResponseDTO"; 2 | import type { GasPriceResponseDTO } from "../models/GasPriceResponseDTO"; 3 | import type { SingleTxDTO } from "../models/SingleTxDTO"; 4 | import type { SingleTxOutputDTO } from "../models/SingleTxOutputDTO"; 5 | import type { TokenPriceResponseDTO } from "../models/TokenPriceResponseDTO"; 6 | import type { TransactionReceiptResponseDTO } from "../models/TransactionReceiptResponseDTO"; 7 | 8 | import type { CancelablePromise } from "../core/CancelablePromise"; 9 | import { OpenAPI } from "../core/OpenAPI"; 10 | import { request as __request } from "../core/request"; 11 | import { BridgeName } from "../models/BridgeDetails"; 12 | import { ChainId } from "../models/ChainId"; 13 | 14 | export class Server { 15 | 16 | 17 | 18 | /** 19 | * @returns GasPriceResponseDTO Current gas prices for a chain 20 | * @throws ApiError 21 | */ 22 | public static getGasPrice({ 23 | chainId, 24 | }: { 25 | /** ID of chain, e.g Ethereum Mainnet = 1 **/ 26 | chainId: ChainId; 27 | }): CancelablePromise { 28 | return __request(OpenAPI, { 29 | method: "GET", 30 | url: "/v2/gas-price", 31 | query: { 32 | chainId: chainId, 33 | }, 34 | headers: { 35 | "API-KEY": OpenAPI.API_KEY, 36 | } 37 | }); 38 | } 39 | 40 | /** 41 | * @returns TokenPriceResponseDTO Returns price of token for a given chain 42 | * @throws ApiError 43 | */ 44 | public static getTokenPrice({ 45 | tokenAddress, 46 | chainId, 47 | }: { 48 | /** Token contract address on network, e.g USDC on Ethereum Mainnet **/ 49 | tokenAddress: string; 50 | /** ID of chain, e.g Ethereum Mainnet = 1 **/ 51 | chainId: ChainId; 52 | }): CancelablePromise { 53 | return __request(OpenAPI, { 54 | method: "GET", 55 | url: "/v2/token-price", 56 | headers: { 57 | "API-KEY": OpenAPI.API_KEY, 58 | }, 59 | query: { 60 | tokenAddress: tokenAddress, 61 | chainId: chainId, 62 | }, 63 | }); 64 | } 65 | 66 | /** 67 | * @returns SingleTxOutputDTO Get the tx details for the route. 68 | * @returns any 69 | * @throws ApiError 70 | */ 71 | public static getSingleTx({ 72 | requestBody, 73 | }: { 74 | requestBody: SingleTxDTO; 75 | }): CancelablePromise { 76 | return __request(OpenAPI, { 77 | method: "POST", 78 | url: "/v2/build-tx", 79 | headers: { 80 | "API-KEY": OpenAPI.API_KEY, 81 | }, 82 | body: requestBody, 83 | mediaType: "application/json", 84 | }); 85 | } 86 | 87 | /** 88 | * @returns BridgeStatusResponseDTO Returns the status of the bridging transaction if completed or pending. 89 | * @throws ApiError 90 | */ 91 | public static getBridgingStatus({ 92 | transactionHash, 93 | fromChainId, 94 | toChainId, 95 | bridgeName, 96 | isBridgeProtectionTx 97 | }: { 98 | /** Transaction hash originating from the source chain while bridging assets. **/ 99 | transactionHash: string; 100 | /** ID of source chain, e.g Ethereum Mainnet = 1 **/ 101 | fromChainId: number; 102 | /** ID of destination chain, e.g Ethereum Mainnet = 1 **/ 103 | toChainId: number; 104 | /** Name of the bridge used while bridging. **/ 105 | bridgeName?: BridgeName; 106 | /** To indicate if bridge protection is enabled for the transaction */ 107 | isBridgeProtectionTx?: boolean; 108 | }): CancelablePromise { 109 | return __request(OpenAPI, { 110 | method: "GET", 111 | url: "/v2/bridge-status", 112 | headers: { 113 | "API-KEY": OpenAPI.API_KEY, 114 | }, 115 | query: { 116 | transactionHash: transactionHash, 117 | fromChainId: fromChainId, 118 | toChainId: toChainId, 119 | bridgeName: bridgeName, 120 | isBridgeProtectionTx: isBridgeProtectionTx, 121 | }, 122 | }); 123 | } 124 | 125 | /** 126 | * @returns TransactionReceiptResponseDTO Returns the receipt of the transaction. 127 | * @throws ApiError 128 | */ 129 | public static getTransactionReceipt({ 130 | transactionHash, 131 | chainId, 132 | }: { 133 | /** Transaction hash originating from the source chain while bridging assets. **/ 134 | transactionHash: string; 135 | /** ID of source chain, e.g Ethereum Mainnet = 1 **/ 136 | chainId: ChainId; 137 | }): CancelablePromise { 138 | return __request(OpenAPI, { 139 | method: "GET", 140 | url: "/v2/tx-receipt", 141 | headers: { 142 | "API-KEY": OpenAPI.API_KEY, 143 | }, 144 | query: { 145 | transactionHash: transactionHash, 146 | chainId: chainId, 147 | }, 148 | }); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/client/services/Supported.ts: -------------------------------------------------------------------------------- 1 | import type { SupportedBridgesOutputDTO } from "../models/SupportedBridgesOutputDTO"; 2 | import type { SupportedChainsOutputDTO } from "../models/SupportedChainsOutputDTO"; 3 | 4 | import type { CancelablePromise } from "../core/CancelablePromise"; 5 | import { OpenAPI } from "../core/OpenAPI"; 6 | import { request as __request } from "../core/request"; 7 | import { ChainId } from "@socket.tech/ll-core"; 8 | 9 | export class Supported { 10 | /** 11 | * @returns SupportedBridgesOutputDTO All Supported Bridges 12 | * @throws ApiError 13 | */ 14 | public static getAllBridges(): CancelablePromise { 15 | return __request(OpenAPI, { 16 | method: "GET", 17 | url: "/v2/supported/bridges", 18 | headers: { 19 | "API-KEY": OpenAPI.API_KEY, 20 | }, 21 | }); 22 | } 23 | 24 | /** 25 | * @returns SupportedChainsOutputDTO All Supported Chains by Movr 26 | * @throws ApiError 27 | */ 28 | public static getAllSupportedChains(): CancelablePromise { 29 | return __request(OpenAPI, { 30 | method: "GET", 31 | url: "/v2/supported/chains", 32 | headers: { 33 | "API-KEY": OpenAPI.API_KEY, 34 | }, 35 | }); 36 | } 37 | 38 | /** 39 | * @returns SupportedChainsOutputDTO Get if token is supported 40 | * @throws ApiError 41 | */ 42 | public static getIfTokenIsSupported({ 43 | chainId, 44 | address, 45 | }: { 46 | /** Id of chain, e.g Optimism = 10 **/ 47 | chainId: ChainId; 48 | /** Contract address of the token **/ 49 | address: string; 50 | }): CancelablePromise { 51 | return __request(OpenAPI, { 52 | method: "GET", 53 | url: "/v2/supported/token-support", 54 | headers: { 55 | "API-KEY": OpenAPI.API_KEY, 56 | }, 57 | query: { 58 | chainId, 59 | address: address, 60 | }, 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/client/services/TokenLists.ts: -------------------------------------------------------------------------------- 1 | import type { TokenListOutputDTO } from "../models/TokenListOutputDTO"; 2 | 3 | import type { CancelablePromise } from "../core/CancelablePromise"; 4 | import { OpenAPI } from "../core/OpenAPI"; 5 | import { request as __request } from "../core/request"; 6 | import { TokenListRequest } from "../models/TokenListRequest"; 7 | 8 | export class TokenLists { 9 | /** 10 | * @returns TokenListOutputDTO All Supported token by a given chainId 11 | * @throws ApiError 12 | */ 13 | public static getFromTokenList({ 14 | fromChainId, 15 | toChainId, 16 | disableSwapping, 17 | includeDexes, 18 | excludeDexes, 19 | includeBridges, 20 | excludeBridges, 21 | singleTxOnly, 22 | isShortList, 23 | }: TokenListRequest): CancelablePromise { 24 | return __request(OpenAPI, { 25 | method: "GET", 26 | url: "/v2/token-lists/from-token-list", 27 | headers: { 28 | "API-KEY": OpenAPI.API_KEY, 29 | }, 30 | query: { 31 | fromChainId: fromChainId, 32 | toChainId: toChainId, 33 | disableSwapping: disableSwapping, 34 | includeDexes: includeDexes, 35 | excludeDexes: excludeDexes, 36 | includeBridges: includeBridges, 37 | excludeBridges: excludeBridges, 38 | singleTxOnly: singleTxOnly, 39 | isShortList: isShortList, 40 | }, 41 | }); 42 | } 43 | 44 | /** 45 | * @returns TokenListOutputDTO All Supported token by a given route 46 | * @throws ApiError 47 | */ 48 | public static getToTokenList({ 49 | fromChainId, 50 | toChainId, 51 | disableSwapping, 52 | includeDexes, 53 | excludeDexes, 54 | includeBridges, 55 | excludeBridges, 56 | singleTxOnly, 57 | isShortList, 58 | }: TokenListRequest): CancelablePromise { 59 | return __request(OpenAPI, { 60 | method: "GET", 61 | url: "/v2/token-lists/to-token-list", 62 | headers: { 63 | "API-KEY": OpenAPI.API_KEY, 64 | }, 65 | query: { 66 | fromChainId: fromChainId, 67 | toChainId: toChainId, 68 | disableSwapping: disableSwapping, 69 | includeDexes: includeDexes, 70 | excludeDexes: excludeDexes, 71 | includeBridges: includeBridges, 72 | excludeBridges: excludeBridges, 73 | singleTxOnly: singleTxOnly, 74 | isShortList: isShortList, 75 | }, 76 | }); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const NATIVE_TOKEN_ADDRESS = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { Socket } from "./socket"; 2 | export * from "./web3ConnectedSocket"; 3 | export { SocketTx } from "./socketTx"; 4 | export { Path } from "./path"; 5 | export { Token } from "./client/models/Token"; 6 | export { TokenList } from "./tokenList"; 7 | export { Chain } from "./chain"; 8 | export * as constants from "./constants"; 9 | export * from "./types"; 10 | export * from "./client"; 11 | -------------------------------------------------------------------------------- /src/mocks/mockActiveRoute.json: -------------------------------------------------------------------------------- 1 | { 2 | "totalUserTx": 2, 3 | "fromAmount": "7141717909854740", 4 | "toAmount": "7141717909854740", 5 | "activeRouteId": 123, 6 | "userAddress": "0x0", 7 | "fromChainId": 1, 8 | "toChainId": 137, 9 | "fromAssetAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 10 | "toAssetAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 11 | "routeStatus": "PENDING", 12 | "createdAt": "", 13 | "updatedAt": "", 14 | "currentUserTxIndex": 0, 15 | "fromAsset": { 16 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 17 | "chainId": 1, 18 | "symbol": "ETH" 19 | }, 20 | "toAsset": { 21 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 22 | "chainId": 137, 23 | "symbol": "MATIC" 24 | }, 25 | "userTxs": [ 26 | { 27 | "userTxType": "fund-movr", 28 | "txType": "eth_sendTransaction", 29 | "chainId": 1, 30 | "toAmount": "6766014423161256", 31 | "toAsset": { 32 | "chainId": 1, 33 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 34 | "symbol": "ETH" 35 | }, 36 | "stepCount": 1, 37 | "routePath": "0-999", 38 | "sender": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 39 | "approvalData": { 40 | "minimumApprovalAmount": "7141717909854740", 41 | "approvalTokenAddress": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", 42 | "allowanceTarget": "0x88DCDC47D2f83a99CF0000FDF667A468bB958a78", 43 | "owner": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" 44 | }, 45 | "steps": [ 46 | { 47 | "type": "bridge", 48 | "protocol": { 49 | "name": "celer", 50 | "displayName": "Celer", 51 | "icon": "https://cbridge.celer.network/static/media/favicon.e3350473.png" 52 | }, 53 | "fromChainId": 1, 54 | "fromAsset": { 55 | "chainId": 1, 56 | "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", 57 | "symbol": "WETH" 58 | }, 59 | "fromAmount": "7141717909854740", 60 | "toChainId": 137, 61 | "toAsset": { 62 | "chainId": 137, 63 | "address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", 64 | "symbol": "ETH" 65 | }, 66 | "toAmount": "6766014423161256", 67 | "gasFees": { 68 | "gasAmount": "10000", 69 | "gasLimit": 120000, 70 | "asset": { 71 | "chainId": 137, 72 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 73 | "symbol": "MATIC" 74 | }, 75 | "feesInUsd": 0.003960684 76 | }, 77 | "serviceTime": 1200 78 | } 79 | ], 80 | "gasFees": { 81 | "gasAmount": "10000", 82 | "feesInUsd": 0.003960684, 83 | "asset": { 84 | "chainId": 137, 85 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 86 | "symbol": "MATIC" 87 | }, 88 | "gasLimit": 120000 89 | }, 90 | "serviceTime": 1200, 91 | "recipient": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 92 | "userTxIndex": 0 93 | } 94 | ] 95 | } 96 | -------------------------------------------------------------------------------- /src/mocks/mockChainDetails.json: -------------------------------------------------------------------------------- 1 | { 2 | "chainId": 100, 3 | "name": "Gnosis", 4 | "icon": "https://movricons.s3.ap-south-1.amazonaws.com/gnosis.svg", 5 | "isL1": false, 6 | "sendingEnabled": true, 7 | "receivingEnabled": true, 8 | "refuel": { 9 | "sendingEnabled": true, 10 | "receivingEnabled": true 11 | }, 12 | "currency": { 13 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 14 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/dai.svg", 15 | "name": "XDAI", 16 | "symbol": "XDAI", 17 | "decimals": 18, 18 | "minNativeCurrencyForGas": "2100000000000000" 19 | }, 20 | "rpcs": [ 21 | "https://rpc.xdaichain.com", 22 | "https://xdai.poanetwork.dev", 23 | "wss://rpc.xdaichain.com/wss", 24 | "wss://xdai.poanetwork.dev/wss", 25 | "http://xdai.poanetwork.dev", 26 | "https://dai.poa.network", 27 | "ws://xdai.poanetwork.dev:8546" 28 | ], 29 | "explorers": ["https://blockscout.com/xdai/mainnet"] 30 | } 31 | -------------------------------------------------------------------------------- /src/mocks/mockRoute.json: -------------------------------------------------------------------------------- 1 | { 2 | "routeId": "670eaa9b-cc47-47fd-a551-f7bfb702cdaa", 3 | "fromAmount": "20000000000000000000", 4 | "toAmount": "38514450278702297", 5 | "usedBridgeNames": ["celer"], 6 | "minimumGasBalances": { 7 | "56": "2800000000000000", 8 | "137": "60000000000000000" 9 | }, 10 | "chainGasBalances": { 11 | "56": { 12 | "minGasBalance": "2800000000000000", 13 | "hasGasBalance": false 14 | }, 15 | "137": { 16 | "minGasBalance": "60000000000000000", 17 | "hasGasBalance": false 18 | } 19 | }, 20 | "totalUserTx": 3, 21 | "sender": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 22 | "recipient": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 23 | "totalGasFeesInUsd": 0.40975913566139993, 24 | "userTxs": [ 25 | { 26 | "userTxType": "dex-swap", 27 | "txType": "eth_sendTransaction", 28 | "chainId": 137, 29 | "protocol": { 30 | "name": "oneinch", 31 | "displayName": "1Inch", 32 | "icon": "https://bridgelogos.s3.ap-south-1.amazonaws.com/1inch.png" 33 | }, 34 | "fromAsset": { 35 | "chainId": 137, 36 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 37 | "symbol": "MATIC", 38 | "name": "MATIC", 39 | "decimals": 18, 40 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/matic.svg", 41 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/matic.svg", 42 | "chainAgnosticId": null 43 | }, 44 | "approvalData": null, 45 | "fromAmount": "20000000000000000000", 46 | "toAsset": { 47 | "chainId": 137, 48 | "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", 49 | "symbol": "WETH", 50 | "name": "Wrapped Ether", 51 | "decimals": 18, 52 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/weth.svg", 53 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/weth.svg", 54 | "chainAgnosticId": "ETH" 55 | }, 56 | "toAmount": "7141717909854740", 57 | "gasFees": { 58 | "gasLimit": 690262, 59 | "asset": { 60 | "chainId": 137, 61 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 62 | "symbol": "MATIC", 63 | "name": "MATIC", 64 | "decimals": 18, 65 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/matic.svg", 66 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/matic.svg", 67 | "chainAgnosticId": null 68 | }, 69 | "feesInUsd": 0.0227825804934 70 | }, 71 | "sender": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 72 | "recipient": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 73 | "userTxIndex": 0 74 | }, 75 | { 76 | "userTxType": "fund-movr", 77 | "txType": "eth_sendTransaction", 78 | "chainId": 137, 79 | "toAmount": "6766014423161256", 80 | "toAsset": { 81 | "chainId": 56, 82 | "address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", 83 | "symbol": "ETH", 84 | "name": "Ethereum Token", 85 | "decimals": 18, 86 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg", 87 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg", 88 | "chainAgnosticId": "ETH" 89 | }, 90 | "stepCount": 1, 91 | "routePath": "0-999", 92 | "sender": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 93 | "approvalData": { 94 | "minimumApprovalAmount": "7141717909854740", 95 | "approvalTokenAddress": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", 96 | "allowanceTarget": "0x88DCDC47D2f83a99CF0000FDF667A468bB958a78", 97 | "owner": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" 98 | }, 99 | "steps": [ 100 | { 101 | "type": "bridge", 102 | "protocol": { 103 | "name": "celer", 104 | "displayName": "Celer", 105 | "icon": "https://cbridge.celer.network/static/media/favicon.e3350473.png", 106 | "securityScore": 3, 107 | "robustnessScore": 3 108 | }, 109 | "fromChainId": 137, 110 | "fromAsset": { 111 | "chainId": 137, 112 | "address": "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", 113 | "symbol": "WETH", 114 | "name": "Wrapped Ether", 115 | "decimals": 18, 116 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/weth.svg", 117 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/weth.svg", 118 | "chainAgnosticId": "ETH" 119 | }, 120 | "fromAmount": "7141717909854740", 121 | "toChainId": 56, 122 | "toAsset": { 123 | "chainId": 56, 124 | "address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", 125 | "symbol": "ETH", 126 | "name": "Ethereum Token", 127 | "decimals": 18, 128 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg", 129 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg", 130 | "chainAgnosticId": "ETH" 131 | }, 132 | "toAmount": "6766014423161256", 133 | "gasFees": { 134 | "gasLimit": 120000, 135 | "asset": { 136 | "chainId": 137, 137 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 138 | "symbol": "MATIC", 139 | "name": "MATIC", 140 | "decimals": 18, 141 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/matic.svg", 142 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/matic.svg", 143 | "chainAgnosticId": null 144 | }, 145 | "feesInUsd": 0.003960684 146 | }, 147 | "protocolFees": { 148 | "asset": { 149 | "chainId": 56, 150 | "address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", 151 | "symbol": "ETH", 152 | "name": "Ethereum Token", 153 | "decimals": 18, 154 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg", 155 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg", 156 | "chainAgnosticId": "ETH" 157 | }, 158 | "feesInUsd": 0, 159 | "amount": "375449653378449" 160 | }, 161 | "serviceTime": 1200 162 | } 163 | ], 164 | "gasFees": { 165 | "feesInUsd": 0.003960684, 166 | "asset": { 167 | "chainId": 137, 168 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 169 | "symbol": "MATIC", 170 | "name": "MATIC", 171 | "decimals": 18, 172 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/matic.svg", 173 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/matic.svg", 174 | "chainAgnosticId": null 175 | }, 176 | "gasLimit": 120000 177 | }, 178 | "serviceTime": 1200, 179 | "recipient": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 180 | "userTxIndex": 1 181 | }, 182 | { 183 | "userTxType": "dex-swap", 184 | "txType": "eth_sendTransaction", 185 | "chainId": 56, 186 | "protocol": { 187 | "name": "oneinch", 188 | "displayName": "1Inch", 189 | "icon": "https://bridgelogos.s3.ap-south-1.amazonaws.com/1inch.png" 190 | }, 191 | "fromAsset": { 192 | "chainId": 56, 193 | "address": "0x2170Ed0880ac9A755fd29B2688956BD959F933F8", 194 | "symbol": "ETH", 195 | "name": "Ethereum Token", 196 | "decimals": 18, 197 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg", 198 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/eth.svg", 199 | "chainAgnosticId": "ETH" 200 | }, 201 | "approvalData": { 202 | "minimumApprovalAmount": "6766014423161256", 203 | "approvalTokenAddress": "0x2170ed0880ac9a755fd29b2688956bd959f933f8", 204 | "allowanceTarget": "0xd286595d2e3D879596FAB51f83A702D10a6db27b", 205 | "owner": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE" 206 | }, 207 | "fromAmount": "6766014423161256", 208 | "toAsset": { 209 | "chainId": 56, 210 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 211 | "symbol": "BNB", 212 | "name": "BNB", 213 | "decimals": 18, 214 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/bnb.svg", 215 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/bnb.svg", 216 | "chainAgnosticId": null 217 | }, 218 | "toAmount": "38514450278702297", 219 | "gasFees": { 220 | "gasLimit": 252364, 221 | "asset": { 222 | "chainId": 56, 223 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 224 | "symbol": "BNB", 225 | "name": "BNB", 226 | "decimals": 18, 227 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/bnb.svg", 228 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/bnb.svg", 229 | "chainAgnosticId": null 230 | }, 231 | "feesInUsd": 0.38301587116799996 232 | }, 233 | "sender": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 234 | "recipient": "0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", 235 | "userTxIndex": 2 236 | } 237 | ], 238 | "serviceTime": 1200 239 | } 240 | -------------------------------------------------------------------------------- /src/mocks/mockTokenList.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "xDAI", 4 | "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", 5 | "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png", 6 | "decimals": 18, 7 | "symbol": "XDAI", 8 | "chainId": 100, 9 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png", 10 | "chainAgnosticId": null 11 | }, 12 | { 13 | "name": "Agave", 14 | "address": "0x3a97704a1b25f08aa230ae53b352e2e72ef52843", 15 | "icon": "https://raw.githubusercontent.com/1Hive/default-token-list/master/src/assets/xdai/0x3a97704a1b25f08aa230ae53b352e2e72ef52843/logo.png", 16 | "decimals": 18, 17 | "symbol": "AGVE", 18 | "chainId": 100, 19 | "logoURI": "https://raw.githubusercontent.com/1Hive/default-token-list/master/src/assets/xdai/0x3a97704a1b25f08aa230ae53b352e2e72ef52843/logo.png", 20 | "chainAgnosticId": null 21 | }, 22 | { 23 | "name": "BaoToken on xDai", 24 | "address": "0x82dfe19164729949fd66da1a37bc70dd6c4746ce", 25 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/bao.svg", 26 | "decimals": 18, 27 | "symbol": "BAO", 28 | "chainId": 100, 29 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/bao.svg", 30 | "chainAgnosticId": null 31 | }, 32 | { 33 | "name": "Bright from Ethereum", 34 | "address": "0x83ff60e2f93f8edd0637ef669c69d5fb4f64ca8e", 35 | "icon": "https://assets.coingecko.com/coins/images/18415/small/bright.PNG?1631841211", 36 | "decimals": 18, 37 | "symbol": "BRIGHT", 38 | "chainId": 100, 39 | "logoURI": "https://assets.coingecko.com/coins/images/18415/small/bright.PNG?1631841211", 40 | "chainAgnosticId": null 41 | }, 42 | { 43 | "name": "BUSD Token from BSC", 44 | "address": "0xdd96b45877d0e8361a4ddb732da741e97f3191ff", 45 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/busd.svg", 46 | "decimals": 18, 47 | "symbol": "BUSD", 48 | "chainId": 100, 49 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/busd.svg", 50 | "chainAgnosticId": null 51 | }, 52 | { 53 | "name": "Dai Stablecoin from Ethereum", 54 | "address": "0x44fa8e6f47987339850636f88629646662444217", 55 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/dai.svg", 56 | "decimals": 18, 57 | "symbol": "DAI", 58 | "chainId": 100, 59 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/dai.svg", 60 | "chainAgnosticId": null 61 | }, 62 | { 63 | "name": "Streamr DATA on xDai", 64 | "address": "0x256eb8a51f382650b2a1e946b8811953640ee47d", 65 | "icon": "https://assets.coingecko.com/coins/images/18500/small/data_32.png?1632209427", 66 | "decimals": 18, 67 | "symbol": "DATA", 68 | "chainId": 100, 69 | "logoURI": "https://assets.coingecko.com/coins/images/18500/small/data_32.png?1632209427", 70 | "chainAgnosticId": null 71 | }, 72 | { 73 | "name": "Decentralized Insurance Protocol on xDai", 74 | "address": "0x48b1b0d077b4919b65b4e4114806dd803901e1d9", 75 | "icon": "https://assets.coingecko.com/coins/images/13186/small/deipool.png?1606085265", 76 | "decimals": 18, 77 | "symbol": "DIP", 78 | "chainId": 100, 79 | "logoURI": "https://assets.coingecko.com/coins/images/13186/small/deipool.png?1606085265", 80 | "chainAgnosticId": null 81 | }, 82 | { 83 | "name": "Donut on xDai", 84 | "address": "0x524b969793a64a602342d89bc2789d43a016b13a", 85 | "icon": "https://assets.coingecko.com/coins/images/7538/small/Donut.png?1548234345", 86 | "decimals": 18, 87 | "symbol": "DONUT", 88 | "chainId": 100, 89 | "logoURI": "https://assets.coingecko.com/coins/images/7538/small/Donut.png?1548234345", 90 | "chainAgnosticId": null 91 | }, 92 | { 93 | "name": "DefiPulse Index from Ethereum", 94 | "address": "0xd3d47d5578e55c880505dc40648f7f9307c3e7a8", 95 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/dpi.svg", 96 | "decimals": 18, 97 | "symbol": "DPI", 98 | "chainId": 100, 99 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/dpi.svg", 100 | "chainAgnosticId": null 101 | }, 102 | { 103 | "name": "DXdao from Ethereum", 104 | "address": "0xb90d6bec20993be5d72a5ab353343f7a0281f158", 105 | "icon": "https://assets.coingecko.com/coins/images/11148/small/dxdao.png?1607999331", 106 | "decimals": 18, 107 | "symbol": "DXD", 108 | "chainId": 100, 109 | "logoURI": "https://assets.coingecko.com/coins/images/11148/small/dxdao.png?1607999331", 110 | "chainAgnosticId": null 111 | }, 112 | { 113 | "name": "FOX from Ethereum", 114 | "address": "0x21a42669643f45bc0e086b8fc2ed70c23d67509d", 115 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/fox.svg", 116 | "decimals": 18, 117 | "symbol": "FOX", 118 | "chainId": 100, 119 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/fox.svg", 120 | "chainAgnosticId": null 121 | }, 122 | { 123 | "name": "Giveth from Mainnet", 124 | "address": "0x4f4f9b8d5b4d0dc10506e5551b0513b61fd59e75", 125 | "icon": "https://assets.coingecko.com/coins/images/8996/small/giv.png?1629952637", 126 | "decimals": 18, 127 | "symbol": "GIV", 128 | "chainId": 100, 129 | "logoURI": "https://assets.coingecko.com/coins/images/8996/small/giv.png?1629952637", 130 | "chainAgnosticId": null 131 | }, 132 | { 133 | "name": "Gnosis Token", 134 | "address": "0x9c58bacc331c9aa871afd802db6379a98e80cedb", 135 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/gno.svg", 136 | "decimals": 18, 137 | "symbol": "GNO", 138 | "chainId": 100, 139 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/gno.svg", 140 | "chainAgnosticId": null 141 | }, 142 | { 143 | "name": "Haus", 144 | "address": "0xb0c5f3100a4d9d9532a4cfd68c55f1ae8da987eb", 145 | "icon": "https://assets.coingecko.com/coins/images/14551/small/jN3kkqke_400x400.png?1616990048", 146 | "decimals": 18, 147 | "symbol": "HAUS", 148 | "chainId": 100, 149 | "logoURI": "https://assets.coingecko.com/coins/images/14551/small/jN3kkqke_400x400.png?1616990048", 150 | "chainAgnosticId": null 151 | }, 152 | { 153 | "name": "Honey", 154 | "address": "0x71850b7e9ee3f13ab46d67167341e4bdc905eef9", 155 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/hny.svg", 156 | "decimals": 18, 157 | "symbol": "HNY", 158 | "chainId": 100, 159 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/hny.svg", 160 | "chainAgnosticId": null 161 | }, 162 | { 163 | "name": "ChainLink Token on xDai", 164 | "address": "0xe2e73a1c69ecf83f464efce6a5be353a37ca09b2", 165 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/link.svg", 166 | "decimals": 18, 167 | "symbol": "LINK", 168 | "chainId": 100, 169 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/link.svg", 170 | "chainAgnosticId": null 171 | }, 172 | { 173 | "name": "Minerva Wallet SuperToken", 174 | "address": "0x63e62989d9eb2d37dfdb1f93a22f063635b07d51", 175 | "icon": "https://minerva.digital/i/MIVA-Token_200x200.png", 176 | "decimals": 18, 177 | "symbol": "MIVA", 178 | "chainId": 100, 179 | "logoURI": "https://minerva.digital/i/MIVA-Token_200x200.png", 180 | "chainAgnosticId": null 181 | }, 182 | { 183 | "name": "Panvala pan on xDai", 184 | "address": "0x981fb9ba94078a2275a8fc906898ea107b9462a8", 185 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/pan.svg", 186 | "decimals": 18, 187 | "symbol": "PAN", 188 | "chainId": 100, 189 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/pan.svg", 190 | "chainAgnosticId": null 191 | }, 192 | { 193 | "name": "Pinakion on xDai", 194 | "address": "0x37b60f4e9a31a64ccc0024dce7d0fd07eaa0f7b3", 195 | "icon": "https://assets.coingecko.com/coins/images/3833/small/kleros.png?1547975322", 196 | "decimals": 18, 197 | "symbol": "PNK", 198 | "chainId": 100, 199 | "logoURI": "https://assets.coingecko.com/coins/images/3833/small/kleros.png?1547975322", 200 | "chainAgnosticId": null 201 | }, 202 | { 203 | "name": "Particle", 204 | "address": "0xb5d592f85ab2d955c25720ebe6ff8d4d1e1be300", 205 | "icon": "https://raw.githubusercontent.com/ShenaniganDApp/docs/master/static/img/SHELogo.png", 206 | "decimals": 18, 207 | "symbol": "PRTCLE", 208 | "chainId": 100, 209 | "logoURI": "https://raw.githubusercontent.com/ShenaniganDApp/docs/master/static/img/SHELogo.png", 210 | "chainAgnosticId": null 211 | }, 212 | { 213 | "name": "Punk from Mainnet", 214 | "address": "0x988d1be68f2c5cde2516a2287c59bd6302b7d20d", 215 | "icon": "https://assets.coingecko.com/coins/images/17018/small/Punk.png?1626086346", 216 | "decimals": 18, 217 | "symbol": "PUNK", 218 | "chainId": 100, 219 | "logoURI": "https://assets.coingecko.com/coins/images/17018/small/Punk.png?1626086346", 220 | "chainAgnosticId": null 221 | }, 222 | { 223 | "name": "Raid Guild Token from Ethereum", 224 | "address": "0x18e9262e68cc6c6004db93105cc7c001bb103e49", 225 | "icon": "https://assets.coingecko.com/coins/images/18133/small/raid_200_oswlvz.png?1630631876", 226 | "decimals": 18, 227 | "symbol": "RAID", 228 | "chainId": 100, 229 | "logoURI": "https://assets.coingecko.com/coins/images/18133/small/raid_200_oswlvz.png?1630631876", 230 | "chainAgnosticId": null 231 | }, 232 | { 233 | "name": "Rare Coin v2", 234 | "address": "0x57e93bb58268de818b42e3795c97bad58afcd3fe", 235 | "icon": "https://affinityharmonics.s3.ca-central-1.amazonaws.com/Cloud/rare-logo.png", 236 | "decimals": 18, 237 | "symbol": "RAREV2", 238 | "chainId": 100, 239 | "logoURI": "https://affinityharmonics.s3.ca-central-1.amazonaws.com/Cloud/rare-logo.png", 240 | "chainAgnosticId": null 241 | }, 242 | { 243 | "name": "DAOSquare Governance Token from Ethereum", 244 | "address": "0x97edc0e345fbbbd8460847fcfa3bc2a13bf8641f", 245 | "icon": "https://assets.coingecko.com/coins/images/17204/small/RICE-200x200.png?1626847877", 246 | "decimals": 18, 247 | "symbol": "RICE", 248 | "chainId": 100, 249 | "logoURI": "https://assets.coingecko.com/coins/images/17204/small/RICE-200x200.png?1626847877", 250 | "chainAgnosticId": null 251 | }, 252 | { 253 | "name": "SNAFU", 254 | "address": "0x27b9c2bd4baea18abdf49169054c1c1c12af9862", 255 | "icon": "https://gateway.ipfs.io/ipfs/QmaEPqRKrJDN8iNKVSFj19rbHzZSyWENdj241oek3EJYn7", 256 | "decimals": 18, 257 | "symbol": "SNAFU", 258 | "chainId": 100, 259 | "logoURI": "https://gateway.ipfs.io/ipfs/QmaEPqRKrJDN8iNKVSFj19rbHzZSyWENdj241oek3EJYn7", 260 | "chainAgnosticId": null 261 | }, 262 | { 263 | "name": "Stake Token on xDai", 264 | "address": "0xb7d311e2eb55f2f68a9440da38e7989210b9a05e", 265 | "icon": "https://assets.coingecko.com/coins/images/11061/small/xdai.png?1587714165", 266 | "decimals": 18, 267 | "symbol": "STAKE", 268 | "chainId": 100, 269 | "logoURI": "https://assets.coingecko.com/coins/images/11061/small/xdai.png?1587714165", 270 | "chainAgnosticId": null 271 | }, 272 | { 273 | "name": "Symmetric", 274 | "address": "0xc45b3c1c24d5f54e7a2cf288ac668c74dd507a84", 275 | "icon": "https://assets.coingecko.com/coins/images/18525/small/SYMM-Coin-2.png?1632276841", 276 | "decimals": 18, 277 | "symbol": "SYMM", 278 | "chainId": 100, 279 | "logoURI": "https://assets.coingecko.com/coins/images/18525/small/SYMM-Coin-2.png?1632276841", 280 | "chainAgnosticId": null 281 | }, 282 | { 283 | "name": "Trips on xDai", 284 | "address": "0x479e32cdff5f216f93060700c711d1cc8e811a6b", 285 | "icon": "https://assets.coingecko.com/coins/images/14648/small/trips_32.png?1617491133", 286 | "decimals": 18, 287 | "symbol": "TRIPS", 288 | "chainId": 100, 289 | "logoURI": "https://assets.coingecko.com/coins/images/14648/small/trips_32.png?1617491133", 290 | "chainAgnosticId": null 291 | }, 292 | { 293 | "name": "UNCL on xDai", 294 | "address": "0x703120f2f2011a0d03a03a531ac0e84e81f15989", 295 | "icon": "https://assets.coingecko.com/coins/images/13102/small/uncl_logo.png?1605230945", 296 | "decimals": 18, 297 | "symbol": "UNCL", 298 | "chainId": 100, 299 | "logoURI": "https://assets.coingecko.com/coins/images/13102/small/uncl_logo.png?1605230945", 300 | "chainAgnosticId": null 301 | }, 302 | { 303 | "name": "UniCrypt on xDai", 304 | "address": "0x0116e28b43a358162b96f70b4de14c98a4465f25", 305 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/uncx.svg", 306 | "decimals": 18, 307 | "symbol": "UNCX", 308 | "chainId": 100, 309 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/uncx.svg", 310 | "chainAgnosticId": null 311 | }, 312 | { 313 | "name": "USDC on xDai", 314 | "address": "0xddafbb505ad214d7b80b1f830fccc89b60fb7a83", 315 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/usdc.svg", 316 | "decimals": 6, 317 | "symbol": "USDC", 318 | "chainId": 100, 319 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/usdc.svg", 320 | "chainAgnosticId": "USDC" 321 | }, 322 | { 323 | "name": "Tether on xDai", 324 | "address": "0x4ecaba5870353805a9f068101a40e0f32ed605c6", 325 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/usdt.svg", 326 | "decimals": 6, 327 | "symbol": "USDT", 328 | "chainId": 100, 329 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/usdt.svg", 330 | "chainAgnosticId": "USDT" 331 | }, 332 | { 333 | "name": "Wrapped BTC on xDai", 334 | "address": "0x8e5bbbb09ed1ebde8674cda39a0c169401db4252", 335 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/wbtc.svg", 336 | "decimals": 8, 337 | "symbol": "WBTC", 338 | "chainId": 100, 339 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/wbtc.svg", 340 | "chainAgnosticId": null 341 | }, 342 | { 343 | "name": "Wrapped Ether on xDai", 344 | "address": "0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1", 345 | "icon": "https://maticnetwork.github.io/polygon-token-assets/assets/weth.svg", 346 | "decimals": 18, 347 | "symbol": "WETH", 348 | "chainId": 100, 349 | "logoURI": "https://maticnetwork.github.io/polygon-token-assets/assets/weth.svg", 350 | "chainAgnosticId": "ETH" 351 | }, 352 | { 353 | "name": "Wrapped xDai", 354 | "address": "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d", 355 | "icon": "https://raw.githubusercontent.com/sushiswap/logos/main/network/xdai/0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d.jpg", 356 | "decimals": 18, 357 | "symbol": "WXDAI", 358 | "chainId": 100, 359 | "logoURI": "https://raw.githubusercontent.com/sushiswap/logos/main/network/xdai/0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d.jpg", 360 | "chainAgnosticId": "DAI" 361 | }, 362 | { 363 | "name": "xDai Native Comb", 364 | "address": "0x38fb649ad3d6ba1113be5f57b927053e97fc5bf7", 365 | "icon": "https://raw.githubusercontent.com/1Hive/default-token-list/master/src/assets/xdai/0x38Fb649Ad3d6BA1113Be5F57B927053E97fC5bF7/logo.png", 366 | "decimals": 18, 367 | "symbol": "XCOMB", 368 | "chainId": 100, 369 | "logoURI": "https://raw.githubusercontent.com/1Hive/default-token-list/master/src/assets/xdai/0x38Fb649Ad3d6BA1113Be5F57B927053E97fC5bF7/logo.png", 370 | "chainAgnosticId": null 371 | }, 372 | { 373 | "name": "Xion Global Token", 374 | "address": "0xc25af3123d2420054c8fcd144c21113aa2853f39", 375 | "icon": "https://xion.finance/images/xgt_icon.png", 376 | "decimals": 18, 377 | "symbol": "XGTV2", 378 | "chainId": 100, 379 | "logoURI": "https://xion.finance/images/xgt_icon.png", 380 | "chainAgnosticId": null 381 | }, 382 | { 383 | "name": "xMOON on xDai", 384 | "address": "0x1e16aa4df73d29c029d94ceda3e3114ec191e25a", 385 | "icon": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/xdai/assets/0x1e16aa4Df73d29C029d94CeDa3e3114EC191E25A/logo.png", 386 | "decimals": 18, 387 | "symbol": "XMOON", 388 | "chainId": 100, 389 | "logoURI": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/xdai/assets/0x1e16aa4Df73d29C029d94CeDa3e3114EC191E25A/logo.png", 390 | "chainAgnosticId": null 391 | }, 392 | { 393 | "name": "Elk", 394 | "address": "0xeeeeeb57642040be42185f49c52f7e9b38f8eeee", 395 | "icon": "https://assets.coingecko.com/coins/images/22780/small/Untitled-design-p-500.png?1642578931", 396 | "decimals": 18, 397 | "symbol": "ELK", 398 | "chainId": 100, 399 | "logoURI": "https://assets.coingecko.com/coins/images/22780/small/Untitled-design-p-500.png?1642578931", 400 | "chainAgnosticId": null 401 | }, 402 | { 403 | "name": "RealT SOON Token", 404 | "address": "0xaa2c0cf54cb418eb24e7e09053b82c875c68bb88", 405 | "icon": "https://tokens.1inch.io/0xaa2c0cf54cb418eb24e7e09053b82c875c68bb88.png", 406 | "decimals": 18, 407 | "symbol": "SOON", 408 | "chainId": 100, 409 | "logoURI": "https://tokens.1inch.io/0xaa2c0cf54cb418eb24e7e09053b82c875c68bb88.png", 410 | "chainAgnosticId": null 411 | } 412 | ] 413 | -------------------------------------------------------------------------------- /src/path.test.ts: -------------------------------------------------------------------------------- 1 | import { Path } from "./path"; 2 | describe("Path", () => { 3 | it("assigns from and to correctly", async () => { 4 | const fromToken = { address: "0x0", chainId: 1, symbol: "A" }; 5 | const toToken = { address: "0x1", chainId: 2, symbol: "B" }; 6 | const path = new Path({ fromToken, toToken }); 7 | 8 | expect(path.fromToken).toBe(fromToken); 9 | expect(path.toToken).toBe(toToken); 10 | }); 11 | 12 | it("throws when fromToken undefined", async () => { 13 | expect(() => { 14 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 15 | // @ts-ignore 16 | new Path({ fromToken: { address: "0x0", chainId: 1, symbol: "A" }, toToken: undefined }); 17 | }).toThrow(); 18 | }); 19 | 20 | it("throws when toToken undefined", async () => { 21 | expect(() => { 22 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 23 | // @ts-ignore 24 | new Path({ fromToken: undefined, toToken: { address: "0x0", chainId: 1, symbol: "A" } }); 25 | }).toThrow(); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/path.ts: -------------------------------------------------------------------------------- 1 | import { Token } from "./client"; 2 | 3 | /** 4 | * The Path object represents a trade from a source token to a destination token. 5 | */ 6 | export class Path { 7 | /** 8 | * The source token 9 | */ 10 | fromToken: Token; 11 | /** 12 | * The destination token 13 | */ 14 | toToken: Token; 15 | 16 | /** 17 | * 18 | * @param options The options for the path 19 | * @param options.fromToken The source token 20 | * @param options.toToken The destination token 21 | */ 22 | constructor({ fromToken, toToken }: { fromToken: Token; toToken: Token }) { 23 | if (!fromToken || !toToken) { 24 | throw new Error("`fromToken` and `toToken` must be defined."); 25 | } 26 | this.fromToken = fromToken; 27 | this.toToken = toToken; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/socket.test.ts: -------------------------------------------------------------------------------- 1 | import { Socket } from "."; 2 | import { NextTxResponse, OpenAPI } from "./client"; 3 | import { Routes } from "./client/services/Routes"; 4 | import { Path } from "./path"; 5 | import mockRoute from "./mocks/mockRoute.json"; 6 | import { SocketQuote } from "./types"; 7 | import { UserTxType } from "./client/models/UserTxType"; 8 | import { TxType } from "./client/models/TxType"; 9 | import { BridgeName } from "./client/models/BridgeDetails"; 10 | import { PrepareActiveRouteStatus } from "./client/models/RouteStatusOutputDTO"; 11 | import { Middleware } from "@socket.tech/ll-core"; 12 | 13 | jest.mock("./client/services/Routes"); 14 | const mockedRoutes = jest.mocked(Routes, true); 15 | 16 | const MOCK_ROUTE = mockRoute; 17 | const MOCK_ROUTE_TX0 = MOCK_ROUTE.userTxs[0]; 18 | const MOCK_NEXT_TX: NextTxResponse = { 19 | activeRouteId: 123, 20 | totalUserTx: 3, 21 | userTxIndex: 0, 22 | value: "100", 23 | txData: "0x0", 24 | txTarget: "0x0", 25 | chainId: MOCK_ROUTE_TX0.chainId, 26 | txType: MOCK_ROUTE_TX0.txType as TxType, 27 | approvalData: MOCK_ROUTE_TX0.approvalData, 28 | userTxType: MOCK_ROUTE_TX0.userTxType as UserTxType, 29 | }; 30 | 31 | describe("Socket", () => { 32 | it("assigns apikey", async () => { 33 | const socket = new Socket({ apiKey: "abc" }); 34 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 35 | // @ts-expect-error 36 | expect(socket._options.apiKey).toBe("abc"); 37 | expect(OpenAPI.API_KEY).toBe("abc"); 38 | }); 39 | 40 | it("assigns base Url", async () => { 41 | const socket = new Socket({ apiKey: "abc", baseUrl: "http://localhost:8080" }); 42 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 43 | // @ts-expect-error 44 | expect(socket._options.baseUrl).toBe("http://localhost:8080"); 45 | expect(OpenAPI.BASE).toBe("http://localhost:8080"); 46 | }); 47 | 48 | it("both include and exclude dex invalid", () => { 49 | const socket = new Socket({ apiKey: "abc" }); 50 | expect(() => 51 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 52 | // @ts-expect-error 53 | socket.validatePreferences({ 54 | includeDexes: [Middleware.OneInch], 55 | excludeDexes: [Middleware.OneInch], 56 | }) 57 | ).toThrow(); 58 | }); 59 | 60 | it("both include and exclude bridge invalid", () => { 61 | const socket = new Socket({ apiKey: "abc" }); 62 | expect(() => 63 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 64 | // @ts-expect-error 65 | socket.validatePreferences({ 66 | includeBridges: [BridgeName.AnySwap], 67 | excludeBridges: [BridgeName.AnySwap], 68 | }) 69 | ).toThrow(); 70 | }); 71 | }); 72 | 73 | describe("Socket - Execute", () => { 74 | it("executes all steps", async () => { 75 | const socket = new Socket({ apiKey: "abc" }); 76 | const fromToken = { address: "0x0", chainId: 1, symbol: "A" }; 77 | const toToken = { address: "0x1", chainId: 2, symbol: "B" }; 78 | const quote: SocketQuote = { 79 | address: "0x0", 80 | amount: "10000", 81 | path: new Path({ fromToken, toToken }), 82 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 83 | // @ts-ignore 84 | route: MOCK_ROUTE, 85 | }; 86 | mockedRoutes.startActiveRoute.mockResolvedValueOnce({ 87 | status: true, 88 | result: { 89 | ...MOCK_NEXT_TX, 90 | txData: "0x0", 91 | userTxIndex: 0, 92 | }, 93 | }); 94 | 95 | // Immediately resolve submissions 96 | mockedRoutes.updateActiveRoute.mockResolvedValue({ 97 | status: true, 98 | result: PrepareActiveRouteStatus.COMPLETED, 99 | }); 100 | 101 | const generaetor = await socket.start(quote); 102 | expect(generaetor.activeRouteId).toEqual(123); 103 | const tx0 = await generaetor.next(); 104 | if (!tx0.value) throw new Error("did not give tx"); 105 | expect(tx0.value.txData).toBe("0x0"); 106 | 107 | mockedRoutes.nextTx.mockResolvedValueOnce({ 108 | status: true, 109 | result: { 110 | ...MOCK_NEXT_TX, 111 | txData: "0x1", 112 | userTxIndex: 1, 113 | }, 114 | }); 115 | 116 | const tx1 = await generaetor.next("0x123"); 117 | expect(mockedRoutes.updateActiveRoute.mock.calls).toContainEqual([ 118 | { 119 | activeRouteId: 123, 120 | userTxIndex: 0, 121 | txHash: "0x123", 122 | }, 123 | ]); 124 | 125 | if (!tx1.value) throw new Error("did not give tx"); 126 | expect(tx1.value.txData).toBe("0x1"); 127 | 128 | mockedRoutes.nextTx.mockResolvedValueOnce({ 129 | status: true, 130 | result: { 131 | ...MOCK_NEXT_TX, 132 | txData: "0x2", 133 | userTxIndex: 2, 134 | }, 135 | }); 136 | 137 | const tx2 = await generaetor.next("0x456"); 138 | expect(mockedRoutes.updateActiveRoute.mock.calls).toContainEqual([ 139 | { 140 | activeRouteId: 123, 141 | userTxIndex: 1, 142 | txHash: "0x456", 143 | }, 144 | ]); 145 | 146 | if (!tx2.value) throw new Error("did not give tx"); 147 | expect(tx2.value.txData).toBe("0x2"); 148 | 149 | const txDone = await generaetor.next("0x789"); 150 | expect(txDone.done).toBe(true); 151 | expect(mockedRoutes.updateActiveRoute.mock.calls).toContainEqual([ 152 | { 153 | activeRouteId: 123, 154 | userTxIndex: 2, 155 | txHash: "0x789", 156 | }, 157 | ]); 158 | }); 159 | }); 160 | -------------------------------------------------------------------------------- /src/socket.ts: -------------------------------------------------------------------------------- 1 | import { Web3Provider } from "@ethersproject/providers"; 2 | import { SocketQuote, Web3ConnectedSocket } from "."; 3 | import { BaseSocket } from "./baseSocket"; 4 | 5 | /** 6 | * @inheritdoc 7 | */ 8 | export class Socket extends BaseSocket { 9 | /** 10 | * Start executing a socket quote/route. 11 | * @param quote 12 | * @returns An iterator that will yield each transaction required in the route 13 | */ 14 | async start(quote: SocketQuote) { 15 | return this._startQuote(quote); 16 | } 17 | 18 | /** 19 | * Continue an active route 20 | * @param activeRouteId The active route id of the desired route to continue 21 | * @returns An iterator that will yield each transaction required in the route 22 | */ 23 | async continue(activeRouteId: number) { 24 | return this._continueRoute(activeRouteId); 25 | } 26 | 27 | /** 28 | * Connect Socket to a web3 provider that will be used to execute routes 29 | * @param provider The web3 provider to use as user wallet 30 | */ 31 | connect(provider: Web3Provider) { 32 | return new Web3ConnectedSocket(this._options, provider); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/socketTx.test.ts: -------------------------------------------------------------------------------- 1 | import { NextTxResponse } from "./client"; 2 | import { TxType } from "./client/models/TxType"; 3 | import { UserTxType } from "./client/models/UserTxType"; 4 | import { ActiveRouteResponse } from "./client/models/ActiveRouteResponse"; 5 | import { SocketTx } from "./socketTx"; 6 | import { Approvals } from "./client/services/Approvals"; 7 | import { Routes } from "./client/services/Routes"; 8 | import { PrepareActiveRouteStatus } from "./client/models/RouteStatusOutputDTO"; 9 | import mockActiveRoute from "./mocks/mockActiveRoute.json"; 10 | 11 | jest.mock("./client/services/Approvals"); 12 | const mockedApprovals = jest.mocked(Approvals, true); 13 | 14 | jest.mock("./client/services/Routes"); 15 | const mockedRoutes = jest.mocked(Routes, true); 16 | 17 | const MOCK_ACTIVE_ROUTE: ActiveRouteResponse = mockActiveRoute as ActiveRouteResponse; 18 | 19 | const MOCK_TX: NextTxResponse = { 20 | activeRouteId: 123, 21 | approvalData: { 22 | allowanceTarget: "0x0", 23 | approvalTokenAddress: "0x1", 24 | minimumApprovalAmount: "500", 25 | owner: "0x5", 26 | }, 27 | chainId: 1, 28 | totalUserTx: 2, 29 | txData: "0x123", 30 | txTarget: "0xc30141B657f4216252dc59Af2e7CdB9D8792e1B0", 31 | txType: TxType.ETH_SEND_TRANSACTION, 32 | userTxIndex: 0, 33 | userTxType: UserTxType.FUND_MOVR, 34 | value: "500", 35 | }; 36 | 37 | describe("Socket Tx - Creation", () => { 38 | it("initialises checks correctly", async () => { 39 | const tx = new SocketTx(MOCK_TX); 40 | expect(tx.done).toBe(false); 41 | expect(tx.approvalChecked).toBe(false); 42 | }); 43 | }); 44 | 45 | describe("Socket Tx - Approval Required", () => { 46 | it("returns false if no approval data", async () => { 47 | const tx = new SocketTx({ 48 | ...MOCK_TX, 49 | approvalData: null, 50 | }); 51 | const required = await tx.approvalRequired(); 52 | expect(required).toBe(false); 53 | }); 54 | 55 | it("flags checked when approval checked", async () => { 56 | const tx = new SocketTx({ 57 | ...MOCK_TX, 58 | approvalData: null, 59 | }); 60 | await tx.approvalRequired(); 61 | expect(tx.approvalChecked).toBe(true); 62 | }); 63 | 64 | it.each([ 65 | { test: "below", allowance: "499", expected: true }, 66 | { test: "exceeds", allowance: "600", expected: false }, 67 | { test: "equals", allowance: "500", expected: false }, 68 | ])("return $expected if approval $test required", async ({ allowance, expected }) => { 69 | const tx = new SocketTx({ 70 | ...MOCK_TX, 71 | }); 72 | 73 | mockedApprovals.fetchApprovals.mockResolvedValue({ 74 | success: true, 75 | result: { 76 | value: allowance, 77 | tokenAddress: "0x1", 78 | }, 79 | }); 80 | 81 | const required = await tx.approvalRequired(); 82 | expect(required).toBe(expected); 83 | }); 84 | }); 85 | 86 | describe("Socket Tx - Get Send Transaction", () => { 87 | it("throws error if approval not checked", async () => { 88 | const tx = new SocketTx(MOCK_TX); 89 | expect(tx.approvalChecked).toBe(false); 90 | await expect(tx.getSendTransaction()).rejects.toThrow(); 91 | }); 92 | 93 | it("provides transaction data if approval checked", async () => { 94 | const tx = new SocketTx(MOCK_TX); 95 | mockedApprovals.fetchApprovals.mockResolvedValue({ 96 | success: true, 97 | result: { 98 | value: "600", 99 | tokenAddress: "0x1", 100 | }, 101 | }); 102 | mockedRoutes.getActiveRoute.mockResolvedValue({ 103 | success: true, 104 | result: MOCK_ACTIVE_ROUTE, 105 | }); 106 | const required = await tx.approvalRequired(); 107 | expect(required).toBe(false); 108 | 109 | const txData = await tx.getSendTransaction(); 110 | expect(txData.to).toBe(tx.txTarget); 111 | expect(txData.data).toBe(tx.txData); 112 | expect(txData.value).toBe(tx.value); 113 | }); 114 | }); 115 | 116 | describe("Socket Tx - Submit", () => { 117 | it("resolves when update is completed", async () => { 118 | const tx = new SocketTx(MOCK_TX, 50); 119 | mockedRoutes.updateActiveRoute.mockResolvedValue({ 120 | status: true, 121 | result: PrepareActiveRouteStatus.PENDING, 122 | }); 123 | setTimeout(() => { 124 | mockedRoutes.updateActiveRoute.mockResolvedValue({ 125 | status: true, 126 | result: PrepareActiveRouteStatus.COMPLETED, 127 | }); 128 | }, 100); 129 | const result = await tx.submit("0x0"); 130 | expect(result).toBe(PrepareActiveRouteStatus.COMPLETED); 131 | }); 132 | 133 | it("does not allow multiple hash to be submitted", async () => { 134 | const tx = new SocketTx(MOCK_TX, 50); 135 | tx.submit("0x123"); 136 | await expect(tx.submit("0x456")).rejects.toThrow(); 137 | }); 138 | }); 139 | 140 | describe("Socket Tx - Validate send address", () => { 141 | it("invalid when address included in socket addresses", async () => { 142 | const tx = new SocketTx({ 143 | ...MOCK_TX, 144 | txTarget: "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d", // not socket address 145 | }); 146 | await tx.approvalRequired(); 147 | await expect(async () => tx.getSendTransaction()).rejects.toThrow(); 148 | }); 149 | }); 150 | -------------------------------------------------------------------------------- /src/socketTx.ts: -------------------------------------------------------------------------------- 1 | import { Bridge, ChainId } from "@socket.tech/ll-core"; 2 | import { BigNumber } from "ethers"; 3 | import { Approvals, NextTxResponse, Routes } from "./client"; 4 | import { PrepareActiveRouteStatus } from "./client/models/RouteStatusOutputDTO"; 5 | import { UserTxType } from "./client/models/UserTxType"; 6 | import { sleep } from "./utils"; 7 | 8 | // eslint-disable-next-line @typescript-eslint/no-empty-interface 9 | export interface SocketTx extends NextTxResponse {} 10 | 11 | /** 12 | * An entity representing the transaction prompted by the socket api 13 | */ 14 | export class SocketTx { 15 | /** 16 | * How often in ms to poll for status updates when checking the transaction 17 | */ 18 | private statusCheckInterval: number; 19 | /** 20 | * If the approval has been checked 21 | */ 22 | approvalChecked = false; 23 | /** 24 | * If the transaction is done 25 | */ 26 | done = false; 27 | /** 28 | * Hash associated with this socket transaction step 29 | */ 30 | hash: string | undefined; 31 | 32 | /** 33 | * @param nextTx The api object for the next transaction 34 | * @param statusCheckInterval How often in ms to poll for status updates when checking the transaction 35 | */ 36 | constructor(nextTx: NextTxResponse, statusCheckInterval = 10000) { 37 | Object.assign(this, nextTx); 38 | this.statusCheckInterval = statusCheckInterval; 39 | } 40 | 41 | /** 42 | * Whether an approval transaction is required. 43 | * @returns True if required, otherwise false. 44 | */ 45 | async approvalRequired() { 46 | this.approvalChecked = true; 47 | if (!this.approvalData) return false; 48 | 49 | const allowance = ( 50 | await Approvals.fetchApprovals({ 51 | chainId: this.chainId, 52 | owner: this.approvalData?.owner, 53 | allowanceTarget: this.approvalData?.allowanceTarget, 54 | tokenAddress: this.approvalData?.approvalTokenAddress, 55 | }) 56 | ).result; 57 | 58 | const allowanceValue = BigNumber.from(allowance.value); 59 | const minimumApprovalAmount = BigNumber.from(this.approvalData.minimumApprovalAmount); 60 | return allowanceValue.lt(minimumApprovalAmount); 61 | } 62 | 63 | private async _validateSend(send: { 64 | data?: string | undefined; 65 | to?: string | undefined; 66 | from?: string | undefined; 67 | }) { 68 | if (this.userTxType === UserTxType.FUND_MOVR) { 69 | // Fetch route. 70 | const routeResponse = await Routes.getActiveRoute({ activeRouteId: this.activeRouteId }); 71 | if (!routeResponse.success) throw new Error("Error while fetching route."); 72 | const route = routeResponse.result; 73 | 74 | // Current User Tx - Bridge Name 75 | const currentUserTx = route.userTxs[this.userTxIndex]; 76 | const bridgeStep = currentUserTx.steps.find((step) => step.type === "bridge"); 77 | if (bridgeStep) { 78 | const { name: protocolName } = bridgeStep.protocol; 79 | if (protocolName === Bridge.PolygonBridge && this.chainId === ChainId.POLYGON_CHAIN_ID) { 80 | const { address: fromAssetAddress } = bridgeStep.fromAsset; 81 | if (fromAssetAddress.toLowerCase() !== send.to?.toLowerCase()) { 82 | throw new Error( 83 | `Polygon Native Bridge burns the token when withdrawn to Ethereum. ${send.to} does not match the token being burnt.` 84 | ); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | /** 92 | * Get the apporval transaction data if it is required 93 | * @returns Apporval data to be sent if required, otherwise null 94 | */ 95 | async getApproveTransaction() { 96 | const approvalRequired = await this.approvalRequired(); 97 | if (!approvalRequired) { 98 | return null; 99 | } 100 | 101 | if (!this.approvalData) { 102 | return null; 103 | } 104 | 105 | const buildApproval = ( 106 | await Approvals.fetchApprovalsCalldata({ 107 | chainId: this.chainId, 108 | allowanceTarget: this.approvalData.allowanceTarget, 109 | amount: this.approvalData.minimumApprovalAmount, 110 | owner: this.approvalData.owner, 111 | tokenAddress: this.approvalData.approvalTokenAddress, 112 | }) 113 | ).result; 114 | 115 | return buildApproval; 116 | } 117 | 118 | /** 119 | * Get the transaction data 120 | * @returns Send transaction data 121 | */ 122 | async getSendTransaction() { 123 | if (!this.approvalChecked) { 124 | throw new Error( 125 | "Approval not checked. Check `getApproveTransaction` before attempting to send." 126 | ); 127 | } 128 | 129 | const tx = { 130 | to: this.txTarget, 131 | data: this.txData, 132 | value: this.value, 133 | }; 134 | 135 | await this._validateSend(tx); 136 | 137 | return tx; 138 | } 139 | 140 | /** 141 | * Get the latest status for the transaction 142 | * @param hash The hash for this transaction on the network 143 | * @returns The current status 144 | */ 145 | private async updateActiveRoute(hash: string) { 146 | const status = await Routes.updateActiveRoute({ 147 | activeRouteId: this.activeRouteId, 148 | userTxIndex: this.userTxIndex, 149 | txHash: hash, 150 | }); 151 | 152 | return status.result; 153 | } 154 | 155 | /** 156 | * Submit the hash for this transaction and wait until it is marked as complete 157 | * @param hash The hash for this transaction on the network 158 | * @returns Returns the final status "COMPLETED" once the transaction is complete 159 | */ 160 | async submit(hash: string) { 161 | if (this.hash) { 162 | throw new Error( 163 | `The transaction step ${this.userTxIndex}: ${this.userTxType} has hash already set to ${this.hash}` 164 | ); 165 | } 166 | this.hash = hash; 167 | for (;;) { 168 | const currentStatus = await this.updateActiveRoute(hash); 169 | const pending = currentStatus === PrepareActiveRouteStatus.PENDING; 170 | if (pending) { 171 | await sleep(this.statusCheckInterval); 172 | } else { 173 | this.done = true; 174 | return currentStatus; 175 | } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/tokenList.test.ts: -------------------------------------------------------------------------------- 1 | import { TokenList } from "."; 2 | import MOCK_TOKEN_LIST from "./mocks/mockTokenList.json"; 3 | 4 | describe("Token List", () => { 5 | let tokenList: TokenList; 6 | 7 | beforeEach(() => { 8 | tokenList = new TokenList(100, MOCK_TOKEN_LIST); 9 | }); 10 | 11 | it("created chain id", () => { 12 | expect(tokenList.chainId).toBe(100); 13 | expect(tokenList.nativeToken.chainId).toBe(100); 14 | }); 15 | 16 | it("native token", () => { 17 | expect(tokenList.nativeToken.chainId).toBe(100); 18 | expect(tokenList.nativeToken.address).toBe("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); 19 | expect(tokenList.nativeToken.symbol).toBe("XDAI"); 20 | }); 21 | 22 | it("tokenByAddress", () => { 23 | const usdc = tokenList.tokenByAddress("0xddafbb505ad214d7b80b1f830fccc89b60fb7a83"); 24 | expect(usdc?.address).toBe("0xddafbb505ad214d7b80b1f830fccc89b60fb7a83"); 25 | expect(usdc?.chainId).toBe(100); 26 | expect(usdc?.symbol).toBe("USDC"); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/tokenList.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "@socket.tech/ll-core"; 2 | import { Token } from "."; 3 | import { NATIVE_TOKEN_ADDRESS } from "./constants"; 4 | 5 | /** 6 | * The TokenList represents a list of tokens for a given chain 7 | */ 8 | export class TokenList { 9 | tokens: Array; 10 | chainId: ChainId; 11 | 12 | constructor(chainId: ChainId, tokens: Array) { 13 | this.tokens = tokens; 14 | this.chainId = chainId; 15 | } 16 | 17 | /** 18 | * The native token of the chain 19 | */ 20 | get nativeToken() { 21 | const token = this.tokens.find((token) => token.address.toLowerCase() === NATIVE_TOKEN_ADDRESS); 22 | if (!token) throw new Error("No native token found"); 23 | return token; 24 | } 25 | 26 | /** 27 | * Retrieve token by its address 28 | * @param address token address 29 | * @returns token object 30 | */ 31 | tokenByAddress(address: string) { 32 | return this.tokens.find((token) => token.address.toLowerCase() === address.toLowerCase()); 33 | } 34 | 35 | /** 36 | * Retrieve token by its symbol 37 | * @param symbol symbol. Example: USDC 38 | * @returns token object 39 | */ 40 | tokenBySymbol(symbol: string) { 41 | return this.tokens.find((token) => token.symbol === symbol); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { Route } from "./client"; 2 | import { BridgeRouteErrors } from "./client/models/BridgeRouteErrors"; 3 | import { QuotePreferences } from "./client/models/QuoteRequest"; 4 | import { RefuelData } from "./client/models/RefuelData"; 5 | import { Path } from "./path"; 6 | 7 | /** 8 | * The parameters for a quote request 9 | */ 10 | export interface QuoteParams { 11 | /** The path desired */ 12 | path: Path; 13 | /** Amount of the quote */ 14 | amount: string; 15 | /** User address */ 16 | address: string; 17 | } 18 | 19 | /** 20 | * Quote parameters and the retrieved route 21 | */ 22 | export interface SocketQuote extends QuoteParams { 23 | /** The route retrieved for the quote */ 24 | route: Route; 25 | /** Refuel Data */ 26 | refuel?: RefuelData; 27 | /** Errors */ 28 | errors: BridgeRouteErrors; 29 | } 30 | 31 | /** Sdk options */ 32 | export interface SocketOptions { 33 | /** The socket api key */ 34 | apiKey: string; 35 | /** How often in ms to poll for status updates when checking transactions */ 36 | statusCheckInterval?: number; 37 | /** The preferences used when retrieving quotes from the api */ 38 | defaultQuotePreferences?: QuotePreferences; 39 | /** Optional Base URL for the API */ 40 | baseUrl?: string; 41 | } 42 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Async sleep for a duration. 3 | * @param millis Number of millisedconds to sleep 4 | * @returns When the sleep time is finished 5 | */ 6 | export async function sleep(millis: number) { 7 | return new Promise((resolve) => setTimeout(resolve, millis)); 8 | } 9 | -------------------------------------------------------------------------------- /src/web3ConnectedSocket.ts: -------------------------------------------------------------------------------- 1 | import type { Web3Provider } from "@ethersproject/providers"; 2 | import { ChainId } from "@socket.tech/ll-core"; 3 | import { ethers } from "ethers"; 4 | import { SocketOptions, SocketQuote } from "./types"; 5 | import { SocketTx } from "."; 6 | import { ActiveRouteGenerator, BaseSocket } from "./baseSocket"; 7 | 8 | export interface AddEthereumChainParameters { 9 | chainId: string; // A 0x-prefixed hexadecimal string 10 | chainName: string; 11 | nativeCurrency: { 12 | name: string; 13 | symbol: string; // 2-6 characters long 14 | decimals: number; 15 | }; 16 | rpcUrls: string[]; 17 | blockExplorerUrls?: string[]; 18 | iconUrls?: string[]; // Currently ignored. 19 | } 20 | 21 | /** Callback when a socket transaction is complete. */ 22 | export type SocketTxDoneCallback = (tx: SocketTx) => void; 23 | /** Callback when a transaction for send/approval has completed. */ 24 | export type TxDoneCallback = (tx: SocketTx, hash: string) => void; 25 | /** Callback when chain switch has completed. */ 26 | export type ChainSwitchDoneCallback = (chainId: ChainId) => void; 27 | 28 | export interface EventCallbacks { 29 | /** Callback when a new socket transaction has begun. */ 30 | onTx?: (tx: SocketTx) => SocketTxDoneCallback | void; 31 | /** Callback when an approval is being requested. */ 32 | onApprove?: (tx: SocketTx) => TxDoneCallback | void; 33 | /** Callback when a send transaction is being requested. */ 34 | onSend?: (tx: SocketTx) => TxDoneCallback | void; 35 | /** Callback when switching chains is being requested. */ 36 | onChainSwitch?: (fromChainId: ChainId, toChainId: ChainId) => ChainSwitchDoneCallback | void; 37 | /** Callback when the route execution has completed. */ 38 | onDone?: (activerouteId: number) => void; 39 | } 40 | 41 | /** 42 | * @inheritdoc 43 | * 44 | * The connected socket sdk interfaces directly with wallets 45 | */ 46 | export class Web3ConnectedSocket extends BaseSocket { 47 | readonly _provider: Web3Provider; 48 | 49 | constructor(options: SocketOptions, provider: Web3Provider) { 50 | super(options); 51 | this._provider = provider; 52 | } 53 | 54 | /** 55 | * Switch to the desired network 56 | * @param chainId chain 57 | */ 58 | private async _switchNetwork(chainId: ChainId) { 59 | const chain = await this.getChain(chainId); 60 | try { 61 | await this._provider.send("wallet_switchEthereumChain", [ 62 | { chainId: ethers.utils.hexlify(chainId) }, 63 | ]); 64 | } catch (switchError: any) { 65 | // This error code indicates that the chain has not been added to MetaMask. 66 | if (switchError.code === 4902) { 67 | try { 68 | const addPayload: AddEthereumChainParameters = { 69 | chainId: ethers.utils.hexlify(chainId), 70 | chainName: chain.name, 71 | nativeCurrency: { 72 | name: chain.currency.name, 73 | symbol: chain.currency.symbol, 74 | decimals: chain.currency.decimals, 75 | }, 76 | rpcUrls: chain.rpcs, 77 | blockExplorerUrls: chain.explorers, 78 | iconUrls: [chain.icon], 79 | }; 80 | await this._provider.send("wallet_addEthereumChain", [addPayload]); 81 | } catch (addError: any) { 82 | throw new Error(`Failed to switch to ${chainId}: ${addError}`); 83 | } 84 | } 85 | } 86 | } 87 | 88 | /** 89 | * Ensure that the provider is on the given chain 90 | * @param chainId chain 91 | * @param onChainSwitch Callback for chain switching 92 | */ 93 | private async _ensureChain(chainId: ChainId, onChainSwitch: EventCallbacks["onChainSwitch"]) { 94 | const network = await this._provider.getNetwork(); 95 | if (network.chainId !== chainId) { 96 | const doneCallback = onChainSwitch && onChainSwitch(network.chainId, chainId); 97 | await this._switchNetwork(chainId); 98 | if (doneCallback) doneCallback(chainId); 99 | } 100 | } 101 | 102 | /** Execute the quote */ 103 | private async _execute(iterator: ActiveRouteGenerator, callbacks: EventCallbacks) { 104 | let next = await iterator.next(); 105 | 106 | while (!next.done && next.value) { 107 | const tx = next.value; 108 | const txDoneCallback = callbacks.onTx && callbacks.onTx(tx); 109 | 110 | const approvalTxData = await tx.getApproveTransaction(); 111 | if (approvalTxData) { 112 | await this._ensureChain(tx.chainId, callbacks.onChainSwitch); 113 | const approveCallback = callbacks.onApprove && callbacks.onApprove(tx); 114 | const approvalTx = await this._provider.getSigner().sendTransaction(approvalTxData); 115 | if (approveCallback) approveCallback(tx, approvalTx.hash); 116 | await approvalTx.wait(); 117 | } 118 | 119 | const sendTxData = await tx.getSendTransaction(); 120 | await this._ensureChain(tx.chainId, callbacks.onChainSwitch); 121 | const sendCallback = callbacks.onSend && callbacks.onSend(tx); 122 | const sendTx = await this._provider.getSigner().sendTransaction(sendTxData); 123 | if (sendCallback) sendCallback(tx, sendTx.hash); 124 | await sendTx.wait(); 125 | 126 | next = await iterator.next(sendTx.hash); 127 | if (txDoneCallback) txDoneCallback(tx); 128 | } 129 | 130 | if (callbacks.onDone) callbacks.onDone(iterator.activeRouteId); 131 | } 132 | 133 | /** 134 | * Start executing the quote on the provider 135 | * @param quote The quote to execute 136 | * @param callbacks optional callbacks for different states of the execution 137 | */ 138 | async start(quote: SocketQuote, callbacks: EventCallbacks): Promise { 139 | const iterator = await this._startQuote(quote); 140 | this._execute(iterator, callbacks); 141 | return iterator.activeRouteId; 142 | } 143 | 144 | /** 145 | * Continue an active route 146 | * @param activeRouteId The active route id of the desired route to continue 147 | * @param callbacks optional callbacks for different states of the execution 148 | */ 149 | async continue(activeRouteId: number, callbacks: EventCallbacks) { 150 | const iterator = await this._continueRoute(activeRouteId); 151 | await this._execute(iterator, callbacks); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "declaration": true, 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "strict": false, 8 | "allowSyntheticDefaultImports": true, 9 | "noImplicitReturns": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "importHelpers": true, 13 | "sourceMap": true, 14 | "resolveJsonModule": true, 15 | "esModuleInterop": true, 16 | "rootDir": ".", 17 | "outDir": "lib" 18 | }, 19 | "include": ["src/**/*"] 20 | } 21 | --------------------------------------------------------------------------------