@xswap-link/sdk 0.2.5 → 0.3.0
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/.github/workflows/main.yml +2 -2
- package/.github/workflows/publish.yml +2 -2
- package/CHANGELOG.md +12 -0
- package/dist/index.css +325 -257
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1296 -516
- package/dist/index.mjs +1323 -537
- package/package.json +1 -1
- package/src/components/Alert/index.tsx +1 -1
- package/src/components/Skeleton/index.tsx +3 -1
- package/src/components/Spinner/index.tsx +28 -0
- package/src/components/TxConfigForm/BalanceComponent.tsx +1 -1
- package/src/components/TxConfigForm/Button.tsx +1 -1
- package/src/components/TxConfigForm/ChainListElement.tsx +2 -2
- package/src/components/TxConfigForm/ConfirmationAmount.tsx +91 -0
- package/src/components/TxConfigForm/Description.tsx +5 -3
- package/src/components/TxConfigForm/ErrorField.tsx +4 -2
- package/src/components/TxConfigForm/FeesDetails.tsx +25 -31
- package/src/components/TxConfigForm/Form.tsx +763 -118
- package/src/components/TxConfigForm/History.tsx +5 -5
- package/src/components/TxConfigForm/HistoryCard.tsx +33 -40
- package/src/components/TxConfigForm/PoweredBy.tsx +2 -2
- package/src/components/TxConfigForm/Settings.tsx +33 -27
- package/src/components/TxConfigForm/Summary.tsx +33 -31
- package/src/components/TxConfigForm/SwapPanel.tsx +12 -15
- package/src/components/TxConfigForm/TokenPicker.tsx +36 -34
- package/src/components/TxConfigForm/TopBar.tsx +8 -8
- package/src/components/TxConfigForm/UsdPrice.tsx +4 -14
- package/src/components/TxConfigForm/index.tsx +37 -10
- package/src/components/TxStatusButton/index.tsx +42 -28
- package/src/components/UnknownTokenLogo/UnknownTokenLogo.tsx +1 -1
- package/src/components/global.css +7 -5
- package/src/components/icons/CircularProgressIcon.tsx +1 -1
- package/src/components/icons/ErrorIcon.tsx +32 -0
- package/src/components/icons/SignIcon.tsx +17 -0
- package/src/components/icons/SuccessIcon.tsx +29 -0
- package/src/components/icons/index.ts +3 -0
- package/src/config/init.tsx +42 -24
- package/src/constants/index.ts +2 -2
- package/src/models/payloads/GetSwapTxPayload.ts +2 -0
- package/src/services/integrations/monitoring.ts +10 -3
- package/src/services/integrations/transactions.ts +9 -3
- package/src/utils/contracts.ts +18 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/parseWeb3Error.ts +93 -0
- package/tailwind.config.js +40 -0
|
@@ -24,8 +24,6 @@ export const TokenPicker: FC<Props> = ({
|
|
|
24
24
|
}) => {
|
|
25
25
|
const [searchValue, setSearchValue] = useState("");
|
|
26
26
|
|
|
27
|
-
const modalRoot = document.querySelector("#xswap-modal");
|
|
28
|
-
|
|
29
27
|
const onBackdropClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
30
28
|
e.stopPropagation();
|
|
31
29
|
e.nativeEvent.stopImmediatePropagation();
|
|
@@ -57,40 +55,42 @@ export const TokenPicker: FC<Props> = ({
|
|
|
57
55
|
);
|
|
58
56
|
}, [searchValue, tokens]);
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
const xpayRoot = document.querySelector("#xpay-root");
|
|
59
|
+
|
|
60
|
+
return xpayRoot
|
|
61
61
|
? createPortal(
|
|
62
62
|
<div
|
|
63
63
|
onClick={onBackdropClick}
|
|
64
|
-
className="box-border fixed h-full w-full z-[999] top-0 left-0 bg-[rgba(0,0,0,0.8)] flex items-center justify-center p-5"
|
|
64
|
+
className="xpay-box-border xpay-fixed xpay-h-full xpay-w-full xpay-z-[999] xpay-top-0 xpay-left-0 xpay-bg-[rgba(0,0,0,0.8)] xpay-flex xpay-items-center xpay-justify-center xpay-p-5"
|
|
65
65
|
>
|
|
66
|
-
<div className="relative bg-black rounded-3xl p-6 max-h-[70%] flex flex-col text-base text-white w-[452px] h-[70%] border border-solid border-[rgba(255,255,255,0.2)]">
|
|
66
|
+
<div className="xpay-relative xpay-bg-black xpay-rounded-3xl xpay-p-6 xpay-max-h-[70%] xpay-flex xpay-flex-col xpay-text-base xpay-text-white xpay-w-[452px] xpay-h-[70%] xpay-border xpay-border-solid xpay-border-[rgba(255,255,255,0.2)]">
|
|
67
67
|
<div
|
|
68
68
|
onClick={onCloseClick}
|
|
69
|
-
className="absolute top-4 right-4 cursor-pointer"
|
|
69
|
+
className="xpay-absolute xpay-top-4 xpay-right-4 xpay-cursor-pointer"
|
|
70
70
|
>
|
|
71
71
|
<CloseIcon />
|
|
72
72
|
</div>
|
|
73
|
-
<p className="text-base mb-4">Pick a token</p>
|
|
74
|
-
<div className="flex items-center border border-solid border-[rgba(255,255,255,0.1)] rounded-lg py-0 px-3 gap-2 bg-[rgba(15,15,15,1)]">
|
|
75
|
-
<div className="w-6 h-6">
|
|
73
|
+
<p className="xpay-text-base xpay-mb-4">Pick a token</p>
|
|
74
|
+
<div className="xpay-flex xpay-items-center xpay-border xpay-border-solid xpay-border-[rgba(255,255,255,0.1)] xpay-rounded-lg xpay-py-0 xpay-px-3 xpay-gap-2 xpay-bg-[rgba(15,15,15,1)]">
|
|
75
|
+
<div className="xpay-w-6 xpay-h-6">
|
|
76
76
|
<SearchIcon />
|
|
77
77
|
</div>
|
|
78
78
|
<input
|
|
79
79
|
placeholder="Search name or paste address"
|
|
80
80
|
value={searchValue}
|
|
81
81
|
onChange={(e) => setSearchValue(e.target.value)}
|
|
82
|
-
className="relative h-[41px] leading-[41px] font-normal z-[1] w-full text-white bg-transparent border-none outline-none placeholder:text-sm"
|
|
82
|
+
className="xpay-relative xpay-h-[41px] xpay-leading-[41px] xpay-font-normal xpay-z-[1] xpay-w-full xpay-text-white xpay-bg-transparent xpay-border-none xpay-outline-none placeholder:xpay-text-sm"
|
|
83
83
|
/>
|
|
84
84
|
</div>
|
|
85
|
-
<div className="my-4 mx-0 flex flex-wrap gap-3">
|
|
85
|
+
<div className="xpay-my-4 xpay-mx-0 xpay-flex xpay-flex-wrap xpay-gap-3">
|
|
86
86
|
{tokens
|
|
87
87
|
?.filter((token) => token?.quickPick)
|
|
88
88
|
.map((token) => (
|
|
89
89
|
<div
|
|
90
90
|
key={token.address}
|
|
91
|
-
className={`flex gap-2 py-1 px-2 items-center bg-[rgb(15,15,15)] rounded-2xl border border-solid border-[rgba(255,255,255,0.1)] hover:bg-[rgb(25,25,25)] hover:cursor-pointer ${
|
|
91
|
+
className={`xpay-flex xpay-gap-2 xpay-py-1 xpay-px-2 xpay-items-center xpay-bg-[rgb(15,15,15)] xpay-rounded-2xl xpay-border xpay-border-solid xpay-border-[rgba(255,255,255,0.1)] hover:xpay-bg-[rgb(25,25,25)] hover:xpay-cursor-pointer ${
|
|
92
92
|
token.address === selectedToken?.address
|
|
93
|
-
? "bg-[rgb(35, 35, 35)]"
|
|
93
|
+
? "xpay-bg-[rgb(35, 35, 35)]"
|
|
94
94
|
: ""
|
|
95
95
|
}`}
|
|
96
96
|
onClick={() => {
|
|
@@ -98,20 +98,24 @@ export const TokenPicker: FC<Props> = ({
|
|
|
98
98
|
onCloseClick();
|
|
99
99
|
}}
|
|
100
100
|
>
|
|
101
|
-
<img
|
|
102
|
-
|
|
101
|
+
<img
|
|
102
|
+
src={token.image}
|
|
103
|
+
alt={token.name}
|
|
104
|
+
className="xpay-w-5"
|
|
105
|
+
/>
|
|
106
|
+
<div className="xpay-text-sm">{token.symbol}</div>
|
|
103
107
|
</div>
|
|
104
108
|
))}
|
|
105
109
|
</div>
|
|
106
|
-
<div className="h-[2px] my-0 mx-[-24px] w-[100%+48px] px-12 bg-[rgba(255,255,255,0.1)]" />
|
|
107
|
-
<div className="overflow-y-scroll h-full mx-[-24px] w-[100%+48px] flex flex-col">
|
|
110
|
+
<div className="xpay-h-[2px] xpay-my-0 xpay-mx-[-24px] xpay-w-[100%+48px] xpay-px-12 xpay-bg-[rgba(255,255,255,0.1)]" />
|
|
111
|
+
<div className="xpay-overflow-y-scroll xpay-h-full xpay-mx-[-24px] xpay-w-[100%+48px] xpay-flex xpay-flex-col">
|
|
108
112
|
{filteredTokens && filteredTokens.length > 0 ? (
|
|
109
113
|
filteredTokens.map((token, index) => (
|
|
110
114
|
<div
|
|
111
115
|
key={`${index}_${token.address}`}
|
|
112
|
-
className={`flex gap-3 py-2 px-[24px] w-full items-center hover:bg-[rgb(25,25,25)] hover:cursor-pointer ${
|
|
116
|
+
className={`xpay-flex xpay-gap-3 xpay-py-2 xpay-px-[24px] xpay-w-full xpay-items-center hover:xpay-bg-[rgb(25,25,25)] hover:xpay-cursor-pointer ${
|
|
113
117
|
token.address === selectedToken?.address
|
|
114
|
-
? "bg-[rgb(35,35,35)]"
|
|
118
|
+
? "xpay-bg-[rgb(35,35,35)]"
|
|
115
119
|
: ""
|
|
116
120
|
}`}
|
|
117
121
|
onClick={() => {
|
|
@@ -120,22 +124,18 @@ export const TokenPicker: FC<Props> = ({
|
|
|
120
124
|
}}
|
|
121
125
|
>
|
|
122
126
|
{token.image ? (
|
|
123
|
-
<img
|
|
124
|
-
src={token.image}
|
|
125
|
-
alt={token.name}
|
|
126
|
-
className="token-picker__all__logo"
|
|
127
|
-
/>
|
|
127
|
+
<img src={token.image} alt={token.name} />
|
|
128
128
|
) : (
|
|
129
|
-
<div className="flex items-center justify-center w-9 h-9 text-[15px] ml-1 rounded-full bg-[#272e40] text-[#e0e7fa]">
|
|
129
|
+
<div className="xpay-flex xpay-items-center xpay-justify-center xpay-w-9 xpay-h-9 xpay-text-[15px] xpay-ml-1 xpay-rounded-full xpay-bg-[#272e40] xpay-text-[#e0e7fa]">
|
|
130
130
|
{token?.symbol.substring(0, 1)}
|
|
131
131
|
</div>
|
|
132
132
|
)}
|
|
133
|
-
<div className="flex justify-between items-center gap-1 overflow-hidden grow">
|
|
134
|
-
<div className="flex flex-col leading-[16px]">
|
|
135
|
-
<div className="overflow-hidden whitespace-nowrap text-ellipsis text-[15px]">
|
|
133
|
+
<div className="xpay-flex xpay-justify-between xpay-items-center xpay-gap-1 xpay-overflow-hidden xpay-grow">
|
|
134
|
+
<div className="xpay-flex xpay-flex-col xpay-leading-[16px]">
|
|
135
|
+
<div className="xpay-overflow-hidden xpay-whitespace-nowrap xpay-text-ellipsis xpay-text-[15px]">
|
|
136
136
|
{token.name}
|
|
137
137
|
</div>
|
|
138
|
-
<div className="text-[#888] text-[11px]">
|
|
138
|
+
<div className="xpay-text-[#888] xpay-text-[11px]">
|
|
139
139
|
{token.symbol}
|
|
140
140
|
</div>
|
|
141
141
|
</div>
|
|
@@ -144,7 +144,7 @@ export const TokenPicker: FC<Props> = ({
|
|
|
144
144
|
{balances &&
|
|
145
145
|
balances[token.address] &&
|
|
146
146
|
token.decimals ? (
|
|
147
|
-
<div className="text-xs">
|
|
147
|
+
<div className="xpay-text-xs">
|
|
148
148
|
{weiToHumanReadable({
|
|
149
149
|
amount:
|
|
150
150
|
balances[token.address]?.toString() || "0",
|
|
@@ -153,7 +153,7 @@ export const TokenPicker: FC<Props> = ({
|
|
|
153
153
|
})}
|
|
154
154
|
</div>
|
|
155
155
|
) : (
|
|
156
|
-
<Skeleton width="w-12" height="h-3" />
|
|
156
|
+
<Skeleton width="xpay-w-12" height="xpay-h-3" />
|
|
157
157
|
)}
|
|
158
158
|
</>
|
|
159
159
|
)}
|
|
@@ -161,14 +161,16 @@ export const TokenPicker: FC<Props> = ({
|
|
|
161
161
|
</div>
|
|
162
162
|
))
|
|
163
163
|
) : (
|
|
164
|
-
<div className="mt-4 flex justify-center">
|
|
165
|
-
<p className="text-sm text-white">
|
|
164
|
+
<div className="xpay-mt-4 xpay-flex xpay-justify-center">
|
|
165
|
+
<p className="xpay-text-sm xpay-text-white">
|
|
166
|
+
No tokens found.
|
|
167
|
+
</p>
|
|
166
168
|
</div>
|
|
167
169
|
)}
|
|
168
170
|
</div>
|
|
169
171
|
</div>
|
|
170
172
|
</div>,
|
|
171
|
-
|
|
173
|
+
xpayRoot,
|
|
172
174
|
)
|
|
173
175
|
: null;
|
|
174
176
|
};
|
|
@@ -18,30 +18,30 @@ export const TopBar: FC<Props> = ({
|
|
|
18
18
|
onClose,
|
|
19
19
|
}) => {
|
|
20
20
|
return (
|
|
21
|
-
<div className="flex w-full justify-between items-center mx-auto p-2">
|
|
22
|
-
<div className="rounded-lg text-[rgba(255,255,255,0.6)] text-xs sm:text-base whitespace-nowrap">
|
|
21
|
+
<div className="xpay-flex xpay-w-full xpay-justify-between xpay-items-center xpay-mx-auto xpay-p-2">
|
|
22
|
+
<div className="xpay-rounded-lg xpay-text-[rgba(255,255,255,0.6)] xpay-text-xs sm:xpay-text-base xpay-whitespace-nowrap">
|
|
23
23
|
{signer
|
|
24
24
|
? `Connected wallet: ${shortAddress(signer)}`
|
|
25
25
|
: `Wallet disconnected`}
|
|
26
26
|
</div>
|
|
27
|
-
<div className="flex gap-2 justify-center items-center">
|
|
27
|
+
<div className="xpay-flex xpay-gap-2 xpay-justify-center xpay-items-center">
|
|
28
28
|
{!historyTabShown && (
|
|
29
29
|
<div
|
|
30
30
|
onClick={() => setSettingsShown(true)}
|
|
31
|
-
className="flex items-center text-xs gap-1 cursor-pointer"
|
|
31
|
+
className="xpay-flex xpay-items-center xpay-text-xs xpay-gap-1 xpay-cursor-pointer"
|
|
32
32
|
>
|
|
33
|
-
<div className="w-7 h-7 rounded-full bg-gradient-to-r from-x_blue_light to-x_blue_dark flex items-center justify-center">
|
|
33
|
+
<div className="xpay-w-7 xpay-h-7 xpay-rounded-full xpay-bg-gradient-to-r xpay-from-x_blue_light xpay-to-x_blue_dark xpay-flex xpay-items-center xpay-justify-center">
|
|
34
34
|
<SettingsIcon />
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
37
37
|
)}
|
|
38
38
|
<div
|
|
39
39
|
onClick={() => setHistoryTabShown((x) => !x)}
|
|
40
|
-
className="flex items-center text-sm gap-1 cursor-pointer"
|
|
40
|
+
className="xpay-flex xpay-items-center xpay-text-sm xpay-gap-1 xpay-cursor-pointer"
|
|
41
41
|
>
|
|
42
42
|
{!historyTabShown ? (
|
|
43
43
|
<>
|
|
44
|
-
<div className="w-7 h-7 rounded-full bg-gradient-to-r from-x_blue_light to-x_blue_dark flex items-center justify-center">
|
|
44
|
+
<div className="xpay-w-7 xpay-h-7 xpay-rounded-full xpay-bg-gradient-to-r xpay-from-x_blue_light xpay-to-x_blue_dark xpay-flex xpay-items-center xpay-justify-center">
|
|
45
45
|
<HistoryIcon />
|
|
46
46
|
</div>
|
|
47
47
|
</>
|
|
@@ -49,7 +49,7 @@ export const TopBar: FC<Props> = ({
|
|
|
49
49
|
<div>Back</div>
|
|
50
50
|
)}
|
|
51
51
|
</div>
|
|
52
|
-
<div className="cursor-pointer text-white" onClick={onClose}>
|
|
52
|
+
<div className="xpay-cursor-pointer xpay-text-white" onClick={onClose}>
|
|
53
53
|
<CloseIcon />
|
|
54
54
|
</div>
|
|
55
55
|
</div>
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { Alert, Skeleton } from "@src/components";
|
|
2
2
|
import { Token, TokenPrices } from "@src/models";
|
|
3
|
-
import { FC, useEffect,
|
|
4
|
-
import { EXPRESS_DELIVERY_LIMIT } from "@src/constants";
|
|
3
|
+
import { FC, useEffect, useState } from "react";
|
|
5
4
|
|
|
6
5
|
type Props = {
|
|
7
6
|
prices: TokenPrices | undefined;
|
|
8
7
|
token: Token | undefined;
|
|
9
8
|
amount: string | undefined;
|
|
10
9
|
loading: boolean;
|
|
11
|
-
type: "src" | "dst";
|
|
12
|
-
setExceedsExpressDeliveryLimit?: (exceeds: boolean) => void;
|
|
13
10
|
};
|
|
14
11
|
|
|
15
12
|
export const UsdPrice: FC<Props> = ({
|
|
@@ -17,29 +14,22 @@ export const UsdPrice: FC<Props> = ({
|
|
|
17
14
|
token,
|
|
18
15
|
amount = "0",
|
|
19
16
|
loading = false,
|
|
20
|
-
type,
|
|
21
|
-
setExceedsExpressDeliveryLimit,
|
|
22
17
|
}) => {
|
|
23
18
|
const [usdPrice, setUsdPrice] = useState<string>("0.00");
|
|
24
19
|
|
|
25
20
|
useEffect(() => {
|
|
26
21
|
if (token && prices && prices[token.address]) {
|
|
27
22
|
const newUsdPrice = (
|
|
28
|
-
Number(amount) * Number(prices[token.address])
|
|
23
|
+
Number(amount) * Number(prices[token.address.toLowerCase()])
|
|
29
24
|
).toFixed(2);
|
|
30
|
-
if (type === "src" && setExceedsExpressDeliveryLimit) {
|
|
31
|
-
setExceedsExpressDeliveryLimit(
|
|
32
|
-
Number(newUsdPrice) > EXPRESS_DELIVERY_LIMIT,
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
25
|
setUsdPrice(newUsdPrice);
|
|
36
26
|
}
|
|
37
27
|
}, [token, prices, amount]);
|
|
38
28
|
|
|
39
29
|
return (
|
|
40
|
-
<div className="opacity-60
|
|
30
|
+
<div className="xpay-opacity-60">
|
|
41
31
|
{loading ? (
|
|
42
|
-
<Skeleton width="w-12" height="h-4" />
|
|
32
|
+
<Skeleton width="xpay-w-12" height="xpay-h-4" />
|
|
43
33
|
) : token ? (
|
|
44
34
|
prices && prices[token.address] ? (
|
|
45
35
|
<div>{`$${usdPrice}`}</div>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "@src/global.css";
|
|
2
|
-
import { MouseEvent, useMemo } from "react";
|
|
2
|
+
import { MouseEvent, useMemo, useState } from "react";
|
|
3
3
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
4
4
|
import { WagmiProvider } from "wagmi";
|
|
5
|
-
import { getWagmiConfig, waitForInitialization,
|
|
5
|
+
import { getWagmiConfig, waitForInitialization, xpayRoot } from "@src/config";
|
|
6
6
|
import { Chain, ContractCall, Route } from "@src/models";
|
|
7
7
|
import { PoweredBy } from "@src/components/TxConfigForm/PoweredBy";
|
|
8
8
|
import { Form } from "./Form";
|
|
@@ -14,6 +14,8 @@ export type TxConfigFormProps = {
|
|
|
14
14
|
customContractCalls: ContractCall[];
|
|
15
15
|
desc?: string;
|
|
16
16
|
supportedChains: Chain[];
|
|
17
|
+
retrunTransactions?: boolean;
|
|
18
|
+
dstDisplayTokenAddr?: string;
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
const TxConfigForm = ({
|
|
@@ -23,16 +25,19 @@ const TxConfigForm = ({
|
|
|
23
25
|
customContractCalls,
|
|
24
26
|
desc,
|
|
25
27
|
supportedChains,
|
|
28
|
+
retrunTransactions,
|
|
29
|
+
dstDisplayTokenAddr,
|
|
26
30
|
onSubmit,
|
|
27
31
|
onClose,
|
|
28
32
|
}: TxConfigFormProps & {
|
|
29
33
|
onSubmit: (route: Route) => void;
|
|
30
34
|
onClose: () => void;
|
|
31
35
|
}) => {
|
|
36
|
+
const [backdropClickDisabled, setBackdropClickDisabled] = useState(false);
|
|
32
37
|
const onBackdropClick = (e: MouseEvent) => {
|
|
33
38
|
e.stopPropagation();
|
|
34
39
|
e.nativeEvent.stopImmediatePropagation();
|
|
35
|
-
if (e.target === e.currentTarget) {
|
|
40
|
+
if (e.target === e.currentTarget && !backdropClickDisabled) {
|
|
36
41
|
onClose();
|
|
37
42
|
}
|
|
38
43
|
};
|
|
@@ -47,10 +52,10 @@ const TxConfigForm = ({
|
|
|
47
52
|
<WagmiProvider config={wagmiConfig}>
|
|
48
53
|
<QueryClientProvider client={queryClient}>
|
|
49
54
|
<div
|
|
50
|
-
className="top-0 left-0 right-0 bottom-0 bg-[rgba(0,0,0,0.8)] fixed flex items-center justify-center z-10"
|
|
51
|
-
|
|
55
|
+
className="xpay-top-0 xpay-left-0 xpay-right-0 xpay-bottom-0 xpay-bg-[rgba(0,0,0,0.8)] xpay-fixed xpay-flex xpay-items-center xpay-justify-center xpay-z-10"
|
|
56
|
+
onMouseDown={onBackdropClick}
|
|
52
57
|
>
|
|
53
|
-
<div className="relative bg-black rounded-3xl overflow-auto text-white border-2 border-solid border-[rgba(255,255,255,0.1)]">
|
|
58
|
+
<div className="xpay-relative xpay-bg-black xpay-rounded-3xl xpay-overflow-auto xpay-text-white xpay-border-2 xpay-border-solid xpay-border-[rgba(255,255,255,0.1)]">
|
|
54
59
|
<Form
|
|
55
60
|
integratorId={integratorId}
|
|
56
61
|
dstChainId={dstChainId}
|
|
@@ -58,8 +63,14 @@ const TxConfigForm = ({
|
|
|
58
63
|
customContractCalls={customContractCalls}
|
|
59
64
|
desc={desc}
|
|
60
65
|
supportedChains={supportedChains}
|
|
66
|
+
wagmiConfig={wagmiConfig}
|
|
67
|
+
retrunTransactions={!!retrunTransactions}
|
|
68
|
+
dstDisplayTokenAddr={dstDisplayTokenAddr}
|
|
61
69
|
onSubmit={onSubmit}
|
|
62
70
|
onClose={onClose}
|
|
71
|
+
setBackdropClickDisabled={(disabled: boolean) =>
|
|
72
|
+
setBackdropClickDisabled(disabled)
|
|
73
|
+
}
|
|
63
74
|
/>
|
|
64
75
|
<PoweredBy />
|
|
65
76
|
</div>
|
|
@@ -76,11 +87,19 @@ export const openTxConfigForm = async ({
|
|
|
76
87
|
customContractCalls,
|
|
77
88
|
desc,
|
|
78
89
|
supportedChains,
|
|
90
|
+
retrunTransactions,
|
|
91
|
+
dstDisplayTokenAddr,
|
|
79
92
|
}: TxConfigFormProps): Promise<Route> => {
|
|
80
93
|
try {
|
|
94
|
+
if (xpayRoot === null) {
|
|
95
|
+
throw new Error("XPay was incorrectly initialised");
|
|
96
|
+
}
|
|
81
97
|
return await new Promise(async (resolve) => {
|
|
82
98
|
await waitForInitialization();
|
|
83
|
-
|
|
99
|
+
if (xpayRoot === null) {
|
|
100
|
+
throw new Error("XPay was incorrectly initialised");
|
|
101
|
+
}
|
|
102
|
+
xpayRoot.render(
|
|
84
103
|
<TxConfigForm
|
|
85
104
|
integratorId={integratorId}
|
|
86
105
|
dstChainId={dstChainId}
|
|
@@ -88,12 +107,20 @@ export const openTxConfigForm = async ({
|
|
|
88
107
|
customContractCalls={customContractCalls}
|
|
89
108
|
desc={desc}
|
|
90
109
|
supportedChains={supportedChains}
|
|
110
|
+
retrunTransactions={retrunTransactions}
|
|
111
|
+
dstDisplayTokenAddr={dstDisplayTokenAddr}
|
|
91
112
|
onSubmit={(route) => {
|
|
92
|
-
|
|
93
|
-
|
|
113
|
+
if (retrunTransactions) {
|
|
114
|
+
resolve(route);
|
|
115
|
+
}
|
|
116
|
+
if (xpayRoot) {
|
|
117
|
+
xpayRoot.render("");
|
|
118
|
+
}
|
|
94
119
|
}}
|
|
95
120
|
onClose={() => {
|
|
96
|
-
|
|
121
|
+
if (xpayRoot) {
|
|
122
|
+
xpayRoot.render("");
|
|
123
|
+
}
|
|
97
124
|
}}
|
|
98
125
|
/>,
|
|
99
126
|
);
|
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
XSwapLogo,
|
|
9
9
|
} from "../icons";
|
|
10
10
|
import { MonitoredTransaction } from "@src/models";
|
|
11
|
-
import {
|
|
11
|
+
import { xpayTxStatusRoot } from "@src/config";
|
|
12
12
|
import { UnknownTokenLogo } from "../UnknownTokenLogo/UnknownTokenLogo";
|
|
13
|
+
import { Spinner } from "../Spinner";
|
|
13
14
|
|
|
14
15
|
interface Props {
|
|
15
16
|
transaction: MonitoredTransaction;
|
|
@@ -32,56 +33,60 @@ export const TxStatusButton: FC<Props> = ({ transaction }) => {
|
|
|
32
33
|
const background = useMemo(
|
|
33
34
|
() =>
|
|
34
35
|
isDone
|
|
35
|
-
? "bg-[rgba(2,2,2,1)] bg-gradient-to-r from-[rgba(76,175,80,0.2)] to-[rgba(46,125,50,0.2)]"
|
|
36
|
-
: "bg-[rgba(2,2,2,1)] bg-gradient-to-r from-[rgba(54,129,198,0.2)] to-[rgba(43,74,157,0.2)]",
|
|
36
|
+
? "xpay-bg-[rgba(2,2,2,1)] xpay-bg-gradient-to-r xpay-from-[rgba(76,175,80,0.2)] xpay-to-[rgba(46,125,50,0.2)]"
|
|
37
|
+
: "xpay-bg-[rgba(2,2,2,1)] xpay-bg-gradient-to-r xpay-from-[rgba(54,129,198,0.2)] xpay-to-[rgba(43,74,157,0.2)]",
|
|
37
38
|
[isDone],
|
|
38
39
|
);
|
|
39
40
|
|
|
40
41
|
const border = useMemo(
|
|
41
42
|
() =>
|
|
42
43
|
isDone
|
|
43
|
-
? "border border-solid border-x_green_dark"
|
|
44
|
-
: "border border-solid border-x_blue_dark",
|
|
44
|
+
? "xpay-border xpay-border-solid xpay-border-x_green_dark"
|
|
45
|
+
: "xpay-border xpay-border-solid xpay-border-x_blue_dark",
|
|
45
46
|
[isDone],
|
|
46
47
|
);
|
|
47
48
|
|
|
48
49
|
return (
|
|
49
|
-
<div
|
|
50
|
+
<div
|
|
51
|
+
className="xpay-flex xpay-gap-2"
|
|
52
|
+
onClick={() => setIsWide((x: boolean) => !x)}
|
|
53
|
+
>
|
|
50
54
|
{isWide && (
|
|
51
55
|
<div
|
|
52
|
-
className={`flex items-center min-w-60 sm:w-[520px] text-white h-14 px-4 text-sm gap-3 rounded-xl ${background} ${border}`}
|
|
56
|
+
className={`xpay-flex xpay-items-center xpay-min-w-60 sm:xpay-w-[520px] xpay-text-white xpay-h-14 xpay-px-4 xpay-text-sm xpay-gap-3 xpay-rounded-xl ${background} ${border}`}
|
|
53
57
|
>
|
|
54
|
-
<div className="flex justify-between flex-col gap-2 sm:gap-5 sm:flex-row w-full">
|
|
55
|
-
<div className="flex items-center gap-2">
|
|
58
|
+
<div className="xpay-flex xpay-justify-between xpay-flex-col xpay-gap-2 sm:xpay-gap-5 sm:xpay-flex-row xpay-w-full">
|
|
59
|
+
<div className="xpay-flex xpay-items-center xpay-gap-2">
|
|
56
60
|
<img
|
|
57
|
-
className="w-5 h-5"
|
|
61
|
+
className="xpay-w-5 xpay-h-5"
|
|
58
62
|
src={fromChainImage}
|
|
59
63
|
alt="source chain"
|
|
60
64
|
/>
|
|
61
65
|
<div>{fromChain}</div>
|
|
62
|
-
<div className="w-3.5 h-3.5">
|
|
66
|
+
<div className="xpay-w-3.5 xpay-h-3.5">
|
|
63
67
|
<ArrowRightIcon />
|
|
64
68
|
</div>
|
|
65
|
-
<img
|
|
69
|
+
<img
|
|
70
|
+
className="xpay-w-5 xpay-h-5"
|
|
71
|
+
src={toChainImage}
|
|
72
|
+
alt="to chain"
|
|
73
|
+
/>
|
|
66
74
|
<div>{toChain}</div>
|
|
67
75
|
</div>
|
|
68
76
|
|
|
69
|
-
<div className="flex items-center gap-2">
|
|
77
|
+
<div className="xpay-flex xpay-items-center xpay-gap-2">
|
|
70
78
|
<div>Sent</div>
|
|
71
79
|
<div>{fromAmount}</div>
|
|
72
80
|
<div>{fromToken}</div>
|
|
73
81
|
|
|
74
82
|
{fromTokenImage ? (
|
|
75
83
|
<img
|
|
76
|
-
className="w-5 h-5"
|
|
84
|
+
className="xpay-w-5 xpay-h-5"
|
|
77
85
|
src={fromTokenImage}
|
|
78
86
|
alt="source token"
|
|
79
87
|
/>
|
|
80
88
|
) : (
|
|
81
|
-
<UnknownTokenLogo
|
|
82
|
-
tokenName={"?"}
|
|
83
|
-
className="token-select__generated-logo"
|
|
84
|
-
/>
|
|
89
|
+
<UnknownTokenLogo tokenName={"?"} />
|
|
85
90
|
)}
|
|
86
91
|
</div>
|
|
87
92
|
</div>
|
|
@@ -89,32 +94,32 @@ export const TxStatusButton: FC<Props> = ({ transaction }) => {
|
|
|
89
94
|
href={explorer}
|
|
90
95
|
target="_blank"
|
|
91
96
|
rel="noreferrer"
|
|
92
|
-
className="no-underline cursor-pointer"
|
|
97
|
+
className="xpay-no-underline xpay-cursor-pointer"
|
|
93
98
|
aria-label="Show the transaction in the chain explorer"
|
|
94
99
|
>
|
|
95
|
-
<div className="w-3.5 h-3.5">
|
|
100
|
+
<div className="xpay-w-3.5 xpay-h-3.5">
|
|
96
101
|
<ArrowUpRightIcon />
|
|
97
102
|
</div>
|
|
98
103
|
</a>
|
|
99
104
|
</div>
|
|
100
105
|
)}
|
|
101
106
|
<div
|
|
102
|
-
className={`text-white rounded-xl cursor-pointer ${background} ${border}`}
|
|
107
|
+
className={`xpay-text-white xpay-rounded-xl xpay-cursor-pointer ${background} ${border}`}
|
|
103
108
|
>
|
|
104
|
-
<div className="flex gap-2 items-center h-14 w-20 justify-center">
|
|
109
|
+
<div className="xpay-flex xpay-gap-2 xpay-items-center xpay-h-14 xpay-w-20 xpay-justify-center">
|
|
105
110
|
<ArrowLeftIcon />
|
|
106
|
-
<div className="relative flex items-center justify-center w-9 h-9">
|
|
111
|
+
<div className="xpay-relative xpay-flex xpay-items-center xpay-justify-center xpay-w-9 xpay-h-9">
|
|
107
112
|
{isDone ? (
|
|
108
|
-
<div className="text-x_green w-5 h-5">
|
|
113
|
+
<div className="xpay-text-x_green xpay-w-5 xpay-h-5">
|
|
109
114
|
<CheckIcon />
|
|
110
115
|
</div>
|
|
111
116
|
) : (
|
|
112
117
|
<>
|
|
113
|
-
<div className="w-5 h-5">
|
|
118
|
+
<div className="xpay-w-5 xpay-h-5">
|
|
114
119
|
<XSwapLogo />
|
|
115
120
|
</div>
|
|
116
|
-
<div className="absolute flex items-center justify-center top-0 left-0 right-0 bottom-0 text-white">
|
|
117
|
-
<
|
|
121
|
+
<div className="xpay-absolute xpay-flex xpay-items-center xpay-justify-center xpay-top-0 xpay-left-0 xpay-right-0 xpay-bottom-0 xpay-text-white">
|
|
122
|
+
<Spinner width="10" height="10" />
|
|
118
123
|
</div>
|
|
119
124
|
</>
|
|
120
125
|
)}
|
|
@@ -154,5 +159,14 @@ export const renderTxStatusButtons = () => {
|
|
|
154
159
|
const buttons = renderedTransactions.map((item) => (
|
|
155
160
|
<TxStatusButton key={item.txHash} transaction={item} />
|
|
156
161
|
));
|
|
157
|
-
|
|
162
|
+
|
|
163
|
+
if (xpayTxStatusRoot === null) {
|
|
164
|
+
throw new Error("XPay was incorrectly initialised");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
xpayTxStatusRoot.render(
|
|
168
|
+
<div className="xpay-fixed xpay-bottom-5 xpay-right-2 xpay-flex xpay-flex-col xpay-gap-3 xpay-pl-9 xpay-items-end xpay-w-full xpay-z-50">
|
|
169
|
+
{buttons}
|
|
170
|
+
</div>,
|
|
171
|
+
);
|
|
158
172
|
};
|
|
@@ -6,7 +6,7 @@ interface Props {
|
|
|
6
6
|
export const UnknownTokenLogo = ({ tokenName, className }: Props) => {
|
|
7
7
|
return (
|
|
8
8
|
<div
|
|
9
|
-
className={`w-5 h-5 rounded-full bg-white shadow-sm flex items-center justify-center text-black ${className}`}
|
|
9
|
+
className={`xpay-w-5 xpay-h-5 xpay-rounded-full xpay-bg-white xpay-shadow-sm xpay-flex xpay-items-center xpay-justify-center xpay-text-black ${className}`}
|
|
10
10
|
>
|
|
11
11
|
{tokenName.substring(0, 1)}
|
|
12
12
|
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
@tailwind components;
|
|
3
3
|
@tailwind utilities;
|
|
4
4
|
|
|
5
|
-
.
|
|
5
|
+
.xpay {
|
|
6
6
|
/* Works on Firefox */
|
|
7
7
|
* {
|
|
8
8
|
scrollbar-width: thin;
|
|
@@ -23,12 +23,14 @@
|
|
|
23
23
|
background-color: #252525;
|
|
24
24
|
border-radius: 4px;
|
|
25
25
|
}
|
|
26
|
+
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
@layer components {
|
|
29
|
+
.xpay-global-border {
|
|
28
30
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
29
31
|
}
|
|
30
|
-
}
|
|
31
32
|
|
|
32
|
-
.
|
|
33
|
-
|
|
33
|
+
.xpay-tx-status {
|
|
34
|
+
@apply xpay-fixed xpay-bottom-5 xpay-right-2 xpay-flex xpay-flex-col xpay-gap-3 xpay-pl-9 xpay-items-end xpay-w-full xpay-z-50;
|
|
35
|
+
}
|
|
34
36
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const ErrorIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="81"
|
|
5
|
+
height="80"
|
|
6
|
+
viewBox="0 0 81 80"
|
|
7
|
+
fill="none"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
>
|
|
10
|
+
<g id="error_FILL0_wght400_GRAD0_opsz24 1">
|
|
11
|
+
<path
|
|
12
|
+
id="Vector"
|
|
13
|
+
d="M40.5001 56.6667C41.4445 56.6667 42.2362 56.3473 42.8751 55.7084C43.514 55.0695 43.8334 54.2779 43.8334 53.3334C43.8334 52.389 43.514 51.5973 42.8751 50.9584C42.2362 50.3195 41.4445 50.0001 40.5001 50.0001C39.5556 50.0001 38.764 50.3195 38.1251 50.9584C37.4862 51.5973 37.1667 52.389 37.1667 53.3334C37.1667 54.2779 37.4862 55.0695 38.1251 55.7084C38.764 56.3473 39.5556 56.6667 40.5001 56.6667ZM37.1667 43.3334H43.8334V23.3334H37.1667V43.3334ZM40.5001 73.3334C35.889 73.3334 31.5556 72.4584 27.5001 70.7084C23.4445 68.9584 19.9167 66.5834 16.9167 63.5834C13.9167 60.5834 11.5417 57.0556 9.79175 53.0001C8.04175 48.9445 7.16675 44.6112 7.16675 40.0001C7.16675 35.389 8.04175 31.0556 9.79175 27.0001C11.5417 22.9445 13.9167 19.4167 16.9167 16.4167C19.9167 13.4167 23.4445 11.0417 27.5001 9.29175C31.5556 7.54175 35.889 6.66675 40.5001 6.66675C45.1112 6.66675 49.4445 7.54175 53.5001 9.29175C57.5556 11.0417 61.0834 13.4167 64.0834 16.4167C67.0834 19.4167 69.4584 22.9445 71.2084 27.0001C72.9584 31.0556 73.8334 35.389 73.8334 40.0001C73.8334 44.6112 72.9584 48.9445 71.2084 53.0001C69.4584 57.0556 67.0834 60.5834 64.0834 63.5834C61.0834 66.5834 57.5556 68.9584 53.5001 70.7084C49.4445 72.4584 45.1112 73.3334 40.5001 73.3334ZM40.5001 66.6667C47.9445 66.6667 54.2501 64.0834 59.4167 58.9167C64.5834 53.7501 67.1667 47.4445 67.1667 40.0001C67.1667 32.5556 64.5834 26.2501 59.4167 21.0834C54.2501 15.9167 47.9445 13.3334 40.5001 13.3334C33.0556 13.3334 26.7501 15.9167 21.5834 21.0834C16.4167 26.2501 13.8334 32.5556 13.8334 40.0001C13.8334 47.4445 16.4167 53.7501 21.5834 58.9167C26.7501 64.0834 33.0556 66.6667 40.5001 66.6667Z"
|
|
14
|
+
fill="url(#paint0_linear_230_166)"
|
|
15
|
+
/>
|
|
16
|
+
</g>
|
|
17
|
+
<defs>
|
|
18
|
+
<linearGradient
|
|
19
|
+
id="paint0_linear_230_166"
|
|
20
|
+
x1="7.16675"
|
|
21
|
+
y1="6.66675"
|
|
22
|
+
x2="77.4571"
|
|
23
|
+
y2="10.7358"
|
|
24
|
+
gradientUnits="userSpaceOnUse"
|
|
25
|
+
>
|
|
26
|
+
<stop stopColor="#F44336" />
|
|
27
|
+
<stop offset="1" stopColor="#C62828" />
|
|
28
|
+
</linearGradient>
|
|
29
|
+
</defs>
|
|
30
|
+
</svg>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const SignIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="24"
|
|
5
|
+
height="24"
|
|
6
|
+
viewBox="0 0 24 24"
|
|
7
|
+
fill="none"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
>
|
|
10
|
+
<path
|
|
11
|
+
d="M4 21V16.75L17.175 3.6C17.375 3.4 17.6 3.25 17.85 3.15C18.1 3.05 18.35 3 18.6 3C18.8667 3 19.1208 3.05 19.3625 3.15C19.6042 3.25 19.8167 3.4 20 3.6L21.4 5C21.6 5.18333 21.75 5.39583 21.85 5.6375C21.95 5.87917 22 6.13333 22 6.4C22 6.65 21.95 6.9 21.85 7.15C21.75 7.4 21.6 7.625 21.4 7.825L8.25 21H4ZM6 19H7.4L17.225 9.2L16.525 8.475L15.8 7.775L6 17.6V19ZM16.525 8.475L15.8 7.775L17.225 9.2L16.525 8.475ZM14 21C15.2333 21 16.375 20.6917 17.425 20.075C18.475 19.4583 19 18.6 19 17.5C19 16.9 18.8417 16.3833 18.525 15.95C18.2083 15.5167 17.7833 15.1417 17.25 14.825L15.775 16.3C16.1583 16.4667 16.4583 16.65 16.675 16.85C16.8917 17.05 17 17.2667 17 17.5C17 17.8833 16.6958 18.2292 16.0875 18.5375C15.4792 18.8458 14.7833 19 14 19C13.7167 19 13.4792 19.0958 13.2875 19.2875C13.0958 19.4792 13 19.7167 13 20C13 20.2833 13.0958 20.5208 13.2875 20.7125C13.4792 20.9042 13.7167 21 14 21ZM4.575 13.35L6.075 11.85C5.74167 11.7167 5.47917 11.5792 5.2875 11.4375C5.09583 11.2958 5 11.15 5 11C5 10.8 5.15 10.6 5.45 10.4C5.75 10.2 6.38333 9.89167 7.35 9.475C8.81667 8.84167 9.79167 8.26667 10.275 7.75C10.7583 7.23333 11 6.65 11 6C11 5.08333 10.6333 4.35417 9.9 3.8125C9.16667 3.27083 8.2 3 7 3C6.25 3 5.57917 3.13333 4.9875 3.4C4.39583 3.66667 3.94167 3.99167 3.625 4.375C3.44167 4.59167 3.36667 4.83333 3.4 5.1C3.43333 5.36667 3.55833 5.58333 3.775 5.75C3.99167 5.93333 4.23333 6.00833 4.5 5.975C4.76667 5.94167 4.99167 5.83333 5.175 5.65C5.40833 5.41667 5.66667 5.25 5.95 5.15C6.23333 5.05 6.58333 5 7 5C7.68333 5 8.1875 5.1 8.5125 5.3C8.8375 5.5 9 5.73333 9 6C9 6.23333 8.85417 6.44583 8.5625 6.6375C8.27083 6.82917 7.6 7.16667 6.55 7.65C5.21667 8.23333 4.29167 8.7625 3.775 9.2375C3.25833 9.7125 3 10.3 3 11C3 11.5333 3.14167 11.9875 3.425 12.3625C3.70833 12.7375 4.09167 13.0667 4.575 13.35Z"
|
|
12
|
+
fill="white"
|
|
13
|
+
fillOpacity="0.5"
|
|
14
|
+
/>
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const SuccessIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="86"
|
|
5
|
+
height="85"
|
|
6
|
+
viewBox="0 0 86 85"
|
|
7
|
+
fill="none"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
>
|
|
10
|
+
<path
|
|
11
|
+
d="M38.0416 58.7916L63.0103 33.8228L58.052 28.8645L38.0416 48.8749L27.9478 38.7812L22.9895 43.7395L38.0416 58.7916ZM42.9999 77.9166C38.1006 77.9166 33.4964 76.9869 29.1874 75.1275C24.8784 73.2681 21.1301 70.7447 17.9426 67.5572C14.7551 64.3697 12.2317 60.6214 10.3723 56.3124C8.51294 52.0034 7.58325 47.3992 7.58325 42.4999C7.58325 37.6006 8.51294 32.9964 10.3723 28.6874C12.2317 24.3784 14.7551 20.6301 17.9426 17.4426C21.1301 14.2551 24.8784 11.7317 29.1874 9.87231C33.4964 8.01294 38.1006 7.08325 42.9999 7.08325C47.8992 7.08325 52.5034 8.01294 56.8124 9.87231C61.1214 11.7317 64.8697 14.2551 68.0572 17.4426C71.2447 20.6301 73.7681 24.3784 75.6275 28.6874C77.4869 32.9964 78.4166 37.6006 78.4166 42.4999C78.4166 47.3992 77.4869 52.0034 75.6275 56.3124C73.7681 60.6214 71.2447 64.3697 68.0572 67.5572C64.8697 70.7447 61.1214 73.2681 56.8124 75.1275C52.5034 76.9869 47.8992 77.9166 42.9999 77.9166ZM42.9999 70.8332C50.9096 70.8332 57.6093 68.0885 63.0989 62.5989C68.5885 57.1093 71.3332 50.4096 71.3332 42.4999C71.3332 34.5902 68.5885 27.8905 63.0989 22.401C57.6093 16.9114 50.9096 14.1666 42.9999 14.1666C35.0902 14.1666 28.3905 16.9114 22.901 22.401C17.4114 27.8905 14.6666 34.5902 14.6666 42.4999C14.6666 50.4096 17.4114 57.1093 22.901 62.5989C28.3905 68.0885 35.0902 70.8332 42.9999 70.8332Z"
|
|
12
|
+
fill="url(#paint0_linear_1287_1610)"
|
|
13
|
+
/>
|
|
14
|
+
<defs>
|
|
15
|
+
<linearGradient
|
|
16
|
+
id="paint0_linear_1287_1610"
|
|
17
|
+
x1="7.58325"
|
|
18
|
+
y1="7.08325"
|
|
19
|
+
x2="82.2668"
|
|
20
|
+
y2="11.4066"
|
|
21
|
+
gradientUnits="userSpaceOnUse"
|
|
22
|
+
>
|
|
23
|
+
<stop stopColor="#4CAF50" />
|
|
24
|
+
<stop offset="1" stopColor="#2E7D32" />
|
|
25
|
+
</linearGradient>
|
|
26
|
+
</defs>
|
|
27
|
+
</svg>
|
|
28
|
+
);
|
|
29
|
+
};
|