Skip to content

Commit

Permalink
Merge pull request #128 from yourtrading-ai/feat/xrpl-connector-v2
Browse files Browse the repository at this point in the history
Feat/XRPL connector v2
  • Loading branch information
rapcmia authored Dec 1, 2023
2 parents 7f9363b + d35d850 commit 81576ca
Show file tree
Hide file tree
Showing 40 changed files with 17,605 additions and 23 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/dist
/db
/.idea
/*.iml
package-lock.json
/certs
/coverage
Expand All @@ -13,4 +14,4 @@ package-lock.json

# VSCode
.vscode/
.history
.history
2 changes: 1 addition & 1 deletion docs/swagger/definitions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2103,4 +2103,4 @@ definitions:
example: 0.5
txHash:
type: 'string'
example: '0x...'
example: '0x...'
12 changes: 11 additions & 1 deletion docs/swagger/wallet-routes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ paths:
operationId: 'get'
produces:
- 'application/json'
parameters:
- in: 'body'
name: 'body'
required: true
schema:
$ref: '#/definitions/GetWalletRequest'
responses:
'200':
description: 'Wallet list'
Expand All @@ -27,7 +33,11 @@ paths:
required: true
schema:
$ref: '#/definitions/AddWalletRequest'
responses: '200'
responses:
'200':
description: 'Added wallet address'
schema:
$ref: '#/definitions/AddWalletResponse'

/wallet/remove:
delete:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"winston": "^3.3.3",
"winston-daily-rotate-file": "^4.5.5",
"xsswap-sdk": "^1.0.1",
"xrpl": "^2.7.0",
"yarn": "^1.22.17"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export const startGateway = async () => {
Math.random().toString(16).substr(2, 14)
);
}
logger.info(`Gateway Version: ${gateway_version}`) // display gateway version
logger.info(`Gateway Version: ${gateway_version}`); // display gateway version
logger.info(`⚡️ Starting Gateway API on port ${port}...`);
if (ConfigManagerV2.getInstance().get('server.unsafeDevModeWithHTTP')) {
logger.info('Running in UNSAFE HTTP! This could expose private keys.');
Expand Down
24 changes: 24 additions & 0 deletions src/chains/xrpl/xrpl-middlewares.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { HttpException } from '../../services/error-handler';
import { XRPL } from './xrpl';
import { NextFunction, Request, Response } from 'express';

export const verifyXRPLIsAvailable = async (
req: Request,
_res: Response,
next: NextFunction
) => {
if (!req || !req.body || !req.body.network) {
throw new HttpException(404, 'No XRPL network informed.');
}

const xrpl = await XRPL.getInstance(req.body.network);
if (!xrpl.ready) {
await xrpl.init();
}

if (!xrpl.isConnected()) {
await xrpl.client.connect();
}

return next();
};
62 changes: 62 additions & 0 deletions src/chains/xrpl/xrpl.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ConfigManagerV2 } from '../../services/config-manager-v2';

export interface NetworkConfig {
name: string;
nodeUrl: string; // example: wss://xrplcluster.com/
tokenListType: string; // default: FILE
tokenListSource: string; // default: src/chains/xrpl/xrpl_tokens.json
marketListType: string; // default: FILE
marketListSource: string; // default: src/chains/xrpl/xrpl_markets.json
nativeCurrencySymbol: string; // XRP
maxLRUCacheInstances: number;
}

export interface Config {
// "mainnet" | "testnet" | "devnet"
network: NetworkConfig;
requestTimeout: number; // default: 20
connectionTimeout: number; // default: 5
feeCushion: number; // default: 1.2
maxFeeXRP: string; // default: 2
orderDbPath: string;
}

// @todo: find out which configs are required
export function getXRPLConfig(chainName: string, networkName: string): Config {
const configManager = ConfigManagerV2.getInstance();
return {
network: {
name: networkName,
nodeUrl: configManager.get(
chainName + '.networks.' + networkName + '.nodeURL'
),
tokenListType: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.tokenListType'
),
tokenListSource: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.tokenListSource'
),
marketListType: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.marketListType'
),
marketListSource: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.marketListSource'
),
nativeCurrencySymbol: ConfigManagerV2.getInstance().get(
chainName + '.networks.' + networkName + '.nativeCurrencySymbol'
),
maxLRUCacheInstances: ConfigManagerV2.getInstance().get(
chainName + '.maxLRUCacheInstances'
),
},
requestTimeout: ConfigManagerV2.getInstance().get(
chainName + '.requestTimeout'
),
connectionTimeout: ConfigManagerV2.getInstance().get(
chainName + '.connectionTimeout'
),
feeCushion: ConfigManagerV2.getInstance().get(chainName + '.feeCushion'),
maxFeeXRP: ConfigManagerV2.getInstance().get(chainName + '.maxFeeXRP'),
orderDbPath: ConfigManagerV2.getInstance().get(chainName + '.orderDbPath'),
};
}
26 changes: 26 additions & 0 deletions src/chains/xrpl/xrpl.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ConfigManagerV2 } from '../../services/config-manager-v2';

const configManager = ConfigManagerV2.getInstance();

export const constants = {
retry: {
all: {
maxNumberOfRetries:
configManager.get('xrpl.retry.all.maxNumberOfRetries') || 0, // 0 means no retries
delayBetweenRetries:
configManager.get('xrpl.retry.all.delayBetweenRetries') || 0, // 0 means no delay (milliseconds)
},
},
timeout: {
all: configManager.get('xrpl.timeout.all') || 0, // 0 means no timeout (milliseconds)
},
parallel: {
all: {
batchSize: configManager.get('xrpl.parallel.all.batchSize') || 0, // 0 means no batching (group all)
delayBetweenBatches:
configManager.get('xrpl.parallel.all.delayBetweenBatches') || 0, // 0 means no delay (milliseconds)
},
},
};

export default constants;
125 changes: 125 additions & 0 deletions src/chains/xrpl/xrpl.controllers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { Wallet } from 'xrpl';
import { XRPTokenInfo, XRPLish } from './xrpl';
import { TokenInfo, latency } from '../../services/base';
import {
HttpException,
LOAD_WALLET_ERROR_CODE,
LOAD_WALLET_ERROR_MESSAGE,
} from '../../services/error-handler';
import { getNetworkId } from '../../chains/xrpl/xrpl.helpers';

import {
XRPLBalanceRequest,
XRPLBalanceResponse,
XRPLPollRequest,
XRPLPollResponse,
} from './xrpl.requests';

import {
validateXRPLBalanceRequest,
validateXRPLPollRequest,
validateXRPLGetTokenRequest,
} from './xrpl.validators';
import { TokensRequest } from '../../network/network.requests';

export class XRPLController {
static async currentBlockNumber(xrplish: XRPLish): Promise<number> {
return xrplish.getCurrentLedgerIndex();
}

static async balances(
xrplish: XRPLish,
req: XRPLBalanceRequest
): Promise<XRPLBalanceResponse> {
const initTime = Date.now();
let wallet: Wallet;

validateXRPLBalanceRequest(req);

try {
wallet = await xrplish.getWallet(req.address);
} catch (err) {
throw new HttpException(
500,
LOAD_WALLET_ERROR_MESSAGE + err,
LOAD_WALLET_ERROR_CODE
);
}

const xrplBalances = await xrplish.getAllBalance(wallet);

const balances: Record<string, string> = {};
xrplBalances.forEach((balance) => {
balances[balance.currency] = balance.value;
});

return {
network: xrplish.network,
timestamp: initTime,
latency: latency(initTime, Date.now()),
address: req.address,
balances,
};
}

static async poll(
xrplish: XRPLish,
req: XRPLPollRequest
): Promise<XRPLPollResponse> {
validateXRPLPollRequest(req);

const initTime = Date.now();
const currentLedgerIndex = await xrplish.getCurrentLedgerIndex();
const txData = await xrplish.getTransaction(req.txHash);
const txStatus = await xrplish.getTransactionStatusCode(txData);
const sequence = txData ? txData.result.Sequence : undefined;
const txLedgerIndex = txData ? txData.result.ledger_index : undefined;

return {
network: xrplish.network,
timestamp: initTime,
currentLedgerIndex: currentLedgerIndex,
sequence: sequence,
txHash: req.txHash,
txStatus: txStatus,
txLedgerIndex: txLedgerIndex,
txData: txData,
};
}

static async getTokens(
xrplish: XRPLish,
req: TokensRequest
): Promise<{ tokens: TokenInfo[] }> {
validateXRPLGetTokenRequest(req);
let xrpTokens: XRPTokenInfo[] = [];
if (!req.tokenSymbols) {
xrpTokens = xrplish.storedTokenList;
} else {
for (const t of req.tokenSymbols as []) {
const arr = xrplish.getTokenForSymbol(t);
if (arr !== undefined) {
arr.forEach((token) => {
xrpTokens.push(token);
});
}
}
}

const tokens: TokenInfo[] = [];

// Convert xrpTokens into tokens
xrpTokens.map((xrpToken) => {
const token: TokenInfo = {
address: xrpToken.issuer,
chainId: getNetworkId(req.network),
decimals: 15,
name: xrpToken.title,
symbol: xrpToken.code,
};
tokens.push(token);
});

return { tokens };
}
}
Loading

0 comments on commit 81576ca

Please sign in to comment.