@hot-labs/kit 1.1.0-beta.5 → 1.1.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/build/core/Intents.d.ts +20 -9
  2. package/build/core/Intents.js +110 -56
  3. package/build/core/Intents.js.map +1 -1
  4. package/build/core/utils.js +11 -1
  5. package/build/core/utils.js.map +1 -1
  6. package/build/exchange.js +1 -1
  7. package/build/exchange.js.map +1 -1
  8. package/build/ui/Popup.d.ts +1 -1
  9. package/build/ui/Popup.js +4 -0
  10. package/build/ui/Popup.js.map +1 -1
  11. package/build/ui/Toast.d.ts +4 -0
  12. package/build/ui/Toast.js +33 -0
  13. package/build/ui/Toast.js.map +1 -0
  14. package/build/ui/connect/ConnectWallet.d.ts +6 -2
  15. package/build/ui/connect/ConnectWallet.js +5 -5
  16. package/build/ui/connect/ConnectWallet.js.map +1 -1
  17. package/build/ui/connect/PrimaryWallet.d.ts +6 -0
  18. package/build/ui/connect/PrimaryWallet.js +18 -0
  19. package/build/ui/connect/PrimaryWallet.js.map +1 -0
  20. package/build/ui/payment/Bridge.js +1 -1
  21. package/build/ui/payment/Bridge.js.map +1 -1
  22. package/build/ui/payment/Payment.d.ts +9 -2
  23. package/build/ui/payment/Payment.js +42 -42
  24. package/build/ui/payment/Payment.js.map +1 -1
  25. package/build/ui/payment/TokenCard.d.ts +4 -2
  26. package/build/ui/payment/TokenCard.js +4 -4
  27. package/build/ui/payment/TokenCard.js.map +1 -1
  28. package/build/ui/router.d.ts +15 -5
  29. package/build/ui/router.js +15 -3
  30. package/build/ui/router.js.map +1 -1
  31. package/build/ui/styles.js +2 -0
  32. package/build/ui/styles.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/core/Intents.ts +113 -58
  35. package/src/core/utils.ts +6 -1
  36. package/src/exchange.ts +1 -1
  37. package/src/ui/Popup.tsx +5 -0
  38. package/src/ui/Toast.tsx +45 -0
  39. package/src/ui/connect/ConnectWallet.tsx +11 -6
  40. package/src/ui/connect/PrimaryWallet.tsx +65 -0
  41. package/src/ui/payment/Bridge.tsx +1 -1
  42. package/src/ui/payment/Payment.tsx +79 -90
  43. package/src/ui/payment/TokenCard.tsx +21 -17
  44. package/src/ui/router.tsx +43 -4
  45. package/src/ui/styles.ts +2 -0
