mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-28 06:44:40 +03:00
Nostr test
This commit is contained in:
parent
d4b3a708b2
commit
49c2469bb1
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": "Born as a group of computer scientists with different experiences, we discovered bitcoin at the end of 2013 and became enthusiastic and dedicated to bitcoin, blockchain and cryptocurrencies in general, in particular helping, informing and following all companies on the path to literacy in the world of digital currency. and the private individuals who have placed their trust in us in recent years.",
|
||||
"motto": "Your NON-Virtual Guides on Bitcoin, Blockchain and Crypto.",
|
||||
"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