@xswap-link/sdk 0.2.3 → 0.2.5
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/CHANGELOG.md +17 -0
- package/dist/index.css +20 -21
- package/dist/index.d.mts +24 -13
- package/dist/index.d.ts +24 -13
- package/dist/index.js +747 -527
- package/dist/index.mjs +645 -425
- package/package.json +1 -1
- package/src/components/TxConfigForm/FeesDetails.tsx +55 -37
- package/src/components/TxConfigForm/Form.tsx +30 -7
- package/src/components/TxConfigForm/History.tsx +31 -4
- package/src/components/TxConfigForm/Summary.tsx +28 -11
- package/src/components/TxConfigForm/SwapPanel.tsx +5 -1
- package/src/components/TxConfigForm/TopBar.tsx +8 -3
- package/src/components/TxConfigForm/UsdPrice.tsx +14 -1
- package/src/components/TxConfigForm/index.tsx +10 -10
- package/src/components/TxStatusButton/index.tsx +21 -13
- package/src/components/UnknownTokenLogo/UnknownTokenLogo.tsx +14 -0
- package/src/components/WaitingForInit/index.tsx +20 -0
- package/src/components/global.css +1 -1
- package/src/config/init.tsx +75 -0
- package/src/constants/index.ts +1 -0
- package/src/index.ts +9 -1
- package/src/models/Route.ts +6 -2
- package/src/models/TokenData.ts +1 -1
- package/src/models/TransactionHistory.ts +12 -8
- package/src/models/payloads/GetRoutePayload.ts +1 -0
- package/src/models/payloads/GetSwapTxPayload.ts +1 -0
- package/src/services/api.ts +9 -0
- package/src/services/blockchain.ts +49 -0
- package/src/services/index.ts +1 -0
- package/src/services/integrations/monitoring.ts +34 -14
- package/src/services/integrations/transactions.ts +8 -3
- package/src/utils/contracts.ts +2 -4
- package/src/utils/strings.ts +4 -0
- package/tailwind.config.js +2 -2
- package/src/config/init.ts +0 -35
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createRoot, Root } from "react-dom/client";
|
|
2
|
+
import xswapConfig from "../../xswap.config";
|
|
3
|
+
import { version } from "../../package.json";
|
|
4
|
+
import { WaitingForInit } from "@src/components/WaitingForInit";
|
|
5
|
+
|
|
6
|
+
export let initIndicatorRoot: Root;
|
|
7
|
+
export let xswapRoot: Root;
|
|
8
|
+
export let txStatusRoot: Root;
|
|
9
|
+
|
|
10
|
+
let initCompleted = false;
|
|
11
|
+
|
|
12
|
+
export const initDocument = () => {
|
|
13
|
+
if (typeof document === "undefined") {
|
|
14
|
+
throw new Error("Can't render XPay components from server side.");
|
|
15
|
+
}
|
|
16
|
+
createInitIndicatorRoot();
|
|
17
|
+
|
|
18
|
+
Promise.all([
|
|
19
|
+
createStyleElement(),
|
|
20
|
+
createXSwapRoot(),
|
|
21
|
+
createTxStatusRoot(),
|
|
22
|
+
]).then(() => {
|
|
23
|
+
initCompleted = true;
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const waitForInitialization = async () => {
|
|
28
|
+
if (!initCompleted) {
|
|
29
|
+
displayInitIndicator(true);
|
|
30
|
+
while (!initCompleted) {
|
|
31
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
32
|
+
}
|
|
33
|
+
displayInitIndicator(false);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const createStyleElement = async () => {
|
|
38
|
+
const css = await fetch(
|
|
39
|
+
`${xswapConfig.apiUrl}/sdk/css?${new URLSearchParams({
|
|
40
|
+
data: JSON.stringify({ version }),
|
|
41
|
+
})}`,
|
|
42
|
+
);
|
|
43
|
+
const text = await css.text();
|
|
44
|
+
const style = document.createElement("style");
|
|
45
|
+
style.textContent = text;
|
|
46
|
+
document.body.appendChild(style);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const createXSwapRoot = async () => {
|
|
50
|
+
const xswapElement = document.createElement("div");
|
|
51
|
+
xswapElement.setAttribute("id", "xswap-modal");
|
|
52
|
+
document.body.appendChild(xswapElement);
|
|
53
|
+
xswapRoot = createRoot(xswapElement);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const createTxStatusRoot = async () => {
|
|
57
|
+
const txStatusElement = document.createElement("div");
|
|
58
|
+
txStatusElement.setAttribute("id", "xswap-tx-status");
|
|
59
|
+
txStatusElement.setAttribute("class", "xswap-tx-status");
|
|
60
|
+
document.body.appendChild(txStatusElement);
|
|
61
|
+
txStatusRoot = createRoot(txStatusElement);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const createInitIndicatorRoot = () => {
|
|
65
|
+
const initIndicatorElement = document.createElement("div");
|
|
66
|
+
initIndicatorElement.setAttribute("id", "xswap-init-indicator");
|
|
67
|
+
document.body.appendChild(initIndicatorElement);
|
|
68
|
+
initIndicatorRoot = createRoot(initIndicatorElement);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const displayInitIndicator = (display: boolean) => {
|
|
72
|
+
display
|
|
73
|
+
? initIndicatorRoot.render(<WaitingForInit />)
|
|
74
|
+
: initIndicatorRoot.render("");
|
|
75
|
+
};
|
package/src/constants/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export const NUMBER_INPUT_REGEX = /^[0-9]*[.,]?[0-9]*$/;
|
|
|
5
5
|
export const SLIPPAGE_PRESETS: number[] = [0.5, 1.5, 3.0];
|
|
6
6
|
export const DELIVERY_TIME = 30 * 1000 * 60;
|
|
7
7
|
export const EXPRESS_DELIVERY_TIME = 30 * 1000;
|
|
8
|
+
export const EXPRESS_DELIVERY_LIMIT = 1000; // USD
|
|
8
9
|
export const BALANCES_CHUNK_SIZE = 500;
|
|
9
10
|
export const ROUTE_TIMEOUT_MS = 5000;
|
|
10
11
|
export const MINIMUM_DISPLAYED_TOKEN_AMOUNT = 0.0001;
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { initDocument } from "@src/config/init";
|
|
2
|
+
import { openTransactionModal, renderTxStatus } from "@src/services";
|
|
2
3
|
|
|
3
4
|
initDocument();
|
|
4
5
|
|
|
5
6
|
export * from "@src/models";
|
|
6
|
-
export {
|
|
7
|
+
export { openTransactionModal, renderTxStatus } from "@src/services";
|
|
8
|
+
|
|
9
|
+
const XPay = {
|
|
10
|
+
openTransactionModal,
|
|
11
|
+
renderTxStatus,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default XPay;
|
package/src/models/Route.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { TransactionRequest } from "@ethersproject/providers";
|
|
2
|
-
|
|
3
1
|
export type Route = {
|
|
4
2
|
estAmountOut: string;
|
|
5
3
|
minAmountOut: string;
|
|
@@ -13,6 +11,12 @@ export type Transactions = {
|
|
|
13
11
|
swap: TransactionRequest;
|
|
14
12
|
};
|
|
15
13
|
|
|
14
|
+
export type TransactionRequest = {
|
|
15
|
+
to: string;
|
|
16
|
+
data: string;
|
|
17
|
+
value: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
16
20
|
export type XSwapFees = {
|
|
17
21
|
ccipFee?: string;
|
|
18
22
|
xSwapFee: XSwapFee;
|
package/src/models/TokenData.ts
CHANGED
|
@@ -34,7 +34,11 @@ export type HistoryTransaction = {
|
|
|
34
34
|
} | null;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
export type TransactionStatus =
|
|
37
|
+
export type TransactionStatus =
|
|
38
|
+
| "IN_PROGRESS"
|
|
39
|
+
| "DONE"
|
|
40
|
+
| "REVERTED"
|
|
41
|
+
| "NOT_FOUND";
|
|
38
42
|
|
|
39
43
|
export type Transaction = {
|
|
40
44
|
hash: string;
|
|
@@ -50,13 +54,13 @@ export type Transaction = {
|
|
|
50
54
|
};
|
|
51
55
|
|
|
52
56
|
export type MonitoredTransaction = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
srcChain: string | undefined;
|
|
58
|
+
srcChainImage: string | undefined;
|
|
59
|
+
srcToken: string | undefined;
|
|
60
|
+
srcTokenImage: string | undefined;
|
|
61
|
+
srcAmount: string | undefined;
|
|
62
|
+
dstChain: string | undefined;
|
|
63
|
+
dstChainImage: string | undefined;
|
|
60
64
|
isDone: boolean;
|
|
61
65
|
txHash: string;
|
|
62
66
|
explorer: string | undefined;
|
package/src/services/api.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
Token,
|
|
10
10
|
TokenPrices,
|
|
11
11
|
TransactionHistory,
|
|
12
|
+
TransactionStatus,
|
|
12
13
|
} from "@src/models";
|
|
13
14
|
|
|
14
15
|
async function _sendRequest<T>(
|
|
@@ -110,3 +111,11 @@ export async function getPrices(payload: GetPricesPayload) {
|
|
|
110
111
|
})}`,
|
|
111
112
|
);
|
|
112
113
|
}
|
|
114
|
+
|
|
115
|
+
export async function getTxStatus(payload: { transferId: string }) {
|
|
116
|
+
return await _sendRequest<{ status: TransactionStatus }>(
|
|
117
|
+
`/getTxStatus?${new URLSearchParams({
|
|
118
|
+
data: JSON.stringify(payload), // todo remove once gcp starts working
|
|
119
|
+
})}`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ERC20Abi } from "@src/contracts";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
import { Chain, Token } from "@src/models";
|
|
4
|
+
|
|
5
|
+
export const getCustomTokenData = async (
|
|
6
|
+
chain: Chain,
|
|
7
|
+
tokenAddress: string,
|
|
8
|
+
): Promise<Token> => {
|
|
9
|
+
const rpcUrl = chain.publicRpcUrls[0];
|
|
10
|
+
|
|
11
|
+
const customTokenContract = new ethers.Contract(
|
|
12
|
+
tokenAddress.toLowerCase(),
|
|
13
|
+
ERC20Abi,
|
|
14
|
+
ethers.getDefaultProvider(rpcUrl),
|
|
15
|
+
);
|
|
16
|
+
try {
|
|
17
|
+
const query = async (
|
|
18
|
+
contract: ethers.Contract | null,
|
|
19
|
+
method: string,
|
|
20
|
+
args: unknown[] = [],
|
|
21
|
+
) => {
|
|
22
|
+
if (contract?.provider) {
|
|
23
|
+
const response = await contract[method](...args);
|
|
24
|
+
return response;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
const responses = await Promise.all([
|
|
29
|
+
query(customTokenContract, "decimals"),
|
|
30
|
+
query(customTokenContract, "name"),
|
|
31
|
+
query(customTokenContract, "symbol"),
|
|
32
|
+
]);
|
|
33
|
+
const [decimals, name, symbol] = responses;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
address: tokenAddress,
|
|
37
|
+
symbol: symbol,
|
|
38
|
+
name: name,
|
|
39
|
+
decimals: decimals,
|
|
40
|
+
quickPick: false,
|
|
41
|
+
supported: true,
|
|
42
|
+
priority: 0,
|
|
43
|
+
};
|
|
44
|
+
} catch (_) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Provided token ${tokenAddress} does not exists on chain ${chain.displayName}`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
};
|
package/src/services/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Chain, MonitoredTransaction, Web3Environment } from "@src/models";
|
|
2
|
-
import { getChains } from "@src/services";
|
|
2
|
+
import { getChains, getTxStatus } from "@src/services";
|
|
3
3
|
import { ethers } from "ethers";
|
|
4
4
|
import { weiToHumanReadable } from "@src/utils";
|
|
5
5
|
import {
|
|
@@ -18,8 +18,9 @@ import {
|
|
|
18
18
|
} from "@src/components";
|
|
19
19
|
import { ADDRESSES } from "@src/contracts";
|
|
20
20
|
import retry from "async-retry";
|
|
21
|
+
import { getCustomTokenData } from "../blockchain";
|
|
21
22
|
|
|
22
|
-
export const
|
|
23
|
+
export const renderTxStatus = async (
|
|
23
24
|
txChainId: string,
|
|
24
25
|
txHash: string,
|
|
25
26
|
): Promise<void> => {
|
|
@@ -52,11 +53,20 @@ export const monitorTransactionStatus = async (
|
|
|
52
53
|
const srcChain = supportedChains.find(
|
|
53
54
|
(chain) => chain.chainId === txChainId.toString(),
|
|
54
55
|
);
|
|
56
|
+
if (!srcChain) {
|
|
57
|
+
throw new Error(`Unknown src chain ${txChainId}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const tokenAddress = decodedMessageSentEvent.args?.token.toString();
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
let srcToken = srcChain.tokens.find(
|
|
57
63
|
(token) => token.address === decodedMessageSentEvent.args?.token.toString(),
|
|
58
64
|
);
|
|
59
65
|
|
|
66
|
+
if (!srcToken) {
|
|
67
|
+
srcToken = await getCustomTokenData(srcChain, tokenAddress);
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
const dstChain = supportedChains.find(
|
|
61
71
|
(chain) =>
|
|
62
72
|
chain.ccipChainId ===
|
|
@@ -69,19 +79,21 @@ export const monitorTransactionStatus = async (
|
|
|
69
79
|
throw new Error(`Unknown destination chain!`);
|
|
70
80
|
}
|
|
71
81
|
|
|
82
|
+
const tokenAmount = weiToHumanReadable({
|
|
83
|
+
amount: decodedMessageSentEvent.args?.tokenAmount.toString(),
|
|
84
|
+
decimals: srcToken?.decimals || 18,
|
|
85
|
+
precisionFractionalPlaces: 4,
|
|
86
|
+
});
|
|
87
|
+
|
|
72
88
|
const transaction: MonitoredTransaction = {
|
|
73
89
|
txHash: txHash,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
amount: decodedMessageSentEvent.args?.tokenAmount.toString(),
|
|
82
|
-
decimals: srcToken?.decimals || 18,
|
|
83
|
-
precisionFractionalPlaces: 4,
|
|
84
|
-
}),
|
|
90
|
+
srcChain: srcChain.displayName,
|
|
91
|
+
srcChainImage: srcChain.image,
|
|
92
|
+
dstChain: dstChain.displayName,
|
|
93
|
+
dstChainImage: dstChain.image,
|
|
94
|
+
srcToken: srcToken?.symbol,
|
|
95
|
+
srcTokenImage: srcToken?.image,
|
|
96
|
+
srcAmount: tokenAmount === "0" ? "<0.0001" : tokenAmount,
|
|
85
97
|
isDone: false,
|
|
86
98
|
explorer: `${CCIP_EXPLORER}/tx/${txHash}`,
|
|
87
99
|
};
|
|
@@ -120,6 +132,14 @@ export const monitorTransactionStatus = async (
|
|
|
120
132
|
removeTransactionFromRenderedTransactions(txHash);
|
|
121
133
|
renderTxStatusButtons();
|
|
122
134
|
};
|
|
135
|
+
|
|
136
|
+
// check tx status on backend
|
|
137
|
+
getTxStatus({ transferId: messageId }).then((response) => {
|
|
138
|
+
if (response.status === "DONE") {
|
|
139
|
+
onSuccess();
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
123
143
|
xSwapRouterOnDestination.once(msgReceivedEventFilter, () => onSuccess());
|
|
124
144
|
};
|
|
125
145
|
|
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
} from "@src/models";
|
|
7
7
|
import { getChains } from "@src/services";
|
|
8
8
|
import { openTxConfigForm } from "@src/components";
|
|
9
|
+
import { isETHAddressValid } from "@src/utils";
|
|
9
10
|
|
|
10
|
-
export const
|
|
11
|
+
export const openTransactionModal = async ({
|
|
12
|
+
integratorId,
|
|
11
13
|
dstChain,
|
|
12
14
|
dstToken,
|
|
13
15
|
customContractCalls = [],
|
|
@@ -21,6 +23,7 @@ export const getSwapTx = async ({
|
|
|
21
23
|
validateSwapTxData(supportedChains, dstChain, dstToken);
|
|
22
24
|
|
|
23
25
|
const route = await openTxConfigForm({
|
|
26
|
+
integratorId: integratorId,
|
|
24
27
|
dstChainId: dstChain,
|
|
25
28
|
dstTokenAddr: dstToken,
|
|
26
29
|
customContractCalls,
|
|
@@ -43,7 +46,9 @@ const validateSwapTxData = (
|
|
|
43
46
|
throw new Error(`Provided chain '${dstChain}' is not supported`);
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
if (!
|
|
47
|
-
throw new Error(
|
|
49
|
+
if (!isETHAddressValid(dstToken)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Provided token address ${dstToken} on chain ${dstChain} is not correct`,
|
|
52
|
+
);
|
|
48
53
|
}
|
|
49
54
|
};
|
package/src/utils/contracts.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { BigNumber, BigNumberish, ethers } from "ethers";
|
|
2
|
-
import { TransactionRequest } from "@ethersproject/providers";
|
|
3
|
-
import { safeBigNumberFrom } from "./numbers";
|
|
4
2
|
import { ADDRESSES, BatchQueryAbi, ERC20Abi } from "@src/contracts";
|
|
5
|
-
import { Chain, TokenBalances } from "@src/models";
|
|
3
|
+
import { Chain, TokenBalances, TransactionRequest } from "@src/models";
|
|
6
4
|
import { chunkArray } from "@src/utils";
|
|
7
5
|
import { BALANCES_CHUNK_SIZE } from "@src/constants";
|
|
8
6
|
|
|
@@ -104,7 +102,7 @@ export const generateApproveTxData = (
|
|
|
104
102
|
return tokenAddress !== ethers.constants.AddressZero
|
|
105
103
|
? {
|
|
106
104
|
to: tokenAddress,
|
|
107
|
-
value:
|
|
105
|
+
value: "0",
|
|
108
106
|
data: IERC20.encodeFunctionData("approve", [spender, amount]),
|
|
109
107
|
}
|
|
110
108
|
: undefined;
|
package/src/utils/strings.ts
CHANGED
package/tailwind.config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** @type {import("tailwindcss").Config} */
|
|
2
|
-
|
|
2
|
+
export default {
|
|
3
3
|
content: ["./src/components/**/*.{js,ts,jsx,tsx,mdx}"],
|
|
4
4
|
theme: {
|
|
5
5
|
extend: {
|
|
@@ -18,7 +18,7 @@ module.exports = {
|
|
|
18
18
|
},
|
|
19
19
|
width: {
|
|
20
20
|
x_desktop: "600px",
|
|
21
|
-
x_mobile: "
|
|
21
|
+
x_mobile: "360px",
|
|
22
22
|
},
|
|
23
23
|
animation: {
|
|
24
24
|
"spin-slow": "spin 3s linear infinite",
|
package/src/config/init.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { createRoot, Root } from "react-dom/client";
|
|
2
|
-
import xswapConfig from "../../xswap.config";
|
|
3
|
-
import { version } from "../../package.json";
|
|
4
|
-
|
|
5
|
-
export let xswapRoot: Root;
|
|
6
|
-
export let txStatusRoot: Root;
|
|
7
|
-
|
|
8
|
-
export const initDocument = () => {
|
|
9
|
-
if (typeof document === "undefined") {
|
|
10
|
-
throw new Error("Can't render XPay components from server side.");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const cssRequestParams = new URLSearchParams({
|
|
14
|
-
data: JSON.stringify({ version }),
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
fetch(`${xswapConfig.apiUrl}/sdk/css?${cssRequestParams}`).then(
|
|
18
|
-
async (css) => {
|
|
19
|
-
const style = document.createElement("style");
|
|
20
|
-
style.textContent = await css.text();
|
|
21
|
-
document.body.appendChild(style);
|
|
22
|
-
},
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
const xswapElement = document.createElement("div");
|
|
26
|
-
xswapElement.setAttribute("id", "xswap-modal");
|
|
27
|
-
document.body.appendChild(xswapElement);
|
|
28
|
-
xswapRoot = createRoot(xswapElement);
|
|
29
|
-
|
|
30
|
-
const txStatusElement = document.createElement("div");
|
|
31
|
-
txStatusElement.setAttribute("id", "xswap-tx-status");
|
|
32
|
-
txStatusElement.setAttribute("class", "xswap-tx-status");
|
|
33
|
-
document.body.appendChild(txStatusElement);
|
|
34
|
-
txStatusRoot = createRoot(txStatusElement);
|
|
35
|
-
};
|