Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/XRPL connector v2 #128

Merged
merged 45 commits into from Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bb9e7df
Updating .gitignore.
Danilo-Araujo-Silva Feb 17, 2023
df59dad
Add XRPL & XRPLDEX connector (v1)
mlguys Feb 17, 2023
8ed27ad
add unified clob swagger schema
mlguys Feb 27, 2023
f01cf29
Fix swagger missing field error for get orderbook
mlguys Mar 1, 2023
14d0e87
update yarn lock
mlguys Mar 24, 2023
b0a0bc1
wip
mlguys Jun 1, 2023
22b585c
WIP
mlguys Jun 4, 2023
a820ae3
WIP
mlguys Jun 6, 2023
f2a2bbc
Add order tracker logic wip
mlguys Jun 6, 2023
b1411d7
Finish connecter and add routes test
mlguys Jun 8, 2023
ad5704a
Remove old implementation and disable comments
mlguys Jun 8, 2023
8ba3776
Remove artifacts
mlguys Jun 8, 2023
3f49fba
Move order tracker to chain directory
mlguys Jun 8, 2023
b2b589e
add space
mlguys Jun 8, 2023
056e212
WIP
mlguys Jun 9, 2023
0af5945
Add basic e2e test scripts + fix bugs
mlguys Jun 26, 2023
6609840
Finish order posting and cancelling scenario test
mlguys Jun 27, 2023
1111b27
Finish fixing order tracking
mlguys Jun 30, 2023
4547c7e
merge development + fix test cases
mlguys Jul 14, 2023
d7844c8
Add tracking fills
mlguys Jul 26, 2023
edc2d9c
add orderhash to filldata
mlguys Jul 27, 2023
1737d24
fix balances chain endpoint for xrpl
mlguys Aug 1, 2023
32d96ea
update getTokens endpoint
mlguys Aug 1, 2023
7c81471
add get midprice for a market
mlguys Aug 2, 2023
a6f1a2b
fix getTokens
mlguys Aug 2, 2023
be86ef0
Fix bugs
mlguys Aug 8, 2023
8443ba3
fix getting order status & trading fees
mlguys Aug 10, 2023
d941a4e
Prepare PR WIP
mlguys Aug 11, 2023
7d48910
polish test cases
mlguys Aug 11, 2023
7e79813
fix getting order from db
mlguys Aug 15, 2023
67e6cb6
rebase latest changes
mlguys Aug 22, 2023
b08cf97
Remove network.routes.ts
mlguys Aug 22, 2023
e4d7778
Fix test errors on CI/CD
mlguys Aug 29, 2023
7b98601
fix client not disconect on close
mlguys Aug 29, 2023
2a8a778
fix tests
mlguys Aug 29, 2023
a05c732
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Aug 29, 2023
0c74069
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Aug 30, 2023
ceec75f
remove swagger doc changes
mlguys Sep 7, 2023
ba86d93
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Sep 7, 2023
6280dff
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Nov 10, 2023
a1a1ef5
fix UNKNOW state due to double offer cancel
mlguys Nov 10, 2023
8a2c1e2
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Nov 10, 2023
b84f8d7
update test
mlguys Nov 11, 2023
6f6bd8f
update yarn
mlguys Nov 13, 2023
d35d850
Merge branch 'development' into feat/xrpl-connector-v2
mlguys Nov 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
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
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
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
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
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
@@ -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
@@ -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
@@ -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
@@ -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 };
}
}