UNPKG

@drift-labs/common

Version:

Common functions for Drift

299 lines 12.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SwiftClient = void 0; const sdk_1 = require("@drift-labs/sdk"); const rxjs_1 = require("rxjs"); const logger_1 = require("../utils/logger"); class SwiftClient { static init(baseUrl, swiftClientConsumer) { this.baseUrl = baseUrl; this.swiftClientConsumer = swiftClientConsumer; } static get(url) { if (!this.baseUrl) { throw new Error('SwiftClient not initialized'); } return new Promise((res) => { const headers = new Headers({ ...this.getSwiftHeaders(), }); fetch(`${this.baseUrl}${url}`, { headers, }) .then(async (response) => { if (!response.ok) { res({ success: false, body: await response.text(), status: response.status, }); return; } res({ success: true, body: await response.text(), status: response.status, }); }) .catch((err) => { res({ success: false, body: err, status: 0 }); }); }); } static post(url, bodyObject) { if (!this.baseUrl) { throw new Error('SwiftClient not initialized'); } const requestOptions = { method: 'POST', headers: { ...this.getSwiftHeaders() }, body: JSON.stringify(bodyObject), }; return new Promise((res) => { const postRequest = new Request(`${this.baseUrl}${url}`, requestOptions); fetch(postRequest) .then(async (response) => { let resBody = null; try { resBody = (await response.json()); res({ success: response.ok, body: resBody, status: response.status, }); } catch (err) { (0, logger_1.allEnvDlog)('swiftClient', 'Error reading response body', err); res({ success: false, body: err, status: response.status, }); } }) .catch((err) => { res({ success: false, body: err, status: 0 }); }); }); } static async sendSwiftOrder(marketIndex, marketType, message, signature, takerPubkey, signingAuthority) { var _a, _b, _c; const requestPayload = { market_index: marketIndex, market_type: (0, sdk_1.isVariant)(marketType, 'perp') ? 'perp' : 'spot', message, signature: signature.toString('base64'), signing_authority: (_a = signingAuthority === null || signingAuthority === void 0 ? void 0 : signingAuthority.toBase58()) !== null && _a !== void 0 ? _a : '', taker_authority: takerPubkey.toBase58(), }; const response = await this.post('/orders', requestPayload); if (response.status !== 200) { console.error(`Non-200 status code received for sent Swift order: ${response.status}`); (0, logger_1.allEnvDlog)('swiftClient', 'full non-200 response body', response.body); return { message: ((_b = response.body) === null || _b === void 0 ? void 0 : _b.error) || ((_c = response.body) === null || _c === void 0 ? void 0 : _c.message) || `HTTP ${response.status}: Error from Swift server`, status: response.status, success: false, }; } return { message: `Successfully sent Swift order`, body: { hash: (0, sdk_1.digestSignature)(Uint8Array.from(signature)), }, success: true, status: 200, }; } static async confirmSwiftOrderWS(connection, client, signedMsgUserOrdersAccount, signedMsgOrderUuid, confirmDuration) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Order not found')); }, confirmDuration); connection .getAccountInfo(signedMsgUserOrdersAccount, 'confirmed') .then((accountInfo) => { if (!accountInfo) { reject(new Error('Swift message account not found')); return; } const order = this.findOrderInSignedMsgUserOrdersAccount(client, accountInfo, signedMsgOrderUuid); if (order) { (0, logger_1.allEnvDlog)('swiftClient', 'confirmed in initial fetch orderID\n', order.orderId); clearTimeout(timeout); resolve(order.orderId); } }); const subId = connection.onAccountChange(signedMsgUserOrdersAccount, (accountInfo) => { const order = this.findOrderInSignedMsgUserOrdersAccount(client, accountInfo, signedMsgOrderUuid); if (order) { (0, logger_1.allEnvDlog)('swiftClient', 'confirmed in onAccountChange orderID\n', order.orderId); connection.removeAccountChangeListener(subId); clearTimeout(timeout); resolve(order.orderId); } }, 'confirmed'); }); } static findOrderInSignedMsgUserOrdersAccount(client, ordersAccount, signedMsgOrderUuid) { const accountDecoder = client.program.account.signedMsgUserOrders.coder.accounts.decodeUnchecked.bind(client.program.account.signedMsgUserOrders.coder.accounts); const decodedAccount = accountDecoder('SignedMsgUserOrders', ordersAccount.data); (0, logger_1.allEnvDlog)('swiftClient findOrder', 'decodedAccount\n', decodedAccount, signedMsgOrderUuid.toString()); const order = decodedAccount.signedMsgOrderData.find((order) => order.uuid.toString() === signedMsgOrderUuid.toString()); (0, logger_1.allEnvDlog)('swiftClient findOrder', 'order\n', order); return order; } static async confirmSwiftOrder(hash, confirmDuration) { const expireTime = Date.now() + confirmDuration; while (Date.now() < expireTime) { const confirmResponse = await this.get(`/confirmation/hash-status?hash=${encodeURIComponent(hash)}`); if (confirmResponse.status === 200) { console.log('Confirmed hash: ', hash); return { success: true, status: 200, message: 'Confirmed hash: ' + hash, body: { orderId: confirmResponse.body, status: 'confirmed', }, }; } else if (confirmResponse.status >= 500 || confirmResponse.status < 200) { break; } await new Promise((resolve) => setTimeout(resolve, 1000)); } console.error('Failed to confirm hash: ', hash); return { success: false, status: 408, message: 'Failed to confirm hash: ' + hash, body: { status: 'expired', }, }; } static async handleSwiftOrderSubscriber(subscriber, marketIndex, marketType, message, signature, takerPubkey, confirmDuration, signingAuthority) { // First send the order const sendResponse = await this.sendSwiftOrder(marketIndex, marketType, message, signature, takerPubkey, signingAuthority); if (!sendResponse.success) { subscriber.next({ type: 'errored', hash: '', message: sendResponse.message, status: sendResponse.status, }); subscriber.error(); return; } else { subscriber.next({ type: 'sent', hash: sendResponse.body.hash, }); } const hash = sendResponse.body.hash; // Then confirm it const confirmResponse = await this.confirmSwiftOrder(hash, confirmDuration); if (!confirmResponse.success) { subscriber.next({ type: confirmResponse.body.status, hash, message: confirmResponse.message, status: confirmResponse.status, }); subscriber.error(); } if (confirmResponse.body.status === 'confirmed') { subscriber.next({ type: 'confirmed', orderId: confirmResponse.body.orderId, hash, }); subscriber.complete(); } } static async handleSwiftOrderSubscriberWS(subscriber, connection, client, marketIndex, marketType, message, signature, takerPubkey, signedMsgUserOrdersAccountPubkey, signedMsgOrderUuid, confirmDuration, signingAuthority) { // First send the order const sendResponse = await this.sendSwiftOrder(marketIndex, marketType, message, signature, takerPubkey, signingAuthority); (0, logger_1.allEnvDlog)('swiftClient', 'sendResponse\n', sendResponse); if (!sendResponse.success) { subscriber.next({ type: 'errored', hash: '', message: 'Error from swift node: ' + sendResponse.message, status: sendResponse.status, }); subscriber.error(); return; } else { subscriber.next({ type: 'sent', hash: sendResponse.body.hash, }); } const hash = sendResponse.body.hash; // Then confirm it const orderID = await this.confirmSwiftOrderWS(connection, client, signedMsgUserOrdersAccountPubkey, signedMsgOrderUuid, confirmDuration).catch((err) => { (0, logger_1.allEnvDlog)('swiftClient', 'confirmSwiftOrderWS error', err); subscriber.next({ type: 'expired', hash, message: 'Order failed to confirm', status: 408, }); subscriber.error(); }); if (!orderID) { subscriber.next({ type: 'expired', hash, message: 'Order failed to confirm', status: 408, }); subscriber.error(); } else { subscriber.next({ type: 'confirmed', orderId: orderID.toString(), hash, }); subscriber.complete(); } } static sendAndConfirmSwiftOrder(marketIndex, marketType, message, signature, takerPubkey, confirmDuration, signingAuthority) { return new rxjs_1.Observable((subscriber) => { this.handleSwiftOrderSubscriber(subscriber, marketIndex, marketType, message, signature, takerPubkey, confirmDuration, signingAuthority); }); } static sendAndConfirmSwiftOrderWS(connection, client, marketIndex, marketType, message, signature, takerPubkey, signedMsgUserOrdersAccountPubkey, signedMsgOrderUuid, confirmDuration, signingAuthority) { return new rxjs_1.Observable((subscriber) => { this.handleSwiftOrderSubscriberWS(subscriber, connection, client, marketIndex, marketType, message, signature, takerPubkey, signedMsgUserOrdersAccountPubkey, signedMsgOrderUuid, confirmDuration, signingAuthority); }); } static async isSwiftServerHealthy() { const response = await this.get('/health'); return response.status === 200; } static getSwiftHeaders() { var _a; return { 'Content-Type': 'application/json', 'X-Swift-Client-Consumer': (_a = this.swiftClientConsumer) !== null && _a !== void 0 ? _a : 'default', }; } static isSupportedOrderType(orderType) { return this.supportedOrderTypes.includes(orderType); } } exports.SwiftClient = SwiftClient; SwiftClient.baseUrl = ''; SwiftClient.supportedOrderTypes = [sdk_1.OrderType.MARKET, sdk_1.OrderType.LIMIT]; //# sourceMappingURL=swiftClient.js.map