2024-09-12 08:10:27 +00:00
|
|
|
import { SimplePool } from 'nostr-tools';
|
2023-10-27 10:01:59 +00:00
|
|
|
import {
|
|
|
|
Coordinator,
|
2023-10-27 11:00:53 +00:00
|
|
|
type Exchange,
|
|
|
|
type Origin,
|
|
|
|
type PublicOrder,
|
|
|
|
type Settings,
|
2023-10-27 10:01:59 +00:00
|
|
|
defaultExchange,
|
|
|
|
} from '.';
|
|
|
|
import defaultFederation from '../../static/federation.json';
|
2024-07-12 10:57:09 +00:00
|
|
|
import { systemClient } from '../services/System';
|
2023-12-30 12:27:27 +00:00
|
|
|
import { getHost } from '../utils';
|
2024-06-25 10:52:20 +00:00
|
|
|
import { coordinatorDefaultValues } from './Coordinator.model';
|
2023-10-27 10:01:59 +00:00
|
|
|
import { updateExchangeInfo } from './Exchange.model';
|
2024-09-12 08:10:27 +00:00
|
|
|
import eventToPublicOrder from '../utils/nostr';
|
2023-10-27 10:01:59 +00:00
|
|
|
|
2024-08-15 14:26:04 +00:00
|
|
|
type FederationHooks = 'onFederationUpdate';
|
2023-10-27 10:01:59 +00:00
|
|
|
|
|
|
|
export class Federation {
|
2024-03-28 22:21:15 +00:00
|
|
|
constructor(origin: Origin, settings: Settings, hostUrl: string) {
|
2023-10-27 10:01:59 +00:00
|
|
|
this.coordinators = Object.entries(defaultFederation).reduce(
|
|
|
|
(acc: Record<string, Coordinator>, [key, value]: [string, any]) => {
|
2023-12-30 16:01:08 +00:00
|
|
|
if (getHost() !== '127.0.0.1:8000' && key === 'local') {
|
2023-12-30 12:27:27 +00:00
|
|
|
// Do not add `Local Dev` unless it is running on localhost
|
|
|
|
return acc;
|
|
|
|
} else {
|
2024-05-28 06:17:25 +00:00
|
|
|
acc[key] = new Coordinator(value, origin, settings, hostUrl);
|
2024-05-25 17:33:07 +00:00
|
|
|
|
2023-12-30 12:27:27 +00:00
|
|
|
return acc;
|
|
|
|
}
|
2023-10-27 10:01:59 +00:00
|
|
|
},
|
|
|
|
{},
|
|
|
|
);
|
|
|
|
this.exchange = {
|
|
|
|
...defaultExchange,
|
|
|
|
totalCoordinators: Object.keys(this.coordinators).length,
|
|
|
|
};
|
2024-09-12 08:10:27 +00:00
|
|
|
this.book = {};
|
2023-10-27 10:01:59 +00:00
|
|
|
this.hooks = {
|
2024-03-07 16:06:53 +00:00
|
|
|
onFederationUpdate: [],
|
2023-10-27 10:01:59 +00:00
|
|
|
};
|
2024-03-28 22:21:15 +00:00
|
|
|
|
2024-06-25 10:52:20 +00:00
|
|
|
Object.keys(defaultFederation).forEach((key) => {
|
|
|
|
if (key !== 'local' || getHost() === '127.0.0.1:8000') {
|
|
|
|
// Do not add `Local Dev` unless it is running on localhost
|
|
|
|
this.addCoordinator(origin, settings, hostUrl, defaultFederation[key]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-03-28 22:21:15 +00:00
|
|
|
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
|
2024-06-25 10:52:20 +00:00
|
|
|
this.loading = true;
|
2024-03-28 22:21:15 +00:00
|
|
|
|
|
|
|
const host = getHost();
|
|
|
|
const url = `${window.location.protocol}//${host}`;
|
2024-06-25 10:52:20 +00:00
|
|
|
|
2024-03-28 22:21:15 +00:00
|
|
|
const tesnetHost = Object.values(this.coordinators).find((coor) => {
|
|
|
|
return Object.values(coor.testnet).includes(url);
|
|
|
|
});
|
|
|
|
if (tesnetHost) settings.network = 'testnet';
|
2023-10-27 10:01:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public coordinators: Record<string, Coordinator>;
|
|
|
|
public exchange: Exchange;
|
2024-09-12 08:10:27 +00:00
|
|
|
public book: Record<string, PublicOrder>;
|
2023-10-27 10:01:59 +00:00
|
|
|
public loading: boolean;
|
|
|
|
|
2023-10-27 11:00:53 +00:00
|
|
|
public hooks: Record<FederationHooks, Array<() => void>>;
|
2023-10-27 10:01:59 +00:00
|
|
|
|
2024-09-12 08:10:27 +00:00
|
|
|
public relayPool: SimplePool = new SimplePool();
|
|
|
|
|
|
|
|
connectNostr = (): void => {
|
|
|
|
this.loading = true;
|
|
|
|
this.book = {};
|
|
|
|
|
|
|
|
const relays = ['ws://satstraoq35jffvkgpfoqld32nzw2siuvowanruindbfojowpwsjdgad.onion/nostr'];
|
|
|
|
|
|
|
|
this.exchange.loadingCoordinators = relays.length;
|
|
|
|
|
|
|
|
const authors = Object.values(defaultFederation)
|
|
|
|
.map((f) => f.nostrHexPubkey)
|
|
|
|
.filter((item) => item !== undefined);
|
|
|
|
|
|
|
|
this.relayPool.trustedRelayURLs = new Set<string>(relays);
|
|
|
|
this.relayPool.subscribeMany(
|
|
|
|
relays,
|
|
|
|
[
|
|
|
|
{
|
|
|
|
authors,
|
|
|
|
kinds: [38383],
|
|
|
|
'#n': ['mainnet'],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
{
|
|
|
|
onevent: (event) => {
|
|
|
|
const { dTag, publicOrder } = eventToPublicOrder(event);
|
|
|
|
|
|
|
|
if (publicOrder) {
|
|
|
|
this.book[dTag] = publicOrder;
|
|
|
|
} else {
|
|
|
|
delete this.book[dTag];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
oneose: () => {
|
|
|
|
this.exchange.loadingCoordinators = this.exchange.loadingCoordinators - 1;
|
|
|
|
this.loading = this.exchange.loadingCoordinators > 0;
|
|
|
|
this.updateExchange();
|
|
|
|
this.triggerHook('onFederationUpdate');
|
|
|
|
},
|
|
|
|
onclose: () => {
|
|
|
|
this.exchange.loadingCoordinators = this.exchange.loadingCoordinators - 1;
|
|
|
|
this.loading = this.exchange.loadingCoordinators > 0;
|
|
|
|
this.updateExchange();
|
|
|
|
this.triggerHook('onFederationUpdate');
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2024-06-25 10:52:20 +00:00
|
|
|
addCoordinator = (
|
|
|
|
origin: Origin,
|
|
|
|
settings: Settings,
|
|
|
|
hostUrl: string,
|
|
|
|
attributes: Record<any, any>,
|
2024-08-10 15:54:13 +00:00
|
|
|
): void => {
|
2024-06-25 10:52:20 +00:00
|
|
|
const value = {
|
|
|
|
...coordinatorDefaultValues,
|
|
|
|
...attributes,
|
|
|
|
};
|
|
|
|
this.coordinators[value.shortAlias] = new Coordinator(value, origin, settings, hostUrl);
|
|
|
|
this.exchange.totalCoordinators = Object.keys(this.coordinators).length;
|
|
|
|
this.updateEnabledCoordinators();
|
|
|
|
this.triggerHook('onFederationUpdate');
|
|
|
|
};
|
|
|
|
|
2023-10-27 10:01:59 +00:00
|
|
|
// Hooks
|
|
|
|
registerHook = (hookName: FederationHooks, fn: () => void): void => {
|
|
|
|
this.hooks[hookName].push(fn);
|
|
|
|
};
|
|
|
|
|
|
|
|
triggerHook = (hookName: FederationHooks): void => {
|
2023-10-27 11:00:53 +00:00
|
|
|
this.hooks[hookName]?.forEach((fn) => {
|
|
|
|
fn();
|
|
|
|
});
|
2023-10-27 10:01:59 +00:00
|
|
|
};
|
|
|
|
|
2023-12-30 10:32:18 +00:00
|
|
|
onCoordinatorSaved = (): void => {
|
2024-09-12 08:10:27 +00:00
|
|
|
// this.book = Object.values(this.coordinators).reduce<Record<string, PublicOrder>>(
|
|
|
|
// (book, coordinator) => {
|
|
|
|
// return { ...book, ...coordinator.book };
|
|
|
|
// },
|
|
|
|
// {},
|
|
|
|
// );
|
2024-01-23 23:30:16 +00:00
|
|
|
this.exchange.loadingCoordinators =
|
|
|
|
this.exchange.loadingCoordinators < 1 ? 0 : this.exchange.loadingCoordinators - 1;
|
|
|
|
this.loading = this.exchange.loadingCoordinators > 0;
|
2024-03-15 20:06:54 +00:00
|
|
|
this.updateExchange();
|
|
|
|
this.triggerHook('onFederationUpdate');
|
2023-10-27 10:01:59 +00:00
|
|
|
};
|
|
|
|
|
2024-03-28 22:21:15 +00:00
|
|
|
updateUrl = async (origin: Origin, settings: Settings, hostUrl: string): Promise<void> => {
|
2024-07-12 10:57:09 +00:00
|
|
|
const federationUrls = {};
|
2023-12-30 16:45:45 +00:00
|
|
|
for (const coor of Object.values(this.coordinators)) {
|
2024-03-28 22:21:15 +00:00
|
|
|
coor.updateUrl(origin, settings, hostUrl);
|
2024-07-12 10:57:09 +00:00
|
|
|
federationUrls[coor.shortAlias] = coor.url;
|
2023-12-30 16:45:45 +00:00
|
|
|
}
|
2024-07-12 10:57:09 +00:00
|
|
|
systemClient.setCookie('federation', JSON.stringify(federationUrls));
|
2023-12-30 16:45:45 +00:00
|
|
|
};
|
|
|
|
|
2023-11-02 14:15:18 +00:00
|
|
|
update = async (): Promise<void> => {
|
2024-01-23 23:30:16 +00:00
|
|
|
this.loading = true;
|
2024-03-15 20:06:54 +00:00
|
|
|
this.exchange.info = {
|
|
|
|
num_public_buy_orders: 0,
|
|
|
|
num_public_sell_orders: 0,
|
|
|
|
book_liquidity: 0,
|
|
|
|
active_robots_today: 0,
|
|
|
|
last_day_nonkyc_btc_premium: 0,
|
|
|
|
last_day_volume: 0,
|
|
|
|
lifetime_volume: 0,
|
|
|
|
version: { major: 0, minor: 0, patch: 0 },
|
|
|
|
};
|
2024-03-28 22:21:15 +00:00
|
|
|
this.exchange.onlineCoordinators = 0;
|
2024-01-23 23:30:16 +00:00
|
|
|
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
|
2024-03-28 22:21:15 +00:00
|
|
|
this.updateEnabledCoordinators();
|
2024-09-12 08:10:27 +00:00
|
|
|
|
|
|
|
this.connectNostr();
|
|
|
|
|
2023-11-02 14:15:18 +00:00
|
|
|
for (const coor of Object.values(this.coordinators)) {
|
2024-08-10 15:54:13 +00:00
|
|
|
void coor.update(() => {
|
2024-03-28 22:21:15 +00:00
|
|
|
this.exchange.onlineCoordinators = this.exchange.onlineCoordinators + 1;
|
2023-12-30 10:32:18 +00:00
|
|
|
this.onCoordinatorSaved();
|
2023-10-27 11:00:53 +00:00
|
|
|
});
|
2023-11-02 14:15:18 +00:00
|
|
|
}
|
2023-10-27 10:01:59 +00:00
|
|
|
};
|
|
|
|
|
2023-12-22 12:58:59 +00:00
|
|
|
updateBook = async (): Promise<void> => {
|
2024-09-12 08:10:27 +00:00
|
|
|
// this.loading = true;
|
|
|
|
// this.book = [];
|
|
|
|
// this.triggerHook('onFederationUpdate');
|
|
|
|
// this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
|
|
|
|
// for (const coor of Object.values(this.coordinators)) {
|
|
|
|
// void coor.updateBook(() => {
|
|
|
|
// this.onCoordinatorSaved();
|
|
|
|
// this.triggerHook('onFederationUpdate');
|
|
|
|
// });
|
|
|
|
// }
|
2023-12-22 12:58:59 +00:00
|
|
|
};
|
|
|
|
|
2023-11-02 14:15:18 +00:00
|
|
|
updateExchange = (): void => {
|
2023-10-27 10:01:59 +00:00
|
|
|
this.exchange.info = updateExchangeInfo(this);
|
2024-03-07 16:06:53 +00:00
|
|
|
this.triggerHook('onFederationUpdate');
|
2023-10-27 10:01:59 +00:00
|
|
|
};
|
|
|
|
|
2023-10-27 11:00:53 +00:00
|
|
|
// Coordinators
|
2023-10-27 10:01:59 +00:00
|
|
|
getCoordinator = (shortAlias: string): Coordinator => {
|
|
|
|
return this.coordinators[shortAlias];
|
|
|
|
};
|
|
|
|
|
2023-11-02 14:15:18 +00:00
|
|
|
disableCoordinator = (shortAlias: string): void => {
|
2023-10-27 10:01:59 +00:00
|
|
|
this.coordinators[shortAlias].disable();
|
2024-01-23 23:30:16 +00:00
|
|
|
this.updateEnabledCoordinators();
|
2024-08-15 14:26:04 +00:00
|
|
|
this.triggerHook('onFederationUpdate');
|
2023-10-27 10:01:59 +00:00
|
|
|
};
|
|
|
|
|
2023-11-02 14:15:18 +00:00
|
|
|
enableCoordinator = (shortAlias: string): void => {
|
2023-11-01 12:18:00 +00:00
|
|
|
this.coordinators[shortAlias].enable(() => {
|
2024-01-23 23:30:16 +00:00
|
|
|
this.updateEnabledCoordinators();
|
2024-08-15 14:26:04 +00:00
|
|
|
this.triggerHook('onFederationUpdate');
|
2023-11-01 12:18:00 +00:00
|
|
|
});
|
2023-10-27 10:01:59 +00:00
|
|
|
};
|
2024-01-23 23:30:16 +00:00
|
|
|
|
|
|
|
updateEnabledCoordinators = (): void => {
|
|
|
|
this.exchange.enabledCoordinators = Object.values(this.coordinators).filter(
|
|
|
|
(c) => c.enabled,
|
|
|
|
).length;
|
2024-03-15 20:06:54 +00:00
|
|
|
this.triggerHook('onFederationUpdate');
|
2024-01-23 23:30:16 +00:00
|
|
|
};
|
2023-10-27 10:01:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export default Federation;
|