@ercworldio/blockchain-shared 1.0.17 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -18,12 +18,14 @@ export interface FetchPriceData {
|
|
|
18
18
|
declare class AccountingService extends BaseErrors {
|
|
19
19
|
private dbPool;
|
|
20
20
|
private config;
|
|
21
|
+
private logger;
|
|
21
22
|
constructor(dbPool: IDatabasePool, config: IConfig);
|
|
22
23
|
deposit_address_exists_by_user: (user_id: number, address: string, blockchain: BlockchainType) => Promise<boolean>;
|
|
23
24
|
get_crypto_withdrawal_request: (request_id: number) => Promise<WithdrawRequestDetails | null>;
|
|
24
25
|
get_crypto_rate: (chain_id: ChainId) => Promise<number>;
|
|
25
26
|
private fetch_crypto_rate_task;
|
|
26
27
|
convert_amount_to_currency: (amount_in_source: number, source_currency: string, destination_currency: string) => Promise<number>;
|
|
28
|
+
private validate_currency_code;
|
|
27
29
|
private convert_currency_pair_task;
|
|
28
30
|
find_withdrawal_by_hash: (tx_hash: string, request_id: number) => Promise<any>;
|
|
29
31
|
find_withdrawals_batch: (lookups: WithdrawalLookup[]) => Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountingService.d.ts","sourceRoot":"","sources":["../../src/services/AccountingService.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"AccountingService.d.ts","sourceRoot":"","sources":["../../src/services/AccountingService.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAO9D,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAC5E,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAElF,MAAM,WAAW,qBAAqB;IAClC,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,OAAO,CAAA;CACpB;AAED,cAAM,iBAAkB,SAAQ,UAAU;IACtC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,MAAM,CAAS;gBACX,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO;IAO3C,8BAA8B,GAAU,SAAS,MAAM,EAAE,SAAS,MAAM,EAAE,YAAY,cAAc,sBAe1G;IAEM,6BAA6B,GAAU,YAAY,MAAM,4CAc/D;IAEM,eAAe,GAAU,UAAU,OAAO,qBAQhD;IAGD,OAAO,CAAC,sBAAsB,CAoB7B;IAGM,0BAA0B,GAAU,kBAAkB,MAAM,EAAE,iBAAiB,MAAM,EAAE,sBAAsB,MAAM,qBAiBzH;IAED,OAAO,CAAC,sBAAsB,CAQ7B;IAED,OAAO,CAAC,0BAA0B,CAuBjC;IAEM,uBAAuB,GAAU,SAAS,MAAM,EAAE,YAAY,MAAM,kBAe1E;IAEM,sBAAsB,GACzB,SAAS,gBAAgB,EAAE,kBA8B7B;IAEK,4BAA4B,GAAU,QAAO,MAAY,kBAmB/D;IAqDM,qCAAqC,GAAU,QAAO,MAAY,EAAE,kBAAiB,MAAU,kBA4DrG;CACJ;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -16,6 +16,9 @@ const errors_1 = __importDefault(require("../errors/errors"));
|
|
|
16
16
|
const Wallets_1 = require("../utils/Wallets");
|
|
17
17
|
const ChainManager_1 = __importDefault(require("./ChainManager"));
|
|
18
18
|
const AsyncTTLCache_1 = __importDefault(require("../utils/AsyncTTLCache"));
|
|
19
|
+
const Logger_1 = require("../utils/Logger");
|
|
20
|
+
// Currency tickers are short alphanumeric codes (EUR, USD, USDT, ...); anything else is rejected before it reaches the DB.
|
|
21
|
+
const CURRENCY_CODE_PATTERN = /^[A-Z0-9]{2,10}$/;
|
|
19
22
|
class AccountingService extends errors_1.default {
|
|
20
23
|
constructor(dbPool, config) {
|
|
21
24
|
super();
|
|
@@ -63,20 +66,45 @@ class AccountingService extends errors_1.default {
|
|
|
63
66
|
return Number(result[0].fn_get_crypto_rate);
|
|
64
67
|
});
|
|
65
68
|
this.convert_amount_to_currency = (amount_in_source, source_currency, destination_currency) => __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
const source = this.validate_currency_code(source_currency, "source_currency");
|
|
70
|
+
const destination = this.validate_currency_code(destination_currency, "destination_currency");
|
|
71
|
+
if (typeof amount_in_source !== "number" || !Number.isFinite(amount_in_source)) {
|
|
72
|
+
this.logger.warn(`Rejected currency conversion: invalid amount`, { data: { amount_in_source: String(amount_in_source).slice(0, 200), source, destination } });
|
|
73
|
+
throw new Error(`Invalid amount for currency conversion`);
|
|
74
|
+
}
|
|
66
75
|
// const unit_rate = await PriceCache.getInstance().get_rate(source_currency, destination_currency, this.convert_currency_pair_task);
|
|
67
|
-
const key = `${
|
|
68
|
-
const unit_rate = yield AsyncTTLCache_1.default.getInstance().get_value(key, { amount_in_source, source_currency, destination_currency }, this.convert_currency_pair_task);
|
|
76
|
+
const key = `${source}/${destination}`;
|
|
77
|
+
const unit_rate = yield AsyncTTLCache_1.default.getInstance().get_value(key, { amount_in_source, source_currency: source, destination_currency: destination }, this.convert_currency_pair_task);
|
|
69
78
|
return unit_rate * amount_in_source;
|
|
70
79
|
});
|
|
80
|
+
this.validate_currency_code = (value, field) => {
|
|
81
|
+
const code = typeof value === "string" ? value.trim().toUpperCase() : "";
|
|
82
|
+
if (!CURRENCY_CODE_PATTERN.test(code)) {
|
|
83
|
+
// Warn goes to Elastic: an unexpected value here is either a client bug or an injection attempt.
|
|
84
|
+
this.logger.warn(`Rejected currency conversion: invalid ${field}`, { data: { field, value: String(value).slice(0, 200) } });
|
|
85
|
+
throw new Error(`Invalid ${field}`);
|
|
86
|
+
}
|
|
87
|
+
return code;
|
|
88
|
+
};
|
|
71
89
|
this.convert_currency_pair_task = (data) => __awaiter(this, void 0, void 0, function* () {
|
|
72
90
|
if (!this.dbPool.customerAppDataSource)
|
|
73
91
|
throw new Error(this.NotInitializedDatabase());
|
|
74
92
|
const { amount_in_source, source_currency, destination_currency } = data;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
93
|
+
this.logger.info(`Fetching currency conversion from DB`, { data: { amount_in_source, source_currency, destination_currency } });
|
|
94
|
+
let result;
|
|
95
|
+
try {
|
|
96
|
+
result = yield this.dbPool.customerAppDataSource.query(`
|
|
97
|
+
SELECT accounting.fn_convert_amount_to_currency($1::numeric, $2::text, $3::text)
|
|
98
|
+
`, [amount_in_source, source_currency, destination_currency]);
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
this.logger.error(`Currency conversion query failed`, { data: { error, amount_in_source, source_currency, destination_currency } });
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
if (!result.length || result[0].fn_convert_amount_to_currency === null) {
|
|
105
|
+
this.logger.warn(`No conversion rate returned`, { data: { source_currency, destination_currency } });
|
|
79
106
|
throw new Error(`Failed to get crypto rate for pair ${source_currency}/${destination_currency}`);
|
|
107
|
+
}
|
|
80
108
|
return Number(result[0].fn_convert_amount_to_currency);
|
|
81
109
|
});
|
|
82
110
|
this.find_withdrawal_by_hash = (tx_hash, request_id) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -234,6 +262,7 @@ class AccountingService extends errors_1.default {
|
|
|
234
262
|
});
|
|
235
263
|
this.dbPool = dbPool;
|
|
236
264
|
this.config = config;
|
|
265
|
+
this.logger = (0, Logger_1.createLogger)(`AccountingService`, this.config.pipelineConfig.azureAppName);
|
|
237
266
|
}
|
|
238
267
|
}
|
|
239
268
|
exports.default = AccountingService;
|