diff --git a/examples/swaps-api-nextjs-bitcoin/src/components/Swap.tsx b/examples/swaps-api-nextjs-bitcoin/src/components/Swap.tsx index 33202ea..aa9f444 100644 --- a/examples/swaps-api-nextjs-bitcoin/src/components/Swap.tsx +++ b/examples/swaps-api-nextjs-bitcoin/src/components/Swap.tsx @@ -113,15 +113,15 @@ const Swap = () => { }, [recipientAddress]); useEffect(() => { - if(!xDefiConfig?.isInstalled!()) { - console.log('not installed') + if (!xDefiConfig?.isInstalled!()) { + console.log("not installed"); toast({ variant: "destructive", title: "xDefi Wallet not installed", description: "Please install xDefi wallet in your browser", }); } - }, []) + }, []); async function connectWallet(chainId?: number) { try { @@ -147,20 +147,21 @@ const Swap = () => { } } - async function getTransStatus(transId: string, txHash: string): Promise { + async function getTransStatus( + transId: string, + txHash: string, + ): Promise { try { - const transactionStatus = await getTransationStatus({ id: transId, txHash, }); - + setTransStatus(transactionStatus); - - return transactionStatus!; - } catch(e) { - return { status: 'Submitted' } + return transactionStatus!; + } catch (e) { + return { status: "Submitted" }; } } @@ -214,7 +215,6 @@ const Swap = () => { try { // Get a quote from the Swing API const quotes = await getQuoteRequest({ - fromChain: transferParams.fromChain, fromTokenAddress: transferParams.fromTokenAddress, fromUserAddress: transferParams.fromUserAddress, @@ -312,13 +312,13 @@ const Swap = () => { let txResponse; - if (transfer?.tx.meta) { + if (transfer?.tx.meta) { // For Bitcoin to ETH, the send endpoint will return an object called `meta` - + const { from, recipient, amount, memo } = transfer.tx.meta; // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window.xfi as any)?.bitcoin.request( + (window.xfi as any)?.bitcoin.request( // Here, we're prompting a users wallet using xDEFI injected SDK { method: "transfer", @@ -335,20 +335,20 @@ const Swap = () => { (error: any, result: any) => { console.log(error, result); - if(error) { + if (error) { toast({ variant: "destructive", title: "Something went wrong!", description: "Swap error, please check your balance or swap config", }); - + setIsLoading(false); - setTransStatus(null) + setTransStatus(null); } - txResponse = result + txResponse = result; pollTransactionStatus(transfer.id.toString(), txResponse); - console.log(txResponse) + console.log(txResponse); }, ); } else { @@ -357,7 +357,6 @@ const Swap = () => { const receipt = await txResponse?.wait(); console.log("Transaction receipt:", receipt); } - // Wait for the transaction to be mined } catch (error) { @@ -418,7 +417,10 @@ const Swap = () => {
- switchTransferParams()} /> + switchTransferParams()} + />
diff --git a/examples/swaps-api-nextjs-solana/package.json b/examples/swaps-api-nextjs-solana/package.json index 43d2795..25e4744 100644 --- a/examples/swaps-api-nextjs-solana/package.json +++ b/examples/swaps-api-nextjs-solana/package.json @@ -27,7 +27,7 @@ "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-toast": "^1.1.5", "@solana/web3.js": "^1.91.8", - "@swing.xyz/sdk": "^0.54.0", + "@swing.xyz/sdk": "^0.57.5", "@tailwindcss/forms": "^0.5.7", "@thirdweb-dev/react": "^4.4.17", "@thirdweb-dev/sdk": "^4.0.44", diff --git a/examples/swaps-api-nextjs-tron/.eslintrc.js b/examples/swaps-api-nextjs-tron/.eslintrc.js new file mode 100644 index 0000000..ea43de2 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/.eslintrc.js @@ -0,0 +1,8 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + extends: ["eslint-config-examples/index.js"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + }, +}; diff --git a/examples/swaps-api-nextjs-tron/.gitignore b/examples/swaps-api-nextjs-tron/.gitignore new file mode 100644 index 0000000..55175ef --- /dev/null +++ b/examples/swaps-api-nextjs-tron/.gitignore @@ -0,0 +1,32 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel diff --git a/examples/swaps-api-nextjs-tron/README.md b/examples/swaps-api-nextjs-tron/README.md new file mode 100644 index 0000000..6eeb410 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/README.md @@ -0,0 +1,633 @@ +# Cross-chain Swaps using the Swing API in Next.js For Solana + +This example is built with: + +- [@swing.xyz/sdk](https://developers.swing.xyz/reference/sdk) +- [@thirdweb-dev/react](https://portal.thirdweb.com/react) +- [@thirdweb-dev/sdk](https://portal.thirdweb.com/typescript) +- [@solana/web3.js](https://www.npmjs.com/package/@solana/web3.js) +- [Next.js App Router](https://nextjs.org) +- [Tailwind CSS](https://tailwindcss.com) + +## Demo + +View the live demo [https://swaps-api-nextjs-solana.vercel.app](https://swaps-api-nextjs-solana.vercel.app/) + +## Swing Integration + +> The implementation of Swing's [Cross-chain API](https://developers.swing.xyz/reference/api) and [Platform API](https://developers.swing.xyz/reference/api/platform/a2glq2e1w44ad-project-configuration) can be found in [src/components/Swap.tsx](./src/components/Swap.tsx) + +This example demonstrates how you can perform a cross-chain transaction between the Solana and Ethereum chains using Swing's Cross-Chain and Platform APIs via Swing's SDK. + +In this example, we will be using thirdweb's SDK and `@solana/web3.js` wallet connector to connect to a user's Ethereum and Solana wallets, respectively. We will also demonstrate how to utilize Swing's SDK exported API functions, namely `crossChainAPI` and `platformAPI`, to build out a fully functionaly cross-chain application. + +The process/steps for performing a SOL to ETH transaction, and vice versa, are as follows: + +- Getting a [quote](https://developers.swing.xyz/reference/api/cross-chain/1169f8cbb6937-request-a-transfer-quote) and selecting the best route +- Sending a [token approval](https://developers.swing.xyz/reference/api/contract-calls/approval) request for ERC20 Tokens. (Optional for SOL > ETH Route) +- Sending a [transaction](https://developers.swing.xyz/reference/api/cross-chain/d83d0d65028dc-send-transfer) + +> Although not essential for performing a swap transaction, providing your users with real-time updates on the transaction's status by polling the [status](https://developers.swing.xyz/reference/api/cross-chain/6b61efd1b798a-transfer-status) can significantly enhance the user experience. + +## Getting started + +To get started with this template, first install the required npm dependencies: + +```bash +yarn install +``` + +Next, launch the development server by running the following command: + +```bash +yarn dev --filter=swaps-api-nextjs-solana +``` + +Finally, open [http://localhost:3000](http://localhost:3000) in your browser to view the website. + +## Initializing Swing's SDK + +Swing's SDK contains two (2) vital objects that are crucial to our API integration. Namely, the `platformAPI` and the `crossChainAPI` objects. These objects are a wrapper for all of Swing's Cross-Chain and Platform APIs removing the need for developers to make API requests using libraries like `fetch` or `axios`. The SDK handles those API requests from behind the scenes. + +We've included all the necessary request and response interfaces in the `src/interfaces` folder to aid development. + +Navigating to our `src/services/requests.ts`, let's start by initializing Swing's SDK in our `SwingServiceAPI` class: + +```typescript +import { SwingSDK } from "@swing.xyz/sdk"; + +export class SwingServiceAPI implements ISwingServiceAPI { + private readonly swingSDK: SwingSDK; + + constructor() { + this.swingSDK = new SwingSDK({ + projectId: "replug", + debug: true, + }); + } +} +``` + +The `SwingSDK` constructor accepts a `projectId` as a mandatory parameter and a few other optional parameters: + +| Property | Example | Description | +| ------------- | ------------ | ---------------------------------------------------------------- | +| `projectId` | `replug` | [Swing Platform project identifier](https://platform.swing.xyz/) | +| `debug` | `true` | Enable verbose logging | +| `environment` | `production` | Set's SwingAPI to operate either on testnet or mainnet chains | +| `analytics` | `false` | Enable analytics and error reporting | + +> You can get your `projectId` by signing up to [Swing!](https://platform.swing.xyz/) + +## Getting a Quote + +To perform a swap between ETH and SOL, we first have to get a quote from Swing's Cross-Chain API. + +URL: [https://swap.prod.swing.xyz/v0/transfer/quote](https://swap.prod.swing.xyz/v0/transfer/quote) + +**Parameters**: + +| Property | Example | Description | +| ------------------ | -------------------------------------------- | ------------------------------------------------------- | +| `tokenAmount` | 1000000000000000000 | Amount of the source token being sent (in wei for ETH). | +| `fromChain` | `ethereum` | Source Chain slug | +| `fromUserAddress` | 0x018c15DA1239B84b08283799B89045CD476BBbBb | Sender's wallet address | +| `fromTokenAddress` | 0x0000000000000000000000000000000000000000 | Source Token Address | +| `tokenSymbol` | `ETH` | Source Token slug | +| `toTokenAddress` | `11111111111111111111111111111111` | Destination Token Address. | +| `toTokenSymbol` | `SOL` | Destination Token slug | +| `toChain` | `solana` | Destination Chain slug | +| `toUserAddress` | ELoruRy7quAskANEgC99XBYfEnCcrVGSqnwGETWKZtsU | Receiver's wallet address | +| `projectId` | `replug` | [Your project's ID](https://platform.swing.xyz/) | + +Navigating to our `src/services/requests.ts` file, you will find our method for getting a quote from Swing's Cross-Chain API called `getQuoteRequest()`. + +```typescript +async getQuoteRequest( + queryParams: QuoteQueryParams, +): Promise { + try { + const response = await this.swingSDK.crossChainAPI.GET( + "/v0/transfer/quote", + { + params: { + query: queryParams, + }, + }, + ); + return response.data; + } catch (error) { + console.error("Error fetching quote:", error); + throw error; + } +} +``` + +The response received from the `getQuoteRequest` endpoint provides us with the `fees` a user will have to pay when performing a transaction, as well as a list of possible `routes` for the user to choose from. + +The definition for the `getQuoteRequest` response can be found in `src/interfaces/quote.interface.ts.` + +```typescript +export interface QuoteAPIResponse { + routes: Route[]; + fromToken: Token; + fromChain: Chain; + toToken: Token; + toChain: Chain; +} +``` + +Each `Route` contains a `gasFee`, `bridgeFee` and the amount of tokens the destination wallet will receive. + +Here's an example response that contains the route data: + +```json +"routes": [ + { + "duration": 1, + "gas": "2770874189563960", + "quote": { + "integration": "debridge", + "type": "swap", + "bridgeFee": "20867118", + "bridgeFeeInNativeToken": "1000000000000000", + "amount": "482153281", + "decimals": 9, + "amountUSD": "69.869", + "bridgeFeeUSD": "3.024", + "bridgeFeeInNativeTokenUSD": "2.931", + "fees": [ + { + "type": "bridge", + "amount": "20867118", + "amountUSD": "3.024", + "chainSlug": "solana", + "tokenSymbol": "SOL", + "tokenAddress": "11111111111111111111111111111111", + "decimals": 9, + "deductedFromSourceToken": true + }, + { + "type": "bridge", + "amount": "1000000000000000", + "amountUSD": "2.931", + "chainSlug": "ethereum", + "tokenSymbol": "ETH", + "tokenAddress": "0x0000000000000000000000000000000000000000", + "decimals": 18, + "deductedFromSourceToken": false + }, + { + "type": "gas", + "amount": "2770874189563960", + "amountUSD": "8.123", + "chainSlug": "ethereum", + "tokenSymbol": "ETH", + "tokenAddress": "0x0000000000000000000000000000000000000000", + "decimals": 18, + "deductedFromSourceToken": false + }, + { + "type": "partner", + "amount": "0", + "amountUSD": "0", + "chainSlug": "ethereum", + "tokenSymbol": "ETH", + "tokenAddress": "0x0000000000000000000000000000000000000000", + "decimals": 18, + "deductedFromSourceToken": true + } + ] + }, + "route": [ + { + "bridge": "debridge", + "bridgeTokenAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "steps": [ + "allowance", + "approve", + "send" + ], + "name": "USDC", + "part": 100 + } + ], + "distribution": { + "debridge": 1 + }, + "gasUSD": "8.123" + } +] +``` + +Navigating to our `src/components/Swap.tsx` file, you'll find our `defaultTransferParams` object which will store the default transaction config for our example: + +```typescript +const defaultTransferParams: TransferParams = { + tokenAmount: "1", + fromChain: "ethereum", + tokenSymbol: "ETH", + fromUserAddress: "", + fromTokenAddress: "0x0000000000000000000000000000000000000000", + fromNativeTokenSymbol: "ETH", + fromTokenIconUrl: + "https://raw.githubusercontent.com/Pymmdrza/Cryptocurrency_Logos/mainx/PNG/eth.png", + fromChainIconUrl: + "https://raw.githubusercontent.com/polkaswitch/assets/master/blockchains/ethereum/info/logo.png", + fromChainDecimal: 18, + toTokenAddress: "11111111111111111111111111111111", + toTokenSymbol: "SOL", + toNativeTokenSymbol: "SOL", + toChain: "solana", + toTokenIconUrl: + "https://raw.githubusercontent.com/Pymmdrza/Cryptocurrency_Logos/mainx/SVG/sol.svg", + toChainIconUrl: + "https://raw.githubusercontent.com/Pymmdrza/Cryptocurrency_Logos/mainx/SVG/sol.svg", + toUserAddress: "", //solana wallet address + toChainDecimal: 9, +}; +``` + +## Sending a Token Approval Request for ERC20 Tokens (Optional for SOL > ETH Route) + +If you're attempting to bridge an ERC20 token from a user's wallet to Solana, you need to prompt the user to approve the required amount of tokens to be bridged. + +Navigating to our `src/components/Swap.tsx` file, inside our `startTransfer()` method, you will find our implementation of the `getAllowanceRequest()` and `getApprovalTxDataRequest()` methods. Before approving, you have to perform two checks: + +- First, we will check if we're performing a native currency swap by comparing the values of `tokenSymbol` and `fromNativeTokenSymbol` on the source chain. If we're not dealing with a native currency swap, we then proceed to ask for an allowance. +- Next, we will check if an allowance has already been made by Swing on a user's wallet by calling the `getAllowanceRequest()` method. If no approved allowance is found, we will then proceed to make an approval request by calling the `getApprovalTxDataRequest()` method. + +Since the `/approval` and `/approve` endpoints are specific to EVM chains, we have to check that source chain via `fromChain` is anything but `solana`. Skipping this check will result in the `/approval` endpoint returning an error to the user: + +```json +{ + "statusCode": 400, + "message": "Non-evm is not supported for approval method: solana", + "error": "Bad Request" +} +``` + +Let's execute these steps: + +```typescript +if ( + transferParams.tokenSymbol !== transferParams.fromNativeTokenSymbol && + transferParams.fromChain !== "solana" +) { + const checkAllowance = await getAllowanceRequest({ + bridge: transferRoute.quote.integration, + fromAddress: transferParams.fromUserAddress, + fromChain: transferParams.fromChain, + tokenAddress: transferParams.fromTokenAddress, + tokenSymbol: transferParams.tokenSymbol, + toChain: transferParams.toChain, + toTokenAddress: transferParams.toTokenAddress!, + toTokenSymbol: transferParams.toTokenSymbol!, + contractCall: false, + }); + + if (checkAllowance.allowance === tokenAmount) { + setTransStatus({ + status: `Wallet Interaction Required: Approval Token`, + }); + + const getApprovalTxData = await getApprovalTxDataRequest({ + tokenAmount: Number(tokenAmount), + bridge: transferRoute.quote.integration, + fromAddress: transferParams.fromUserAddress, + fromChain: transferParams.fromChain, + tokenAddress: transferParams.fromTokenAddress, + tokenSymbol: transferParams.tokenSymbol, + toChain: transferParams.toChain, + toTokenAddress: transferParams.toTokenAddress!, + toTokenSymbol: transferParams.toTokenSymbol!, + contractCall: false, + }); + + const txData: TransactionDetails = { + data: getApprovalTxData.tx[0].data, + from: getApprovalTxData.tx[0].from, + to: getApprovalTxData.tx[0].to, + }; + + const txResponse = await signer?.sendTransaction(txData); + + const receipt = await txResponse?.wait(); + console.log("Transaction receipt:", receipt); + + setTransStatus({ status: "Token allowance approved" }); + } +} +``` + +## Sending a Transaction + +After getting a quote, you'll next have to send a transaction to Swing's Cross-Chain API. + +The steps for sending a transaction are as followed: + +- First, we will make a request to [`https://swap.prod.swing.xyz/v0/transfer/send`](https://developers.swing.xyz/reference/api/cross-chain/d83d0d65028dc-send-transfer) +- Using the `txData` returned from the `/send` request, sign the transaction by using a user's wallet + +### Making a `/send` Request + +Navigating to our `src/services/requests.ts`, you'll find our request implemenation for the `/send` endpoint: + +```typescript +async sendTransactionRequest( + payload: SendTransactionPayload, +): Promise { + try { + const response = await this.swingSDK.crossChainAPI.POST( + "/v0/transfer/send", + { + body: payload, + }, + ); + return response.data; + } catch (error) { + console.error("Error sending transaction:", error); + throw error; + } +} +``` + +The `SendTransactionPayload` body payload contains the `source chain`, `destination chain`, `tokenAmount`, and the desired `route`. + +URL: [https://swap.prod.swing.xyz/v0/transfer/send](https://swap.prod.swing.xyz/v0/transfer/send) + +**Parameters**: + +| Key | Example | Description | +| ------------------ | ------------------------------------------------ | ------------------------------------------------------- | +| `fromChain` | ethereum | The blockchain where the transaction originates. | +| `fromTokenAddress` | 0x0000000000000000000000000000000000000000 | Source Token Address | +| `fromUserAddress` | 0x018c15DA1239B84b08283799B89045CD476BBbBb | Sender's wallet address | +| `tokenSymbol` | ETH | Source Token slug | +| `toTokenAddress` | 11111111111111111111111111111111 | Destination Token Address. | +| `toChain` | solana | Destination Source slug | +| `toTokenAmount` | 4000000 | Amount of the destination token being received. | +| `toTokenSymbol` | SOL | Destination Chain slug | +| `toUserAddress` | ELoruRy7quAskANEgC99XBYfEnCcrVGSqnwGETWKZtsU | Receiver's wallet address | +| `tokenAmount` | 1000000000000000000 | Amount of the source token being sent (in wei for ETH). | +| `type` | swap | Type of transaction. | +| `projectId` | `replug` | [Your project's ID](https://platform.swing.xyz/) | +| `route` | see `Route` in`src/interfaces/send.interface.ts` | Selected Route | + +Since performing a swap will change the state of a user's wallet, the next step of this transaction must be done via a `Smart Contract` Transaction and not via Swing's Cross-Chain API. The response received from the `sendTransactionRequest` endpoint provides us with the necessary `txData/callData` needed to be passed on to a user's wallet to sign the transaction. + +The `txData` from the `sendTransactionRequest` will look something like this: + +```json +{ + .... + "tx": { + "from": "0x018c15DA1239B84b08283799B89045CD476BBbBb", + "to": "0x39E3e49C99834C9573c9FC7Ff5A4B226cD7B0E63", + "data": "0x301a3720000000000000000000000000eeeeeeeeeeee........", + "value": "0x0e35fa931a0000", + "gas": "0x06a02f" + } + .... +} + +``` + +To demonstrate, we first make a request by calling the `sendTransactionRequest` method. + +```typescript +// src/components/Swaps.tsx + +const transfer = await sendTransactionRequest({ + fromChain: transferParams.fromChain, + fromTokenAddress: transferParams.fromTokenAddress, + fromUserAddress: transferParams.fromUserAddress, + tokenSymbol: transferParams.tokenSymbol, + + toTokenAddress: transferParams.toTokenAddress!, + toChain: transferParams.toChain, + toTokenAmount: transferRoute.quote.amount, + toTokenSymbol: transferParams.toTokenSymbol!, + toUserAddress: transferParams.toUserAddress!, + + tokenAmount: convertEthToWei( + transferParams.tokenAmount, + transferParams.fromChainDecimal, + ), + route: transferRoute.route, + type: "swap", +}); +``` + +Next, we'll extract the `txData`: + +```typescript +// src/components/Swaps.tsx + +let txData: any = { + data: transfer.tx.data, + from: transfer.tx.from, + to: transfer.tx.to, + value: transfer.tx.value, + gasLimit: transfer.tx.gas, +}; +``` + +Using our wallet provider, we will send a transaction to the user's wallet. + +```typescript +// src/components/Swaps.tsx + +const txResponse = await signer?.sendTransaction(txData); // <- `txResponse` contains the `txHash` of our transaction. You will need this later for getting a transaction's status. + +const receipt = await txResponse?.wait(); + +console.log("Transaction receipt:", receipt); +``` + +The definition for the `sendTransactionRequest` response can be found in `src/interfaces/send.interface.ts.` + +```typescript +export interface SendTransactionApiResponse { + id: number; + fromToken: Token; + toToken: Token; + fromChain: Chain; + toChain: Chain; + route: Route[]; + tx: TransactionDetails; +} +``` + +> The `sendTransactionRequest` will return and `id` whilst the `txResponse` will contain a `txHash` which we will need later for checking the status of a transaction. + +### Sending a Solana Transaction to the Network + +If you decided to perform a cross chain swap with Solana as the source chain, you'll need to sign the transaction using a wallet provider that supports Solana like [Phantom](https://phantom.app/). + +> Remember, you'll have to call the `/send` endpoint via `sendTransactionRequest` before signing the transaction. + +We will sign the `txData` returned from the `/send` endpoint using the `@solana/web3.js` library. To begin, let's include the necessary imports into our app: + +```typescript +import { Transaction, VersionedTransaction } from "@solana/web3.js"; +``` + +For our next steps, we will read the raw transaction data by decoding the value of the `data` property in our `txData`: + +```typescript +const rawTx = Uint8Array.from(Buffer.from(txData.data as any, "hex")); +``` + +Next, we will create a transaction object by deserializing the raw transaction data. If it fails as a regular transaction, we attempt to deserialize it as a [Versioned Transaction](https://solana.com/docs/advanced/versions): + +```typescript +let transaction: Transaction | VersionedTransaction; +try { + // Attempt to deserialize the transaction as a regular transaction + transaction = Transaction.from(rawTx); +} catch (error) { + // If the transaction is not a regular transaction, attempt to deserialize it as a versioned transaction + transaction = VersionedTransaction.deserialize(rawTx); +} +``` + +Next, we will proceed to sign and send the transaction over to the network. + +```typescript +try { + // Attempt to deserialize the transaction as a regular transaction + transaction = Transaction.from(rawTx); +} catch (error) { + // If the transaction is not a regular transaction, attempt to deserialize it as a versioned transaction + transaction = VersionedTransaction.deserialize(rawTx); +} + +const response = await window.solana?.signAndSendTransaction(transaction); +``` + +## Polling Transaction Status + +After sending a transaction over to the network, for the sake of user experience, it's best to poll the transaction status endpoint by periodically checking to see if the transaction is complete. This will let the user using your dapp know in realtime, the status of the current transaction. + +Navigating to our `src/services/requests.ts` file, you will find our method for getting a transaction status called `getTransationStatus()`. + +```typescript +async getTransationStatusRequest( + queryParams: TransactionStatusParams, +): Promise { + try { + const response = await this.swingSDK.platformAPI.GET( + "/projects/{projectId}/transactions/{transactionId}", + { + params: { + path: { + transactionId: queryParams.id, + projectId, + }, + query: { + txHash: queryParams.txHash, + }, + }, + }, + ); + + return response.data; + } catch (error) { + console.error("Error fetching transaction status:", error); + throw error; + } +} +``` + +The `TransactionStatusParams` params contains the three properties, namely: `id`, `txHash` and `projectId` + +URL: [https://platform.swing.xyz/api/v1/projects/{projectId}/transactions/{transactionId}](https://developers.swing.xyz/reference/api/platform/ehwqfo1kv00ce-get-transaction) + +**Parameters**: + +| Key | Example | Description | +| ----------- | ----------------------------------- | ------------------------------------------------ | +| `id` | 239750 | Transaction ID from `/send` response | +| `txHash` | 0x3b2a04e2d16489bcbbb10960a248..... | The transaction hash identifier. | +| `projectId` | `replug` | [Your project's ID](https://platform.swing.xyz/) | + +To poll the `/status` endpoint, we'll be using `setTimeout()` to to retry `getTransationStatus()` over a period of time. We will define a function, `pollTransactionStatus()`, which will recursively call `getTransStatus()` until the transaction is completed. + +```typescript +// src/components/Swaps.tsx + +async function getTransStatus(transId: string, txHash: string) { + const transactionStatus = await swingServiceAPI?.getTransationStatusRequest({ + id: transId, + txHash, + }); + + setTransStatus(transactionStatus); + + return transactionStatus; +} + +async function pollTransactionStatus(transId: string, txHash: string) { + const transactionStatus = await getTransStatus(transId, txHash); + + if (transactionStatus?.status! === "Pending") { + setTimeout( + () => pollTransactionStatus(transId, txHash), + transactionPollingDuration, + ); + } else { + if (transactionStatus?.status === "Success") { + toast({ + title: "Transaction Successful", + description: `Bridge Successful`, + }); + } else if (transactionStatus?.status === "Failed") { + toast({ + variant: "destructive", + title: "Transaction Failed", + description: transStatus?.errorReason, + }); + } + + setTransferRoute(null); + setIsTransacting(false); + (sendInputRef.current as HTMLInputElement).value = ""; + } +} +``` + +In our `startTransfer()` method, we will execute the `pollTransactionStatus()` right after our transaction is sent over the network + +```typescript +// src/components/Swaps.tsx + +let txHash = ""; + +if (transferParams.fromChain === "solana") { + const hash = await sendSolTrans({ + ...txData, + from: transferParams.fromUserAddress, + }); + txHash = hash!; +} else { + const txResponse = await signer?.sendTransaction({ + data: txData.data, + from: txData.from, + to: txData.to, + value: txData.value, + gasLimit: txData.gasLimit, + }); + // Wait for the transaction to be mined + + const receipt = await txResponse?.wait(); + console.log("Transaction receipt:", receipt); + txHash = txResponse?.hash!; +} + +pollTransactionStatus(transfer?.id.toString()!, txHash); +``` + +## Customizing + +You can start editing this template by modifying the files in the `/src` folder. The site will auto-update as you edit these files. diff --git a/examples/swaps-api-nextjs-tron/components.json b/examples/swaps-api-nextjs-tron/components.json new file mode 100644 index 0000000..44d0040 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "styles/globals.css", + "baseColor": "zinc", + "cssVariables": false, + "prefix": "" + }, + "aliases": { + "components": "components", + "utils": "lib" + } +} \ No newline at end of file diff --git a/examples/swaps-api-nextjs-tron/next-env.d.ts b/examples/swaps-api-nextjs-tron/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/swaps-api-nextjs-tron/next.config.mjs b/examples/swaps-api-nextjs-tron/next.config.mjs new file mode 100644 index 0000000..d5456a1 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/next.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +}; + +export default nextConfig; diff --git a/examples/swaps-api-nextjs-tron/package.json b/examples/swaps-api-nextjs-tron/package.json new file mode 100644 index 0000000..74c5e63 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/package.json @@ -0,0 +1,61 @@ +{ + "name": "swaps-api-nextjs-tron", + "demo": "https://swaps-api-nextjs-solana.vercel.app/", + "keywords": [ + "api", + "swaps", + "nextjs", + "thirdweb" + ], + "license": "MIT", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "next dev", + "lint": "eslint src", + "build": "next build", + "start": "next start", + "clean": "rm -rf .next node_modules" + }, + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.5.1", + "@fortawesome/free-brands-svg-icons": "^6.5.1", + "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/react-fontawesome": "^0.2.0", + "@headlessui/react": "^1.7.18", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-toast": "^1.1.5", + "@solana/web3.js": "^1.91.8", + "@swing.xyz/sdk": "^0.57.5", + "@tailwindcss/forms": "^0.5.7", + "@thirdweb-dev/react": "^4.4.17", + "@thirdweb-dev/sdk": "^4.0.44", + "@tronweb3/tronwallet-adapter-react-hooks": "^1.1.7", + "@tronweb3/tronwallet-adapter-react-ui": "^1.1.8", + "axios": "^1.6.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.0", + "framer-motion": "^6.5.1", + "lucide-react": "^0.368.0", + "next": "14.1.3", + "react": "18.2.0", + "react-dom": "18.2.0", + "tailwind-merge": "^2.2.2", + "tailwindcss-animate": "^1.0.7", + "use-debounce": "^10.0.0" + }, + "devDependencies": { + "@types/node": "20.11.26", + "@types/react": "18.2.65", + "@types/react-dom": "18.2.22", + "autoprefixer": "^10.4.18", + "eslint": "^8.57.0", + "eslint-config-examples": "workspace:*", + "pino-pretty": "^10.3.1", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "typescript": "^5.4.2", + "typescript-config": "workspace:*" + } +} diff --git a/examples/swaps-api-nextjs-tron/postcss.config.js b/examples/swaps-api-nextjs-tron/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/examples/swaps-api-nextjs-tron/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/examples/swaps-api-nextjs-tron/public/favicon.ico b/examples/swaps-api-nextjs-tron/public/favicon.ico new file mode 100644 index 0000000..88b1f7a Binary files /dev/null and b/examples/swaps-api-nextjs-tron/public/favicon.ico differ diff --git a/examples/swaps-api-nextjs-tron/public/fonts/Inter-italic.var.woff2 b/examples/swaps-api-nextjs-tron/public/fonts/Inter-italic.var.woff2 new file mode 100644 index 0000000..b826d5a Binary files /dev/null and b/examples/swaps-api-nextjs-tron/public/fonts/Inter-italic.var.woff2 differ diff --git a/examples/swaps-api-nextjs-tron/public/fonts/Inter-roman.var.woff2 b/examples/swaps-api-nextjs-tron/public/fonts/Inter-roman.var.woff2 new file mode 100644 index 0000000..6a256a0 Binary files /dev/null and b/examples/swaps-api-nextjs-tron/public/fonts/Inter-roman.var.woff2 differ diff --git a/examples/swaps-api-nextjs-tron/src/app/layout.tsx b/examples/swaps-api-nextjs-tron/src/app/layout.tsx new file mode 100644 index 0000000..810f73f --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/app/layout.tsx @@ -0,0 +1,21 @@ +import "styles/globals.css"; +import "@fortawesome/fontawesome-svg-core/styles.css"; + +import { Header } from "../components/ui/Header"; + +import { Toaster } from "components/ui/toaster"; + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + +
+ +
{children}
+ + + + ); +} diff --git a/examples/swaps-api-nextjs-tron/src/app/page.tsx b/examples/swaps-api-nextjs-tron/src/app/page.tsx new file mode 100644 index 0000000..d066105 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/app/page.tsx @@ -0,0 +1,12 @@ +import { Hero } from "../components/ui/Hero"; +import { ThirdwebProvider } from "../components/ThirdwebProvider"; +import { Backdrop } from "components/ui/Backdrop"; + +export default function Home() { + return ( + + + + + ); +} diff --git a/examples/swaps-api-nextjs-tron/src/components/Swap.tsx b/examples/swaps-api-nextjs-tron/src/components/Swap.tsx new file mode 100644 index 0000000..4264759 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/Swap.tsx @@ -0,0 +1,923 @@ +"use client"; + +import clsx from "clsx"; +import { useEffect, useRef, useState } from "react"; +import { useConnect, metamaskWallet } from "@thirdweb-dev/react"; +import { + useConnectionStatus, + useAddress, + useSigner, +} from "@thirdweb-dev/react"; +import { SwingServiceAPI } from "services/requests"; +import { convertEthToWei, convertWeiToEth } from "utils/ethToWei"; +import { useDebouncedCallback } from "use-debounce"; +import { useToast } from "components/ui/use-toast"; +import { TransactionStatusAPIResponse } from "interfaces/status.interface"; +import { AxiosError } from "axios"; +import { Chain } from "interfaces/chain.interface"; +import { Token } from "interfaces/token.interface"; +import { SelectTokenPanel } from "./ui/SelectTokenPanel"; +import { SelectChainPanel } from "./ui/SelectChainPanel"; +import { TbSwitchVertical, TbSwitchHorizontal } from "react-icons/tb"; +import { faCircleNotch, faHistory } from "@fortawesome/free-solid-svg-icons"; +import { TransferParams } from "types/transfer.types"; +import { TransferHistoryPanel } from "./ui/TransferHistoryPanel"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { ISwingServiceAPI } from "interfaces/swing-service.interface"; +import { QuoteAPIResponse, Route } from "interfaces/quote.interface"; +import { TransactionData } from "interfaces/approval.interface"; +import { TransferQuote } from "@swing.xyz/sdk"; + +const walletConfig = metamaskWallet(); + +const defaultTransferParams: TransferParams = { + tokenAmount: "1", + fromChain: "ethereum", + tokenSymbol: "ETH", + fromUserAddress: "", + dummyFromUserAddress: "0x018c15DA1239B84b08283799B89045CD476BBbBb", + dummyToUserAddress: "TV6ybRmqiUK6a7JVMRwPg2cDDkLqgR5MaZ", + fromTokenAddress: "0x0000000000000000000000000000000000000000", + fromNativeTokenSymbol: "ETH", + fromTokenIconUrl: + "https://raw.githubusercontent.com/Pymmdrza/Cryptocurrency_Logos/mainx/PNG/eth.png", + fromChainIconUrl: + "https://raw.githubusercontent.com/polkaswitch/assets/master/blockchains/ethereum/info/logo.png", + fromChainDecimal: 18, + toTokenAddress: "0x0000000000000000000000000000000000000000", + toTokenSymbol: "TRX", + toNativeTokenSymbol: "TRX", + toChain: "tron", + toTokenIconUrl: + "https://raw.githubusercontent.com/Pymmdrza/Cryptocurrency_Logos/mainx/SVG/trx.svg", + toChainIconUrl: + "https://raw.githubusercontent.com/Pymmdrza/Cryptocurrency_Logos/mainx/SVG/trx.svg", + toUserAddress: "", // tron wallet address + toChainDecimal: 6, +}; + +const transactionPollingDuration = 10000; + +const Swap = () => { + const [isLoading, setIsLoading] = useState(false); + const [isTransacting, setIsTransacting] = useState(false); + + const [transferParams, setTransferParams] = useState( + defaultTransferParams, + ); + + const [tronWalletAddress, setTronWalletAddress] = useState(""); + + const [transferRoute, setTransferRoute] = useState(null); + const [transStatus, setTransStatus] = + useState(); + const [tokens, setTokens] = useState([]); + const [tronTokens, setTronTokens] = useState([]); + const [chains, setChains] = useState([]); + const [quotes, setQuotes] = useState(); + const [swingServiceAPI, setSwingServiceAPI] = useState< + ISwingServiceAPI | undefined + >(); + + const connect = useConnect(); + const address = useAddress(); + + const connectionStatus = useConnectionStatus(); + const walletAddress = useAddress(); + const signer = useSigner(); + + const { toast } = useToast(); + const sendInputRef = useRef(null); + + // const { wallet, address, connected, select, connect, disconnect, signMessage, signTransaction } = useWallet(); + + const debounced = useDebouncedCallback((value) => { + setTransferParams((prev: TransferParams) => ({ + ...prev, + tokenAmount: value, + })); + getQuote(value); + }, 1000); + + //Initialize Swing Service API from service.ts file and connect to tron wallet if installed + useEffect(() => { + setSwingServiceAPI(new SwingServiceAPI()); + if (window.tronLink) { + window.tronLink + .request({ method: "tron_requestAccounts" }) + .then(() => { + const isConnected = window.tronLink?.ready; + if (isConnected) { + // Get user's wallet address + const walletAddress = + window.tronLink?.tronWeb.defaultAddress.base58; + setTronWalletAddress(walletAddress!); + setTransferParams((prev) => ({ + ...prev, + toUserAddress: walletAddress!, + })); + } else { + console.log("TronLink is not connected."); + } + }) + .catch((err) => console.error("Failed to connect Tron wallet:", err)); + } + }, []); + + //Fetch chains and tokens whenever wallet address changes + useEffect(() => { + setTransferParams((prev) => { + return { + ...prev, + fromUserAddress: walletAddress!, + }; + }); + + setChains([]); + setTokens([]); + setTronTokens([]); + + swingServiceAPI + ?.getChainsRequest({ type: "evm" }) + .then((chains: Chain[] | undefined) => { + setChains(chains!); + }); + + swingServiceAPI + ?.getTokensRequest({ chain: defaultTransferParams.fromChain }) + .then((tokens: Token[] | undefined) => { + setTokens(tokens!); + }); + + swingServiceAPI + ?.getTokensRequest({ chain: "tron" }) + .then((tokens: Token[] | undefined) => { + setTronTokens(tokens!); + }); + }, [walletAddress]); + + const selectRef = useRef(null); + + // Function to change the selected item + const changeSelectedItem = (index: string) => { + if (selectRef.current) { + selectRef.current.value = index; + // Trigger the onChange event manually + const event = new Event("change", { bubbles: true }); + selectRef.current.dispatchEvent(event); + } + }; + + //Connect to Ethereum Wallet + async function connectWallet(chainId?: number) { + try { + // Connect to MetaMask + await connect(walletConfig, { chainId }); + + // Connect wallet signer to Swing SDK + const walletAddress = address; + + setTransferParams((prev) => { + return { + ...prev, + fromUserAddress: walletAddress!, + }; + }); + } catch (error) { + console.error("Connect Wallet Error:", error); + toast({ + variant: "destructive", + title: "Something went wrong!", + description: (error as Error).message, + }); + } + } + + //Connect to Tron Wallet + const connectToTron = async () => { + try { + const { tronLink } = window; + if (tronLink) { + const response = await tronLink.request({ + method: "tron_requestAccounts", + }); + const isConnected = window.tronLink?.ready; + if (isConnected) { + // Get user's wallet address + const walletAddress = window.tronLink?.tronWeb.defaultAddress.base58; + console.log("Connected to TronLink wallet:", walletAddress); + return walletAddress; + } else { + console.log("TronLink is not connected."); + } + + setTronWalletAddress(response.publicKey.toString()); + setTransferParams((prev) => ({ + ...prev, + toUserAddress: response.publicKey.toString(), + })); + } else { + toast({ + variant: "destructive", + title: "Something went wrong!", + description: "Tron wallet not found. Please install it.", + }); + } + } catch (error) { + console.error("Failed to connect to Tron wallet:", error); + toast({ + variant: "destructive", + title: "Something went wrong!", + description: `Failed to connect to Tron wallet: ${(error as Error).message}`, + }); + } + }; + + async function getTransStatus(transId: string, txHash: string) { + const transactionStatus = await swingServiceAPI?.getTransationStatusRequest( + { + id: transId, + txHash, + }, + ); + + setTransStatus(transactionStatus); + + return transactionStatus; + } + + async function pollTransactionStatus(transId: string, txHash: string) { + const transactionStatus = await getTransStatus(transId, txHash); + + if (transactionStatus?.status! === "Pending") { + setTimeout( + () => pollTransactionStatus(transId, txHash), + transactionPollingDuration, + ); + } else { + if (transactionStatus?.status === "Success") { + toast({ + title: "Transaction Successful", + description: `Bridge Successful`, + }); + } else if (transactionStatus?.status === "Failed") { + toast({ + variant: "destructive", + title: "Transaction Failed", + description: transStatus?.errorReason, + }); + } + + setTransferRoute(null); + setTransStatus(null); + setIsTransacting(false); + setIsLoading(false); + (sendInputRef.current as HTMLInputElement).value = ""; + } + } + + async function getQuote(value: string) { + if (Number(value) <= 0) { + return; + } + + setIsLoading(true); + + try { + if (transferParams.toUserAddress === "") { + toast({ + variant: "destructive", + title: "TRX Address Not Set", + description: "Please connect your TRX wallet", + }); + return; + } + + const quotes = await swingServiceAPI?.getQuoteRequest({ + fromChain: transferParams.fromChain, + fromTokenAddress: transferParams.fromTokenAddress, + fromUserAddress: + transferParams.fromUserAddress ?? + defaultTransferParams.dummyFromUserAddress, + toChain: transferParams.toChain, + tokenSymbol: transferParams.tokenSymbol, + toTokenAddress: transferParams.toTokenAddress, + toTokenSymbol: transferParams.toTokenSymbol, + toUserAddress: + transferParams.toUserAddress ?? + defaultTransferParams.dummyToUserAddress, + tokenAmount: convertEthToWei(value, transferParams.fromChainDecimal), + }); + + if (!quotes?.routes?.length) { + toast({ + variant: "destructive", + title: "No routes found", + description: "No routes available. Try increasing the send amount.", + }); + setIsLoading(false); + changeSelectedItem("NON"); + return; + } + + setQuotes(quotes.routes); + setTransferRoute(quotes.routes[0]); + changeSelectedItem(quotes.routes[0].quote.integration); + } catch (error) { + console.error("Quote Error:", error); + toast({ + variant: "destructive", + title: "Something went wrong!", + description: (error as Error).message, + }); + } + + setIsLoading(false); + } + + async function sendTronTrans( + txData: TransactionData, + ): Promise { + try { + // Assuming txData contains 'to' (recipient address) and 'amount' (in SUN) + const transaction = + await window.tronLink?.tronWeb.transactionBuilder.sendTrx( + txData.to, // Recipient address (in base58 format) + Number(txData.value), // Amount in SUN (1 TRX = 1,000,000 SUN) + window?.tronLink.tronWeb.defaultAddress.base58, // Sender's address (from TronLink) + ); + + // Sign the transaction + const signedTransaction = + await window.tronLink?.tronWeb.trx.sign(transaction); + + // Broadcast the signed transaction + const response = + await window.tronLink?.tronWeb.trx.sendRawTransaction( + signedTransaction, + ); + + // If the transaction was successful, return the transaction ID (txid) + if (response && response.result) { + return response.txid; + } else { + console.error("Transaction failed:", response); + return undefined; + } + } catch (error) { + console.error("Error sending Tron transaction:", error); + return undefined; + } + } + + function switchTransferParams() { + const tempTransferParams: TransferParams = Object.create(transferParams); + + const newTransferParams: TransferParams = { + tokenAmount: "0", + fromChain: tempTransferParams.toChain, + tokenSymbol: tempTransferParams.toTokenSymbol!, + fromUserAddress: tempTransferParams.toUserAddress!, + fromTokenAddress: tempTransferParams.toTokenAddress!, + fromTokenIconUrl: tempTransferParams.toTokenIconUrl, + fromChainIconUrl: tempTransferParams.toChainIconUrl, + fromChainDecimal: tempTransferParams.toChainDecimal, + fromNativeTokenSymbol: tempTransferParams.toNativeTokenSymbol, + toTokenAddress: tempTransferParams.fromTokenAddress, + toTokenSymbol: tempTransferParams.tokenSymbol, + toChain: tempTransferParams.fromChain, + toChainIconUrl: tempTransferParams.fromChainIconUrl, + toTokenIconUrl: tempTransferParams.fromTokenIconUrl!, + toUserAddress: tempTransferParams.fromUserAddress, + toChainDecimal: tempTransferParams.fromChainDecimal, + toNativeTokenSymbol: tempTransferParams.fromNativeTokenSymbol, + }; + + setTransferRoute(null); + setTransferParams(newTransferParams); + + (sendInputRef.current as HTMLInputElement).value = ""; + } + + function onEVMChainSelect(chain: Chain) { + swingServiceAPI + ?.getTokensRequest({ chain: chain.slug }) + .then((tokens: Token[] | undefined) => { + setTokens(tokens!); + }); + + if (transferParams.fromChain !== "tron") { + setTransferParams((prev) => ({ + ...prev, + tokenAmount: "0", + fromChain: chain.slug, + fromChainIconUrl: chain.logo, + + tokenSymbol: chain.nativeToken?.symbol!, + fromTokenAddress: chain.nativeToken?.address!, + fromTokenIconUrl: chain.nativeToken?.logo, + fromChainDecimal: chain.nativeToken?.decimals, + fromNativeTokenSymbol: chain.nativeToken?.symbol, + })); + } else { + setTransferParams((prev) => ({ + ...prev, + tokenAmount: "0", + toChain: chain.slug, + toChainIconUrl: chain.logo, + + toTokenSymbol: chain.nativeToken?.symbol!, + toTokenAddress: chain.nativeToken?.address!, + toTokenIconUrl: chain.nativeToken?.logo!, + toChainDecimal: chain.nativeToken?.decimals!, + toNativeTokenSymbol: chain.nativeToken?.symbol, + })); + } + + setTransferRoute(null); + } + + function onFromTokenSelect(token: Token) { + setTransferParams((prev) => ({ + ...prev, + tokenAmount: "0", + tokenSymbol: token.symbol, + fromTokenAddress: token.address, + fromTokenIconUrl: token.logo, + fromChainDecimal: token.decimals, + })); + setTransferRoute(null); + } + + function onToTokenSelect(token: Token) { + setTransferParams((prev) => ({ + ...prev, + tokenAmount: "0", + toTokenSymbol: token.symbol, + toTokenAddress: token.address, + toTokenIconUrl: token.logo, + toChainDecimal: token.decimals, + })); + setTransferRoute(null); + } + + async function startTransfer() { + if (!transferRoute) { + toast({ + variant: "destructive", + title: "Something went wrong!", + description: "Please get a route first before attempting a transaction", + }); + return; + } + + setIsLoading(true); + setIsTransacting(true); + + const tokenAmount = convertEthToWei( + transferParams.tokenAmount, + transferParams.fromChainDecimal, + ); + + try { + if ( + transferParams.tokenSymbol !== transferParams.fromNativeTokenSymbol && + transferParams.fromChain !== "tron" + ) { + const checkAllowance = await swingServiceAPI?.getAllowanceRequest({ + bridge: transferRoute.quote.integration, + fromAddress: transferParams.fromUserAddress, + fromChain: transferParams.fromChain, + tokenAddress: transferParams.fromTokenAddress, + tokenSymbol: transferParams.tokenSymbol, + toChain: transferParams.toChain, + toTokenAddress: transferParams.toTokenAddress!, + toTokenSymbol: transferParams.toTokenSymbol!, + contractCall: false, + }); + + if (checkAllowance?.allowance! != tokenAmount) { + setTransStatus({ + status: `Wallet Interaction Required: Approval Token`, + }); + + const getApprovalTxData = + await swingServiceAPI?.getApprovalTxDataRequest({ + tokenAmount: tokenAmount, + bridge: transferRoute.quote.integration, + fromAddress: transferParams.fromUserAddress, + fromChain: transferParams.fromChain, + tokenAddress: transferParams.fromTokenAddress, + tokenSymbol: transferParams.tokenSymbol, + toChain: transferParams.toChain, + toTokenAddress: transferParams.toTokenAddress!, + toTokenSymbol: transferParams.toTokenSymbol!, + contractCall: false, + }); + + const txData: TransactionData = { + data: getApprovalTxData?.tx?.at(0)?.data!, + from: getApprovalTxData?.tx?.at(0)?.from!, + to: getApprovalTxData?.tx?.at(0)?.to!, + }; + + const txResponse = await signer?.sendTransaction(txData); + + const receipt = await txResponse?.wait(); + console.log("Transaction receipt:", receipt); + + setTransStatus({ status: "Token allowance approved" }); + } + } + + const transfer = await swingServiceAPI?.sendTransactionRequest({ + fromChain: transferParams.fromChain, + fromTokenAddress: transferParams.fromTokenAddress, + fromUserAddress: transferParams.fromUserAddress, + tokenSymbol: transferParams.tokenSymbol, + + toTokenAddress: transferParams.toTokenAddress!, + toChain: transferParams.toChain, + toTokenAmount: transferRoute.quote.amount, + toTokenSymbol: transferParams.toTokenSymbol!, + toUserAddress: transferParams.toUserAddress!, + integration: transferRoute.quote.integration, + + tokenAmount, + route: transferRoute.route, + type: "swap", + }); + + if (!transfer) { + toast({ + variant: "destructive", + title: "Something went wrong!", + description: "Transaction Failed", + }); + setIsLoading(false); + setIsTransacting(false); + setTransStatus(null); + return; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const txData: any = { + data: transfer?.tx?.data, + from: transfer?.tx?.from!, + to: transfer?.tx?.to!, + value: transfer?.tx?.value!, + txId: transfer?.tx?.txId!, + gasLimit: transfer?.tx?.gas!, + }; + + setTransStatus({ + status: "Wallet Interaction Required: Approve Transaction", + }); + + let txHash = ""; + + if (transferParams.fromChain === "tron") { + const hash = await sendTronTrans({ + ...txData, + from: transferParams.fromUserAddress, + }); + txHash = hash!; + } else { + const txResponse = await signer?.sendTransaction({ + data: txData.data, + from: txData.from, + to: txData.to, + value: txData.value, + gasLimit: txData.gasLimit, + }); + // Wait for the transaction to be mined + + const receipt = await txResponse?.wait(); + console.log("Transaction receipt:", receipt); + txHash = txResponse?.hash!; + } + + pollTransactionStatus(transfer?.id.toString()!, txHash); + } catch (error) { + console.error("Transfer Error:", error); + toast({ + variant: "destructive", + title: "Something went wrong!", + description: + // eslint-disable-next-line @typescript-eslint/no-explicit-any + `${(error as AxiosError & any)?.response?.data?.error} : ${(error as AxiosError & any)?.response?.data?.message}` ?? + (error as Error).message ?? + "Something went wrong", + }); + + setIsTransacting(false); + setTransStatus(null); + } + } + + function SelectFromChainPanel() { + return ( +
+ {transferParams.fromChain === "tron" ? ( + + ) : ( + + )} +
+ ); + } + + function SelectToChainPanel() { + return ( +
+ {transferParams.toChain === "tron" ? ( + + ) : ( + + )} +
+ ); + } + + function SelectFromTokenPanel() { + return ( +
+ +
+ ); + } + + function SelectToTokenPanel() { + return ( +
+ +
+ ); + } + + return ( +
+
+
+

Tron Gate

+
+ {" "} + +
+
+
+
+ {!isTransacting ? ( +
+
+

SRC

+ + +
+
{ + switchTransferParams() + }} + > + +
+ +
+ + + +

DEST

+
+
+ ) : ( +
+ {transStatus?.status} + +
+ )} +
+
+ { + debounced(e.target.value); + setTransferRoute(null); // Reset transfer route + }} + type="number" + /> +
+

+ {transferParams.tokenSymbol} +

+ +
+
+
+ { + setTransferRoute(null); // Reset transfer route + setTransferParams((prev) => ({ + ...prev, + amount: e.target.value, + })); + }} + /> +
+

+ {transferParams.toTokenSymbol} +

+ +
+
+

+ You'll receive: {formatUSD(transferRoute?.quote?.amountUSD! ?? 0)} +

+
+ + +
+
+ + +
+
+
+ +
+
+
+ ); +}; + +function formatUSD(amount: string) { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + currencyDisplay: "narrowSymbol", + }).format(Number(amount)); +} + +function shortentronAddress(address: string) { + return address.slice(0, 4) + "..." + address.slice(-4); +} + +export default Swap; diff --git a/examples/swaps-api-nextjs-tron/src/components/ThirdwebProvider.tsx b/examples/swaps-api-nextjs-tron/src/components/ThirdwebProvider.tsx new file mode 100644 index 0000000..b420a91 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ThirdwebProvider.tsx @@ -0,0 +1,3 @@ +"use client"; + +export { ThirdwebProvider } from "@thirdweb-dev/react"; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/Backdrop.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/Backdrop.tsx new file mode 100644 index 0000000..16d18d8 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/Backdrop.tsx @@ -0,0 +1,7 @@ +export const Backdrop = () => { + return ( +
+
+
+ ); +}; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/Button.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/Button.tsx new file mode 100644 index 0000000..251668b --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/Button.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import Link from "next/link"; +import clsx from "clsx"; + +const baseStyles = { + solid: + "inline-flex justify-center rounded-2xl py-2 px-3 text-sm font-semibold outline-2 outline-offset-2 transition-colors", + outline: + "inline-flex justify-center rounded-2xl border py-[calc(theme(spacing.2)-1px)] px-[calc(theme(spacing.3)-1px)] text-sm outline-2 outline-offset-2 transition-colors", +}; + +const variantStyles = { + solid: { + cyan: "relative overflow-hidden bg-cyan-500 text-white before:absolute before:inset-0 active:before:bg-transparent hover:before:bg-white/10 active:bg-cyan-600 active:text-white/80 before:transition-colors", + white: + "bg-white text-cyan-900 hover:bg-white/90 active:bg-white/90 active:text-cyan-900/70", + gray: "bg-gray-800 text-white hover:bg-gray-900 active:bg-gray-800 active:text-white/80", + }, + outline: { + gray: "border-gray-300 text-gray-700 hover:border-gray-400 active:bg-gray-100 active:text-gray-700/80", + }, +}; + +export const Button = function Button({ + variant = "solid", + color = "gray", + className, + href, + ...props +}: { + variant?: "solid" | "outline"; + color?: "cyan" | "white" | "gray"; + className?: string; + href?: string; +} & React.InputHTMLAttributes) { + const styles = variantStyles[variant]; + + className = clsx( + baseStyles[variant], + color in styles ? styles[color as keyof typeof styles] : {}, + className, + ); + + const isExternalLink = href?.startsWith("http"); + + return href ? ( + + ) : ( + + ); +}; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/ChainTokenItem.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/ChainTokenItem.tsx new file mode 100644 index 0000000..9154280 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/ChainTokenItem.tsx @@ -0,0 +1,17 @@ +export const ChainTokenItem = ({ + logo, + name, + onItemSelect, +}: { + logo: string; + name: string; + onItemSelect?: () => void | undefined; +}) => ( +
onItemSelect?.()} + > + {name} + {name} +
+); diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/Container.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/Container.tsx new file mode 100644 index 0000000..af45582 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/Container.tsx @@ -0,0 +1,17 @@ +import clsx from "clsx"; + +export function Container({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) { + return ( +
+ {children} +
+ ); +} diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/Header.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/Header.tsx new file mode 100644 index 0000000..f6aed8e --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/Header.tsx @@ -0,0 +1,159 @@ +"use client"; + +import Link from "next/link"; +import { Popover } from "@headlessui/react"; +import { AnimatePresence, motion } from "framer-motion"; + +import { Button } from "./Button"; +import { Container } from "./Container"; +import { NavLinks } from "./NavLinks"; +import React from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faGithub } from "@fortawesome/free-brands-svg-icons"; + +function MenuIcon({ className }: { className?: string }) { + return ( + + ); +} + +function ChevronUpIcon({ className }: { className?: string }) { + return ( + + ); +} + +function MobileNavLink({ + children, + href, +}: { + children: React.ReactNode; + href: string; +}) { + return ( + + {children} + + ); +} + +export function Header() { + return ( +
+ +
+ ); +} diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/Hero.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/Hero.tsx new file mode 100644 index 0000000..82f1c4f --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/Hero.tsx @@ -0,0 +1,21 @@ +"use client"; + +import React from "react"; +import { Container } from "./Container"; + +import sols from "images/logos/sols.png"; + +import SwapSDK from "../Swap"; +import Image from "next/image"; + +export function Hero() { + return ( +
+ +
+ +
+
+
+ ); +} diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/NavLinks.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/NavLinks.tsx new file mode 100644 index 0000000..23bffa2 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/NavLinks.tsx @@ -0,0 +1,40 @@ +import { useState } from "react"; +import Link from "next/link"; +import { AnimatePresence, motion } from "framer-motion"; + +export function NavLinks() { + const [hoveredIndex, setHoveredIndex] = useState(null); + + return ( + <> + {[ + ["Swap", "#"], + ["Documentation", "#"], + ].map(([label, href], index) => ( + setHoveredIndex(index)} + onMouseLeave={() => setHoveredIndex(null)} + > + + {hoveredIndex === index && ( + + )} + + {label} + + ))} + + ); +} diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/SelectChainPanel.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/SelectChainPanel.tsx new file mode 100644 index 0000000..94b6cf6 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/SelectChainPanel.tsx @@ -0,0 +1,43 @@ +import { useState } from "react"; +import { Popover, PopoverContent, PopoverTrigger } from "./popover"; +import { ChainTokenItem } from "./ChainTokenItem"; +import { Chain } from "interfaces/chain.interface"; + +const allowedChains = ["ethereum", "polygon", "avalanche", "arbitrum"]; + +export const SelectChainPanel = ({ + chains, + transferParams, + onChainSelect, + +}: { + chains: Chain[]; + transferParams: { chainIconUrl: string; chain: string | undefined }; + onChainSelect?: (chain: Chain) => void; +}) => { + const [isOpen, setIsOpen] = useState(false); + + return ( + + + + + + {chains.map( + (chain) => + allowedChains.includes(chain.slug) && ( + { + onChainSelect?.(chain); + setIsOpen(false); + }} + /> + ), + )} + + + ); +}; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/SelectTokenPanel.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/SelectTokenPanel.tsx new file mode 100644 index 0000000..2225c15 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/SelectTokenPanel.tsx @@ -0,0 +1,68 @@ +import { useEffect, useState } from "react"; +import { Popover, PopoverContent, PopoverTrigger } from "./popover"; +import { Token } from "interfaces/token.interface"; +import { ChainTokenItem } from "./ChainTokenItem"; + +const allowedTokens = ["MATIC", "USDC", "USDT", "ARB", "ETH", "AVAX", "POL"]; + +export const SelectTokenPanel = ({ + tokens, + transferParams, + onTokenSelect, +}: { + tokens: Token[]; + transferParams: { + tokenIconUrl: string; + chain: string | undefined; + token: string | undefined; + }; + onTokenSelect?: (token: Token) => void; +}) => { + const [isOpen, setIsOpen] = useState(false); + + const [filteredTokens, setFilteredTokens] = useState(tokens); + + useEffect(() => { + if (isOpen) { + setFilteredTokens(tokens); + } + }, [isOpen]); + + return ( + + + {transferParams.token} + + + { + const tokenResults = tokens?.filter((token) => + token.symbol + .toLowerCase() + .startsWith(e.target.value.toLowerCase()), + ); + setFilteredTokens(() => [...tokenResults!]); + }} + /> + {filteredTokens?.map( + (token, index) => + allowedTokens.includes(token.symbol) && ( + { + onTokenSelect?.(token); + setIsOpen(false); + }} + /> + ), + )} + + + ); +}; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/TransferHistoryPanel.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/TransferHistoryPanel.tsx new file mode 100644 index 0000000..7e54f77 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/TransferHistoryPanel.tsx @@ -0,0 +1,112 @@ +import { useEffect, useState } from "react"; +import { Popover, PopoverContent, PopoverTrigger } from "./popover"; +import { + Transaction, + TransactionResponseAPIResponse, +} from "interfaces/history.interface"; +import { MdOutlineHistory } from "react-icons/md"; +import clsx from "clsx"; +import { pendingStatuses } from "interfaces/send.interface"; +import { ISwingServiceAPI } from "interfaces/swing-service.interface"; + +export const TransferHistoryPanel = ({ + swingServiceAPI, + userAddress = "", + className, + onItemSelect, +}: { + userAddress: string; + className?: string; + onItemSelect?: (token: Transaction) => void; + swingServiceAPI: ISwingServiceAPI; +}) => { + const [isOpen, setIsOpen] = useState(false); + + const [historyList, sethistoryList] = useState([]); + const [filteredItems, setFilteredItems] = useState( + [], + ); + + useEffect(() => { + if (isOpen && userAddress.length) { + swingServiceAPI + ?.getTransationHistoryRequest({ userAddress }) + .then((response: TransactionResponseAPIResponse | undefined) => { + sethistoryList(response?.transactions); + setFilteredItems(response?.transactions); + }); + } + }, [isOpen]); + + return ( + + + + + + { + const historyResults = historyList?.filter( + (history) => + history.status + .toLowerCase() + .startsWith(e.target.value.toLowerCase()) || + history + .fromChainSlug!.toLowerCase() + .startsWith(e.target.value.toLowerCase()) || + history + .toChainSlug!.toLowerCase() + .startsWith(e.target.value.toLowerCase()), + ); + setFilteredItems(() => [...historyResults!]); + }} + /> + {filteredItems?.reverse().map((transaction, index) => ( +
onItemSelect?.(transaction)} + > +
+ ROUTE + + {transaction.fromChainSlug?.toUpperCase().substring(0, 3)} ( + {transaction.fromTokenSymbol}) {">"}{" "} + {transaction.toChainSlug?.toUpperCase().substring(0, 3)} ( + {transaction.toTokenSymbol}) + +
+ +
+ AMOUNT + + {transaction.toAmountUsdValue} USD + +
+ +
+ STATUS + + {transaction.status} + +
+
+ ))} +
+
+ ); +}; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/input.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/input.tsx new file mode 100644 index 0000000..433ae98 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; + +import { cn } from "lib"; + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ); + }, +); +Input.displayName = "Input"; + +export { Input }; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/label.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/label.tsx new file mode 100644 index 0000000..644132e --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/label.tsx @@ -0,0 +1,26 @@ +"use client"; + +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "lib"; + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", +); + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)); +Label.displayName = LabelPrimitive.Root.displayName; + +export { Label }; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/popover.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/popover.tsx new file mode 100644 index 0000000..640adb7 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/popover.tsx @@ -0,0 +1,31 @@ +"use client"; + +import * as React from "react"; +import * as PopoverPrimitive from "@radix-ui/react-popover"; + +import { cn } from "lib"; + +const Popover = PopoverPrimitive.Root; + +const PopoverTrigger = PopoverPrimitive.Trigger; + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; + +export { Popover, PopoverTrigger, PopoverContent }; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/toast.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/toast.tsx new file mode 100644 index 0000000..550ac85 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/toast.tsx @@ -0,0 +1,130 @@ +"use client"; + +import * as React from "react"; +import * as ToastPrimitives from "@radix-ui/react-toast"; +import { cva, type VariantProps } from "class-variance-authority"; +import { X } from "lucide-react"; + +import { cn } from "lib"; + +const ToastProvider = ToastPrimitives.Provider; + +const ToastViewport = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastViewport.displayName = ToastPrimitives.Viewport.displayName; + +const toastVariants = cva( + "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-xl border border-zinc-200 p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full dark:border-zinc-800", + { + variants: { + variant: { + default: + "border bg-white text-zinc-950 dark:bg-zinc-950 dark:text-zinc-50", + destructive: + "destructive group border-red-500 bg-red-500 text-zinc-50 dark:border-red-900 dark:bg-red-900 dark:text-zinc-50", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +const Toast = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, ...props }, ref) => { + return ( + + ); +}); +Toast.displayName = ToastPrimitives.Root.displayName; + +const ToastAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastAction.displayName = ToastPrimitives.Action.displayName; + +const ToastClose = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +ToastClose.displayName = ToastPrimitives.Close.displayName; + +const ToastTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastTitle.displayName = ToastPrimitives.Title.displayName; + +const ToastDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +ToastDescription.displayName = ToastPrimitives.Description.displayName; + +type ToastProps = React.ComponentPropsWithoutRef; + +type ToastActionElement = React.ReactElement; + +export { + type ToastProps, + type ToastActionElement, + ToastProvider, + ToastViewport, + Toast, + ToastTitle, + ToastDescription, + ToastClose, + ToastAction, +}; diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/toaster.tsx b/examples/swaps-api-nextjs-tron/src/components/ui/toaster.tsx new file mode 100644 index 0000000..32376bc --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/toaster.tsx @@ -0,0 +1,35 @@ +"use client"; + +import { + Toast, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, +} from "components/ui/toast"; +import { useToast } from "components/ui/use-toast"; + +export function Toaster() { + const { toasts } = useToast(); + + return ( + + {toasts.map(function ({ id, title, description, action, ...props }) { + return ( + +
+ {title && {title}} + {description && ( + {description} + )} +
+ {action} + +
+ ); + })} + +
+ ); +} diff --git a/examples/swaps-api-nextjs-tron/src/components/ui/use-toast.ts b/examples/swaps-api-nextjs-tron/src/components/ui/use-toast.ts new file mode 100644 index 0000000..7bb041c --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/components/ui/use-toast.ts @@ -0,0 +1,191 @@ +"use client"; + +// Inspired by react-hot-toast library +import * as React from "react"; + +import type { ToastActionElement, ToastProps } from "components/ui/toast"; + +const TOAST_LIMIT = 1; +const TOAST_REMOVE_DELAY = 1000000; + +type ToasterToast = ToastProps & { + id: string; + title?: React.ReactNode; + description?: React.ReactNode; + action?: ToastActionElement; +}; + +const actionTypes = { + ADD_TOAST: "ADD_TOAST", + UPDATE_TOAST: "UPDATE_TOAST", + DISMISS_TOAST: "DISMISS_TOAST", + REMOVE_TOAST: "REMOVE_TOAST", +} as const; + +let count = 0; + +function genId() { + count = (count + 1) % Number.MAX_SAFE_INTEGER; + return count.toString(); +} + +type ActionType = typeof actionTypes; + +type Action = + | { + type: ActionType["ADD_TOAST"]; + toast: ToasterToast; + } + | { + type: ActionType["UPDATE_TOAST"]; + toast: Partial; + } + | { + type: ActionType["DISMISS_TOAST"]; + toastId?: ToasterToast["id"]; + } + | { + type: ActionType["REMOVE_TOAST"]; + toastId?: ToasterToast["id"]; + }; + +interface State { + toasts: ToasterToast[]; +} + +const toastTimeouts = new Map>(); + +const addToRemoveQueue = (toastId: string) => { + if (toastTimeouts.has(toastId)) { + return; + } + + const timeout = setTimeout(() => { + toastTimeouts.delete(toastId); + dispatch({ + type: "REMOVE_TOAST", + toastId: toastId, + }); + }, TOAST_REMOVE_DELAY); + + toastTimeouts.set(toastId, timeout); +}; + +export const reducer = (state: State, action: Action): State => { + switch (action.type) { + case "ADD_TOAST": + return { + ...state, + toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), + }; + + case "UPDATE_TOAST": + return { + ...state, + toasts: state.toasts.map((t) => + t.id === action.toast.id ? { ...t, ...action.toast } : t, + ), + }; + + case "DISMISS_TOAST": { + const { toastId } = action; + + // ! Side effects ! - This could be extracted into a dismissToast() action, + // but I'll keep it here for simplicity + if (toastId) { + addToRemoveQueue(toastId); + } else { + state.toasts.forEach((toast) => { + addToRemoveQueue(toast.id); + }); + } + + return { + ...state, + toasts: state.toasts.map((t) => + t.id === toastId || toastId === undefined + ? { + ...t, + open: false, + } + : t, + ), + }; + } + case "REMOVE_TOAST": + if (action.toastId === undefined) { + return { + ...state, + toasts: [], + }; + } + return { + ...state, + toasts: state.toasts.filter((t) => t.id !== action.toastId), + }; + } +}; + +const listeners: Array<(state: State) => void> = []; + +let memoryState: State = { toasts: [] }; + +function dispatch(action: Action) { + memoryState = reducer(memoryState, action); + listeners.forEach((listener) => { + listener(memoryState); + }); +} + +type Toast = Omit; + +function toast({ ...props }: Toast) { + const id = genId(); + + const update = (props: ToasterToast) => + dispatch({ + type: "UPDATE_TOAST", + toast: { ...props, id }, + }); + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }); + + dispatch({ + type: "ADD_TOAST", + toast: { + ...props, + id, + open: true, + onOpenChange: (open) => { + if (!open) dismiss(); + }, + }, + }); + + return { + id: id, + dismiss, + update, + }; +} + +function useToast() { + const [state, setState] = React.useState(memoryState); + + React.useEffect(() => { + listeners.push(setState); + return () => { + const index = listeners.indexOf(setState); + if (index > -1) { + listeners.splice(index, 1); + } + }; + }, [state]); + + return { + ...state, + toast, + dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), + }; +} + +export { useToast, toast }; diff --git a/examples/swaps-api-nextjs-tron/src/images/logos/cnn.svg b/examples/swaps-api-nextjs-tron/src/images/logos/cnn.svg new file mode 100644 index 0000000..a640727 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/images/logos/cnn.svg @@ -0,0 +1,11 @@ + + + + + diff --git a/examples/swaps-api-nextjs-tron/src/images/logos/forbes.svg b/examples/swaps-api-nextjs-tron/src/images/logos/forbes.svg new file mode 100644 index 0000000..f3929e8 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/images/logos/forbes.svg @@ -0,0 +1,5 @@ + + + diff --git a/examples/swaps-api-nextjs-tron/src/images/logos/sols.png b/examples/swaps-api-nextjs-tron/src/images/logos/sols.png new file mode 100644 index 0000000..38664dc Binary files /dev/null and b/examples/swaps-api-nextjs-tron/src/images/logos/sols.png differ diff --git a/examples/swaps-api-nextjs-tron/src/images/logos/techcrunch.svg b/examples/swaps-api-nextjs-tron/src/images/logos/techcrunch.svg new file mode 100644 index 0000000..de91f48 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/images/logos/techcrunch.svg @@ -0,0 +1,5 @@ + + + diff --git a/examples/swaps-api-nextjs-tron/src/images/logos/wired.svg b/examples/swaps-api-nextjs-tron/src/images/logos/wired.svg new file mode 100644 index 0000000..e76e50b --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/images/logos/wired.svg @@ -0,0 +1,5 @@ + + + diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/allowance.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/allowance.interface.ts new file mode 100644 index 0000000..1493f6f --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/allowance.interface.ts @@ -0,0 +1,15 @@ +export interface AllowanceQueryParams { + bridge: string; + fromAddress: string; + fromChain: string; + toChain: string; + tokenAddress: string; + tokenSymbol: string; + toTokenSymbol: string; + toTokenAddress: string; + contractCall: boolean; +} + +export interface AllowanceAPIResponse { + allowance: string; +} diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/approval.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/approval.interface.ts new file mode 100644 index 0000000..bf0b00e --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/approval.interface.ts @@ -0,0 +1,33 @@ +export interface ApprovalTxDataQueryParams { + bridge: string; + fromAddress: string; + fromChain: string; + toChain: string; + tokenAddress: string; + tokenSymbol: string; + tokenAmount: string; + toTokenSymbol: string; + toTokenAddress: string; + contractCall: boolean; +} + +export interface ApprovalTxDataAPIResponse { + tx?: TransactionData[] | undefined; + fromChain: Chain | undefined; +} + +export interface TransactionData { + data: string; + to: string; + value?: string | undefined; + gas?: string | undefined; + from: string; + nonce?: number | undefined; +} + +interface Chain { + chainId: number; + name?: string | undefined; + slug: string; + protocolType: "evm" | "ibc" | "solana" | "multiversx"; +} diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/chain.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/chain.interface.ts new file mode 100644 index 0000000..0564684 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/chain.interface.ts @@ -0,0 +1,24 @@ +export interface Chain { + id: string; + slug: string; + name: string; + logo: string; + type: "solana" | "evm" | "bitcoin" | "ibc" | "multiversx"; + singleChainSwap: boolean; + singleChainStaking: boolean; + txExplorer?: string | undefined; + tokenExplorer?: string | undefined; + rpcUrl?: string | undefined; + nativeToken?: { + symbol: string; + decimals: number; + logo: string; + address: string; + chain: string; + }; +} + +export interface ChainsQueryParams { + integration?: string | undefined; + type?: "evm" | "ibc" | "solana" | "multiversx" | "bitcoin" | undefined; +} diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/history.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/history.interface.ts new file mode 100644 index 0000000..424bd5d --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/history.interface.ts @@ -0,0 +1,61 @@ +export interface Transaction { + type?: + | "swap" + | "deposit" + | "withdraw" + | "claim" + | "custom_contract" + | null + | undefined; + status: + | "Submitted" + | "Pending Source Chain" + | "Pending Destination Chain" + | "Completed" + | "Refund Required" + | "Refunded" + | "Failed Source Chain" + | "Failed Destination Chain" + | "Fallback" + | "Not Sent" + | "Claim Required"; + reason?: string; + bridge?: string | undefined; + txId?: string | undefined; + integration?: string; + needClaim?: boolean; + refundReason?: string; + errorReason?: string; + fromTokenAddress?: string; + fromChainId?: number; + fromChainSlug?: string; + fromAmount?: string; + fromAmountUsdValue?: string; + toTokenAddress?: string; + toChainId?: number; + toChainSlug?: string; + toAmount?: string; + toAmountUsdValue?: string; + fromChainTxHash?: string; + toChainTxHash?: string; + fromTokenSymbol?: string; + toTokenSymbol?: string; + fromUserAddress?: string; + toUserAddress?: string; + txStartedTimestamp?: number; + txCompletedTimestamp?: number; + updatedAt?: string; + createdAt?: string; + fallbackTokenAddress?: string; + fallbackAmount?: string; + id?: number; + projectId?: string; +} + +export interface TransactionResponseAPIResponse { + transactions?: Transaction[] | undefined; +} + +export interface TransactionQueryParams { + userAddress: string; +} diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/quote.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/quote.interface.ts new file mode 100644 index 0000000..979c028 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/quote.interface.ts @@ -0,0 +1,104 @@ +export interface QuoteQueryParams { + tokenSymbol: string; + dummyFromUserAddress?: string; + toTokenSymbol: string; + tokenAmount: string; + fromTokenAddress: string; + dummyToUserAddress?: string; + fromChain: string; + toChain: string; + fromUserAddress: string; + maxSlippage?: number; + toUserAddress: string; + partner?: string; + projectId?: string; + toTokenAddress: string; + debug?: string; + contractCall?: boolean; + skipGasEstimate?: boolean; + fee?: number; + nativeStaking?: boolean; +} + +export interface QuoteAPIResponse { + routes: Route[]; + fromToken: Token; + fromChain: Chain; + toToken: Token; + toChain: Chain; +} + +export interface Route { + route: RouteStep[]; + quote: Quote; + duration: number; + gas: string; + distribution?: { + [key: string]: number; + }; + gasUSD: string; +} + +export interface RouteStep { + bridge: string; + bridgeTokenAddress: string; + steps: ( + | "allowance" + | "approve" + | "send" + | "nativeStaking" + | "sign" + | "claim" + | "bridge" + )[]; + name: string; + part: number; + encryptionKeyRequired?: boolean | undefined; +} + +interface Quote { + bridgeFeeInNativeToken: string; + bridgeFee: string; + integration: string; + type: "swap" | "custom_contract" | "deposit" | "withdraw" | "claim" | null; + fromAmount?: string; + amount: string; + decimals: number; + amountUSD: string; + bridgeFeeUSD: string; + bridgeFeeInNativeTokenUSD: string; + fees: Fee[]; +} + +interface Fee { + type: "bridge" | "gas" | "partner"; + amount: string; + amountUSD: string; + tokenSymbol: string; + tokenAddress: string; + chainSlug: string; + decimals: number; + deductedFromSourceToken: boolean; +} + +interface Token { + symbol: string; + name?: string | undefined; + address: string; + decimals: number; + chainId?: number | undefined; + chain?: string | undefined; + logoURI?: string | undefined; +} + +interface Chain { + chainId: number; + name?: string; + slug: string; + protocolType: "evm" | "ibc" | "solana" | "multiversx"; + logo?: string; + isSingleChainSupported?: boolean; + blockExploreUrls?: string[]; + tokenExplorer?: string; + txExplorer?: string; +} diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/send.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/send.interface.ts new file mode 100644 index 0000000..4d1e315 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/send.interface.ts @@ -0,0 +1,76 @@ +export const pendingStatuses = [ + "Submitted", + "Not Sent", + "Pending Source Chain", + "Pending Destination Chain", +]; + +export interface SendTransactionApiResponse { + id: number; + fromToken: Token; + toToken: Token; + fromChain: Chain; + toChain: Chain; + route: Route[]; + tx?: TransactionDetails; +} + +interface Token { + symbol: string; + name?: string | undefined; + address: string; + decimals: number; + chainId?: number | undefined; + chain?: string | undefined; + logoURI?: string | undefined; +} + +interface Chain { + chainId: number; + name?: string; + slug: string; + protocolType: string; +} + +export interface TransactionDetails { + from: string; + to: string; + data: string; + value?: string; + nonce?: number; + txId?: string; + gas?: string; +} + +export interface SendTransactionPayload { + fromUserAddress: string; + toUserAddress: string; + tokenSymbol: string; + fromTokenAddress: string; + fromChain: string; + toTokenSymbol: string; + toTokenAddress: string; + toChain: string; + tokenAmount: string; + toTokenAmount: string; + route: Route[]; + projectId?: string; + type?: string; + integration?: string; +} + +interface Route { + bridge: string; + bridgeTokenAddress: string; + steps: ( + | "allowance" + | "approve" + | "send" + | "nativeStaking" + | "sign" + | "claim" + | "bridge" + )[]; + name: string; + part: number; +} diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/status.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/status.interface.ts new file mode 100644 index 0000000..4d6029c --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/status.interface.ts @@ -0,0 +1,48 @@ +export interface TransactionStatusAPIResponse { + type?: + | "swap" + | "deposit" + | "withdraw" + | "claim" + | "custom_contract" + | null + | undefined; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + status: "Pending" | "Success" | "Failed" | any; + reason?: string; + bridge?: string | undefined; + txId?: string | undefined; + integration?: string; + needClaim?: boolean; + refundReason?: string; + errorReason?: string; + fromTokenAddress?: string; + fromChainId?: number; + fromChainSlug?: string; + fromAmount?: string; + fromAmountUsdValue?: string; + toTokenAddress?: string; + toChainId?: number; + toChainSlug?: string; + toAmount?: string; + toAmountUsdValue?: string; + fromChainTxHash?: string; + toChainTxHash?: string; + fromTokenSymbol?: string; + toTokenSymbol?: string; + fromUserAddress?: string; + toUserAddress?: string; + txStartedTimestamp?: number; + txCompletedTimestamp?: number; + updatedAt?: string; + createdAt?: string; + fallbackTokenAddress?: string; + fallbackAmount?: string; + id?: string; + projectId?: string; +} + +export interface TransactionStatusParams { + id: string; + txHash?: string; +} diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/swing-service.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/swing-service.interface.ts new file mode 100644 index 0000000..940e52f --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/swing-service.interface.ts @@ -0,0 +1,48 @@ +import { + AllowanceQueryParams, + AllowanceAPIResponse, +} from "./allowance.interface"; +import { + ApprovalTxDataQueryParams, + ApprovalTxDataAPIResponse, +} from "./approval.interface"; +import { ChainsQueryParams, Chain } from "./chain.interface"; +import { + TransactionQueryParams, + TransactionResponseAPIResponse, +} from "./history.interface"; +import { QuoteAPIResponse, QuoteQueryParams } from "./quote.interface"; +import { + SendTransactionPayload, + SendTransactionApiResponse, +} from "./send.interface"; +import { + TransactionStatusParams, + TransactionStatusAPIResponse, +} from "./status.interface"; +import { TokenQueryParams, Token } from "./token.interface"; + +export interface ISwingServiceAPI { + getQuoteRequest( + queryParams: QuoteQueryParams, + ): Promise; + getAllowanceRequest( + queryParams: AllowanceQueryParams, + ): Promise; + getApprovalTxDataRequest( + queryParams: ApprovalTxDataQueryParams, + ): Promise; + getChainsRequest( + queryParams: ChainsQueryParams, + ): Promise; + getTokensRequest(queryParams: TokenQueryParams): Promise; + getTransationHistoryRequest( + queryParams: TransactionQueryParams, + ): Promise; + getTransationStatusRequest( + queryParams: TransactionStatusParams, + ): Promise; + sendTransactionRequest( + payload: SendTransactionPayload, + ): Promise; +} diff --git a/examples/swaps-api-nextjs-tron/src/interfaces/token.interface.ts b/examples/swaps-api-nextjs-tron/src/interfaces/token.interface.ts new file mode 100644 index 0000000..55a059a --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/interfaces/token.interface.ts @@ -0,0 +1,11 @@ +export interface TokenQueryParams { + chain: string; +} + +export interface Token { + symbol: string; + address: string; + logo: string; + decimals: number; + chain: string; +} diff --git a/examples/swaps-api-nextjs-tron/src/lib.ts b/examples/swaps-api-nextjs-tron/src/lib.ts new file mode 100644 index 0000000..365058c --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/lib.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/examples/swaps-api-nextjs-tron/src/services/requests.ts b/examples/swaps-api-nextjs-tron/src/services/requests.ts new file mode 100644 index 0000000..c8398cf --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/services/requests.ts @@ -0,0 +1,205 @@ +import { SwingSDK } from "@swing.xyz/sdk"; +import { + AllowanceAPIResponse, + AllowanceQueryParams, +} from "interfaces/allowance.interface"; +import { + ApprovalTxDataAPIResponse, + ApprovalTxDataQueryParams, +} from "interfaces/approval.interface"; +import { Chain, ChainsQueryParams } from "interfaces/chain.interface"; +import { + TransactionQueryParams, + TransactionResponseAPIResponse, +} from "interfaces/history.interface"; +import { QuoteAPIResponse, QuoteQueryParams } from "interfaces/quote.interface"; +import { + SendTransactionApiResponse, + SendTransactionPayload, +} from "interfaces/send.interface"; +import { + TransactionStatusAPIResponse, + TransactionStatusParams, +} from "interfaces/status.interface"; +import { ISwingServiceAPI } from "interfaces/swing-service.interface"; +import { Token, TokenQueryParams } from "interfaces/token.interface"; + +const projectId = "replug"; + +export class SwingServiceAPI implements ISwingServiceAPI { + private readonly swingSDK: SwingSDK; + + constructor() { + this.swingSDK = new SwingSDK({ + projectId: "replug", + // environment: 'testnet', + debug: true, + }); + } + + async getQuoteRequest( + queryParams: QuoteQueryParams, + ): Promise { + try { + const response = await this.swingSDK.crossChainAPI.GET( + "/v0/transfer/quote", + { + params: { + query: queryParams, + }, + }, + ); + return response.data; + } catch (error) { + console.error("Error fetching quote:", error); + throw error; + } + } + + async getAllowanceRequest( + queryParams: AllowanceQueryParams, + ): Promise { + try { + const response = await this.swingSDK.crossChainAPI.GET( + "/v0/transfer/allowance", + { + params: { + query: queryParams, + }, + }, + ); + + return response.data; + } catch (error) { + console.error("Error fetching allowance:", error); + throw error; + } + } + + async getApprovalTxDataRequest( + queryParams: ApprovalTxDataQueryParams, + ): Promise { + try { + const response = await this.swingSDK.crossChainAPI.GET( + "/v0/transfer/approve", + { + params: { + query: queryParams, + }, + }, + ); + return response.data; + } catch (error) { + console.error("Error fetching approval:", error); + throw error; + } + } + + async getChainsRequest( + queryParams: ChainsQueryParams, + ): Promise { + try { + const response = await this.swingSDK.platformAPI.GET("/chains", { + params: { + query: queryParams, + }, + }); + return response.data; + } catch (error) { + console.error("Error fetching approval:", error); + throw error; + } + } + + async getTokensRequest( + queryParams: TokenQueryParams, + ): Promise { + try { + const response = await this.swingSDK.platformAPI.GET("/tokens", { + params: { + query: queryParams, + }, + }); + return response.data; + } catch (error) { + console.error("Error fetching approval:", error); + throw error; + } + } + + async getTransationHistoryRequest( + queryParams: TransactionQueryParams, + ): Promise { + try { + const response = await this.swingSDK.crossChainAPI.GET( + "/v0/transfer/history", + { + params: { + query: queryParams, + }, + }, + ); + + return response.data; + } catch (error) { + console.error("Error fetching transaction status:", error); + + throw error; + } + } + + async getTransationStatusRequest( + queryParams: TransactionStatusParams, + ): Promise { + try { + const response = await this.swingSDK.platformAPI.GET( + "/projects/{projectId}/transactions/{transactionId}", + { + params: { + path: { + transactionId: queryParams.id, + projectId, + }, + query: { + txHash: queryParams.txHash, + }, + }, + }, + ); + + return response.data; + } catch (error) { + console.error("Error fetching transaction status:", error); + throw error; + } + } + + async sendTransactionRequest( + payload: SendTransactionPayload, + ): Promise { + try { + const response = await this.swingSDK.crossChainAPI.POST( + "/v0/transfer/send", + { + body: payload, + }, + ); + return response.data; + } catch (error) { + console.error("Error sending transaction:", error); + throw error; + } + } + + get isSDKConnected() { + return this.swingSDK.isReady; + } + + get swingSdk(): null | this { + if (this.isSDKConnected) { + return this; + } + + return null; + } +} diff --git a/examples/swaps-api-nextjs-tron/src/styles/globals.css b/examples/swaps-api-nextjs-tron/src/styles/globals.css new file mode 100644 index 0000000..61d3838 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/styles/globals.css @@ -0,0 +1,16 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + --tw-bg-opacity: 1; + background-color: rgb(72, 44, 44); +} + +@layer base { + input[type="number"]::-webkit-inner-spin-button, + input[type="number"]::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } +} \ No newline at end of file diff --git a/examples/swaps-api-nextjs-tron/src/types/global.d.ts b/examples/swaps-api-nextjs-tron/src/types/global.d.ts new file mode 100644 index 0000000..25cc8a9 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/types/global.d.ts @@ -0,0 +1,44 @@ +interface TronLinkProvider { + tronWeb: { + defaultAddress: { + base58: string; + hex: string; + }; + toHex: (address: string) => string; + toBase58: (address: string) => string; + transactionBuilder: { + sendTrx: ( + to: string, + amount: number, + from: string, + ) => Promise; + triggerSmartContract: ( + contractAddress: string, + functionSelector: string, + options: { + feeLimit: number; + callValue: number; + }, + parameters: any[], + issuerAddress?: string, + ) => Promise; + }; + trx: { + sign: (transaction: Transaction) => Promise; + sendRawTransaction: ( + signedTransaction: SignedTransaction, + ) => Promise<{ result: boolean; txid: string }>; + }; + }; + ready: boolean; + request: (args: { method: string; params?: any[] }) => Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + on: (event: string, handler: (args: any) => void) => void; + sign: (transaction: object) => Promise; + sendTransaction: (to: string, amount: number) => Promise<{ txid: string }>; + disconnect: () => Promise; +} + +interface Window { + tronLink?: TronLinkProvider; +} diff --git a/examples/swaps-api-nextjs-tron/src/types/transfer.types.ts b/examples/swaps-api-nextjs-tron/src/types/transfer.types.ts new file mode 100644 index 0000000..c6b1109 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/types/transfer.types.ts @@ -0,0 +1,29 @@ +import { QuoteQueryParams } from "interfaces/quote.interface"; + +interface ChainDecimals { + fromChainDecimal?: number; + toChainDecimal?: number; +} + +interface ChainIcons { + fromChainIconUrl?: string; + toChainIconUrl?: string; + fromTokenIconUrl?: string; + toTokenIconUrl?: string; +} + +interface NativeSourceToken { + fromNativeTokenSymbol?: string; + toNativeTokenSymbol?: string; +} + +interface ChainIds { + fromChainId?: string; + toChainId?: string; +} + +export type TransferParams = QuoteQueryParams & + ChainDecimals & + ChainIcons & + NativeSourceToken & + ChainIds; diff --git a/examples/swaps-api-nextjs-tron/src/utils/ethToWei.ts b/examples/swaps-api-nextjs-tron/src/utils/ethToWei.ts new file mode 100644 index 0000000..77c06be --- /dev/null +++ b/examples/swaps-api-nextjs-tron/src/utils/ethToWei.ts @@ -0,0 +1,19 @@ +import { ethers } from "ethers"; + +/** + * Converts ETH to Wei. + * + * @param {string | number} ethAmount The amount in ETH to convert. + * @returns {string} The amount in Wei as a string. + */ +export const convertEthToWei = (ethAmount: string, decimals = 18) => { + // Convert the amount to a string to handle both string and number inputs + const weiAmount = ethers.utils.parseUnits(ethAmount.toString(), decimals); + return weiAmount.toString(); +}; + +export const convertWeiToEth = (weiAmount: string, decimals = 18) => { + // Convert the amount to a string to handle both string and number inputs + const ethAmount = ethers.utils.formatUnits(weiAmount.toString(), decimals); + return Number(ethAmount.toString()).toFixed(2).trim(); +}; diff --git a/examples/swaps-api-nextjs-tron/styles/globals.css b/examples/swaps-api-nextjs-tron/styles/globals.css new file mode 100644 index 0000000..91f55f0 --- /dev/null +++ b/examples/swaps-api-nextjs-tron/styles/globals.css @@ -0,0 +1,4 @@ +@tailwind base; + @tailwind components; + @tailwind utilities; + \ No newline at end of file diff --git a/examples/swaps-api-nextjs-tron/tailwind.config.js b/examples/swaps-api-nextjs-tron/tailwind.config.js new file mode 100644 index 0000000..456c63a --- /dev/null +++ b/examples/swaps-api-nextjs-tron/tailwind.config.js @@ -0,0 +1,127 @@ +const defaultTheme = require('tailwindcss/defaultTheme'); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ['class'], + content: ['./pages/**/*.{ts,tsx}', './components/**/*.{ts,tsx}', './app/**/*.{ts,tsx}', './src/**/*.{ts,tsx}'], + prefix: '', + theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px', + }, + }, + fontSize: { + xs: ['0.75rem', { lineHeight: '1rem' }], + sm: ['0.875rem', { lineHeight: '1.5rem' }], + base: ['1rem', { lineHeight: '1.5rem' }], + lg: ['1.125rem', { lineHeight: '2rem' }], + xl: ['1.25rem', { lineHeight: '1.75rem' }], + '2xl': ['1.5rem', { lineHeight: '2rem' }], + '3xl': ['2rem', { lineHeight: '3rem' }], + '4xl': ['2.5rem', { lineHeight: '3rem' }], + '5xl': ['3rem', { lineHeight: '1' }], + '6xl': ['3.75rem', { lineHeight: '1' }], + '7xl': ['4.5rem', { lineHeight: '1' }], + '8xl': ['6rem', { lineHeight: '1' }], + '9xl': ['8rem', { lineHeight: '1' }], + }, + extend: { + colors: ({ colors }) => ({ + gray: colors.neutral, + }), + fontFamily: { + sans: ['Consolas', 'Courier New', ...defaultTheme.fontFamily.sans], + }, + keyframes: { + 'fade-in': { + from: { + opacity: 0, + }, + to: { + opacity: 1, + }, + }, + marquee: { + '100%': { + transform: 'translateY(-50%)', + }, + }, + 'spin-reverse': { + to: { + transform: 'rotate(-360deg)', + }, + }, + }, + maxWidth: { + '2xl': '40rem', + }, + colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + '4xl': '2rem', + '5xl': '2.5rem', + }, + keyframes: { + 'accordion-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: '0' }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', + 'fade-in': 'fade-in 0.5s linear forwards', + marquee: 'marquee var(--marquee-duration) linear infinite', + 'spin-slow': 'spin 4s linear infinite', + 'spin-slower': 'spin 6s linear infinite', + 'spin-reverse': 'spin-reverse 1s linear infinite', + 'spin-reverse-slow': 'spin-reverse 4s linear infinite', + 'spin-reverse-slower': 'spin-reverse 6s linear infinite', + }, + }, + }, + plugins: [require('tailwindcss-animate'), require('@tailwindcss/forms')], +}; diff --git a/examples/swaps-api-nextjs-tron/tsconfig.json b/examples/swaps-api-nextjs-tron/tsconfig.json new file mode 100644 index 0000000..0bf84cb --- /dev/null +++ b/examples/swaps-api-nextjs-tron/tsconfig.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "typescript-config/nextjs.json", + "compilerOptions": { + "rootDir": ".", + "baseUrl": "./src", + "plugins": [ + { + "name": "next" + } + ] + }, + "include": ["next-env.d.ts", "src", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/examples/swaps-api-nextjs-tron/turbo.json b/examples/swaps-api-nextjs-tron/turbo.json new file mode 100644 index 0000000..1dbe6de --- /dev/null +++ b/examples/swaps-api-nextjs-tron/turbo.json @@ -0,0 +1,11 @@ +{ + "extends": ["//"], + "$schema": "https://turbo.build/schema.json", + "pipeline": { + "build": { + "outputs": [".next/**", "!.next/cache/**"], + "env": ["NEXT_PUBLIC_THIRD_WEB_CLIENT_ID", "THIRD_WEB_SECRET_KEY"], + "outputMode": "new-only" + } + } +} diff --git a/yarn.lock b/yarn.lock index 2935fa6..5324357 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2239,6 +2239,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.25.0": + version: 7.25.4 + resolution: "@babel/runtime@npm:7.25.4" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10/70d2a420c24a3289ea6c4addaf3a1c4186bc3d001c92445faa3cd7601d7d2fbdb32c63b3a26b9771e20ff2f511fa76b726bf256f823cdb95bc37b8eadbd02f70 + languageName: node + linkType: hard + "@babel/template@npm:7.20.7": version: 7.20.7 resolution: "@babel/template@npm:7.20.7" @@ -2366,6 +2375,18 @@ __metadata: languageName: node linkType: hard +"@cosmjs/amino@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/amino@npm:0.32.4" + dependencies: + "@cosmjs/crypto": "npm:^0.32.4" + "@cosmjs/encoding": "npm:^0.32.4" + "@cosmjs/math": "npm:^0.32.4" + "@cosmjs/utils": "npm:^0.32.4" + checksum: 10/2644d268c10990ad3c6509f241c3b789064d1e38046ad80aed306c3cc17c70ffb7198f3fd19b515b5e22cea483062d0f5bf74bc1963a840534dfc495a3233827 + languageName: node + linkType: hard + "@cosmjs/cosmwasm-stargate@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/cosmwasm-stargate@npm:0.32.3" @@ -2384,6 +2405,24 @@ __metadata: languageName: node linkType: hard +"@cosmjs/cosmwasm-stargate@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/cosmwasm-stargate@npm:0.32.4" + dependencies: + "@cosmjs/amino": "npm:^0.32.4" + "@cosmjs/crypto": "npm:^0.32.4" + "@cosmjs/encoding": "npm:^0.32.4" + "@cosmjs/math": "npm:^0.32.4" + "@cosmjs/proto-signing": "npm:^0.32.4" + "@cosmjs/stargate": "npm:^0.32.4" + "@cosmjs/tendermint-rpc": "npm:^0.32.4" + "@cosmjs/utils": "npm:^0.32.4" + cosmjs-types: "npm:^0.9.0" + pako: "npm:^2.0.2" + checksum: 10/d3c0f771409dc7079864e3357f9999cc85e2a2af825075910bb52d643024363ecbe19f516634fc9b8e3fac7a94e68eb96b32c362d000ff30369e7442778cc2d4 + languageName: node + linkType: hard + "@cosmjs/crypto@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/crypto@npm:0.32.3" @@ -2399,6 +2438,21 @@ __metadata: languageName: node linkType: hard +"@cosmjs/crypto@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/crypto@npm:0.32.4" + dependencies: + "@cosmjs/encoding": "npm:^0.32.4" + "@cosmjs/math": "npm:^0.32.4" + "@cosmjs/utils": "npm:^0.32.4" + "@noble/hashes": "npm:^1" + bn.js: "npm:^5.2.0" + elliptic: "npm:^6.5.4" + libsodium-wrappers-sumo: "npm:^0.7.11" + checksum: 10/68b78aa5c91bdd81cefdd629d2574bdd2e41bcd27b38b18b86a38fa768b1096930313bcea5bb8479f32358ac4465d6e583f4cc253b942af428cf98cf4e84d4b1 + languageName: node + linkType: hard + "@cosmjs/encoding@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/encoding@npm:0.32.3" @@ -2410,6 +2464,17 @@ __metadata: languageName: node linkType: hard +"@cosmjs/encoding@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/encoding@npm:0.32.4" + dependencies: + base64-js: "npm:^1.3.0" + bech32: "npm:^1.1.4" + readonly-date: "npm:^1.0.0" + checksum: 10/592f1ec81f9a4216fa047f65b6f8f5c00b3dc41f19f22e590d98954810f22b247137e7a8de62e7c93bcc7a557fd2c8d87cefee5b39a951a848790259b8e95db7 + languageName: node + linkType: hard + "@cosmjs/json-rpc@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/json-rpc@npm:0.32.3" @@ -2420,6 +2485,16 @@ __metadata: languageName: node linkType: hard +"@cosmjs/json-rpc@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/json-rpc@npm:0.32.4" + dependencies: + "@cosmjs/stream": "npm:^0.32.4" + xstream: "npm:^11.14.0" + checksum: 10/a837c8954ed3f1457e8359233b3242915d822a2691b4ec7d194d9145bade39a7bf2f02c70c4785d106c0b27e2af40435f93da4a5b4462e31ce05fc341496e56f + languageName: node + linkType: hard + "@cosmjs/math@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/math@npm:0.32.3" @@ -2429,6 +2504,15 @@ __metadata: languageName: node linkType: hard +"@cosmjs/math@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/math@npm:0.32.4" + dependencies: + bn.js: "npm:^5.2.0" + checksum: 10/1b67f46cb8ace0dcd01bc4a63bc935d48624de5a342144944d1c45d7b49fa3f83f052dc70be20b39314611c6b31a6b6ad0ff1c7d655cf5f6a4dc28e22759588f + languageName: node + linkType: hard + "@cosmjs/proto-signing@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/proto-signing@npm:0.32.3" @@ -2443,6 +2527,20 @@ __metadata: languageName: node linkType: hard +"@cosmjs/proto-signing@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/proto-signing@npm:0.32.4" + dependencies: + "@cosmjs/amino": "npm:^0.32.4" + "@cosmjs/crypto": "npm:^0.32.4" + "@cosmjs/encoding": "npm:^0.32.4" + "@cosmjs/math": "npm:^0.32.4" + "@cosmjs/utils": "npm:^0.32.4" + cosmjs-types: "npm:^0.9.0" + checksum: 10/6369d26a2949236a525162358fdd78ffefc13710db279be764d22f79e103941f7681e86dd4a770b87c41b9bfb0fa162ccb629c70b6b7b8de49e7e7393f7353da + languageName: node + linkType: hard + "@cosmjs/socket@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/socket@npm:0.32.3" @@ -2455,6 +2553,18 @@ __metadata: languageName: node linkType: hard +"@cosmjs/socket@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/socket@npm:0.32.4" + dependencies: + "@cosmjs/stream": "npm:^0.32.4" + isomorphic-ws: "npm:^4.0.1" + ws: "npm:^7" + xstream: "npm:^11.14.0" + checksum: 10/37a4dfb6a03c4caa0227770933d933a5a7225ce9d1de3a78c66ff90299c7869779c17f039710938d6e9ffc819ee2f09c6413197ab6e95e7151635606b290c0cb + languageName: node + linkType: hard + "@cosmjs/stargate@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/stargate@npm:0.32.3" @@ -2473,6 +2583,24 @@ __metadata: languageName: node linkType: hard +"@cosmjs/stargate@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/stargate@npm:0.32.4" + dependencies: + "@confio/ics23": "npm:^0.6.8" + "@cosmjs/amino": "npm:^0.32.4" + "@cosmjs/encoding": "npm:^0.32.4" + "@cosmjs/math": "npm:^0.32.4" + "@cosmjs/proto-signing": "npm:^0.32.4" + "@cosmjs/stream": "npm:^0.32.4" + "@cosmjs/tendermint-rpc": "npm:^0.32.4" + "@cosmjs/utils": "npm:^0.32.4" + cosmjs-types: "npm:^0.9.0" + xstream: "npm:^11.14.0" + checksum: 10/fc65de1cbd8f381d804c4d8cd277eb089ec9239b3acbc008d83933dd8b61102481d5b69346e17e24f3296f08f27c06c34b7666462a704b75563b1e5974b7a258 + languageName: node + linkType: hard + "@cosmjs/stream@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/stream@npm:0.32.3" @@ -2482,6 +2610,15 @@ __metadata: languageName: node linkType: hard +"@cosmjs/stream@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/stream@npm:0.32.4" + dependencies: + xstream: "npm:^11.14.0" + checksum: 10/fa55d3f29e8a7c56d5da4128989709f0b02fcee5319efa2504f62e4f2b0c64725ccb603d88f435f6fbe308dc6ef76b1fd3ea5aecfcb877a871c111366ddd3489 + languageName: node + linkType: hard + "@cosmjs/tendermint-rpc@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/tendermint-rpc@npm:0.32.3" @@ -2500,6 +2637,24 @@ __metadata: languageName: node linkType: hard +"@cosmjs/tendermint-rpc@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/tendermint-rpc@npm:0.32.4" + dependencies: + "@cosmjs/crypto": "npm:^0.32.4" + "@cosmjs/encoding": "npm:^0.32.4" + "@cosmjs/json-rpc": "npm:^0.32.4" + "@cosmjs/math": "npm:^0.32.4" + "@cosmjs/socket": "npm:^0.32.4" + "@cosmjs/stream": "npm:^0.32.4" + "@cosmjs/utils": "npm:^0.32.4" + axios: "npm:^1.6.0" + readonly-date: "npm:^1.0.0" + xstream: "npm:^11.14.0" + checksum: 10/e82f4e340ad592bc9c957e6bd664d5ac18344baacdac9892fd05aa1e3701f947fbdbde9e0f57616b6c7e61cd1129cf29a3eb387f44169ff970a339793c22446c + languageName: node + linkType: hard + "@cosmjs/utils@npm:^0.32.3": version: 0.32.3 resolution: "@cosmjs/utils@npm:0.32.3" @@ -2507,6 +2662,13 @@ __metadata: languageName: node linkType: hard +"@cosmjs/utils@npm:^0.32.4": + version: 0.32.4 + resolution: "@cosmjs/utils@npm:0.32.4" + checksum: 10/92f4d0878bedda53d113894ebadd31a6d189fdd45f2f884049ee99c2d7f907703b6dc40c8bc9b88b912443c38f3dbf77a9474183f41b85dec1f9ef3bec9d86c4 + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -4872,7 +5034,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.4.0": +"@noble/curves@npm:1.4.0, @noble/curves@npm:^1.4.0": version: 1.4.0 resolution: "@noble/curves@npm:1.4.0" dependencies: @@ -4881,6 +5043,24 @@ __metadata: languageName: node linkType: hard +"@noble/curves@npm:^1.4.2": + version: 1.5.0 + resolution: "@noble/curves@npm:1.5.0" + dependencies: + "@noble/hashes": "npm:1.4.0" + checksum: 10/d7707d756a887a0daf9eba709526017ac6905d4be58760947e0f0652961926295ba62a5a699d9a9f0bf2a2e0c6803381373e14542be5ff3885b3434bb59be86c + languageName: node + linkType: hard + +"@noble/curves@npm:~1.4.0": + version: 1.4.2 + resolution: "@noble/curves@npm:1.4.2" + dependencies: + "@noble/hashes": "npm:1.4.0" + checksum: 10/f433a2e8811ae345109388eadfa18ef2b0004c1f79417553241db4f0ad0d59550be6298a4f43d989c627e9f7551ffae6e402a4edf0173981e6da95fc7cab5123 + languageName: node + linkType: hard + "@noble/hashes@npm:1.3.1": version: 1.3.1 resolution: "@noble/hashes@npm:1.3.1" @@ -4895,7 +5075,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1, @noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.4.0": +"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1, @noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.4.0": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" checksum: 10/e156e65794c473794c52fa9d06baf1eb20903d0d96719530f523cc4450f6c721a957c544796e6efd0197b2296e7cd70efeb312f861465e17940a3e3c7e0febc6 @@ -6706,6 +6886,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.6": + version: 1.1.7 + resolution: "@scure/base@npm:1.1.7" + checksum: 10/fc50ffaab36cb46ff9fa4dc5052a06089ab6a6707f63d596bb34aaaec76173c9a564ac312a0b981b5e7a5349d60097b8878673c75d6cbfc4da7012b63a82099b + languageName: node + linkType: hard + "@scure/bip32@npm:1.3.1": version: 1.3.1 resolution: "@scure/bip32@npm:1.3.1" @@ -6728,6 +6915,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.4.0": + version: 1.4.0 + resolution: "@scure/bip32@npm:1.4.0" + dependencies: + "@noble/curves": "npm:~1.4.0" + "@noble/hashes": "npm:~1.4.0" + "@scure/base": "npm:~1.1.6" + checksum: 10/6cd5062d902564d9e970597ec8b1adacb415b2eadfbb95aee1a1a0480a52eb0de4d294d3753aa8b48548064c9795ed108d348a31a8ce3fc88785377bb12c63b9 + languageName: node + linkType: hard + "@scure/bip39@npm:1.2.1": version: 1.2.1 resolution: "@scure/bip39@npm:1.2.1" @@ -6738,6 +6936,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.3.0": + version: 1.3.0 + resolution: "@scure/bip39@npm:1.3.0" + dependencies: + "@noble/hashes": "npm:~1.4.0" + "@scure/base": "npm:~1.1.6" + checksum: 10/7d71fd58153de22fe8cd65b525f6958a80487bc9d0fbc32c71c328aeafe41fa259f989d2f1e0fa4fdfeaf83b8fcf9310d52ed9862987e46c2f2bfb9dd8cf9fc1 + languageName: node + linkType: hard + "@sentry-internal/feedback@npm:7.114.0": version: 7.114.0 resolution: "@sentry-internal/feedback@npm:7.114.0" @@ -6749,6 +6957,17 @@ __metadata: languageName: node linkType: hard +"@sentry-internal/feedback@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry-internal/feedback@npm:7.119.0" + dependencies: + "@sentry/core": "npm:7.119.0" + "@sentry/types": "npm:7.119.0" + "@sentry/utils": "npm:7.119.0" + checksum: 10/9f3848f4d0cb7e2744f3a4d6b08070094a9cc04b9a604e03aee1c291e4b0f2d2b099aa50e7865847abf42db322e780a60b120d91f7ccfdde6c17e546a47a704c + languageName: node + linkType: hard + "@sentry-internal/replay-canvas@npm:7.114.0": version: 7.114.0 resolution: "@sentry-internal/replay-canvas@npm:7.114.0" @@ -6761,6 +6980,18 @@ __metadata: languageName: node linkType: hard +"@sentry-internal/replay-canvas@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry-internal/replay-canvas@npm:7.119.0" + dependencies: + "@sentry/core": "npm:7.119.0" + "@sentry/replay": "npm:7.119.0" + "@sentry/types": "npm:7.119.0" + "@sentry/utils": "npm:7.119.0" + checksum: 10/077ed5234b34b99d1c37fffd821fb5bea28bc0a63c923dd906c02ff3d1ddc4a70ce34e4c7ab093b697617666878a1d144f72ccdb1aefc75fa7d192dad88649f5 + languageName: node + linkType: hard + "@sentry-internal/tracing@npm:7.114.0": version: 7.114.0 resolution: "@sentry-internal/tracing@npm:7.114.0" @@ -6772,6 +7003,17 @@ __metadata: languageName: node linkType: hard +"@sentry-internal/tracing@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry-internal/tracing@npm:7.119.0" + dependencies: + "@sentry/core": "npm:7.119.0" + "@sentry/types": "npm:7.119.0" + "@sentry/utils": "npm:7.119.0" + checksum: 10/dd1e58c7bd19671e094aaeea0fd3006668cb611b6029f98989a4cc72fb3d2df4f126e526f517fe9071c4eb6ec315e545403b2ae7bae0f0fdf1986c69ce8cf2cd + languageName: node + linkType: hard + "@sentry/browser@npm:7.114.0": version: 7.114.0 resolution: "@sentry/browser@npm:7.114.0" @@ -6788,6 +7030,22 @@ __metadata: languageName: node linkType: hard +"@sentry/browser@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry/browser@npm:7.119.0" + dependencies: + "@sentry-internal/feedback": "npm:7.119.0" + "@sentry-internal/replay-canvas": "npm:7.119.0" + "@sentry-internal/tracing": "npm:7.119.0" + "@sentry/core": "npm:7.119.0" + "@sentry/integrations": "npm:7.119.0" + "@sentry/replay": "npm:7.119.0" + "@sentry/types": "npm:7.119.0" + "@sentry/utils": "npm:7.119.0" + checksum: 10/88f82e2ef2647a19d8ad4c16c3a1ff462ee1f79ad3596c032aeb73b5b1dd20a01b6abebe88eadc40a36dec323f600f7ea77197a6b08a63f457a54c0ba09ed6da + languageName: node + linkType: hard + "@sentry/core@npm:7.114.0": version: 7.114.0 resolution: "@sentry/core@npm:7.114.0" @@ -6798,6 +7056,16 @@ __metadata: languageName: node linkType: hard +"@sentry/core@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry/core@npm:7.119.0" + dependencies: + "@sentry/types": "npm:7.119.0" + "@sentry/utils": "npm:7.119.0" + checksum: 10/d7416cef5126706887df2843a5dc890821b7fed2be40a0866b5dd5e185c4aae8944689818c75a1d2ce9ce0bd3fd0ca141b5b9f6b22d8a09b033150d2aa5e9fd2 + languageName: node + linkType: hard + "@sentry/integrations@npm:7.114.0": version: 7.114.0 resolution: "@sentry/integrations@npm:7.114.0" @@ -6810,6 +7078,18 @@ __metadata: languageName: node linkType: hard +"@sentry/integrations@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry/integrations@npm:7.119.0" + dependencies: + "@sentry/core": "npm:7.119.0" + "@sentry/types": "npm:7.119.0" + "@sentry/utils": "npm:7.119.0" + localforage: "npm:^1.8.1" + checksum: 10/a4c3609d831be4b47753bd9ed5634e834f59749d25ea80df46df4a6644a83efd3d31c727dbb3cabc1952bf9907fb5a6ff104dde9bf5b2121aa3b43beb901e1fe + languageName: node + linkType: hard + "@sentry/react@npm:7.114.0": version: 7.114.0 resolution: "@sentry/react@npm:7.114.0" @@ -6837,6 +7117,18 @@ __metadata: languageName: node linkType: hard +"@sentry/replay@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry/replay@npm:7.119.0" + dependencies: + "@sentry-internal/tracing": "npm:7.119.0" + "@sentry/core": "npm:7.119.0" + "@sentry/types": "npm:7.119.0" + "@sentry/utils": "npm:7.119.0" + checksum: 10/aac68c3cfd4981dd73d7c15de04036cdef8e624ce62b8a76e1933956b02f54fcc939767b04803efd3bb397803ab0b6a67166a2edbad754ecf2a25394d7a1c740 + languageName: node + linkType: hard + "@sentry/types@npm:7.114.0": version: 7.114.0 resolution: "@sentry/types@npm:7.114.0" @@ -6844,6 +7136,13 @@ __metadata: languageName: node linkType: hard +"@sentry/types@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry/types@npm:7.119.0" + checksum: 10/231c567f7717d1d0a49fdc8a627aa28931b2b41795b1faed3c3308874b7dfb8781846b8e8e4fa7ce197e254a566c773848d4c301200611cc8583dae2f9859e99 + languageName: node + linkType: hard + "@sentry/utils@npm:7.114.0": version: 7.114.0 resolution: "@sentry/utils@npm:7.114.0" @@ -6853,6 +7152,15 @@ __metadata: languageName: node linkType: hard +"@sentry/utils@npm:7.119.0": + version: 7.119.0 + resolution: "@sentry/utils@npm:7.119.0" + dependencies: + "@sentry/types": "npm:7.119.0" + checksum: 10/66eb9a5a6d3366d34821fff59af5e9a64f60eec54c3aeed9941acb81413e19512ded36be919bd5504b5b4720b3be28e4b9e80ea05a326474d87fe636759c6af1 + languageName: node + linkType: hard + "@sigstore/bundle@npm:^1.1.0": version: 1.1.0 resolution: "@sigstore/bundle@npm:1.1.0" @@ -6936,6 +7244,29 @@ __metadata: languageName: node linkType: hard +"@solana/web3.js@npm:^1.95.2": + version: 1.95.3 + resolution: "@solana/web3.js@npm:1.95.3" + dependencies: + "@babel/runtime": "npm:^7.25.0" + "@noble/curves": "npm:^1.4.2" + "@noble/hashes": "npm:^1.4.0" + "@solana/buffer-layout": "npm:^4.0.1" + agentkeepalive: "npm:^4.5.0" + bigint-buffer: "npm:^1.1.5" + bn.js: "npm:^5.2.1" + borsh: "npm:^0.7.0" + bs58: "npm:^4.0.1" + buffer: "npm:6.0.3" + fast-stable-stringify: "npm:^1.0.0" + jayson: "npm:^4.1.1" + node-fetch: "npm:^2.7.0" + rpc-websockets: "npm:^9.0.2" + superstruct: "npm:^2.0.2" + checksum: 10/25bdc5100faae6d3e48cbfac965b129060bec61669dcd75d0a525cea3ce8d23632ebea249a7b21616c89641bf7ea26d18826ce51246274b6aa1278d32180c870 + languageName: node + linkType: hard + "@stablelib/aead@npm:^1.0.1": version: 1.0.1 resolution: "@stablelib/aead@npm:1.0.1" @@ -7258,6 +7589,15 @@ __metadata: languageName: node linkType: hard +"@swc/helpers@npm:^0.5.11": + version: 0.5.12 + resolution: "@swc/helpers@npm:0.5.12" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10/f04a4728c38a6e75a85b077408e175e1abbc1650a76e4b78008d6380ca1422d9f7f4f9fe61b42f8fb889140f05ced6a5a9983037a8d5d8086bf6bc80a0b2118b + languageName: node + linkType: hard + "@swing.xyz/cross-chain-api@npm:^0.2.0": version: 0.2.0 resolution: "@swing.xyz/cross-chain-api@npm:0.2.0" @@ -7301,13 +7641,6 @@ __metadata: languageName: node linkType: hard -"@swing.xyz/platform-api@npm:^0.7.0": - version: 0.7.0 - resolution: "@swing.xyz/platform-api@npm:0.7.0" - checksum: 10/7f81d928aea5b614116b0c0f4c18b5ca60df3b67f04daee54ae35df0b9af0ec39294519ab7cc5c2832c45b8ee30068ad944c33cd6921518fc8b360e3bcf28dd9 - languageName: node - linkType: hard - "@swing.xyz/sdk@npm:0.53.2, @swing.xyz/sdk@npm:^0.53.2": version: 0.53.2 resolution: "@swing.xyz/sdk@npm:0.53.2" @@ -7336,12 +7669,12 @@ __metadata: languageName: node linkType: hard -"@swing.xyz/sdk@npm:^0.54.0": - version: 0.54.0 - resolution: "@swing.xyz/sdk@npm:0.54.0" +"@swing.xyz/sdk@npm:^0.57.5": + version: 0.57.5 + resolution: "@swing.xyz/sdk@npm:0.57.5" dependencies: - "@cosmjs/cosmwasm-stargate": "npm:^0.32.3" - "@cosmjs/stargate": "npm:^0.32.3" + "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" + "@cosmjs/stargate": "npm:^0.32.4" "@ethersproject/abi": "npm:^5.7.0" "@ethersproject/bignumber": "npm:^5.7.0" "@ethersproject/constants": "npm:^5.7.0" @@ -7353,14 +7686,16 @@ __metadata: "@multiversx/sdk-core": "npm:^11.6.0" "@multiversx/sdk-extension-provider": "npm:^2.0.7" "@multiversx/sdk-network-providers": "npm:^1.5.0" - "@sentry/browser": "npm:7.114.0" - "@solana/web3.js": "npm:^1.91.8" - "@swing.xyz/cross-chain-api": "npm:^0.2.0" - "@swing.xyz/platform-api": "npm:^0.7.0" + "@sentry/browser": "npm:7.119.0" + "@solana/web3.js": "npm:^1.95.2" + "@ton/core": "npm:^0.56.3" + "@ton/crypto": "npm:^3.3.0" + "@ton/ton": "npm:^14.0.0" ethers: "npm:^5.7.2" eventemitter3: "npm:^4.0.7" - viem: "npm:^2.10.5" - checksum: 10/4f336258168869913c2805bb19915f5afba087ef04602e7df09dbed1e4fd9c1d79c998f0dbc20cafcf84f8f2def5001ce1bc83d9267dd3fbd2422f2d7595be5f + lodash.throttle: "npm:^4.1.1" + viem: "npm:^2.19.4" + checksum: 10/8d871a1b68463ea5fb76a26a64f12ab533680f1a0f8fb620e6e092965f59c98633c1135d21042e0b3082948464957f7b70a4aa59d41395357043bf3e7cc462e5 languageName: node linkType: hard @@ -8039,6 +8374,53 @@ __metadata: languageName: node linkType: hard +"@ton/core@npm:^0.56.3": + version: 0.56.3 + resolution: "@ton/core@npm:0.56.3" + dependencies: + symbol.inspect: "npm:1.0.1" + peerDependencies: + "@ton/crypto": ">=3.2.0" + checksum: 10/5fe0284bc66789e4b408cce7d459d30d0f8477ce981337ef7bfe7beeee93eda575e8178338fb3d73d47cd8f78065ed84988b82db839b463a39e576be39831243 + languageName: node + linkType: hard + +"@ton/crypto-primitives@npm:2.1.0": + version: 2.1.0 + resolution: "@ton/crypto-primitives@npm:2.1.0" + dependencies: + jssha: "npm:3.2.0" + checksum: 10/71119f74461ae17bf2cfe7e0a6fcea8d4e359665ea6878b0c935cfd83ca0d84f9c299df3467adb1b1b7ba50f7d446732f2c13b5ea5e26dc1703a6dc24063be3a + languageName: node + linkType: hard + +"@ton/crypto@npm:^3.3.0": + version: 3.3.0 + resolution: "@ton/crypto@npm:3.3.0" + dependencies: + "@ton/crypto-primitives": "npm:2.1.0" + jssha: "npm:3.2.0" + tweetnacl: "npm:1.0.3" + checksum: 10/0561f2c95df7a53c47262393590bae8c5254ea5802cbecd2fea589073d926f60466292d594171aa3f3375ccaa2b763368fa1023bd5b6ebfe4da16297731cb379 + languageName: node + linkType: hard + +"@ton/ton@npm:^14.0.0": + version: 14.0.0 + resolution: "@ton/ton@npm:14.0.0" + dependencies: + axios: "npm:^1.6.7" + dataloader: "npm:^2.0.0" + symbol.inspect: "npm:1.0.1" + teslabot: "npm:^1.3.0" + zod: "npm:^3.21.4" + peerDependencies: + "@ton/core": ">=0.56.0" + "@ton/crypto": ">=3.2.0" + checksum: 10/2458391fb1acf46beab8425db2c075fd12fa20c2efbdc8ebc686bbaf0594941bb4555ccd97379debcecb48d6f994665c9cb4423bd2f7c7bcf2ee5cb83808e131 + languageName: node + linkType: hard + "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -8046,6 +8428,50 @@ __metadata: languageName: node linkType: hard +"@tronweb3/tronwallet-abstract-adapter@npm:^1.1.6": + version: 1.1.6 + resolution: "@tronweb3/tronwallet-abstract-adapter@npm:1.1.6" + dependencies: + eventemitter3: "npm:^4.0.0" + checksum: 10/2e07c41aac8d8439697546ae895c4e08a4f637bdf1eafc2edc1d50e3910d0034ae762521991e72e09c08335a0444cb89262c051ab7889d210b8c84f462a866b2 + languageName: node + linkType: hard + +"@tronweb3/tronwallet-adapter-react-hooks@npm:^1.1.7": + version: 1.1.7 + resolution: "@tronweb3/tronwallet-adapter-react-hooks@npm:1.1.7" + dependencies: + "@tronweb3/tronwallet-abstract-adapter": "npm:^1.1.6" + "@tronweb3/tronwallet-adapter-tronlink": "npm:^1.1.9" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10/b45b80302b6efee83038557f0a7eb88c1b6b62893ba908e58c8b3eb36b11a8a2ed70b7d088b20b9beb7c7ce37261ed53118a40514b23b659120409320ae861b1 + languageName: node + linkType: hard + +"@tronweb3/tronwallet-adapter-react-ui@npm:^1.1.8": + version: 1.1.8 + resolution: "@tronweb3/tronwallet-adapter-react-ui@npm:1.1.8" + dependencies: + "@tronweb3/tronwallet-abstract-adapter": "npm:^1.1.6" + "@tronweb3/tronwallet-adapter-react-hooks": "npm:^1.1.7" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 10/520d3a2a95017757c159e64d42f59f9cb7a24c7757ecea1ff8199c1093e04870a09ca646e200671ec353503d5255e0558dd0ac930f9e3ea15fb84fb22bf17297 + languageName: node + linkType: hard + +"@tronweb3/tronwallet-adapter-tronlink@npm:^1.1.9": + version: 1.1.9 + resolution: "@tronweb3/tronwallet-adapter-tronlink@npm:1.1.9" + dependencies: + "@tronweb3/tronwallet-abstract-adapter": "npm:^1.1.6" + checksum: 10/333cbf2f21ca7d25dc4e2fd7c6fa9107ee5f89b2b184063df306dedff2eda598c70668311b8324e614f0da8d39730db13613e037e959211c01650769caad7f6c + languageName: node + linkType: hard + "@trysound/sax@npm:0.2.0": version: 0.2.0 resolution: "@trysound/sax@npm:0.2.0" @@ -8660,6 +9086,13 @@ __metadata: languageName: node linkType: hard +"@types/uuid@npm:^8.3.4": + version: 8.3.4 + resolution: "@types/uuid@npm:8.3.4" + checksum: 10/6f11f3ff70f30210edaa8071422d405e9c1d4e53abbe50fdce365150d3c698fe7bbff65c1e71ae080cbfb8fded860dbb5e174da96fdbbdfcaa3fb3daa474d20f + languageName: node + linkType: hard + "@types/ws@npm:^7.4.4": version: 7.4.7 resolution: "@types/ws@npm:7.4.7" @@ -8669,6 +9102,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.2.2": + version: 8.5.12 + resolution: "@types/ws@npm:8.5.12" + dependencies: + "@types/node": "npm:*" + checksum: 10/d8a3ddfb5ff8fea992a043113579d61ac1ea21e8464415af9e2b01b205ed19d817821ad64ca1b3a90062d1df1c23b0f586d8351d25ca6728844df99a74e8f76d + languageName: node + linkType: hard + "@types/ws@npm:^8.5.1, @types/ws@npm:^8.5.5": version: 8.5.9 resolution: "@types/ws@npm:8.5.9" @@ -10085,6 +10527,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:1.0.5": + version: 1.0.5 + resolution: "abitype@npm:1.0.5" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: 10/1acd0d9687945dd78442b71bd84ff3b9dceae27d15f0d8b14b16554a0c8c9518eeb971ff8e94d507f4d9f05a8a8b91eb8fafd735eaecebac37d5c5a4aac06d8e + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -10836,6 +11293,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.6.7": + version: 1.7.5 + resolution: "axios@npm:1.7.5" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.0" + proxy-from-env: "npm:^1.1.0" + checksum: 10/6cbcfe943a84089f420a900a3a3aeb54ee94dcc9c2b81b150434896357be5d1079eff0b1bbb628597371e79f896b1bc5776df04184756ba99656ff31df9a75bf + languageName: node + linkType: hard + "axobject-query@npm:^3.2.1": version: 3.2.1 resolution: "axobject-query@npm:3.2.1" @@ -12795,6 +13263,13 @@ __metadata: languageName: node linkType: hard +"dataloader@npm:^2.0.0": + version: 2.2.2 + resolution: "dataloader@npm:2.2.2" + checksum: 10/9c7a1f02cfa6391ab8bc21ebd0ef60b03832bd3beafdfecf48b111fba14090f98d33965f8e268045ba3c289f801b6a9000a9e61a41188363bdee2344811f64f1 + languageName: node + linkType: hard + "date-fns@npm:^2.29.3": version: 2.30.0 resolution: "date-fns@npm:2.30.0" @@ -14982,6 +15457,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.15.6": + version: 1.15.6 + resolution: "follow-redirects@npm:1.15.6" + peerDependenciesMeta: + debug: + optional: true + checksum: 10/70c7612c4cab18e546e36b991bbf8009a1a41cf85354afe04b113d1117569abf760269409cb3eb842d9f7b03d62826687086b081c566ea7b1e6613cf29030bf7 + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -17112,6 +17597,28 @@ __metadata: languageName: node linkType: hard +"jayson@npm:^4.1.1": + version: 4.1.2 + resolution: "jayson@npm:4.1.2" + dependencies: + "@types/connect": "npm:^3.4.33" + "@types/node": "npm:^12.12.54" + "@types/ws": "npm:^7.4.4" + JSONStream: "npm:^1.3.5" + commander: "npm:^2.20.3" + delay: "npm:^5.0.0" + es6-promisify: "npm:^5.0.0" + eyes: "npm:^0.1.8" + isomorphic-ws: "npm:^4.0.1" + json-stringify-safe: "npm:^5.0.1" + uuid: "npm:^8.3.2" + ws: "npm:^7.5.10" + bin: + jayson: bin/jayson.js + checksum: 10/7ad5e80e11ef39b7382509d046546883d2595998aa245768b342bcc0a63843e011e16f02a023d5a78fb74df788b5f97c1e850568fc1b90c138fa4772cc55572c + languageName: node + linkType: hard + "jest-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-util@npm:29.7.0" @@ -17473,6 +17980,13 @@ __metadata: languageName: node linkType: hard +"jssha@npm:3.2.0": + version: 3.2.0 + resolution: "jssha@npm:3.2.0" + checksum: 10/6d01e8fa96a05534f19f81f7311f3f925d456007fd772085acada24f6013573c2854650d6b23c193afe18ba06efb40b93ce3ea6c45797ae4f79c0afcfaf6def0 + languageName: node + linkType: hard + "jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.5": version: 3.3.5 resolution: "jsx-ast-utils@npm:3.3.5" @@ -17975,6 +18489,13 @@ __metadata: languageName: node linkType: hard +"lodash.throttle@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.throttle@npm:4.1.1" + checksum: 10/9be9fb2ffd686c20543167883305542f4564062a5f712a40e8c6f2f0d9fd8254a6e9d801c2470b1b24e0cdf2ae83c1277b55aa0fb4799a2db6daf545f53820e1 + languageName: node + linkType: hard + "lodash.uniq@npm:^4.5.0": version: 4.5.0 resolution: "lodash.uniq@npm:4.5.0" @@ -22325,6 +22846,28 @@ __metadata: languageName: node linkType: hard +"rpc-websockets@npm:^9.0.2": + version: 9.0.2 + resolution: "rpc-websockets@npm:9.0.2" + dependencies: + "@swc/helpers": "npm:^0.5.11" + "@types/uuid": "npm:^8.3.4" + "@types/ws": "npm:^8.2.2" + buffer: "npm:^6.0.3" + bufferutil: "npm:^4.0.1" + eventemitter3: "npm:^5.0.1" + utf-8-validate: "npm:^5.0.2" + uuid: "npm:^8.3.2" + ws: "npm:^8.5.0" + dependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10/d558958888cd3469fb8560840305352e59c9ffcd71c7a443c0c5710995ecc3c130b1473f5d4a9d316dbd408fa7473e0de720b875cf8c6ada2668cf8fac072859 + languageName: node + linkType: hard + "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -23663,6 +24206,13 @@ __metadata: languageName: node linkType: hard +"superstruct@npm:^2.0.2": + version: 2.0.2 + resolution: "superstruct@npm:2.0.2" + checksum: 10/10e1944a9da4baee187fbaa6c5d97d7af266b55786dfe50bce67f0f1e7d93f1a5a42dd51e245a2e16404f8336d07c21c67f1c1fbc4ad0a252d3d2601d6c926da + languageName: node + linkType: hard + "supports-color@npm:^2.0.0": version: 2.0.0 resolution: "supports-color@npm:2.0.0" @@ -23804,10 +24354,54 @@ __metadata: "@radix-ui/react-popover": "npm:^1.0.7" "@radix-ui/react-toast": "npm:^1.1.5" "@solana/web3.js": "npm:^1.91.8" - "@swing.xyz/sdk": "npm:^0.54.0" + "@swing.xyz/sdk": "npm:^0.57.5" + "@tailwindcss/forms": "npm:^0.5.7" + "@thirdweb-dev/react": "npm:^4.4.17" + "@thirdweb-dev/sdk": "npm:^4.0.44" + "@types/node": "npm:20.11.26" + "@types/react": "npm:18.2.65" + "@types/react-dom": "npm:18.2.22" + autoprefixer: "npm:^10.4.18" + axios: "npm:^1.6.0" + class-variance-authority: "npm:^0.7.0" + clsx: "npm:^2.1.0" + eslint: "npm:^8.57.0" + eslint-config-examples: "workspace:*" + framer-motion: "npm:^6.5.1" + lucide-react: "npm:^0.368.0" + next: "npm:14.1.3" + pino-pretty: "npm:^10.3.1" + postcss: "npm:^8.4.35" + react: "npm:18.2.0" + react-dom: "npm:18.2.0" + tailwind-merge: "npm:^2.2.2" + tailwindcss: "npm:^3.4.1" + tailwindcss-animate: "npm:^1.0.7" + typescript: "npm:^5.4.2" + typescript-config: "workspace:*" + use-debounce: "npm:^10.0.0" + languageName: unknown + linkType: soft + +"swaps-api-nextjs-tron@workspace:examples/swaps-api-nextjs-tron": + version: 0.0.0-use.local + resolution: "swaps-api-nextjs-tron@workspace:examples/swaps-api-nextjs-tron" + dependencies: + "@fortawesome/fontawesome-svg-core": "npm:^6.5.1" + "@fortawesome/free-brands-svg-icons": "npm:^6.5.1" + "@fortawesome/free-solid-svg-icons": "npm:^6.5.1" + "@fortawesome/react-fontawesome": "npm:^0.2.0" + "@headlessui/react": "npm:^1.7.18" + "@radix-ui/react-label": "npm:^2.0.2" + "@radix-ui/react-popover": "npm:^1.0.7" + "@radix-ui/react-toast": "npm:^1.1.5" + "@solana/web3.js": "npm:^1.91.8" + "@swing.xyz/sdk": "npm:^0.57.5" "@tailwindcss/forms": "npm:^0.5.7" "@thirdweb-dev/react": "npm:^4.4.17" "@thirdweb-dev/sdk": "npm:^4.0.44" + "@tronweb3/tronwallet-adapter-react-hooks": "npm:^1.1.7" + "@tronweb3/tronwallet-adapter-react-ui": "npm:^1.1.8" "@types/node": "npm:20.11.26" "@types/react": "npm:18.2.65" "@types/react-dom": "npm:18.2.22" @@ -24106,6 +24700,13 @@ __metadata: languageName: node linkType: hard +"symbol.inspect@npm:1.0.1": + version: 1.0.1 + resolution: "symbol.inspect@npm:1.0.1" + checksum: 10/47fa8d38d0bc5d04c06df2f71bba1a723ee0e015ca042c47b29c11f107877dd1a2e2d2154c9ef5eec11e92e4165d126c844f06d05da80e477581c8f284f05fdf + languageName: node + linkType: hard + "tailwind-merge@npm:^2.2.2": version: 2.2.2 resolution: "tailwind-merge@npm:2.2.2" @@ -24318,6 +24919,13 @@ __metadata: languageName: node linkType: hard +"teslabot@npm:^1.3.0": + version: 1.5.0 + resolution: "teslabot@npm:1.5.0" + checksum: 10/714ab9a3eadf17efdbcc78c140246c6cf02b0fceff2fd3b9e37de7c86ad05af816037145a258b4b115327bc88219d782f64047812483169037d6956f46bb1450 + languageName: node + linkType: hard + "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0" @@ -24706,6 +25314,13 @@ __metadata: languageName: node linkType: hard +"tweetnacl@npm:1.0.3, tweetnacl@npm:^1.0.3": + version: 1.0.3 + resolution: "tweetnacl@npm:1.0.3" + checksum: 10/ca122c2f86631f3c0f6d28efb44af2a301d4a557a62a3e2460286b08e97567b258c2212e4ad1cfa22bd6a57edcdc54ba76ebe946847450ab0999e6d48ccae332 + languageName: node + linkType: hard + "tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": version: 0.14.5 resolution: "tweetnacl@npm:0.14.5" @@ -24713,13 +25328,6 @@ __metadata: languageName: node linkType: hard -"tweetnacl@npm:^1.0.3": - version: 1.0.3 - resolution: "tweetnacl@npm:1.0.3" - checksum: 10/ca122c2f86631f3c0f6d28efb44af2a301d4a557a62a3e2460286b08e97567b258c2212e4ad1cfa22bd6a57edcdc54ba76ebe946847450ab0999e6d48ccae332 - languageName: node - linkType: hard - "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -25547,24 +26155,25 @@ __metadata: languageName: node linkType: hard -"viem@npm:^2.10.5": - version: 2.10.11 - resolution: "viem@npm:2.10.11" +"viem@npm:^2.19.4": + version: 2.20.1 + resolution: "viem@npm:2.20.1" dependencies: "@adraffy/ens-normalize": "npm:1.10.0" - "@noble/curves": "npm:1.2.0" - "@noble/hashes": "npm:1.3.2" - "@scure/bip32": "npm:1.3.2" - "@scure/bip39": "npm:1.2.1" - abitype: "npm:1.0.0" + "@noble/curves": "npm:1.4.0" + "@noble/hashes": "npm:1.4.0" + "@scure/bip32": "npm:1.4.0" + "@scure/bip39": "npm:1.3.0" + abitype: "npm:1.0.5" isows: "npm:1.0.4" - ws: "npm:8.13.0" + webauthn-p256: "npm:0.0.5" + ws: "npm:8.17.1" peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 10/ccf473d1f1d9a85974800547e21b5fe2fd31235233ab1bec7b3293ed8e990bf5967c7cb9ad77a573be580a9aed34fcaf84727a6dc80bca85ed494a19167893ad + checksum: 10/6fb6c11449ca624c854e70ea5d92fb85880ffe7efa38996495cb0129d3da041cebf003f29923d51b6e16dc0d5f549fa619ebd3d4d5fb47aaf7ed088fd0ef1a3d languageName: node linkType: hard @@ -25934,6 +26543,16 @@ __metadata: languageName: node linkType: hard +"webauthn-p256@npm:0.0.5": + version: 0.0.5 + resolution: "webauthn-p256@npm:0.0.5" + dependencies: + "@noble/curves": "npm:^1.4.0" + "@noble/hashes": "npm:^1.4.0" + checksum: 10/6bf5d1857dfb99ecb3b318af06eddea874c10135e6ebb9f046270f5cbb162933bc6caf77aedb033e14c09971dda544a5fb367ac545e4ec8001b309ba517555cf + languageName: node + linkType: hard + "webdriver-js-extender@npm:2.1.0": version: 2.1.0 resolution: "webdriver-js-extender@npm:2.1.0" @@ -26748,6 +27367,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.17.1": + version: 8.17.1 + resolution: "ws@npm:8.17.1" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10/4264ae92c0b3e59c7e309001e93079b26937aab181835fb7af79f906b22cd33b6196d96556dafb4e985742dd401e99139572242e9847661fdbc96556b9e6902d + languageName: node + linkType: hard + "ws@npm:8.9.0": version: 8.9.0 resolution: "ws@npm:8.9.0" @@ -26778,6 +27412,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^7.5.10": + version: 7.5.10 + resolution: "ws@npm:7.5.10" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10/9c796b84ba80ffc2c2adcdfc9c8e9a219ba99caa435c9a8d45f9ac593bba325563b3f83edc5eb067cc6d21b9a6bf2c930adf76dd40af5f58a5ca6859e81858f0 + languageName: node + linkType: hard + "ws@npm:^8.13.0, ws@npm:^8.4.2, ws@npm:^8.5.0": version: 8.16.0 resolution: "ws@npm:8.16.0" @@ -27072,6 +27721,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.21.4": + version: 3.23.8 + resolution: "zod@npm:3.23.8" + checksum: 10/846fd73e1af0def79c19d510ea9e4a795544a67d5b34b7e1c4d0425bf6bfd1c719446d94cdfa1721c1987d891321d61f779e8236fde517dc0e524aa851a6eff1 + languageName: node + linkType: hard + "zod@npm:^3.22.4": version: 3.22.4 resolution: "zod@npm:3.22.4"