@drift-labs/common
Version:
Common functions for Drift
299 lines • 12.7 kB
JavaScript
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
;