diff --git a/configuration/common.ts b/configuration/common.ts new file mode 100644 index 0000000..7112954 --- /dev/null +++ b/configuration/common.ts @@ -0,0 +1,51 @@ +import config from './config'; + +type ChainConfig = { + chains: { + [key: string]: { + localName: string; + chainName: string; + chainSymbol: string; + }; + }; +}; + +type Options = { + chainId?: string | number | null; + chainSymbol?: string | null; +}; + +const getChainIdFromChainSymbol = async (chainSymbol: string): Promise => { + const upperCaseChainSymbol = chainSymbol.toUpperCase(); + + for (const chain in (config as ChainConfig).chains) { + if ((config as ChainConfig).chains[chain].chainSymbol.toUpperCase() === upperCaseChainSymbol) { + return chain; + } + } + + return null; +} + +export const getChainId = async (options: Options): Promise => { + /* + * This function returns the appropriate chainId + * for the given combination of chainId and chainSymbol + */ + + let chainId: string | null = options.chainId ? options.chainId.toString() : null; + const chainSymbol: string | null = options.chainSymbol ? options.chainSymbol.toUpperCase() : null; + + if (chainId === null && chainSymbol === null) { + // By default setting it to EVM based chains + chainId = "1"; + } else if (chainId === null && chainSymbol != null) { + // Fetch the equivalent chain ID from the configuration file + chainId = await getChainIdFromChainSymbol(chainSymbol); + } else if (chainId != null) { + // Ensure chainId is a string + chainId = chainId.toString(); + } + + return chainId; +}; diff --git a/configuration/config.json b/configuration/config.json index 2d5074f..bbc785c 100644 --- a/configuration/config.json +++ b/configuration/config.json @@ -1,3 +1,4 @@ + { "chains": { "1": { @@ -444,3 +445,4 @@ } } } + diff --git a/configuration/config.ts b/configuration/config.ts new file mode 100644 index 0000000..ee4805a --- /dev/null +++ b/configuration/config.ts @@ -0,0 +1,507 @@ +type ChainConfig = { + localName: string; + chainName: string; + chainSymbol: string; + rpc?: string; + publicRpc?: string; + aptosCoin?: string; + nativeEthAddress?: string; + domain?: string; + sslMateId?: string; + apiKey?: string; + url?: string; + networkPassphrase?: string; + gasPrice?: string; + network?: string; + fee?: number; + }; + + type FireblocksConfig = { + baseUrl:string; + createTransaction: string; + } + + type CircleProgrammableWalletConfig = { + baseUrl:string; + } + type UrlConfig = { + apiurl:string; + } + + type DexesConfig = { + localName?: string; + chainName: string; + chainId: string; + dexName: string; + localDexName?: string; + }; + + type DYdXConfig = { + chainId: string; + USDC : string; + MAX_CLIENT_ID : any ; + squidRouterAPIBaseUrl : string ; + signingMsg: any; + rpc : string; + }; + + + type Config = { + chains: Record; + fireblocks: FireblocksConfig; + circleProgrammableWallet : CircleProgrammableWalletConfig; + url: UrlConfig; + Mask250: string; + dexes : Record; + dYdXV4: DYdXConfig; + }; + + +const config: Config = { + "chains": { + "1": { + "localName": "Ethereum", + "chainName": "Evm", + "chainSymbol": "ETH" + }, + "3": { + "localName": "EthereumTestnetRopsten", + "chainName": "Evm", + "chainSymbol": "TETHRPT" + }, + "4": { + "localName": "EthereumTestnetRinkeby", + "chainName": "Evm", + "chainSymbol": "TETHRNK" + }, + "5": { + "localName": "EthereumTestnetGoerli", + "chainName": "Evm", + "chainSymbol": "TETHGRL" + }, + "42": { + "localName": "EthereumTestnetKovan", + "chainName": "Evm", + "chainSymbol": "TETHKVN" + }, + "17000": { + "localName": "EthereumTestnetHolesky", + "chainName": "Evm", + "chainSymbol": "TETHHSKY" + }, + "56": { + "localName": "BinanceSmartChain", + "chainName": "Evm", + "chainSymbol": "BSC" + }, + "97": { + "localName": "BinanceSmartChainTestnet", + "chainName": "Evm", + "chainSymbol": "TBSC" + }, + "43114": { + "localName": "Avalanche", + "chainName": "Evm", + "chainSymbol": "AVAL" + }, + "43113": { + "localName": "AvalancheTesnet", + "chainName": "Evm", + "chainSymbol": "TAVAL" + }, + "137": { + "localName": "Polygon", + "chainName": "Evm", + "chainSymbol": "MATIC" + }, + "80001": { + "localName": "PolygonTestnet", + "chainName": "Evm", + "chainSymbol": "TMATIC" + }, + "25": { + "localName": "Cronos", + "chainName": "Evm", + "chainSymbol": "CRO" + }, + "338": { + "localName": "CronosTestnet", + "chainName": "Evm", + "chainSymbol": "TCRO" + }, + "42161": { + "localName": "Arbitrum", + "chainName": "Evm", + "chainSymbol": "AETH" + }, + "421611": { + "localName": "ArbitrumTestnet", + "chainName": "Evm", + "chainSymbol": "TAETH" + }, + "250": { + "localName": "Fantom", + "chainName": "Evm", + "chainSymbol": "FTM" + }, + "4002": { + "localName": "FantomTestnet", + "chainName": "Evm", + "chainSymbol": "TFTM" + }, + "324": { + "localName": "zkSync", + "chainName": "Evm", + "chainSymbol": "ZKS" + }, + "300": { + "localName": "zkSync", + "chainName": "Evm", + "chainSymbol": "TZKS" + }, + "900": { + "localName": "Solana", + "chainName": "Solana", + "chainSymbol": "SOL" + }, + "901": { + "localName": "SolanaDevnet", + "chainName": "Solana", + "chainSymbol": "DSOL" + }, + "1000": { + "localName": "Tron", + "chainName": "Tron", + "chainSymbol": "TRON" + }, + "1001": { + "localName": "TronTestnet", + "chainName": "Tron", + "chainSymbol": "TTRON" + }, + "1200": { + "localName": "Near", + "chainName": "Near", + "chainSymbol": "NEAR" + }, + "1201": { + "localName": "NearTestnet", + "chainName": "Near", + "chainSymbol": "TNEAR" + }, + "1300": { + "localName": "Algorand", + "chainName": "Algorand", + "chainSymbol": "ALGO" + }, + "1301": { + "localName": "AlgorandTestnet", + "chainName": "Algorand", + "chainSymbol": "TALGO" + }, + "10": { + "localName": "Optimism", + "chainName": "Evm", + "chainSymbol": "OPT" + }, + "101": { + "localName": "Sui", + "chainName": "Sui", + "chainSymbol": "SUI", + "rpc": "https://sui-mainnet-rpc.nodereal.io", + "publicRpc": "https://fullnode.mainnet.sui.io:443" + }, + "102": { + "localName": "SuiDevnet", + "chainName": "Sui", + "chainSymbol": "DSUI", + "rpc": "https://fullnode.devnet.sui.io:443", + "publicRpc": "https://fullnode.devnet.sui.io:443" + }, + "103": { + "localName": "SuiTestnet", + "chainName": "Sui", + "chainSymbol": "TSUI", + "rpc": "https://fullnode.testnet.sui.io:443", + "publicRpc": "https://fullnode.testnet.sui.io:443" + }, + "1400": { + "localName": "Aptos", + "chainName": "Aptos", + "chainSymbol": "APT", + "aptosCoin": "0x1::aptos_coin::AptosCoin", + "rpc": "https://fullnode.mainnet.aptoslabs.com", + "publicRpc": "https://fullnode.mainnet.aptoslabs.com" + }, + "1401": { + "localName": "AptosTestnet", + "chainName": "Aptos", + "chainSymbol": "TAPT", + "aptosCoin": "0x1::aptos_coin::AptosCoin", + "rpc": "https://fullnode.testnet.aptoslabs.com", + "publicRpc": "https://fullnode.testnet.aptoslabs.com" + }, + "400": { + "localName": "StarkNet", + "chainName": "StarkNet", + "chainSymbol": "STRK", + "nativeEthAddress": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + "rpc": "https://starknet-mainnet.blastapi.io/efc44012-502c-4b57-a7b2-f7e7a44535f5", + "publicRpc": "https://starknet-mainnet.blastapi.io/efc44012-502c-4b57-a7b2-f7e7a44535f5", + "domain": "infura.io", + "sslMateId": "4054117900" + }, + "401": { + "localName": "StarkNetTestnet", + "chainName": "StarkNet", + "chainSymbol": "TSTRK", + "nativeEthAddress": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + "rpc": "https://starknet-testnet.blastapi.io/a0c768c4-cd06-4dfc-8a5c-a6a7a0716e2a", + "publicRpc": "https://starknet-testnet.blastapi.io/efc44012-502c-4b57-a7b2-f7e7a44535f5", + "domain": "infura.io", + "sslMateId": "4054117900" + }, + "1100": { + "localName": "TON", + "chainName": "TON", + "chainSymbol": "TON", + "rpc": "https://toncenter.com/api/v2/jsonRPC", + "publicRpc": "https://toncenter.com/api/v2/jsonRPC", + "apiKey": "f5dcc14824318f08f20c7c10b29a3eaac29b9d0c73609f10c1d8f125dd3f6cd2" + }, + "1101": { + "localName": "TONTestnet", + "chainName": "TON", + "chainSymbol": "TONTest", + "rpc": "https://testnet.toncenter.com/api/v2/jsonRPC", + "publicRpc": "https://testnet.toncenter.com/api/v2/jsonRPC", + "apiKey": "f5dcc14824318f08f20c7c10b29a3eaac29b9d0c73609f10c1d8f125dd3f6cd2" + }, + "8453": { + "localName": "Base", + "chainName": "Evm", + "chainSymbol": "BASE", + "rpc": "https://mainnet.base.org", + "publicRpc": "https://mainnet.base.org", + "domain": "base.org" + }, + "84532": { + "localName": "BaseTestnet2", + "chainName": "Evm", + "chainSymbol": "TBASE2", + "rpc": "https://sepolia.base.org", + "publicRpc": "https://sepolia.base.org", + "domain": "base.org" + }, + "11155111": { + "localName": "EthereumTestnetSepolia", + "chainName": "Evm", + "chainSymbol": "TETHSPL", + "rpc": "https://sepolia.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161", + "publicRpc": "https://sepolia.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161", + "domain": "infura.io" + }, + "421614": { + "localName": "ArbitrumSepoliaTestnet", + "chainName": "Evm", + "chainSymbol": "TASPL", + "rpc": "https://sepolia-rollup.arbitrum.io/rpc", + "publicRpc": "https://sepolia-rollup.arbitrum.io/rpc", + "domain": "arbitrum.io" + }, + "11155420": { + "localName": "OptimismSepoliaTestnet", + "chainName": "Evm", + "chainSymbol": "TSPL", + "rpc": "https://sepolia.optimism.io", + "publicRpc": "https://sepolia.optimism.io", + "domain": "optimism.io" + }, + "1500": { + "localName": "Stellar", + "chainName": "Stellar", + "chainSymbol": "XLM", + "rpc": "https://horizon.stellar.org", + "networkPassphrase": "Public Global Stellar Network ; September 2015" + }, + "1501": { + "localName": "StellarTestnet", + "chainName": "Stellar", + "chainSymbol": "TXLM", + "networkPassphrase": "Test SDF Network ; September 2015", + "rpc": "https://horizon-testnet.stellar.org" + }, + "cosmoshub-4": { + "localName": "cosmoshub_mainnet", + "chainName": "Cosmos", + "chainSymbol": "cosmos", + "rpc": "https://cosmos-rpc.publicnode.com:443", + "publicRpc": "https://cosmos-rpc.publicnode.com:443", + "gasPrice": "3000" + }, + "theta-testnet-001": { + "localName": "cosmoshub_testnet", + "chainName": "Cosmos", + "chainSymbol": "cosmos", + "rpc": "https://rpc.sentry-01.theta-testnet.polypore.xyz", + "publicRpc": "https://rpc.sentry-01.theta-testnet.polypore.xyz", + "gasPrice": "3000" + }, + "1600": { + "localName": "XRPLedger", + "chainName": "XRPL", + "chainSymbol": "XRPL", + "network": "mainnet" + }, + "1601": { + "localName": "XRPLedgerTestnet", + "chainName": "XRPL", + "chainSymbol": "TXRPL", + "network": "testnet" + }, + "1700": { + "localName": "Stacks", + "chainName": "Stacks", + "chainSymbol": "STX", + "network": "mainnet" + }, + "1701": { + "localName": "StacksTestnet", + "chainName": "Stacks", + "chainSymbol": "TSTX", + "network": "testnet" + }, + "1800": { + "localName": "Bitcoin", + "chainName": "Bitcoin", + "chainSymbol": "BTC", + "network": "mainnet", + "fee": 2000 + }, + "1801": { + "localName": "BitcoinTestnet", + "chainName": "Bitcoin", + "chainSymbol": "TBTC", + "network": "testnet", + "fee": 2000 + } + }, + "fireblocks": { + "baseUrl": "https://api.fireblocks.io", + "createTransaction": "/v1/transactions" + }, + "circleProgrammableWallet": { + "baseUrl": "https://api.circle.com/v1/w3s/user/transactions/" + }, + "Mask250": "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "url": { + "apiurl" : "https://api.expand.network" + }, + "dexes": { + "1900": { + "localName": "UniswapX", + "dexName": "UniswapX", + "chainName": "Ethereum", + "chainId": "1" + }, + "1901": { + "localName": "UniswapXTestnet", + "dexName": "UniswapX", + "chainName": "Ethereum", + "chainId": "5" + }, + "2200": { + "dexName": "Kyberswap", + "localDexName": "Kyberswap", + "chainName": "Ethereum", + "chainId": "1" + }, + "2201": { + "dexName": "Kyberswap", + "localDexName": "KyberswapBsc", + "chainName": "Ethereum", + "chainId": "56" + }, + "2202": { + "dexName": "Kyberswap", + "localDexName": "KyberswapPolygon", + "chainName": "Ethereum", + "chainId": "137" + }, + "2203": { + "dexName": "Kyberswap", + "localDexName": "KyberswapArbitrum", + "chainName": "Ethereum", + "chainId": "42161" + }, + "2204": { + "dexName": "Kyberswap", + "localDexName": "KyberswapAvalanche", + "chainName": "Ethereum", + "chainId": "43114" + }, + "2205": { + "dexName": "Kyberswap", + "localDexName": "KyberswapOptimism", + "chainName": "Ethereum", + "chainId": "10" + }, + "2206": { + "dexName": "Kyberswap", + "localDexName": "KyberswapBase", + "chainName": "Ethereum", + "chainId": "8453" + }, + "2207": { + "dexName": "Kyberswap", + "localDexName": "KyberswapFantom", + "chainName": "Ethereum", + "chainId": "250" + }, + "2208": { + "dexName": "Kyberswap", + "localDexName": "KyberswapCronos", + "chainName": "Ethereum", + "chainId": "25" + } + }, + + "dYdXV4": { + "chainId": "dydx-testnet-4", + "USDC": "ibc/8E27BA2D5493AF5636760E354E46004562C46AB7EC0CC4C1CA14E9E20E2545B5", + "MAX_CLIENT_ID": 4294967295, + "squidRouterAPIBaseUrl": "https://testnet.api.squidrouter.com/v1/", + "rpc": "https://sepolia.infura.io/v3/fc5d23096e754d64a5f261f5f07170d5", + "signingMsg": { + "domain": { + "name": "dYdX V4", + "chainId": 11155111 + }, + "primaryType": "dYdX", + "types": { + "EIP712Domain": [ + { + "name": "name", + "type": "string" + }, + { + "name": "chainId", + "type": "uint256" + } + ], + "dYdX": [ + { + "name": "action", + "type": "string" + } + ] + }, + "message": { + "action": "dYdX Chain Onboarding" + } + } + } +}; + +export default config; + diff --git a/configuration/dYdXCommon.ts b/configuration/dYdXCommon.ts new file mode 100644 index 0000000..2a9ea28 --- /dev/null +++ b/configuration/dYdXCommon.ts @@ -0,0 +1,37 @@ +import { + LocalWallet, + Network, + CompositeClient, + BECH32_PREFIX, + SubaccountClient, + ValidatorClient, + } from "@dydxprotocol/v4-client-js"; + + export const getSubAccountCompositeClient = async ( + mnemonic: string, + subAccountNumber: number + ): Promise<{ + client: CompositeClient; + subaccount: SubaccountClient; + }> => { + const wallet = await LocalWallet.fromMnemonic(mnemonic, BECH32_PREFIX); + const client = await CompositeClient.connect(Network.testnet()); + + const subaccount = new SubaccountClient(wallet, subAccountNumber); + return { client, subaccount }; + }; + + export const getSubAccountValidatorClient = async ( + mnemonic: string, + subAccountNumber: number + ): Promise<{ + client: ValidatorClient; + subaccount: SubaccountClient; + }> => { + const wallet = await LocalWallet.fromMnemonic(mnemonic, BECH32_PREFIX); + const client = await ValidatorClient.connect(Network.testnet().validatorConfig); + + const subaccount = new SubaccountClient(wallet, subAccountNumber); + return { client, subaccount }; + }; + \ No newline at end of file diff --git a/configuration/errorMessage.ts b/configuration/errorMessage.ts new file mode 100644 index 0000000..a26d254 --- /dev/null +++ b/configuration/errorMessage.ts @@ -0,0 +1,43 @@ +interface ErrorMessages { + invalidInput: string; + notApplicable: string; + invalidChainId: string; + invalidDexId: string; + invalidLendBorrowId: string; + invalidYieldAggregatorId: string; + invalidSrcToken: string; + invalidOrderId: string; +} + +interface ErrorCodes { + invalidInput: number; + notApplicable: number; +} + +interface ErrorResponse { + error: { + message: ErrorMessages; + code: ErrorCodes; + }; +} + +const errorResponse: ErrorResponse = { + error: { + message: { + invalidInput: "Please provide proper input data", + notApplicable: "Given function is not applicable", + invalidChainId: "Invalid chain ID", + invalidDexId: "Invalid Dex Id", + invalidLendBorrowId: "Invalid Lend Borrow Id", + invalidYieldAggregatorId: "Invalid Yield Aggregator Id", + invalidSrcToken: "Invalid/Unsupported Src Token", + invalidOrderId: "Invalid Order Id" + }, + code: { + invalidInput: 400, + notApplicable: 406 + } + } +}; + +export default errorResponse; \ No newline at end of file diff --git a/configuration/intialiseWeb3.ts b/configuration/intialiseWeb3.ts new file mode 100644 index 0000000..020e967 --- /dev/null +++ b/configuration/intialiseWeb3.ts @@ -0,0 +1,79 @@ +import EvmWeb from "web3"; +import solanaWeb from "@solana/web3.js"; +import TronWeb from "tronweb"; +import nearApi from "near-api-js"; +import algosdk from "algosdk"; +import { JsonRpcProvider, Connection } from "@mysten/sui.js"; +import { AptosClient } from "aptos"; +import { TonClient } from "@ton/ton"; +import { StargateClient } from "@cosmjs/stargate"; +import StellarSdk from "stellar-sdk"; +import * as common from "./common"; +import config from "./config"; +import errorMessage from "./errorMessage"; + +import Server from "stellar-sdk"; + +const invalidChainId = { + error: errorMessage.error.message.invalidChainId, + code: errorMessage.error.code.invalidInput +}; + +export const initialiseWeb3 = async (data: any) => { + const chainId = await common.getChainId({ + chainId: data.chainId, + chainSymbol: data.chainSymbol + }); + + if (!chainId) { + return invalidChainId; // Return an error if chainId is null + } + + let rpc:any; + let chainName:any; + + try { + rpc = data.rpc || config.chains[chainId].rpc; + chainName = config.chains[chainId].chainName; + } catch (error) { + return invalidChainId; + } + + let web3:any; + + if (chainName === "Evm") { + web3 = new EvmWeb(rpc); + } else if (chainName === "Solana") { + web3 = new solanaWeb.Connection(rpc); + } else if (chainName === "Tron") { + const HttpProvider = TronWeb.providers.HttpProvider; + const fullNode = new HttpProvider(rpc); + const solidityNode = new HttpProvider(rpc); + const eventServer = new HttpProvider(rpc); + web3 = new TronWeb(fullNode, solidityNode, eventServer); + } else if (chainName === "Near") { + web3 = await nearApi.connect({ + networkId: data.networkId, + nodeUrl: rpc + }); + } else if (chainName === "Algorand") { + const token = { "x-api-key": data.key }; + web3 = + data.connectionType === "idx" + ? new algosdk.Indexer(token, rpc, "") + : new algosdk.Algodv2(token, rpc, ""); + } else if (chainName === "Sui") { + const connection = new Connection({ fullnode: rpc }); + web3 = new JsonRpcProvider(connection); + } else if (chainName === "Aptos") { + web3 = new AptosClient(rpc); + } else if (chainName === "TON") { + web3 = new TonClient({ endpoint: rpc, apiKey: config.chains[chainId]?.apiKey }); + } else if (chainName === "Stellar") { + web3 = new StellarSdk.Server(rpc); + } else if (chainName === "Cosmos") { + web3 = await StargateClient.connect(rpc); + } + + return web3; +}; diff --git a/configuration/schema.js b/configuration/schema.js index c00729c..fed663c 100644 --- a/configuration/schema.js +++ b/configuration/schema.js @@ -69,7 +69,7 @@ exports.jsonSchema = { networkId: { type: "string" } }, - required: ["from", "to"] + required: ["from"] } }, diff --git a/configuration/schema.ts b/configuration/schema.ts new file mode 100644 index 0000000..6e05d2f --- /dev/null +++ b/configuration/schema.ts @@ -0,0 +1,169 @@ +/** + * JSON Schema Definition + * For the functions under chain category, + * the validation is set on the function level. + * For example, for function `getBlock()`, the request will be the same, regardless of the chain ID and symbol. + * Whereas for all the other categories, validations are broken one level down, i.e., to the protocol level. + */ + +export const jsonSchema: { + type: string; + allOf: Array<{ + if: { + properties: { + function: { + type: string; + pattern: string; + }; + }; + }; + then: { + properties: Record; + required: string[]; + }; + }>; + } = { + type: "object", + allOf: [ + { + if: { + properties: { + function: { type: "string", pattern: "prepareTransaction()" }, + }, + }, + then: { + properties: { + chainId: { type: "string" }, + chainSymbol: { type: "string" }, + xApiKey: { type: "string" }, + rpc: { type: "string" }, + }, + required: ["xApiKey"], + }, + }, + { + if: { + properties: { + function: { type: "string", pattern: "signTransaction()" }, + }, + }, + then: { + properties: { + rpc: { type: "string" }, + privateKey: { type: "string" }, + chainId: { type: "string" }, + chainSymbol: { type: "string" }, + key: { type: "string" }, + xApiKey: { type: "string" }, + }, + required: ["xApiKey"], + }, + }, + { + if: { + properties: { + function: { type: "string", pattern: "transactionObject()" }, + }, + }, + then: { + properties: { + from: { type: "string" }, + to: { type: "string" }, + value: { type: "string" }, + gas: { type: "string" }, + data: { type: "string" }, + networkId: { type: "string" }, + }, + required: ["from"], + }, + }, + { + if: { + properties: { + function: { type: "string", pattern: "tonTxObject()" }, + }, + }, + then: { + properties: { + from: { type: "string" }, + to: { type: "string" }, + value: { type: "string" }, + gas: { type: "string" }, + data: { type: "string" }, + }, + required: ["to", "value"], + }, + }, + { + if: { + properties: { + function: { type: "string", pattern: "txObjSol()" }, + }, + }, + then: { + properties: { + from: { type: "string" }, + to: { type: "string" }, + value: { type: "string" }, + gas: { type: "string" }, + data: { type: "string" }, + }, + required: ["from", "to"], + }, + }, + { + if: { + properties: { + function: { type: "string", pattern: "sendTransaction()" }, + }, + }, + then: { + properties: { + chainId: { type: "string" }, + chainSymbol: { type: "string", maxLength: 7, minLength: 3 }, + rawTransaction: { type: "string" }, + signature: { type: "string" }, + xApiKey: { type: "string" }, + rpc: { type: "string" }, + }, + required: ["rawTransaction"], + }, + }, + { + if: { + properties: { + function: { type: "string", pattern: "TONTransaction()" }, + }, + }, + then: { + properties: { + chainId: { type: "string" }, + chainSymbol: { type: "string", maxLength: 7, minLength: 3 }, + rawTransaction: { type: "object" }, + xApiKey: { type: "string" }, + rpc: { type: "string" }, + }, + required: ["rawTransaction"], + }, + }, + { + if: { + properties: { + function: { type: "string", pattern: "FordefiTransaction()" }, + }, + }, + then: { + properties: { + data: { type: "string" }, + timestamp: { type: "number" }, + signature: { type: "string" }, + accessToken: { type: "string" }, + }, + required: ["data", "signature", "accessToken", "timestamp"], + }, + }, + // Add the rest of the JSON schema mappings here + // Following the same pattern as the mappings above + ], + }; + \ No newline at end of file diff --git a/configuration/schemaValidator.ts b/configuration/schemaValidator.ts new file mode 100644 index 0000000..03904c5 --- /dev/null +++ b/configuration/schemaValidator.ts @@ -0,0 +1,40 @@ +// Copyright 2022 The CmLab Authors +// This file is part of the expand Library. + +import Ajv from "ajv"; +import addErrors from "ajv-errors"; +import errorMessage from "./errorMessage"; +import { jsonSchema } from "./schema"; + +const ajv = new Ajv({ allErrors: true, strict: true, useDefaults: true }); +addErrors(ajv); + +export const validateInput = async(options:any) => { + /* + * This functions validate the given options as per the schema + * Returns the validate response as of following + * + * { + * "valid": false, + * "message" : { + * "error": { + * missingProperty: 'address' + * } + * } + * "code" : 401 + * } + */ + + const validate = ajv.compile(jsonSchema); + const valid = validate(options); + const error = valid ? null : (validate.errors && validate.errors[0]?.params); + const response: any = {}; + response.valid = valid; + + if (!valid) { + response.message = error || { error: "Unknown validation error" }; + response.code = errorMessage.error.code.invalidInput; + } + + return (response); +}; \ No newline at end of file diff --git a/configuration/squidRouterTokenConfig.ts b/configuration/squidRouterTokenConfig.ts new file mode 100644 index 0000000..bd2bb5c --- /dev/null +++ b/configuration/squidRouterTokenConfig.ts @@ -0,0 +1,57 @@ +type TokenMap = { + [key: string]: string; +}; + +type NetworkTokenData = { + [networkId: string]: TokenMap; +}; + +const networkTokenData: NetworkTokenData = { + "5": { + ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + WETH: "0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6", + DAI: "0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60", + AUSDC: "0x254d06f33bDc5b8ee05b2ea472107E300226659A", + WSTETH: "0x6320cd32aa674d2898a68ec82e869385fc5f7e2f", + }, + "97": { + BNB: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + WBNB: "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd", + AUSDC: "0xc2fA98faB811B785b81c64Ac875b31CC9E40F9D2", + }, + "80001": { + MATIC: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + WMATIC: "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889", + AUSDC: "0x2c852e740B62308c46DD29B982FBb650D063Bd07", + AXLETH: "0x786D82A436EA836A8669919D605FfeaEFa51744e", + }, + "420": { + ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + WETH: "0x4200000000000000000000000000000000000006", + AUSDC: "0x254d06f33bDc5b8ee05b2ea472107E300226659A", + }, + "43113": { + AVAX: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + WAVAX: "0xd00ae08403B9bbb9124bB305C09058E32C39A48c", + AXL: "0x46Cc87ea84586C03bB2109ED9B33F998d40B7623", + AUSDC: "0x57f1c63497aee0be305b8852b354cec793da43bb", + AXLETH: "0xe840BE8D9aB1ACD5AfC7168b05EC350B7FD18709", + }, + "421613": { + ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + WETH: "0xEe01c0CD76354C383B8c7B4e65EA88D00B06f36f", + AUSDC: "0x254d06f33bDc5b8ee05b2ea472107E300226659A", + }, + "4002": { + FTM: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + WFTM: "0xf1277d1Ed8AD466beddF92ef448A132661956621", + AUSDC: "0x75Cc4fDf1ee3E781C1A3Ee9151D5c6Ce34Cf5C61", + }, + "84531": { + ETH: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + WETH: "0x4200000000000000000000000000000000000006", + AUSDC: "0x254d06f33bDc5b8ee05b2ea472107E300226659A", + }, +}; + +export default networkTokenData; diff --git a/package-lock.json b/package-lock.json index d008316..05302b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,7 +51,6 @@ "ethers": "6.13.1", "ethers-5": "npm:ethers@^5.7.2", "js-sha256": "0.9.0", - "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "long": "^5.2.3", "near-api-js": "4.0.2", @@ -60,18 +59,25 @@ "stellar-sdk": "^11.2.2", "tiny-secp256k1": "2.2.3", "ton-crypto": "3.2.0", - "tronweb": "5.3.1", "tweetnacl": "1.0.3", "web3": "1.7.5", "xrpl": "^3.0.0" }, "devDependencies": { "@babel/eslint-parser": "^7.22.15", + "@types/jsonwebtoken": "^9.0.7", + "@types/node": "^22.10.1", "eslint": "8.56.0", "eslint-config-airbnb-base": "15.0.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", - "jest": "29.7.0" + "jest": "29.7.0", + "jsonwebtoken": "^9.0.2", + "tronweb": "^6.0.0", + "ts-node": "^10.9.2", + "tsx": "^4.19.2", + "typescript": "^5.7.2", + "uuid": "^11.0.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -996,8 +1002,6 @@ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -1010,8 +1014,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1183,6 +1185,390 @@ "pbts": "bin/pbts" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2905,11 +3291,22 @@ } }, "node_modules/@noble/curves": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", - "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", "dependencies": { - "@noble/hashes": "1.3.3" + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -3107,41 +3504,30 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@scure/base": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", - "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip32": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", - "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", - "dependencies": { - "@noble/curves": "~1.1.0", - "@noble/hashes": "~1.3.1", - "@scure/base": "~1.1.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32/node_modules/@noble/curves": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", - "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", "dependencies": { - "@noble/hashes": "1.3.1" + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip32/node_modules/@noble/hashes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", - "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "engines": { "node": ">= 16" }, @@ -3617,7 +4003,7 @@ "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-11.0.1.tgz", "integrity": "sha512-VQh+1KEtFjegD6spx08+lENt8tQOkQQQZoLtqExjpRXyWlqDhEe+bXMlBTYKDc5MIynHyD42RPEib27UG17trA==", "dependencies": { - "@stellar/js-xdr": "^3.1.1", + "@stellar/js-xdr": "^3.1.2", "base32.js": "^0.1.0", "bignumber.js": "^9.1.2", "buffer": "^6.0.3", @@ -3625,7 +4011,7 @@ "tweetnacl": "^1.0.3" }, "optionalDependencies": { - "sodium-native": "^4.0.10" + "sodium-native": "^4.1.1" } }, "node_modules/@suchipi/femver": { @@ -3706,39 +4092,32 @@ "node_modules/@tronweb3/google-protobuf": { "version": "3.21.2", "resolved": "https://registry.npmjs.org/@tronweb3/google-protobuf/-/google-protobuf-3.21.2.tgz", - "integrity": "sha512-IVcT2GfWX3K6tHUVhs14NP5uzKhQt4KeDya1g9ACxuZsUzsaoGUIGzceK2Ltu7xp1YV94AaHOf4yxLAivlvEkQ==" + "integrity": "sha512-IVcT2GfWX3K6tHUVhs14NP5uzKhQt4KeDya1g9ACxuZsUzsaoGUIGzceK2Ltu7xp1YV94AaHOf4yxLAivlvEkQ==", + "dev": true }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -3876,6 +4255,15 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", + "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -4077,8 +4465,6 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "acorn": "^8.11.0" }, @@ -4236,9 +4622,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/argparse": { "version": "2.0.1", @@ -5149,7 +5533,8 @@ "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true }, "node_modules/buffer-from": { "version": "1.1.2", @@ -5730,9 +6115,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/cross-fetch": { "version": "3.1.6", @@ -5997,8 +6380,6 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=0.3.1" } @@ -6077,6 +6458,7 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, "dependencies": { "safe-buffer": "^5.0.1" } @@ -6345,6 +6727,45 @@ "ext": "^1.1.2" } }, + "node_modules/esbuild": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -6740,33 +7161,34 @@ } }, "node_modules/ethereum-cryptography": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", - "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", "dependencies": { - "@noble/curves": "1.1.0", - "@noble/hashes": "1.3.1", - "@scure/bip32": "1.3.1", - "@scure/bip39": "1.2.1" + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" } }, - "node_modules/ethereum-cryptography/node_modules/@noble/curves": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", - "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", - "dependencies": { - "@noble/hashes": "1.3.1" + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" }, "funding": { "url": "https://paulmillr.com/funding/" } }, - "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", - "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", - "engines": { - "node": ">= 16" + "node_modules/ethereum-cryptography/node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -7623,6 +8045,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-tsconfig": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -8167,11 +8601,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/injectpromise": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/injectpromise/-/injectpromise-1.0.0.tgz", - "integrity": "sha512-qNq5wy4qX4uWHcVFOEU+RqZkoVG65FhvGkyDWbuBxILMjK6A1LFf5A1mgXZkD4nRx5FCorD81X/XvPKp/zVfPA==" - }, "node_modules/inquirer": { "version": "8.2.6", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", @@ -8765,6 +9194,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" }, + "node_modules/jayson/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -9533,6 +9970,7 @@ "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dev": true, "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", @@ -9554,6 +9992,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -9565,6 +10004,7 @@ "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -9578,7 +10018,8 @@ "node_modules/jsonwebtoken/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/jsprim": { "version": "1.4.2", @@ -9606,6 +10047,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dev": true, "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -9616,6 +10058,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dev": true, "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" @@ -9723,32 +10166,38 @@ "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -9759,7 +10208,8 @@ "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true }, "node_modules/log-symbols": { "version": "4.1.0", @@ -9872,9 +10322,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/makeerror": { "version": "1.0.12", @@ -11156,14 +11604,6 @@ "node": ">=0.10.0" } }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", - "engines": { - "node": ">=0.4.x" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11443,6 +11883,15 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -11596,6 +12045,14 @@ "utf-8-validate": "^5.0.2" } }, + "node_modules/rpc-websockets/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/rpc-websockets/node_modules/ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", @@ -12657,42 +13114,44 @@ } }, "node_modules/tronweb": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/tronweb/-/tronweb-5.3.1.tgz", - "integrity": "sha512-JF3HE0ab8KQWJWETCF7UqZHD4uMNNGlEPrquBoObu5bWc8A8eerBV9M2PJq+HcSYBivmEHeBqFgmpMAQP3ofpw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tronweb/-/tronweb-6.0.0.tgz", + "integrity": "sha512-mIh00KG00Iu80UT1SLDgNEBLzWiR24WnttlObP8B9eQyNJ6mg4oD2gE+vG0cd6FcHL9DV6Jd18gKeBp4y3Y7Ew==", + "dev": true, "dependencies": { "@babel/runtime": "^7.0.0", - "@ethersproject/abi": "^5.7.0", "@tronweb3/google-protobuf": "^3.21.2", - "axios": "^0.26.1", + "axios": "^1.7.4", "bignumber.js": "^9.0.1", - "ethereum-cryptography": "^2.0.0", - "ethers": "^6.6.0", + "ethereum-cryptography": "^2.1.3", + "ethers": "^6.13.1", "eventemitter3": "^3.1.0", - "injectpromise": "^1.0.0", - "lodash": "^4.17.21", - "querystring-es3": "^0.2.1", "semver": "^5.6.0", "validator": "^13.7.0" } }, "node_modules/tronweb/node_modules/axios": { - "version": "0.26.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", - "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, "dependencies": { - "follow-redirects": "^1.14.8" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/tronweb/node_modules/eventemitter3": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "dev": true }, "node_modules/tronweb/node_modules/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, "bin": { "semver": "bin/semver" } @@ -12702,8 +13161,6 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, - "optional": true, - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -12780,6 +13237,25 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" }, + "node_modules/tsx": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", + "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "dev": true, + "dependencies": { + "esbuild": "~0.23.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -12928,8 +13404,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "optional": true, - "peer": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -13103,20 +13578,23 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "optional": true, - "peer": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "9.2.0", @@ -13136,6 +13614,7 @@ "version": "13.11.0", "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "dev": true, "engines": { "node": ">= 0.10" } @@ -13997,8 +14476,6 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, - "optional": true, - "peer": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index 46d4b61..dc84e1b 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "3.2.4", "description": "The external SDK for the adapter", "main": "src/index.js", + "type": "commonjs", "parserOptions": { "ecmaVersion": 2018 }, @@ -63,7 +64,6 @@ "ethers": "6.13.1", "ethers-5": "npm:ethers@^5.7.2", "js-sha256": "0.9.0", - "jsonwebtoken": "^9.0.2", "lodash": "^4.17.21", "long": "^5.2.3", "near-api-js": "4.0.2", @@ -72,17 +72,24 @@ "stellar-sdk": "^11.2.2", "tiny-secp256k1": "2.2.3", "ton-crypto": "3.2.0", - "tronweb": "5.3.1", "tweetnacl": "1.0.3", "web3": "1.7.5", "xrpl": "^3.0.0" }, "devDependencies": { "@babel/eslint-parser": "^7.22.15", + "@types/jsonwebtoken": "^9.0.7", + "@types/node": "^22.10.1", "eslint": "8.56.0", "eslint-config-airbnb-base": "15.0.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-import": "2.29.1", - "jest": "29.7.0" + "jest": "29.7.0", + "jsonwebtoken": "^9.0.2", + "tronweb": "^6.0.0", + "ts-node": "^10.9.2", + "tsx": "^4.19.2", + "typescript": "^5.7.2", + "uuid": "^11.0.3" } } diff --git a/src/adapters/Wallet/index.ts b/src/adapters/Wallet/index.ts new file mode 100644 index 0000000..7241725 --- /dev/null +++ b/src/adapters/Wallet/index.ts @@ -0,0 +1,207 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import BN from 'bn.js'; +import * as rawTransaction from './signTransaction/index'; +import config from '../../../configuration/config'; +import * as common from '../../../configuration/common'; +import * as schemaValidator from '../../../configuration/schemaValidator'; +import { initialiseWeb3 } from '../../../configuration/intialiseWeb3'; +import { ethers } from 'ethers-5'; + +interface WalletOptions { + privateKey: string; + xApiKey: string; +} + +interface TransactionObject { + chainId: string; + chainSymbol?: string; + value: string | BN; + to?: string; + gas?: string; + [key: string]: any; +} + +interface SignOrderOptions { + dexId: string; + domain: any; + types: any; + values?: any; + message?: any; + orderType?: string; +} + + + +class Wallet { + private privateKey: string; + private xApiKey: string; + + constructor(options: WalletOptions) { + this.privateKey = options.privateKey; + this.xApiKey = options.xApiKey; + } + + signTransaction = async (transactionObject: TransactionObject) => { + const configuration: AxiosRequestConfig = { params: {} }; + const transactionOptions = { ...transactionObject, function: "transactionObject()" }; + const validObject = await schemaValidator.validateInput(transactionOptions); + + if (!validObject.valid) { + return validObject; + } + + axios.defaults.headers['X-API-KEY'] = this.xApiKey; + const apiURL = `${config.url.apiurl}/chain/getpublicrpc/`; + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol + }); + + if (chainId === null) { + throw new Error("chainId is null"); + } + + configuration.params = { chainId }; + + let rpc = await axios.get(apiURL, configuration); + rpc = rpc.data.data.rpc; + + const web3 = await initialiseWeb3({ rpc, chainId, key: this.xApiKey }); + transactionOptions.value = new BN(transactionOptions.value); + + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + console.log(chainName); + + const options = { + privateKey: this.privateKey, + chainId: transactionObject.chainId, + rpc + }; + + const rawData:any = await rawTransaction[`signTransaction${chainName}`as keyof typeof rawTransaction](web3, transactionObject, options); + rawData.chainId = chainId; + + return rawData; + }; + + signVersionedTransaction = async (transactionObject: TransactionObject) => { + const configuration: AxiosRequestConfig = { params: {} }; + transactionObject.function = "txObjSol()"; + const validObject = await schemaValidator.validateInput(transactionObject); + + if (!validObject.valid) { + return validObject; + } + + axios.defaults.headers['X-API-KEY'] = this.xApiKey; + const apiURL = `${config.url.apiurl}/chain/getpublicrpc/`; + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol + }); + + const chainName: string = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "Solana") { + throw new Error("Chain not supported"); + } + + configuration.params = { chainId }; + + let rpc = await axios.get(apiURL, configuration); + rpc = rpc.data.data.rpc; + + const web3 = await initialiseWeb3({ rpc, chainId, key: this.xApiKey }); + const options = { privateKey: this.privateKey }; + const rawData = await rawTransaction[`signVersionedTransaction${chainName}`](web3, transactionObject, options); + rawData.chainId = chainId; + + return rawData; + }; + + sendTransaction = async (options: Record) => { + const filterOptions = { ...options, function: "sendTransaction()" }; + const validJson = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/sendtransaction/`; + const params: AxiosRequestConfig = { + method: "post", + url: apiURL, + data: options, + headers: { + "x-api-key": this.xApiKey + } + }; + + const transactionHash = await axios(params); + return transactionHash.data; + } catch (error) { + return error; + } + }; + + signOrderRFQ = async (options: SignOrderOptions) => { + const filterOptions = { ...options, function: "signOrderRFQ()" }; + const validJson = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + const { dexId, domain, types, values } = options; + const { chainId } = config.dexes[dexId as unknown as keyof typeof config.dexes]; + + const apiConfig: AxiosRequestConfig = { + method: 'get', + maxBodyLength: Infinity, + url: `${config.url.apiurl}/chain/getpublicrpc?chainId=${chainId}`, + headers: { 'x-api-key': this.xApiKey } + }; + + let rpc:any = await axios.request(apiConfig); + rpc = rpc.data.data.rpc; + + const provider = new ethers.providers.JsonRpcProvider(rpc); + const signer = new ethers.Wallet(this.privateKey, provider); + const signature = await signer._signTypedData(domain, types, values); + + return { signature }; + }; + + signLimitOrder = async (options: SignOrderOptions) => { + const filterOptions = { ...options, function: "signLimitOrder()" }; + const validJson = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + const { dexId, orderType, domain, types, message } = options; + const { chainId } = config.dexes[dexId as unknown as keyof typeof config.dexes]; + + const apiConfig: AxiosRequestConfig = { + method: 'get', + maxBodyLength: Infinity, + url: `${config.url.apiurl}/chain/getpublicrpc?chainId=${chainId}`, + headers: { 'x-api-key': this.xApiKey } + }; + + let rpc:any = await axios.request(apiConfig); + rpc = rpc.data.data.rpc; + + const provider = new ethers.providers.JsonRpcProvider(rpc); + const signer = new ethers.Wallet(this.privateKey, provider); + const signature = orderType === "create" + ? await signer._signTypedData(domain, { Order: types.Order }, message) + : await signer._signTypedData(domain, { CancelOrder: types.CancelOrder }, message); + + return { signature, ...(orderType === "create" && { salt: message?.salt }) }; + }; +} + +export { Wallet }; diff --git a/src/adapters/Wallet/signTransaction/Algorand.ts b/src/adapters/Wallet/signTransaction/Algorand.ts new file mode 100644 index 0000000..8a768d3 --- /dev/null +++ b/src/adapters/Wallet/signTransaction/Algorand.ts @@ -0,0 +1,54 @@ +import algosdk, { Algodv2, SuggestedParams } from 'algosdk'; + +interface TransactionObject { + from: string; + to: string; + gas: number | string; + value: number | string; +} + +interface Options { + privateKey: string; // Mnemonic key +} + +export const AlgorandUtils:any = { + async signTransactionAlgorand( + algorandWeb3: Algodv2, + transactionObject: TransactionObject, + options: Options + ): Promise<{ rawTransaction: string } | Error> { + /* + * Function will sign the transaction payload for Algorand chain + */ + + try { + // Fetch transaction parameters + const params: SuggestedParams = await algorandWeb3.getTransactionParams().do(); + + // Convert mnemonic key to secret key + const account = algosdk.mnemonicToSecretKey(options.privateKey); + + // Create the payment transaction + const transaction = algosdk.makePaymentTxnWithSuggestedParamsFromObject({ + from: transactionObject.from, + to: transactionObject.to, + amount: Number(transactionObject.value), // Convert value to number + suggestedParams: { + ...params, + fee: Number(transactionObject.gas), // Override fee + }, + note: new Uint8Array(0), // Empty note + }); + + // Sign the transaction + const signedTransaction = transaction.signTxn(account.sk); + + // Serialize transaction to base64 + const rawTransaction = Buffer.from(signedTransaction).toString('base64'); + + return { rawTransaction }; + } catch (error) { + return error as Error; + } + }, +}; diff --git a/src/adapters/Wallet/signTransaction/Aptos.ts b/src/adapters/Wallet/signTransaction/Aptos.ts new file mode 100644 index 0000000..ba1fbfd --- /dev/null +++ b/src/adapters/Wallet/signTransaction/Aptos.ts @@ -0,0 +1,58 @@ +import { TxnBuilderTypes, BCS, AptosAccount, + AptosClient, HexString } from 'aptos'; +import config from '../../../../configuration/config'; + + + +const signTransactionAptos = async (web3:any, transactionObject:any, options:any) => { + /* + * Function will sign the transaction payload for Aptos chain + */ + + try { + const { privateKey } = options; + const chainId: number = (options.chainId && options.chainId === "1400") ? 1 : 2; + + const accountFrom = new AptosAccount(HexString.ensure(privateKey).toUint8Array()); + + let { data } = transactionObject; + + let transactionBuffer: any; + + if (!data){ + const seq = await web3.getAccount(accountFrom.address()); + transactionBuffer = new TxnBuilderTypes.RawTransaction( + TxnBuilderTypes.AccountAddress.fromHex(transactionObject.from), + BigInt(seq.sequence_number), + new TxnBuilderTypes.TransactionPayloadEntryFunction( + TxnBuilderTypes.EntryFunction.natural( + '0x1::coin', + "transfer", + [new TxnBuilderTypes.TypeTagStruct(TxnBuilderTypes.StructTag.fromString(config.chains[options.chainId]!.aptosCoin || "undefined"))], + [BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(transactionObject.to)), BCS.bcsSerializeUint64(transactionObject.value)] + ), + ), + options.gas ? options.gas : 1000, + BigInt(100), + BigInt(Math.floor(Date.now() / 1000) + 10000), + new TxnBuilderTypes.ChainId(chainId), + ); + } else { + const decodedBytes = Buffer.from(data, "base64"); + const deserializer = new BCS.Deserializer(new Uint8Array(decodedBytes)); + transactionBuffer = TxnBuilderTypes.RawTransaction.deserialize(deserializer); + } + + const bcsTxn = AptosClient.generateBCSTransaction(accountFrom, transactionBuffer); + + const rawTransaction = Buffer.from(bcsTxn).toString("base64"); + + return { "rawTransaction": rawTransaction }; + + } + catch (error) { + return error; + } +} + +export { signTransactionAptos } \ No newline at end of file diff --git a/src/adapters/Wallet/signTransaction/Ethereum.ts b/src/adapters/Wallet/signTransaction/Ethereum.ts new file mode 100644 index 0000000..a79fb7b --- /dev/null +++ b/src/adapters/Wallet/signTransaction/Ethereum.ts @@ -0,0 +1,34 @@ +import Web3 from 'web3'; + +interface TransactionObject { + to: string; + value: string; + gas: string; + data?: string; + // Add other properties as necessary +} + +interface Options { + privateKey: string; +} + + const signTransactionEvm:any = async ( + web3: Web3, + transactionObject: TransactionObject, + options: Options +): Promise => { + /* + * Function will sign the transaction payload for Ethereum-based chains + */ + + try { + const signedTransaction = await web3.eth.accounts.signTransaction(transactionObject, options.privateKey); + return signedTransaction; + } catch (error) { + console.error('Error signing transaction:', error); + throw new Error('Transaction signing failed'); + } +}; + + +export default { signTransactionEvm }; \ No newline at end of file diff --git a/src/adapters/Wallet/signTransaction/Near.ts b/src/adapters/Wallet/signTransaction/Near.ts new file mode 100644 index 0000000..55aa9cc --- /dev/null +++ b/src/adapters/Wallet/signTransaction/Near.ts @@ -0,0 +1,81 @@ +import nearAPI from 'near-api-js'; + +import sha256 from "js-sha256"; + +const { KeyPair, keyStores } = nearAPI; + + +interface AccessKey { + nonce: number; + block_hash: string; + block_height:any; +} + + const signTransactionNear = async (web3:any, transactionObject:any, options:any) => { + /* + * Function will sign the transaction payload for Near chain + */ + + try { + const keyStore = new keyStores.InMemoryKeyStore(); + + const keyPair = KeyPair.fromString(options.privateKey); + + // adds the key you just created to your keyStore which can hold multiple keys + await keyStore.setKey(transactionObject.networkId, transactionObject.from, keyPair); + + // connect to NEAR + const near = new nearAPI.providers.JsonRpcProvider(options.rpc); + + const publicKey = keyPair.getPublicKey(); + + const actions = [nearAPI.transactions.transfer(transactionObject.value)]; + + const accessKey = await near.query( + `access_key/${transactionObject.from}/${publicKey.toString()}`, + "" + ); + // eslint-disable-next-line no-plusplus + const nonce:number = ++accessKey.nonce; + + const recentBlockHash = nearAPI.utils.serialize.base_decode( + accessKey.block_hash + ); + + const transaction = nearAPI.transactions.createTransaction( + transactionObject.from, + publicKey, + transactionObject.to, + nonce, + actions, + recentBlockHash + ); + + const serializedTx = nearAPI.utils.serialize.serialize( + nearAPI.transactions.SCHEMA.Transaction, + transaction + ); + + const serializedTxHash = new Uint8Array(sha256.sha256.array(serializedTx)); + + const signature = keyPair.sign(serializedTxHash); + + const signedTransaction = new nearAPI.transactions.SignedTransaction({ + transaction, + signature: new nearAPI.transactions.Signature({ + keyType: transaction.publicKey.keyType, + data: signature.signature, + }), + }); + + const rawTransaction = Buffer.from(signedTransaction.encode()).toString('base64'); + return {"rawTransaction": rawTransaction }; + + } + catch (error) { + return error; + } + + } + +export {signTransactionNear}; \ No newline at end of file diff --git a/src/adapters/Wallet/signTransaction/Solana.ts b/src/adapters/Wallet/signTransaction/Solana.ts new file mode 100644 index 0000000..d09ab75 --- /dev/null +++ b/src/adapters/Wallet/signTransaction/Solana.ts @@ -0,0 +1,131 @@ +import { Wallet } from '@project-serum/anchor'; +import { + Keypair, + Transaction, + SystemProgram, + VersionedTransaction, + TransactionMessage, + PublicKey, + Connection, +} from '@solana/web3.js'; +import * as sign from 'tweetnacl'; +import bs58 from 'bs58'; +import BN from 'bn.js'; + +interface TransactionObject { + data?: string; + from?: string; + to?: string; + value?: string | BN; + additionalSigners?: string; +} + +interface Options { + privateKey: string; +} + +export const SolanaUtils:any = { + async signTransactionSolana( + web3: Connection, + transactionObject: TransactionObject, + options: Options + ): Promise<{ rawTransaction: string } | Error | { msg: string }> { + /* + * Function will sign the transaction payload for Solana Chain + */ + try { + const from = Keypair.fromSecretKey(bs58.decode(options.privateKey)); + const blockHeight = await web3.getLatestBlockhash(); + let preparedTx: Transaction; + let transactionBuffer: Buffer; + + if (!transactionObject.data) { + transactionObject.value = new BN(transactionObject.value || 0); + preparedTx = new Transaction({ + blockhash: blockHeight.blockhash, + lastValidBlockHeight: blockHeight.lastValidBlockHeight + 1500, + feePayer: from.publicKey, + }); + preparedTx.add( + SystemProgram.transfer({ + fromPubkey: from.publicKey, + toPubkey: new PublicKey(transactionObject.to), + lamports: transactionObject.value.toNumber(), + }) + ); + } else { + if (transactionObject.from !== from.publicKey.toBase58()) { + return { msg: 'Signer is not matching with the from address' }; + } + const buffer = Buffer.from(transactionObject.data, 'base64'); + preparedTx = Transaction.from(buffer); + preparedTx.recentBlockhash = blockHeight.blockhash; + } + + transactionBuffer = preparedTx.serializeMessage(); + const signature = sign.detached(transactionBuffer, from.secretKey); + preparedTx.addSignature(from.publicKey, signature); + + if (transactionObject.additionalSigners) { + const additionalKey = Keypair.fromSecretKey(bs58.decode(transactionObject.additionalSigners)); + const additionalSignature = sign.detached(transactionBuffer, additionalKey.secretKey); + preparedTx.addSignature(additionalKey.publicKey, additionalSignature); + } + + const serializedTx = preparedTx.serialize(); + const rawTransaction = Buffer.from(serializedTx).toString('base64'); + return { rawTransaction }; + } catch (error) { + return error as Error; + } + }, + + async signVersionedTransactionSolana( + web3: Connection, + transactionObject: TransactionObject, + options: Options + ): Promise<{ rawTransaction: string } | Error | { msg: string }> { + + try { + const from:any = Keypair.fromSecretKey(bs58.decode(options.privateKey)); + const wallet = new Wallet(from); + const recentBlockhash = await web3.getRecentBlockhash(); + let preparedTx: VersionedTransaction; + + if (!transactionObject.data) { + const instructions = [ + SystemProgram.transfer({ + fromPubkey: from.publicKey, + toPubkey: new PublicKey(transactionObject.to), + lamports: transactionObject.value instanceof BN ? transactionObject.value.toNumber() : transactionObject.value || 0, + }), + ]; + const versionedMessage = new TransactionMessage({ + payerKey: from.publicKey, + recentBlockhash: recentBlockhash.blockhash, + instructions, + }).compileToV0Message(); + preparedTx = new VersionedTransaction(versionedMessage); + } else { + if (transactionObject.from !== from.publicKey.toBase58()) { + return { msg: 'Signer is not matching with the from address' }; + } + const buffer = Buffer.from(transactionObject.data, 'base64'); + preparedTx = VersionedTransaction.deserialize(buffer); + } + + preparedTx.sign([wallet.payer]); + + if (transactionObject.additionalSigners) { + const additionalKey = Keypair.fromSecretKey(bs58.decode(transactionObject.additionalSigners)); + preparedTx.sign([additionalKey]); + } + + const serializedTx = preparedTx.serialize(); + const rawTransaction = Buffer.from(serializedTx).toString('base64'); + return { rawTransaction }; + } catch (error) { + return error as Error; + } + }, +}; diff --git a/src/adapters/Wallet/signTransaction/Sui.ts b/src/adapters/Wallet/signTransaction/Sui.ts new file mode 100644 index 0000000..f795114 --- /dev/null +++ b/src/adapters/Wallet/signTransaction/Sui.ts @@ -0,0 +1,40 @@ +import { TransactionBlock, Ed25519Keypair, RawSigner } from '@mysten/sui.js'; +import { fromB64 } from "@mysten/bcs"; + + + + const signTransactionSui = async (web3:any, transactionObject:any, options:any) => { + + try { + // get the secretkey from options + const secretKey = options.privateKey; + const privateKeyBase64 = Buffer.from(secretKey, "hex").toString("base64"); // Convert hex to base64 string + // Create the keypair from converted private key + const keypair = Ed25519Keypair.fromSecretKey(fromB64(privateKeyBase64)); + // Create a signer with the provided keypair and network + const signer = new RawSigner(keypair, web3); + // Create the transaction with given input + const tx = new TransactionBlock(); + // Currently we support sui coin transfer + const [coin] = tx.splitCoins(tx.gas, [tx.pure(transactionObject.value)]); + // Add the instruction + tx.transferObjects( + [coin], + tx.pure( + transactionObject.to + ) + ); + // Sign the transaction Block + const signedTransaction = await signer.signTransactionBlock({ + transactionBlock: tx, + }); + // Return the raw Transaction + return { "rawTransaction": signedTransaction }; + + } catch (error) { + return (error); + + } + } + + export { signTransactionSui } \ No newline at end of file diff --git a/src/adapters/Wallet/signTransaction/Tron.ts b/src/adapters/Wallet/signTransaction/Tron.ts new file mode 100644 index 0000000..3456bfb --- /dev/null +++ b/src/adapters/Wallet/signTransaction/Tron.ts @@ -0,0 +1,31 @@ +import * as TronWeb from 'tronweb'; + + + + const signTransactionTron = async (web3:any, transactionObject:any, options:any) => { + + try { + const tronWeb = new TronWeb.TronWeb({ + fullHost: options.rpc, + privateKey: options.privateKey, + }); + const tradeobj = await tronWeb.transactionBuilder.sendTrx( + tronWeb.address.toHex(transactionObject.to), + transactionObject.value, + tronWeb.address.toHex(transactionObject.from) + ); + const signedtxn = await tronWeb.trx.sign( + tradeobj, + options.privateKey + ); + + const rawTransaction = Buffer.from(JSON.stringify(signedtxn)).toString("base64"); + return { "rawTransaction": rawTransaction }; + + } + catch (error) { + return error; + } + } + +export { signTransactionTron } \ No newline at end of file diff --git a/src/adapters/Wallet/signTransaction/index.ts b/src/adapters/Wallet/signTransaction/index.ts new file mode 100644 index 0000000..b443b42 --- /dev/null +++ b/src/adapters/Wallet/signTransaction/index.ts @@ -0,0 +1,76 @@ + import * as Algorand from './Algorand'; +import Ethereum from './Ethereum'; + import * as Near from './Near'; + import * as Solana from './Solana'; +import * as Tron from './Tron'; + import * as Sui from './Sui'; +import * as Aptos from './Aptos'; +// import StarkNet from './Starknet'; + +const signTransactionAlgorand:any = async (web3: any, transactionObject: any, options: any) => { + + const rawData = await Algorand.AlgorandUtils.signTransactionAlgorand(web3, transactionObject, options); + return rawData; + +}; + +const signTransactionEvm:any = async (web3: any, transactionObject: any, options: any) => { + const rawData = await Ethereum.signTransactionEvm(web3, transactionObject, options); + return rawData; +}; + + + +const signTransactionNear = async (web3:any, transactionObject:any , options:any) => { + + const rawData = await Near.signTransactionNear(web3, transactionObject, options); + return rawData; + +}; + +const signTransactionTron = async (web3:any, transactionObject:any, options:any) => { + + const rawData = await Tron.signTransactionTron(web3, transactionObject, options); + return rawData; + }; + +const signTransactionSolana:any = async (web3:any, transactionObject:any, options:any) => { + + const rawData = await Solana.SolanaUtils.signVersionedTransactionSolana(web3, transactionObject, options); + return rawData; +}; + +const signTransactionSui = async (web3:any, transactionObject:any, options:any) => { + + const rawData = await Sui.signTransactionSui(web3, transactionObject, options); + return rawData; +}; + + +const signTransactionAptos = async (web3:any, transactionObject:any, options:any) => { + + const rawData = await Aptos.signTransactionAptos(web3, transactionObject, options); + return rawData; +}; + +// exports.signTransactionStarkNet = async (web3, transactionObject, options) => { + +// const rawData = await StarkNet.signTransactionStarkNet(web3, transactionObject, options); +// return rawData; +// }; + +const signVersionedTransactionSolana = async (web3:any, transactionObject:any, options:any) => { + + const rawData = await Solana.SolanaUtils.signVersionedTransactionSolana(web3, transactionObject, options); + return rawData; +}; + + +export {signTransactionEvm, + signTransactionSolana, + signVersionedTransactionSolana, + signTransactionAlgorand, + signTransactionNear, + signTransactionSui, + signTransactionTron, + signTransactionAptos,}; \ No newline at end of file diff --git a/src/adapters/WalletBitcoin/index.ts b/src/adapters/WalletBitcoin/index.ts new file mode 100644 index 0000000..71348a9 --- /dev/null +++ b/src/adapters/WalletBitcoin/index.ts @@ -0,0 +1,152 @@ +import axios from "axios"; +import * as bitcoin from "bitcoinjs-lib"; +import { ECPairFactory, ECPairInterface } from "ecpair"; +import * as bip39 from "bip39"; +import * as tinysecp from "tiny-secp256k1"; +import * as schemaValidator from "../../../configuration/schemaValidator"; +import * as common from "../../../configuration/common"; +import config from "../../../configuration/config"; + +interface WalletBitcoinOptions { + privateKey: string; + xApiKey: string; +} + +interface TransactionOptions { + chainSymbol?: string; + to: string; + value: string; + utxo: { txId: string; vout: number; value: number }; + chainId?: string; + [key: string]: any; +} + +interface GetKeysOptions { + chainSymbol?: string; + mnemonic: string; + chainId?: string; + [key: string]: any; +} + +export class WalletBitcoin { + private privateKey: string; + private xApiKey: string; + + constructor(options: WalletBitcoinOptions) { + this.privateKey = options.privateKey; + this.xApiKey = options.xApiKey; + } + + signTransaction = async (options: TransactionOptions): Promise => { + options.function = "BTCSignTransaction()"; + const validJson = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + const { chainSymbol, to, value, utxo } = options; + let { chainId } = options; + + chainId = await common.getChainId({ chainId, chainSymbol }) ?? "unknown"; + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "Bitcoin") { + return { + msg: "Bitcoin wallet can be used only with Bitcoin Wallet", + }; + } + + const ECPair = ECPairFactory(tinysecp); + const network = chainId === "1800" ? bitcoin.networks.bitcoin : bitcoin.networks.testnet; + + + const privateKeyBuffer = Buffer.from(this.privateKey, "hex"); + const keyPair: ECPairInterface = ECPair.fromPrivateKey(privateKeyBuffer, { network }); + const from = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey, network }).address; + + const txb = new bitcoin.TransactionBuilder(network); + + txb.addInput(utxo.txId, utxo.vout); + txb.addOutput(to, Number(value)); + + const fee = config.chains[chainId].fee ?? 0; + const actualAmount = utxo.value - Number(value) - fee; + + if (actualAmount > 0) { + txb.addOutput(from!, actualAmount); + } + + txb.sign(0, keyPair); + const rawTx = txb.build().toHex(); + return { chainId, rawTransaction: rawTx }; + }; + + sendTransaction = async (options: any): Promise => { + const filterOptions = { ...options, function: "sendTransaction()" }; + const validJson = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/sendtransaction/`; + const params = { + method: "post", + url: apiURL, + data: options, + headers: { + "x-api-key": this.xApiKey, + }, + }; + + const transactionHash = await axios(params); + return transactionHash.data; + } catch (error) { + return error; + } + }; +} + +export const getKeysFromMnemonic = async ( + options: GetKeysOptions, + path: string = "m/44'/0'/0'/0/0" +): Promise => { + options.function = "BTCGetKeys()"; + const validJson = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + let { chainId } = options; + const { chainSymbol, mnemonic } = options; + + chainId = await common.getChainId({ chainId, chainSymbol }) ?? "unknown"; + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "Bitcoin") { + return { + msg: "Bitcoin wallet can be used only with Bitcoin Wallet", + }; + } + + const seed = await bip39.mnemonicToSeed(mnemonic); + const network = chainId === "1800" ? bitcoin.networks.bitcoin : bitcoin.networks.testnet; + + const root = bitcoin.bip32.fromSeed(seed, network); + const child = root.derivePath(path); + + const privateKeyWIF = child.toWIF(); + const privateKeyBuffer = bitcoin.ECPair.fromWIF(privateKeyWIF, network).privateKey!; + const privateKeyHex = privateKeyBuffer.toString("hex"); + const publickKey = child.publicKey.toString("hex"); + + const { address } = bitcoin.payments.p2pkh({ + pubkey: child.publicKey, + network, + }); + + return { privateKeyWIF, privateKeyHex, publickKey, address }; +}; diff --git a/src/adapters/WalletCircle/index.ts b/src/adapters/WalletCircle/index.ts new file mode 100644 index 0000000..d6aa38b --- /dev/null +++ b/src/adapters/WalletCircle/index.ts @@ -0,0 +1,137 @@ +import { initiateUserControlledWalletsClient } from '@circle-fin/user-controlled-wallets'; +import { v4 as uuidv4 } from 'uuid'; +import axios, { AxiosRequestConfig } from 'axios'; +import * as schemaValidator from '../../../configuration/schemaValidator'; +import conf from '../../../configuration/config'; + +interface WalletCircleOptions { + appId: string; + apiKey: string; + userId: string; + userToken: string; + walletId: string; + encryptionKey: string; +} + +interface TransactionObject { + value: number; + data?: string; + to: string; + tokenId?: string; + function?: string; +} + +interface ValidObject { + valid: boolean; +} + +class WalletCircle { + private appId: string; + private apiKey: string; + private client: ReturnType; + private userId: string; + private userToken: string; + private walletId: string; + private encryptionKey: string; + + constructor(options: WalletCircleOptions) { + this.appId = options.appId; + this.apiKey = options.apiKey; + this.client = initiateUserControlledWalletsClient({ + apiKey: options.apiKey, + }); + this.userId = options.userId; + this.userToken = options.userToken; + this.walletId = options.walletId; + this.encryptionKey = options.encryptionKey; + } + + static async getUserToken(options: { apiKey: string; userId: string }): Promise { + const client = initiateUserControlledWalletsClient({ + apiKey: options.apiKey, + }); + const userToken:any = await client.createUserToken({ userId: options.userId }).then(res => res.data); + return userToken; + } + + async signTransaction(transactionObject: TransactionObject): Promise { + const transactionOptions = transactionObject; + transactionOptions.function = "transactionObject()"; + const validObject: ValidObject = await schemaValidator.validateInput(transactionObject); + + if (!validObject.valid) { + return validObject; + } + + if (transactionObject.data) { + const data = JSON.stringify({ + userId: this.userId, + idempotencyKey: uuidv4(), + amounts: [transactionObject.value], + callData: transactionObject.data, + contractAddress: transactionObject.to, + walletId: this.walletId, + feeLevel: "MEDIUM", + }); + + const config: AxiosRequestConfig = { + method: 'post', + maxBodyLength: Infinity, + url: `${conf.circleProgrammableWallet.baseUrl}contractExecution`, + headers: { + 'Content-Type': 'application/json', + authorization: `Bearer ${this.apiKey}`, + 'X-User-Token': this.userToken, + }, + data, + }; + + try { + const challengeId = await axios.request(config).then(res => res.data); + return challengeId; + } catch (error: any) { + return error; + } + } else { + const data = JSON.stringify({ + userId: this.userId, + idempotencyKey: uuidv4(), + amounts: [transactionObject.value], + destinationAddress: transactionObject.to, + tokenId: transactionObject.tokenId, + walletId: this.walletId, + feeLevel: "MEDIUM", + }); + + const config: AxiosRequestConfig = { + method: 'post', + maxBodyLength: Infinity, + url: `${conf.circleProgrammableWallet.baseUrl}/transfer`, + headers: { + 'Content-Type': 'application/json', + authorization: `Bearer ${this.apiKey}`, + 'X-User-Token': this.userToken, + }, + data, + }; + + try { + const challengeId = await axios.request(config).then(res => res.data); + return challengeId; + } catch (error: any) { + return error.response?.data; + } + } + } + + async sendTransaction(challengeId: { data: { challengeId: string } }): Promise> { + return { + appId: this.appId, + userToken: this.userToken, + encryptionKey: this.encryptionKey, + challengeId: challengeId.data.challengeId, + }; + } +} + +export { WalletCircle }; diff --git a/src/adapters/WalletCoinbase/index.ts b/src/adapters/WalletCoinbase/index.ts new file mode 100644 index 0000000..bf7812a --- /dev/null +++ b/src/adapters/WalletCoinbase/index.ts @@ -0,0 +1,98 @@ +import axios from 'axios'; +import BN from 'bn.js'; +import * as rawTransaction from './signTransaction/index'; +import config from '../../../configuration/config'; +import * as common from '../../../configuration/common'; +import * as schemaValidator from '../../../configuration/schemaValidator'; +import { initialiseWeb3 } from '../../../configuration/intialiseWeb3'; + +type TransactionObject = { + chainId: string; + chainSymbol: string; + value: string | BN; + [key: string]: any; +}; + +type SendTransactionOptions = { + [key: string]: any; +}; + +type ValidObject = { + valid: boolean; + [key: string]: any; +}; + +class WalletCoinbase { + private privateKey: string; + private xApiKey: string; + + constructor(options: { privateKey: string; xApiKey: string }) { + this.privateKey = options.privateKey; + this.xApiKey = options.xApiKey; + } + + signTransaction = async (transactionObject: TransactionObject): Promise => { + const configuration: { params: Record } = { params: {} }; + const transactionOptions = { ...transactionObject, function: 'transactionObject()' }; + + const validObject: ValidObject = await schemaValidator.validateInput(transactionObject); + + if (!validObject.valid) { + return validObject; + } + + axios.defaults.headers['X-API-KEY'] = this.xApiKey; + const apiURL = `${config.url.apiurl}/chain/getpublicrpc/`; + + const chainId = await common.getChainId({ chainId: transactionObject.chainId, chainSymbol: transactionObject.chainSymbol }); + + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== 'Evm') { + throw new Error('chain not Supported'); + } + + configuration.params = { chainId }; + + let rpcResponse = await axios.get(apiURL, configuration); + const rpc = rpcResponse.data.data.rpc; + const web3 = await initialiseWeb3({ rpc, chainId, key: this.xApiKey }); + + transactionOptions.value = new BN(transactionOptions.value); + + const options = { privateKey: this.privateKey }; + const rawData = await rawTransaction[`signTransaction${chainName}`](web3, transactionObject, options); + rawData.chainId = chainId; + + return rawData; + }; + + sendTransaction = async (options: SendTransactionOptions): Promise => { + const filterOptions = { ...options, function: 'sendTransaction()' }; + const validJson: ValidObject = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/sendtransaction/`; + + const params = { + method: 'post', + url: apiURL, + data: options, + headers: { + 'x-api-key': this.xApiKey, + }, + }; + + const transactionHash = await axios(params); + return transactionHash.data; + } catch (error: any) { + return error; + } + }; +} + +export { WalletCoinbase }; diff --git a/src/adapters/WalletCoinbase/signTransaction/Ethereum.ts b/src/adapters/WalletCoinbase/signTransaction/Ethereum.ts new file mode 100644 index 0000000..6af63ce --- /dev/null +++ b/src/adapters/WalletCoinbase/signTransaction/Ethereum.ts @@ -0,0 +1,34 @@ +import Web3 from 'web3'; + +interface TransactionObject { + to: string; + value: string; + gas?: string; + gasPrice?: string; + data?: string; + nonce?: number; // Update nonce to be a number instead of string +} + +interface SignTransactionOptions { + privateKey: string; +} + +export const signTransactionEvm = async ( + web3: Web3, + transactionObject: TransactionObject, + options: SignTransactionOptions +): Promise => { + /* + * Function will sign the transaction payload for Ethereum-based chains + */ + + try { + const signedTransaction = await web3.eth.accounts.signTransaction( + transactionObject, + options.privateKey + ); + return signedTransaction; + } catch (error) { + return error; + } +}; diff --git a/src/adapters/WalletCoinbase/signTransaction/index.ts b/src/adapters/WalletCoinbase/signTransaction/index.ts new file mode 100644 index 0000000..24a97c5 --- /dev/null +++ b/src/adapters/WalletCoinbase/signTransaction/index.ts @@ -0,0 +1,10 @@ +import * as Ethereum from './Ethereum'; + + + +export const signTransactionEvm = async (web3: any, transactionObject: any, options:any) => { + + const rawData = await Ethereum.signTransactionEvm(web3, transactionObject, options); + return rawData; +}; + diff --git a/src/adapters/WalletCosmos/index.ts b/src/adapters/WalletCosmos/index.ts new file mode 100644 index 0000000..f5709b5 --- /dev/null +++ b/src/adapters/WalletCosmos/index.ts @@ -0,0 +1,134 @@ +import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"; +import { SigningStargateClient } from "@cosmjs/stargate"; +import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; +import axios from "axios"; +import * as schemaValidator from "../../../configuration/schemaValidator"; +import * as common from "../../../configuration/common"; +import * as config from "../../../configuration/config"; + +interface WalletCosmosOptions { + privateKey: string; + xApiKey: string; +} + +interface TransactionObject { + chainId: string; + chainSymbol?: string; + to: string; + value: string; + function?: string; +} + +interface SendTransactionOptions { + [key: string]: any; + function?: string; +} + +interface ValidJsonResponse { + valid: boolean; + [key: string]: any; +} + +class WalletCosmos { + private wallet: string; + private xApiKey: string; + + constructor(options: WalletCosmosOptions) { + this.wallet = options.privateKey; + this.xApiKey = options.xApiKey; + } + + signTransaction = async (transactionObject: TransactionObject): Promise => { + const configuration = { params: {} }; + transactionObject.function = "txObjSol()"; + const validJson: ValidJsonResponse = await schemaValidator.validateInput(transactionObject); + + if (!validJson.valid) { + return validJson; + } + + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol, + }); + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + + + axios.defaults.headers["X-API-KEY"] = this.xApiKey; + const apiURL = `${config.url.apiurl}/chain/getpublicrpc/`; + + configuration.params = { + chainId, + }; + + let rpc:any = await axios.get(apiURL, configuration); + rpc = rpc.data.data.rpc; + + if (chainName !== "Cosmos") { + return { + msg: "Cosmos wallet can be used only with Cosmos chains", + }; + } + + const wallet = await DirectSecp256k1HdWallet.fromMnemonic(this.wallet, { + prefix: "cosmos", + }); + + const account = (await wallet.getAccounts())[0].address; + const signingClient = await SigningStargateClient.connectWithSigner(rpc, wallet); + + const tx = await signingClient.sign( + account, + [ + { + typeUrl: "/cosmos.bank.v1beta1.MsgSend", + value: { + fromAddress: account, + toAddress: transactionObject.to, + amount: [{ denom: "uatom", amount: transactionObject.value }], + }, + }, + ], + { + amount: [{ denom: "uatom", amount: "1000" }], + gas: "200000", + }, + "expand" + ); + + const encodedTx = TxRaw.encode(tx).finish(); + const rawString = Buffer.from(encodedTx).toString("base64"); + return { chainId, rawTransaction: rawString }; + }; + + sendTransaction = async (options: SendTransactionOptions): Promise => { + const filterOptions = options; + filterOptions.function = "sendTransaction()"; + const validJson: ValidJsonResponse = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/sendtransaction/`; + + const params = { + method: "post", + url: apiURL, + data: options, + headers: { + "x-api-key": this.xApiKey, + }, + }; + + const transactionHash = await axios(params); + return transactionHash.data; + } catch (error) { + return error; + } + }; +} + +export { WalletCosmos }; diff --git a/src/adapters/WalletDFNS/index.ts b/src/adapters/WalletDFNS/index.ts new file mode 100644 index 0000000..109d390 --- /dev/null +++ b/src/adapters/WalletDFNS/index.ts @@ -0,0 +1,165 @@ +import { DfnsApiClient } from "@dfns/sdk"; +import { AsymmetricKeySigner } from "@dfns/sdk-keysigner"; +import { DfnsWallet } from "@dfns/lib-ethersjs6"; +import { JsonRpcProvider, Network, Transaction } from "ethers"; +import axios from "axios"; +import * as rawTransaction from "./signTransaction/index"; +import config from "../../../configuration/config"; +import * as common from "../../../configuration/common"; +import * as schemaValidator from "../../../configuration/schemaValidator"; + +type WalletDFNSOptions = { + xApiKey: string; + privateKey: string; + credId: string; + appId: string; + authToken: string; + walletId: string; + baseUrl: string; + appOrigin: string; +}; + +type TransactionObject = { + chainId: string; + chainSymbol?: string; + rawTransaction?: any; + to: string; + value: string; + gas: string; + [key: string]: any; +}; + +type Options = { + wallet: DfnsWallet; + xApiKey: string; + rpcProvider: JsonRpcProvider; + privateKey?: string; + }; + +type ExtendedAsymmetricKeySignerOptions = { + credId: string; + privateKey: string; + appOrigin?: string; + }; + +class WalletDFNS { + private xApiKey: string; + private privateKey: string; + private credId: string; + private appId: string; + private authToken: string; + private walletId: string; + private baseUrl: string; + private appOrigin: string; + private signer: AsymmetricKeySigner; + private dfnsClient: DfnsApiClient; + private wallet: DfnsWallet; + + constructor(options: WalletDFNSOptions) { + this.xApiKey = options.xApiKey; + this.privateKey = options.privateKey; + this.credId = options.credId; + this.appId = options.appId; + this.authToken = options.authToken; + this.walletId = options.walletId; + this.baseUrl = options.baseUrl; + this.appOrigin = options.appOrigin; + + this.signer = new AsymmetricKeySigner({ + privateKey: options.privateKey, + credId: options.credId, + appOrigin: options.appOrigin, // Include this if needed + } as ExtendedAsymmetricKeySignerOptions); + + this.dfnsClient = new DfnsApiClient({ + appId: options.appId, + authToken: options.authToken, + baseUrl: options.baseUrl, + signer: this.signer, + }); + + this.wallet = new DfnsWallet({ + walletId: this.walletId, + dfnsClient: this.dfnsClient, + maxRetries: 10, + }); + } + + public signTransaction = async (transactionObject: TransactionObject): Promise => { + try { + const transactionOptions = { ...transactionObject, function: "transactionObject()" }; + const validObject = await schemaValidator.validateInput(transactionObject); + + if (!validObject.valid) { + return validObject; + } + + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol, + }); + + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + axios.defaults.headers["X-API-KEY"] = this.xApiKey; + const apiURL = `${config.url.apiurl}/chain/getpublicrpc/`; + const configuration = { params: { chainId } }; + let rpc:any = await axios.get(apiURL, configuration); + rpc = rpc.data.data.rpc; + + const rpcProvider = new JsonRpcProvider(rpc, Number(transactionObject.chainId)); + + if (chainName !== "Evm") { + throw new Error("Chain not supported"); + } + + const options = { + wallet: this.wallet, + xApiKey: this.xApiKey, + rpcProvider, + }; + + const response = await rawTransaction[`signTransaction${chainName}`](transactionObject, options); + return response; + } catch (error) { + return error; + } + }; + + public sendTransaction = async (transactionObject: TransactionObject): Promise => { + try { + const filterOptions = { ...transactionObject, function: "DFNSTransaction()" }; + const validJson = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) { + return validJson; + } + + axios.defaults.headers["X-API-KEY"] = this.xApiKey; + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol, + }); + + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + const apiURL = `${config.url.apiurl}/chain/getpublicrpc/`; + const configuration = { params: { chainId } }; + let rpc:any = await axios.get(apiURL, configuration); + rpc = rpc.data.data.rpc; + + if (chainName !== "Evm") { + throw new Error("Chain not supported"); + } + + const rpcProvider = new JsonRpcProvider(rpc, Number(transactionObject.chainId)); + const wallet = this.wallet.connect(rpcProvider); + const transaction = Transaction.from(transactionObject.rawTransaction); + + const txHash = await wallet.sendTransaction(transaction); + return { TxHash: txHash.hash }; + } catch (error) { + return error; + } + }; +} + +export { WalletDFNS }; diff --git a/src/adapters/WalletDFNS/signTransaction/Ethereum.ts b/src/adapters/WalletDFNS/signTransaction/Ethereum.ts new file mode 100644 index 0000000..563bf17 --- /dev/null +++ b/src/adapters/WalletDFNS/signTransaction/Ethereum.ts @@ -0,0 +1,54 @@ +import { Wallet, JsonRpcProvider } from "ethers"; + +interface TransactionObject { + to: string; + value: string | number; + gas: number | string; + chainId: number; + data?: string; +} + +interface Options { + wallet: Wallet; + rpcProvider: JsonRpcProvider; +} + +interface SignedTransactionResponse { + rawTransaction: string; +} + + const signTransactionEvm = async ( + transactionObject: TransactionObject, + options: Options +): Promise => { + /* + * Function will sign the transaction payload for Ethereum-based chains + */ + try { + const nonce = await options.rpcProvider.getTransactionCount( + await options.wallet.getAddress(), + "latest" + ); + + const tx = { + nonce, + to: transactionObject.to, + data: transactionObject.data, + value: transactionObject.value, + gas: transactionObject.gas, + chainId: transactionObject.chainId, + maxFeePerGas: "120000000000", // Set these values as required + maxPriorityFeePerGas: "120000000000", + gasLimit: transactionObject.gas, + }; + + const signedTx = await options.wallet.signTransaction(tx); + const response: SignedTransactionResponse = { rawTransaction: signedTx }; + + return response; + } catch (error) { + return error as Error; + } +}; + +export default { signTransactionEvm }; \ No newline at end of file diff --git a/src/adapters/WalletDFNS/signTransaction/index.ts b/src/adapters/WalletDFNS/signTransaction/index.ts new file mode 100644 index 0000000..8a6d998 --- /dev/null +++ b/src/adapters/WalletDFNS/signTransaction/index.ts @@ -0,0 +1,10 @@ +import Ethereum from './Ethereum'; + + +export const signTransactionEvm = async ( + transactionObject: any, + options: any +): Promise => { + const rawData = await Ethereum.signTransactionEvm(transactionObject, options); + return rawData; +}; diff --git a/src/adapters/WalletFireblocks/index.ts b/src/adapters/WalletFireblocks/index.ts new file mode 100644 index 0000000..fab745e --- /dev/null +++ b/src/adapters/WalletFireblocks/index.ts @@ -0,0 +1,150 @@ +import axios from 'axios'; +import * as jwt from 'jsonwebtoken'; +import { v4 as uuidv4 } from 'uuid'; +import crypto from 'crypto'; +import config from '../../../configuration/config'; +import * as common from '../../../configuration/common'; +import * as schemaValidator from '../../../configuration/schemaValidator'; + +interface WalletFireblocksOptions { + baseUrl?: string; + apiSecret: string; + apiKey: string; +} + +interface TransactionObject { + chainId: string; + chainSymbol: string; + from: string; + to: string; + value?: number; + assetId?: string; + assetDecimals?: number; + note?: string; + data?: string; + internal?: boolean; + function?: string; +} + +interface RawTransaction { + jwt: string; + path: string; + data: any; + method: string; + function?: string; +} + +class WalletFireblocks { + private baseUrl: string; + private apiSecret: string; + private apiKey: string; + + constructor(options: WalletFireblocksOptions) { + this.baseUrl = options.baseUrl || config.fireblocks.baseUrl; + this.apiSecret = options.apiSecret; + this.apiKey = options.apiKey; + } + + private jwtSign(path: string, data: any): string { + const token = jwt.sign( + { + uri: path, + nonce: uuidv4(), + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 55, + sub: this.apiKey, + bodyHash: crypto.createHash('sha256').update(JSON.stringify(data || '')).digest().toString('hex'), + }, + this.apiSecret, + { algorithm: 'RS256' } + ); + return token; + } + + public signTransaction = async (transactionObject: TransactionObject): Promise => { + try { + transactionObject.function = 'FireblockSign()'; + const validJson = await schemaValidator.validateInput(transactionObject); + + if (!validJson.valid) { + return validJson; + } + + const chainId = await common.getChainId({ chainId: transactionObject.chainId, chainSymbol: transactionObject.chainSymbol }); + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + const txData: any = { + operation: transactionObject.data ? 'CONTRACT_CALL' : 'TRANSFER', + source: { + type: 'VAULT_ACCOUNT', + id: transactionObject.from, + }, + }; + + if (transactionObject.internal) { + txData.destination = { + type: 'VAULT_ACCOUNT', + id: transactionObject.to, + }; + } else { + txData.destination = { + type: 'ONE_TIME_ADDRESS', + oneTimeAddress: { + address: transactionObject.to, + }, + }; + } + + const assetDecimals = transactionObject.assetDecimals || 18; + txData.assetId = transactionObject.assetId || 'ETH_TEST3'; + txData.amount = transactionObject.value ? transactionObject.value / 10 ** assetDecimals : '0'; + txData.note = transactionObject.note || 'expand'; + + if (transactionObject.data) { + txData.extraParameters = { + contractCallData: transactionObject.data, + }; + } + + const signature = this.jwtSign('/v1/transactions', txData); + const rawTx: RawTransaction = { + jwt: signature, + path: config.fireblocks.createTransaction, + data: txData, + method: 'POST', + }; + + return rawTx; + } catch (error) { + return error; + } + }; + + public sendTransaction = async (rawTx: RawTransaction): Promise => { + try { + rawTx.function = 'SendFireblocks()'; + const validJson = await schemaValidator.validateInput(rawTx); + + if (!validJson.valid) { + return validJson; + } + + const response = await axios({ + url: `${this.baseUrl}${rawTx.path}`, + method: rawTx.method, + data: rawTx.data, + headers: { + 'X-API-Key': this.apiKey, + Authorization: `Bearer ${rawTx.jwt}`, + }, + }); + + return response.data; + } catch (error:any) { + console.error(error); + return error.data; + } + }; +} + +export { WalletFireblocks }; diff --git a/src/adapters/WalletFordefi/index.ts b/src/adapters/WalletFordefi/index.ts new file mode 100644 index 0000000..602f860 --- /dev/null +++ b/src/adapters/WalletFordefi/index.ts @@ -0,0 +1,100 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import * as rawTransaction from './signTransaction/index'; +import config from '../../../configuration/config'; +import { getChainId } from '../../../configuration/common'; +import * as schemaValidator from '../../../configuration/schemaValidator'; + +interface WalletOptions { + accessToken: string; + xApiKey: string; + privateKeyFile: string; + vault_id: string; +} + +interface TransactionObject { + chainId: string; + chainSymbol?: string; + [key: string]: any; +} + +interface SendTransactionResponse { + accessToken: string; + timestamp: string; + signature: string; + data: any; +} + +class WalletFordefi { + private accessToken: string; + private xApiKey: string; + private privateKeyFile: string; + private vault_id: string; + + constructor(options: WalletOptions) { + this.accessToken = options.accessToken; + this.xApiKey = options.xApiKey; + this.privateKeyFile = options.privateKeyFile; + this.vault_id = options.vault_id; + } + + signTransaction = async (transactionObject: TransactionObject): Promise => { + try { + const transactionOptions = { ...transactionObject, function: "transactionObject()" }; + const validObject = await schemaValidator.validateInput(transactionObject); + + if (!validObject.valid) { + return validObject; + } + + const chainId = await getChainId({ chainId: transactionObject.chainId, chainSymbol: transactionObject.chainSymbol }); + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "Evm" && chainName !== "Solana") { + throw new Error("Chain not supported"); + } + + const options = { + vault_id: this.vault_id, + privateKeyFile: this.privateKeyFile, + accessToken: this.accessToken, + xApiKey: this.xApiKey, + }; + + const response = await rawTransaction[`signTransaction${chainName}`](transactionObject, options); + return response; + } catch (error: any) { + return error; + } + }; + + sendTransaction = async (response: SendTransactionResponse): Promise => { + try { + const filterOptions = { ...response, function: "FordefiTransaction()" }; + const validJson = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) { + return validJson; + } + + const path = "/api/v1/transactions"; + const axiosConfig: AxiosRequestConfig = { + method: "POST", + url: `https://api.fordefi.com${path}`, + headers: { + 'Content-Type': 'application/json', + Authorization: response.accessToken, + 'X-Timestamp': response.timestamp, + 'X-Signature': response.signature, + }, + data: response.data, + }; + + const resp = await axios.request(axiosConfig); + return resp.data; + } catch (error: any) { + return error.response?.data || error; + } + }; +} + +export { WalletFordefi }; diff --git a/src/adapters/WalletFordefi/signTransaction/Ethereum.ts b/src/adapters/WalletFordefi/signTransaction/Ethereum.ts new file mode 100644 index 0000000..3c1bbd2 --- /dev/null +++ b/src/adapters/WalletFordefi/signTransaction/Ethereum.ts @@ -0,0 +1,103 @@ +import crypto from "crypto"; +import fs from "fs"; + +interface TransactionObject { + chainId?: number | string; + gas: number; + to: string; + value: string; + data?: string; +} + +interface Options { + vault_id: string; + privateKeyFile: string; + accessToken: string; +} + +interface Gas { + type: string; + priority_level: string; + gas_limit: number; +} + +interface TransactionDetails { + chain: number; + gas: Gas; + to: string; + value: string; + type: string; + data?: { + type: string; + hex_data: string; + }; +} + +interface SignTransactionResponse { + data: string; + timestamp: number; + signature: string; + accessToken: string; +} + + const signTransactionEvm = async ( + transactionObject: TransactionObject, + options: Options +): Promise => { + /* + * Function will sign the transaction payload for ethereum-based chains + */ + + try { + let reqBody: any = {}; + reqBody.type = "evm_transaction"; + reqBody.vault_id = options.vault_id; + + const path = "/api/v1/transactions"; + + const details: TransactionDetails = { + chain: parseInt(transactionObject.chainId as string, 10) || 1, + gas: { + type: "priority", + priority_level: "medium", + gas_limit: transactionObject.gas, + }, + to: transactionObject.to, + value: transactionObject.value, + type: "evm_raw_transaction", + }; + + if (transactionObject.data) { + details.data = { + type: "hex", + hex_data: transactionObject.data, + }; + } + + reqBody.details = details; + reqBody.signer_type = "api_signer"; + reqBody = JSON.stringify(reqBody); + + const timestamp = new Date().getTime(); + const payload = `${path}|${timestamp}|${reqBody}`; + + const secretPem = fs.readFileSync(options.privateKeyFile, "utf8"); + const privateKey = crypto.createPrivateKey(secretPem); + + const sign = crypto.createSign("SHA256").update(payload, "utf8").end(); + const signature = sign.sign(privateKey, "base64"); + + const response: SignTransactionResponse = { + data: reqBody, + timestamp, + signature, + accessToken: `Bearer ${options.accessToken}`, + }; + + return response; + } catch (error) { + return error as Error; + } +}; + +export default { signTransactionEvm }; \ No newline at end of file diff --git a/src/adapters/WalletFordefi/signTransaction/Solana.ts b/src/adapters/WalletFordefi/signTransaction/Solana.ts new file mode 100644 index 0000000..b523584 --- /dev/null +++ b/src/adapters/WalletFordefi/signTransaction/Solana.ts @@ -0,0 +1,95 @@ +import crypto from 'crypto'; +import fs from 'fs'; + +interface TransactionObject { + chainId: string; + to: string; + value: string | number; +} + +interface AssetIdentifier { + type: string; + details: { + type: string; + chain: string; + }; +} + +interface Options { + vault_id: string; + privateKeyFile: string; + accessToken: string; +} + +interface Response { + data: string; + timestamp: number; + signature: string; + accessToken: string; +} + +const signTransactionSolana = async ( + transactionObject: TransactionObject, + options: Options +): Promise => { + /* + * Function will sign the transaction payload for Solana-based chains + */ + + try { + let reqBody: any = {}; + reqBody.vault_id = options.vault_id; + reqBody.type = 'solana_transaction'; + reqBody.signer_type = 'api_signer'; + + // Determine the chain + const chain = transactionObject.chainId === '901' ? 'solana_devnet' : 'solana_mainnet'; + + const path = '/api/v1/transactions'; + + // Prepare details object + const details: any = { + format: 'hash_binary', + type: 'solana_transfer', + to: transactionObject.to, + value: { + type: 'value', + value: transactionObject.value, + }, + asset_identifier: { + type: 'solana', + details: { + type: 'native', + chain: chain, + }, + }, + }; + + reqBody.details = details; + reqBody = JSON.stringify(reqBody); + + const timestamp = Date.now(); + const payload = `${path}|${timestamp}|${reqBody}`; + + // Read private key + const secretPem = fs.readFileSync(options.privateKeyFile, 'utf8'); + const privateKey = crypto.createPrivateKey(secretPem); + + // Create signature + const sign = crypto.createSign('SHA256').update(payload, 'utf8').end(); + const signature = sign.sign(privateKey, 'base64'); + + const response: Response = { + data: reqBody, + timestamp, + signature, + accessToken: `Bearer ${options.accessToken}`, + }; + + return response; + } catch (error) { + return error as Error; + } +}; + +export default { signTransactionSolana } ; \ No newline at end of file diff --git a/src/adapters/WalletFordefi/signTransaction/index.ts b/src/adapters/WalletFordefi/signTransaction/index.ts new file mode 100644 index 0000000..a8af1f5 --- /dev/null +++ b/src/adapters/WalletFordefi/signTransaction/index.ts @@ -0,0 +1,21 @@ +import Ethereum from './Ethereum'; +import Solana from './Solana'; + + + const signTransactionEvm = async ( + transactionObject: any, + options: any +): Promise => { + const rawData = await Ethereum.signTransactionEvm(transactionObject, options); + return rawData; +}; + + const signTransactionSolana = async ( + transactionObject: any, + options: any +): Promise => { + const rawData = await Solana.signTransactionSolana(transactionObject, options); + return rawData; +}; + +export { signTransactionEvm, signTransactionSolana }; \ No newline at end of file diff --git a/src/adapters/WalletPhantom/index.ts b/src/adapters/WalletPhantom/index.ts new file mode 100644 index 0000000..0d450a2 --- /dev/null +++ b/src/adapters/WalletPhantom/index.ts @@ -0,0 +1,137 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import BN from 'bn.js'; +import * as rawTransaction from './signTransaction/index'; +import config from '../../../configuration/config'; +import * as common from '../../../configuration/common'; +import * as schemaValidator from '../../../configuration/schemaValidator'; +import { initialiseWeb3 } from '../../../configuration/intialiseWeb3'; + +interface WalletPhantomOptions { + privateKey: string; + xApiKey: string; +} + +interface TransactionObject { + function?: string; + chainId: string | number; + chainSymbol: string; + [key: string]: any; +} + +interface ValidObject { + valid: boolean; + [key: string]: any; +} + +class WalletPhantom { + private privateKey: string; + private xApiKey: string; + + constructor(options: WalletPhantomOptions) { + this.privateKey = options.privateKey; + this.xApiKey = options.xApiKey; + } + + signTransaction = async (transactionObject: TransactionObject): Promise => { + const configuration: AxiosRequestConfig = { params: {} }; + transactionObject.function = "txObjSol()"; + + const validObject: ValidObject = await schemaValidator.validateInput(transactionObject); + if (!validObject.valid) { + return validObject; + } + + axios.defaults.headers['X-API-KEY'] = this.xApiKey; + const apiURL = `${config.url.apiurl}/chain/getpublicrpc/`; + + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol, + }); + + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "Evm" && chainName !== "Solana") { + return new Error("Chain not supported"); + } + + configuration.params = { chainId }; + + let rpcResponse = await axios.get(apiURL, configuration); + const rpc = rpcResponse.data.data.rpc; + + const web3 = await initialiseWeb3({ rpc, chainId, key: this.xApiKey }); + + const options = { privateKey: this.privateKey }; + const rawData = await rawTransaction[`signTransaction${chainName}`](web3, transactionObject, options); + rawData.chainId = chainId; + + return rawData; + }; + + signVersionedTransaction = async (transactionObject: TransactionObject): Promise => { + const configuration: AxiosRequestConfig = { params: {} }; + transactionObject.function = "txObjectSol()"; + + const validObject: ValidObject = await schemaValidator.validateInput(transactionObject); + if (!validObject.valid) { + return validObject; + } + + axios.defaults.headers['X-API-KEY'] = this.xApiKey; + const apiURL = `${config.url.apiurl}/chain/getpublicrpc/`; + + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol, + }); + + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "Solana") { + return new Error("Chain not supported"); + } + + configuration.params = { chainId }; + + let rpcResponse = await axios.get(apiURL, configuration); + const rpc = rpcResponse.data.data.rpc; + + const web3 = await initialiseWeb3({ rpc, chainId, key: this.xApiKey }); + + const options = { privateKey: this.privateKey }; + const rawData = await rawTransaction[`signVersionedTransaction${chainName}`](web3, transactionObject, options); + rawData.chainId = chainId; + + return rawData; + }; + + sendTransaction = async (options: TransactionObject): Promise => { + options.function = "sendTransaction()"; + + const validJson: ValidObject = await schemaValidator.validateInput(options); + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/sendtransaction/`; + + const params: AxiosRequestConfig = { + method: "post", + url: apiURL, + data: options, + headers: { + "x-api-key": this.xApiKey, + }, + }; + + const transactionHash = await axios(params); + return transactionHash.data; + } catch (error) { + return error; + } + }; +} + +export { WalletPhantom }; diff --git a/src/adapters/WalletPhantom/signTransaction/Ethereum.ts b/src/adapters/WalletPhantom/signTransaction/Ethereum.ts new file mode 100644 index 0000000..e612067 --- /dev/null +++ b/src/adapters/WalletPhantom/signTransaction/Ethereum.ts @@ -0,0 +1,19 @@ +export const signTransactionEvm = async ( + web3: any, + transactionObject: Record, + options: { privateKey: string } + ): Promise => { + /** + * Function to sign the transaction payload for Ethereum-based chains + */ + try { + const signedTransaction = await web3.eth.accounts.signTransaction( + transactionObject, + options.privateKey + ); + return signedTransaction; + } catch (error) { + return error; + } + }; + \ No newline at end of file diff --git a/src/adapters/WalletPhantom/signTransaction/Solana.ts b/src/adapters/WalletPhantom/signTransaction/Solana.ts new file mode 100644 index 0000000..7f93ade --- /dev/null +++ b/src/adapters/WalletPhantom/signTransaction/Solana.ts @@ -0,0 +1,126 @@ +import { Wallet } from '@project-serum/anchor'; +import { + Keypair, + Transaction, + SystemProgram, + VersionedTransaction, + TransactionMessage, + PublicKey, + Connection, + BlockhashWithExpiryBlockHeight +} from '@solana/web3.js'; +import nacl from 'tweetnacl'; +import bs58 from 'bs58'; +import BN from 'bn.js'; + +interface TransactionObject { + to: string; + value: number | BN; + data?: string; + from?: string; + additionalSigners?: string; +} + +interface Options { + privateKey: string; + additionalSigners?: string; +} + +export const signTransactionSolana = async ( + web3: Connection, + transactionObject: TransactionObject, + options: Options +): Promise<{ rawTransaction: string } | { msg: string } | Error> => { + try { + const from = Keypair.fromSecretKey(bs58.decode(options.privateKey)); + const blockHeight: BlockhashWithExpiryBlockHeight = await web3.getLatestBlockhash(); + let preparedTx: Transaction; + let transactionBuffer: Buffer; + + if (!transactionObject.data) { + transactionObject.value = new BN(transactionObject.value); + preparedTx = new Transaction({ + blockhash: blockHeight.blockhash, + lastValidBlockHeight: blockHeight.lastValidBlockHeight + 1500, + feePayer: from.publicKey, + }); + preparedTx.add( + SystemProgram.transfer({ + fromPubkey: from.publicKey, + toPubkey: new PublicKey(transactionObject.to), + lamports: transactionObject.value.toNumber(), + }) + ); + } else { + if (transactionObject.from !== from.publicKey.toBase58()) { + return { msg: 'Signer is not matching with the from address' }; + } + const buffer = Buffer.from(transactionObject.data, 'base64'); + preparedTx = Transaction.from(buffer); + preparedTx.recentBlockhash = blockHeight.blockhash; + } + + transactionBuffer = preparedTx.serializeMessage(); + const signature:any = nacl.sign.detached(transactionBuffer, from.secretKey); + preparedTx.addSignature(from.publicKey, signature); + + if (transactionObject.additionalSigners) { + const additionalKey = Keypair.fromSecretKey(bs58.decode(transactionObject.additionalSigners)); + const additionalSignature:any = nacl.sign.detached(transactionBuffer, additionalKey.secretKey); + preparedTx.addSignature(additionalKey.publicKey, additionalSignature); + } + + const serializedTx = preparedTx.serialize(); + const rawTransaction = Buffer.from(serializedTx).toString('base64'); + return { rawTransaction }; + } catch (error) { + return error as Error; + } +}; + +export const signVersionedTransactionSolana = async ( + web3: Connection, + transactionObject: TransactionObject, + options: Options +): Promise<{ rawTransaction: string } | { msg: string } | Error> => { + try { + const from = Keypair.fromSecretKey(bs58.decode(options.privateKey)); + const wallet = new Wallet(from); + const recentBlockhash: BlockhashWithExpiryBlockHeight = await web3.getLatestBlockhash(); + let preparedTx: VersionedTransaction; + + if (!transactionObject.data) { + const instructions = [ + SystemProgram.transfer({ + fromPubkey: from.publicKey, + toPubkey: new PublicKey(transactionObject.to), + lamports: transactionObject.value as number, + }), + ]; + const versionedMessage = new TransactionMessage({ + payerKey: from.publicKey, + recentBlockhash: recentBlockhash.blockhash, + instructions, + }).compileToV0Message(); + preparedTx = new VersionedTransaction(versionedMessage); + } else { + if (transactionObject.from !== from.publicKey.toBase58()) { + return { msg: 'Signer is not matching with the from address' }; + } + const buffer = Buffer.from(transactionObject.data, 'base64'); + preparedTx = VersionedTransaction.deserialize(buffer); + } + + preparedTx.sign([wallet.payer]); + + if (transactionObject.additionalSigners) { + preparedTx.sign([Keypair.fromSecretKey(bs58.decode(transactionObject.additionalSigners))]); + } + + const serializedTx = preparedTx.serialize(); + const rawTransaction = Buffer.from(serializedTx).toString('base64'); + return { rawTransaction }; + } catch (error) { + return error as Error; + } +}; diff --git a/src/adapters/WalletPhantom/signTransaction/index.ts b/src/adapters/WalletPhantom/signTransaction/index.ts new file mode 100644 index 0000000..ceff2f3 --- /dev/null +++ b/src/adapters/WalletPhantom/signTransaction/index.ts @@ -0,0 +1,29 @@ +import * as Solana from './Solana'; +import * as Ethereum from './Ethereum'; + +export const signTransactionEvm = async ( + web3: any, + transactionObject: any, + options: any +): Promise => { + const rawData = await Ethereum.signTransactionEvm(web3, transactionObject, options); + return rawData; +}; + +export const signTransactionSolana = async ( + web3: any, + transactionObject: any, + options: any +): Promise => { + const rawData = await Solana.signTransactionSolana(web3, transactionObject, options); + return rawData; +}; + +export const signVersionedTransactionSolana = async ( + web3: any, + transactionObject: any, + options: any +): Promise => { + const rawData = await Solana.signVersionedTransactionSolana(web3, transactionObject, options); + return rawData; +}; diff --git a/src/adapters/WalletStacks/index.ts b/src/adapters/WalletStacks/index.ts new file mode 100644 index 0000000..a87cd1b --- /dev/null +++ b/src/adapters/WalletStacks/index.ts @@ -0,0 +1,174 @@ +import axios from "axios"; +import { bytesToHex } from "@stacks/common"; +import { + TransactionSigner, + createStacksPrivateKey, + getPublicKey, + publicKeyToString, + makeUnsignedSTXTokenTransfer, + AnchorMode, + makeUnsignedContractCall, + makeContractCall, + StacksTransaction, + TransactionVersion, +} from "@stacks/transactions"; +import * as schemaValidator from '../../../configuration/schemaValidator'; +import * as common from '../../../configuration/common'; +import config from '../../../configuration/config'; +import { StacksMainnet, StacksTestnet, StacksNetwork } from "@stacks/network"; +import { getStxAddress, generateWallet } from "@stacks/wallet-sdk"; + +interface WalletOptions { + privateKey: string; + xApiKey: string; +} + +interface TransactionOptions { + chainSymbol: string; + to: string; + value: string; + message?: string; + data?: string; + chainId?: string; +} + + +interface SendTransactionOptions { + [key: string]: any; +} + +class WalletStacks { + private privateKey: string; + private xApiKey: string; + + constructor(options: WalletOptions) { + this.privateKey = options.privateKey; + this.xApiKey = options.xApiKey; + } + + signTransaction = async (options: any): Promise => { + options.function = "stacksSignTransaction()"; + const validJson = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + const { chainSymbol, to, value, message, data } = options; + let { chainId } = options; + + chainId = await common.getChainId({ chainId, chainSymbol }) ?? "unknown"; + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "Stacks") { + return { + msg: "Stacks wallet can be used only with Stacks chain" + }; + } + + const network: StacksNetwork = chainId === "1700" ? new StacksMainnet() : new StacksTestnet(); + let transaction: StacksTransaction; + let fee: string; + + try { + const apiURL = `${config.url.apiurl}/chain/getgasprice/`; + const params = { + method: "post", + url: apiURL, + data: options, + headers: { + "x-api-key": this.xApiKey + } + }; + const res = await axios(params); + fee = res.data.gasPrice; + } catch (error) { + fee = '1000'; + } + + if (data) { + // Contract Call from Stacks SDK + const parsedData = JSON.parse(atob(data)); + transaction = await makeContractCall({ + ...parsedData, + fee, + network, + senderKey: this.privateKey, + anchorMode: AnchorMode.Any, + }); + } else { + // Transfer token function from Stacks SDK + const privateKeyBuffer = createStacksPrivateKey(this.privateKey); + const publicKeyBuffer = getPublicKey(privateKeyBuffer); + const publicKey = publicKeyToString(publicKeyBuffer); + transaction = await makeUnsignedSTXTokenTransfer({ + network, + recipient: to, + amount: value, + fee, + memo: message || "through expand", + publicKey: publicKey, + anchorMode: AnchorMode.Any, + }); + } + + const signer = new TransactionSigner(transaction); + signer.signOrigin(createStacksPrivateKey(this.privateKey)); + + // Serialize the signed transaction + const serializedTx = transaction.serialize(); + const rawTransaction = bytesToHex(serializedTx); + + return { chainId, rawTransaction }; + }; + + sendTransaction = async (options: SendTransactionOptions): Promise => { + const filterOptions = { ...options }; + filterOptions.function = "sendTransaction()"; + const validJson = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/sendtransaction/`; + const params = { + method: "post", + url: apiURL, + data: options, + headers: { + "x-api-key": this.xApiKey + } + }; + + const transactionHash = await axios(params); + return transactionHash.data; + } catch (error) { + return error; + } + }; +} + +const getStacksPrivateKey = async (mnemonic: string, password: string) => { + const wallet = await generateWallet({ + secretKey: mnemonic, + password + }); + + const wallets = wallet.accounts.map(account => ({ + mainnetAddress: getStxAddress({ + account, + transactionVersion: TransactionVersion.Mainnet + }), + testnetAddress: getStxAddress({ + account, + transactionVersion: TransactionVersion.Testnet + }), + privateKey: account.stxPrivateKey, + })); + + return wallets; +}; + +export { WalletStacks, getStacksPrivateKey }; \ No newline at end of file diff --git a/src/adapters/WalletStellar/index.ts b/src/adapters/WalletStellar/index.ts new file mode 100644 index 0000000..2931999 --- /dev/null +++ b/src/adapters/WalletStellar/index.ts @@ -0,0 +1,98 @@ +import { TransactionBuilder, Keypair } from "stellar-sdk"; +import * as schemaValidator from '../../../configuration/schemaValidator'; +import * as common from '../../../configuration/common'; +import config from '../../../configuration/config'; +import axios, { AxiosRequestConfig } from "axios"; + +interface WalletStellarOptions { + privateKey: string; + xApiKey: string; +} + +interface SignTransactionOptions { + function?: string; + chainSymbol: string; + data: string; + chainId?: string; +} + +interface SendTransactionOptions { + function?: string; + chainSymbol: string; + data: string; + chainId?: string; +} + +class WalletStellar { + private privateKey: string; + private xApiKey: string; + + constructor(options: WalletStellarOptions) { + this.privateKey = options.privateKey; + this.xApiKey = options.xApiKey; + } + + signTransaction = async (options: SignTransactionOptions) => { + options.function = "stellarSignTransaction()"; + const validJson = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + const { chainSymbol, data } = options; + let { chainId } = options; + + chainId = await common.getChainId({ chainId, chainSymbol }) || undefined; + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "Stellar") { + return { + msg: "Stellar wallet can be used only with Stellar chain", + }; + } + + const userKeyPair = Keypair.fromSecret(this.privateKey); + const networkPassphrase = config.chains[chainId as keyof typeof config.chains]?.networkPassphrase; + + if (!networkPassphrase) { + return { msg: "Network passphrase is not defined" }; + } + + let rawTransaction = TransactionBuilder.fromXDR(data, networkPassphrase); + rawTransaction.sign(userKeyPair); + const xdrString = rawTransaction.toEnvelope().toXDR("base64"); + + + return { rawTransaction: xdrString, chainId }; + }; + + sendTransaction = async (options: SendTransactionOptions) => { + const filterOptions = options; + filterOptions.function = "sendTransaction()"; + const validJson = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/sendtransaction/`; + const params: AxiosRequestConfig = { + method: "post", + url: apiURL, + data: options, + headers: { + "x-api-key": this.xApiKey, + }, + }; + + const transactionHash = await axios(params); + return transactionHash.data; + } catch (error) { + return error; + } + }; +} + +export { WalletStellar }; diff --git a/src/adapters/WalletTON/index.ts b/src/adapters/WalletTON/index.ts new file mode 100644 index 0000000..7f5b336 --- /dev/null +++ b/src/adapters/WalletTON/index.ts @@ -0,0 +1,137 @@ +import { mnemonicToPrivateKey, keyPairFromSecretKey } from "ton-crypto"; +import { WalletContractV4, internal, Cell } from "@ton/ton"; +import * as schemaValidator from '../../../configuration/schemaValidator'; +import * as common from '../../../configuration/common'; +import { initialiseWeb3 } from "../../../configuration/intialiseWeb3"; +import config from '../../../configuration/config'; + +interface WalletTONOptions { + privateKey: string; + xApiKey: string; +} + +interface TransactionObject { + function?: string; + chainId: string; + chainSymbol?: string; + to: string; + value: string; + message?: string; + rawTransaction?: any; +} + +class WalletTON { + private privateKey: Buffer; + private keyPair: { publicKey: Uint8Array; secretKey: Uint8Array }; + private xApiKey: string; + private wallet: WalletContractV4; + private _nanotons = 10 ** 9; + + constructor(options: WalletTONOptions) { + this.privateKey = Buffer.from(options.privateKey, 'hex'); + this.keyPair = keyPairFromSecretKey(this.privateKey); + this.xApiKey = options.xApiKey; + + this.wallet = WalletContractV4.create({ + publicKey: Buffer.from(this.keyPair.publicKey), + workchain: 0 + }); + } + + static async getPrivateKey(mnemonic: string): Promise { + const arr = mnemonic.split(" "); + const keyPair = await mnemonicToPrivateKey(arr); + const privateKey = Buffer.from(keyPair.secretKey).toString('hex'); + return privateKey; + } + + async signTransaction(transactionObject: TransactionObject): Promise { + const configuration = { params: {} }; + transactionObject.function = "tonTxObject()"; + const validJson = await schemaValidator.validateInput(transactionObject); + + if (!validJson.valid) { + return validJson; + } + + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol + }); + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "TON") { + return { + msg: "ton wallet can be used only with TON chain" + }; + } + + const web3 = await initialiseWeb3({ chainId, key: this.xApiKey }); + const walletContract = web3.open(this.wallet); + + let body; + try { + body = Cell.fromBase64(transactionObject?.message || "through expand"); + } catch (error) { + body = transactionObject.message || "through expand"; // Optional comment + } + + const seqno = await walletContract.getSeqno(); + const rawData = await walletContract.createTransfer({ + secretKey: this.privateKey, + seqno, + messages: [ + internal({ + to: transactionObject.to, + value: BigInt(transactionObject.value) / BigInt(this._nanotons), // Convert to bigint + body, + bounce: false + }) + ] + }); + + return { rawTransaction: rawData, chainId }; + } + + async sendTransaction(transactionObject: TransactionObject): Promise { + transactionObject.function = "TONTransaction()"; + const validJson = await schemaValidator.validateInput(transactionObject); + if (!validJson.valid) { + return validJson; + } + + try { + const chainId = await common.getChainId({ + chainId: transactionObject.chainId, + chainSymbol: transactionObject.chainSymbol + }); + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + const web3 = await initialiseWeb3({ chainId, key: this.xApiKey }); + + if (chainName !== "TON") { + return { + msg: "ton wallet can be used only with TON chain" + }; + } + + const walletContract = web3.open(this.wallet); + const currentSeqno = await walletContract.getSeqno(); + const txHash = await walletContract.send(transactionObject.rawTransaction); + const timer = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + await timer(5000); + const seqno = await walletContract.getSeqno(); + + if (seqno > currentSeqno) { + return { seqno, message: "transaction has been sent to the blockchain" }; + } + + return { + message: "transaction has been sent to the blockchain" + }; + } catch (error) { + return error; + } + } +} + +export { WalletTON }; \ No newline at end of file diff --git a/src/adapters/WalletXRPL/index.ts b/src/adapters/WalletXRPL/index.ts new file mode 100644 index 0000000..10f0c31 --- /dev/null +++ b/src/adapters/WalletXRPL/index.ts @@ -0,0 +1,92 @@ +import axios from "axios"; +import xrpl from 'xrpl'; +import * as schemaValidator from '../../../configuration/schemaValidator'; +import * as common from '../../../configuration/common'; +import config from '../../../configuration/config'; + +interface WalletXRPLConstructorOptions { + privateKey: string; + xApiKey: string; +} + +interface SignTransactionOptions { + privateKey?: string; + xApiKey?: string; + chainSymbol: string; + data: string; + chainId?: string | number; + function?: string; +} + +interface SendTransactionOptions { + [key: string]: any; + function?: string; +} + +class WalletXRPL { + private privateKey: string; + private xApiKey: string; + + constructor(options: WalletXRPLConstructorOptions) { + this.privateKey = options.privateKey; + this.xApiKey = options.xApiKey; + } + + public signTransaction = async (options: SignTransactionOptions): Promise<{ chainId: string | number; rawTransaction?: string; msg?: string }> => { + options.function = "xrplSignTransaction()"; + const validJson = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + const { chainSymbol, data } = options; + let { chainId } = options; + + chainId = (await common.getChainId({ chainId, chainSymbol })) ?? "unknown"; + const chainName = config.chains[chainId as keyof typeof config.chains].chainName; + + if (chainName !== "XRPL") { + return { + chainId, + msg: "XRPL wallet can be used only with XRP Ledger", + }; + } + + const account = xrpl.Wallet.fromMnemonic(this.privateKey); + + const decodedData = JSON.parse(Buffer.from(data, 'base64').toString('utf-8')); + const signedTx = account.sign(decodedData); + + return { chainId, rawTransaction: signedTx.tx_blob }; + }; + + public sendTransaction = async (options: SendTransactionOptions): Promise => { + const filterOptions = options; + filterOptions.function = "sendTransaction()"; + const validJson = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/sendtransaction/`; + const params = { + method: "post", + url: apiURL, + data: options, + headers: { + "x-api-key": this.xApiKey, + }, + }; + + const transactionHash = await axios(params); + return transactionHash.data; + } catch (error) { + return error; + } + }; +} + +export { WalletXRPL }; diff --git a/src/dydx/cancelOrder.ts b/src/dydx/cancelOrder.ts new file mode 100644 index 0000000..2e2aeb3 --- /dev/null +++ b/src/dydx/cancelOrder.ts @@ -0,0 +1,60 @@ +import axios from 'axios'; +import { getSubAccountCompositeClient } from '../../configuration/dYdXCommon'; +import { OrderFlags } from '@dydxprotocol/v4-client-js'; +import errorMessage from '../../configuration/errorMessage'; + +interface CancelOrderOptions { + subAccountNumber: number; + mnemonic: string; + orderId: string; + goodTillTimeInSeconds: string; +} + +interface OrderResponse { + clientId: string; + ticker: string; +} + +interface ErrorResponse { + message: string; + code: number; +} + +export const cancelOrder = async (options: CancelOrderOptions): Promise => { + const { subAccountNumber, mnemonic, orderId, goodTillTimeInSeconds } = options; + + const { client, subaccount } = await getSubAccountCompositeClient(mnemonic, subAccountNumber); + + const orderConfig = { + method: 'get' as const, + maxBodyLength: Infinity, + url: `https://indexer.v4testnet.dydx.exchange/v4/orders/${orderId}`, + headers: {}, + }; + + let order: OrderResponse; + + try { + const res = await axios.request(orderConfig); + order = res.data; // Assuming the response directly contains `clientId` and `ticker` + } catch (err) { + return { + message: errorMessage.error.message.invalidOrderId, + code: errorMessage.error.code.invalidInput, + } as ErrorResponse; + } + + try { + const tx = await client.cancelOrder( + subaccount, + Number(order.clientId), + OrderFlags.LONG_TERM, + order.ticker, + 0, + Number(goodTillTimeInSeconds) + ); + return tx; + } catch (error: any) { + return error.message; + } +}; diff --git a/src/dydx/deposit.ts b/src/dydx/deposit.ts new file mode 100644 index 0000000..3e10a11 --- /dev/null +++ b/src/dydx/deposit.ts @@ -0,0 +1,98 @@ +import axios from 'axios'; +import config from '../../configuration/config'; +import tokenConfig from '../../configuration/squidRouterTokenConfig'; +import { Wallet } from '../adapters/Wallet'; +import dotenv from 'dotenv'; +import errorMessage from '../../configuration/errorMessage'; + +dotenv.config(); + +interface DepositOptions { + srcChainId: number; + from: string; + to: string; + amountIn: string; + tokenIn: string; + slippage: number; + gas: number; + privateKey: string; +} + +interface ErrorResponse { + message: string; + code: string; +} + +interface TransactionRequest { + gasPrice: string; + data: string; + targetAddress: string; + value: string; +} + +export const deposit = async (options: DepositOptions): Promise => { + const { + srcChainId: fromChain, + from: fromAddress, + to: toAddress, + amountIn: fromAmount, + tokenIn, + slippage, + gas, + privateKey + } = options; + + const fromToken = tokenConfig[fromChain]?.[tokenIn.toUpperCase()]; + + if (fromToken === undefined) { + return { + message: errorMessage.error.message.invalidSrcToken, + code: errorMessage.error.code.invalidInput, + }; + } + + const routeURL = `${config.dYdXV4.squidRouterAPIBaseUrl}route`; + + let result; + try { + result = await axios.get(routeURL, { + params: { + fromChain, + fromToken, + fromAddress, + fromAmount, + toChain: config.dYdXV4.chainId, + toToken: config.dYdXV4.USDC, + toAddress, + slippage, + quoteOnly: false, + }, + }); + } catch (err: any) { + return err.response?.data || { message: 'An unknown error occurred.' }; + } + + const { gasPrice, data, targetAddress: to, value } = result.data.route.transactionRequest as TransactionRequest; + + const wallet = new Wallet({ + privateKey, + xApiKey: process.env.xApiKey || '', + }); + + const createTransaction = await wallet.signTransaction({ + chainId: String(fromChain), + from: fromAddress, + gas: String(gas), + gasPrice, + data, + value, + to, + }); + + if (!createTransaction?.name?.valid) { + return new Error('Invalid transaction: ' + createTransaction?.message); + } + + const transactionReceipt = await wallet.sendTransaction(createTransaction); + return transactionReceipt; +}; diff --git a/src/dydx/index.ts b/src/dydx/index.ts new file mode 100644 index 0000000..d47d533 --- /dev/null +++ b/src/dydx/index.ts @@ -0,0 +1,87 @@ +import * as schemaValidator from '../../configuration/schemaValidator'; +import * as PlaceOrder from './placeOrder'; +import * as UserOnboarding from './userOnboarding'; +import * as CancelOrder from './cancelOrder'; +import * as Transfer from './transfer'; +import * as Deposit from './deposit'; + +interface Options { + side?: string; + type?: string; + timeInForce?: string; + market?: string; + [key: string]: any; +} + +interface ValidationResult { + valid: boolean; + errors?: string[]; + [key: string]: any; +} + +interface CancelOrderOptions { + subAccountNumber: string; + mnemonic: string; + orderId: string; + goodTillTimeInSeconds: number; + function: string; + side?: string; + type?: string; + timeInForce?: string; + market?: string; +} + +export const userOnboarding = async (options: Options): Promise => { + const filterOptions = { ...options, function: "userOnboardingDYDX()" }; + const validJson: ValidationResult = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) return validJson; + return UserOnboarding.userOnboarding(options); +}; + +export const placeOrder = async (options: Options): Promise => { + let filterOptions = options; + + const { side, type, timeInForce, market } = filterOptions; + filterOptions = { + ...filterOptions, + side: side?.toUpperCase(), + type: type?.toUpperCase(), + timeInForce: timeInForce?.toUpperCase(), + market: market?.toUpperCase(), + function: "placeOrderDYDX()", + }; + + const validJson: ValidationResult = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) return validJson; + return PlaceOrder.placeOrder(filterOptions); +}; + +export const cancelOrder = async (options: Options): Promise => { + const filterOptions = { ...options, function: "cancelOrderDYDX()" }; + const validJson: ValidationResult = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) return validJson; + return CancelOrder.cancelOrder(filterOptions); +}; + +export const transfer = async (options: Options): Promise => { + const filterOptions = { ...options, function: "transferDYDX()" }; + const validJson: ValidationResult = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) return validJson; + return Transfer.transfer(filterOptions); +}; + +export const deposit = async (options: Options): Promise => { + const filterOptions = { ...options, function: "depositDYDX()" }; + const validJson: ValidationResult = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) return validJson; + try { + return Deposit.deposit(filterOptions); + } catch (err) { + return err; + } +}; diff --git a/src/dydx/placeOrder.ts b/src/dydx/placeOrder.ts new file mode 100644 index 0000000..5ad9d0f --- /dev/null +++ b/src/dydx/placeOrder.ts @@ -0,0 +1,68 @@ +import { OrderExecution, OrderType, OrderSide, OrderTimeInForce } from '@dydxprotocol/v4-client-js'; +import { getSubAccountCompositeClient } from '../../configuration/dYdXCommon'; +import config from "../../configuration/config"; + +interface PlaceOrderOptions { + subAccountNumber: number; + mnemonic: string; + market: string; + type: keyof typeof OrderType; + side: keyof typeof OrderSide; + timeInForce: keyof typeof OrderTimeInForce; + time: number; + price: number; + postOnly: boolean; + reduceOnly: boolean; + triggerPrice?: number; + size: number; +} + +export const placeOrder = async (options: PlaceOrderOptions) => { + const { + subAccountNumber, + mnemonic, + market, + type: orderType, + side: orderSide, + timeInForce: orderTimeInForce, + time, + price, + postOnly, + reduceOnly, + triggerPrice, + size + } = options; + + const { client, subaccount } = await getSubAccountCompositeClient(mnemonic, subAccountNumber); + + try { + const type = OrderType[orderType]; + const side = OrderSide[orderSide]; + const timeInForce = OrderTimeInForce[orderTimeInForce]; + const timeInForceSeconds = timeInForce === OrderTimeInForce.GTT ? time : 0; + + let tx = await client.placeOrder( + subaccount, + market, + type, + side, + price, + size, + Math.floor(Math.random() * config.dYdXV4.MAX_CLIENT_ID), + timeInForce, + timeInForceSeconds, + OrderExecution.DEFAULT, + postOnly, + reduceOnly, + triggerPrice + ); + + if (typeof tx.hash === 'object') { + tx = { ...(tx as any), hex: `0x${Buffer.from(tx.hash).toString('hex')}` }; + } + + return tx; + } catch (error) { + return (error as Error).message; + } +}; diff --git a/src/dydx/transfer.ts b/src/dydx/transfer.ts new file mode 100644 index 0000000..cc9cd94 --- /dev/null +++ b/src/dydx/transfer.ts @@ -0,0 +1,50 @@ +import Long from 'long'; +import { Method } from '@cosmjs/tendermint-rpc'; +import { getSubAccountValidatorClient } from '../../configuration/dYdXCommon'; +import { EncodeObject } from '@cosmjs/proto-signing'; + +interface TransferOptions { + subAccountNumber: number; + mnemonic: string; + recipient: string; + assetId: number; + amount: string | number | Long; +} + +export const transfer = async (options: TransferOptions): Promise => { + const { subAccountNumber, mnemonic, recipient, assetId } = options; + let amount: Long = Long.fromValue(options.amount); + + const { client, subaccount } = await getSubAccountValidatorClient(mnemonic, subAccountNumber); + // amount = Long.fromValue(amount); + + const msgs: Promise = new Promise((resolve) => { + const msg = client.post.composer.composeMsgWithdrawFromSubaccount( + subaccount.address, + subaccount.subaccountNumber, + Number(assetId), + amount, + recipient, + ); + + resolve([msg]); + }); + + const totalFee = await client.post.simulate( + subaccount.wallet, + () => msgs, + undefined, + ); + + const amountAfterFee = amount.sub(Long.fromString(totalFee.amount[0].amount)); + + const tx = await client.post.withdraw( + subaccount, + assetId, + amountAfterFee, + recipient, + Method.BroadcastTxCommit, + ); + + return tx; +}; diff --git a/src/dydx/userOnboarding.ts b/src/dydx/userOnboarding.ts new file mode 100644 index 0000000..d893d24 --- /dev/null +++ b/src/dydx/userOnboarding.ts @@ -0,0 +1,18 @@ +import { BECH32_PREFIX, LocalWallet } from '@dydxprotocol/v4-client-js'; +import { ethers } from 'ethers-5'; +import config from '../../configuration/config'; +import { deriveHDKeyFromEthereumSignature } from '@dydxprotocol/v4-client-js/build/src/lib/onboarding'; + + +export const userOnboarding= async (options:any) => { + const provider = new ethers.providers.JsonRpcProvider(config.dYdXV4.rpc); + const signer = new ethers.Wallet(options.privateKey, provider); + const {signingMsg} = config.dYdXV4; + + const signature = await signer._signTypedData(signingMsg.domain, { dYdX: signingMsg.types.dYdX }, signingMsg.message); + const keys = deriveHDKeyFromEthereumSignature(signature); + const {mnemonic, publicKey, privateKey} = keys; + const wallet = await LocalWallet.fromMnemonic(mnemonic, BECH32_PREFIX); + return {mnemonic, publicKey, privateKey, address: wallet.address} + } + diff --git a/src/helper/Starknet.ts b/src/helper/Starknet.ts new file mode 100644 index 0000000..232e542 --- /dev/null +++ b/src/helper/Starknet.ts @@ -0,0 +1,72 @@ +/* eslint-disable camelcase */ +import Web3 from 'web3'; +import BN from "bn.js"; +import config from '../../configuration/config'; + +interface Call { + contractAddress: string; + entrypoint: string; + calldata?: string[]; +} + +interface CallArrayItem { + to: string; + selector: string; + data_offset: string; + data_len: string; +} + +interface MulticallArrays { + callArray: CallArrayItem[]; + calldata: string[]; +} + +export const callsToArrayData = async (callObject: Call[]): Promise => { + const web3 = new Web3(); + + function bigNumberishArrayToDecimalStringArray(rawCalldata: (string | number | bigint)[]): string[] { + return rawCalldata.map((x) => BigInt(x).toString(10)); + } + + const transformCallsToMulticallArrays = (calls: Call[]): MulticallArrays => { + const callArray: CallArrayItem[] = []; + const calldata: (string | number | bigint)[] = []; + + calls.forEach((call) => { + const selectordata = new BN( + `${BigInt(BigInt(web3.utils.keccak256(call.entrypoint)).toString(10)).toString(16)}`, + 16 + ).iand(new BN(config.Mask250, 16)); + + const data = call.calldata || []; + callArray.push({ + to: BigInt(call.contractAddress).toString(10), + selector: `${BigInt(`0x${selectordata}`).toString(16)}`, + data_offset: calldata.length.toString(), + data_len: data.length.toString(), + }); + + calldata.push(...data); + }); + + return { + callArray, + calldata: bigNumberishArrayToDecimalStringArray(calldata), + }; + }; + + const fromCallsToExecuteCalldata = (): string[] => { + const { callArray, calldata } = transformCallsToMulticallArrays(callObject); + + return [ + callArray.length.toString(), + ...callArray + .map(({ to, selector, data_offset, data_len }) => [to, selector, data_offset, data_len]) + .flat(), + calldata.length.toString(), + ...calldata, + ]; + }; + + return fromCallsToExecuteCalldata(); +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..dabbe55 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,91 @@ +import axios from "axios"; +import config from "../configuration/config"; +import * as schemaValidator from "../configuration/schemaValidator"; +import { + Wallet, + WalletFordefi, + WalletDFNS, + WalletTON, + WalletFireblocks, + WalletPhantom, + WalletCoinbase, + WalletCircle, + WalletCosmos, + WalletStellar, + WalletXRPL, + WalletStacks, + WalletBitcoin +} from "./interfaces/index"; + +export const prepareTransaction = async (apiURL:any, options:any) => { + const filterOptions = options; + filterOptions.function = "prepareTransaction()"; + const validJson = await schemaValidator.validateInput(filterOptions); + + if (!validJson.valid) { + return validJson; + } + + const { chainId, xApiKey } = filterOptions; + try { + const paramConfig = { + method: "post", + url: apiURL, + data: filterOptions, + headers: { + "x-api-key": xApiKey, + }, + }; + + const response = await axios(paramConfig).then((result) => result.data); + if (chainId) response.data.chainId = chainId; + return response.data; + } catch (error) { + return error; + } +}; + +export const decodeTransaction = async (options:any) => { + const filterOptions = options; + filterOptions.function = "decodeTransaction()"; + const validJson = await schemaValidator.validateInput(options); + + if (!validJson.valid) { + return validJson; + } + + try { + const apiURL = `${config.url.apiurl}/chain/decodetransaction/`; + + const paramConfig = { + method: "post", + url: apiURL, + data: filterOptions, + headers: { + "x-api-key": filterOptions.xApiKey, + }, + }; + + const response = await axios(paramConfig).then((result) => result.data); + return response.data; + } catch (error) { + return error; + } +}; + +// Replace `exports.Wallet = Wallet;` with: +export { + Wallet, + WalletFordefi, + WalletDFNS, + WalletTON, + WalletFireblocks, + WalletPhantom, + WalletCoinbase, + WalletCircle, + WalletCosmos, + WalletStellar, + WalletXRPL, + WalletStacks, + WalletBitcoin +}; diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts new file mode 100644 index 0000000..a7d5ae1 --- /dev/null +++ b/src/interfaces/index.ts @@ -0,0 +1,29 @@ +import { Wallet } from '../adapters/Wallet'; +import { WalletFordefi } from '../adapters/WalletFordefi'; +import { WalletDFNS } from '../adapters/WalletDFNS'; +import { WalletPhantom } from '../adapters/WalletPhantom'; +import { WalletCoinbase } from '../adapters/WalletCoinbase'; +import { WalletTON } from '../adapters/WalletTON'; +import { WalletFireblocks } from '../adapters/WalletFireblocks'; +import { WalletCircle } from '../adapters/WalletCircle'; +import { WalletCosmos } from "../adapters/WalletCosmos"; +import { WalletStellar } from '../adapters/WalletStellar'; +import { WalletXRPL } from '../adapters/WalletXRPL'; +import { WalletStacks } from '../adapters/WalletStacks'; +import { WalletBitcoin } from '../adapters/WalletBitcoin'; + +export { + Wallet, + WalletFordefi, + WalletDFNS, + WalletPhantom, + WalletCoinbase, + WalletTON, + WalletFireblocks, + WalletCircle, + WalletStellar, + WalletXRPL, + WalletCosmos, + WalletStacks, + WalletBitcoin +}; \ No newline at end of file diff --git a/test/circleTest.ts b/test/circleTest.ts new file mode 100644 index 0000000..e52018a --- /dev/null +++ b/test/circleTest.ts @@ -0,0 +1,76 @@ +import { WalletCircle, prepareTransaction } from '../src/index'; +import dotenv from 'dotenv'; + +interface CircleWalletOptions { + appId: string; + apiKey: string; + userId: string; + walletId: string; + userToken?: string; + encryptionKey?: string; +} + +interface UserTokenResponse { + userToken: string; + encryptionKey: string; +} + +async function initCircleWallet(options: CircleWalletOptions): Promise { + + const userTokenResponse = await WalletCircle.getUserToken(options); + + + const userToken: UserTokenResponse = typeof userTokenResponse === 'string' + ? JSON.parse(userTokenResponse) + : userTokenResponse; + + + const walletOptions = { + ...options, + userToken: userToken.userToken, + encryptionKey: userToken.encryptionKey, + } as Required; + + + const wallet = new WalletCircle(walletOptions); + return wallet; +} + +async function main() { + + dotenv.config(); + + + const wallet = await initCircleWallet({ + appId: process.env.appId as string, + apiKey: process.env.apiKey as string, + userId: process.env.userId as string, + walletId: process.env.walletId as string, + }); + + + const prepareApproveTx = await prepareTransaction('https://api.expand.network/fungibletoken/approve', { + from: "0x6E5eAf34c73D1CD0be4e24f923b97CF38e10d1f3", + tokenAddress: "0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60", + to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + amount: "10000", + gas: "25000", + xApiKey: process.env.xApiKey as string, + }); + + + const signedTx = await wallet.signTransaction(prepareApproveTx); + + + console.log("signedTx:", signedTx); + + + const tx = await wallet.sendTransaction(signedTx); + + + console.log("sent tx:", tx); +} + +main().catch((error) => { + console.error("An error occurred:", error); +}); diff --git a/test/cosmosTest.ts b/test/cosmosTest.ts new file mode 100644 index 0000000..fa6cffb --- /dev/null +++ b/test/cosmosTest.ts @@ -0,0 +1,20 @@ +import { WalletCosmos } from '../src'; + +const xApiKey = 'X_API_KEY'; + +async function main() { + const preparedTx: any = { + "chainId": "theta-testnet-001", + "from": "cosmos1h7sp085zemehp5gunplymxhflrp8ls4qm3mxrq", + "to": "cosmos1h6r7sgwxfxps4payfyc8rl56svzmx6t5kpumg3", + "value": "1000", + } + + const wallet = new WalletCosmos({ privateKey:'your Mnemonic in English', xApiKey:xApiKey}); + const signedTx = await wallet.signTransaction(preparedTx); + console.log(signedTx); + // const TxHash = await wallet.sendTransaction(signedTx); + // console.log(TxHash); +} + +main(); \ No newline at end of file diff --git a/test/swapTest.ts b/test/swapTest.ts new file mode 100644 index 0000000..35cfd12 --- /dev/null +++ b/test/swapTest.ts @@ -0,0 +1,40 @@ +import { Wallet, prepareTransaction } from "../src/index"; +import dotenv from 'dotenv'; + +async function swap() { + dotenv.config(); + + const xApiKey = "tK503cR23o8YTvXhNoDNo7kQf5sQdbXP8qbqkBeQ"; + const privateKey = "7b030343684de5d0d35dd68333dac40ffaed6f138da85464ca6227b89b17eae9"; + + const wallet = new Wallet({ privateKey , xApiKey }); + + // Preparing transaction + const preparedTx = await prepareTransaction('https://api.expand.network/dex/swap', { + "dexId": "1300", + "path": [ + "0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0", + "0xdac17f958d2ee523a2206206994597c13d831ec7" + ], + "amountIn": "50000000000000000000", + "amountOutMin": "730655", + "to": "0xB409cB0b5DB9f148d0bc491E140D9E0FDd789C11", + "gas": "80000", + "from": "0xB409cB0b5DB9f148d0bc491E140D9E0FDd789C11", + "deadline": "1716461989", + "slippage": "1", + xApiKey, + function: "" + }); + + console.log(preparedTx); + + // Signing transaction + const signedTx = await wallet.signTransaction(preparedTx); + console.log("Transaction Pending....", signedTx); + + // //Sending transaction + // const tx = await wallet.sendTransaction(signedTx); + // console.log("Transaction Pending....", tx.data); +} +swap(); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..00ef3cc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES6", + "module": "CommonJS", + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "moduleResolution": "node", + "sourceMap": true, + "forceConsistentCasingInFileNames": true , + "allowSyntheticDefaultImports": true, + "jsx": "react", + "outDir": "./dist", + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src/**/*", "errorMessage.json"], + "exclude": ["node_modules", "dist"] + } + \ No newline at end of file