diff --git a/examples/next/src/app/components/Connected.tsx b/examples/next/src/app/components/Connected.tsx index 951316b34..87a290832 100644 --- a/examples/next/src/app/components/Connected.tsx +++ b/examples/next/src/app/components/Connected.tsx @@ -1,4 +1,3 @@ -import { type CheckoutSettings } from '@0xsequence/checkout' import { ContractVerificationStatus, signEthAuthProof, @@ -11,7 +10,7 @@ import { } from '@0xsequence/connect' import { Button, Card, cn, Text } from '@0xsequence/design-system' import { useIndexerClient } from '@0xsequence/hooks' -import { allNetworks, ChainId } from '@0xsequence/network' +import { allNetworks } from '@0xsequence/network' import { useOpenWalletModal } from '@0xsequence/wallet-widget' import { CardButton, Header, WalletListItem } from 'example-shared-components' import { useCallback, useEffect, useState, type ComponentProps } from 'react' @@ -739,34 +738,3 @@ export const Alert = ({ title, description, secondaryDescription, variant, butto ) } - -export const getCheckoutSettings = (_address?: string) => { - const checkoutSettings: CheckoutSettings = { - cryptoCheckout: { - chainId: ChainId.POLYGON, - triggerTransaction: async () => { - console.log('triggered transaction') - }, - coinQuantity: { - contractAddress: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - amountRequiredRaw: '10000000000' - } - }, - orderSummaryItems: [ - { - chainId: ChainId.POLYGON, - contractAddress: '0x631998e91476da5b870d741192fc5cbc55f5a52e', - tokenId: '66597', - quantityRaw: '100' - }, - { - chainId: ChainId.POLYGON, - contractAddress: '0x624e4fa6980afcf8ea27bfe08e2fb5979b64df1c', - tokenId: '1741', - quantityRaw: '100' - } - ] - } - - return checkoutSettings -} diff --git a/examples/react/src/components/Connected.tsx b/examples/react/src/components/Connected.tsx index 136545dd7..9f03a6069 100644 --- a/examples/react/src/components/Connected.tsx +++ b/examples/react/src/components/Connected.tsx @@ -1,13 +1,4 @@ -import { - TransactionOnRampProvider, - useAddFundsModal, - useCheckoutModal, - useERC1155SaleContractCheckout, - useSelectPaymentModal, - useSwapModal, - useTransactionStatusModal, - type SwapModalSettings -} from '@0xsequence/checkout' +import { useCreditCardCheckoutModal, useTransactionStatusModal } from '@0xsequence/checkout' import { getModalPositionCss, signEthAuthProof, @@ -18,7 +9,7 @@ import { useWallets, validateEthProof } from '@0xsequence/connect' -import { Button, Card, cn, Modal, Scroll, Switch, Text, TextInput } from '@0xsequence/design-system' +import { Button, Card, cn, Modal, Switch, Text, TextInput } from '@0xsequence/design-system' import { allNetworks, ChainId } from '@0xsequence/network' import { useOpenWalletModal } from '@0xsequence/wallet-widget' import { CardButton, Header, WalletListItem } from 'example-shared-components' @@ -34,26 +25,20 @@ import { abi } from '../constants/nft-abi' import { delay, getCheckoutSettings, getOrderbookCalldata } from '../utils' import { checkoutPresets } from '../utils/checkout' -import { CustomCheckout } from './CustomCheckout' import { Select } from './Select' // append ?debug to url to enable debug mode const searchParams = new URLSearchParams(location.search) const isDebugMode = searchParams.has('debug') const checkoutProvider = searchParams.get('checkoutProvider') -const onRampProvider = searchParams.get('onRampProvider') const checkoutPreset = searchParams.get('checkoutPreset') || 'forte-transak-payment-erc1155-sale-native-token-testnet' export const Connected = () => { const { openTransactionStatusModal } = useTransactionStatusModal() - const [isOpenCustomCheckout, setIsOpenCustomCheckout] = React.useState(false) const { setOpenConnectModal } = useOpenConnectModal() const { address } = useAccount() - const { openSwapModal } = useSwapModal() const { setOpenWalletModal } = useOpenWalletModal() - const { triggerCheckout } = useCheckoutModal() - const { triggerAddFunds } = useAddFundsModal() - const { openSelectPaymentModal } = useSelectPaymentModal() + const { initiateCreditCardCheckout } = useCreditCardCheckoutModal() const { setIsSocialLinkOpen } = useSocialLink() const { data: walletClient } = useWalletClient() const storage = useStorage() @@ -83,17 +68,6 @@ export const Connected = () => { reset: resetSendUnsponsoredTransaction } = useSendTransaction() - const { openCheckoutModal, isLoading: erc1155CheckoutLoading } = useERC1155SaleContractCheckout({ - chain: 137, - contractAddress: '0xf0056139095224f4eec53c578ab4de1e227b9597', - wallet: address || '', - collectionAddress: '0x92473261f2c26f2264429c451f70b0192f858795', - items: [{ tokenId: '1', quantity: '1' }], - onSuccess: txnHash => { - console.log('txnHash', txnHash) - } - }) - const [isSigningMessage, setIsSigningMessage] = React.useState(false) const [isMessageValid, setIsMessageValid] = React.useState() const [messageSig, setMessageSig] = React.useState() @@ -384,43 +358,15 @@ export const Connected = () => { setIsCheckoutInfoModalOpen(true) } - const onClickSwap = () => { - const chainId = 137 - const toTokenAddress = '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359' - const toTokenAmount = '200000' - const data = encodeFunctionData({ abi: parseAbi(['function demo()']), functionName: 'demo', args: [] }) - - const swapModalSettings: SwapModalSettings = { - onSuccess: () => { - console.log('swap successful!') - }, - chainId, - toTokenAddress, - toTokenAmount, - postSwapTransactions: [ - { - to: '0x37470dac8a0255141745906c972e414b1409b470', - data - } - ], - title: 'Swap and Pay', - description: 'Select a token in your wallet to swap to 0.2 USDC.' - } - - openSwapModal(swapModalSettings) - } - - const onClickSelectPayment = () => { + const onClickInitiateCreditCardPayment = () => { if (!address) { return } const creditCardProvider = checkoutProvider || 'forte' - openSelectPaymentModal({ - recipientAddress: address, - creditCardProviders: [creditCardProvider], - onRampProvider: onRampProvider ? (onRampProvider as TransactionOnRampProvider) : TransactionOnRampProvider.transak, + initiateCreditCardCheckout({ + provider: creditCardProvider, onSuccess: (txnHash?: string) => { console.log('success!', txnHash) }, @@ -460,18 +406,10 @@ export const Connected = () => { recipient: recipientAddress }) }) - triggerCheckout(checkoutSettings) + initiateCreditCardCheckout(checkoutSettings) } } - const onClickAddFunds = () => { - triggerAddFunds({ - walletAddress: address || '', - provider: onRampProvider ? (onRampProvider as TransactionOnRampProvider) : TransactionOnRampProvider.transak, - transakOnRampKind: 'default' - }) - } - const onClickConnect = () => { setOpenConnectModal(true) } @@ -786,7 +724,11 @@ export const Connected = () => { Web SDK Checkout - onClickAddFunds()} /> + {isDebugMode && ( <> @@ -797,18 +739,6 @@ export const Connected = () => { description="Set orderbook order id, token contract address and token id to test checkout (on Polygon)" onClick={onClickCheckout} /> - setIsOpenCustomCheckout(true)} - /> - - { )} - - - - {(chainId === ChainId.ARBITRUM_NOVA || chainId === ChainId.ARBITRUM_SEPOLIA || isWaasConnectionActive) && ( Misc @@ -954,25 +872,6 @@ export const Connected = () => { )} - - {isOpenCustomCheckout && ( - setIsOpenCustomCheckout(false)} - > - - - - - )} - ) } diff --git a/examples/react/src/components/CustomCheckout/index.tsx b/examples/react/src/components/CustomCheckout/index.tsx deleted file mode 100644 index ec2f2ec13..000000000 --- a/examples/react/src/components/CustomCheckout/index.tsx +++ /dev/null @@ -1,197 +0,0 @@ -import { CreditCardProviders, useCheckoutUI } from '@0xsequence/checkout' -import { CryptoOption } from '@0xsequence/connect' -import { Button, Image, NetworkImage, Spinner, Text } from '@0xsequence/design-system' -import { useState } from 'react' -import { encodeFunctionData, toHex } from 'viem' -import { useAccount } from 'wagmi' - -import { ERC_1155_SALE_CONTRACT } from '../../constants/erc1155-sale-contract' - -export const CustomCheckout = () => { - const { address } = useAccount() - - // NATIVE token sale - // const currencyAddress = zeroAddress - // const salesContractAddress = '0xf0056139095224f4eec53c578ab4de1e227b9597' - // const collectionAddress = '0x92473261f2c26f2264429c451f70b0192f858795' - // const price = '200000000000000' - // const contractId = '674eb55a3d739107bbd18ecb' - - // // ERC-20 contract - const currencyAddress = '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359' - const salesContractAddress = '0xe65b75eb7c58ffc0bf0e671d64d0e1c6cd0d3e5b' - const collectionAddress = '0xdeb398f41ccd290ee5114df7e498cf04fac916cb' - const price = '20000' - - const chainId = 137 - - const collectible = { - tokenId: '1', - quantity: '1' - } - - const purchaseTransactionData = encodeFunctionData({ - abi: ERC_1155_SALE_CONTRACT, - functionName: 'mint', - // [to, tokenIds, amounts, data, expectedPaymentToken, maxTotal, proof] - args: [ - address, - [BigInt(collectible.tokenId)], - [BigInt(collectible.quantity)], - toHex(0), - currencyAddress, - price, - [toHex(0, { size: 32 })] - ] - }) - - const checkoutUIParams = { - collectible, - chain: chainId, - totalPriceRaw: price, - targetContractAddress: salesContractAddress, - recipientAddress: address || '', - currencyAddress, - collectionAddress, - creditCardProvider: 'transak' as CreditCardProviders, - onSuccess: (txnHash?: string) => { - console.log('success!', txnHash) - }, - onError: (error: Error) => { - console.error(error) - }, - txData: purchaseTransactionData - } - - const { orderSummary, creditCardPayment, cryptoPayment } = useCheckoutUI(checkoutUIParams) - - const OrderSummary = () => { - if (orderSummary.isLoading) { - return - } - - if (orderSummary.error) { - return Error loading order summary - } - - return ( -
-
-
-
- -
-
- - {orderSummary.data?.collectibleItem?.collectionName} - - - {`${orderSummary.data?.collectibleItem?.collectibleName} x${orderSummary.data?.collectibleItem?.quantityFormatted}`} - -
-
-
-
-
- - {`${orderSummary?.data?.formattedCryptoPrice} ${orderSummary?.data?.cryptoSymbol}`} -
-
- - {`$${orderSummary?.data?.totalPriceFiat} estimated total`} - -
-
-
- ) - } - - const CreditCardPayment = () => { - const [showCreditCardPayment, setShowCreditCardPayment] = useState(false) - if (creditCardPayment.isLoading) { - return - } - - if (creditCardPayment.error) { - return Error loading credit card payment - } - - const CreditCardIframe = creditCardPayment.data?.CreditCardIframe - const EventListener = creditCardPayment.data?.EventListener - - if (showCreditCardPayment) { - return ( -
- - -
- ) - } - - return - } - - const CryptoPayment = () => { - if (cryptoPayment.cryptoOptions.data.length === 0) { - return - } - - if (cryptoPayment.cryptoOptions.error) { - return Error loading crypto payment - } - - return ( -
- {cryptoPayment.cryptoOptions.data.map(option => ( - { - cryptoPayment.purchaseAction.setSelectedCurrencyAddress(option.address) - }} - isSelected={option.isSelected} - showInsufficientFundsWarning={option.isInsufficientFunds} - disabled={option.isInsufficientFunds} - /> - ))} - {cryptoPayment.cryptoOptions.isLoading && } - -
- ) - } - - return ( -
- The following data is generated by the useCheckoutUI hook - - Order Summary section - - - - Crypto Payment section - - - - Credit Card Payment section - - -
- ) -} - -export default CustomCheckout diff --git a/examples/react/src/utils/checkout.ts b/examples/react/src/utils/checkout.ts index e891c00b9..31f86caae 100644 --- a/examples/react/src/utils/checkout.ts +++ b/examples/react/src/utils/checkout.ts @@ -1,4 +1,4 @@ -import { type ForteConfig } from '@0xsequence/checkout' +import { type CreditCardCheckoutSettings } from '@0xsequence/checkout' import { zeroAddress } from 'viem' import { encodeFunctionData, toHex } from 'viem' @@ -67,21 +67,10 @@ const getOrderbookTransactionData = ({ recipientAddress, requestId, quantity }: }) as `0x${string}` } -interface CheckoutPreset { - chain: number | string - currencyAddress: string - targetContractAddress: string - collectionAddress: string - price: string - collectibles: { - tokenId?: string - quantity: string - }[] - txData: `0x${string}` - forteConfig?: ForteConfig -} - -export const checkoutPresets: Record CheckoutPreset> = { +export const checkoutPresets: Record< + string, + (recipientAddress: string) => Omit +> = { 'erc1155-sale-native-token-polygon': (recipientAddress: string) => { const collectibles = [ { @@ -91,13 +80,17 @@ export const checkoutPresets: Record Check ] const price = '200000000000000' return { - chain: 137, + chainId: 137, + contractAddress: '0xf0056139095224f4eec53c578ab4de1e227b9597', + recipientAddress, + currencyQuantity: price, + currencySymbol: 'MATIC', currencyAddress: zeroAddress, - targetContractAddress: '0xf0056139095224f4eec53c578ab4de1e227b9597', - collectionAddress: '0x92473261f2c26f2264429c451f70b0192f858795', - price, - collectibles, - txData: getPurchaseTransactionERC1155Sale({ + currencyDecimals: '18', + nftId: collectibles[0].tokenId || '', + nftAddress: '0x92473261f2c26f2264429c451f70b0192f858795', + nftQuantity: collectibles[0].quantity, + calldata: getPurchaseTransactionERC1155Sale({ recipientAddress, currencyAddress: zeroAddress, price, @@ -114,13 +107,17 @@ export const checkoutPresets: Record Check ] const price = '20000' return { - chain: 137, + chainId: 137, + contractAddress: '0xe65b75eb7c58ffc0bf0e671d64d0e1c6cd0d3e5b', + recipientAddress, + currencyQuantity: price, + currencySymbol: 'USDC', currencyAddress: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - targetContractAddress: '0xe65b75eb7c58ffc0bf0e671d64d0e1c6cd0d3e5b', - collectionAddress: '0xdeb398f41ccd290ee5114df7e498cf04fac916cb', - price, - collectibles, - txData: getPurchaseTransactionERC1155Sale({ + currencyDecimals: '6', + nftId: collectibles[0].tokenId || '', + nftAddress: '0xdeb398f41ccd290ee5114df7e498cf04fac916cb', + nftQuantity: collectibles[0].quantity, + calldata: getPurchaseTransactionERC1155Sale({ recipientAddress, currencyAddress: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', price, @@ -136,13 +133,17 @@ export const checkoutPresets: Record Check ] const price = '1' return { - chain: 137, + chainId: 137, + contractAddress: '0xa0284905d29cbeb19f4be486f9091fac215b7a6a', + recipientAddress, + currencyQuantity: price, + currencySymbol: 'USDC', currencyAddress: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', - targetContractAddress: '0xa0284905d29cbeb19f4be486f9091fac215b7a6a', - collectionAddress: '0xd705db0a96075b98758c4bdafe8161d8566a68f8', - price, - collectibles, - txData: getPurchaseTransactionERC721Sale({ + currencyDecimals: '6', + nftId: '', // ERC721 doesn't have tokenId in collectibles for this preset + nftAddress: '0xd705db0a96075b98758c4bdafe8161d8566a68f8', + nftQuantity: collectibles[0].quantity, + calldata: getPurchaseTransactionERC721Sale({ recipientAddress, currencyAddress: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', price @@ -159,13 +160,17 @@ export const checkoutPresets: Record Check const price = '1000000000000000' const structuredCalldata = '' return { - chain: 11155111, + chainId: 11155111, + contractAddress: '0x1130e2e03f682f05f298fd702787d9bd0bf94316', + recipientAddress, + currencyQuantity: price, + currencySymbol: 'ETH', currencyAddress: zeroAddress, - targetContractAddress: '0x1130e2e03f682f05f298fd702787d9bd0bf94316', - collectionAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', - price, - collectibles, - txData: getPurchaseTransactionERC1155Sale({ + currencyDecimals: '18', + nftId: collectibles[0].tokenId || '', + nftAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', + nftQuantity: collectibles[0].quantity, + calldata: getPurchaseTransactionERC1155Sale({ recipientAddress, currencyAddress: zeroAddress, price, @@ -222,13 +227,17 @@ export const checkoutPresets: Record Check } return { - chain: 11155111, + chainId: 11155111, + contractAddress: '0x1130e2e03f682f05f298fd702787d9bd0bf94316', + recipientAddress, + currencyQuantity: price, + currencySymbol: 'ETH', currencyAddress: zeroAddress, - targetContractAddress: '0x1130e2e03f682f05f298fd702787d9bd0bf94316', - collectionAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', - price, - collectibles, - txData: getPurchaseTransactionERC1155Sale({ + currencyDecimals: '18', + nftId: collectibles[0].tokenId || '', + nftAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', + nftQuantity: collectibles[0].quantity, + calldata: getPurchaseTransactionERC1155Sale({ recipientAddress, currencyAddress: zeroAddress, price, @@ -292,13 +301,17 @@ export const checkoutPresets: Record Check } return { - chain: 11155111, + chainId: 11155111, + contractAddress: '0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712', + recipientAddress, + currencyQuantity: price, + currencySymbol: 'ETH', currencyAddress: zeroAddress, - targetContractAddress: '0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712', - collectionAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', - price, - collectibles, - txData, + currencyDecimals: '18', + nftId: collectibles[0].tokenId || '', + nftAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', + nftQuantity: collectibles[0].quantity, + calldata: txData, forteConfig: { protocol: 'custom_evm_call', calldata: structuredCalldata, @@ -351,19 +364,23 @@ export const checkoutPresets: Record Check } return { - chain: 11155111, + chainId: 11155111, + contractAddress: '0x0c29598a69aeda9f3fed0ba64a2d94c54f83e8c6', + recipientAddress, + currencyQuantity: price, + currencySymbol: 'WETH', currencyAddress, - targetContractAddress: '0x0c29598a69aeda9f3fed0ba64a2d94c54f83e8c6', - approvedSpenderAddress: '0x0c29598a69aeda9f3fed0ba64a2d94c54f83e8c6', - collectionAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', - price, - collectibles, - txData: getPurchaseTransactionERC1155Sale({ + currencyDecimals: '18', + nftId: collectibles[0].tokenId || '', + nftAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', + nftQuantity: collectibles[0].quantity, + calldata: getPurchaseTransactionERC1155Sale({ recipientAddress, currencyAddress, price, collectibles }), + approvedSpenderAddress: '0x0c29598a69aeda9f3fed0ba64a2d94c54f83e8c6', forteConfig: { protocol: 'mint', calldata: structuredCalldata, @@ -414,13 +431,17 @@ export const checkoutPresets: Record Check } return { - chain: 11155111, + chainId: 11155111, + contractAddress: '0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712', + recipientAddress, + currencyQuantity: price, + currencySymbol: 'WETH', currencyAddress, - targetContractAddress: '0xfdb42A198a932C8D3B506Ffa5e855bC4b348a712', - collectionAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', - price, - collectibles, - txData, + currencyDecimals: '18', + nftId: collectibles[0].tokenId || '', + nftAddress: '0xb496d64e1fe4f3465fb83f3fd8cb50d8e227101b', + nftQuantity: collectibles[0].quantity, + calldata: txData, forteConfig: { protocol: 'custom_evm_call', calldata: structuredCalldata, diff --git a/examples/react/src/utils/helpers.ts b/examples/react/src/utils/helpers.ts index 4262526cd..f155f7396 100644 --- a/examples/react/src/utils/helpers.ts +++ b/examples/react/src/utils/helpers.ts @@ -1,4 +1,4 @@ -import { CheckoutSettings } from '@0xsequence/checkout' +import { CreditCardCheckoutSettings } from '@0xsequence/checkout' import { encodeFunctionData, Hex } from 'viem' import { orderbookAbi } from '../constants/orderbook-abi' @@ -24,24 +24,14 @@ export interface GetCheckoutSettings { } export const getCheckoutSettings = (args: GetCheckoutSettings) => { - const checkoutSettings: CheckoutSettings = { - creditCardCheckout: { - onSuccess: hash => { - console.log('credit card checkout success', hash) - }, - onError: e => { - console.log('credit card checkout error', e) - }, - ...args - } - // orderSummaryItems: [ - // { - // chainId: args.chainId, - // contractAddress: args.nftAddress, - // tokenId: args.nftId, - // quantityRaw: String(args.nftQuantity) - // } - // ] + const checkoutSettings: CreditCardCheckoutSettings = { + onSuccess: hash => { + console.log('credit card checkout success', hash) + }, + onError: e => { + console.log('credit card checkout error', e) + }, + ...args } return checkoutSettings diff --git a/packages/checkout/README.md b/packages/checkout/README.md index ccd6e351d..98e083dda 100644 --- a/packages/checkout/README.md +++ b/packages/checkout/README.md @@ -35,271 +35,3 @@ const App = () => { ) } ``` - -# NFT Checkout - -
- -
- -NFT Checkout allows users to purchase NFTs using various payment methods. Users can pay with the main currency (e.g., ETH), swap tokens for payment, or use a credit card provided the smart contract is whitelisted (contact a member of the Sequence team to whitelist your contract for credit card payments). - -## Basic Usage - -To enable this functionality in your app, use the `useSelectPaymentModal` hook from the `@0xsequence/checkout` package. The following code demonstrates how to set up the checkout modal and trigger it on a button click: - -```js -import { useSelectPaymentModal, type SelectPaymentSettings } from '@0xsequence/checkout' - -const MyComponent = () => { - const { openSelectPaymentModal } = useSelectPaymentModal() - - const onClick = () => { - const erc1155SalesContractAbi = [ - { - type: 'function', - name: 'mint', - inputs: [ - { name: 'to', type: 'address', internalType: 'address' }, - { name: 'tokenIds', type: 'uint256[]', internalType: 'uint256[]' }, - { name: 'amounts', type: 'uint256[]', internalType: 'uint256[]' }, - { name: 'data', type: 'bytes', internalType: 'bytes' }, - { name: 'expectedPaymentToken', type: 'address', internalType: 'address' }, - { name: 'maxTotal', type: 'uint256', internalType: 'uint256' }, - { name: 'proof', type: 'bytes32[]', internalType: 'bytes32[]' } - ], - outputs: [], - stateMutability: 'payable' - } - ] - - const purchaseTransactionData = encodeFunctionData({ - abi: erc1155SalesContractAbi, - functionName: 'mint', - args: [ - recipientAddress, - collectibles.map(c => BigInt(c.tokenId)), - collectibles.map(c => BigInt(c.quantity)), - toHex(0), - currencyAddress, - price, - [toHex(0, { size: 32 })] - ] - }) - - const settings: SelectPaymentSettings = { - collectibles: [ - { - tokenId: '1', - quantity: '1' - } - ], - chain: chainId, - price, - targetContractAddress: salesContractAddress, - recipientAddress: address, - currencyAddress, - collectionAddress, - creditCardProviders: ['transak'], - copyrightText: 'ⓒ2024 Sequence', - onSuccess: (txnHash?: string) => { - console.log('success!', txnHash) - }, - onError: (error: Error) => { - console.error(error) - }, - txData: purchaseTransactionData, - } - - openSelectPaymentModal(settings) - } - - return -} -``` - -## Parameters - -- **collectibles**: List of NFT collectibles, including their token IDs and quantities. -- **chain**: The blockchain network ID. -- **price**: Total price for the transaction in the selected currency. This value should not contain decimals. -- **currencyAddress**: The address of the currency used for executing the transaction on the target contract. -- **targetContractAddress**: The address of the smart contract handling the minting function. - creditCardProviders: Providers like sardine for credit card payments. -- **collectionAddress**: The contract address of the collectible such as an ERC-1155 or ERC-721 -- **creditCardProviders**: The list of credit card providers to execute a payment with. It is up to the developer to make sure that the region, currency and network is compatible. -- **txData**: Encoded transaction data to interact with the mint function. -- **copyrightText**: The copyright text shown at the bottom of the modal. -- **onSuccess**: Callback function triggered once the transaction has been confirmed on the blockchain. -- **blockConfirmations**: The number of block confirmations required for the transaction to be considered successful and trigger `onSuccess`. -- **onError**: Callback function triggered if an error has occurred before or after sending the transaction. - -## Utility functions - -The `@0xsequence/checkout` library indeed simplifies the integration of Web3 payment solutions by providing utility functions. One such function, `useERC1155SaleContractPaymentModal`, is tailored for use cases involving the minting of ERC-1155 tokens. This function works in conjunction with Sequence's wallet ecosystem and its deployable smart contract infrastructure, such as the ERC-1155 sale contract available through the [Sequence Builder](https://sequence.build). - -```js -import { useERC1155SaleContractCheckout } from "@0xsequence/checkout"; -import { useAccount } from "wagmi"; - -const MyComponent = () => { - const { address: userAddress } = useAccount(); - const { openCheckoutModal } = useERC1155SaleContractCheckout({ - chain: 80001, // chainId of the chain the collectible is on - contractAddress: "0x0327b2f274e04d292e74a06809bcd687c63a4ba4", // address of the contract handling the minting function - wallet: userAddress!, // address of the recipient - collectionAddress: "0x888a322db4b8033bac3ff84412738c096f87f9d0", // address of the collection contract - items: [ - // array of collectibles to purchase - { - tokenId: "0", - quantity: "1", - }, - ], - onSuccess: (txnHash?: string) => { - console.log("success!", txnHash); - }, - onError: (error: Error) => { - console.error(error); - }, - }); - - const onClick = () => { - if (!userAddress) { - return; - } - openCheckoutModal(); - }; - - return ; -}; -``` - -# Swap - -
- -
- -The **Swap Modal** allows users to swap one currency for another (e.g., ETH to USDC) before completing a transaction. This feature is useful when users need to convert their tokens into the correct currency for payment. - -## Basic Usage - -Here’s an example of how to use the Swap Modal with the `useSwapModal` hook: - -```js -import { useSwapModal, type SwapModalSettings } from '@0xsequence/checkout' - -const MyComponent = () => { - const { openSwapModal } = useSwapModal() - - const onClick = () => { - const chainId = 137 - const currencyAddress = '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359' - const currencyAmount = '20000' - const data = encodeFunctionData({ abi: parseAbi(['function demo()']), functionName: 'demo', args: []}) - - const swapModalSettings: SwapModalSettings = { - onSuccess: () => { - console.log('swap successful!') - }, - chainId, - currencyAddress, - currencyAmount, - postSwapTransactions: [ - { - to: '0x37470dac8a0255141745906c972e414b1409b470', - data - } - ], - title: 'Swap and Pay', - description: 'Select a token in your wallet to swap to 0.2 USDC.' - } - - openSwapModal(swapModalSettings) - } - - return -} -``` - -## Key Parameters - -- **currencyAddress**: The address of the token to swap from (e.g., USDC). -- **currencyAmount**: The amount to swap. -- **postSwapTransactions**: An optional array of transactions to be executed after the swap, using the swapped tokens. -- **title**: The modal’s title. -- **description**: A description of the swap and payment process. - -# Fiat Onramp - -
- -
- -The Fiat Onramp feature allows users to convert traditional fiat currencies (e.g., USD) into cryptocurrencies. This feature makes it easier for non-crypto users to interact with decentralized applications (dApps) by onboarding them directly through fiat payments. - -```js -import { useAddFundsModal } from '@0xsequence/checkout' - -const MyComponent = () => { - const { triggerAddFunds } = useAddFundsModal() - - const onClick = () => { - triggerAddFunds({ - walletAddress: recipientAddress - }) - } - - return -} -``` - -## Custom Checkout UIs - -Fully customized checkout UIs can be created with the `useCheckoutUI` hook. The hook will return three objects `orderSummary`, `creditCardPayment` and `cryptoPayment` which serve to create the order summary section, credit card payment section and crypto payment section of a checkout UI. API calls are done within the hook and all that if left to build is placing the information and actions. - -Each section comes with its own loading, error and data states. - -```js -import { useCheckoutUI } from '@0xsequence/checkout' - -const CustomCheckoutUI = () => { - const checkoutUIParams = { - collectible, - chain: chainId, - totalPriceRaw: price, - targetContractAddress: salesContractAddress, - recipientAddress: address || '', - currencyAddress, - collectionAddress, - creditCardProvider: 'transak' as CreditCardProviders, - onSuccess: (txnHash?: string) => { - console.log('success!', txnHash) - }, - onError: (error: Error) => { - console.error(error) - }, - txData: purchaseTransactionData - } - - const { orderSummary, creditCardPayment, cryptoPayment } = useCheckoutUI(checkoutUIParams) - - const isLoading = orderSummary.isLoading || creditCardPayment.isLoading || cryptoPayment.isLoading - - const error = orderSummary.error || creditCardPayment.error || cryptoPayment.error - - if (isLoading) { - return
loading...
- } - if (error) { - return
an error has occurred
- } - - return ( - - - - ) -} -``` diff --git a/packages/checkout/src/components/SequenceCheckoutProvider/SequenceCheckoutProvider.tsx b/packages/checkout/src/components/SequenceCheckoutProvider/SequenceCheckoutProvider.tsx index fad8f80f8..38ee5bff6 100644 --- a/packages/checkout/src/components/SequenceCheckoutProvider/SequenceCheckoutProvider.tsx +++ b/packages/checkout/src/components/SequenceCheckoutProvider/SequenceCheckoutProvider.tsx @@ -6,35 +6,23 @@ import { AnimatePresence } from 'motion/react' import { useEffect, useState, type ReactNode } from 'react' import { - AddFundsContextProvider, - CheckoutModalContextProvider, + CreditCardCheckoutModalContextProvider, EnvironmentContextProvider, NavigationCheckoutContextProvider, NavigationContextProvider, - SelectPaymentContextProvider, - SwapModalContextProvider, TransactionStatusModalContextProvider, TransferFundsContextProvider, - type AddFundsSettings, - type CheckoutSettings, + type CreditCardCheckoutSettings, type EnvironmentOverrides, type History, type HistoryCheckout, type Navigation, type NavigationCheckout, - type SelectPaymentSettings, - type SwapModalSettings, type TransactionStatusSettings, type TransferFundsSettings } from '../../contexts/index.js' import { - AddFundsContent, - CheckoutSelection, - PaymentSelectionContent, - PaymentSelectionHeader, PendingCreditCardTransaction, - Swap, - TokenSelectionContent, TransactionError, TransactionStatus, TransactionSuccess, @@ -63,36 +51,21 @@ const getDefaultLocationCheckout = (): NavigationCheckout => { } export const SequenceCheckoutProvider = ({ children, config }: SequenceCheckoutProviderProps) => { const { theme, position } = useTheme() - const [openCheckoutModal, setOpenCheckoutModal] = useState(false) - const [openAddFundsModal, setOpenAddFundsModal] = useState(false) + const [openCreditCardCheckoutModal, setOpenCreditCardCheckoutModal] = useState(false) const [openTransferFundsModal, setOpenTransferFundsModal] = useState(false) - const [openPaymentSelectionModal, setOpenPaymentSelectionModal] = useState(false) const [openTransactionStatusModal, setOpenTransactionStatusModal] = useState(false) - const [isOpenSwapModal, setIsOpenSwapModal] = useState(false) - const [settings, setSettings] = useState() - const [selectPaymentSettings, setSelectPaymentSettings] = useState() - const [addFundsSettings, setAddFundsSettings] = useState() + const [creditCardSettings, setCreditCardSettings] = useState() const [transferFundsSettings, setTransferFundsSettings] = useState() const [transactionStatusSettings, setTransactionStatusSettings] = useState() - const [swapModalSettings, setSwapModalSettings] = useState() const [history, setHistory] = useState([]) const [checkoutHistory, setCheckoutHistory] = useState([getDefaultLocationCheckout()]) const { customCSS } = useConnectConfigContext() const getDefaultLocation = (): Navigation => { - // skip the order summary for credit card checkout if no items provided - const orderSummaryItems = settings?.orderSummaryItems || [] - const creditCardSettings = settings?.creditCardCheckout - if (orderSummaryItems.length === 0 && creditCardSettings) { - return { - location: 'transaction-pending', - params: { - creditCardCheckout: creditCardSettings - } - } - } else { - return { - location: 'select-method-checkout' + return { + location: 'transaction-pending', + params: { + creditCardCheckout: creditCardSettings! } } } @@ -100,28 +73,22 @@ export const SequenceCheckoutProvider = ({ children, config }: SequenceCheckoutP // TODO: remove this navigation logic and all associated code, including components, once flows are migrated to updated checkout ui const navigation = history.length > 0 ? history[history.length - 1] : getDefaultLocation() - const checkoutNavigation = - checkoutHistory.length > 0 ? checkoutHistory[checkoutHistory.length - 1] : getDefaultLocationCheckout() - - const triggerCheckout = (settings: CheckoutSettings) => { - setSettings(settings) - setOpenCheckoutModal(true) + const initiateCreditCardCheckout = (settings: CreditCardCheckoutSettings) => { + setCreditCardSettings(settings) + setOpenCreditCardCheckoutModal(true) } - const closeCheckout = () => { - setOpenCheckoutModal(false) + const closeCreditCardCheckout = () => { + setOpenCreditCardCheckoutModal(false) } - const triggerAddFunds = (settings: AddFundsSettings) => { - setAddFundsSettings(settings) - setOpenAddFundsModal(true) + const triggerTransactionStatusModal = (settings: TransactionStatusSettings) => { + setTransactionStatusSettings(settings) + setOpenTransactionStatusModal(true) } - const closeAddFunds = () => { - setOpenAddFundsModal(false) - if (addFundsSettings?.onClose) { - addFundsSettings.onClose() - } + const closeTransactionStatusModal = () => { + setOpenTransactionStatusModal(false) } const openTransferFunds = (settings: TransferFundsSettings) => { @@ -138,46 +105,16 @@ export const SequenceCheckoutProvider = ({ children, config }: SequenceCheckoutP } } - const openSelectPaymentModal = (settings: SelectPaymentSettings) => { - setSelectPaymentSettings(settings) - setOpenPaymentSelectionModal(true) - } - - const closeSelectPaymentModal = () => { - setOpenPaymentSelectionModal(false) - } - - const triggerTransactionStatusModal = (settings: TransactionStatusSettings) => { - setTransactionStatusSettings(settings) - setOpenTransactionStatusModal(true) - } - - const closeTransactionStatusModal = () => { - setOpenTransactionStatusModal(false) - } - const openSwapModal = (settings: SwapModalSettings) => { - setSwapModalSettings(settings) - setIsOpenSwapModal(true) - } - - const closeSwapModal = () => { - setIsOpenSwapModal(false) - } - const getCheckoutContent = () => { const { location } = navigation switch (location) { - case 'select-method-checkout': - return - case 'transaction-pending': - return case 'transaction-success': return case 'transaction-error': return - case 'transaction-form': + case 'transaction-pending': default: - return + return } } @@ -196,51 +133,11 @@ export const SequenceCheckoutProvider = ({ children, config }: SequenceCheckoutP } } - const getAddFundsHeader = () => { - const { location } = navigation - switch (location) { - default: - return - } - } - - const getAddFundsContent = () => { - const { location } = navigation - switch (location) { - default: - return - } - } - - const getCheckoutFlowHeader = () => { - const { location } = checkoutNavigation - switch (location) { - default: - return - } - } - - const getCheckoutFlowContent = () => { - const { location } = checkoutNavigation - switch (location) { - case 'token-selection': - return - default: - return - } - } - useEffect(() => { - if (openCheckoutModal || openAddFundsModal || openPaymentSelectionModal) { + if (openCreditCardCheckoutModal) { setHistory([]) } - }, [openCheckoutModal, openAddFundsModal, openPaymentSelectionModal]) - - useEffect(() => { - if (openPaymentSelectionModal) { - setCheckoutHistory([getDefaultLocationCheckout()]) - } - }, [openPaymentSelectionModal]) + }, [openCreditCardCheckoutModal]) return ( - - - - - + - - - - - - {openCheckoutModal && ( - setOpenCheckoutModal(false)} - > -
- {getCheckoutHeader()} - {getCheckoutContent()} -
-
- )} - {openAddFundsModal && ( - -
- {getAddFundsHeader()} - {getAddFundsContent()} -
-
- )} - {openPaymentSelectionModal && ( - setOpenPaymentSelectionModal(false)} - > -
- {getCheckoutFlowHeader()} - {getCheckoutFlowContent()} -
-
- )} - {openTransferFundsModal && ( - -
- - -
-
- )} - {openTransactionStatusModal && ( - -
- -
-
- )} - {isOpenSwapModal && ( - -
- - -
-
- )} -
-
- {children} -
-
-
-
-
-
-
-
+ + + {openCreditCardCheckoutModal && ( + setOpenCreditCardCheckoutModal(false)} + > +
+ {getCheckoutHeader()} + {getCheckoutContent()} +
+
+ )} + {openTransferFundsModal && ( + +
+ + +
+
+ )} + {openTransactionStatusModal && ( + +
+ +
+
+ )} +
+
+ {children} + + + + +
) diff --git a/packages/checkout/src/contexts/AddFundsModal.ts b/packages/checkout/src/contexts/AddFundsModal.ts deleted file mode 100644 index 8eda9ea92..000000000 --- a/packages/checkout/src/contexts/AddFundsModal.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { TransactionOnRampProvider } from '@0xsequence/marketplace' -import type { Hex } from 'viem' - -import { createGenericContext } from './genericContext.js' - -export interface AddFundsSettings { - walletAddress: string | Hex - fiatAmount?: string - fiatCurrency?: string - defaultFiatAmount?: string - defaultCryptoCurrency?: string - cryptoCurrencyList?: string - networks?: string - onClose?: () => void - onOrderCreated?: (data: any) => void - onOrderSuccessful?: (data: any) => void - onOrderFailed?: (data: any) => void - provider?: TransactionOnRampProvider - transakOnRampKind?: 'default' | 'windowed' - cryptoAmount?: string - windowedOnRampMessage?: string -} - -type AddFundsModalContext = { - isAddFundsModalOpen: boolean - triggerAddFunds: (settings: AddFundsSettings) => void - closeAddFunds: () => void - addFundsSettings?: AddFundsSettings -} - -const [useAddFundsModalContext, AddFundsContextProvider] = createGenericContext() - -export { AddFundsContextProvider, useAddFundsModalContext } diff --git a/packages/checkout/src/contexts/CheckoutModal.ts b/packages/checkout/src/contexts/CreditCardCheckout.ts similarity index 60% rename from packages/checkout/src/contexts/CheckoutModal.ts rename to packages/checkout/src/contexts/CreditCardCheckout.ts index 1b6977a88..7dd742083 100644 --- a/packages/checkout/src/contexts/CheckoutModal.ts +++ b/packages/checkout/src/contexts/CreditCardCheckout.ts @@ -1,21 +1,16 @@ 'use client' -import type { Theme } from '@0xsequence/design-system' import { type SequenceIndexer, type TransactionReceipt } from '@0xsequence/indexer' import { createGenericContext } from './genericContext.js' -import type { ActionButtons, SupplementaryAnalyticsInfo } from './SelectPaymentModal.js' -interface CoinQuantity { - contractAddress: string - amountRequiredRaw: string +export interface SupplementaryAnalyticsInfo { + [key: string]: string } -interface OrderSummaryItem { - chainId: number - contractAddress: string - quantityRaw: string - tokenId: string +export interface ActionButtons { + label: string + action: () => void } export interface TransakConfig { @@ -49,7 +44,7 @@ export interface ForteEventsCallbacks { export type ForteConfig = (ForteMintConfig | ForteCustomEvmCallConfig) & ForteEventsCallbacks -export interface CreditCardCheckout { +export interface CreditCardCheckoutSettings { chainId: number contractAddress: string recipientAddress: string @@ -62,11 +57,11 @@ export interface CreditCardCheckout { nftQuantity: string nftDecimals?: string calldata: string - provider?: 'transak' | 'forte' + provider?: 'transak' | 'forte' | string transakConfig?: TransakConfig forteConfig?: ForteConfig - onSuccess?: (transactionHash?: string, settings?: CreditCardCheckout) => void - onError?: (error: Error, settings: CreditCardCheckout) => void + onSuccess?: (transactionHash?: string, settings?: CreditCardCheckoutSettings) => void + onError?: (error: Error, settings: CreditCardCheckoutSettings) => void onClose?: () => void approvedSpenderAddress?: string supplementaryAnalyticsInfo?: SupplementaryAnalyticsInfo @@ -74,23 +69,13 @@ export interface CreditCardCheckout { onSuccessChecker?: (receipt: TransactionReceipt, indexerClient?: SequenceIndexer) => Promise } -export interface CheckoutSettings { - creditCardCheckout?: CreditCardCheckout - cryptoCheckout?: { - chainId: number - triggerTransaction: () => void - coinQuantity: CoinQuantity - } - orderSummaryItems?: OrderSummaryItem[] -} - -type CheckoutModalContext = { - triggerCheckout: (settings: CheckoutSettings) => void - closeCheckout: () => void - settings?: CheckoutSettings - theme: Theme +type CreditCardCheckoutContext = { + initiateCreditCardCheckout: (settings: CreditCardCheckoutSettings) => void + closeCreditCardCheckout: () => void + settings?: CreditCardCheckoutSettings } -const [useCheckoutModalContext, CheckoutModalContextProvider] = createGenericContext() +const [useCreditCardCheckoutModalContext, CreditCardCheckoutModalContextProvider] = + createGenericContext() -export { CheckoutModalContextProvider, useCheckoutModalContext } +export { CreditCardCheckoutModalContextProvider, useCreditCardCheckoutModalContext } diff --git a/packages/checkout/src/contexts/FortePayment.ts b/packages/checkout/src/contexts/FortePayment.ts index 8eec10a30..a775fe776 100644 --- a/packages/checkout/src/contexts/FortePayment.ts +++ b/packages/checkout/src/contexts/FortePayment.ts @@ -1,12 +1,12 @@ 'use client' -import { type CreditCardCheckout } from './CheckoutModal.js' +import { type CreditCardCheckoutSettings } from './CreditCardCheckout.js' import { createGenericContext } from './genericContext.js' export interface FortePaymentData { paymentIntentId: string widgetData: any - creditCardCheckout: CreditCardCheckout + creditCardCheckout: CreditCardCheckoutSettings } export interface FortePaymentController { diff --git a/packages/checkout/src/contexts/Navigation.ts b/packages/checkout/src/contexts/Navigation.ts index 528529def..b455556c1 100644 --- a/packages/checkout/src/contexts/Navigation.ts +++ b/packages/checkout/src/contexts/Navigation.ts @@ -1,6 +1,6 @@ 'use client' -import type { CreditCardCheckout } from '../contexts/CheckoutModal.js' +import type { CreditCardCheckoutSettings } from '../contexts/CreditCardCheckout.js' import { createGenericContext } from './genericContext.js' @@ -31,7 +31,7 @@ export interface TransactionErrorNavigation { } export interface TransactionPendingParams { - creditCardCheckout: CreditCardCheckout + creditCardCheckout: CreditCardCheckoutSettings } export interface TransactionPendingNavigation { diff --git a/packages/checkout/src/contexts/SelectPaymentModal.ts b/packages/checkout/src/contexts/SelectPaymentModal.ts deleted file mode 100644 index 0243b6763..000000000 --- a/packages/checkout/src/contexts/SelectPaymentModal.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { type SequenceIndexer, type TransactionReceipt } from '@0xsequence/indexer' -import { TransactionOnRampProvider } from '@0xsequence/marketplace' -import type { Hex } from 'viem' - -import type { ForteConfig, TransakConfig } from '../contexts/CheckoutModal.js' - -import { createGenericContext } from './genericContext.js' - -export type CreditCardProviders = 'transak' | 'forte' - -export interface Collectible { - tokenId?: string - quantity: string - decimals?: number - price?: string -} - -export interface SupplementaryAnalyticsInfo { - [key: string]: string -} - -export interface ActionButtons { - label: string - action: () => void -} - -export interface SelectPaymentSettings { - collectibles: Collectible[] - chain: number | string - currencyAddress: string | Hex - price: string - targetContractAddress: string | Hex - txData: Hex - collectionAddress: string | Hex - recipientAddress: string | Hex - approvedSpenderAddress?: string - transactionConfirmations?: number - onSuccess?: (txHash?: string) => void - onError?: (error: Error) => void - onClose?: () => void - onRampProvider?: TransactionOnRampProvider - creditCardProviders?: string[] - transakConfig?: TransakConfig - forteConfig?: ForteConfig - customProviderCallback?: (onSuccess: (txHash: string) => void, onError: (error: Error) => void, onClose: () => void) => void - supplementaryAnalyticsInfo?: SupplementaryAnalyticsInfo - skipNativeBalanceCheck?: boolean - slippageBps?: number - nativeTokenAddress?: string - successActionButtons?: ActionButtons[] - onSuccessChecker?: (receipt: TransactionReceipt, indexerClient?: SequenceIndexer) => Promise -} - -type SelectPaymentModalContext = { - openSelectPaymentModal: (settings: SelectPaymentSettings) => void - closeSelectPaymentModal: () => void - selectPaymentSettings?: SelectPaymentSettings -} - -const [useSelectPaymentContext, SelectPaymentContextProvider] = createGenericContext() - -export { SelectPaymentContextProvider, useSelectPaymentContext } diff --git a/packages/checkout/src/contexts/SwapModal.ts b/packages/checkout/src/contexts/SwapModal.ts deleted file mode 100644 index b9f2c9fd6..000000000 --- a/packages/checkout/src/contexts/SwapModal.ts +++ /dev/null @@ -1,36 +0,0 @@ -'use client' - -import type { Hex } from 'viem' - -import { createGenericContext } from './genericContext.js' - -interface Transaction { - to: Hex - data?: Hex - value?: bigint -} - -export interface SwapModalSettings { - chainId: number - toTokenAddress: string - toTokenAmount: string - slippageBps?: number - title?: string - description?: string - disableMainCurrency?: boolean - postSwapTransactions?: Transaction[] - blockConfirmations?: number - customSwapErrorMessage?: string - onSuccess?: (txHash: string) => void -} - -type SwapModalContext = { - isSwapModalOpen: boolean - openSwapModal: (settings: SwapModalSettings) => void - closeSwapModal: () => void - swapModalSettings?: SwapModalSettings -} - -const [useSwapModalContext, SwapModalContextProvider] = createGenericContext() - -export { SwapModalContextProvider, useSwapModalContext } diff --git a/packages/checkout/src/contexts/TransactionStatusModal.ts b/packages/checkout/src/contexts/TransactionStatusModal.ts index 53f96a8a3..f72d1ab49 100644 --- a/packages/checkout/src/contexts/TransactionStatusModal.ts +++ b/packages/checkout/src/contexts/TransactionStatusModal.ts @@ -1,7 +1,7 @@ import { type SequenceIndexer, type TransactionReceipt } from '@0xsequence/indexer' +import type { ActionButtons } from './CreditCardCheckout.js' import { createGenericContext } from './genericContext.js' -import type { ActionButtons } from './SelectPaymentModal.js' interface Item { tokenId?: string diff --git a/packages/checkout/src/contexts/index.ts b/packages/checkout/src/contexts/index.ts index fc38d9e36..76e864f37 100644 --- a/packages/checkout/src/contexts/index.ts +++ b/packages/checkout/src/contexts/index.ts @@ -1,9 +1,6 @@ -export * from './AddFundsModal.js' -export * from './CheckoutModal.js' +export * from './CreditCardCheckout.js' export * from './Navigation.js' export * from './NavigationCheckout.js' -export * from './SelectPaymentModal.js' -export * from './SwapModal.js' export * from './TransferFundsModal.js' export * from './TransactionStatusModal.js' export * from './Environment.js' diff --git a/packages/checkout/src/hooks/index.ts b/packages/checkout/src/hooks/index.ts index d75718c55..96c18e84f 100644 --- a/packages/checkout/src/hooks/index.ts +++ b/packages/checkout/src/hooks/index.ts @@ -1,14 +1,7 @@ -export * from './useAddFundsModal.js' -export * from './useCheckoutModal.js' +export * from './useCreditCardCheckoutModal.js' export * from './useNavigation.js' -export * from './useModalTheme.js' -export * from './useSelectPaymentModal.js' -export * from './useTransferFundsModal.js' export * from './useTransactionStatusModal.js' -export * from './useSwapModal.js' -export * from './useCheckoutOptionsSalesContract.js' -export * from './useERC1155SaleContractCheckout.js' +export * from './useTransferFundsModal.js' export * from './useSkipOnCloseCallback.js' export * from './useFortePaymentIntent.js' -export * from './useAddFundsModal.js' export * from './useTransactionCounter.js' diff --git a/packages/checkout/src/hooks/useAddFundsModal.ts b/packages/checkout/src/hooks/useAddFundsModal.ts deleted file mode 100644 index 07ae4721f..000000000 --- a/packages/checkout/src/hooks/useAddFundsModal.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { useAddFundsModalContext, type AddFundsSettings } from '../contexts/AddFundsModal.js' - -/** - * Return type for the useAddFundsModal hook. - * - * @property Function to open the On-ramp modal `triggerAddFunds` - * @property Function to close the On-ramp modal `closeAddFunds` - * @property Current settings for the On-ramp modal `addFundsSettings` - */ -type UseAddFundsModalReturnType = { - isAddFundsModalOpen: boolean - triggerAddFunds: (settings: AddFundsSettings) => void - closeAddFunds: () => void - addFundsSettings: AddFundsSettings | undefined -} - -/** - * Hook to manage the On-ramp modal that allows users to buy cryptocurrency with a credit/debit card. - * - * This hook provides methods to open and close the modal, and access its current settings. - * The modal integration is powered by Transak, allowing users to purchase crypto directly within - * your application. - * - * Go to {@link https://docs.sequence.xyz/sdk/web/checkout-sdk/hooks/useAddFundsModal} for more detailed documentation. - * - * @returns An object containing functions and settings for the On-ramp modal {@link UseAddFundsModalReturnType} - * - * @example - * ```tsx - * import { useAddFundsModal } from '@0xsequence/checkout' - * - * const YourComponent = () => { - * const { triggerAddFunds } = useAddFundsModal() - * const walletAddress = '0x123...' // User's wallet address - * - * const handleAddFunds = () => { - * triggerAddFunds({ - * walletAddress, - * defaultFiatAmount: '50', - * defaultCryptoCurrency: 'USDC', - * onOrderSuccessful: (data) => { - * console.log('Order successful!', data) - * } - * }) - * } - * - * return ( - * - * ) - * } - * ``` - */ -export const useAddFundsModal = (): UseAddFundsModalReturnType => { - const { isAddFundsModalOpen, triggerAddFunds, closeAddFunds, addFundsSettings } = useAddFundsModalContext() - - return { isAddFundsModalOpen, triggerAddFunds, closeAddFunds, addFundsSettings } -} diff --git a/packages/checkout/src/hooks/useCheckoutModal.ts b/packages/checkout/src/hooks/useCheckoutModal.ts deleted file mode 100644 index 396a8d21d..000000000 --- a/packages/checkout/src/hooks/useCheckoutModal.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { useCheckoutModalContext, type CheckoutSettings } from '../contexts/CheckoutModal.js' - -/** - * Return type for the useCheckoutModal hook. - * - * @property {function(settings: CheckoutSettings): void} triggerCheckout - Function to open the Checkout modal - * @property {function(): void} closeCheckout - Function to close the Checkout modal - * @property {CheckoutSettings|undefined} settings - Current settings for the Checkout modal - */ -type UseCheckoutModalReturnType = { - triggerCheckout: (settings: CheckoutSettings) => void - closeCheckout: () => void - settings: CheckoutSettings | undefined -} - -/** - * Hook to manage the Checkout modal that allows users to complete purchases using various payment methods. - * - * This hook provides methods to open and close the checkout modal, and access its current settings. - * Checkout supports credit card payments and crypto payments for purchasing digital assets. - * - * Go to {@link https://docs.sequence.xyz/sdk/web/checkout-sdk/hooks/useCheckoutModal} for more detailed documentation. - * - * @returns An object containing functions and settings for the Checkout modal {@link UseCheckoutModalReturnType} - * - * @example - * ```tsx - * import { useCheckoutModal } from '@0xsequence/checkout' - * import { ChainId } from '@0xsequence/network' - * import { getOrderbookCalldata } from '../utils' - * - * const YourComponent = () => { - * const { address } = useAccount() - * const { triggerCheckout } = useCheckoutModal() - * - * const handleCheckout = () => { - * // NFT purchase settings - * const chainId = ChainId.POLYGON - * const orderbookAddress = '0xB537a160472183f2150d42EB1c3DD6684A55f74c' - * const nftQuantity = '1' - * const orderId = 'your-order-id' - * const tokenContractAddress = '0xabcdef...' // NFT contract address - * const tokenId = '123' // NFT token ID - * - * triggerCheckout({ - * creditCardCheckout: { - * chainId, - * contractAddress: orderbookAddress, - * recipientAddress: address || '', - * currencyQuantity: '100000', - * currencySymbol: 'USDC', - * currencyAddress: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', - * currencyDecimals: '6', - * nftId: tokenId, - * nftAddress: tokenContractAddress, - * nftQuantity, - * approvedSpenderAddress: orderbookAddress, - * calldata: getOrderbookCalldata({ - * orderId, - * quantity: nftQuantity, - * recipient: address || '' - * }), - * onSuccess: (txHash) => console.log('Success!', txHash) - * }, - * orderSummaryItems: [ - * { - * title: 'NFT #' + tokenId, - * subtitle: 'Your Collection', - * imageUrl: 'https://example.com/nft.png' - * } - * ] - * }) - * } - * - * return ( - * - * ) - * } - * ``` - */ -export const useCheckoutModal = (): UseCheckoutModalReturnType => { - const { triggerCheckout, closeCheckout, settings } = useCheckoutModalContext() - - return { triggerCheckout, closeCheckout, settings } -} diff --git a/packages/checkout/src/hooks/useCheckoutOptionsSalesContract.ts b/packages/checkout/src/hooks/useCheckoutOptionsSalesContract.ts deleted file mode 100644 index 36c106f39..000000000 --- a/packages/checkout/src/hooks/useCheckoutOptionsSalesContract.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { CheckoutOptionsSalesContractArgs } from '@0xsequence/marketplace' -import { useQuery } from '@tanstack/react-query' - -import { useMarketplaceClient } from './useMarketplaceClient.js' - -export interface UseGenerateBuyTransactionOptions { - disabled?: boolean -} - -export const useCheckoutOptionsSalesContract = ( - chain: number | string, - args: CheckoutOptionsSalesContractArgs, - options?: UseGenerateBuyTransactionOptions -) => { - const marketplaceClient = useMarketplaceClient({ chain }) - - return useQuery({ - queryKey: ['useCheckoutOptionsSalesContract', args], - queryFn: async () => { - const res = await marketplaceClient.checkoutOptionsSalesContract(args) - - return res - }, - retry: false, - staleTime: 360 * 1000, - enabled: !options?.disabled && !!args.wallet - }) -} diff --git a/packages/checkout/src/hooks/useCheckoutUI/index.tsx b/packages/checkout/src/hooks/useCheckoutUI/index.tsx deleted file mode 100644 index 33bc0ae72..000000000 --- a/packages/checkout/src/hooks/useCheckoutUI/index.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { useGetContractInfo, useGetTokenMetadata } from '@0xsequence/hooks' -import { findSupportedNetwork } from '@0xsequence/network' -import type { Hex } from 'viem' - -import type { TransakConfig } from '../../contexts/CheckoutModal.js' -import type { Collectible, CreditCardProviders } from '../../contexts/SelectPaymentModal.js' - -import { useCreditCardPayment, type UseCreditCardPaymentReturn } from './useCreditCardPayment.js' -import { useCryptoPayment, type UseCryptoPaymentReturn } from './useCryptoPayment.js' -import { useOrderSummary, type UseOrderSummaryReturn } from './useOrderSummary.js' - -interface UseCheckoutUIArgs { - chain: string | number - currencyAddress: string - totalPriceRaw: string - collectible: Collectible - collectionAddress: string - recipientAddress: string - targetContractAddress: string - txData: Hex - transactionConfirmations?: number - slippageBps?: number - creditCardProvider?: CreditCardProviders - transakConfig?: TransakConfig - onSuccess?: (txHash: string) => void - onError?: (error: Error) => void -} - -interface UseCheckoutUIReturn { - orderSummary: UseOrderSummaryReturn - creditCardPayment: UseCreditCardPaymentReturn - cryptoPayment: UseCryptoPaymentReturn -} - -export const useCheckoutUI = ({ - chain, - currencyAddress, - totalPriceRaw, - collectible, - collectionAddress, - recipientAddress, - targetContractAddress, - txData, - transactionConfirmations, - creditCardProvider, - transakConfig, - slippageBps, - onSuccess, - onError -}: UseCheckoutUIArgs): UseCheckoutUIReturn => { - const network = findSupportedNetwork(chain) - const chainId = network?.chainId || 137 - - const { - data: tokenMetadatas, - isLoading: isLoadingTokenMetadatas, - error: errorTokenMetadata - } = useGetTokenMetadata( - { - chainID: String(chainId), - contractAddress: collectionAddress, - tokenIDs: [collectible.tokenId ?? ''] - }, - { - disabled: !collectible.tokenId - } - ) - - const { - data: dataCollectionInfo, - isLoading: isLoadingCollectionInfo, - error: errorCollectionInfo - } = useGetContractInfo({ - chainID: String(chainId), - contractAddress: collectionAddress - }) - - const { - data: currencyInfo, - isLoading: isLoadingCurrencyInfo, - error: errorCurrencyInfo - } = useGetContractInfo({ - chainID: String(chainId), - contractAddress: currencyAddress - }) - - const orderSummary = useOrderSummary({ - chain, - currencyAddress, - totalPriceRaw, - collectible, - collectionAddress, - currencyInfo, - tokenMetadatas, - dataCollectionInfo, - isLoadingCollectionInfo, - errorCollectionInfo, - isLoadingCurrencyInfo, - isLoadingTokenMetadatas, - errorTokenMetadata, - errorCurrencyInfo - }) - - const creditCardPayment = useCreditCardPayment({ - chain, - currencyAddress, - totalPriceRaw, - collectible, - collectionAddress, - recipientAddress, - targetContractAddress, - txData, - creditCardProvider, - transakConfig, - onSuccess, - onError, - currencyInfo, - tokenMetadatas, - dataCollectionInfo, - isLoadingCollectionInfo, - errorCollectionInfo, - isLoadingTokenMetadatas, - errorTokenMetadata, - isLoadingCurrencyInfo, - errorCurrencyInfo - }) - - const cryptoPayment = useCryptoPayment({ - chain, - currencyAddress, - totalPriceRaw, - collectible, - collectionAddress, - recipientAddress, - targetContractAddress, - txData, - transactionConfirmations, - onSuccess, - onError, - currencyInfo, - tokenMetadatas, - dataCollectionInfo, - isLoadingCollectionInfo, - errorCollectionInfo, - isLoadingTokenMetadatas, - errorTokenMetadata, - isLoadingCurrencyInfo, - errorCurrencyInfo, - slippageBps - }) - - return { - orderSummary, - creditCardPayment, - cryptoPayment - } -} diff --git a/packages/checkout/src/hooks/useCheckoutUI/useCreditCardPayment.tsx b/packages/checkout/src/hooks/useCheckoutUI/useCreditCardPayment.tsx deleted file mode 100644 index eacb68f62..000000000 --- a/packages/checkout/src/hooks/useCheckoutUI/useCreditCardPayment.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import { compareAddress } from '@0xsequence/connect' -import type { ContractInfo, TokenMetadata } from '@0xsequence/metadata' -import { findSupportedNetwork } from '@0xsequence/network' -import React, { useEffect, useMemo, useRef } from 'react' -import { formatUnits, zeroAddress, type Hex } from 'viem' - -import type { TransakConfig } from '../../contexts/CheckoutModal.js' -import type { Collectible, CreditCardProviders } from '../../contexts/SelectPaymentModal.js' -import { getCurrencyCode, getTransakProxyAddress } from '../../utils/transak.js' -import { useTransakWidgetUrl } from '../useTransakWidgetUrl.js' -const TRANSAK_IFRAME_ID = 'credit-card-payment-transak-iframe' - -export interface UseCreditCardPaymentArgs { - chain: string | number - currencyAddress: string - totalPriceRaw: string - collectible: Collectible - collectionAddress: string - recipientAddress: string - targetContractAddress: string - txData: Hex - creditCardProvider?: CreditCardProviders - transakConfig?: TransakConfig - onSuccess?: (txHash: string) => void - onError?: (error: Error) => void - currencyInfo: ContractInfo | undefined - tokenMetadatas: TokenMetadata[] | undefined - dataCollectionInfo: ContractInfo | undefined - isLoadingCollectionInfo: boolean - errorCollectionInfo: Error | null - isLoadingTokenMetadatas: boolean - errorTokenMetadata: Error | null - isLoadingCurrencyInfo: boolean - errorCurrencyInfo: Error | null -} - -interface UseCreditCardPaymentData { - iframeId: string - paymentUrl?: string - CreditCardIframe: React.ComponentType - EventListener: React.ComponentType -} - -export interface UseCreditCardPaymentReturn { - error: Error | null - data: UseCreditCardPaymentData - isLoading: boolean -} - -export const useCreditCardPayment = ({ - chain, - currencyAddress, - totalPriceRaw, - collectible, - collectionAddress, - recipientAddress, - targetContractAddress, - txData, - creditCardProvider, - transakConfig, - onSuccess, - onError, - currencyInfo, - tokenMetadatas, - dataCollectionInfo, - isLoadingCollectionInfo, - errorCollectionInfo, - isLoadingTokenMetadatas, - errorTokenMetadata, - isLoadingCurrencyInfo, - errorCurrencyInfo -}: UseCreditCardPaymentArgs): UseCreditCardPaymentReturn => { - const disableTransakWidgetUrlFetch = - isLoadingTokenMetadatas || isLoadingCurrencyInfo || isLoadingCollectionInfo || creditCardProvider !== 'transak' - - const network = findSupportedNetwork(chain) - const error = errorCollectionInfo || errorTokenMetadata || errorCurrencyInfo - const isLoading = isLoadingCollectionInfo || isLoadingTokenMetadatas || isLoadingCurrencyInfo - const isNativeCurrency = compareAddress(currencyAddress, zeroAddress) - const currencySymbol = isNativeCurrency ? network?.nativeToken.symbol : currencyInfo?.symbol || 'POL' - const currencyDecimals = isNativeCurrency ? network?.nativeToken.decimals : currencyInfo?.decimals || 18 - const iframeRef = useRef(null) - const tokenMetadata = tokenMetadatas?.[0] - - // Transak requires the recipient address to be the proxy address - // so we need to replace the recipient address with the proxy address in the calldata - // this is a weird hack so that credit card integrations are as simple as possible and should work 99% of the time - // If an issue arises, the user can override the calldata in the transak settings - - const transakProxyAddress = getTransakProxyAddress(network?.chainId || 137) || '' - const calldataWithProxy = - transakConfig?.callDataOverride ?? - txData.replace(recipientAddress.toLowerCase().substring(2), transakProxyAddress.substring(2)) - - const price = Number(formatUnits(BigInt(totalPriceRaw), Number(currencyDecimals || 18))) - - const transakNftData = [ - { - imageURL: tokenMetadata?.image || '', - nftName: tokenMetadata?.name || 'collectible', - collectionAddress: collectionAddress, - tokenIDs: [collectible.tokenId || ''], - prices: [price], - quantity: Number(collectible.quantity), - nftType: dataCollectionInfo?.type || 'ERC721' - } - ] - - const estimatedGasLimit = 500000 - - const partnerOrderId = useMemo(() => { - return `${recipientAddress}-${new Date().getTime()}` - }, [recipientAddress]) - - // Note: the network name might not always line up with Transak. A conversion function might be necessary - const networkName = network?.name.toLowerCase() - - const { - data: transakLinkData, - isLoading: isLoadingTransakLink, - error: errorTransakLink - } = useTransakWidgetUrl( - { - isNFT: true, - calldata: calldataWithProxy, - targetContractAddress, - cryptoCurrencyCode: getCurrencyCode({ - chainId: network?.chainId || 137, - currencyAddress, - defaultCurrencyCode: currencySymbol || 'ETH' - }), - estimatedGasLimit, - nftData: transakNftData, - walletAddress: recipientAddress, - disableWalletAddressForm: true, - partnerOrderId, - network: networkName, - referrerDomain: window.location.origin - }, - disableTransakWidgetUrlFetch - ) - - const missingCreditCardProvider = !creditCardProvider - - if (missingCreditCardProvider) { - return { - error: new Error('Missing credit card provider or transak config'), - data: { - iframeId: '', - CreditCardIframe: () => null, - EventListener: () => null - }, - isLoading: false - } - } - - if (error || isLoading) { - return { - error, - data: { - iframeId: '', - CreditCardIframe: () => null, - EventListener: () => null - }, - isLoading - } - } - - if (creditCardProvider === 'transak') { - const transakLink = transakLinkData?.url - - return { - error: errorTransakLink, - data: { - iframeId: TRANSAK_IFRAME_ID, - paymentUrl: transakLink, - CreditCardIframe: () => ( -
-