@hot-labs/kit 1.1.0-beta.2 → 1.1.0-beta.4
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.
- package/build/OmniConnector.d.ts +1 -0
- package/build/OmniConnector.js.map +1 -1
- package/build/core/Intents.d.ts +18 -0
- package/build/core/Intents.js +19 -2
- package/build/core/Intents.js.map +1 -1
- package/build/core/api.d.ts +16 -1
- package/build/core/api.js +14 -4
- package/build/core/api.js.map +1 -1
- package/build/cosmos/connector.d.ts +2 -2
- package/build/cosmos/connector.js +15 -19
- package/build/cosmos/connector.js.map +1 -1
- package/build/cosmos/wallet.js.map +1 -1
- package/build/exchange.js +1 -5
- package/build/exchange.js.map +1 -1
- package/build/ui/Popup.d.ts +1 -1
- package/build/ui/Popup.js +1 -1
- package/build/ui/Popup.js.map +1 -1
- package/build/ui/payment/Bridge.js +13 -3
- package/build/ui/payment/Bridge.js.map +1 -1
- package/build/ui/payment/Payment.d.ts +7 -15
- package/build/ui/payment/Payment.js +44 -53
- package/build/ui/payment/Payment.js.map +1 -1
- package/build/ui/payment/Stepper.d.ts +13 -0
- package/build/ui/payment/Stepper.js +22 -0
- package/build/ui/payment/Stepper.js.map +1 -0
- package/build/ui/router.d.ts +6 -3
- package/build/ui/router.js +7 -7
- package/build/ui/router.js.map +1 -1
- package/package.json +1 -1
- package/src/OmniConnector.ts +1 -0
- package/src/core/Intents.ts +20 -2
- package/src/core/api.ts +26 -4
- package/src/cosmos/connector.ts +20 -24
- package/src/cosmos/wallet.ts +0 -1
- package/src/exchange.ts +2 -5
- package/src/ui/Popup.tsx +7 -4
- package/src/ui/payment/Bridge.tsx +15 -3
- package/src/ui/payment/Payment.tsx +73 -98
- package/src/ui/payment/Stepper.tsx +50 -0
- package/src/ui/router.tsx +15 -12
package/src/cosmos/connector.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { hex } from "@scure/base";
|
|
|
6
6
|
|
|
7
7
|
import { api } from "../core/api";
|
|
8
8
|
import { chains, WalletType } from "../core/chains";
|
|
9
|
-
import { ConnectorType, OmniConnector, WC_ICON } from "../OmniConnector";
|
|
9
|
+
import { ConnectorType, OmniConnector, OmniConnectorOption, WC_ICON } from "../OmniConnector";
|
|
10
10
|
import { HotConnector } from "../HotConnector";
|
|
11
11
|
import { OmniWallet } from "../OmniWallet";
|
|
12
12
|
|
|
@@ -20,9 +20,9 @@ declare global {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const wallets = {
|
|
23
|
+
const wallets: Record<string, OmniConnectorOption> = {
|
|
24
24
|
keplr: {
|
|
25
|
-
name: "Keplr",
|
|
25
|
+
name: "Keplr Wallet",
|
|
26
26
|
icon: "https://cdn.prod.website-files.com/667dc891bc7b863b5397495b/68a4ca95f93a9ab64dc67ab4_keplr-symbol.svg",
|
|
27
27
|
download: "https://www.keplr.app/get",
|
|
28
28
|
deeplink: "keplrwallet://wcV2?",
|
|
@@ -30,13 +30,21 @@ const wallets = {
|
|
|
30
30
|
id: "keplr",
|
|
31
31
|
},
|
|
32
32
|
leap: {
|
|
33
|
-
name: "Leap",
|
|
33
|
+
name: "Leap Wallet",
|
|
34
34
|
icon: "https://framerusercontent.com/images/AbGYvbwnLekBbsdf5g7PI5PpSg.png?scale-down-to=512",
|
|
35
35
|
download: "https://www.leapwallet.io/download",
|
|
36
36
|
deeplink: "leapcosmos://wcV2?",
|
|
37
37
|
type: "extension",
|
|
38
38
|
id: "leap",
|
|
39
39
|
},
|
|
40
|
+
gonkaWallet: {
|
|
41
|
+
name: "Gonka Wallet",
|
|
42
|
+
icon: "https://gonka-wallet.startonus.com/images/logo.png",
|
|
43
|
+
download: "https://t.me/gonka_wallet",
|
|
44
|
+
deeplink: "https://gonka-wallet.startonus.com/wc?wc=",
|
|
45
|
+
type: "external",
|
|
46
|
+
id: "gonkaWallet",
|
|
47
|
+
},
|
|
40
48
|
};
|
|
41
49
|
|
|
42
50
|
export default class CosmosConnector extends OmniConnector<CosmosWallet> {
|
|
@@ -50,28 +58,12 @@ export default class CosmosConnector extends OmniConnector<CosmosWallet> {
|
|
|
50
58
|
constructor(wibe3: HotConnector) {
|
|
51
59
|
super(wibe3);
|
|
52
60
|
|
|
53
|
-
this.options =
|
|
54
|
-
{
|
|
55
|
-
name: "Keplr",
|
|
56
|
-
download: "https://www.keplr.app/get",
|
|
57
|
-
icon: "https://cdn.prod.website-files.com/667dc891bc7b863b5397495b/68a4ca95f93a9ab64dc67ab4_keplr-symbol.svg",
|
|
58
|
-
type: "keplr" in window ? "extension" : "external",
|
|
59
|
-
id: "keplr",
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
name: "leap" in window ? "Leap" : "Leap Mobile",
|
|
63
|
-
download: "https://www.leapwallet.io/download",
|
|
64
|
-
icon: "https://framerusercontent.com/images/AbGYvbwnLekBbsdf5g7PI5PpSg.png?scale-down-to=512",
|
|
65
|
-
type: "leap" in window ? "extension" : "external",
|
|
66
|
-
id: "leap",
|
|
67
|
-
},
|
|
68
|
-
];
|
|
69
|
-
|
|
61
|
+
this.options = Object.values(wallets);
|
|
70
62
|
Keplr.getKeplr().then((keplr) => {
|
|
71
63
|
const option = this.options.find((option) => option.id === "keplr")!;
|
|
72
64
|
runInAction(() => {
|
|
73
65
|
option.type = keplr ? "extension" : "external";
|
|
74
|
-
option.name = keplr ? "Keplr" : "Keplr Mobile";
|
|
66
|
+
option.name = keplr ? "Keplr Wallet" : "Keplr Mobile";
|
|
75
67
|
});
|
|
76
68
|
});
|
|
77
69
|
|
|
@@ -113,7 +105,7 @@ export default class CosmosConnector extends OmniConnector<CosmosWallet> {
|
|
|
113
105
|
return chains.getByType(WalletType.COSMOS).map((t) => t.key);
|
|
114
106
|
}
|
|
115
107
|
|
|
116
|
-
async setupWalletConnect(id?: "keplr" | "leap"): Promise<CosmosWallet> {
|
|
108
|
+
async setupWalletConnect(id?: "keplr" | "leap" | "gonkaWallet"): Promise<CosmosWallet> {
|
|
117
109
|
const wc = await this.wc;
|
|
118
110
|
if (!wc) throw new Error("WalletConnect not found");
|
|
119
111
|
|
|
@@ -183,7 +175,7 @@ export default class CosmosConnector extends OmniConnector<CosmosWallet> {
|
|
|
183
175
|
);
|
|
184
176
|
}
|
|
185
177
|
|
|
186
|
-
async connectKeplr(type: "keplr" | "leap", extension?: Keplr): Promise<OmniWallet | { qrcode: string; deeplink?: string; task: Promise<OmniWallet> }> {
|
|
178
|
+
async connectKeplr(type: "keplr" | "leap" | "gonkaWallet", extension?: Keplr): Promise<OmniWallet | { qrcode: string; deeplink?: string; task: Promise<OmniWallet> }> {
|
|
187
179
|
if (!extension) {
|
|
188
180
|
return await this.connectWalletConnect({
|
|
189
181
|
onConnect: () => this.setupWalletConnect(type),
|
|
@@ -232,6 +224,10 @@ export default class CosmosConnector extends OmniConnector<CosmosWallet> {
|
|
|
232
224
|
});
|
|
233
225
|
}
|
|
234
226
|
|
|
227
|
+
if (id === "gonkaWallet") {
|
|
228
|
+
return await this.connectKeplr("gonkaWallet");
|
|
229
|
+
}
|
|
230
|
+
|
|
235
231
|
if (id === "keplr") {
|
|
236
232
|
const keplr = await Keplr.getKeplr();
|
|
237
233
|
return await this.connectKeplr("keplr", keplr);
|
package/src/cosmos/wallet.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { StargateClient } from "@cosmjs/stargate";
|
|
|
2
2
|
import { OmniWallet } from "../OmniWallet";
|
|
3
3
|
import { chains, WalletType } from "../core/chains";
|
|
4
4
|
import { ReviewFee } from "../core/bridge";
|
|
5
|
-
import CosmosConnector from "./connector";
|
|
6
5
|
import { Commitment } from "../core";
|
|
7
6
|
|
|
8
7
|
interface ProtocolWallet {
|
package/src/exchange.ts
CHANGED
|
@@ -334,14 +334,11 @@ export class Exchange {
|
|
|
334
334
|
|
|
335
335
|
const depositAddress = review.qoute.depositAddress!;
|
|
336
336
|
let hash = "";
|
|
337
|
+
|
|
337
338
|
if (review.from.chain === Network.Hot) {
|
|
338
339
|
hash = await this.wibe3
|
|
339
340
|
.intentsBuilder(sender)
|
|
340
|
-
.transfer({
|
|
341
|
-
amount: review.amountIn,
|
|
342
|
-
token: review.from.address as OmniToken,
|
|
343
|
-
recipient: depositAddress,
|
|
344
|
-
})
|
|
341
|
+
.transfer({ amount: review.amountIn, token: review.from.address as OmniToken, recipient: depositAddress })
|
|
345
342
|
.execute();
|
|
346
343
|
} else {
|
|
347
344
|
hash = await sender.transfer({
|
package/src/ui/Popup.tsx
CHANGED
|
@@ -22,7 +22,7 @@ interface PopupProps {
|
|
|
22
22
|
widget?: boolean;
|
|
23
23
|
children: React.ReactNode;
|
|
24
24
|
header?: React.ReactNode;
|
|
25
|
-
onClose
|
|
25
|
+
onClose?: () => void;
|
|
26
26
|
style?: React.CSSProperties;
|
|
27
27
|
mobileFullscreen?: boolean;
|
|
28
28
|
}
|
|
@@ -57,9 +57,12 @@ const Popup = ({ widget, children, header, onClose, style, mobileFullscreen }: P
|
|
|
57
57
|
<ModalContent ref={contentRef} $mobileFullscreen={mobileFullscreen} style={{ opacity: 0, transform: "translateY(20px)", transition: "all 0.2s ease-in-out" }}>
|
|
58
58
|
{header && (
|
|
59
59
|
<ModalHeader>
|
|
60
|
-
|
|
61
|
-
<
|
|
62
|
-
|
|
60
|
+
{onClose != null && (
|
|
61
|
+
<button onClick={onClose} style={{ position: "absolute", right: 16, top: 16 }}>
|
|
62
|
+
<CloseIcon />
|
|
63
|
+
</button>
|
|
64
|
+
)}
|
|
65
|
+
|
|
63
66
|
{header}
|
|
64
67
|
</ModalHeader>
|
|
65
68
|
)}
|
|
@@ -21,6 +21,12 @@ import { openSelectRecipient, openSelectTokenPopup, openSelectSender } from "../
|
|
|
21
21
|
import { TokenIcon } from "./TokenCard";
|
|
22
22
|
import DepositQR from "./DepositQR";
|
|
23
23
|
|
|
24
|
+
const animations = {
|
|
25
|
+
success: "https://hex.exchange/success.json",
|
|
26
|
+
failed: "https://hex.exchange/error.json",
|
|
27
|
+
loading: "https://hex.exchange/loading.json",
|
|
28
|
+
};
|
|
29
|
+
|
|
24
30
|
export interface BridgeProps {
|
|
25
31
|
hot: HotConnector;
|
|
26
32
|
widget?: boolean;
|
|
@@ -56,6 +62,12 @@ export const Bridge = observer(({ hot, widget, setup, onClose, onProcess, onSele
|
|
|
56
62
|
const [isError, setIsError] = useState<string | null>(null);
|
|
57
63
|
const [isReviewing, setIsReviewing] = useState(false);
|
|
58
64
|
|
|
65
|
+
useState(() => {
|
|
66
|
+
fetch(animations.loading);
|
|
67
|
+
fetch(animations.success);
|
|
68
|
+
fetch(animations.failed);
|
|
69
|
+
});
|
|
70
|
+
|
|
59
71
|
const [processing, setProcessing] = useState<{
|
|
60
72
|
status: "qr" | "processing" | "success" | "error";
|
|
61
73
|
resolve?: (value: BridgeReview) => void;
|
|
@@ -227,7 +239,7 @@ export const Bridge = observer(({ hot, widget, setup, onClose, onProcess, onSele
|
|
|
227
239
|
<Popup widget={widget} onClose={onClose} header={<p>{title}</p>} mobileFullscreen={setup?.mobileFullscreen}>
|
|
228
240
|
<div style={{ width: "100%", height: 400, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
|
|
229
241
|
{/* @ts-expect-error: dotlottie-wc is not typed */}
|
|
230
|
-
<dotlottie-wc key="loading" src=
|
|
242
|
+
<dotlottie-wc key="loading" src={animations.loading} speed="1" style={{ width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
|
|
231
243
|
<p style={{ marginTop: -32, fontSize: 16 }}>{processing.message}</p>
|
|
232
244
|
</div>
|
|
233
245
|
</Popup>
|
|
@@ -239,7 +251,7 @@ export const Bridge = observer(({ hot, widget, setup, onClose, onProcess, onSele
|
|
|
239
251
|
<Popup widget={widget} onClose={onClose} header={<p>{title}</p>} mobileFullscreen={setup?.mobileFullscreen}>
|
|
240
252
|
<div style={{ width: "100%", height: 400, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
|
|
241
253
|
{/* @ts-expect-error: dotlottie-wc is not typed */}
|
|
242
|
-
<dotlottie-wc key="success" src=
|
|
254
|
+
<dotlottie-wc key="success" src={animations.success} speed="1" style={{ width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
|
|
243
255
|
<p style={{ fontSize: 24, marginTop: -32, fontWeight: "bold" }}>Exchange successful</p>
|
|
244
256
|
</div>
|
|
245
257
|
<PopupButton style={{ marginTop: "auto" }} onClick={() => (cancelReview(), onClose())}>
|
|
@@ -254,7 +266,7 @@ export const Bridge = observer(({ hot, widget, setup, onClose, onProcess, onSele
|
|
|
254
266
|
<Popup widget={widget} onClose={onClose} header={<p>{title}</p>} mobileFullscreen={setup?.mobileFullscreen}>
|
|
255
267
|
<div style={{ width: "100%", height: 400, gap: 8, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
|
|
256
268
|
{/* @ts-expect-error: dotlottie-wc is not typed */}
|
|
257
|
-
<dotlottie-wc key="error" src=
|
|
269
|
+
<dotlottie-wc key="error" src={animations.failed} speed="1" style={{ width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
|
|
258
270
|
<p style={{ fontSize: 24, marginTop: -32, fontWeight: "bold" }}>Exchange failed</p>
|
|
259
271
|
<p style={{ fontSize: 14 }}>{processing.message}</p>
|
|
260
272
|
</div>
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { observer } from "mobx-react-lite";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
3
|
|
|
4
|
+
import { WalletIcon } from "../icons/wallet";
|
|
5
|
+
import { PopupButton, PopupOption, PopupOptionInfo } from "../styles";
|
|
4
6
|
import { Commitment, formatter, Intents } from "../../core";
|
|
5
7
|
import { Recipient } from "../../core/recipient";
|
|
6
8
|
import { Network } from "../../core/chains";
|
|
@@ -15,75 +17,26 @@ import { HotConnector } from "../../HotConnector";
|
|
|
15
17
|
import Popup from "../Popup";
|
|
16
18
|
|
|
17
19
|
import { TokenCard, TokenIcon } from "./TokenCard";
|
|
18
|
-
import {
|
|
19
|
-
import { WalletIcon } from "../icons/wallet";
|
|
20
|
+
import { HorizontalStepper } from "./Stepper";
|
|
20
21
|
import { Loader } from "./Profile";
|
|
21
22
|
|
|
22
23
|
interface PaymentProps {
|
|
23
24
|
intents: Intents;
|
|
24
25
|
connector: HotConnector;
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
payload?: Record<string, any>;
|
|
27
|
+
onReject: (message: string) => void;
|
|
28
|
+
onSuccess: (task: { paymentId: string; tx: string }) => void;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
import React from "react";
|
|
30
|
-
|
|
31
31
|
const animations = {
|
|
32
32
|
success: "https://hex.exchange/success.json",
|
|
33
33
|
failed: "https://hex.exchange/error.json",
|
|
34
34
|
loading: "https://hex.exchange/loading.json",
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
label: string;
|
|
39
|
-
completed?: boolean;
|
|
40
|
-
active?: boolean;
|
|
41
|
-
}
|
|
37
|
+
const PAY_SLIPPAGE = 0.002;
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
steps: Step[];
|
|
45
|
-
currentStep: number;
|
|
46
|
-
style?: React.CSSProperties;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const HorizontalStepper: React.FC<StepperProps> = ({ steps, currentStep, style }) => {
|
|
50
|
-
return (
|
|
51
|
-
<div style={{ padding: "0 32px 32px", display: "flex", alignItems: "center", width: "100%", margin: "16px 0", ...style }}>
|
|
52
|
-
{steps.map((step, idx) => {
|
|
53
|
-
const isCompleted = idx < currentStep;
|
|
54
|
-
const isActive = idx === currentStep;
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<React.Fragment key={idx}>
|
|
58
|
-
<div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
|
|
59
|
-
<div
|
|
60
|
-
style={{
|
|
61
|
-
width: 16,
|
|
62
|
-
height: 16,
|
|
63
|
-
position: "relative",
|
|
64
|
-
borderRadius: "50%",
|
|
65
|
-
border: isActive || isCompleted ? "2px solid #ffffff" : "2px solid #a0a0a0",
|
|
66
|
-
background: isCompleted ? "#ffffff" : "#333",
|
|
67
|
-
display: "flex",
|
|
68
|
-
alignItems: "center",
|
|
69
|
-
justifyContent: "center",
|
|
70
|
-
transition: "all 0.2s",
|
|
71
|
-
zIndex: 1,
|
|
72
|
-
}}
|
|
73
|
-
>
|
|
74
|
-
<p style={{ fontSize: 16, color: "#fff", opacity: isActive ? 1 : 0.5, position: "absolute", top: 24, width: 100 }}>{step.label}</p>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
|
|
78
|
-
{idx < steps.length - 1 && <div style={{ transition: "background 0.2s", flex: 1, height: 2, background: idx < currentStep ? "#ffffff" : "#333", margin: "0 6px", borderRadius: 24, minWidth: 24 }} />}
|
|
79
|
-
</React.Fragment>
|
|
80
|
-
);
|
|
81
|
-
})}
|
|
82
|
-
</div>
|
|
83
|
-
);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export const Payment = observer(({ connector, intents, onClose, onConfirm }: PaymentProps) => {
|
|
39
|
+
export const Payment = observer(({ connector, intents, payload, onReject, onSuccess }: PaymentProps) => {
|
|
87
40
|
useState(() => {
|
|
88
41
|
fetch(animations.loading);
|
|
89
42
|
fetch(animations.success);
|
|
@@ -99,8 +52,8 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
99
52
|
token?: Token;
|
|
100
53
|
wallet?: OmniWallet;
|
|
101
54
|
commitment?: Commitment;
|
|
102
|
-
review?: BridgeReview;
|
|
103
|
-
|
|
55
|
+
review?: BridgeReview | "direct";
|
|
56
|
+
data?: { paymentId: string; tx: string };
|
|
104
57
|
step?: "selectToken" | "sign" | "transfer" | "success" | "error" | "loading";
|
|
105
58
|
success?: boolean;
|
|
106
59
|
loading?: boolean;
|
|
@@ -114,25 +67,36 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
114
67
|
const selectToken = async (from: Token, wallet?: OmniWallet) => {
|
|
115
68
|
if (!wallet) return;
|
|
116
69
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
70
|
+
// Set signer as payer wallet if not set another
|
|
71
|
+
if (!intents.signer) intents.attachWallet(wallet);
|
|
72
|
+
|
|
73
|
+
if (from.id === need.id) {
|
|
74
|
+
return setFlow({ token: from, wallet, review: "direct", step: "sign" });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
setFlow({ token: from, wallet, review: undefined, step: "sign" });
|
|
79
|
+
const review = await connector.exchange.reviewSwap({
|
|
80
|
+
recipient: Recipient.fromWallet(intents.signer)!,
|
|
81
|
+
amount: needAmount + (needAmount * BigInt(Math.floor(PAY_SLIPPAGE * 1000))) / BigInt(1000),
|
|
82
|
+
slippage: PAY_SLIPPAGE,
|
|
83
|
+
sender: wallet,
|
|
84
|
+
refund: wallet,
|
|
85
|
+
type: "exactOut",
|
|
86
|
+
to: need,
|
|
87
|
+
from,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
setFlow({ token: from, wallet, review, step: "sign" });
|
|
91
|
+
} catch {
|
|
92
|
+
setFlow({ token: from, wallet, error: true, step: "sign" });
|
|
93
|
+
}
|
|
130
94
|
};
|
|
131
95
|
|
|
132
96
|
const signStep = async () => {
|
|
133
97
|
try {
|
|
134
98
|
setFlow((t) => (t ? { ...t, step: "sign", loading: true } : null));
|
|
135
|
-
const commitment = await intents.
|
|
99
|
+
const commitment = await intents.sign();
|
|
136
100
|
setFlow((t) => (t ? { ...t, step: "transfer", commitment, loading: false } : null));
|
|
137
101
|
} catch (error) {
|
|
138
102
|
console.error(error);
|
|
@@ -146,17 +110,17 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
146
110
|
const commitment = flow?.commitment;
|
|
147
111
|
if (!commitment) throw new Error("Commitment not found");
|
|
148
112
|
if (!flow?.review) throw new Error("Review not found");
|
|
149
|
-
|
|
150
113
|
setFlow((t) => (t ? { ...t, step: "loading" } : null));
|
|
151
|
-
const result = await connector.exchange.makeSwap(flow.review, { log: () => {} });
|
|
152
114
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
await
|
|
115
|
+
// make swap if need
|
|
116
|
+
let depositAddress: string | undefined;
|
|
117
|
+
if (flow.review != "direct") {
|
|
118
|
+
const result = await connector.exchange.makeSwap(flow.review, { log: () => {} });
|
|
119
|
+
depositAddress = typeof result.review?.qoute === "object" ? result.review?.qoute?.depositAddress : undefined;
|
|
157
120
|
}
|
|
158
121
|
|
|
159
|
-
|
|
122
|
+
const data = await api.yieldIntentCall({ depositAddress, commitment, payload });
|
|
123
|
+
setFlow((t) => (t ? { ...t, step: "success", loading: false, success: true, data } : null));
|
|
160
124
|
} catch (error) {
|
|
161
125
|
console.error(error);
|
|
162
126
|
setFlow((t) => (t ? { ...t, step: "error", loading: false, error } : null));
|
|
@@ -166,13 +130,13 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
166
130
|
|
|
167
131
|
if (flow?.step === "success") {
|
|
168
132
|
return (
|
|
169
|
-
<Popup
|
|
133
|
+
<Popup header={<p>{title}</p>}>
|
|
170
134
|
<div style={{ width: "100%", height: 400, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
|
|
171
135
|
{/* @ts-expect-error: dotlottie-wc is not typed */}
|
|
172
136
|
<dotlottie-wc key="success" src={animations.success} speed="1" style={{ width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
|
|
173
137
|
<p style={{ fontSize: 24, marginTop: -32, fontWeight: "bold" }}>Payment successful</p>
|
|
174
138
|
</div>
|
|
175
|
-
<PopupButton style={{ marginTop: "auto" }} onClick={() =>
|
|
139
|
+
<PopupButton style={{ marginTop: "auto" }} onClick={() => onSuccess(flow.data!)}>
|
|
176
140
|
Continue
|
|
177
141
|
</PopupButton>
|
|
178
142
|
</Popup>
|
|
@@ -181,7 +145,7 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
181
145
|
|
|
182
146
|
if (flow?.step === "loading") {
|
|
183
147
|
return (
|
|
184
|
-
<Popup
|
|
148
|
+
<Popup header={<p>{title}</p>}>
|
|
185
149
|
<div style={{ width: "100%", height: 400, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
|
|
186
150
|
{/* @ts-expect-error: dotlottie-wc is not typed */}
|
|
187
151
|
<dotlottie-wc key="loading" src={animations.loading} speed="1" style={{ marginTop: -64, width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
|
|
@@ -193,14 +157,14 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
193
157
|
|
|
194
158
|
if (flow?.step === "error") {
|
|
195
159
|
return (
|
|
196
|
-
<Popup
|
|
160
|
+
<Popup header={<p>{title}</p>}>
|
|
197
161
|
<div style={{ width: "100%", height: 400, gap: 8, display: "flex", justifyContent: "center", alignItems: "center", flexDirection: "column" }}>
|
|
198
162
|
{/* @ts-expect-error: dotlottie-wc is not typed */}
|
|
199
163
|
<dotlottie-wc key="error" src={animations.failed} speed="1" style={{ width: 300, height: 300 }} mode="forward" loop autoplay></dotlottie-wc>
|
|
200
164
|
<p style={{ fontSize: 24, marginTop: -32, fontWeight: "bold" }}>Payment failed</p>
|
|
201
|
-
<p style={{ fontSize: 14 }}>{flow.error?.toString?.() ?? "Unknown error"}</p>
|
|
165
|
+
<p style={{ fontSize: 14, width: "80%", textAlign: "center", overflowY: "auto", lineBreak: "anywhere" }}>{flow.error?.toString?.() ?? "Unknown error"}</p>
|
|
202
166
|
</div>
|
|
203
|
-
<PopupButton onClick={() =>
|
|
167
|
+
<PopupButton onClick={() => onReject(flow.error?.toString?.() ?? "Unknown error")}>Close</PopupButton>
|
|
204
168
|
</Popup>
|
|
205
169
|
);
|
|
206
170
|
}
|
|
@@ -209,7 +173,7 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
209
173
|
if (!flow.token) return null;
|
|
210
174
|
if (!flow.wallet) return null;
|
|
211
175
|
return (
|
|
212
|
-
<Popup onClose={
|
|
176
|
+
<Popup onClose={() => onReject("closed")} header={<p>{title}</p>}>
|
|
213
177
|
<HorizontalStepper steps={[{ label: "Select" }, { label: "Review" }, { label: "Confirm" }]} currentStep={2} />
|
|
214
178
|
|
|
215
179
|
<PopupOption style={{ marginTop: 8 }}>
|
|
@@ -222,8 +186,8 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
222
186
|
|
|
223
187
|
{flow.review ? (
|
|
224
188
|
<div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
|
|
225
|
-
<p style={{ textAlign: "right", fontSize: 20 }}>{flow.token.readable(flow.review?.amountIn ?? 0)}</p>
|
|
226
|
-
<p style={{ textAlign: "right", fontSize: 14, color: "#c6c6c6" }}>${flow.token.readable(flow.review?.amountIn ?? 0n, flow.token.usd)}</p>
|
|
189
|
+
<p style={{ textAlign: "right", fontSize: 20 }}>{flow.token.readable(flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0)}</p>
|
|
190
|
+
<p style={{ textAlign: "right", fontSize: 14, color: "#c6c6c6" }}>${flow.token.readable(flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0n, flow.token.usd)}</p>
|
|
227
191
|
</div>
|
|
228
192
|
) : (
|
|
229
193
|
<div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
|
|
@@ -243,7 +207,7 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
243
207
|
if (!flow.token) return null;
|
|
244
208
|
if (!flow.wallet) return null;
|
|
245
209
|
return (
|
|
246
|
-
<Popup onClose={
|
|
210
|
+
<Popup onClose={() => onReject("closed")} header={<p>{title}</p>}>
|
|
247
211
|
<HorizontalStepper steps={[{ label: "Select" }, { label: "Review" }, { label: "Confirm" }]} currentStep={1} />
|
|
248
212
|
|
|
249
213
|
<PopupOption style={{ marginTop: 8 }}>
|
|
@@ -256,40 +220,51 @@ export const Payment = observer(({ connector, intents, onClose, onConfirm }: Pay
|
|
|
256
220
|
|
|
257
221
|
{flow.review ? (
|
|
258
222
|
<div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
|
|
259
|
-
<p style={{ textAlign: "right", fontSize: 20 }}>{flow.token.readable(flow.review?.amountIn ?? 0)}</p>
|
|
260
|
-
<p style={{ textAlign: "right", fontSize: 14, color: "#c6c6c6" }}>${flow.token.readable(flow.review?.amountIn ?? 0n, flow.token.usd)}</p>
|
|
223
|
+
<p style={{ textAlign: "right", fontSize: 20 }}>{flow.token.readable(flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0)}</p>
|
|
224
|
+
<p style={{ textAlign: "right", fontSize: 14, color: "#c6c6c6" }}>${flow.token.readable(flow.review === "direct" ? needAmount : flow.review?.amountIn ?? 0n, flow.token.usd)}</p>
|
|
261
225
|
</div>
|
|
262
226
|
) : (
|
|
263
227
|
<div style={{ paddingRight: 4, marginLeft: "auto", alignItems: "flex-end" }}>
|
|
264
|
-
|
|
228
|
+
{flow.error ? (
|
|
229
|
+
<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" }}>
|
|
230
|
+
<circle cx="14" cy="14" r="13" stroke="#E74C3C" strokeWidth="2" />
|
|
231
|
+
<path d="M9 9l10 10M19 9l-10 10" stroke="#E74C3C" strokeWidth="2.5" strokeLinecap="round" />
|
|
232
|
+
</svg>
|
|
233
|
+
) : (
|
|
234
|
+
<Loader />
|
|
235
|
+
)}
|
|
265
236
|
</div>
|
|
266
237
|
)}
|
|
267
238
|
</PopupOption>
|
|
268
239
|
|
|
269
|
-
|
|
270
|
-
{
|
|
271
|
-
|
|
240
|
+
{flow.error ? (
|
|
241
|
+
<PopupButton style={{ marginTop: 24 }} onClick={() => setFlow(null)}>
|
|
242
|
+
Select another token
|
|
243
|
+
</PopupButton>
|
|
244
|
+
) : (
|
|
245
|
+
<PopupButton style={{ marginTop: 24 }} disabled={!flow?.review} onClick={signStep}>
|
|
246
|
+
{flow?.loading ? "Signing..." : flow?.review ? "Sign review" : "Quoting..."}
|
|
247
|
+
</PopupButton>
|
|
248
|
+
)}
|
|
272
249
|
</Popup>
|
|
273
250
|
);
|
|
274
251
|
}
|
|
275
252
|
|
|
276
253
|
return (
|
|
277
|
-
<Popup onClose={
|
|
254
|
+
<Popup onClose={() => onReject("closed")} header={<p>{title}</p>}>
|
|
278
255
|
<HorizontalStepper steps={[{ label: "Select" }, { label: "Review" }, { label: "Confirm" }]} currentStep={0} />
|
|
279
256
|
|
|
280
257
|
{connector.walletsTokens.map(({ token, wallet, balance }) => {
|
|
281
|
-
if (token.id === need.id) return null;
|
|
282
258
|
const availableBalance = token.float(balance) - token.reserve;
|
|
283
259
|
|
|
284
260
|
if (need.originalChain === Network.Gonka || need.originalChain === Network.Juno) {
|
|
285
261
|
if (token.id === need.id) return null;
|
|
286
262
|
if (token.originalAddress !== need.originalAddress) return null;
|
|
287
|
-
|
|
288
263
|
if (availableBalance < need.float(needAmount)) return null;
|
|
289
264
|
return <TokenCard key={token.id} token={token} onSelect={selectToken} hot={connector} wallet={wallet} />;
|
|
290
265
|
}
|
|
291
266
|
|
|
292
|
-
if (availableBalance * token.usd <= need.usd * need.float(needAmount)) return null;
|
|
267
|
+
if (availableBalance * token.usd <= need.usd * need.float(needAmount) * (1 + PAY_SLIPPAGE)) return null;
|
|
293
268
|
return <TokenCard key={token.id} token={token} onSelect={selectToken} hot={connector} wallet={wallet} />;
|
|
294
269
|
})}
|
|
295
270
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
interface Step {
|
|
4
|
+
label: string;
|
|
5
|
+
completed?: boolean;
|
|
6
|
+
active?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface StepperProps {
|
|
10
|
+
steps: Step[];
|
|
11
|
+
currentStep: number;
|
|
12
|
+
style?: React.CSSProperties;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const HorizontalStepper: React.FC<StepperProps> = ({ steps, currentStep, style }) => {
|
|
16
|
+
return (
|
|
17
|
+
<div style={{ padding: "0 32px 32px", display: "flex", alignItems: "center", width: "100%", margin: "16px 0", ...style }}>
|
|
18
|
+
{steps.map((step, idx) => {
|
|
19
|
+
const isCompleted = idx < currentStep;
|
|
20
|
+
const isActive = idx === currentStep;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<React.Fragment key={idx}>
|
|
24
|
+
<div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
|
|
25
|
+
<div
|
|
26
|
+
style={{
|
|
27
|
+
width: 16,
|
|
28
|
+
height: 16,
|
|
29
|
+
position: "relative",
|
|
30
|
+
borderRadius: "50%",
|
|
31
|
+
border: isActive || isCompleted ? "2px solid #ffffff" : "2px solid #a0a0a0",
|
|
32
|
+
background: isCompleted ? "#ffffff" : "#333",
|
|
33
|
+
display: "flex",
|
|
34
|
+
alignItems: "center",
|
|
35
|
+
justifyContent: "center",
|
|
36
|
+
transition: "all 0.2s",
|
|
37
|
+
zIndex: 1,
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
<p style={{ fontSize: 16, color: "#fff", opacity: isActive ? 1 : 0.5, position: "absolute", top: 24, width: 100 }}>{step.label}</p>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
{idx < steps.length - 1 && <div style={{ transition: "background 0.2s", flex: 1, height: 2, background: idx < currentStep ? "#ffffff" : "#333", margin: "0 6px", borderRadius: 24, minWidth: 24 }} />}
|
|
45
|
+
</React.Fragment>
|
|
46
|
+
);
|
|
47
|
+
})}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
package/src/ui/router.tsx
CHANGED
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
import { HotConnector } from "../HotConnector";
|
|
2
2
|
import { OmniConnector } from "../OmniConnector";
|
|
3
|
-
import { BridgeReview } from "../exchange";
|
|
4
|
-
import { Token } from "../core/token";
|
|
5
3
|
import { OmniWallet } from "../OmniWallet";
|
|
4
|
+
|
|
5
|
+
import { BridgeReview } from "../exchange";
|
|
6
6
|
import { WalletType } from "../core/chains";
|
|
7
7
|
import { Recipient } from "../core/recipient";
|
|
8
8
|
import { Intents } from "../core/Intents";
|
|
9
|
+
import { Token } from "../core/token";
|
|
9
10
|
|
|
10
11
|
import { present } from "./Popup";
|
|
12
|
+
import { SelectTokenPopup } from "./payment/SelectToken";
|
|
13
|
+
import { SelectRecipient } from "./payment/SelectRecipient";
|
|
14
|
+
import { SelectSender } from "./payment/SelectSender";
|
|
15
|
+
import { BridgeProps } from "./payment/Bridge";
|
|
11
16
|
import { Payment } from "./payment/Payment";
|
|
12
|
-
import { LogoutPopup } from "./connect/LogoutPopup";
|
|
13
|
-
import { Bridge } from "./payment/Bridge";
|
|
14
17
|
import { Profile } from "./payment/Profile";
|
|
15
|
-
import {
|
|
18
|
+
import { Bridge } from "./payment/Bridge";
|
|
19
|
+
|
|
20
|
+
import { LogoutPopup } from "./connect/LogoutPopup";
|
|
16
21
|
import { WalletPicker } from "./connect/WalletPicker";
|
|
17
|
-
import { BridgeProps } from "./payment/Bridge";
|
|
18
22
|
import { Connector } from "./connect/ConnectWallet";
|
|
19
|
-
import { SelectSender } from "./payment/SelectSender";
|
|
20
|
-
import { SelectRecipient } from "./payment/SelectRecipient";
|
|
21
23
|
import { WCRequest } from "./connect/WCRequest";
|
|
22
24
|
|
|
23
|
-
export const openPayment = (connector: HotConnector, intents: Intents) => {
|
|
24
|
-
return new Promise<
|
|
25
|
+
export const openPayment = (connector: HotConnector, intents: Intents, payload?: Record<string, any>) => {
|
|
26
|
+
return new Promise<{ paymentId: string; tx: string }>((resolve, reject) => {
|
|
25
27
|
present((close) => (
|
|
26
28
|
<Payment //
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
onReject={() => (close(), reject(new Error("User rejected")))}
|
|
30
|
+
onSuccess={(task) => (close(), resolve(task))}
|
|
29
31
|
connector={connector}
|
|
30
32
|
intents={intents}
|
|
33
|
+
payload={payload}
|
|
31
34
|
/>
|
|
32
35
|
));
|
|
33
36
|
});
|