Skip to content

Commit

Permalink
Merge pull request #379 from hummingbot/staging
Browse files Browse the repository at this point in the history
sync / Gateway staging -> master for Hummingbot version 2.2.0
  • Loading branch information
nikspz authored Dec 27, 2024
2 parents 240b51c + 52dcf08 commit ba54f2b
Show file tree
Hide file tree
Showing 36 changed files with 4,749 additions and 364 deletions.
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hummingbot-gateway",
"version": "2.1.0",
"version": "2.2.0",
"description": "Middleware that helps Hummingbot clients access standardized DEX API endpoints on different blockchain networks",
"main": "index.js",
"license": "Apache-2.0",
Expand Down Expand Up @@ -51,6 +51,12 @@
"@pancakeswap/v3-periphery": "^1.0.2",
"@pancakeswap/v3-sdk": "^3.7.0",
"@pangolindex/sdk": "^1.1.0",
"@coral-xyz/anchor": "^0.29.0",
"@solana/web3.js": "^1.95.8",
"@solana/spl-token": "0.4.8",
"@solana/spl-token-registry": "^0.2.4574",
"@solflare-wallet/utl-sdk": "^1.4.0",
"@jup-ag/api": "^6.0.29",
"@sushiswap/sdk": "^5.0.0-canary.116",
"@taquito/rpc": "^17.0.0",
"@taquito/signer": "^17.0.0",
Expand Down Expand Up @@ -115,7 +121,6 @@
"@babel/runtime": "^7.0",
"@connectis/diff-test-coverage": "^1.5.1",
"@improbable-eng/grpc-web": "^0.13.0",
"@solana/web3.js": "^1.58.0",
"@types/app-root-path": "^1.2.4",
"@types/big.js": "^6.1.3",
"@types/bs58": "^4.0.1",
Expand Down
24 changes: 19 additions & 5 deletions src/amm/amm.controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ import {
poolPrice as uniswapV3PoolPrice,
estimateGas as uniswapEstimateGas,
} from '../connectors/uniswap/uniswap.controllers';
import {
price as jupiterPrice,
trade as jupiterTrade,
estimateGas as jupiterEstimateGas,
} from '../connectors/jupiter/jupiter.controllers';
import {
price as carbonPrice,
trade as carbonTrade,
Expand All @@ -50,15 +55,18 @@ import {
Uniswapish,
UniswapLPish,
} from '../services/common-interfaces';
import { Solanaish } from '../chains/solana/solana';
import { Algorand } from '../chains/algorand/algorand';
import { Tinyman } from '../connectors/tinyman/tinyman';
import { Plenty } from '../connectors/plenty/plenty';
import { Osmosis } from '../chains/osmosis/osmosis';
import { Solana } from '../chains/solana/solana';
import { Jupiter } from '../connectors/jupiter/jupiter';
import { Carbonamm } from '../connectors/carbon/carbonAMM';

export async function price(req: PriceRequest): Promise<PriceResponse> {
const chain = await getInitializedChain<
Algorand | Ethereumish | Tezosish | Osmosis
Algorand | Ethereumish | Tezosish | Osmosis | Solana
>(req.chain, req.network);
if (chain instanceof Osmosis){
return chain.controller.price(chain as unknown as Osmosis, req);
Expand All @@ -73,6 +81,8 @@ export async function price(req: PriceRequest): Promise<PriceResponse> {

if (connector instanceof Plenty) {
return plentyPrice(<Tezosish>chain, connector, req);
} else if (connector instanceof Jupiter) {
return jupiterPrice(<Solanaish>chain, connector, req);
} else if (connector instanceof Carbonamm) {
return carbonPrice(<Ethereumish>chain, connector, req);
} else if ('routerAbi' in connector) {
Expand All @@ -84,7 +94,7 @@ export async function price(req: PriceRequest): Promise<PriceResponse> {

export async function trade(req: TradeRequest): Promise<TradeResponse> {
const chain = await getInitializedChain<
Algorand | Ethereumish | Tezosish | Osmosis
Algorand | Ethereumish | Tezosish | Osmosis | Solana
>(req.chain, req.network);
if (chain instanceof Osmosis){
return chain.controller.trade(chain as unknown as Osmosis, req);
Expand All @@ -99,6 +109,8 @@ export async function trade(req: TradeRequest): Promise<TradeResponse> {

if (connector instanceof Plenty) {
return plentyTrade(<Tezosish>chain, connector, req);
} else if (connector instanceof Jupiter) {
return jupiterTrade(<Solanaish>chain, connector, req);
} else if (connector instanceof Carbonamm) {
return carbonTrade(<Ethereumish>chain, connector, req);
} else if ('routerAbi' in connector) {
Expand Down Expand Up @@ -189,21 +201,23 @@ export async function estimateGas(
req: NetworkSelectionRequest
): Promise<EstimateGasResponse> {
const chain = await getInitializedChain<
Algorand | Ethereumish | Tezosish | Osmosis
Algorand | Ethereumish | Tezosish | Osmosis | Solana
>(req.chain, req.network);
if (chain instanceof Osmosis){
return chain.controller.estimateGas(chain as unknown as Osmosis);
}

const connector: Uniswapish | Tinyman | Plenty =
await getConnector<Uniswapish | Tinyman | Plenty>(
const connector: Uniswapish | Tinyman | Plenty | Jupiter =
await getConnector<Uniswapish | Tinyman | Plenty | Jupiter>(
req.chain,
req.network,
req.connector
);

if (connector instanceof Plenty) {
return plentyEstimateGas(<Tezosish>chain, connector);
} else if (connector instanceof Jupiter) {
return jupiterEstimateGas(<Solanaish>chain, connector);
} else if (connector instanceof Carbonamm) {
return carbonEstimateGas(<Ethereumish>chain, connector);
} else if ('routerAbi' in connector) {
Expand Down
2 changes: 1 addition & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const startSwagger = async () => {

export const startGateway = async () => {
const port = ConfigManagerV2.getInstance().get('server.port');
const gateway_version="2.1.0"
const gateway_version="2.2.0"
if (!ConfigManagerV2.getInstance().get('server.id')) {
ConfigManagerV2.getInstance().set(
'server.id',
Expand Down
18 changes: 9 additions & 9 deletions src/chains/cosmos/cosmos-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,15 +218,15 @@ export class CosmosBase {
}

async getLatestBasePrice(): Promise<number> {
var eipPrice = this.manualGasPrice;
if (this.useEIP1559DynamicBaseFeeInsteadOfManualGasPrice){
const eipPrice = await getEIP1559DynamicBaseFee(this.rpcAddressDynamicBaseFee);
if (eipPrice != ''){
this.manualGasPrice = Number(eipPrice);
let eipPrice = this.manualGasPrice;
if (this.useEIP1559DynamicBaseFeeInsteadOfManualGasPrice) {
const dynamicPrice = await getEIP1559DynamicBaseFee(this.rpcAddressDynamicBaseFee);
if (dynamicPrice != '') {
eipPrice = Number(dynamicPrice);
}
}
}
this.manualGasPrice = eipPrice;
return this.manualGasPrice
return this.manualGasPrice;
}

async loadTokens(
Expand All @@ -248,12 +248,12 @@ export class CosmosBase {
tokenListType: TokenListType
): Promise<CosmosAsset[]> {
let tokens: CosmosAsset[] = [];
let tokensJson = [];
let tokensJson: { assets: any[] };

if (tokenListType === 'URL') {
({ data: tokensJson } = await axios.get(tokenListSource));
} else {
(tokensJson = JSON.parse(await fs.readFile(tokenListSource, 'utf8')));
tokensJson = JSON.parse(await fs.readFile(tokenListSource, 'utf8'));
}
for (var tokenAssetIdx=0; tokenAssetIdx<tokensJson.assets.length; tokenAssetIdx++){
var tokenAsset = tokensJson.assets[tokenAssetIdx];
Expand Down
30 changes: 23 additions & 7 deletions src/chains/ethereum/evm.controllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,30 @@ export class EVMController {
validatePollRequest(req);

const currentBlock = await ethereumish.getCurrentBlockNumber();
const txData = await ethereumish.getTransaction(req.txHash);
let txData = await ethereumish.getTransaction(req.txHash);
let txBlock, txReceipt, txStatus;
if (!txData) {
// tx not found, didn't reach the mempool or it never existed
txBlock = -1;
txReceipt = null;
txStatus = -1;
} else {
const MAX_RETRIES = 3;
const RETRY_DELAY_MS = 1000;
let retryCount = 0;

while (retryCount < MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, RETRY_DELAY_MS));
txData = await ethereumish.getTransaction(req.txHash);
if (txData) break;
retryCount++;
}

if (!txData) {
// tx not found after retries
logger.info(`Transaction ${req.txHash} not found in mempool or does not exist after ${MAX_RETRIES} retries.`);
txBlock = -1;
txReceipt = null;
txStatus = -1;
}
}

if (txData) {
txReceipt = await ethereumish.getTransactionReceipt(req.txHash);
if (txReceipt === null) {
// tx is in the mempool
Expand Down Expand Up @@ -195,7 +211,7 @@ export class EVMController {
txBlock,
txStatus,
txData: toEthereumTransactionResponse(txData),
txReceipt: toEthereumTransactionReceipt(txReceipt),
txReceipt: toEthereumTransactionReceipt(txReceipt || null),
};
}

Expand Down
50 changes: 50 additions & 0 deletions src/chains/solana/solana.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { TokenListType } from '../../services/base';
import { ConfigManagerV2 } from '../../services/config-manager-v2';
interface NetworkConfig {
name: string;
nodeURLs: string;
tokenListType: TokenListType;
tokenListSource: string;
nativeCurrencySymbol: string;
}

export interface Config {
network: NetworkConfig;
tokenProgram: string;
transactionLamports: number;
lamportsToSol: number;
timeToLive: number;
}

export function getSolanaConfig(
chainName: string,
networkName: string
): Config {
return {
network: {
name: networkName,
nodeURLs: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.nodeURLs'
),
tokenListType: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.tokenListType'
),
tokenListSource: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.tokenListSource'
),
nativeCurrencySymbol: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.nativeCurrencySymbol'
),
},
tokenProgram: ConfigManagerV2.getInstance().get(
chainName + '.tokenProgram'
),
transactionLamports: ConfigManagerV2.getInstance().get(
chainName + '.transactionLamports'
),
lamportsToSol: ConfigManagerV2.getInstance().get(
chainName + '.lamportsToSol'
),
timeToLive: ConfigManagerV2.getInstance().get(chainName + '.timeToLive'),
};
}
19 changes: 19 additions & 0 deletions src/chains/solana/solana.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const constants = {
retry: {
all: {
maxNumberOfRetries: 0, // 0 means no retries
delayBetweenRetries: 0, // 0 means no delay (milliseconds)
},
},
timeout: {
all: 0, // 0 means no timeout (milliseconds)
},
parallel: {
all: {
batchSize: 0, // 0 means no batching (group all)
delayBetweenBatches: 0, // 0 means no delay (milliseconds)
},
},
};

export default constants;
77 changes: 77 additions & 0 deletions src/chains/solana/solana.controllers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// import { tokenValueToString } from '../../services/base';
import {
BalanceRequest,
TokensRequest,
PollRequest,
} from '../../network/network.requests';
import { CustomTransactionResponse } from '../../services/common-interfaces';
import {
HttpException,
LOAD_WALLET_ERROR_CODE,
LOAD_WALLET_ERROR_MESSAGE,
} from '../../services/error-handler';
import { TokenInfo } from '../ethereum/ethereum-base';

import { Keypair, TransactionResponse } from '@solana/web3.js';
import { Solanaish } from './solana';
import { getNotNullOrThrowError } from './solana.helpers';

export class SolanaController {

static async balances(solanaish: Solanaish, req: BalanceRequest) {
let wallet: Keypair;
try {
wallet = await solanaish.getWallet(req.address);
} catch (err) {
throw new HttpException(
500,
LOAD_WALLET_ERROR_MESSAGE + err,
LOAD_WALLET_ERROR_CODE
);
}

const balances = await solanaish.getBalance(wallet, req.tokenSymbols);

return { balances };
}

static async poll(solanaish: Solanaish, req: PollRequest) {
const currentBlock = await solanaish.getCurrentBlockNumber();
const txData = getNotNullOrThrowError<TransactionResponse>(
await solanaish.getTransaction(req.txHash as any)
);
const txStatus = await solanaish.getTransactionStatusCode(txData);

console.log(`Polling for transaction ${req.txHash}, Status: ${txStatus}`);

return {
currentBlock: currentBlock,
txHash: req.txHash,
txBlock: txData.slot,
txStatus: txStatus,
txData: txData as unknown as CustomTransactionResponse | null,
};
}

static async getTokens(solanaish: Solanaish, req: TokensRequest) {
let tokens: TokenInfo[] = [];

if (!req.tokenSymbols) {
tokens = solanaish.storedTokenList;
} else {
for (const symbol of req.tokenSymbols as string[]) {
const token = solanaish.getTokenBySymbol(symbol);
if (token) {
tokens.push(token);
}
}
}

return { tokens };
}
}

export const balances = SolanaController.balances;
export const poll = SolanaController.poll;
export const getTokens = SolanaController.getTokens;
export let priorityFeeMultiplier: number = 1;
Loading

0 comments on commit ba54f2b

Please sign in to comment.