Skip to content

Commit d836889

Browse files
ElasticBottlejnsdlsMananTank
authored
[v4] Add Buy with Crypto APIs and UI in ConnectWallet component (#2579)
Signed-off-by: Jonas Daniels <jonas.daniels@outlook.com> Co-authored-by: Jonas Daniels <jonas.daniels@outlook.com> Co-authored-by: Manan Tank <manantankm@gmail.com>
1 parent 1f6ee22 commit d836889

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+4311
-689
lines changed

.changeset/cold-moose-exercise.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---
2+
"@thirdweb-dev/react-core": minor
3+
---
4+
5+
Adds "buy with crypto" Hooks, Here's an example of how you can use it to build a UI for Swapping tokens
6+
7+
```tsx
8+
function Component() {
9+
const signer = useSigner();
10+
// 1. get a quote for swapping tokens
11+
const quoteQuery = useBuyWithCryptoQuote(swapParams);
12+
13+
const [buyTxHash, setBuyTxHash] = useState<string | undefined>();
14+
const statusQuery = useBuyWithCryptoStatus(
15+
buyTxHash
16+
? {
17+
clientId: "YOUR_CLIENT_ID",
18+
transactionHash: buyTxHash,
19+
}
20+
: undefined,
21+
);
22+
23+
async function handleBuyWithCrypto() {
24+
if (!quoteQuery.data || !signer) {
25+
return;
26+
}
27+
28+
// 2. if approval is required
29+
if (quoteQuery.data.approval) {
30+
const approveTx = await signer.sendTransaction(quoteQuery.data.approval);
31+
await approveTx.wait();
32+
}
33+
34+
// 3. send the transaction to buy crypto
35+
const buyTx = await signer.sendTransaction(
36+
quoteQuery.data.transactionRequest,
37+
);
38+
await buyTx.wait();
39+
40+
// set buyTx.transactionHash to poll the status of the swap transaction
41+
setBuyTxHash(buyTx.hash);
42+
}
43+
44+
// 4. wait for the status of the transaction
45+
if (statusQuery.data) {
46+
console.log("swap status:", statusQuery.data);
47+
}
48+
49+
return <button onClick={handleBuyWithCrypto}>Swap</button>;
50+
}
51+
```
52+
53+
For more information, check out the [pay documentation](https://portal.thirdweb.com/connect/pay/buy-with-crypto) for purchases with crypto

.changeset/famous-readers-clap.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
"@thirdweb-dev/sdk": patch
3+
---
4+
5+
Adds "buy with crypto" APIs, Here's an example of how you can use it for swapping tokens
6+
7+
```ts
8+
// 1. get a quote for swapping tokens
9+
const quote = await getBuyWithCryptoQuote(quoteParams);
10+
11+
// 2. if approval is required, send the approval transaction
12+
if (quote.approval) {
13+
const approvalTx = await signer.sendTransaction(quote.approval);
14+
await approvalTx.wait();
15+
}
16+
17+
// 3. send the quoted transaction
18+
const buyTx = await signer.sendTransaction(quote.transactionRequest);
19+
await buyTx.wait();
20+
21+
// 4. keep polling the status of the quoted transaction until it * returns a success or failure status
22+
const status = await getBuyWithCryptoStatus({
23+
clientId: "YOUR_CLIENT_ID",
24+
transactionHash: transactionResult.hash,
25+
});
26+
```
27+
28+
For more information, check out the [pay documentation](https://portal.thirdweb.com/connect/pay/buy-with-crypto) for purchases with crypto

.changeset/fluffy-bobcats-brake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
Export `resolvePromisedValue` from `thirdweb/utils`

.changeset/little-squids-matter.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@thirdweb-dev/react": minor
3+
---
4+
5+
Adds `Buy` button in `ConnectWallet` component's details Modal to allow users to Swap tokens. Setting `clientId` on `ThirdWebProvider` is required to enable this feature.
6+
7+
A new prop `hideBuyButton` is added to `ConnectWallet` component to hide the `Buy` button in the details Modal. By default, the `Buy` button is visible.
8+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {
2+
useQuery,
3+
type UseQueryOptions,
4+
type UseQueryResult,
5+
} from "@tanstack/react-query";
6+
import {
7+
BuyWithCryptoHistoryData,
8+
BuyWithCryptoHistoryParams,
9+
getBuyWithCryptoHistory,
10+
} from "@thirdweb-dev/sdk";
11+
12+
type BuyWithCryptoHistoryQueryOptions = Omit<
13+
UseQueryOptions<BuyWithCryptoHistoryData>,
14+
"queryFn" | "queryKey" | "enabled"
15+
>;
16+
17+
/**
18+
* Hook to get the history of purchases a given wallet has performed.
19+
*
20+
* This hook is a React Query wrapper of the [`getBuyWithCryptoHistory`](https://portal.thirdweb.com/references/typescript/v4/getBuyWithCryptoHistory) function.
21+
* You can also use that function directly
22+
* @param params - object of type [`BuyWithCryptoHistoryParams`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoHistoryParams)
23+
* @param queryParams - options to configure the react query
24+
* @returns A React Query object which contains the data of type [`BuyWithCryptoHistoryData`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoHistoryData)
25+
* @example
26+
* ```tsx
27+
* import { useBuyWithCryptoHistory } from "@thirdweb-dev/react";
28+
*
29+
* function Component() {
30+
* const buyWithCryptoHistory = useBuyWithCryptoHistory(params);
31+
* console.log(buyWithCryptoHistory.data);
32+
* return <div> ... </div>;
33+
* }
34+
* ```
35+
*/
36+
export function useBuyWithCryptoHistory(
37+
params?: BuyWithCryptoHistoryParams,
38+
queryParams?: BuyWithCryptoHistoryQueryOptions,
39+
): UseQueryResult<BuyWithCryptoHistoryData> {
40+
return useQuery<BuyWithCryptoHistoryData>({
41+
...queryParams,
42+
queryKey: ["buyWithCryptoHistory", params] as const,
43+
queryFn: () => {
44+
if (!params) {
45+
throw new Error("Swap params are required");
46+
}
47+
return getBuyWithCryptoHistory(params);
48+
},
49+
enabled: !!params,
50+
});
51+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {
2+
useQuery,
3+
type UseQueryResult,
4+
type UseQueryOptions,
5+
} from "@tanstack/react-query";
6+
import {
7+
BuyWithCryptoQuote,
8+
getBuyWithCryptoQuote,
9+
GetBuyWithCryptoQuoteParams,
10+
} from "@thirdweb-dev/sdk";
11+
12+
type BuyWithCryptoQuoteQueryOptions = Omit<
13+
UseQueryOptions<BuyWithCryptoQuote>,
14+
"queryFn" | "queryKey" | "enabled"
15+
>;
16+
17+
/**
18+
* Hook to get a quote of type [`BuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoQuote) for buying tokens with crypto.
19+
* This quote contains the information about the purchase such as token amounts, processing fees, estimated time etc.
20+
*
21+
* This hook is a React Query wrapper of the [`getBuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v4/getBuyWithCryptoQuote) function.
22+
* You can also use that function directly
23+
*
24+
* Once you have the quote, you can use the [`useBuyWithCryptoStatus`](https://portal.thirdweb.com/references/react/v4/useBuyWithCryptoStatus) function to get the status of the swap transaction.
25+
* @param params - object of type [`GetBuyWithCryptoQuoteParams`](https://portal.thirdweb.com/references/react/v4/GetBuyWithCryptoQuoteParams)
26+
* @param queryParams - options to configure the react query
27+
* @returns A React Query object which contains the data of type [`BuyWithCryptoQuote`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoQuote)
28+
* @example
29+
* ```tsx
30+
* import { useSigner, useBuyWithCryptoQuote, useBuyWithCryptoStatus } from "@thirdweb-dev/react";
31+
*
32+
* function Component() {
33+
* const buyWithCryptoQuoteQuery = useBuyWithCryptoQuote* (swapParams);
34+
* const signer = useSigner();
35+
* const [buyTxHash, setBuyTxHash] = useState<string | undefined>();
36+
* const buyWithCryptoStatusQuery = useBuyWithCryptoStatus* (buyTxHash ? {
37+
* clientId: "YOUR_CLIENT_ID",
38+
* transactionHash: buyTxHash,
39+
* }: undefined);
40+
*
41+
* async function handleBuyWithCrypto() {
42+
* if (!buyWithCryptoQuoteQuery.data || !signer) {
43+
* return;
44+
* }
45+
*
46+
* // if approval is required
47+
* if (buyWithCryptoQuoteQuery.data.approval) {
48+
* const approveTx = await signer.sendTransaction* (buyWithCryptoQuoteQuery.data.approval);
49+
* await approveTx.wait();
50+
* }
51+
*
52+
* // send the transaction to buy crypto
53+
* // this promise is resolved when user confirms the transaction * in the wallet and the transaction is sent to the blockchain
54+
* const buyTx = await signer.sendTransaction* (buyWithCryptoQuoteQuery.data.transactionRequest);
55+
* await buyTx.wait();
56+
*
57+
* // set buyTx.hash to poll the status of the swap * transaction
58+
* setBuyTxHash(buyTx.hash);
59+
* }
60+
*
61+
* if (buyWithCryptoStatusQuery.data) {
62+
* console.log('buyWithCryptoStatusQuery.data', * buyWithCryptoStatusQuery.data)
63+
* }
64+
* return <button onClick={handleBuyWithCrypto}>Swap</button>
65+
* }
66+
* ```
67+
*/
68+
export function useBuyWithCryptoQuote(
69+
params?: GetBuyWithCryptoQuoteParams,
70+
queryParams?: BuyWithCryptoQuoteQueryOptions,
71+
): UseQueryResult<BuyWithCryptoQuote> {
72+
return useQuery<BuyWithCryptoQuote>({
73+
...queryParams,
74+
queryKey: ["buyWithCryptoQuote", params],
75+
queryFn: () => {
76+
if (!params) {
77+
throw new Error("Swap params are required");
78+
}
79+
if (!params?.clientId) {
80+
throw new Error("Client ID is required in swap params");
81+
}
82+
return getBuyWithCryptoQuote(params);
83+
},
84+
enabled: !!params,
85+
});
86+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { useQuery } from "@tanstack/react-query";
2+
import {
3+
BuyWithCryptoStatus,
4+
BuyWithCryptoTransaction,
5+
getBuyWithCryptoStatus,
6+
} from "@thirdweb-dev/sdk";
7+
import { useState } from "react";
8+
9+
// TODO: use the estimate to vary the polling interval
10+
const DEFAULT_POLL_INTERVAL = 5000;
11+
12+
/**
13+
* A hook to get a status of Buy with Crypto transaction.
14+
*
15+
* This hook is a React Query wrapper of the [`getBuyWithCryptoStatus`](https://portal.thirdweb.com/references/typescript/v4/getBuyWithCryptoStatus) function.
16+
* You can also use that function directly.
17+
* @param params - object of type [`BuyWithCryptoTransaction`](https://portal.thirdweb.com/references/react/v4/BuyWithCryptoTransaction)
18+
* @returns A react query object which contains the data of type [`BuyWithCryptoStatus`](https://portal.thirdweb.com/references/typescript/v4/BuyWithCryptoStatus)
19+
* @example
20+
* ```tsx
21+
* import { useSigner, useBuyWithCryptoQuote, useBuyWithCryptoStatus } from "@thirdweb-dev/react";
22+
*
23+
* function Component() {
24+
* const buyWithCryptoQuoteQuery = useBuyWithCryptoQuote(swapParams);
25+
* const signer = useSigner();
26+
* const [buyTxHash, setBuyTxHash] = useState<string | undefined>();
27+
* const buyWithCryptoStatusQuery = useBuyWithCryptoStatus* (buyTxHash ? {
28+
* clientId: "YOUR_CLIENT_ID",
29+
* transactionHash: buyTxHash,
30+
* }: undefined);
31+
*
32+
* async function handleBuyWithCrypto() {
33+
* if (!buyWithCryptoQuoteQuery.data || !signer) {
34+
* return;
35+
* }
36+
*
37+
* // if approval is required
38+
* if (buyWithCryptoQuoteQuery.data.approval) {
39+
* const approveTx = await signer.sendTransaction(buyWithCryptoQuoteQuery.data.approval);
40+
* await approveTx.wait();
41+
* }
42+
*
43+
* // send the transaction to buy crypto
44+
* // this promise is resolved when user confirms the transaction * in the wallet and the transaction is sent to the blockchain
45+
* const buyTx = await signer.sendTransaction(buyWithCryptoQuoteQuery.data.transactionRequest);
46+
* await buyTx.wait();
47+
*
48+
* // set buyTx.transactionHash to poll the status of the swap * transaction
49+
* setBuyTxHash(buyTx.hash);
50+
* }
51+
*
52+
* if (buyWithCryptoStatusQuery.data) {
53+
* console.log('buyWithCryptoStatusQuery.data', * buyWithCryptoStatusQuery.data)
54+
* }
55+
* return <button onClick={handleBuyWithCrypto}>Swap</button>
56+
* }
57+
* ```
58+
*/
59+
export function useBuyWithCryptoStatus(params?: BuyWithCryptoTransaction) {
60+
const [refetchInterval, setRefetchInterval] = useState<number>(
61+
DEFAULT_POLL_INTERVAL,
62+
);
63+
64+
return useQuery<BuyWithCryptoStatus, Error>({
65+
queryKey: ["swapStatus", params?.transactionHash] as const,
66+
queryFn: async () => {
67+
if (!params) {
68+
throw new Error("Missing swap status params");
69+
}
70+
if (!params?.clientId) {
71+
throw new Error("Missing clientId in swap status params");
72+
}
73+
74+
const swapStatus_ = await getBuyWithCryptoStatus({
75+
...params,
76+
clientId: params.clientId,
77+
});
78+
if (
79+
swapStatus_.status === "COMPLETED" ||
80+
swapStatus_.status === "FAILED"
81+
) {
82+
setRefetchInterval(0);
83+
}
84+
return swapStatus_;
85+
},
86+
enabled: !!params,
87+
refetchInterval: refetchInterval,
88+
refetchIntervalInBackground: true,
89+
retry: true,
90+
});
91+
}

legacy_packages/react-core/src/evm/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,8 @@ export { getErcs, getErc1155, getErc721, getErc20 } from "./types";
302302
// transaction hooks
303303
export { useWatchTransactions } from "./hooks/useTransactions";
304304
export type { UseWatchTransactionsParams } from "./hooks/useTransactions";
305+
306+
// pay hooks
307+
export { useBuyWithCryptoHistory } from "./hooks/pay/useBuyWithCryptoHistory";
308+
export { useBuyWithCryptoQuote } from "./hooks/pay/useBuyWithCryptoQuote";
309+
export { useBuyWithCryptoStatus } from "./hooks/pay/useBuyWithCryptoStatus";

0 commit comments

Comments
 (0)