mirror of
https://github.com/Reckless-Satoshi/robosats.git
synced 2024-10-03 22:58:01 +03:00
Merge 284e565a15
into 57c2c8fc3b
This commit is contained in:
commit
02b9800129
134
frontend/package-lock.json
generated
134
frontend/package-lock.json
generated
@ -31,8 +31,10 @@
|
||||
"i18next-http-backend": "^2.5.0",
|
||||
"install": "^0.13.0",
|
||||
"js-sha256": "^0.11.0",
|
||||
"latlon-geohash": "^2.0.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"light-bolt11-decoder": "^3.1.1",
|
||||
"nostr-tools": "^2.7.2",
|
||||
"npm": "^10.8.1",
|
||||
"openpgp": "^5.11.0",
|
||||
"react": "^18.2.0",
|
||||
@ -60,6 +62,7 @@
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@babel/runtime": "^7.22.6",
|
||||
"@types/jest": "^29.5.3",
|
||||
"@types/latlon-geohash": "^2.0.3",
|
||||
"@types/leaflet": "^1.9.7",
|
||||
"@types/react": "^18.2.21",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
@ -3931,6 +3934,51 @@
|
||||
"react": ">= 16.14.0 < 19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/ciphers": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz",
|
||||
"integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves/node_modules/@noble/hashes": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz",
|
||||
"integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
@ -4076,6 +4124,45 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/@scure/bip32": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz",
|
||||
"integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "~1.1.0",
|
||||
"@noble/hashes": "~1.3.1",
|
||||
"@scure/base": "~1.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip32/node_modules/@noble/curves": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz",
|
||||
"integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@scure/bip39": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
|
||||
"integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "~1.3.0",
|
||||
"@scure/base": "~1.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.27.8",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||
@ -4513,6 +4600,13 @@
|
||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/latlon-geohash": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/latlon-geohash/-/latlon-geohash-2.0.3.tgz",
|
||||
"integrity": "sha512-VP6CWnHN4GT48Ra83JQl31SN/qSRp0OI2lb3TPPH+PhZpVzSxZbtjMNbUQQNZ2SdIOHMctOCv+q+EIyJQ5EaKw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/leaflet": {
|
||||
"version": "1.9.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.7.tgz",
|
||||
@ -11726,6 +11820,12 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/latlon-geohash": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/latlon-geohash/-/latlon-geohash-2.0.0.tgz",
|
||||
"integrity": "sha512-OKBswTwrvTdtenV+9C9euBmvgGuqyjJNAzpQCarRz1m8/pYD2nz9fKkXmLs2S3jeXaLi3Ry76twQplKKUlgS/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lazystream": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
|
||||
@ -12482,6 +12582,38 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nostr-tools": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.7.2.tgz",
|
||||
"integrity": "sha512-Bq3Ug0SZFtgtL1+0wCnAe8AJtI7yx/00/a2nUug9SkhfOwlKS92Tef12iCK9FdwXw+oFZWMtRnSwcLayQso+xA==",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@noble/ciphers": "^0.5.1",
|
||||
"@noble/curves": "1.2.0",
|
||||
"@noble/hashes": "1.3.1",
|
||||
"@scure/base": "1.1.1",
|
||||
"@scure/bip32": "1.3.1",
|
||||
"@scure/bip39": "1.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"nostr-wasm": "v0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/nostr-wasm": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz",
|
||||
"integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/npm": {
|
||||
"version": "10.8.1",
|
||||
"resolved": "https://registry.npmjs.org/npm/-/npm-10.8.1.tgz",
|
||||
@ -17800,7 +17932,7 @@
|
||||
"version": "5.5.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
|
||||
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
@ -22,6 +22,7 @@
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@babel/runtime": "^7.22.6",
|
||||
"@types/jest": "^29.5.3",
|
||||
"@types/latlon-geohash": "^2.0.3",
|
||||
"@types/leaflet": "^1.9.7",
|
||||
"@types/react": "^18.2.21",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
@ -71,8 +72,10 @@
|
||||
"i18next-http-backend": "^2.5.0",
|
||||
"install": "^0.13.0",
|
||||
"js-sha256": "^0.11.0",
|
||||
"latlon-geohash": "^2.0.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"light-bolt11-decoder": "^3.1.1",
|
||||
"nostr-tools": "^2.7.2",
|
||||
"npm": "^10.8.1",
|
||||
"openpgp": "^5.11.0",
|
||||
"react": "^18.2.0",
|
||||
|
@ -25,7 +25,7 @@ const MakerPage = (): JSX.Element => {
|
||||
|
||||
const matches = useMemo(() => {
|
||||
return filterOrders({
|
||||
orders: federation.book,
|
||||
orders: Object.values(federation.book),
|
||||
baseFilter: {
|
||||
currency: fav.currency === 0 ? 1 : fav.currency,
|
||||
type: fav.type,
|
||||
|
@ -92,7 +92,7 @@ const BookTable = ({
|
||||
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const orders = orderList ?? federation.book;
|
||||
const orders = orderList ?? Object.values(federation.book);
|
||||
|
||||
const [paginationModel, setPaginationModel] = useState<GridPaginationModel>({
|
||||
pageSize: 0,
|
||||
@ -425,6 +425,11 @@ const BookTable = ({
|
||||
width: width * fontSize,
|
||||
renderCell: (params: any) => {
|
||||
const currencyCode = String(currencyDict[params.row.currency.toString()]);
|
||||
const coordinator = federation.getCoordinator(params.row.coordinatorShortAlias);
|
||||
const premium = parseFloat(params.row.premium);
|
||||
const price =
|
||||
(coordinator.limits[params.row.currency.toString()]?.price ?? 1) * (1 + premium / 100);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ cursor: 'pointer' }}
|
||||
@ -432,7 +437,7 @@ const BookTable = ({
|
||||
onOrderClicked(params.row.id, params.row.coordinatorShortAlias);
|
||||
}}
|
||||
>
|
||||
{`${pn(params.row.price)} ${currencyCode}/BTC`}
|
||||
{`${pn(Math.round(price))} ${currencyCode}/BTC`}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@ -575,6 +580,15 @@ const BookTable = ({
|
||||
type: 'number',
|
||||
width: width * fontSize,
|
||||
renderCell: (params: any) => {
|
||||
const coordinator = federation.getCoordinator(params.row.coordinatorShortAlias);
|
||||
const amount = Boolean(params.row.has_range)
|
||||
? parseFloat(params.row.max_amount)
|
||||
: parseFloat(params.row.amount);
|
||||
const premium = parseFloat(params.row.premium);
|
||||
const price =
|
||||
(coordinator.limits[params.row.currency.toString()]?.price ?? 1) * (1 + premium / 100);
|
||||
const satoshisNow = (100000000 * amount) / price;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ cursor: 'pointer' }}
|
||||
@ -582,9 +596,9 @@ const BookTable = ({
|
||||
onOrderClicked(params.row.id, params.row.coordinatorShortAlias);
|
||||
}}
|
||||
>
|
||||
{params.row.satoshis_now > 1000000
|
||||
? `${pn(Math.round(params.row.satoshis_now / 10000) / 100)} M`
|
||||
: `${pn(Math.round(params.row.satoshis_now / 1000))} K`}
|
||||
{satoshisNow > 1000000
|
||||
? `${pn(Math.round(satoshisNow / 10000) / 100)} M`
|
||||
: `${pn(Math.round(satoshisNow / 1000))} K`}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -66,8 +66,8 @@ const DepthChart: React.FC<DepthChartProps> = ({
|
||||
}, [fav.currency]);
|
||||
|
||||
useEffect(() => {
|
||||
if (federation.book.length > 0) {
|
||||
const enriched = federation.book.map((order) => {
|
||||
if (Object.values(federation.book).length > 0) {
|
||||
const enriched = Object.values(federation.book).map((order) => {
|
||||
// We need to transform all currencies to the same base (ex. USD), we don't have the exchange rate
|
||||
// for EUR -> USD, but we know the rate of both to BTC, so we get advantage of it and apply a
|
||||
// simple rule of three
|
||||
|
@ -84,7 +84,7 @@ const MapChart: React.FC<MapChartProps> = ({
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<Paper variant='outlined' style={{ width: '100%', height: '100%', justifyContent: 'center' }}>
|
||||
{federation.book.length < 1 ? (
|
||||
{Object.values(federation.book).length < 1 ? (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
@ -130,7 +130,11 @@ const MapChart: React.FC<MapChartProps> = ({
|
||||
</Tooltip>
|
||||
</Grid>
|
||||
<div style={{ height: `${height - 3.1}em` }}>
|
||||
<Map useTiles={useTiles} orders={federation.book} onOrderClicked={onOrderClicked} />
|
||||
<Map
|
||||
useTiles={useTiles}
|
||||
orders={Object.values(federation.book)}
|
||||
onOrderClicked={onOrderClicked}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
@ -223,9 +223,6 @@ export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.E
|
||||
|
||||
useEffect(() => {
|
||||
void i18n.changeLanguage(settings.language);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('torStatus', (event) => {
|
||||
// Trick to improve UX on Android webview: delay the "Connected to TOR" status by 5 secs to avoid long waits on the first request.
|
||||
setTimeout(
|
||||
|
@ -3,26 +3,26 @@ export interface PublicOrder {
|
||||
created_at: Date;
|
||||
expires_at: Date;
|
||||
type: number;
|
||||
currency: number;
|
||||
currency: number | null;
|
||||
amount: string;
|
||||
base_amount?: number;
|
||||
has_range: boolean;
|
||||
min_amount: number;
|
||||
max_amount: number;
|
||||
min_amount: string | null;
|
||||
max_amount: string | null;
|
||||
payment_method: string;
|
||||
is_explicit: false;
|
||||
premium: number;
|
||||
satoshis: number;
|
||||
satoshis_now: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
bond_size: number;
|
||||
maker: number;
|
||||
premium: string;
|
||||
satoshis: number | null;
|
||||
satoshis_now: number | null;
|
||||
latitude: number | null;
|
||||
longitude: number | null;
|
||||
bond_size: string;
|
||||
maker: number | null;
|
||||
escrow_duration: number;
|
||||
maker_nick: string;
|
||||
maker_hash_id: string;
|
||||
price: number;
|
||||
maker_status: 'Active' | 'Seen recently' | 'Inactive';
|
||||
price: number | null;
|
||||
maker_status?: 'Active' | 'Seen recently' | 'Inactive';
|
||||
coordinatorShortAlias?: string;
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ export class Coordinator {
|
||||
public basePath: string;
|
||||
|
||||
// These properties are fetched from coordinator API
|
||||
public book: PublicOrder[] = [];
|
||||
public book: Record<string, PublicOrder> = {};
|
||||
public loadingBook: boolean = false;
|
||||
public info?: Info | undefined = undefined;
|
||||
public loadingInfo: boolean = false;
|
||||
@ -187,7 +187,7 @@ export class Coordinator {
|
||||
if (this.isUpdated()) onUpdate(this.shortAlias);
|
||||
};
|
||||
|
||||
this.loadBook(onDataLoad);
|
||||
// this.loadBook(onDataLoad);
|
||||
this.loadLimits(onDataLoad);
|
||||
this.loadInfo(onDataLoad);
|
||||
};
|
||||
@ -209,30 +209,30 @@ export class Coordinator {
|
||||
if (this.url === '') return;
|
||||
if (this.loadingBook) return;
|
||||
|
||||
this.loadingBook = true;
|
||||
this.book = [];
|
||||
// this.loadingBook = true;
|
||||
// this.book = [];
|
||||
|
||||
apiClient
|
||||
.get(this.url, `${this.basePath}/api/book/`)
|
||||
.then((data) => {
|
||||
if (!data?.not_found) {
|
||||
this.book = (data as PublicOrder[]).map((order) => {
|
||||
order.coordinatorShortAlias = this.shortAlias;
|
||||
return order;
|
||||
});
|
||||
void this.generateAllMakerAvatars(data);
|
||||
onDataLoad();
|
||||
} else {
|
||||
this.book = [];
|
||||
onDataLoad();
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
})
|
||||
.finally(() => {
|
||||
this.loadingBook = false;
|
||||
});
|
||||
// apiClient
|
||||
// .get(this.url, `${this.basePath}/api/book/`)
|
||||
// .then((data) => {
|
||||
// if (!data?.not_found) {
|
||||
// this.book = (data as PublicOrder[]).map((order) => {
|
||||
// order.coordinatorShortAlias = this.shortAlias;
|
||||
// return order;
|
||||
// });
|
||||
// void this.generateAllMakerAvatars(data);
|
||||
// onDataLoad();
|
||||
// } else {
|
||||
// this.book = [];
|
||||
// onDataLoad();
|
||||
// }
|
||||
// })
|
||||
// .catch((e) => {
|
||||
// console.log(e);
|
||||
// })
|
||||
// .finally(() => {
|
||||
// this.loadingBook = false;
|
||||
// });
|
||||
};
|
||||
|
||||
loadLimits = (onDataLoad: () => void = () => {}): void => {
|
||||
@ -298,7 +298,7 @@ export class Coordinator {
|
||||
this.enabled = false;
|
||||
this.info = undefined;
|
||||
this.limits = {};
|
||||
this.book = [];
|
||||
this.book = {};
|
||||
};
|
||||
|
||||
isUpdated = (): boolean => {
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { SimplePool } from 'nostr-tools';
|
||||
import {
|
||||
Coordinator,
|
||||
type Exchange,
|
||||
@ -11,6 +12,7 @@ import { systemClient } from '../services/System';
|
||||
import { getHost } from '../utils';
|
||||
import { coordinatorDefaultValues } from './Coordinator.model';
|
||||
import { updateExchangeInfo } from './Exchange.model';
|
||||
import eventToPublicOrder from '../utils/nostr';
|
||||
|
||||
type FederationHooks = 'onFederationUpdate';
|
||||
|
||||
@ -33,7 +35,7 @@ export class Federation {
|
||||
...defaultExchange,
|
||||
totalCoordinators: Object.keys(this.coordinators).length,
|
||||
};
|
||||
this.book = [];
|
||||
this.book = {};
|
||||
this.hooks = {
|
||||
onFederationUpdate: [],
|
||||
};
|
||||
@ -59,11 +61,61 @@ export class Federation {
|
||||
|
||||
public coordinators: Record<string, Coordinator>;
|
||||
public exchange: Exchange;
|
||||
public book: PublicOrder[];
|
||||
public book: Record<string, PublicOrder>;
|
||||
public loading: boolean;
|
||||
|
||||
public hooks: Record<FederationHooks, Array<() => void>>;
|
||||
|
||||
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');
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
addCoordinator = (
|
||||
origin: Origin,
|
||||
settings: Settings,
|
||||
@ -92,9 +144,12 @@ export class Federation {
|
||||
};
|
||||
|
||||
onCoordinatorSaved = (): void => {
|
||||
this.book = Object.values(this.coordinators).reduce<PublicOrder[]>((array, coordinator) => {
|
||||
return [...array, ...coordinator.book];
|
||||
}, []);
|
||||
// this.book = Object.values(this.coordinators).reduce<Record<string, PublicOrder>>(
|
||||
// (book, coordinator) => {
|
||||
// return { ...book, ...coordinator.book };
|
||||
// },
|
||||
// {},
|
||||
// );
|
||||
this.exchange.loadingCoordinators =
|
||||
this.exchange.loadingCoordinators < 1 ? 0 : this.exchange.loadingCoordinators - 1;
|
||||
this.loading = this.exchange.loadingCoordinators > 0;
|
||||
@ -126,6 +181,9 @@ export class Federation {
|
||||
this.exchange.onlineCoordinators = 0;
|
||||
this.exchange.loadingCoordinators = Object.keys(this.coordinators).length;
|
||||
this.updateEnabledCoordinators();
|
||||
|
||||
this.connectNostr();
|
||||
|
||||
for (const coor of Object.values(this.coordinators)) {
|
||||
void coor.update(() => {
|
||||
this.exchange.onlineCoordinators = this.exchange.onlineCoordinators + 1;
|
||||
@ -135,16 +193,16 @@ export class Federation {
|
||||
};
|
||||
|
||||
updateBook = async (): Promise<void> => {
|
||||
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');
|
||||
});
|
||||
}
|
||||
// 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');
|
||||
// });
|
||||
// }
|
||||
};
|
||||
|
||||
updateExchange = (): void => {
|
||||
|
101
frontend/src/utils/nostr.ts
Normal file
101
frontend/src/utils/nostr.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import { Event } from 'nostr-tools';
|
||||
import { PublicOrder } from '../models';
|
||||
import { fromUnixTime } from 'date-fns';
|
||||
import Geohash from 'latlon-geohash';
|
||||
import currencyDict from '../../static/assets/currencies.json';
|
||||
import defaultFederation from '../../static/federation.json';
|
||||
|
||||
const eventToPublicOrder = (event: Event): { dTag: string; publicOrder: PublicOrder | null } => {
|
||||
const publicOrder: PublicOrder = {
|
||||
id: 0,
|
||||
coordinatorShortAlias: '',
|
||||
created_at: new Date(),
|
||||
expires_at: new Date(),
|
||||
type: 1,
|
||||
currency: null,
|
||||
amount: '',
|
||||
has_range: false,
|
||||
min_amount: null,
|
||||
max_amount: null,
|
||||
payment_method: '',
|
||||
is_explicit: false,
|
||||
premium: '',
|
||||
satoshis: null,
|
||||
maker: null,
|
||||
escrow_duration: 0,
|
||||
bond_size: '',
|
||||
latitude: null,
|
||||
longitude: null,
|
||||
maker_nick: '',
|
||||
maker_hash_id: '',
|
||||
satoshis_now: null,
|
||||
price: null,
|
||||
};
|
||||
|
||||
const statusTag = event.tags.find((t) => t[0] === 's') ?? [];
|
||||
const dTag = event.tags.find((t) => t[0] === 'd') ?? [];
|
||||
|
||||
if (statusTag[1] !== 'pending') return { dTag: dTag[1], publicOrder: null };
|
||||
|
||||
event.tags.forEach((tag) => {
|
||||
switch (tag[0]) {
|
||||
case 'k':
|
||||
publicOrder.type = tag[1] === 'sell' ? 1 : 0;
|
||||
break;
|
||||
case 'expiration':
|
||||
publicOrder.expires_at = fromUnixTime(parseInt(tag[1], 10));
|
||||
publicOrder.escrow_duration = parseInt(tag[2], 10);
|
||||
break;
|
||||
case 'fa':
|
||||
if (tag[2]) {
|
||||
publicOrder.has_range = true;
|
||||
publicOrder.min_amount = tag[1] ?? null;
|
||||
publicOrder.max_amount = tag[2] ?? null;
|
||||
} else {
|
||||
publicOrder.amount = tag[1];
|
||||
}
|
||||
break;
|
||||
case 'bond':
|
||||
publicOrder.bond_size = tag[1];
|
||||
break;
|
||||
case 'name':
|
||||
publicOrder.maker_nick = tag[1];
|
||||
publicOrder.maker_hash_id = tag[2];
|
||||
break;
|
||||
case 'premium':
|
||||
publicOrder.premium = tag[1];
|
||||
break;
|
||||
case 'pm':
|
||||
tag.shift();
|
||||
publicOrder.payment_method = tag.join(' ');
|
||||
break;
|
||||
case 'g':
|
||||
const { lat, lon } = Geohash.decode(tag[1]);
|
||||
publicOrder.latitude = lat;
|
||||
publicOrder.longitude = lon;
|
||||
break;
|
||||
case 'f':
|
||||
const currencyNumber = Object.entries(currencyDict).find(
|
||||
([_key, value]) => value === tag[1],
|
||||
);
|
||||
publicOrder.currency = currencyNumber?.[0] ? parseInt(currencyNumber[0], 10) : null;
|
||||
break;
|
||||
case 'source':
|
||||
const orderUrl = tag[1].split('/');
|
||||
publicOrder.id = parseInt(orderUrl[orderUrl.length - 1] ?? '0');
|
||||
const coordinatorIdentifier = orderUrl[orderUrl.length - 2] ?? '';
|
||||
publicOrder.coordinatorShortAlias = Object.entries(defaultFederation).find(
|
||||
([key, value]) => value.identifier === coordinatorIdentifier,
|
||||
)?.[0];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// price = limitsList[index].price * (1 + premium / 100);
|
||||
|
||||
return { dTag: dTag[1], publicOrder };
|
||||
};
|
||||
|
||||
export default eventToPublicOrder;
|
@ -2,10 +2,12 @@
|
||||
"temple": {
|
||||
"longAlias": "Temple of Sats",
|
||||
"shortAlias": "temple",
|
||||
"identifier": "templeofsats",
|
||||
"description": "I am passionate about joining Robosats as a coordinator because I believe that peer-to-peer, non-KYC Bitcoin transactions are vital for the community's empowerment and autonomy. I aim to champion users' privacy, and provide a seamless experience for genuine Bitcoin enthusiasts.",
|
||||
"motto": "Privacy and Integrity: Temple of Sats, where Bitcoin's essence thrives.",
|
||||
"color": "#000",
|
||||
"established": "2023-12-02",
|
||||
"nostrHexPubkey": "74001620297035daa61475c069f90b6950087fea0d0134b795fac758c34e7191",
|
||||
"contact": {
|
||||
"email": "coordinator@templeofsats.org",
|
||||
"telegram": "templeofsats",
|
||||
@ -50,10 +52,12 @@
|
||||
"lake": {
|
||||
"longAlias": "TheBigLake",
|
||||
"shortAlias": "lake",
|
||||
"identifier": "thebiglake",
|
||||
"description": "Becoming a RoboSats coordinator represents boosting intrinsic values of decentralization and economic freedom. RoboSats solves the problem of KYC and loss of privacy that big Exchanges are forced to comply with. I believe that decentralizing the lightning nodes will enhance the robustness of the tool, allowing more users to join. I am excited to be part of this new phase of growth.",
|
||||
"motto": "TheBigLake: The Lake of Economic Freedom.",
|
||||
"color": "#000D28",
|
||||
"established": "2023-12-30",
|
||||
"nostrHexPubkey": "f2d4855df39a7db6196666e8469a07a131cddc08dcaa744a344343ffcf54a10c",
|
||||
"contact": {
|
||||
"email": "gabbygator184@proton.me",
|
||||
"telegram": "gabbygator184",
|
||||
@ -95,10 +99,12 @@
|
||||
"veneto": {
|
||||
"longAlias": "BitcoinVeneto",
|
||||
"shortAlias": "veneto",
|
||||
"identifier": "bitcoinveneto",
|
||||
"description": "Nati come gruppo di informatici con esperienze diverse, scoperti i bitcoin a fine 2013 ci siamo entusiasmati e dedicati al bitcoin, alla blockchain ed alle criptovalute in generale, in particolare aiutando, informando e seguendo nel percorso di alfabetizzazione nel mondo della valuta digitale tutte le Aziende ed i privati che negli ultimi anni ci hanno dato la loro fiducia.",
|
||||
"motto": "Le Tue Guide NON Virtuali su Bitcoin, Blockchain e Crypto. In Veneto.",
|
||||
"color": "#000D27",
|
||||
"established": "2024-02-24",
|
||||
"nostrHexPubkey": "c8dc40a80bbb41fe7430fca9d0451b37a2341486ab65f890955528e4732da34a",
|
||||
"contact": {
|
||||
"email": "bitcoinveneto@proton.me",
|
||||
"telegram": "BitcoinVeneto",
|
||||
|
9920
frontend/yarn.lock
Normal file
9920
frontend/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user