From 06fc2e8bd745d2f692f5d865bbb88fc471340349 Mon Sep 17 00:00:00 2001 From: koalasat Date: Tue, 10 Sep 2024 17:55:29 +0200 Subject: [PATCH] Some fixes --- frontend/src/basic/Routes.tsx | 3 +- .../components/OrderDetails/TakeButton.tsx | 8 +- .../src/components/OrderDetails/index.tsx | 4 +- frontend/src/contexts/AppContext.tsx | 6 +- frontend/src/contexts/FederationContext.tsx | 93 +------------------ frontend/src/models/Garage.model.ts | 8 +- frontend/src/models/Maker.model.ts | 9 +- frontend/src/models/Order.model.ts | 11 +++ frontend/src/models/Slot.model.ts | 7 ++ frontend/static/federation.json | 43 --------- 10 files changed, 44 insertions(+), 148 deletions(-) diff --git a/frontend/src/basic/Routes.tsx b/frontend/src/basic/Routes.tsx index 909a7e45..b9a22110 100644 --- a/frontend/src/basic/Routes.tsx +++ b/frontend/src/basic/Routes.tsx @@ -13,11 +13,10 @@ const Routes: React.FC = () => { useEffect(() => { window.addEventListener('navigateToPage', (event) => { - console.log('navigateToPage', JSON.stringify(event)); const orderId: string = event?.detail?.order_id; const coordinator: string = event?.detail?.coordinator; if (orderId && coordinator) { - const slot = garage.getSlotByOrder(coordinator, orderId); + const slot = garage.getSlotByOrder(coordinator, parseInt(orderId, 10)); if (slot?.token) { garage.setCurrentSlot(slot?.token); navigate(`/order/${coordinator}/${orderId}`); diff --git a/frontend/src/components/OrderDetails/TakeButton.tsx b/frontend/src/components/OrderDetails/TakeButton.tsx index 00f7081a..acd5d1fe 100644 --- a/frontend/src/components/OrderDetails/TakeButton.tsx +++ b/frontend/src/components/OrderDetails/TakeButton.tsx @@ -319,12 +319,8 @@ const TakeButton = ({ setLoadingTake(true); - currentOrder - .submitAction(federation, slot, { - action: 'take', - amount: - currentOrder?.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount), - }) + slot + .takeOrder(federation, currentOrder, takeAmount) .then((order) => { if (order?.bad_request !== undefined) { setBadRequest(order.bad_request); diff --git a/frontend/src/components/OrderDetails/index.tsx b/frontend/src/components/OrderDetails/index.tsx index 1f4437ec..a368df36 100644 --- a/frontend/src/components/OrderDetails/index.tsx +++ b/frontend/src/components/OrderDetails/index.tsx @@ -174,8 +174,8 @@ const OrderDetails = ({ const isBuyer = (order.type === 0 && order.is_maker) || (order.type === 1 && !order.is_maker); const tradeFee = order.is_maker - ? coordinator.info?.maker_fee ?? 0 - : coordinator.info?.taker_fee ?? 0; + ? (coordinator.info?.maker_fee ?? 0) + : (coordinator.info?.taker_fee ?? 0); const defaultRoutingBudget = 0.001; const btc_now = order.satoshis_now / 100000000; const rate = Number(order.max_amount ?? order.amount) / btc_now; diff --git a/frontend/src/contexts/AppContext.tsx b/frontend/src/contexts/AppContext.tsx index 26bc3c36..db7e9f13 100644 --- a/frontend/src/contexts/AppContext.tsx +++ b/frontend/src/contexts/AppContext.tsx @@ -76,7 +76,11 @@ const makeTheme = function (settings: Settings): Theme { }; const getHostUrl = (network = 'mainnet'): string => { - let host = defaultFederation.exp[network].onion; + const randomAlias = + Object.keys(defaultFederation)[ + Math.floor(Math.random() * Object.keys(defaultFederation).length) + ]; + let host = defaultFederation[randomAlias][network].onion; let protocol = 'http:'; if (window.NativeRobosats === undefined) { host = getHost(); diff --git a/frontend/src/contexts/FederationContext.tsx b/frontend/src/contexts/FederationContext.tsx index 5aa0ab5e..da541310 100644 --- a/frontend/src/contexts/FederationContext.tsx +++ b/frontend/src/contexts/FederationContext.tsx @@ -8,7 +8,7 @@ import React, { type ReactNode, } from 'react'; -import { type Order, Federation, Settings } from '../models'; +import { Federation, Settings } from '../models'; import { federationLottery } from '../utils'; @@ -28,10 +28,6 @@ export interface FederationContextProviderProps { export interface UseFederationStoreType { federation: Federation; sortedCoordinators: string[]; - setDelay: Dispatch>; - currentOrderId: CurrentOrderIdProps; - setCurrentOrderId: Dispatch>; - currentOrder: Order | null; coordinatorUpdatedAt: string; federationUpdatedAt: string; addNewCoordinator: (alias: string, url: string) => void; @@ -40,10 +36,6 @@ export interface UseFederationStoreType { export const initialFederationContext: UseFederationStoreType = { federation: new Federation('onion', new Settings(), ''), sortedCoordinators: [], - setDelay: () => {}, - currentOrderId: { id: null, shortAlias: null }, - setCurrentOrderId: () => {}, - currentOrder: null, coordinatorUpdatedAt: '', federationUpdatedAt: '', addNewCoordinator: () => {}, @@ -56,24 +48,13 @@ export const FederationContextProvider = ({ }: FederationContextProviderProps): JSX.Element => { const { settings, page, origin, hostUrl, open, torStatus } = useContext(AppContext); - const { setMaker, garage, setBadOrder } = useContext(GarageContext); + const { setMaker, garage } = useContext(GarageContext); const [federation] = useState(new Federation(origin, settings, hostUrl)); const [sortedCoordinators, setSortedCoordinators] = useState(federationLottery(federation)); const [coordinatorUpdatedAt, setCoordinatorUpdatedAt] = useState( new Date().toISOString(), ); const [federationUpdatedAt, setFederationUpdatedAt] = useState(new Date().toISOString()); - const [currentOrderId, setCurrentOrderId] = useState( - initialFederationContext.currentOrderId, - ); - const [currentOrder, setCurrentOrder] = useState( - initialFederationContext.currentOrder, - ); - - const [delay, setDelay] = useState(defaultDelay); - const [timer, setTimer] = useState(() => - setInterval(() => null, delay), - ); useEffect(() => { setMaker((maker) => { @@ -82,65 +63,15 @@ export const FederationContextProvider = ({ federation.registerHook('onFederationUpdate', () => { setFederationUpdatedAt(new Date().toISOString()); }); - federation.registerHook('onCoordinatorUpdate', () => { - setCoordinatorUpdatedAt(new Date().toISOString()); - }); }, []); useEffect(() => { if (window.NativeRobosats === undefined || torStatus === 'ON' || !settings.useProxy) { void federation.updateUrl(origin, settings, hostUrl); void federation.update(); - - const token = garage.getSlot()?.getRobot()?.token; - if (token) void federation.fetchRobot(garage, token); } }, [settings.network, settings.useProxy, torStatus]); - const onOrderReceived = (order: Order): void => { - let newDelay = defaultDelay; - if (order?.bad_request) { - newDelay = 99999999; - setBadOrder(order.bad_request); - garage.updateOrder(null); - setCurrentOrder(null); - } - if (order?.id) { - newDelay = - order.status >= 0 && order.status <= 18 - ? page === 'order' - ? statusToDelay[order.status] - : statusToDelay[order.status] * 5 // If user is not looking at "order" tab, refresh less often. - : 99999999; - garage.updateOrder(order); - setCurrentOrder(order); - setBadOrder(undefined); - } - clearInterval(timer); - console.log('New Delay:', newDelay); - setDelay(newDelay); - setTimer(setTimeout(fetchCurrentOrder, newDelay)); - }; - - const fetchCurrentOrder: () => void = () => { - const slot = garage?.getSlot(); - const robot = slot?.getRobot(); - if (robot && slot?.token && currentOrderId.id && currentOrderId.shortAlias) { - const coordinator = federation.getCoordinator(currentOrderId.shortAlias); - void coordinator?.fetchOrder(currentOrderId.id, robot, slot?.token).then((order) => { - onOrderReceived(order as Order); - }); - } else if (slot?.token && slot?.activeShortAlias && robot?.activeOrderId) { - const coordinator = federation.getCoordinator(slot.activeShortAlias); - void coordinator?.fetchOrder(robot.activeOrderId, robot, slot.token).then((order) => { - onOrderReceived(order as Order); - }); - } else { - clearInterval(timer); - setTimer(setTimeout(fetchCurrentOrder, defaultDelay)); - } - }; - const addNewCoordinator: (alias: string, url: string) => void = (alias, url) => { if (!federation.coordinators[alias]) { const attributes: Record = { @@ -164,24 +95,12 @@ export const FederationContextProvider = ({ newCoordinator.update(() => { setCoordinatorUpdatedAt(new Date().toISOString()); }); - garage.syncCoordinator(newCoordinator); + garage.syncCoordinator(federation, alias); setSortedCoordinators(federationLottery(federation)); setFederationUpdatedAt(new Date().toISOString()); } }; - useEffect(() => { - if (currentOrderId.id && currentOrderId.shortAlias) { - setCurrentOrder(null); - setBadOrder(undefined); - clearInterval(timer); - fetchCurrentOrder(); - } - return () => { - clearInterval(timer); - }; - }, [currentOrderId]); - useEffect(() => { if (page === 'offers') void federation.updateBook(); }, [page]); @@ -191,7 +110,7 @@ export const FederationContextProvider = ({ const slot = garage.getSlot(); if (open.profile && slot?.hashId && slot?.token) { - void federation.fetchRobot(garage, slot?.token); // refresh/update existing robot + void garage.fetchRobot(federation, slot?.token); // refresh/update existing robot } }, [open.profile]); @@ -200,10 +119,6 @@ export const FederationContextProvider = ({ value={{ federation, sortedCoordinators, - currentOrderId, - setCurrentOrderId, - currentOrder, - setDelay, coordinatorUpdatedAt, federationUpdatedAt, addNewCoordinator, diff --git a/frontend/src/models/Garage.model.ts b/frontend/src/models/Garage.model.ts index 6c9b5c0f..509a5a51 100644 --- a/frontend/src/models/Garage.model.ts +++ b/frontend/src/models/Garage.model.ts @@ -79,7 +79,7 @@ class Garage { // Slots getSlot: (token?: string) => Slot | null = (token) => { const currentToken = token ?? this.currentSlot; - return currentToken ? this.slots[currentToken] ?? null : null; + return currentToken ? (this.slots[currentToken] ?? null) : null; }; deleteSlot: (token?: string) => void = (token) => { @@ -100,7 +100,7 @@ class Garage { if (attributes) { if (attributes.copiedToken !== undefined) slot?.setCopiedToken(attributes.copiedToken); this.save(); - this.triggerHook('onRobotUpdate'); + this.triggerHook('onSlotUpdate'); } return slot; }; @@ -108,7 +108,7 @@ class Garage { setCurrentSlot: (currentSlot: string) => void = (currentSlot) => { this.currentSlot = currentSlot; this.save(); - this.triggerHook('onRobotUpdate'); + this.triggerHook('onSlotUpdate'); }; getSlotByOrder: (coordinator: string, orderID: number) => Slot | null = ( @@ -118,7 +118,7 @@ class Garage { return ( Object.values(this.slots).find((slot) => { const robot = slot.getRobot(coordinator); - return slot.activeShortAlias === coordinator && robot?.activeOrderId === orderID; + return slot.activeOrder?.shortAlias === coordinator && robot?.activeOrderId === orderID; }) ?? null ); }; diff --git a/frontend/src/models/Maker.model.ts b/frontend/src/models/Maker.model.ts index 569417cd..f5517e33 100644 --- a/frontend/src/models/Maker.model.ts +++ b/frontend/src/models/Maker.model.ts @@ -1,3 +1,5 @@ +import defaultFederation from '../../static/federation.json'; + export interface Maker { advancedOptions: boolean; coordinator: string; @@ -23,7 +25,10 @@ export interface Maker { export const defaultMaker: Maker = { advancedOptions: false, - coordinator: 'exp', + coordinator: + Object.keys(defaultFederation)[ + Math.floor(Math.random() * Object.keys(defaultFederation).length) + ] ?? '', isExplicit: false, amount: '', paymentMethods: [], @@ -40,6 +45,8 @@ export const defaultMaker: Maker = { maxAmount: '', badPremiumText: '', badSatoshisText: '', + latitude: 0, + longitude: 0, }; export default Maker; diff --git a/frontend/src/models/Order.model.ts b/frontend/src/models/Order.model.ts index ada55f0f..5eae7383 100644 --- a/frontend/src/models/Order.model.ts +++ b/frontend/src/models/Order.model.ts @@ -214,6 +214,17 @@ class Order { return this; }; + take: (federation: Federation, slot: Slot, takeAmount: string) => Promise = async ( + federation, + slot, + takeAmount, + ) => { + return this.submitAction(federation, slot, { + action: 'take', + amount: this?.currency === 1000 ? Number(takeAmount) / 100000000 : Number(takeAmount), + }); + }; + submitAction: (federation: Federation, slot: Slot, action: SubmitActionProps) => Promise = async (federation, slot, action) => { if (this.id < 1) return this; diff --git a/frontend/src/models/Slot.model.ts b/frontend/src/models/Slot.model.ts index b38ea8c7..1c8990af 100644 --- a/frontend/src/models/Slot.model.ts +++ b/frontend/src/models/Slot.model.ts @@ -111,6 +111,12 @@ class Slot { this.updateSlotFromOrder(this.activeOrder); }; + takeOrder = async (federation: Federation, order: Order, takeAmount: string): Promise => { + await order.take(federation, this, takeAmount); + this.updateSlotFromOrder(order); + return order; + }; + makeOrder = async (federation: Federation, attributes: object): Promise => { const order = new Order(attributes); await order.make(federation, this); @@ -124,6 +130,7 @@ class Slot { if (newOrder) { // FIXME: API responses with bad_request should include also order's status if (newOrder?.bad_request?.includes('expired')) newOrder.status = 5; + if (newOrder?.bad_request?.includes('collaborativelly')) newOrder.status = 12; if ( newOrder.id === this.activeOrder?.id && newOrder.shortAlias === this.activeOrder?.shortAlias diff --git a/frontend/static/federation.json b/frontend/static/federation.json index ece01fe6..9bfd3527 100644 --- a/frontend/static/federation.json +++ b/frontend/static/federation.json @@ -1,47 +1,4 @@ { - "exp": { - "longAlias": "Experimental", - "shortAlias": "exp", - "description": "RoboSats node for development and experimentation. This is the original RoboSats coordinator operated by the RoboSats devs since 2022.", - "motto": "Original Robohost. P2P FTW!", - "color": "#1976d2", - "established": "2022-03-01", - "contact": { - "email": "robosats@protonmail.com", - "telegram": "robosats", - "twitter": "robosats", - "reddit": "r/robosats", - "matrix": "#robosats:matrix.org", - "website": "https://learn.robosats.com", - "nostr": "npub1p2psats79rypr8lpnl9t5qdekfp700x660qsgw284xvq4s09lqrqqk3m82", - "pgp": "/static/federation/pgp/B4AB5F19113D4125DDF217739C4585B561315571.asc", - "fingerprint": "B4AB5F19113D4125DDF217739C4585B561315571" - }, - "badges": { - "isFounder": true, - "donatesToDevFund": 20, - "hasGoodOpSec": true, - "robotsLove": true, - "hasLargeLimits": true - }, - "policies": { - "Experimental": "Experimental coordinator used for development. Use at your own risk.", - "Dispute Policy": "Evidence in Disputes: In the event of a dispute, users will be asked to provide transaction-related evidence. This could include transaction IDs, screenshots of payment confirmations, or other pertinent transaction records. Personal information or unrelated transaction details should be redacted to maintain privacy.", - "Non eligible countries": "USA citizens and residents are not allowed to use the Experimental coordinator. F2F transactions are explicitly blocked at creation time for US locations. If a US citizen or resident violates this rule and is found out to be using the Experimental coordinator during a dispute process, they will be denied further service and the dispute mediation will be terminated." - }, - "mainnet": { - "onion": "http://robosats6tkf3eva7x2voqso3a5wcorsnw34jveyxfqi2fu7oyheasid.onion", - "clearnet": "https://unsafe.robosats.com", - "i2p": "http://r7r4sckft6ptmk4r2jajiuqbowqyxiwsle4iyg4fijtoordc6z7a.b32.i2p" - }, - "testnet": { - "onion": "http://robotestagw3dcxmd66r4rgksb4nmmr43fh77bzn2ia2eucduyeafnyd.onion", - "clearnet": "https://unsafe.testnet.robosats.com", - "i2p": "" - }, - "mainnetNodesPubkeys": ["0282eb467bc073833a039940392592bf10cf338a830ba4e392c1667d7697654c7e"], - "testnetNodesPubkeys": ["03ecb271b3e2e36f2b91c92c65bab665e5165f8cdfdada1b5f46cfdd3248c87fd6"] - }, "temple": { "longAlias": "Temple of Sats", "shortAlias": "temple",