@riocrypto/common-server 1.0.2842 → 1.0.2844
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.
- package/build/clients/bitso-client.d.ts +2 -0
- package/build/clients/bitso-client.js +38 -0
- package/build/clients/cluster-client.d.ts +2 -1
- package/build/clients/cluster-client.js +2 -1
- package/build/clients/coincover-client.js +3 -0
- package/build/clients/fireblocks-client.d.ts +6 -0
- package/build/clients/fireblocks-client.js +39 -0
- package/build/clients/kraken-client.d.ts +78 -0
- package/build/clients/kraken-client.js +253 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/models/external-trade.d.ts +4 -0
- package/build/models/external-trade.js +6 -0
- package/build/models/internal-balances.d.ts +3 -0
- package/build/models/internal-balances.js +3 -0
- package/build/models/internal-trade.d.ts +2 -0
- package/build/models/internal-trade.js +3 -0
- package/build/models/internal-transfer.d.ts +13 -1
- package/build/models/internal-transfer.js +37 -0
- package/package.json +2 -2
|
@@ -91,6 +91,8 @@ declare class BitsoClient {
|
|
|
91
91
|
createCryptoWithdrawal(amount: number, crypto: Crypto, address: string): Promise<Object>;
|
|
92
92
|
getDepositByHash(hash: string): Promise<Object>;
|
|
93
93
|
getDepositByConcepto(concepto: string): Promise<Object>;
|
|
94
|
+
listFundings(limit?: number): Promise<any[]>;
|
|
95
|
+
listWithdrawals(limit?: number): Promise<any[]>;
|
|
94
96
|
signRequest(requestConfig: AxiosRequestConfig): AxiosRequestConfig;
|
|
95
97
|
}
|
|
96
98
|
export declare const buildBitsoClient: () => Promise<BitsoClient>;
|
|
@@ -482,6 +482,44 @@ class BitsoClient {
|
|
|
482
482
|
return data.payload.find((deposit) => deposit.details.concepto === concepto);
|
|
483
483
|
});
|
|
484
484
|
}
|
|
485
|
+
// List recent fundings (deposits) so callers can import exchange-native
|
|
486
|
+
// deposits done directly on Bitso. Bitso caps `limit` at 100 per page.
|
|
487
|
+
listFundings(limit = 100) {
|
|
488
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
489
|
+
const requestConfig = {
|
|
490
|
+
method: "GET",
|
|
491
|
+
url: `${this.baseUrl}/api/v3/fundings/?limit=${limit}`,
|
|
492
|
+
headers: {
|
|
493
|
+
"Content-Type": "application/json",
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
const signedRequestConfig = this.signRequest(requestConfig);
|
|
497
|
+
const { data } = yield this.axiosClient(signedRequestConfig);
|
|
498
|
+
if (!data.success) {
|
|
499
|
+
throw new Error("Error listing Bitso fundings");
|
|
500
|
+
}
|
|
501
|
+
return data.payload;
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
// List recent withdrawals so callers can import exchange-native withdrawals
|
|
505
|
+
// done directly on Bitso. Bitso caps `limit` at 100 per page.
|
|
506
|
+
listWithdrawals(limit = 100) {
|
|
507
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
508
|
+
const requestConfig = {
|
|
509
|
+
method: "GET",
|
|
510
|
+
url: `${this.baseUrl}/api/v3/withdrawals/?limit=${limit}`,
|
|
511
|
+
headers: {
|
|
512
|
+
"Content-Type": "application/json",
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
const signedRequestConfig = this.signRequest(requestConfig);
|
|
516
|
+
const { data } = yield this.axiosClient(signedRequestConfig);
|
|
517
|
+
if (!data.success) {
|
|
518
|
+
throw new Error("Error listing Bitso withdrawals");
|
|
519
|
+
}
|
|
520
|
+
return data.payload;
|
|
521
|
+
});
|
|
522
|
+
}
|
|
485
523
|
signRequest(requestConfig) {
|
|
486
524
|
var _a;
|
|
487
525
|
const url = new URL(requestConfig.url || "");
|
|
@@ -180,7 +180,7 @@ declare class ClusterClient {
|
|
|
180
180
|
stopExternalTradingAlgorithm(id: string): Promise<ExternalTradingAlgorithm>;
|
|
181
181
|
getExternalTradingAlgorithm(id: string): Promise<ExternalTradingAlgorithm>;
|
|
182
182
|
getExternalTrade(id: string): Promise<ExternalTrade>;
|
|
183
|
-
placeExternalTrade({ provider, type, originCurrency, destinationCurrency, requestedOriginAmount, requestedDestinationAmount, limitPrice, twoWaySettlementDateOffset, arbitrageSessionId, fxTradeId, timeInForce, valueDate, }: {
|
|
183
|
+
placeExternalTrade({ provider, type, originCurrency, destinationCurrency, requestedOriginAmount, requestedDestinationAmount, limitPrice, twoWaySettlementDateOffset, arbitrageSessionId, fxTradeId, internalTradeId, timeInForce, valueDate, }: {
|
|
184
184
|
provider: ExternalTradingProvider;
|
|
185
185
|
type: ExternalTradeType;
|
|
186
186
|
originCurrency: Crypto | Fiat;
|
|
@@ -191,6 +191,7 @@ declare class ClusterClient {
|
|
|
191
191
|
twoWaySettlementDateOffset?: number;
|
|
192
192
|
arbitrageSessionId?: string;
|
|
193
193
|
fxTradeId?: string;
|
|
194
|
+
internalTradeId?: string;
|
|
194
195
|
timeInForce?: "day" | "gtc" | "ioc" | "fok";
|
|
195
196
|
valueDate?: string;
|
|
196
197
|
}): Promise<ExternalTrade>;
|
|
@@ -654,7 +654,7 @@ class ClusterClient {
|
|
|
654
654
|
return response.data;
|
|
655
655
|
});
|
|
656
656
|
}
|
|
657
|
-
placeExternalTrade({ provider, type, originCurrency, destinationCurrency, requestedOriginAmount, requestedDestinationAmount, limitPrice, twoWaySettlementDateOffset, arbitrageSessionId, fxTradeId, timeInForce, valueDate, }) {
|
|
657
|
+
placeExternalTrade({ provider, type, originCurrency, destinationCurrency, requestedOriginAmount, requestedDestinationAmount, limitPrice, twoWaySettlementDateOffset, arbitrageSessionId, fxTradeId, internalTradeId, timeInForce, valueDate, }) {
|
|
658
658
|
return __awaiter(this, void 0, void 0, function* () {
|
|
659
659
|
const response = yield this.axios.post(`${this.baseUrl}/api/trading/external/trades`, {
|
|
660
660
|
provider,
|
|
@@ -667,6 +667,7 @@ class ClusterClient {
|
|
|
667
667
|
twoWaySettlementDateOffset,
|
|
668
668
|
arbitrageSessionId,
|
|
669
669
|
fxTradeId,
|
|
670
|
+
internalTradeId,
|
|
670
671
|
timeInForce,
|
|
671
672
|
valueDate,
|
|
672
673
|
}, {
|
|
@@ -57,6 +57,9 @@ class CoincoverClient {
|
|
|
57
57
|
[common_1.Crypto.SOL]: "solana",
|
|
58
58
|
[common_1.Crypto.XLM]: "stellar",
|
|
59
59
|
[common_1.Crypto.POL]: "matic-network",
|
|
60
|
+
// Synthetic catch-all for unmapped assets seen on imported treasury
|
|
61
|
+
// movements; it has no CoinGecko id and is never priced.
|
|
62
|
+
[common_1.Crypto.Other]: "",
|
|
60
63
|
};
|
|
61
64
|
this.axiosClient = (0, axios_with_logging_1.buildAxiosWithLogging)();
|
|
62
65
|
}
|
|
@@ -51,6 +51,12 @@ declare class FireblocksClient {
|
|
|
51
51
|
getSingularExchangeCryptoBalance(id: string, usdType: "usdc" | "usdt"): Promise<number>;
|
|
52
52
|
getExchangeFiatBalance(): Promise<number>;
|
|
53
53
|
getTransactions(vaultId: string, crypto: Crypto, nextPage?: string): Promise<any>;
|
|
54
|
+
listVaultTransactions(params: {
|
|
55
|
+
vaultId: string;
|
|
56
|
+
direction: "incoming" | "outgoing";
|
|
57
|
+
afterMs: number;
|
|
58
|
+
status?: string;
|
|
59
|
+
}): Promise<TransactionResponse[]>;
|
|
54
60
|
getDepositAddress(vaultId: string, crypto: Crypto): Promise<string>;
|
|
55
61
|
createVault(userId: string, name: string, type: "transactional", number?: number): Promise<string>;
|
|
56
62
|
createWallet(vaultId: string, crypto: Crypto): Promise<{
|
|
@@ -356,6 +356,45 @@ class FireblocksClient {
|
|
|
356
356
|
return transactionData;
|
|
357
357
|
});
|
|
358
358
|
}
|
|
359
|
+
// Lists a vault's transactions in one direction (incoming = deposits to the
|
|
360
|
+
// vault, outgoing = withdrawals from the vault) created at/after `afterMs`
|
|
361
|
+
// (unix ms). Pages through all results. Used by the treasury import cron to
|
|
362
|
+
// detect movements performed directly in Fireblocks (not initiated by Rio).
|
|
363
|
+
//
|
|
364
|
+
// Note the SDK paging quirk (fireblocks-sdk-js#268): when a page filter is
|
|
365
|
+
// provided it takes priority over the next-page path, so we pass the filter
|
|
366
|
+
// only on the first request and the path on subsequent requests.
|
|
367
|
+
listVaultTransactions(params) {
|
|
368
|
+
var _a;
|
|
369
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
370
|
+
const { vaultId, direction, afterMs, status } = params;
|
|
371
|
+
const filter = {
|
|
372
|
+
after: String(Math.floor(afterMs)),
|
|
373
|
+
orderBy: "createdAt",
|
|
374
|
+
limit: 200,
|
|
375
|
+
};
|
|
376
|
+
if (direction === "incoming") {
|
|
377
|
+
filter.destId = vaultId;
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
filter.sourceId = vaultId;
|
|
381
|
+
}
|
|
382
|
+
if (status) {
|
|
383
|
+
filter.status = status;
|
|
384
|
+
}
|
|
385
|
+
const results = [];
|
|
386
|
+
let nextPage;
|
|
387
|
+
let pagesFetched = 0;
|
|
388
|
+
const MAX_PAGES = 20;
|
|
389
|
+
do {
|
|
390
|
+
const response = yield this.fireblocks.getTransactionsWithPageInfo(nextPage ? undefined : filter, nextPage);
|
|
391
|
+
results.push(...(response.transactions || []));
|
|
392
|
+
nextPage = ((_a = response.pageDetails) === null || _a === void 0 ? void 0 : _a.nextPage) || undefined;
|
|
393
|
+
pagesFetched += 1;
|
|
394
|
+
} while (nextPage && pagesFetched < MAX_PAGES);
|
|
395
|
+
return results;
|
|
396
|
+
});
|
|
397
|
+
}
|
|
359
398
|
getDepositAddress(vaultId, crypto) {
|
|
360
399
|
return __awaiter(this, void 0, void 0, function* () {
|
|
361
400
|
let addresses = yield this.fireblocks.getDepositAddresses(vaultId, crypto);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { RioEnv } from "@riocrypto/common";
|
|
2
|
+
export declare const KRAKEN_ASSET_USD = "ZUSD";
|
|
3
|
+
export interface KrakenDepositMethod {
|
|
4
|
+
method: string;
|
|
5
|
+
limit: string | boolean;
|
|
6
|
+
fee?: string;
|
|
7
|
+
"gen-address"?: boolean;
|
|
8
|
+
"address-setup-fee"?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface KrakenDepositAddress {
|
|
11
|
+
address: string;
|
|
12
|
+
expiretm: string;
|
|
13
|
+
new?: boolean;
|
|
14
|
+
tag?: string;
|
|
15
|
+
memo?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface KrakenFundingStatus {
|
|
18
|
+
method: string;
|
|
19
|
+
aclass: string;
|
|
20
|
+
asset: string;
|
|
21
|
+
refid: string;
|
|
22
|
+
txid: string;
|
|
23
|
+
info: string;
|
|
24
|
+
amount: string;
|
|
25
|
+
fee: string;
|
|
26
|
+
time: number;
|
|
27
|
+
status: string;
|
|
28
|
+
"status-prop"?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface KrakenWithdrawInfo {
|
|
31
|
+
method: string;
|
|
32
|
+
limit: string;
|
|
33
|
+
amount: string;
|
|
34
|
+
fee: string;
|
|
35
|
+
}
|
|
36
|
+
export interface KrakenLedgerEntry {
|
|
37
|
+
refid: string;
|
|
38
|
+
time: number;
|
|
39
|
+
type: string;
|
|
40
|
+
subtype: string;
|
|
41
|
+
aclass: string;
|
|
42
|
+
asset: string;
|
|
43
|
+
amount: string;
|
|
44
|
+
fee: string;
|
|
45
|
+
balance: string;
|
|
46
|
+
}
|
|
47
|
+
declare class KrakenClient {
|
|
48
|
+
private apiKey;
|
|
49
|
+
private env;
|
|
50
|
+
private axiosClient;
|
|
51
|
+
private decodedSecret;
|
|
52
|
+
private lastNonce;
|
|
53
|
+
private requestQueue;
|
|
54
|
+
private restBaseUrl;
|
|
55
|
+
constructor(apiKey: string, apiSecret: string, env: RioEnv);
|
|
56
|
+
private assertFundingAllowed;
|
|
57
|
+
private nextNonce;
|
|
58
|
+
private sign;
|
|
59
|
+
private privatePost;
|
|
60
|
+
private executePrivatePost;
|
|
61
|
+
getBalance(asset: string): Promise<number>;
|
|
62
|
+
getAllBalances(assets: string[]): Promise<Record<string, number>>;
|
|
63
|
+
getDepositMethods(asset: string): Promise<KrakenDepositMethod[]>;
|
|
64
|
+
getDepositAddresses(asset: string, method: string, isNew?: boolean): Promise<KrakenDepositAddress[]>;
|
|
65
|
+
getDepositStatus(asset: string, method?: string): Promise<KrakenFundingStatus[]>;
|
|
66
|
+
getWithdrawInfo(asset: string, key: string, amount: number): Promise<KrakenWithdrawInfo>;
|
|
67
|
+
withdraw(asset: string, key: string, amount: number): Promise<string>;
|
|
68
|
+
getWithdrawStatus(asset: string, method?: string): Promise<KrakenFundingStatus[]>;
|
|
69
|
+
getWithdrawStatusByRefid(asset: string, refid: string): Promise<KrakenFundingStatus | undefined>;
|
|
70
|
+
getDepositByTxId(asset: string, txid: string): Promise<KrakenFundingStatus | undefined>;
|
|
71
|
+
getLedgers(params?: {
|
|
72
|
+
type?: string;
|
|
73
|
+
asset?: string;
|
|
74
|
+
start?: number;
|
|
75
|
+
}): Promise<Record<string, KrakenLedgerEntry>>;
|
|
76
|
+
}
|
|
77
|
+
export declare const buildKrakenClient: () => Promise<KrakenClient>;
|
|
78
|
+
export type { KrakenClient };
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.buildKrakenClient = exports.KRAKEN_ASSET_USD = void 0;
|
|
16
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
17
|
+
const common_1 = require("@riocrypto/common");
|
|
18
|
+
const axios_with_logging_1 = require("./axios-with-logging");
|
|
19
|
+
const secret_manager_client_1 = require("./secret-manager-client");
|
|
20
|
+
// Kraken REST hosts. There is no public Kraken spot testnet, but institutional
|
|
21
|
+
// clients get a separate UAT environment. The host is selected purely from
|
|
22
|
+
// RIO_ENV: only production talks to the live account; every other environment
|
|
23
|
+
// points at the UAT host so it can never move funds on the real account.
|
|
24
|
+
const KRAKEN_PROD_REST_BASE_URL = "https://api.kraken.com";
|
|
25
|
+
const KRAKEN_UAT_REST_BASE_URL = "https://api.uat.kraken.com";
|
|
26
|
+
const getKrakenRestBaseUrl = (env) => env === common_1.RioEnv.Production
|
|
27
|
+
? KRAKEN_PROD_REST_BASE_URL
|
|
28
|
+
: KRAKEN_UAT_REST_BASE_URL;
|
|
29
|
+
// Kraken Balance keys. Fiat USD is `ZUSD`; the stablecoins use their ticker.
|
|
30
|
+
exports.KRAKEN_ASSET_USD = "ZUSD";
|
|
31
|
+
class KrakenClient {
|
|
32
|
+
constructor(apiKey, apiSecret, env) {
|
|
33
|
+
this.apiKey = apiKey;
|
|
34
|
+
this.env = env;
|
|
35
|
+
// Kraken nonces must strictly increase per key. We keep a monotonic floor so
|
|
36
|
+
// two calls in the same millisecond can't produce a decreasing nonce.
|
|
37
|
+
this.lastNonce = 0;
|
|
38
|
+
// Serialization queue for private requests. Even with strictly increasing
|
|
39
|
+
// nonces, concurrent private requests can arrive at Kraken out of order,
|
|
40
|
+
// which Kraken rejects as `EAPI:Invalid nonce`. Chaining every private
|
|
41
|
+
// request through this promise guarantees one in-flight request at a time so
|
|
42
|
+
// nonces always arrive in order.
|
|
43
|
+
this.requestQueue = Promise.resolve();
|
|
44
|
+
this.decodedSecret = Buffer.from(apiSecret, "base64");
|
|
45
|
+
this.axiosClient = (0, axios_with_logging_1.buildAxiosWithLogging)();
|
|
46
|
+
this.restBaseUrl = getKrakenRestBaseUrl(env);
|
|
47
|
+
}
|
|
48
|
+
// Fail-closed guard: a non-production environment must never move funds
|
|
49
|
+
// against the LIVE Kraken host. The host is derived from RIO_ENV, so this
|
|
50
|
+
// also catches an unknown/unset RIO_ENV resolving to the live host.
|
|
51
|
+
assertFundingAllowed() {
|
|
52
|
+
const isLiveHost = this.restBaseUrl === KRAKEN_PROD_REST_BASE_URL;
|
|
53
|
+
if (this.env !== common_1.RioEnv.Production && isLiveHost) {
|
|
54
|
+
throw new Error("Refusing to move Kraken funds: a non-production environment is pointed at the live Kraken host.");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
nextNonce() {
|
|
58
|
+
const now = Date.now();
|
|
59
|
+
const nonce = now > this.lastNonce ? now : this.lastNonce + 1;
|
|
60
|
+
this.lastNonce = nonce;
|
|
61
|
+
return nonce;
|
|
62
|
+
}
|
|
63
|
+
sign(path, nonce, postData) {
|
|
64
|
+
// API-Sign = base64( HMAC-SHA512( path + SHA256(nonce + postData),
|
|
65
|
+
// base64decode(secret) ) )
|
|
66
|
+
const sha256 = crypto_1.default
|
|
67
|
+
.createHash("sha256")
|
|
68
|
+
.update(nonce + postData)
|
|
69
|
+
.digest();
|
|
70
|
+
const message = Buffer.concat([Buffer.from(path, "utf8"), sha256]);
|
|
71
|
+
return crypto_1.default
|
|
72
|
+
.createHmac("sha512", this.decodedSecret)
|
|
73
|
+
.update(message)
|
|
74
|
+
.digest("base64");
|
|
75
|
+
}
|
|
76
|
+
privatePost(method, params = {}) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
// Queue behind any in-flight private request so nonces reach Kraken in
|
|
79
|
+
// strictly increasing order. The queue must survive individual failures,
|
|
80
|
+
// hence the `.catch` on the stored promise (the caller still sees the
|
|
81
|
+
// original rejection via `run`).
|
|
82
|
+
const run = this.requestQueue.then(() => this.executePrivatePost(method, params));
|
|
83
|
+
this.requestQueue = run.catch(() => undefined);
|
|
84
|
+
return run;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
executePrivatePost(method, params = {}) {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
89
|
+
const path = `/0/private/${method}`;
|
|
90
|
+
const nonce = this.nextNonce();
|
|
91
|
+
const form = new URLSearchParams();
|
|
92
|
+
form.set("nonce", String(nonce));
|
|
93
|
+
for (const [key, value] of Object.entries(params)) {
|
|
94
|
+
if (value !== undefined && value !== null) {
|
|
95
|
+
form.set(key, String(value));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const postData = form.toString();
|
|
99
|
+
const signature = this.sign(path, nonce, postData);
|
|
100
|
+
const { data } = yield this.axiosClient.post(`${this.restBaseUrl}${path}`, postData, {
|
|
101
|
+
headers: {
|
|
102
|
+
"API-Key": this.apiKey,
|
|
103
|
+
"API-Sign": signature,
|
|
104
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
// Kraken returns HTTP 200 with errors inside an `error` array.
|
|
108
|
+
if ((data === null || data === void 0 ? void 0 : data.error) && data.error.length > 0) {
|
|
109
|
+
throw new Error(`Kraken API error: ${data.error.join(", ")}`);
|
|
110
|
+
}
|
|
111
|
+
return data.result;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// ----- Balances -----
|
|
115
|
+
getBalance(asset) {
|
|
116
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
117
|
+
const balances = yield this.privatePost("Balance", {});
|
|
118
|
+
const value = balances[asset];
|
|
119
|
+
return value ? parseFloat(value) : 0;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
getAllBalances(assets) {
|
|
123
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
const balances = yield this.privatePost("Balance", {});
|
|
125
|
+
const result = {};
|
|
126
|
+
for (const asset of assets) {
|
|
127
|
+
result[asset] = balances[asset] ? parseFloat(balances[asset]) : 0;
|
|
128
|
+
}
|
|
129
|
+
return result;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// ----- Deposits -----
|
|
133
|
+
getDepositMethods(asset) {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
return this.privatePost("DepositMethods", { asset });
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// Retrieve (or generate) deposit addresses for an asset + method. `method`
|
|
139
|
+
// must exactly match one of the values returned by DepositMethods.
|
|
140
|
+
getDepositAddresses(asset, method, isNew = false) {
|
|
141
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
return this.privatePost("DepositAddresses", {
|
|
143
|
+
asset,
|
|
144
|
+
method,
|
|
145
|
+
new: isNew ? true : undefined,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
getDepositStatus(asset, method) {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
return this.privatePost("DepositStatus", {
|
|
152
|
+
asset,
|
|
153
|
+
method,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// ----- Withdrawals -----
|
|
158
|
+
getWithdrawInfo(asset, key, amount) {
|
|
159
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
160
|
+
return this.privatePost("WithdrawInfo", {
|
|
161
|
+
asset,
|
|
162
|
+
key,
|
|
163
|
+
amount,
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
// Withdraw to a pre-configured, account-whitelisted address referenced by
|
|
168
|
+
// `key` (Kraken does not allow withdrawing to an arbitrary address via API).
|
|
169
|
+
// Returns Kraken's withdrawal reference id (refid).
|
|
170
|
+
withdraw(asset, key, amount) {
|
|
171
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
172
|
+
this.assertFundingAllowed();
|
|
173
|
+
const result = yield this.privatePost("Withdraw", {
|
|
174
|
+
asset,
|
|
175
|
+
key,
|
|
176
|
+
amount,
|
|
177
|
+
});
|
|
178
|
+
if (!(result === null || result === void 0 ? void 0 : result.refid)) {
|
|
179
|
+
throw new Error("Kraken Withdraw returned no refid");
|
|
180
|
+
}
|
|
181
|
+
return result.refid;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
getWithdrawStatus(asset, method) {
|
|
185
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
return this.privatePost("WithdrawStatus", {
|
|
187
|
+
asset,
|
|
188
|
+
method,
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
getWithdrawStatusByRefid(asset, refid) {
|
|
193
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
194
|
+
const statuses = yield this.getWithdrawStatus(asset);
|
|
195
|
+
return statuses.find((s) => s.refid === refid);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
getDepositByTxId(asset, txid) {
|
|
199
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
200
|
+
const statuses = yield this.getDepositStatus(asset);
|
|
201
|
+
return statuses.find((s) => s.txid === txid);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
// ----- Ledgers -----
|
|
205
|
+
// Returns the ledger entries (keyed by ledger id) for the given filters.
|
|
206
|
+
// This is the authoritative source for fiat deposits/withdrawals, which do
|
|
207
|
+
// not appear in DepositStatus/WithdrawStatus. `start` is a unix timestamp in
|
|
208
|
+
// seconds; Kraken returns the most recent entries (max 50 per page) at or
|
|
209
|
+
// after it.
|
|
210
|
+
getLedgers(params = {}) {
|
|
211
|
+
var _a;
|
|
212
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
213
|
+
const result = yield this.privatePost("Ledgers", {
|
|
214
|
+
type: params.type,
|
|
215
|
+
asset: params.asset,
|
|
216
|
+
start: params.start,
|
|
217
|
+
});
|
|
218
|
+
return (_a = result === null || result === void 0 ? void 0 : result.ledger) !== null && _a !== void 0 ? _a : {};
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// Cache a single client per process so the monotonic nonce floor is effective
|
|
223
|
+
// process-wide; building a fresh client per call would reset it and risk
|
|
224
|
+
// `EAPI:Invalid nonce`.
|
|
225
|
+
let krakenClient = null;
|
|
226
|
+
let krakenClientPromise = null;
|
|
227
|
+
const buildKrakenClient = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
228
|
+
if (krakenClient) {
|
|
229
|
+
return krakenClient;
|
|
230
|
+
}
|
|
231
|
+
if (!krakenClientPromise) {
|
|
232
|
+
krakenClientPromise = (() => __awaiter(void 0, void 0, void 0, function* () {
|
|
233
|
+
const KRAKEN_API_KEY = yield secret_manager_client_1.secretManagerClient.getSecretValue("KRAKEN_API_KEY");
|
|
234
|
+
if (!KRAKEN_API_KEY) {
|
|
235
|
+
throw new common_1.SecretManagerError();
|
|
236
|
+
}
|
|
237
|
+
const KRAKEN_API_SECRET = yield secret_manager_client_1.secretManagerClient.getSecretValue("KRAKEN_API_SECRET");
|
|
238
|
+
if (!KRAKEN_API_SECRET) {
|
|
239
|
+
throw new common_1.SecretManagerError();
|
|
240
|
+
}
|
|
241
|
+
krakenClient = new KrakenClient(KRAKEN_API_KEY, KRAKEN_API_SECRET, process.env.RIO_ENV);
|
|
242
|
+
return krakenClient;
|
|
243
|
+
}))();
|
|
244
|
+
}
|
|
245
|
+
try {
|
|
246
|
+
return yield krakenClientPromise;
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
krakenClientPromise = null;
|
|
250
|
+
throw err;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
exports.buildKrakenClient = buildKrakenClient;
|
package/build/index.d.ts
CHANGED
|
@@ -148,6 +148,7 @@ export * from "./clients/circle-client";
|
|
|
148
148
|
export * from "./clients/coincover-client";
|
|
149
149
|
export * from "./clients/google-sheets-api-client";
|
|
150
150
|
export * from "./clients/binance-client";
|
|
151
|
+
export * from "./clients/kraken-client";
|
|
151
152
|
export * from "./helpers/get-user-helper";
|
|
152
153
|
export * from "./helpers/get-processor-fees";
|
|
153
154
|
export * from "./helpers/get-network-fees";
|
package/build/index.js
CHANGED
|
@@ -164,6 +164,7 @@ __exportStar(require("./clients/circle-client"), exports);
|
|
|
164
164
|
__exportStar(require("./clients/coincover-client"), exports);
|
|
165
165
|
__exportStar(require("./clients/google-sheets-api-client"), exports);
|
|
166
166
|
__exportStar(require("./clients/binance-client"), exports);
|
|
167
|
+
__exportStar(require("./clients/kraken-client"), exports);
|
|
167
168
|
__exportStar(require("./helpers/get-user-helper"), exports);
|
|
168
169
|
__exportStar(require("./helpers/get-processor-fees"), exports);
|
|
169
170
|
__exportStar(require("./helpers/get-network-fees"), exports);
|
|
@@ -19,6 +19,8 @@ interface ExternalTradeAttrs {
|
|
|
19
19
|
associatedMidmarketPrice?: number;
|
|
20
20
|
status: ExternalTradeStatus;
|
|
21
21
|
fxTradeId?: string;
|
|
22
|
+
internalTradeId?: string;
|
|
23
|
+
coinbaseConversionId?: string;
|
|
22
24
|
providerOptions?: {
|
|
23
25
|
emarkets?: {
|
|
24
26
|
settlementType?: EmarketsSettlementType;
|
|
@@ -60,6 +62,8 @@ interface ExternalTradeDoc extends Document {
|
|
|
60
62
|
associatedMidmarketPrice?: number;
|
|
61
63
|
status: ExternalTradeStatus;
|
|
62
64
|
fxTradeId?: string;
|
|
65
|
+
internalTradeId?: string;
|
|
66
|
+
coinbaseConversionId?: string;
|
|
63
67
|
providerOptions?: {
|
|
64
68
|
emarkets?: {
|
|
65
69
|
settlementType?: EmarketsSettlementType;
|
|
@@ -19,6 +19,7 @@ interface InternalTradeAttrs {
|
|
|
19
19
|
amountFilled?: number;
|
|
20
20
|
limitPrice?: number;
|
|
21
21
|
coinbaseConversionId?: string;
|
|
22
|
+
externalTradeId?: string;
|
|
22
23
|
}
|
|
23
24
|
interface InternalTradeDoc extends mongoose.Document {
|
|
24
25
|
id: string;
|
|
@@ -40,6 +41,7 @@ interface InternalTradeDoc extends mongoose.Document {
|
|
|
40
41
|
amountFilled?: number;
|
|
41
42
|
limitPrice?: number;
|
|
42
43
|
coinbaseConversionId?: string;
|
|
44
|
+
externalTradeId?: string;
|
|
43
45
|
}
|
|
44
46
|
interface InternalTradeModel extends mongoose.Model<InternalTradeDoc> {
|
|
45
47
|
build(attrs: InternalTradeAttrs): HydratedDocument<InternalTradeDoc>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import mongoose, { HydratedDocument } from "mongoose";
|
|
2
|
-
import { InternalTransferType, TreasuryProvider, Fiat, Crypto, InternalTransferOriginStatus, InternalTransferDestinationStatus } from "@riocrypto/common";
|
|
2
|
+
import { InternalTransferType, TreasuryProvider, Fiat, Crypto, InternalTransferOriginStatus, InternalTransferDestinationStatus, OtherProviderMetadata } from "@riocrypto/common";
|
|
3
3
|
interface InternalTransferAttrs {
|
|
4
4
|
createdAt: Date;
|
|
5
5
|
adminId?: string;
|
|
@@ -8,6 +8,8 @@ interface InternalTransferAttrs {
|
|
|
8
8
|
transferType: InternalTransferType;
|
|
9
9
|
amount: number;
|
|
10
10
|
currency: Fiat | Crypto;
|
|
11
|
+
otherCurrencyCode?: string;
|
|
12
|
+
isImported?: boolean;
|
|
11
13
|
blockchainTxId?: string;
|
|
12
14
|
reference?: string;
|
|
13
15
|
SPEITrackingNumber?: string;
|
|
@@ -17,14 +19,18 @@ interface InternalTransferAttrs {
|
|
|
17
19
|
providerId?: string;
|
|
18
20
|
amountSent?: number;
|
|
19
21
|
status: InternalTransferOriginStatus;
|
|
22
|
+
otherStatus?: string;
|
|
23
|
+
otherProviderMetadata?: OtherProviderMetadata;
|
|
20
24
|
};
|
|
21
25
|
destination: {
|
|
22
26
|
provider: TreasuryProvider;
|
|
23
27
|
providerId?: string;
|
|
24
28
|
amountReceived?: number;
|
|
25
29
|
status: InternalTransferDestinationStatus;
|
|
30
|
+
otherStatus?: string;
|
|
26
31
|
notifyWhenCredited?: boolean;
|
|
27
32
|
notifyWhenCompleted?: boolean;
|
|
33
|
+
otherProviderMetadata?: OtherProviderMetadata;
|
|
28
34
|
};
|
|
29
35
|
}
|
|
30
36
|
interface InternalTransferDoc extends mongoose.Document {
|
|
@@ -34,6 +40,8 @@ interface InternalTransferDoc extends mongoose.Document {
|
|
|
34
40
|
liquiditySourcingPlanId?: string;
|
|
35
41
|
createdAt: Date;
|
|
36
42
|
transferType: InternalTransferType;
|
|
43
|
+
otherCurrencyCode?: string;
|
|
44
|
+
isImported?: boolean;
|
|
37
45
|
blockchainTxId?: string;
|
|
38
46
|
reference?: string;
|
|
39
47
|
SPEITrackingNumber?: string;
|
|
@@ -43,14 +51,18 @@ interface InternalTransferDoc extends mongoose.Document {
|
|
|
43
51
|
providerId?: string;
|
|
44
52
|
amountSent?: number;
|
|
45
53
|
status: InternalTransferOriginStatus;
|
|
54
|
+
otherStatus?: string;
|
|
55
|
+
otherProviderMetadata?: OtherProviderMetadata;
|
|
46
56
|
};
|
|
47
57
|
destination: {
|
|
48
58
|
provider: TreasuryProvider;
|
|
49
59
|
providerId?: string;
|
|
50
60
|
amountReceived?: number;
|
|
51
61
|
status: InternalTransferDestinationStatus;
|
|
62
|
+
otherStatus?: string;
|
|
52
63
|
notifyWhenCredited?: boolean;
|
|
53
64
|
notifyWhenCompleted?: boolean;
|
|
65
|
+
otherProviderMetadata?: OtherProviderMetadata;
|
|
54
66
|
};
|
|
55
67
|
amount: number;
|
|
56
68
|
currency: Fiat | Crypto;
|
|
@@ -26,6 +26,13 @@ const buildInternalTransfer = (mongoose) => {
|
|
|
26
26
|
enum: Object.values(common_1.InternalTransferType),
|
|
27
27
|
required: true,
|
|
28
28
|
},
|
|
29
|
+
otherCurrencyCode: {
|
|
30
|
+
type: String,
|
|
31
|
+
},
|
|
32
|
+
isImported: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
default: false,
|
|
35
|
+
},
|
|
29
36
|
blockchainTxId: {
|
|
30
37
|
type: String,
|
|
31
38
|
},
|
|
@@ -55,6 +62,16 @@ const buildInternalTransfer = (mongoose) => {
|
|
|
55
62
|
enum: Object.values(common_1.InternalTransferOriginStatus),
|
|
56
63
|
required: true,
|
|
57
64
|
},
|
|
65
|
+
otherStatus: {
|
|
66
|
+
type: String,
|
|
67
|
+
},
|
|
68
|
+
otherProviderMetadata: {
|
|
69
|
+
blockchainAddress: { type: String },
|
|
70
|
+
memo: { type: String },
|
|
71
|
+
bankAccountInfo: { type: String },
|
|
72
|
+
bankName: { type: String },
|
|
73
|
+
label: { type: String },
|
|
74
|
+
},
|
|
58
75
|
},
|
|
59
76
|
destination: {
|
|
60
77
|
provider: {
|
|
@@ -73,6 +90,9 @@ const buildInternalTransfer = (mongoose) => {
|
|
|
73
90
|
enum: Object.values(common_1.InternalTransferDestinationStatus),
|
|
74
91
|
required: true,
|
|
75
92
|
},
|
|
93
|
+
otherStatus: {
|
|
94
|
+
type: String,
|
|
95
|
+
},
|
|
76
96
|
notifyWhenCredited: {
|
|
77
97
|
type: Boolean,
|
|
78
98
|
default: false,
|
|
@@ -81,6 +101,13 @@ const buildInternalTransfer = (mongoose) => {
|
|
|
81
101
|
type: Boolean,
|
|
82
102
|
default: false,
|
|
83
103
|
},
|
|
104
|
+
otherProviderMetadata: {
|
|
105
|
+
blockchainAddress: { type: String },
|
|
106
|
+
memo: { type: String },
|
|
107
|
+
bankAccountInfo: { type: String },
|
|
108
|
+
bankName: { type: String },
|
|
109
|
+
label: { type: String },
|
|
110
|
+
},
|
|
84
111
|
},
|
|
85
112
|
amount: {
|
|
86
113
|
type: Number,
|
|
@@ -99,6 +126,16 @@ const buildInternalTransfer = (mongoose) => {
|
|
|
99
126
|
},
|
|
100
127
|
},
|
|
101
128
|
});
|
|
129
|
+
// Indexes to support dedup lookups when importing exchange-native activity.
|
|
130
|
+
InternalTransferSchema.index({
|
|
131
|
+
"origin.provider": 1,
|
|
132
|
+
"origin.providerId": 1,
|
|
133
|
+
});
|
|
134
|
+
InternalTransferSchema.index({
|
|
135
|
+
"destination.provider": 1,
|
|
136
|
+
"destination.providerId": 1,
|
|
137
|
+
});
|
|
138
|
+
InternalTransferSchema.index({ blockchainTxId: 1 });
|
|
102
139
|
InternalTransferSchema.statics.build = (attrs) => {
|
|
103
140
|
return new InternalTransfer(attrs);
|
|
104
141
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riocrypto/common-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2844",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./build/index.js",
|
|
6
6
|
"types": "./build/index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@google-cloud/secret-manager": "^5.6.0",
|
|
29
29
|
"@google-cloud/storage": "^7.19.0",
|
|
30
30
|
"@hyperdx/node-opentelemetry": "^0.10.3",
|
|
31
|
-
"@riocrypto/common": "1.0.
|
|
31
|
+
"@riocrypto/common": "1.0.2649",
|
|
32
32
|
"@slack/web-api": "^7.15.0",
|
|
33
33
|
"@types/express": "^4.17.25",
|
|
34
34
|
"axios": "1.16.0",
|