@@ -0,0 +1,65 @@
1
+ import { useState } from "react";
2
+ import Popup from "../Popup";
3
+ import { PopupButton } from "../styles";
4
+ import { Connector } from "./ConnectWallet";
5
+ import { HotConnector } from "../../HotConnector";
6
+ import { chains, WalletType } from "../../core";
7
+
8
+ const ConnectPrimaryWallet = ({ hot, onClose }: { hot: HotConnector; onClose: () => void }) => {
9
+ const [openConnect, setOpenConnect] = useState(false);
10
+
11
+ return (
12
+ <Popup onClose={onClose} header={<p>Connect main wallet</p>}>
13
+ {openConnect ? (
14
+ <Connector widget={true} walletType={WalletType.OMNI} hot={hot} onClose={onClose} />
15
+ ) : (
16
+ <div style={{ display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", width: "100%", height: 300 }}>
17
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 24, marginTop: 48 }}>
18
+ <WalletIcon />
19
+ <BindIcon />
20
+ <img src={chains.get(-4).logo} style={{ marginLeft: -4, width: 64, height: 64, borderRadius: "50%" }} />
21
+ </div>
22
+
23
+ <p style={{ marginTop: 24, fontSize: 20, fontWeight: "bold" }}>Connect your primary wallet</p>
24
+ <p style={{ marginTop: 8, fontSize: 16, color: "#c6c6c6" }}>Your HEX Balance will be tied to this wallet.</p>
25
+
26
+ <PopupButton style={{ marginTop: "auto" }} onClick={() => setOpenConnect(true)}>
27
+ Connect wallet
28
+ </PopupButton>
29
+ </div>
30
+ )}
31
+ </Popup>
32
+ );
33
+ };
34
+
35
+ const WalletIcon = () => {
36
+ return (
37
+ <svg width="56" height="56" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
38
+ <path
39
+ d="M45.333 6C48.6467 6 51.333 8.68629 51.333 12V15.333H52C55.3136 15.333 57.9998 18.0195 58 21.333V30.1826C60.191 31.2759 62 33.3016 62 35.5244V39.333C62 41.9566 60.3157 44.1858 57.9697 45.001C57.9878 45.1091 58 45.2197 58 45.333V53.333C58 56.6467 55.3137 59.333 52 59.333H12C8.68629 59.333 6 56.6467 6 53.333V12C6 8.68629 8.68629 6 12 6H45.333ZM10 53.333C10 54.4376 10.8954 55.333 12 55.333H52C53.1046 55.333 54 54.4376 54 53.333V45.333C54 45.2208 54.0115 45.1111 54.0293 45.0039L48 44C43.9499 44 41.333 42.0501 41.333 38C41.333 33.9499 43.9499 30.667 48 30.667L54 29.4658V21.333C53.9998 20.2286 53.1045 19.333 52 19.333H10V53.333ZM48 33.333C46.159 33.333 44 36.1591 44 38C44 39.8409 46.159 41.333 48 41.333H56C57.1046 41.333 58 40.4376 58 39.333V36.667C58 35.9442 57.0444 34.7412 56.0752 33.9951C56.0502 33.996 56.0252 34 56 34C55.4082 34 54.8779 33.7416 54.5117 33.333H48ZM12 10C10.8954 10 10 10.8954 10 12V15.333H47.333V12C47.333 10.8954 46.4376 10 45.333 10H12Z"
40
+ fill="#EBDEDC"
41
+ />
42
+ <path
43
+ d="M42.9756 6.00781C46.1457 6.16859 48.667 8.7899 48.667 12V15.333H49.333C52.6466 15.333 55.3328 18.0195 55.333 21.333V30.0371C58.3331 30.3686 60.667 32.9115 60.667 36V38.667C60.6668 41.7553 58.3329 44.2973 55.333 44.6289V50.667C55.3328 53.9806 52.6466 56.667 49.333 56.667H12C8.6864 56.667 6.00018 53.9805 6 50.667V12C6 8.68629 8.6863 6 12 6H42.667L42.9756 6.00781ZM10 50.667C10.0002 51.7714 10.8955 52.667 12 52.667H49.333C50.4375 52.667 51.3328 51.7714 51.333 50.667V44.667H48C43.9501 44.6668 40.667 41.383 40.667 37.333C40.6672 33.2832 43.9502 30.0002 48 30H51.333V21.333C51.3328 20.2286 50.4375 19.333 49.333 19.333H10V50.667ZM48 34C46.1593 34.0002 44.6672 35.4923 44.667 37.333C44.667 39.1738 46.1592 40.6668 48 40.667H54.667C55.7713 40.6668 56.6668 39.7713 56.667 38.667V36C56.667 34.8955 55.7714 34.0002 54.667 34H48ZM12 10C10.8954 10 10 10.8954 10 12V15.333H44.667V12C44.667 10.9645 43.8796 10.1132 42.8711 10.0107L42.667 10H12Z"
44
+ fill="#EBDEDC"
45
+ />
46
+ </svg>
47
+ );
48
+ };
49
+
50
+ const BindIcon = () => {
51
+ return (
52
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
53
+ <path
54
+ d="M10.5552 12.5981C12.2474 10.9059 14.9908 10.9059 16.6831 12.5981L17.6597 13.5737C18.0501 13.9641 18.0499 14.5972 17.6597 14.9878C17.2691 15.3778 16.636 15.3781 16.2456 14.9878L15.269 14.0122C14.3578 13.101 12.8805 13.101 11.9692 14.0122L5.3501 20.6313C4.43935 21.5426 4.43903 23.0201 5.3501 23.9312L7.30225 25.8833C8.21331 26.7944 9.69079 26.794 10.6021 25.8833L13.5786 22.9077C13.9691 22.5172 14.6022 22.5172 14.9927 22.9077C15.383 23.2983 15.3831 23.9313 14.9927 24.3218L12.0161 27.2974C10.3238 28.9892 7.5803 28.9895 5.88818 27.2974L3.93604 25.3452C2.24392 23.6531 2.24424 20.9096 3.93604 19.2173L10.5552 12.5981ZM19.2153 3.97217C20.9038 2.25827 23.6655 2.24848 25.3667 3.94971L27.3013 5.88428C28.9935 7.57655 28.9935 10.3199 27.3013 12.0122L20.6821 18.6313C18.9898 20.3231 16.2463 20.3235 14.5542 18.6313L13.5776 17.6558C13.1874 17.2655 13.1879 16.6323 13.5776 16.2417C13.9682 15.8512 14.6012 15.8512 14.9917 16.2417L15.9683 17.2173C16.8793 18.1283 18.3568 18.128 19.2681 17.2173L25.8872 10.5981C26.7984 9.68692 26.7984 8.20956 25.8872 7.29834L23.9526 5.36377C23.0366 4.44774 21.5493 4.45268 20.6401 5.37549L16.7114 9.36377C16.3238 9.75672 15.6906 9.76194 15.2974 9.37451C14.9042 8.98707 14.8995 8.35389 15.2866 7.96045L19.2153 3.97217Z"
55
+ fill="#EBDEDC"
56
+ />
57
+ <path
58
+ d="M10.5571 12.6016C12.2494 10.9094 14.9928 10.9093 16.6851 12.6016L17.6616 13.5771C18.0517 13.9675 18.0516 14.6008 17.6616 14.9912C17.2712 15.3816 16.6381 15.3814 16.2476 14.9912L15.271 14.0156C14.3598 13.1044 12.8824 13.1045 11.9712 14.0156L5.35205 20.6348C4.44083 21.546 4.44083 23.0233 5.35205 23.9346L8.03174 26.6143C8.9261 27.5083 10.3702 27.5276 11.2876 26.6572L13.6665 24.3984C13.637 24.375 13.6079 24.3505 13.5806 24.3232C13.1901 23.9327 13.1901 23.2997 13.5806 22.9092C14.1232 22.3666 14.8184 22.1462 15.4761 22.332C16.0272 22.488 16.6206 22.997 16.6206 23.7529C16.6206 23.7809 16.618 23.8085 16.6157 23.8359C16.6413 24.1291 16.539 24.4311 16.3091 24.6494L12.6646 28.1084C10.9608 29.7249 8.27866 29.6897 6.61768 28.0293L3.93799 25.3486C2.24572 23.6564 2.24572 20.913 3.93799 19.2207L10.5571 12.6016ZM19.2183 3.97266C20.9066 2.25916 23.6684 2.24938 25.3696 3.9502L28.1753 6.75586C29.8672 8.44789 29.868 11.1914 28.1763 12.8838L21.5581 19.5039C19.8663 21.1962 17.1218 21.1964 15.4292 19.5049L13.9614 18.0371C13.5709 17.6467 13.571 17.0136 13.9614 16.623C14.3519 16.2329 14.985 16.2327 15.3755 16.623L16.8433 18.0908C17.7547 19.0015 19.2321 19.001 20.1431 18.0898L26.7622 11.4697C27.6729 10.5585 27.6721 9.08097 26.7612 8.16992L23.9556 5.36426C23.0395 4.44844 21.5522 4.45431 20.6431 5.37695L16.7134 9.36523C16.3259 9.75836 15.6927 9.76319 15.2993 9.37598C14.906 8.98837 14.902 8.35531 15.2896 7.96191L19.2183 3.97266Z"
59
+ fill="#EBDEDC"
60
+ />
61
+ </svg>
62
+ );
63
+ };
64
+
65
+ export default ConnectPrimaryWallet;
@@ -54,7 +54,7 @@ const FIXED = 6;
54
54
  export const Bridge = observer(({ hot, widget, setup, onClose, onProcess, onSelectPair }: BridgeProps) => {
55
55
  const [isFiat, setIsFiat] = useState(false);
56
56
  const [type, setType] = useState<"exactIn" | "exactOut">(setup?.type || "exactIn");
57
- const [value, setValue] = useState<string>(setup?.amount?.toString() ?? "");
57
+ const [value, setValue] = useState<string>(setup?.amount?.toFixed(6) ?? "");
58
58
  const [from, setFrom] = useState<Token>(setup?.from || tokens.list.find((t) => t.id === localStorage.getItem("bridge:from")) || tokens.list.find((t) => t.symbol === "NEAR")!);
59
59
  const [to, setTo] = useState<Token>(setup?.to || tokens.list.find((t) => t.id === localStorage.getItem("bridge:to")) || tokens.list.find((t) => t.symbol === "USDT")!);
60
60
 
@@ -3,9 +3,8 @@ import { useEffect, useState } from "react";
3
3
 
4
4
  import { WalletIcon } from "../icons/wallet";
5
5
  import { PopupButton, PopupOption, PopupOptionInfo } from "../styles";
6
- import { Commitment, formatter, Intents } from "../../core";
6
+ import { Commitment, Intents } from "../../core";
7
7
  import { Recipient } from "../../core/recipient";
8
- import { Network } from "../../core/chains";
9
8
  import { Token } from "../../core/token";
10
9
 
11
10
  import { BridgeReview } from "../../exchange";
@@ -15,15 +14,21 @@ import { OmniWallet } from "../../OmniWallet";
15
14
  import { HotConnector } from "../../HotConnector";
16
15
  import Popup from "../Popup";
17
16
 
18
- import { TokenCard, TokenIcon } from "./TokenCard";
17
+ import { TokenCard } from "./TokenCard";
19
18
  import { HorizontalStepper } from "./Stepper";
20
19
  import { Loader } from "./Profile";
21
20
 
22
21
  interface PaymentProps {
23
22
  intents: Intents;
23
+ title?: string;
24
+ allowedTokens?: string[];
25
+ prepaidAmount: bigint;
26
+ payableToken: Token;
27
+ needAmount: bigint;
24
28
  connector: HotConnector;
25
29
  onReject: (message: string) => void;
26
- onConfirm: (args: { depositQoute: BridgeReview | "direct"; processing?: () => Promise<BridgeReview> }) => void;
30
+ onConfirm: (args: { depositQoute: BridgeReview | "direct"; processing?: () => Promise<BridgeReview> }) => Promise<void>;
31
+ close: () => void;
27
32
  }
28
33
 
29
34
  const animations = {
@@ -34,7 +39,18 @@ const animations = {
34
39
 
35
40
  const PAY_SLIPPAGE = 0.002;
36
41
 
37
- export const Payment = observer(({ connector, intents, onReject, onConfirm }: PaymentProps) => {
42
+ const serializeError = (error: any) => {
43
+ try {
44
+ if (error instanceof Error) return error.message;
45
+ if (typeof error === "object" && Object.keys(error).length > 0) return JSON.stringify(error);
46
+ if (typeof error === "string" || typeof error === "number") return error.toString();
47
+ return "";
48
+ } catch (error) {
49
+ return "Unknown error";
50
+ }
51
+ };
52
+
53
+ export const Payment = observer(({ connector, intents, title = "Payment", allowedTokens, prepaidAmount, payableToken, needAmount, onReject, onConfirm, close }: PaymentProps) => {
38
54
  useState(() => {
39
55
  fetch(animations.loading);
40
56
  fetch(animations.success);
@@ -55,22 +71,13 @@ export const Payment = observer(({ connector, intents, onReject, onConfirm }: Pa
55
71
  step?: "selectToken" | "sign" | "transfer" | "success" | "error" | "loading";
56
72
  loading?: boolean;
57
73
  error?: any;
58
- } | null>(null);
74
+ } | null>(needAmount === 0n ? { step: "sign", review: "direct" } : null);
59
75
 
60
- const need = connector.omni(intents.need.keys().next().value!);
61
- const needAmount = intents.need.values().next().value || 0n;
62
- const title = `Payment ${need.readable(needAmount)} ${need.symbol}`;
76
+ const paymentTitle = title || `Pay ${payableToken.readable(needAmount)} ${payableToken.symbol}`;
63
77
 
64
78
  const selectToken = async (from: Token, wallet?: OmniWallet) => {
65
79
  if (!wallet) return;
66
80
 
67
- // Set signer as payer wallet if not set another
68
- if (!intents.signer) intents.attachWallet(wallet);
69
-
70
- if (from.id === need.id) {
71
- return setFlow({ token: from, wallet, review: "direct", step: "sign" });
72
- }
73
-
74
81
  try {
75
82
  setFlow({ token: from, wallet, review: undefined, step: "sign" });
76
83
  const review = await connector.exchange.reviewSwap({
@@ -80,7 +87,7 @@ export const Payment = observer(({ connector, intents, onReject, onConfirm }: Pa
80
87
  sender: wallet,
81
88
  refund: wallet,
82
89
  type: "exactOut",
83
- to: need,
90
+ to: payableToken,
84
91
  from,
85
92
  });
86
93
 
@@ -112,11 +119,9 @@ export const Payment = observer(({ connector, intents, onReject, onConfirm }: Pa
112
119
  }
113
120
 
114
121
  const result = await connector.exchange.makeSwap(flow.review, { log: () => {} });
115
- setFlow({
116
- loading: false,
117
- step: "success",
118
- success: { depositQoute: result.review, processing: result.processing },
119
- });
122
+ await onConfirm({ depositQoute: result.review, processing: result.processing });
123
+ setFlow({ loading: false, step: "success" });
124
+ setTimeout(() => close(), 2000);
120
125
  } catch (error) {
121
126
  console.error(error);
122
127
  setFlow((t) => (t ? { ...t, step: "error", loading: false, error } : null));
@@ -126,13 +131,13 @@ export const Payment = observer(({ connector, intents, onReject, onConfirm }: Pa
126
131
 
127
132
  if (flow?.step === "success") {
128
133
  return (
129
- <Popup header={<p>{title}</p>}>
134
+ <Popup header={<p>{paymentTitle}</p>}>
130
135
  <div style={{ width: "100%", height: 400, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
131
136
  {/* @ts-expect-error: dotlottie-wc is not typed */}
132
137
  <dotlottie-wc key="success" src={animations.success} speed="1" style={{ width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
133
- <p style={{ fontSize: 24, marginTop: -32, fontWeight: "bold" }}>Payment successful</p>
138
+ <p style={{ fontSize: 24, marginTop: -32, fontWeight: "bold" }}>Transaction successful</p>
134
139
  </div>
135
- <PopupButton style={{ marginTop: "auto" }} onClick={() => onConfirm(flow.success!)}>
140
+ <PopupButton style={{ marginTop: "auto" }} onClick={() => close()}>
136
141
  Continue
137
142
  </PopupButton>
138
143
  </Popup>
@@ -141,11 +146,11 @@ export const Payment = observer(({ connector, intents, onReject, onConfirm }: Pa
141
146
 
142
147
  if (flow?.step === "loading") {
143
148
  return (
144
- <Popup header={<p>{title}</p>}>
149
+ <Popup header={<p>{paymentTitle}</p>}>
145
150
  <div style={{ width: "100%", height: 400, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
146
151
  {/* @ts-expect-error: dotlottie-wc is not typed */}
147
152
  <dotlottie-wc key="loading" src={animations.loading} speed="1" style={{ marginTop: -64, width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
148
- <p style={{ fontSize: 24, marginTop: -16, fontWeight: "bold" }}>Processing payment</p>
153
+ <p style={{ fontSize: 24, marginTop: -16, fontWeight: "bold" }}>Transaction processing</p>
149
154
  </div>
150
155
  </Popup>
151
156
  );
@@ -153,85 +158,59 @@ export const Payment = observer(({ connector, intents, onReject, onConfirm }: Pa
153
158
 
154
159
  if (flow?.step === "error") {
155
160
  return (
156
- <Popup header={<p>{title}</p>}>
161
+ <Popup header={<p>{paymentTitle}</p>}>
157
162
  <div style={{ width: "100%", height: 400, gap: 8, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
158
163
  {/* @ts-expect-error: dotlottie-wc is not typed */}
159
164
  <dotlottie-wc key="error" src={animations.failed} speed="1" style={{ width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
160
- <p style={{ fontSize: 24, marginTop: -32, fontWeight: "bold" }}>Payment failed</p>
161
- <p style={{ fontSize: 14, width: "80%", textAlign: "center", overflowY: "auto", lineBreak: "anywhere" }}>{flow.error?.toString?.() ?? "Unknown error"}</p>
165
+ <p style={{ fontSize: 24, marginTop: -32, fontWeight: "bold" }}>Transaction failed</p>
166
+ <p style={{ fontSize: 14, width: "80%", textAlign: "center", overflowY: "auto", lineBreak: "anywhere" }}>{serializeError(flow.error)}</p>
162
167
  </div>
163
- <PopupButton onClick={() => onReject(flow.error?.toString?.() ?? "Unknown error")}>Close</PopupButton>
168
+ <PopupButton onClick={() => onReject(serializeError(flow.error))}>Close</PopupButton>
164
169
  </Popup>
165
170
  );
166
171
  }
167
172
 
168
173
  if (flow?.step === "transfer") {
169
- if (!flow.token) return null;
170
- if (!flow.wallet) return null;
171
174
  return (
172
- <Popup onClose={() => onReject("closed")} header={<p>{title}</p>}>
175
+ <Popup onClose={() => onReject("closed")} header={<p>{paymentTitle}</p>}>
173
176
  <HorizontalStepper steps={[{ label: "Select" }, { label: "Review" }, { label: "Confirm" }]} currentStep={2} />
174
177
 
175
- <PopupOption style={{ marginTop: 8 }}>
176
- <TokenIcon token={flow.token} wallet={flow.wallet} />
177
-
178
- <div style={{ marginTop: -2, textAlign: "left" }}>
179
- <p style={{ textAlign: "left", fontSize: 20, fontWeight: "bold" }}>{flow.token.symbol}</p>
180
- <p style={{ textAlign: "left", fontSize: 14, color: "#c6c6c6" }}>${formatter.amount(flow.token.usd)}</p>
181
- </div>
182
-
183
- {flow.review ? (
184
- <div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
185
- <p style={{ textAlign: "right", fontSize: 20 }}>{flow.token.readable(flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0)}</p>
186
- <p style={{ textAlign: "right", fontSize: 14, color: "#c6c6c6" }}>${flow.token.readable(flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0n, flow.token.usd)}</p>
187
- </div>
188
- ) : (
189
- <div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
190
- <Loader />
191
- </div>
192
- )}
193
- </PopupOption>
178
+ {prepaidAmount > 0n && <TokenCard token={payableToken} hot={connector} wallet={intents.signer} amount={prepaidAmount} />}
179
+
180
+ {flow.token != null && (
181
+ <TokenCard //
182
+ hot={connector}
183
+ token={flow.token}
184
+ wallet={flow.wallet}
185
+ amount={flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0n}
186
+ />
187
+ )}
194
188
 
195
189
  <PopupButton style={{ marginTop: 24 }} disabled={!flow?.review} onClick={confirmPaymentStep}>
196
- {flow?.loading ? "Confirming..." : "Confirm payment"}
190
+ {flow?.loading ? "Confirming..." : "Confirm transaction"}
197
191
  </PopupButton>
198
192
  </Popup>
199
193
  );
200
194
  }
201
195
 
202
196
  if (flow?.step === "sign") {
203
- if (!flow.token) return null;
204
- if (!flow.wallet) return null;
197
+ const rightControl = <div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>{flow.error ? <ErrorIcon /> : <Loader />}</div>;
198
+
205
199
  return (
206
200
  <Popup onClose={() => onReject("closed")} header={<p>{title}</p>}>
207
201
  <HorizontalStepper steps={[{ label: "Select" }, { label: "Review" }, { label: "Confirm" }]} currentStep={1} />
208
202
 
209
- <PopupOption style={{ marginTop: 8 }}>
210
- <TokenIcon token={flow.token} wallet={flow.wallet} />
211
-
212
- <div style={{ marginTop: -2, textAlign: "left" }}>
213
- <p style={{ textAlign: "left", fontSize: 20, fontWeight: "bold" }}>{flow.token.symbol}</p>
214
- <p style={{ textAlign: "left", fontSize: 14, color: "#c6c6c6" }}>${formatter.amount(flow.token.usd)}</p>
215
- </div>
216
-
217
- {flow.review ? (
218
- <div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
219
- <p style={{ textAlign: "right", fontSize: 20 }}>{flow.token.readable(flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0)}</p>
220
- <p style={{ textAlign: "right", fontSize: 14, color: "#c6c6c6" }}>${flow.token.readable(flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0n, flow.token.usd)}</p>
221
- </div>
222
- ) : (
223
- <div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
224
- {flow.error ? (
225
- <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Failed" style={{ display: "block", margin: "0 auto" }}>
226
- <circle cx="14" cy="14" r="13" stroke="#E74C3C" strokeWidth="2" />
227
- <path d="M9 9l10 10M19 9l-10 10" stroke="#E74C3C" strokeWidth="2.5" strokeLinecap="round" />
228
- </svg>
229
- ) : (
230
- <Loader />
231
- )}
232
- </div>
233
- )}
234
- </PopupOption>
203
+ {prepaidAmount > 0n && <TokenCard token={payableToken} hot={connector} wallet={intents.signer} amount={prepaidAmount} />}
204
+
205
+ {flow.token != null && (
206
+ <TokenCard //
207
+ hot={connector}
208
+ token={flow.token}
209
+ wallet={flow.wallet}
210
+ rightControl={flow.review ? undefined : rightControl}
211
+ amount={flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0n}
212
+ />
213
+ )}
235
214
 
236
215
  {flow.error ? (
237
216
  <PopupButton style={{ marginTop: 24 }} onClick={() => setFlow(null)}>
@@ -251,28 +230,38 @@ export const Payment = observer(({ connector, intents, onReject, onConfirm }: Pa
251
230
  <HorizontalStepper steps={[{ label: "Select" }, { label: "Review" }, { label: "Confirm" }]} currentStep={0} />
252
231
 
253
232
  {connector.walletsTokens.map(({ token, wallet, balance }) => {
233
+ if (token.id === payableToken.id) return null;
254
234
  const availableBalance = token.float(balance) - token.reserve;
255
235
 
256
- if (need.originalChain === Network.Gonka || need.originalChain === Network.Juno) {
257
- if (token.id === need.id) return null;
258
- if (token.originalAddress !== need.originalAddress) return null;
259
- if (availableBalance < need.float(needAmount)) return null;
236
+ // Allow only tokens in the allowedTokens list
237
+ if (allowedTokens != null && !allowedTokens?.includes(token.omniAddress)) return null;
238
+
239
+ // same token as need and enough balance is direct deposit
240
+ if (token.originalChain === payableToken.originalChain && token.originalAddress === payableToken.originalAddress && availableBalance >= payableToken.float(needAmount)) {
260
241
  return <TokenCard key={token.id} token={token} onSelect={selectToken} hot={connector} wallet={wallet} />;
261
242
  }
262
243
 
263
- if (availableBalance * token.usd <= need.usd * need.float(needAmount) * (1 + PAY_SLIPPAGE)) return null;
244
+ if (availableBalance * token.usd <= payableToken.usd * payableToken.float(needAmount) * (1 + PAY_SLIPPAGE)) return null;
264
245
  return <TokenCard key={token.id} token={token} onSelect={selectToken} hot={connector} wallet={wallet} />;
265
246
  })}
266
-
267
247
  <PopupOption onClick={() => openConnector(connector)}>
268
248
  <div style={{ width: 44, height: 44, borderRadius: 16, background: "#000", display: "flex", alignItems: "center", justifyContent: "center" }}>
269
249
  <WalletIcon />
270
250
  </div>
271
251
  <PopupOptionInfo>
272
- <p>Connect wallet</p>
273
- <span className="wallet-address">To more pay options</span>
252
+ <p>Don't find the right token?</p>
253
+ <span className="wallet-address">Connect another wallet</span>
274
254
  </PopupOptionInfo>
275
255
  </PopupOption>
276
256
  </Popup>
277
257
  );
278
258
  });
259
+
260
+ const ErrorIcon = () => {
261
+ return (
262
+ <svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg" aria-label="Failed" style={{ display: "block", margin: "0 auto" }}>
263
+ <circle cx="14" cy="14" r="13" stroke="#E74C3C" strokeWidth="2" />
264
+ <path d="M9 9l10 10M19 9l-10 10" stroke="#E74C3C" strokeWidth="2.5" strokeLinecap="round" />
265
+ </svg>
266
+ );
267
+ };
@@ -63,26 +63,30 @@ export const TokenIcon = observer(({ token, wallet }: { token: Token; wallet?: O
63
63
  );
64
64
  });
65
65
 
66
- export const TokenCard = observer(({ token, onSelect, hot, wallet }: { token: Token; onSelect: (token: Token, wallet?: OmniWallet) => void; hot: HotConnector; wallet?: OmniWallet }) => {
67
- const balance = hot.balance(wallet, token);
68
- const symbol = token.chain === -4 && !token.isMainOmni ? `${token.originalChainSymbol}_${token.symbol}` : token.symbol;
66
+ export const TokenCard = observer(
67
+ ({ token, onSelect, amount, hot, wallet, rightControl }: { rightControl?: React.ReactNode; token: Token; onSelect?: (token: Token, wallet?: OmniWallet) => void; amount?: bigint; hot: HotConnector; wallet?: OmniWallet }) => {
68
+ const balance = amount || hot.balance(wallet, token);
69
+ const symbol = token.chain === -4 && !token.isMainOmni ? `${token.symbol} (${token.originalChainSymbol})` : token.symbol;
69
70
 
70
- return (
71
- <PopupOption key={token.id} onClick={() => onSelect(token, wallet)}>
72
- <TokenIcon token={token} wallet={wallet} />
71
+ return (
72
+ <PopupOption key={token.id} onClick={() => onSelect?.(token, wallet)}>
73
+ <TokenIcon token={token} wallet={wallet} />
73
74
 
74
- <TokenWrap style={{ marginTop: -2, textAlign: "left" }}>
75
- <p style={{ textAlign: "left", fontSize: 20, fontWeight: "bold" }}>{symbol}</p>
76
- <p style={{ textAlign: "left", fontSize: 14, color: "#c6c6c6" }}>${formatter.amount(token.usd)}</p>
77
- </TokenWrap>
75
+ <TokenWrap style={{ marginTop: -2, textAlign: "left" }}>
76
+ <p style={{ textAlign: "left", fontSize: 20, fontWeight: "bold" }}>{symbol}</p>
77
+ <p style={{ textAlign: "left", fontSize: 14, color: "#c6c6c6" }}>${formatter.amount(token.usd)}</p>
78
+ </TokenWrap>
78
79
 
79
- <TokenWrap style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
80
- <p style={{ textAlign: "right", fontSize: 20 }}>{token.readable(balance)}</p>
81
- <p style={{ textAlign: "right", fontSize: 14, color: "#c6c6c6" }}>${token.readable(balance, token.usd)}</p>
82
- </TokenWrap>
83
- </PopupOption>
84
- );
85
- });
80
+ {rightControl || (
81
+ <TokenWrap style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
82
+ <p style={{ textAlign: "right", fontSize: 20 }}>{token.readable(balance)}</p>
83
+ <p style={{ textAlign: "right", fontSize: 14, color: "#c6c6c6" }}>${token.readable(balance, token.usd)}</p>
84
+ </TokenWrap>
85
+ )}
86
+ </PopupOption>
87
+ );
88
+ }
89
+ );
86
90
 
87
91
  const TokenWrap = styled.div`
88
92
  display: flex;
package/src/ui/router.tsx CHANGED
@@ -17,19 +17,46 @@ import { Payment } from "./payment/Payment";
17
17
  import { Profile } from "./payment/Profile";
18
18
  import { Bridge } from "./payment/Bridge";
19
19
 
20
+ import ConnectPrimaryWallet from "./connect/PrimaryWallet";
20
21
  import { LogoutPopup } from "./connect/LogoutPopup";
21
22
  import { WalletPicker } from "./connect/WalletPicker";
22
23
  import { Connector } from "./connect/ConnectWallet";
23
24
  import { WCRequest } from "./connect/WCRequest";
25
+ import Toast from "./Toast";
24
26
 
25
- export const openPayment = (connector: HotConnector, intents: Intents) => {
26
- return new Promise<{ depositQoute: BridgeReview | "direct"; processing?: () => Promise<BridgeReview> }>((resolve, reject) => {
27
+ export const openPayment = (
28
+ connector: HotConnector,
29
+ {
30
+ intents,
31
+ title,
32
+ allowedTokens,
33
+ prepaidAmount,
34
+ payableToken,
35
+ needAmount,
36
+ onConfirm,
37
+ }: {
38
+ intents: Intents;
39
+ title?: string;
40
+ allowedTokens?: string[];
41
+ prepaidAmount: bigint;
42
+ payableToken: Token;
43
+ needAmount: bigint;
44
+ onConfirm: (args: { depositQoute: BridgeReview | "direct"; processing?: () => Promise<BridgeReview> }) => Promise<void>;
45
+ }
46
+ ) => {
47
+ return new Promise<void>((resolve, reject) => {
27
48
  present((close) => (
28
49
  <Payment //
50
+ onConfirm={onConfirm}
51
+ close={() => (close(), resolve())}
29
52
  onReject={() => (close(), reject(new Error("User rejected")))}
30
- onConfirm={(args) => (close(), resolve(args))}
53
+ prepaidAmount={prepaidAmount}
54
+ allowedTokens={allowedTokens}
55
+ payableToken={payableToken}
56
+ needAmount={needAmount}
31
57
  connector={connector}
32
58
  intents={intents}
59
+ title={title}
33
60
  />
34
61
  ));
35
62
  });
@@ -63,7 +90,15 @@ export const openBridge = (hot: HotConnector, setup?: BridgeProps["setup"]) => {
63
90
  };
64
91
 
65
92
  export const openConnector = (hot: HotConnector) => {
66
- present((close) => <Connector hot={hot} onClose={close} />);
93
+ return new Promise<void>((resolve) => {
94
+ present((close) => <Connector hot={hot} onClose={() => (resolve(), close())} />);
95
+ });
96
+ };
97
+
98
+ export const openConnectPrimaryWallet = (hot: HotConnector) => {
99
+ return new Promise<void>((resolve) => {
100
+ present((close) => <ConnectPrimaryWallet hot={hot} onClose={() => (close(), resolve())} />);
101
+ });
67
102
  };
68
103
 
69
104
  export const openProfile = (hot: HotConnector) => {
@@ -91,3 +126,7 @@ export const openWCRequest = <T,>(args: { task: () => Promise<T>; deeplink?: str
91
126
  present((close) => <WCRequest deeplink={args.deeplink} name={args.name} icon={args.icon} onClose={close} task={taskPromise} />);
92
127
  return taskPromise;
93
128
  };
129
+
130
+ export const openToast = (message: string) => {
131
+ return present(() => <Toast message={message} />);
132
+ };
package/src/ui/styles.ts CHANGED
@@ -195,6 +195,7 @@ export const PopupOption = styled.button`
195
195
  outline: none;
196
196
  border: none;
197
197
  background: transparent;
198
+ width: 100%;
198
199
  gap: 12px;
199
200
 
200
201
  img {
@@ -264,6 +265,7 @@ export const PopupButton = styled.button`
264
265
 
265
266
  export const PopupRoot = styled.div`
266
267
  height: 100%;
268
+ width: 100%;
267
269
 
268
270
  h1,
269
271
  h2,