diff --git a/package-lock.json b/package-lock.json
index 9015a2c..8657f56 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,8 @@
"@tanstack/react-query": "^5.86.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
- "html2pdf.js": "^0.12.0",
+ "html2canvas-pro": "^1.5.11",
+ "jspdf": "^3.0.3",
"lucide-react": "^0.542.0",
"next": "15.5.2",
"react": "19.1.0",
@@ -29,12 +30,16 @@
"devDependencies": {
"@biomejs/biome": "2.2.0",
"@tailwindcss/postcss": "^4",
+ "@types/jspdf": "^1.3.3",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"shadcn": "^3.1.0",
"tailwindcss": "^4",
"typescript": "^5"
+ },
+ "engines": {
+ "node": ">=18.18.0"
}
},
"node_modules/@adraffy/ens-normalize": {
@@ -3335,6 +3340,13 @@
"@types/ms": "*"
}
},
+ "node_modules/@types/jspdf": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@types/jspdf/-/jspdf-1.3.3.tgz",
+ "integrity": "sha512-DqwyAKpVuv+7DniCp2Deq1xGvfdnKSNgl9Agun2w6dFvR5UKamiv4VfYUgcypd8S9ojUyARFIlZqBrYrBMQlew==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz",
@@ -7046,6 +7058,7 @@
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"license": "MIT",
+ "optional": true,
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
@@ -7054,14 +7067,17 @@
"node": ">=8.0.0"
}
},
- "node_modules/html2pdf.js": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/html2pdf.js/-/html2pdf.js-0.12.0.tgz",
- "integrity": "sha512-UiaAxJpkNiintpAKZ94V0GTmwDSootT78f5AHw5nUjDXo+RHsJJ0aVhoccrxdWiM7Lx2cJ929ca7mAnbSt13gw==",
+ "node_modules/html2canvas-pro": {
+ "version": "1.5.11",
+ "resolved": "https://registry.npmjs.org/html2canvas-pro/-/html2canvas-pro-1.5.11.tgz",
+ "integrity": "sha512-W4pEeKLG8+9a54RDOSiEKq7gRXXDzt0ORMaLXX+l6a3urSKbmnkmyzcRDCtgTOzmHLaZTLG2wiTQMJqKLlSh3w==",
"license": "MIT",
"dependencies": {
- "html2canvas": "^1.0.0",
- "jspdf": "^3.0.0"
+ "css-line-break": "^2.1.0",
+ "text-segmentation": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=16.0.0"
}
},
"node_modules/http-errors": {
@@ -7571,9 +7587,9 @@
}
},
"node_modules/jspdf": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.2.tgz",
- "integrity": "sha512-G0fQDJ5fAm6UW78HG6lNXyq09l0PrA1rpNY5i+ly17Zb1fMMFSmS+3lw4cnrAPGyouv2Y0ylujbY2Ieq3DSlKA==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.3.tgz",
+ "integrity": "sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.9",
diff --git a/package.json b/package.json
index c8e6a74..cacf16c 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,8 @@
"@tanstack/react-query": "^5.86.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
- "html2pdf.js": "^0.12.0",
+ "html2canvas-pro": "^1.5.11",
+ "jspdf": "^3.0.3",
"lucide-react": "^0.542.0",
"next": "15.5.2",
"react": "19.1.0",
diff --git a/public/r/payment-widget.json b/public/r/payment-widget.json
index 4cdaeb3..5e16dcd 100644
--- a/public/r/payment-widget.json
+++ b/public/r/payment-widget.json
@@ -10,7 +10,8 @@
"@tanstack/react-query@^5.86.0",
"react-hook-form@^7.62.0",
"lucide-react@^0.542.0",
- "html2pdf.js@^0.12.0"
+ "html2canvas-pro@^1.5.11",
+ "jspdf@^3.0.3"
],
"registryDependencies": [
"button",
@@ -22,15 +23,27 @@
"files": [
{
"path": "registry/default/payment-widget/payment-widget.tsx",
- "content": "\"use client\";\n\nimport { type PropsWithChildren, useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { ConnectionHandler } from \"./components/connection-handler\";\nimport { Web3Provider } from \"./context/web3-context\";\nimport { PaymentModal } from \"./components/payment-modal\";\nimport {\n PaymentWidgetProvider,\n usePaymentWidgetContext,\n} from \"./context/payment-widget-context\";\nimport type { PaymentWidgetProps } from \"./payment-widget.types\";\nimport { ICONS } from \"./constants\";\n\nfunction PaymentWidgetInner({ children }: PropsWithChildren) {\n const [isModalOpen, setIsModalOpen] = useState(false);\n const handleModalOpenChange = (open: boolean) => {\n setIsModalOpen(open);\n };\n\n const {\n walletAccount,\n paymentConfig: { rnApiClientId, supportedCurrencies },\n } = usePaymentWidgetContext();\n\n let isButtonDisabled = false;\n\n if (!rnApiClientId || rnApiClientId === \"\") {\n console.error(\"PaymentWidget: rnApiClientId is required in paymentConfig\");\n\n isButtonDisabled = true;\n }\n\n if (supportedCurrencies.length === 0) {\n console.error(\n \"PaymentWidget: supportedCurrencies is required in paymentConfig\",\n );\n\n isButtonDisabled = true;\n }\n\n return (\n
\n
{\n if (isButtonDisabled) return;\n setIsModalOpen(true);\n }}\n variant=\"ghost\"\n className=\"p-0 h-auto bg-transparent hover:bg-transparent\"\n >\n {children || \"Pay with crypto\"}\n \n\n
\n {/** biome-ignore lint/performance/noImgElement: This is a ShadCN library, we can't enforce next syntax on everybody */}\n
\n
Powered by Request Network \n
\n\n {walletAccount !== undefined ? (\n
\n ) : (\n
\n }\n />\n )}\n
\n );\n}\n\nexport function PaymentWidget({\n amountInUsd,\n recipientWallet,\n paymentConfig,\n receiptInfo,\n onSuccess,\n onError,\n uiConfig,\n walletAccount,\n children,\n}: PaymentWidgetProps) {\n return (\n \n \n {children} \n \n \n );\n}\n",
+ "content": "\"use client\";\n\nimport { type PropsWithChildren, useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { ConnectionHandler } from \"./components/connection-handler\";\nimport { Web3Provider } from \"./context/web3-context\";\nimport { PaymentModal } from \"./components/payment-modal\";\nimport { PaymentWidgetProvider } from \"./context/payment-widget-context/payment-widget-provider\";\nimport type { PaymentWidgetProps } from \"./payment-widget.types\";\nimport { ICONS } from \"./constants\";\nimport { usePaymentWidgetContext } from \"./context/payment-widget-context/use-payment-widget-context\";\n\nfunction PaymentWidgetInner({ children }: PropsWithChildren) {\n const [isModalOpen, setIsModalOpen] = useState(false);\n const handleModalOpenChange = (open: boolean) => {\n setIsModalOpen(open);\n };\n\n const {\n walletAccount,\n paymentConfig: { rnApiClientId, supportedCurrencies },\n } = usePaymentWidgetContext();\n\n let isButtonDisabled = false;\n\n if (!rnApiClientId || rnApiClientId === \"\") {\n console.error(\"PaymentWidget: rnApiClientId is required in paymentConfig\");\n\n isButtonDisabled = true;\n }\n\n if (supportedCurrencies.length === 0) {\n console.error(\n \"PaymentWidget: supportedCurrencies is required in paymentConfig\",\n );\n\n isButtonDisabled = true;\n }\n\n return (\n \n
{\n if (isButtonDisabled) return;\n setIsModalOpen(true);\n }}\n variant=\"ghost\"\n className=\"p-0 h-auto bg-transparent hover:bg-transparent\"\n >\n {children || \"Pay with crypto\"}\n \n\n
\n {/** biome-ignore lint/performance/noImgElement: This is a ShadCN library, we can't enforce next syntax on everybody */}\n
\n
Powered by Request Network \n
\n\n {walletAccount !== undefined ? (\n
\n ) : (\n
\n }\n />\n )}\n
\n );\n}\n\nexport function PaymentWidget({\n amountInUsd,\n recipientWallet,\n paymentConfig,\n receiptInfo,\n onSuccess,\n onError,\n uiConfig,\n walletAccount,\n children,\n}: PaymentWidgetProps) {\n return (\n \n \n {children} \n \n \n );\n}\n",
"type": "registry:component",
"target": "components/payment-widget/payment-widget.tsx"
},
{
- "path": "registry/default/payment-widget/context/payment-widget-context.tsx",
- "content": "\"use client\";\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport { useAccount } from \"wagmi\";\nimport type { ReceiptInfo, FeeInfo, PaymentError } from \"../types/index\";\nimport type { TransactionReceipt, WalletClient } from \"viem\";\nimport type { PaymentWidgetProps } from \"../payment-widget.types\";\n\nexport interface PaymentWidgetContextValue {\n amountInUsd: string;\n recipientWallet: string;\n\n walletAccount?: WalletClient;\n connectedWalletAddress?: string;\n isWalletOverride: boolean;\n\n paymentConfig: {\n rnApiClientId: string;\n feeInfo?: FeeInfo;\n supportedCurrencies: string[];\n };\n\n uiConfig: {\n showRequestScanUrl: boolean;\n showReceiptDownload: boolean;\n };\n\n receiptInfo: ReceiptInfo;\n\n onSuccess?: (\n requestId: string,\n transactionReceipts: TransactionReceipt[],\n ) => void | Promise;\n onError?: (error: PaymentError) => void | Promise;\n}\n\nconst PaymentWidgetContext = createContext(\n null,\n);\n\ninterface PaymentWidgetProviderProps {\n children: ReactNode;\n amountInUsd: string;\n recipientWallet: string;\n walletAccount?: WalletClient;\n paymentConfig: Omit<\n PaymentWidgetProps[\"paymentConfig\"],\n \"walletConnectProjectId\"\n >;\n uiConfig?: PaymentWidgetProps[\"uiConfig\"];\n receiptInfo: ReceiptInfo;\n onSuccess?: (\n requestId: string,\n transactionReceipts: TransactionReceipt[],\n ) => void | Promise;\n onError?: (error: PaymentError) => void | Promise;\n}\n\nexport function PaymentWidgetProvider({\n children,\n amountInUsd,\n recipientWallet,\n walletAccount,\n paymentConfig,\n uiConfig,\n receiptInfo,\n onSuccess,\n onError,\n}: PaymentWidgetProviderProps) {\n const { address } = useAccount();\n\n const isWalletOverride = walletAccount !== undefined;\n const connectedWalletAddress = walletAccount\n ? walletAccount.account?.address\n : address;\n\n const contextValue: PaymentWidgetContextValue = useMemo(\n () => ({\n amountInUsd,\n recipientWallet,\n walletAccount,\n connectedWalletAddress,\n isWalletOverride,\n paymentConfig: {\n rnApiClientId: paymentConfig.rnApiClientId,\n feeInfo: paymentConfig.feeInfo,\n supportedCurrencies: paymentConfig.supportedCurrencies,\n },\n uiConfig: {\n showReceiptDownload: uiConfig?.showReceiptDownload ?? true,\n showRequestScanUrl: uiConfig?.showRequestScanUrl ?? true,\n },\n receiptInfo,\n onSuccess,\n onError,\n }),\n [\n amountInUsd,\n recipientWallet,\n walletAccount,\n connectedWalletAddress,\n isWalletOverride,\n paymentConfig.rnApiClientId,\n paymentConfig.feeInfo,\n paymentConfig.supportedCurrencies,\n uiConfig?.showReceiptDownload,\n uiConfig?.showRequestScanUrl,\n receiptInfo,\n onSuccess,\n onError,\n ],\n );\n\n return (\n \n {children}\n \n );\n}\n\nexport function usePaymentWidgetContext(): PaymentWidgetContextValue {\n const context = useContext(PaymentWidgetContext);\n\n if (!context) {\n throw new Error(\n \"usePaymentWidgetContext must be used within a PaymentWidgetProvider\",\n );\n }\n\n return context;\n}\n",
+ "path": "registry/default/payment-widget/context/payment-widget-context/index.ts",
+ "content": "import { createContext } from \"react\";\nimport type { ReceiptInfo, FeeInfo, PaymentError } from \"../../types/index\";\nimport type { TransactionReceipt, WalletClient } from \"viem\";\n\nexport interface PaymentWidgetContextValue {\n amountInUsd: string;\n recipientWallet: string;\n\n walletAccount?: WalletClient;\n connectedWalletAddress?: string;\n isWalletOverride: boolean;\n\n paymentConfig: {\n rnApiClientId: string;\n feeInfo?: FeeInfo;\n supportedCurrencies: string[];\n };\n\n uiConfig: {\n showRequestScanUrl: boolean;\n showReceiptDownload: boolean;\n };\n\n receiptInfo: ReceiptInfo;\n\n onSuccess?: (\n requestId: string,\n transactionReceipts: TransactionReceipt[],\n ) => void | Promise;\n onError?: (error: PaymentError) => void | Promise;\n}\n\nexport const PaymentWidgetContext =\n createContext(null);\n",
"type": "registry:component",
- "target": "components/payment-widget/context/payment-widget-context.tsx"
+ "target": "components/payment-widget/context/payment-widget-context/index.ts"
+ },
+ {
+ "path": "registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx",
+ "content": "\"use client\";\n\nimport { useMemo, type ReactNode } from \"react\";\nimport { useAccount } from \"wagmi\";\nimport type { ReceiptInfo, PaymentError } from \"../../types/index\";\nimport type { TransactionReceipt, WalletClient } from \"viem\";\nimport type { PaymentWidgetProps } from \"../../payment-widget.types\";\nimport { PaymentWidgetContext, type PaymentWidgetContextValue } from \"./index\";\n\ninterface PaymentWidgetProviderProps {\n children: ReactNode;\n amountInUsd: string;\n recipientWallet: string;\n walletAccount?: WalletClient;\n paymentConfig: Omit<\n PaymentWidgetProps[\"paymentConfig\"],\n \"walletConnectProjectId\"\n >;\n uiConfig?: PaymentWidgetProps[\"uiConfig\"];\n receiptInfo: ReceiptInfo;\n onSuccess?: (\n requestId: string,\n transactionReceipts: TransactionReceipt[],\n ) => void | Promise;\n onError?: (error: PaymentError) => void | Promise;\n}\n\nexport function PaymentWidgetProvider({\n children,\n amountInUsd,\n recipientWallet,\n walletAccount,\n paymentConfig,\n uiConfig,\n receiptInfo,\n onSuccess,\n onError,\n}: PaymentWidgetProviderProps) {\n const { address } = useAccount();\n\n const isWalletOverride = walletAccount !== undefined;\n const connectedWalletAddress = walletAccount\n ? walletAccount.account?.address\n : address;\n\n const contextValue: PaymentWidgetContextValue = useMemo(\n () => ({\n amountInUsd,\n recipientWallet,\n walletAccount,\n connectedWalletAddress,\n isWalletOverride,\n paymentConfig: {\n rnApiClientId: paymentConfig.rnApiClientId,\n feeInfo: paymentConfig.feeInfo,\n supportedCurrencies: paymentConfig.supportedCurrencies,\n },\n uiConfig: {\n showReceiptDownload: uiConfig?.showReceiptDownload ?? true,\n showRequestScanUrl: uiConfig?.showRequestScanUrl ?? true,\n },\n receiptInfo,\n onSuccess,\n onError,\n }),\n [\n amountInUsd,\n recipientWallet,\n walletAccount,\n connectedWalletAddress,\n isWalletOverride,\n paymentConfig.rnApiClientId,\n paymentConfig.feeInfo,\n paymentConfig.supportedCurrencies,\n uiConfig?.showReceiptDownload,\n uiConfig?.showRequestScanUrl,\n receiptInfo,\n onSuccess,\n onError,\n ],\n );\n\n return (\n \n {children}\n \n );\n}\n",
+ "type": "registry:component",
+ "target": "components/payment-widget/context/payment-widget-context/payment-widget-provider.tsx"
+ },
+ {
+ "path": "registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts",
+ "content": "import { useContext } from \"react\";\nimport { type PaymentWidgetContextValue, PaymentWidgetContext } from \"./index\";\n\nexport function usePaymentWidgetContext(): PaymentWidgetContextValue {\n const context = useContext(PaymentWidgetContext);\n\n if (!context) {\n throw new Error(\n \"usePaymentWidgetContext must be used within a PaymentWidgetProvider\",\n );\n }\n\n return context;\n}\n",
+ "type": "registry:component",
+ "target": "components/payment-widget/context/payment-widget-context/use-payment-widget-context.ts"
},
{
"path": "registry/default/payment-widget/components/connection-handler.tsx",
@@ -46,13 +59,13 @@
},
{
"path": "registry/default/payment-widget/components/payment-modal.tsx",
- "content": "\"use client\";\n\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from \"@/components/ui/dialog\";\nimport { useState } from \"react\";\nimport { CurrencySelect } from \"./currency-select\";\nimport { BuyerInfoForm } from \"./buyer-info-form\";\nimport { PaymentConfirmation } from \"./payment-confirmation\";\nimport { PaymentSuccess } from \"./payment-success\";\nimport { DisconnectWallet } from \"./disconnect-wallet\";\nimport { usePaymentWidgetContext } from \"../context/payment-widget-context\";\nimport type { BuyerInfo } from \"../types/index\";\nimport type { ConversionCurrency } from \"../utils/currencies\";\nimport type { TransactionReceipt } from \"viem\";\n\ninterface PaymentModalProps {\n isOpen: boolean;\n handleModalOpenChange: (open: boolean) => void;\n}\n\nexport function PaymentModal({\n isOpen,\n handleModalOpenChange,\n}: PaymentModalProps) {\n const { isWalletOverride, receiptInfo, onSuccess } =\n usePaymentWidgetContext();\n\n const [activeStep, setActiveStep] = useState<\n | \"currency-select\"\n | \"buyer-info\"\n | \"payment-confirmation\"\n | \"payment-success\"\n >(\"currency-select\");\n const [selectedCurrency, setSelectedCurrency] =\n useState(null);\n const [buyerInfo, setBuyerInfo] = useState(\n receiptInfo.buyerInfo || undefined,\n );\n const [requestId, setRequestId] = useState(\"\");\n const [txHash, setTxHash] = useState(\"\");\n\n const handleCurrencySelect = (currency: ConversionCurrency) => {\n setSelectedCurrency(currency);\n setActiveStep(\"buyer-info\");\n };\n\n const handleBuyerInfoSubmit = (data: BuyerInfo) => {\n setBuyerInfo(data);\n setActiveStep(\"payment-confirmation\");\n };\n\n const handlePaymentSuccess = async (\n requestId: string,\n transactionReceipts: TransactionReceipt[],\n ) => {\n setRequestId(requestId);\n setTxHash(\n transactionReceipts[transactionReceipts.length - 1].transactionHash,\n );\n setActiveStep(\"payment-success\");\n await onSuccess?.(requestId, transactionReceipts);\n };\n\n const reset = () => {\n setActiveStep(\"currency-select\");\n setSelectedCurrency(null);\n setBuyerInfo(receiptInfo.buyerInfo || undefined);\n setRequestId(\"\");\n };\n\n return (\n {\n // reset modal state when closing from success step\n if (!isOpen && activeStep === \"payment-success\") {\n reset();\n }\n handleModalOpenChange(isOpen);\n }}\n >\n \n \n Payment \n \n Pay with crypto using Request Network\n \n \n\n {activeStep !== \"payment-success\" && !isWalletOverride && (\n \n )}\n\n {activeStep === \"currency-select\" && (\n \n )}\n\n {activeStep === \"buyer-info\" && (\n setActiveStep(\"currency-select\")}\n onSubmit={handleBuyerInfoSubmit}\n />\n )}\n\n {activeStep === \"payment-confirmation\" &&\n selectedCurrency &&\n buyerInfo && (\n setActiveStep(\"buyer-info\")}\n handlePaymentSuccess={handlePaymentSuccess}\n />\n )}\n\n {activeStep === \"payment-success\" && selectedCurrency && buyerInfo && (\n \n )}\n \n \n );\n}\n",
+ "content": "\"use client\";\n\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from \"@/components/ui/dialog\";\nimport { useState } from \"react\";\nimport { CurrencySelect } from \"./currency-select\";\nimport { BuyerInfoForm } from \"./buyer-info-form\";\nimport { PaymentConfirmation } from \"./payment-confirmation\";\nimport { PaymentSuccess } from \"./payment-success\";\nimport { DisconnectWallet } from \"./disconnect-wallet\";\nimport type { BuyerInfo } from \"../types/index\";\nimport type { ConversionCurrency } from \"../utils/currencies\";\nimport type { TransactionReceipt } from \"viem\";\nimport { usePaymentWidgetContext } from \"../context/payment-widget-context/use-payment-widget-context\";\n\ninterface PaymentModalProps {\n isOpen: boolean;\n handleModalOpenChange: (open: boolean) => void;\n}\n\nexport function PaymentModal({\n isOpen,\n handleModalOpenChange,\n}: PaymentModalProps) {\n const { isWalletOverride, receiptInfo, onSuccess } =\n usePaymentWidgetContext();\n\n const [activeStep, setActiveStep] = useState<\n | \"currency-select\"\n | \"buyer-info\"\n | \"payment-confirmation\"\n | \"payment-success\"\n >(\"currency-select\");\n const [selectedCurrency, setSelectedCurrency] =\n useState(null);\n const [buyerInfo, setBuyerInfo] = useState(\n receiptInfo.buyerInfo || undefined,\n );\n const [requestId, setRequestId] = useState(\"\");\n const [txHash, setTxHash] = useState(\"\");\n\n const handleCurrencySelect = (currency: ConversionCurrency) => {\n setSelectedCurrency(currency);\n setActiveStep(\"buyer-info\");\n };\n\n const handleBuyerInfoSubmit = (data: BuyerInfo) => {\n setBuyerInfo(data);\n setActiveStep(\"payment-confirmation\");\n };\n\n const handlePaymentSuccess = async (\n requestId: string,\n transactionReceipts: TransactionReceipt[],\n ) => {\n setRequestId(requestId);\n setTxHash(\n transactionReceipts[transactionReceipts.length - 1].transactionHash,\n );\n setActiveStep(\"payment-success\");\n await onSuccess?.(requestId, transactionReceipts);\n };\n\n const reset = () => {\n setActiveStep(\"currency-select\");\n setSelectedCurrency(null);\n setBuyerInfo(receiptInfo.buyerInfo || undefined);\n setRequestId(\"\");\n };\n\n return (\n {\n // reset modal state when closing from success step\n if (!isOpen && activeStep === \"payment-success\") {\n reset();\n }\n handleModalOpenChange(isOpen);\n }}\n >\n \n \n Payment \n \n Pay with crypto using Request Network\n \n \n\n {activeStep !== \"payment-success\" && !isWalletOverride && (\n \n )}\n\n {activeStep === \"currency-select\" && (\n \n )}\n\n {activeStep === \"buyer-info\" && (\n setActiveStep(\"currency-select\")}\n onSubmit={handleBuyerInfoSubmit}\n />\n )}\n\n {activeStep === \"payment-confirmation\" &&\n selectedCurrency &&\n buyerInfo && (\n setActiveStep(\"buyer-info\")}\n handlePaymentSuccess={handlePaymentSuccess}\n />\n )}\n\n {activeStep === \"payment-success\" && selectedCurrency && buyerInfo && (\n \n )}\n \n \n );\n}\n",
"type": "registry:component",
"target": "components/payment-widget/components/payment-modal.tsx"
},
{
"path": "registry/default/payment-widget/components/currency-select.tsx",
- "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n getConversionCurrencies,\n getSymbolOverride,\n type ConversionCurrency,\n} from \"../utils/currencies\";\nimport { Check } from \"lucide-react\";\nimport { usePaymentWidgetContext } from \"../context/payment-widget-context\";\n\ninterface CurrencySelectProps {\n onSubmit: (currency: ConversionCurrency) => void;\n}\n\nexport function CurrencySelect({ onSubmit }: CurrencySelectProps) {\n const {\n paymentConfig: { rnApiClientId, supportedCurrencies },\n } = usePaymentWidgetContext();\n const [selectedCurrency, setSelectedCurrency] = useState(\"\");\n\n const {\n data: conversionCurrencies,\n isLoading,\n isError,\n refetch,\n } = useQuery({\n queryKey: [\"conversionCurrencies\", rnApiClientId],\n queryFn: () => getConversionCurrencies(rnApiClientId),\n });\n\n const handleSubmit = () => {\n const currency = conversionCurrencies?.find(\n (c) => c.id === selectedCurrency,\n );\n if (currency) {\n onSubmit(currency);\n }\n };\n\n if (isLoading) {\n return Loading currencies...
;\n }\n\n if (isError) {\n return (\n \n
\n Error loading currencies. Please try again later.\n
\n
refetch()}>Retry \n
\n );\n }\n\n if (!conversionCurrencies || conversionCurrencies.length === 0) {\n return No conversion currencies available.
;\n }\n\n const lowerCaseSupportedCurrencies = supportedCurrencies.map((currency) =>\n currency.toLowerCase(),\n );\n\n const eligibleCurrencies =\n lowerCaseSupportedCurrencies.length > 0\n ? conversionCurrencies.filter((currency) =>\n lowerCaseSupportedCurrencies.includes(currency.id.toLowerCase()),\n )\n : conversionCurrencies;\n\n if (eligibleCurrencies.length === 0) {\n console.warn(\n \"Your supportedCurrencies do not match available currencies.\",\n { supportedCurrencies, conversionCurrencies },\n );\n return No supported currencies available.
;\n }\n\n return (\n \n
Select a currency \n
\n \n {eligibleCurrencies.map((currency) => {\n const isSelected = selectedCurrency === currency.id;\n return (\n
\n
\n \n
\n
\n {getSymbolOverride(currency.symbol)}\n
\n
\n {currency.name}\n
\n
\n {isSelected && }\n \n
\n );\n })}\n
\n \n
\n Continue\n \n
\n );\n}\n",
+ "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { Button } from \"@/components/ui/button\";\nimport { RadioGroup, RadioGroupItem } from \"@/components/ui/radio-group\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport {\n getConversionCurrencies,\n getSymbolOverride,\n type ConversionCurrency,\n} from \"../utils/currencies\";\nimport { Check } from \"lucide-react\";\nimport { usePaymentWidgetContext } from \"../context/payment-widget-context/use-payment-widget-context\";\n\ninterface CurrencySelectProps {\n onSubmit: (currency: ConversionCurrency) => void;\n}\n\nexport function CurrencySelect({ onSubmit }: CurrencySelectProps) {\n const {\n paymentConfig: { rnApiClientId, supportedCurrencies },\n } = usePaymentWidgetContext();\n const [selectedCurrency, setSelectedCurrency] = useState(\"\");\n\n const {\n data: conversionCurrencies,\n isLoading,\n isError,\n refetch,\n } = useQuery({\n queryKey: [\"conversionCurrencies\", rnApiClientId],\n queryFn: () => getConversionCurrencies(rnApiClientId),\n });\n\n const handleSubmit = () => {\n const currency = conversionCurrencies?.find(\n (c) => c.id === selectedCurrency,\n );\n if (currency) {\n onSubmit(currency);\n }\n };\n\n if (isLoading) {\n return Loading currencies...
;\n }\n\n if (isError) {\n return (\n \n
\n Error loading currencies. Please try again later.\n
\n
refetch()}>Retry \n
\n );\n }\n\n if (!conversionCurrencies || conversionCurrencies.length === 0) {\n return No conversion currencies available.
;\n }\n\n const lowerCaseSupportedCurrencies = supportedCurrencies.map((currency) =>\n currency.toLowerCase(),\n );\n\n const eligibleCurrencies =\n lowerCaseSupportedCurrencies.length > 0\n ? conversionCurrencies.filter((currency) =>\n lowerCaseSupportedCurrencies.includes(currency.id.toLowerCase()),\n )\n : conversionCurrencies;\n\n if (eligibleCurrencies.length === 0) {\n console.warn(\n \"Your supportedCurrencies do not match available currencies.\",\n { supportedCurrencies, conversionCurrencies },\n );\n return No supported currencies available.
;\n }\n\n return (\n \n
Select a currency \n
\n \n {eligibleCurrencies.map((currency) => {\n const isSelected = selectedCurrency === currency.id;\n return (\n
\n
\n \n
\n
\n {getSymbolOverride(currency.symbol)}\n
\n
\n {currency.name}\n
\n
\n {isSelected && }\n \n
\n );\n })}\n
\n \n
\n Continue\n \n
\n );\n}\n",
"type": "registry:component",
"target": "components/payment-widget/components/currency-select.tsx"
},
@@ -64,7 +77,7 @@
},
{
"path": "registry/default/payment-widget/components/payment-confirmation.tsx",
- "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { ArrowRight, AlertCircle } from \"lucide-react\";\nimport { usePayment } from \"../hooks/use-payment\";\nimport {\n getSymbolOverride,\n type ConversionCurrency,\n} from \"../utils/currencies\";\nimport { usePaymentWidgetContext } from \"../context/payment-widget-context\";\nimport type { BuyerInfo, PaymentError } from \"../types/index\";\nimport { useState } from \"react\";\nimport type { TransactionReceipt } from \"viem\";\n\ninterface PaymentConfirmationProps {\n selectedCurrency: ConversionCurrency;\n buyerInfo: BuyerInfo;\n onBack: () => void;\n handlePaymentSuccess: (\n requestId: string,\n transactionReceipts: TransactionReceipt[],\n ) => void;\n}\n\nexport function PaymentConfirmation({\n buyerInfo,\n selectedCurrency,\n onBack,\n handlePaymentSuccess,\n}: PaymentConfirmationProps) {\n const {\n amountInUsd,\n recipientWallet,\n connectedWalletAddress,\n paymentConfig: { rnApiClientId, feeInfo },\n receiptInfo: { companyInfo: { name: companyName } = {} },\n onError,\n walletAccount,\n } = usePaymentWidgetContext();\n const { isExecuting, executePayment } = usePayment(\n selectedCurrency.network,\n walletAccount,\n );\n const [localError, setLocalError] = useState(null);\n\n const handleExecutePayment = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!connectedWalletAddress) return;\n\n setLocalError(null);\n\n try {\n const { requestId, transactionReceipts } = await executePayment(\n rnApiClientId,\n {\n payerWallet: connectedWalletAddress,\n amountInUsd,\n recipientWallet,\n paymentCurrency: selectedCurrency.id,\n feeInfo,\n customerInfo: {\n email: buyerInfo.email,\n firstName: buyerInfo.firstName,\n lastName: buyerInfo.lastName,\n address: buyerInfo.address\n ? {\n street: buyerInfo.address.street,\n city: buyerInfo.address.city,\n state: buyerInfo.address.state,\n postalCode: buyerInfo.address.postalCode,\n country: buyerInfo.address.country,\n }\n : undefined,\n },\n },\n );\n\n handlePaymentSuccess(requestId, transactionReceipts);\n } catch (error) {\n const paymentError = error as PaymentError;\n\n let errorMessage = \"Payment failed. Please try again.\";\n\n if (paymentError.type === \"wallet\") {\n errorMessage =\n \"Wallet connection error. Please check your wallet and try again.\";\n } else if (paymentError.type === \"transaction\") {\n errorMessage =\n \"Transaction failed. Please check your balance and network connection.\";\n } else if (paymentError.type === \"api\") {\n errorMessage =\n paymentError.error?.message ||\n \"Payment service error. Please try again.\";\n } else if (paymentError.error?.message) {\n errorMessage = paymentError.error.message;\n }\n setLocalError(errorMessage);\n\n onError?.(paymentError);\n }\n };\n\n return (\n \n
Payment Confirmation \n\n
\n {/* Payment Currency (From) */}\n
\n
\n {getSymbolOverride(selectedCurrency.symbol)}\n
\n
\n
From
\n
\n {selectedCurrency.name}\n
\n
\n
\n\n
\n\n
\n
\n USD\n
\n
\n
To
\n
\n ${amountInUsd}\n
\n
\n
\n
\n\n
\n
Payment To \n
\n
\n {companyName}\n
\n
\n
\n Wallet Address\n
\n
\n {recipientWallet}\n
\n
\n
\n
\n\n {localError && (\n
\n
\n
\n
\n
\n Payment Error\n
\n
{localError}
\n
\n
\n
\n )}\n\n
\n \n Back\n \n \n {isExecuting ? \"Processing...\" : \"Pay\"}\n \n
\n
\n );\n}\n",
+ "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { ArrowRight, AlertCircle } from \"lucide-react\";\nimport { usePayment } from \"../hooks/use-payment\";\nimport {\n getSymbolOverride,\n type ConversionCurrency,\n} from \"../utils/currencies\";\nimport type { BuyerInfo, PaymentError } from \"../types/index\";\nimport { useState } from \"react\";\nimport type { TransactionReceipt } from \"viem\";\nimport { usePaymentWidgetContext } from \"../context/payment-widget-context/use-payment-widget-context\";\n\ninterface PaymentConfirmationProps {\n selectedCurrency: ConversionCurrency;\n buyerInfo: BuyerInfo;\n onBack: () => void;\n handlePaymentSuccess: (\n requestId: string,\n transactionReceipts: TransactionReceipt[],\n ) => void;\n}\n\nexport function PaymentConfirmation({\n buyerInfo,\n selectedCurrency,\n onBack,\n handlePaymentSuccess,\n}: PaymentConfirmationProps) {\n const {\n amountInUsd,\n recipientWallet,\n connectedWalletAddress,\n paymentConfig: { rnApiClientId, feeInfo },\n receiptInfo: { companyInfo: { name: companyName } = {} },\n onError,\n walletAccount,\n } = usePaymentWidgetContext();\n const { isExecuting, executePayment } = usePayment(\n selectedCurrency.network,\n walletAccount,\n );\n const [localError, setLocalError] = useState(null);\n\n const handleExecutePayment = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!connectedWalletAddress) return;\n\n setLocalError(null);\n\n try {\n const { requestId, transactionReceipts } = await executePayment(\n rnApiClientId,\n {\n payerWallet: connectedWalletAddress,\n amountInUsd,\n recipientWallet,\n paymentCurrency: selectedCurrency.id,\n feeInfo,\n customerInfo: {\n email: buyerInfo.email,\n firstName: buyerInfo.firstName,\n lastName: buyerInfo.lastName,\n address: buyerInfo.address\n ? {\n street: buyerInfo.address.street,\n city: buyerInfo.address.city,\n state: buyerInfo.address.state,\n postalCode: buyerInfo.address.postalCode,\n country: buyerInfo.address.country,\n }\n : undefined,\n },\n },\n );\n\n handlePaymentSuccess(requestId, transactionReceipts);\n } catch (error) {\n const paymentError = error as PaymentError;\n\n let errorMessage = \"Payment failed. Please try again.\";\n\n if (paymentError.type === \"wallet\") {\n errorMessage =\n \"Wallet connection error. Please check your wallet and try again.\";\n } else if (paymentError.type === \"transaction\") {\n errorMessage =\n \"Transaction failed. Please check your balance and network connection.\";\n } else if (paymentError.type === \"api\") {\n errorMessage =\n paymentError.error?.message ||\n \"Payment service error. Please try again.\";\n } else if (paymentError.error?.message) {\n errorMessage = paymentError.error.message;\n }\n setLocalError(errorMessage);\n\n onError?.(paymentError);\n }\n };\n\n return (\n \n
Payment Confirmation \n\n
\n {/* Payment Currency (From) */}\n
\n
\n {getSymbolOverride(selectedCurrency.symbol)}\n
\n
\n
From
\n
\n {selectedCurrency.name}\n
\n
\n
\n\n
\n\n
\n
\n USD\n
\n
\n
To
\n
\n ${amountInUsd}\n
\n
\n
\n
\n\n
\n
Payment To \n
\n
\n {companyName}\n
\n
\n
\n Wallet Address\n
\n
\n {recipientWallet}\n
\n
\n
\n
\n\n {localError && (\n
\n
\n
\n
\n
\n Payment Error\n
\n
{localError}
\n
\n
\n
\n )}\n\n
\n \n Back\n \n \n {isExecuting ? \"Processing...\" : \"Pay\"}\n \n
\n
\n );\n}\n",
"type": "registry:component",
"target": "components/payment-widget/components/payment-confirmation.tsx"
},
@@ -76,7 +89,7 @@
},
{
"path": "registry/default/payment-widget/components/payment-success.tsx",
- "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { CheckCircle, ExternalLink, Download } from \"lucide-react\";\nimport {\n createReceipt,\n generateReceiptNumber,\n type CreateReceiptParams,\n} from \"../utils/receipt\";\nimport { useRef } from \"react\";\nimport { ReceiptPDFTemplate } from \"../components/receipt/receipt-template\";\nimport { usePaymentWidgetContext } from \"../context/payment-widget-context\";\nimport type { BuyerInfo } from \"../types/index\";\nimport type { ConversionCurrency } from \"../utils/currencies\";\n\ninterface PaymentSuccessProps {\n requestId: string;\n txHash: string;\n selectedCurrency: ConversionCurrency;\n buyerInfo: BuyerInfo;\n}\n\nexport function PaymentSuccess({\n requestId,\n txHash,\n selectedCurrency,\n buyerInfo,\n}: PaymentSuccessProps) {\n const {\n amountInUsd,\n recipientWallet,\n connectedWalletAddress,\n receiptInfo,\n uiConfig,\n } = usePaymentWidgetContext();\n const receiptRef = useRef(null);\n\n const receiptParams: CreateReceiptParams = {\n company: {\n ...receiptInfo.companyInfo,\n walletAddress: recipientWallet,\n },\n buyer: {\n ...buyerInfo,\n walletAddress: connectedWalletAddress || \"\",\n },\n payment: {\n amount: amountInUsd, // TODO connect to actual payout and exchange rate\n chain: selectedCurrency.network,\n currency: selectedCurrency.symbol,\n exchangeRate: \"1\",\n transactionHash: txHash,\n },\n items: receiptInfo.items,\n totals: receiptInfo.totals,\n metadata: {\n receiptNumber: receiptInfo?.receiptNumber\n ? receiptInfo.receiptNumber\n : generateReceiptNumber(),\n notes: `Payment processed through Request Network for ${amountInUsd} USD`,\n },\n };\n\n const handleDownloadReceipt = async () => {\n try {\n const element = receiptRef.current;\n if (!element) {\n console.error(\"Receipt element not found\");\n return;\n }\n\n const html2pdf = (await import(\"html2pdf.js\")).default;\n\n html2pdf()\n .set({\n margin: 1,\n filename: `receipt-${receiptParams.metadata?.receiptNumber || \"payment\"}.pdf`,\n image: { type: \"jpeg\", quality: 0.98 },\n html2canvas: { scale: 2 },\n jsPDF: { unit: \"in\", format: \"a4\", orientation: \"portrait\" },\n })\n .from(element)\n .save();\n } catch (error) {\n console.error(\"Failed to download receipt:\", error);\n alert(\"Failed to download receipt. Please try again.\");\n }\n };\n\n const requestScanUrl = `https://scan.request.network/request/${requestId}`;\n\n return (\n \n
\n
\n
\n
\n Payment Completed!\n \n
\n Your payment has been processed successfully.\n
\n
\n
\n\n
\n {uiConfig.showReceiptDownload && (\n <>\n
\n \n Download Receipt PDF\n \n
\n >\n )}\n {uiConfig.showRequestScanUrl && (\n
\n \n \n View on Request Scan\n \n \n )}\n
\n
\n );\n}\n",
+ "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { CheckCircle, ExternalLink, Download } from \"lucide-react\";\nimport {\n createReceipt,\n generateReceiptNumber,\n type CreateReceiptParams,\n} from \"../utils/receipt\";\nimport { useRef } from \"react\";\nimport { ReceiptPDFTemplate } from \"../components/receipt/receipt-template\";\nimport type { BuyerInfo } from \"../types/index\";\nimport type { ConversionCurrency } from \"../utils/currencies\";\nimport { usePaymentWidgetContext } from \"../context/payment-widget-context/use-payment-widget-context\";\n\ninterface PaymentSuccessProps {\n requestId: string;\n txHash: string;\n selectedCurrency: ConversionCurrency;\n buyerInfo: BuyerInfo;\n}\n\nexport function PaymentSuccess({\n requestId,\n txHash,\n selectedCurrency,\n buyerInfo,\n}: PaymentSuccessProps) {\n const {\n amountInUsd,\n recipientWallet,\n connectedWalletAddress,\n receiptInfo,\n uiConfig,\n } = usePaymentWidgetContext();\n const receiptRef = useRef(null);\n\n const receiptParams: CreateReceiptParams = {\n company: {\n ...receiptInfo.companyInfo,\n walletAddress: recipientWallet,\n },\n buyer: {\n ...buyerInfo,\n walletAddress: connectedWalletAddress || \"\",\n },\n payment: {\n amount: amountInUsd,\n chain: selectedCurrency.network,\n currency: selectedCurrency.symbol,\n exchangeRate: \"1\",\n transactionHash: txHash,\n },\n items: receiptInfo.items,\n totals: receiptInfo.totals,\n metadata: {\n receiptNumber: receiptInfo?.receiptNumber\n ? receiptInfo.receiptNumber\n : generateReceiptNumber(),\n notes: `Payment processed through Request Network for ${amountInUsd} USD`,\n },\n };\n\n const handleDownloadReceipt = async () => {\n try {\n const element = receiptRef.current;\n if (!element) {\n console.error(\"Receipt element not found\");\n return;\n }\n\n const html2canvas = (await import(\"html2canvas-pro\")).default;\n const jsPDF = (await import(\"jspdf\")).default;\n\n const canvas = await html2canvas(element, {\n scale: 2,\n useCORS: true,\n backgroundColor: \"#ffffff\",\n width: element.scrollWidth,\n height: element.scrollHeight,\n windowWidth: element.scrollWidth,\n windowHeight: element.scrollHeight,\n });\n\n const pdf = new jsPDF(\"p\", \"mm\", \"a4\");\n const imgData = canvas.toDataURL(\"image/png\");\n\n const pageWidth = 210;\n const pageHeight = 297;\n const margin = 10;\n let imgWidth = pageWidth - margin * 2;\n let imgHeight = (canvas.height * imgWidth) / canvas.width;\n const maxHeight = pageHeight - margin * 2;\n if (imgHeight > maxHeight) {\n const ratio = maxHeight / imgHeight;\n imgWidth = imgWidth * ratio;\n imgHeight = imgHeight * ratio;\n }\n pdf.addImage(imgData, \"PNG\", margin, margin, imgWidth, imgHeight);\n pdf.save(\n `receipt-${receiptParams.metadata?.receiptNumber || \"payment\"}.pdf`,\n );\n } catch (error) {\n console.error(\"Failed to download receipt:\", error);\n alert(\"Failed to download receipt. Please try again.\");\n }\n };\n\n const requestScanUrl = `https://scan.request.network/request/${requestId}`;\n\n return (\n \n
\n
\n
\n
\n Payment Completed!\n \n
\n Your payment has been processed successfully.\n
\n
\n
\n\n
\n {uiConfig.showReceiptDownload && (\n <>\n
\n \n Download Receipt PDF\n \n
\n >\n )}\n {uiConfig.showRequestScanUrl && (\n
\n \n \n View on Request Scan\n \n \n )}\n
\n
\n );\n}\n",
"type": "registry:component",
"target": "components/payment-widget/components/payment-success.tsx"
},
@@ -88,19 +101,19 @@
},
{
"path": "registry/default/payment-widget/constants.ts",
- "content": "export const RN_API_URL =\n process.env.NEXT_PUBLIC_REQUEST_API_URL || \"https://api.request.network\";\nexport const ICONS = {\n metamask:\n \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzUiIGhlaWdodD0iMzQiIHZpZXdCb3g9IjAgMCAzNSAzNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTMyLjcwNzcgMzIuNzUyMkwyNS4xNjg4IDMwLjUxNzRMMTkuNDgzMyAzMy45MDA4TDE1LjUxNjcgMzMuODk5MUw5LjgyNzkzIDMwLjUxNzRMMi4yOTIyNSAzMi43NTIyTDAgMjUuMDQ4OUwyLjI5MjI1IDE2LjQ5OTNMMCA5LjI3MDk0TDIuMjkyMjUgMC4zMTIyNTZMMTQuMDY3NCA3LjMxNTU0SDIwLjkzMjZMMzIuNzA3NyAwLjMxMjI1NkwzNSA5LjI3MDk0TDMyLjcwNzcgMTYuNDk5M0wzNSAyNS4wNDg5TDMyLjcwNzcgMzIuNzUyMloiIGZpbGw9IiNGRjVDMTYiLz4KPHBhdGggZD0iTTIuMjkzOTUgMC4zMTIyNTZMMTQuMDY5MSA3LjMyMDQ3TDEzLjYwMDggMTIuMTMwMUwyLjI5Mzk1IDAuMzEyMjU2WiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNOS44Mjk1OSAyNS4wNTIyTDE1LjAxMDYgMjguOTgxMUw5LjgyOTU5IDMwLjUxNzVWMjUuMDUyMloiIGZpbGw9IiNGRjVDMTYiLz4KPHBhdGggZD0iTTE0LjU5NjYgMTguNTU2NUwxMy42MDA5IDEyLjEzMzNMNy4yMjY5MiAxNi41MDA5TDcuMjIzNjMgMTYuNDk5M1YxNi41MDI1TDcuMjQzMzUgMjAuOTk4M0w5LjgyODA5IDE4LjU1NjVIOS44Mjk3NEgxNC41OTY2WiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNMzIuNzA3NyAwLjMxMjI1NkwyMC45MzI2IDcuMzIwNDdMMjEuMzk5MyAxMi4xMzAxTDMyLjcwNzcgMC4zMTIyNTZaIiBmaWxsPSIjRkY1QzE2Ii8+CjxwYXRoIGQ9Ik0yNS4xNzIyIDI1LjA1MjJMMTkuOTkxMiAyOC45ODExTDI1LjE3MjIgMzAuNTE3NVYyNS4wNTIyWiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNMjcuNzc2NiAxNi41MDI1SDI3Ljc3ODNIMjcuNzc2NlYxNi40OTkzTDI3Ljc3NSAxNi41MDA5TDIxLjQwMSAxMi4xMzMzTDIwLjQwNTMgMTguNTU2NUgyNS4xNzIyTDI3Ljc1ODYgMjAuOTk4M0wyNy43NzY2IDE2LjUwMjVaIiBmaWxsPSIjRkY1QzE2Ii8+CjxwYXRoIGQ9Ik05LjgyNzkzIDMwLjUxNzVMMi4yOTIyNSAzMi43NTIyTDAgMjUuMDUyMkg5LjgyNzkzVjMwLjUxNzVaIiBmaWxsPSIjRTM0ODA3Ii8+CjxwYXRoIGQ9Ik0xNC41OTQ3IDE4LjU1NDlMMTYuMDM0MSAyNy44NDA2TDE0LjAzOTMgMjIuNjc3N0w3LjIzOTc1IDIwLjk5ODRMOS44MjYxMyAxOC41NTQ5SDE0LjU5M0gxNC41OTQ3WiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMjUuMTcyMSAzMC41MTc1TDMyLjcwNzggMzIuNzUyMkwzNS4wMDAxIDI1LjA1MjJIMjUuMTcyMVYzMC41MTc1WiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMjAuNDA1MyAxOC41NTQ5TDE4Ljk2NTggMjcuODQwNkwyMC45NjA3IDIyLjY3NzdMMjcuNzYwMiAyMC45OTg0TDI1LjE3MjIgMTguNTU0OUgyMC40MDUzWiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMCAyNS4wNDg4TDIuMjkyMjUgMTYuNDk5M0g3LjIyMTgzTDcuMjM5OTEgMjAuOTk2N0wxNC4wMzk0IDIyLjY3NkwxNi4wMzQzIDI3LjgzODlMMTUuMDA4OSAyOC45NzZMOS44Mjc5MyAyNS4wNDcySDBWMjUuMDQ4OFoiIGZpbGw9IiNGRjhENUQiLz4KPHBhdGggZD0iTTM1LjAwMDEgMjUuMDQ4OEwzMi43MDc4IDE2LjQ5OTNIMjcuNzc4M0wyNy43NjAyIDIwLjk5NjdMMjAuOTYwNyAyMi42NzZMMTguOTY1OCAyNy44Mzg5TDE5Ljk5MTIgMjguOTc2TDI1LjE3MjIgMjUuMDQ3MkgzNS4wMDAxVjI1LjA0ODhaIiBmaWxsPSIjRkY4RDVEIi8+CjxwYXRoIGQ9Ik0yMC45MzI1IDcuMzE1NDNIMTcuNDk5OUgxNC4wNjczTDEzLjYwMDYgMTIuMTI1MUwxNi4wMzQyIDI3LjgzNEgxOC45NjU2TDIxLjQwMDggMTIuMTI1MUwyMC45MzI1IDcuMzE1NDNaIiBmaWxsPSIjRkY4RDVEIi8+CjxwYXRoIGQ9Ik0yLjI5MjI1IDAuMzEyMjU2TDAgOS4yNzA5NEwyLjI5MjI1IDE2LjQ5OTNINy4yMjE4M0wxMy41OTkxIDEyLjEzMDFMMi4yOTIyNSAwLjMxMjI1NloiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTEzLjE3IDIwLjQxOTlIMTAuOTM2OUw5LjcyMDk1IDIxLjYwNjJMMTQuMDQwOSAyMi42NzI3TDEzLjE3IDIwLjQxODJWMjAuNDE5OVoiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTMyLjcwNzcgMC4zMTIyNTZMMzQuOTk5OSA5LjI3MDk0TDMyLjcwNzcgMTYuNDk5M0gyNy43NzgxTDIxLjQwMDkgMTIuMTMwMUwzMi43MDc3IDAuMzEyMjU2WiIgZmlsbD0iIzY2MTgwMCIvPgo8cGF0aCBkPSJNMjEuODMzIDIwLjQxOTlIMjQuMDY5NEwyNS4yODUzIDIxLjYwNzlMMjAuOTYwNCAyMi42NzZMMjEuODMzIDIwLjQxODJWMjAuNDE5OVoiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTE5LjQ4MTcgMzAuODM2MkwxOS45OTExIDI4Ljk3OTRMMTguOTY1OCAyNy44NDIzSDE2LjAzMjdMMTUuMDA3MyAyOC45Nzk0TDE1LjUxNjcgMzAuODM2MiIgZmlsbD0iIzY2MTgwMCIvPgo8cGF0aCBkPSJNMTkuNDgxNiAzMC44MzU5VjMzLjkwMjFIMTUuNTE2NlYzMC44MzU5SDE5LjQ4MTZaIiBmaWxsPSIjQzBDNENEIi8+CjxwYXRoIGQ9Ik05LjgyOTU5IDMwLjUxNDJMMTUuNTIgMzMuOTAwOFYzMC44MzQ2TDE1LjAxMDYgMjguOTc3OEw5LjgyOTU5IDMwLjUxNDJaIiBmaWxsPSIjRTdFQkY2Ii8+CjxwYXRoIGQ9Ik0yNS4xNzIxIDMwLjUxNDJMMTkuNDgxNyAzMy45MDA4VjMwLjgzNDZMMTkuOTkxMSAyOC45Nzc4TDI1LjE3MjEgMzAuNTE0MloiIGZpbGw9IiNFN0VCRjYiLz4KPC9zdmc+\",\n walletConnect:\n \"data:image/webp;base64,UklGRoQFAABXRUJQVlA4IHgFAAAQHwCdASp4AHgAPkkkjUSioiEVDGQYKASEsQBrTdi9V/GD8mflPpT9Q+7PEdo66nv5H8k/qfWK8wD+Afwn/AfxP+d9gDzAfar7wHoY/4vqAf6DqAOfE9iL+2/679o/Zd9QD//+oB//5lWRX6GuwHaY3eTHXFDyf94MY8zXvW3sC+Ux7Ff3I9lUnxBszMzMi+F8vttSqgutQquCB9o/6xQmMK42Q/WbdImEUpOqAd6u9h29GS1GgtbyEG2sDVzO4ZMHRCs1Z+SDbXLhIp6P3MCv8qbQY5kqbkHw1whDyDmZDXnO2N0BG0kh4OJlQEVeNeizP7fD34527u7u7u7u7u7sAAD+6Isf/94lW/WpT/f/+3OgFGaTgAAGDhlRYjGqZQcAAT9yh1vvJ8zrnfD5w65SpgvO8JdZndGf06eAg4abEWdwfNrhPidbW62baoeoW6QKe6Po6a3SfDgUxTkBiyfa6aQUtIUGi1uBLXPRARZxxk1RzVeP7tX4YVlU46ZT4F21rfa+L9KZhfgsVYoGdfucm3vMShtAjG1KrxQXIerWSCwntsXHunyedtYaZoKtyPde5PXpW7xpVP32KCdQU5pFOncjUdeC5f0rzo6/PnTKPTrdy16JvvX7zEKrr+WqEZb5hXukMwo6N8Ih6/Emwxd8FgnLe8b5Bnn7k/uM+szg3FzzTeXQeufvevm98uB2YZN/M5Yjadke6+6hxX9Yg6K84Ph+iAklF9vUUs3R38v6EYfsZZ6EyjnjxA+y+Bgr94Mgjuv9vmgfQkpNGC9xgu3TSWRVS0wC37go5DrovSTTs3E8ZiKPerDFKcBNvadynIfa6hFM3wvSQ0SodZsHlh7j6vPa+mtK2EuUN+I0VTKuwuGpPTIo/jrtfdXtSHgjEfm94ypk3Wor04YhlhPB766ggMJBtI4m9/Y6zTBgcG7c28W7SB5v8Vub8I3wuLcqJLP3kAaoVHwAoydvZojNzfsmP6MwbhR1Lr5tsZVH31IUEbg76DrQ+i2ZOTl9qCKDwiQhV5k35/DZGlQDNQg5gXrPDhFUBpIBmyhJize4Z13x54n2sf/RDdrj/51FBrl0d8sNmUeP7FeNGkz5/T7W0NEWBZwxHn2MhV02YkbceGkR583AW98Gvxj5OMHAgfNi39ttJOupfJ7/NOz8W/Z0k5d/sxuyfqQhs7yPDgqxdIWvW0493pAePUIxY0eW2x7bZxfF6l2CSpAuTZQ4/7F7nU0Jc5q2qruF453uW5aGcDv7hb73x1LFBj/w6PxrMjACi/3FWRhZACmvX8Ec5wdWI0K3QsCTSmc7bpk0f17rrgqv/U0iLrqrUuLOc9InkhsxB/5jVsTG37gbkbFFqZSyXBksIuJ0aKHo4MFkTr8N9Tg7d3RRRDNQHtpm+4eG9hpNK+fkJEoQ6Ne3VYoe8sg114BI9eVPqXK3xestyFZNeS16I2gewgFTLNcar8155bz4WSLgTGccxGOSbNv0d1zuTd5o8wdP7oEb4tnT+KMdnjrgVHcvhhUnHFeaFJLXUYdUKzgN37EDPajnygrfldc0NOoNj5Bamnxlq9EEDwGeLI5wJbXCYjOU4Dz5pbDtJsM2MgPQEUfMwRvocWG+DdXe6ETfjIX0H6zr0n3fasZGGmMqFCNZcVMctEx5+mqAG+JnWvd54aoOizkzCMhmYb14IcfiocgQ3KsD3gIC4rQV9E2XaUImdbfMpGF61/wqGvda6aRDxtHd1ncqrv43wQX3Fx21uN1knQwmrwi62tw4Wowsd5IbrPWsl1jMbRdwDkvscxEqzllnBxtHcAVIz5yzQ3NTCMmOkWVm77vmiJmqtx2MBoXs3Y6uVDgNkhNfu7Mvc3jMAAAAAAAAAA==\",\n coinbase:\n \"data:image/webp;base64,UklGRuAFAABXRUJQVlA4INQFAACQIACdASp4AHgAPkkijkQioiGXCyQcKASEsQN8c2HiuAn9m/FXv/s0eA/mf7Vfkz2fu/fw79J/jxmb/G/+n8yf+d/kHuA8wD9Ff9h1AP1g/s3sA/UT/Vf4z3hP4j/kv4B7gP83/KP7p7gH8+/wvWAfoB7AH6SelL+3Xwb/rh+1vtCXRH0ve8f5zhjlYsENIFMv8jf0t0t3mAeyB+wBaFRSEFJ85QT4wMY//h5Wlu4TPcVFeRjNCzkZt45BD1kFBoRy1sqh8qFDgfeX/D+cIlLM5yIHMXM8Z04AaLr6xJpiCnmNrbtii3uMOSRTYDj+6dtl939Kx23Ixh5BAMaZ1HngzO3U+1xDyiUA4ss2AAD+7/246f8RO+IneImyhtikVuTJl5GdRXejyZ8eT2bkasC6C1DoHzZdbsY0DSZv+qv0P6G5Cv37u5VmTK2jd8/KvSxtw1SdX+UjD6wZIJ6Q8Gcn+eMa0OP894J3M/WTO1v9TC2+VmPxrIhgiAzDs0TSGbNMi0psELMxuCu+LqgNGr2bjt3G/F63cM86fE9c7LHp8ply/wO+uxQtcEr1A7z4gpBiif/KPZiwfrKkrtAAmE4jX7Si6sFt0JjSnQfFfuQc7oKqj2/x2Ten6iTJFnTIRPBcR5nzCqT8fjzfyyCeg2HDSldW87YMEGYBLbS1FuNCaMTcSze0IRjPjjAjteIYwemmxRR4qNe2bpYMjG8EvKyiexULpmlU4EKgLmHGBy3JqfnSboHVNqYMBO97kx2eQbzoEgvIEPT1wSMvi5q8BZ0OX4uv1brvwNQfE8RLiON0wdvoivhnCOhx1P+CSsxH9L7flNZfQ2y9/nwSW9C9H5d14rSK2t/vtIarPTQEmHe6naSPkZ5+GcbUQ/SwTA5EkBL+9Z+UJIylYB8dk4Ha+y6YLL9nCclJPz1+D9KWUfJhK380kkFyZ/I4NRJ9P6XdEpx8Z3f/zdeQUI2AZMU/Pf+Ly/99QQpQIlrhOL3DagEdnJVYSYEr3Z1BrUv4Wpm2H1gdpCS3sS4fYkFlbewzr+jjCeYRgXs+d/hmba33VlmyxrnJg9ujTJj1QxoG4utbk1Tf9Tg8/b3B/zgMCRRANPCC9wUx81zORgfDst3wZhOq3qS8XlwfH3uHfLREQ8gaUsY3P/j0Vo/6pihVGc9Coqxxn3zLYcdrqoav/MyEPcXxNschwk15gRP0Omh7i/3LaWr/aAwcCU5jxZ8e/O2/zQZ8ENjpOLgZAy52G6QhYFXVjdoK28DQnB9zM/1gICr7tsxUPxfyUy0aliEZs8Mkc7Pwjq2tJcVNnwTMTAkiH4jdfKHedXwcHMAoMEhTd6gTkOUCz7hCzDguvX0Wk5inBSHo3W6M1kmPcFBHhty4Uz31qBkhWsktj0gEmgF1fWmMAEVw/y7C2Sm8kKekWMpEzpDTqqwoXaY8YyFWnBK+akFoIAgTCttUBDvbFdn2Xx9P1/SjkXxJg2G+aK4vIDQD1tobM/VmoTFnNc+uXqSOyyo8Ip0uQWU2XY+cG2nyR9RmdutY2YqiMOPdm2uTQxDyQ8gn5nUz6pEz4nJ/eeXJBuXeilVON+E6wFfox4EACd6DvukaUif9FJ0L+DWCFwDNzBVr9kCQH7tq65j5WpOfl1n68fEsGnzM/xLJX4Z5TmkumyI7BnHdEMHuFWVqxkHucNuS6qjBnUY0LzFY/eMO7Yn/JM1tBo4nrauNryL+MdwTQXJBooDQLYKw4x6gPfyyPqE6meMdl3CR0L0zUSnkmnMBLoPDDZW+cxrkZ+yTpU1ekcTWQCBLC/nbu7K2C8FdCxqv02mQaWTJW0IWNGLh+39CcdGNIKNJ43wCyMyfuH49jg64SEKZpEE8JXljz1I/XR3PuGM1snokHQomCSXb4cm2fBI4jWTeoxhDus89Wk1ODk50ZWp6DihAMecO3Ul630ci/tqcLfGj2oOrhkRIhFeCA3ZN1dBudQAfeZE6eOk/Hb9PkAAA\",\n safe: \"data:image/webp;base64,UklGRmwEAABXRUJQVlA4IGAEAAAQHQCdASp4AHgAPkkijUQioqEXTPQAKASEoA1XnRfpv4k+DZc/rX4v/lH00e+XfT9pfcBlxn2j8o/7d86f4B/gP41+gHyA8wD9AP7R9oHCA/YD/Jf4D3dP8B/gP8B2AH6ucAB+uXpT/7T3V/2p/0H9m9krNM/7B1cq5zK976PADTDTNfHc+PLcMbk9SFTCv8ZK3c2kA4wxWaJEMekH7chiyjmezfBnGX7l6ZLEA/jDMtgB36h24aGBseH81LSvxBz2xwNujw/xoPLk6GGfe7gLfF2YZ3Zkq3NMEt21YwfxpCxuKElykJJrRDqMJGuBLfCAAP7wYCBRUOxLnuefomvomJgo+dAB3q+0mhgl9zX5m2b0zxrt/3BrCkvWTPpubP4mKKCrfGVI184WtIxifdw//FbCgy0ECDSgBGTtg6PQ1me430CfaGQwNqFjiuMWV381BhXRq7hzE2rtUWjMvT7W3oWJUfPOnY1KIThNxn2cBTQ5owd5fdSenGZckR6V43Ipk9FB7X6MGSRUo3wpoq5VUm+JQsgi/VkAAD3vyuWknHeTw/rP0cKYmZHkuVoA9E0xdcWDQdZO0Qt1wrosPu5BSsFF5osjLFb/v+n5jVASMrMZz/tW35s3GmY9cALgOWXNzgkWXb30BEciwVYgxAvPmnHrQRPGHXt8GBYM7Gi2dXm3RGyHtWpHgSO3jPN0CyGqrECxvuYOkX/hVNBo1KPWsQvgdEKbmw2x50cEOuJqy0E+VKMAfoYM7SeflkVUSiMpNdcA9zEeidL4Sc42m2blDop7c61kxw4ynW6aIgMdAzW/zWiDtILZSgyKk/Ee/ksyY6hmsFrTkxyBLG4KQgrlFXZR/3YLaC3FfH0pL5yUlo/FdRG4lxBm8pHASmePW/oASzAMaHfI8xBv04BjOavXJCxxlQ5/j7F6ShqFhTo+0u5HrJII7Ab25IiXUTSo5MvT42erBSNNo+gV9vqv0kNieeGlqh++PmE2ONC8Ozay7U/NCCTjVgQEMCJ1zEDdc6qkAT9yBTFGShGh/8LUqqhem5Ln2ZT1+SX79SkUP+xubo3cJsCVNxtQtZ6oG1xOCZaNiOdAdx0hcWKmGPTRfJpR+7DqQ9vpc6Sx4G4NCsyB9VpvLoiwZwKw4Aq8AcLwJ4h8geguxsBGaT0BJKGTpOb/JFaHLfDPiCVIrgezK7WFRxtSKKWun4bVDF5YUupz620xC6639ore7sZXvYN7ZneX2vMmR1a+Usq9Z5gfqVNvXOHD3a96PqvHj47SeZ9bQFy1n570OcCrj8fkfNT6jLcdF1iTbK7i9/Yw+5oTz7M4Koda9E8jKW4obgrb2EwkgMYIm0D+/6+O8s+jRgHhxLW/mDExy8avr0XAUtZjnBiWM/nEc5zMBckPwsSsg9tm8RqxV4bV3U6kNDC9FvAjFjDv9BdpHGCJiGaj9vFM7x7poRHF9vAqKGkuIkSTP/tx+Wj/80cBl4DUhdykoAAAAAAA\",\n requestNetwork:\n \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDQiIHZpZXdCb3g9IjAgMCA0MiA0NCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzFfMjQ2MSkiPgo8cGF0aCBkPSJNMjguMjIzMyAyNC43NjIxTDM5LjU2NTMgMTMuOTMxM0M0MC40MTc1IDEzLjEyNDMgNDAuODk1MiAxMi4wMTI2IDQwLjg5NTIgMTAuODY0OUw0MC45MTM5IDIuMTE0MjNDNDAuOTEzOSAxLjY2NTkzIDQwLjczNiAxLjIzNTU3IDQwLjQwODIgMC45MTI3OTZMMTguNjUxNiAyMS43MDQ3QzE2Ljg4MTQgMjMuMzgxMyAxNi44NzIxIDI2LjExNTkgMTguNjIzNSAyNy44MDE1TDE4LjY1MTYgMjcuODI4NEwzMy45OTI2IDQyLjQ2OTdDMzUuNzYyOCA0NC4xNTUzIDM4LjYyODcgNDQuMTQ2MyA0MC4zODAxIDQyLjQ1MThDNDIuMTQwOCA0MC43NTcyIDQyLjEzMTUgMzguMDEzNiA0MC4zNjEzIDM2LjMzN0wyOC4yMjMzIDI0Ljc2MjFaIiBmaWxsPSIjMzI4OTY1Ii8+CjxwYXRoIGQ9Ik00MC42ODA4IC0yLjQ0MzEySDUuMDEzNUMyLjEzMDggLTIuNDQzMTIgMC4wMDA1MzY2OTQgLTAuMjk3MTE3IDAuMDAwNTM2Njk0IDIuNjI4MzhWMzkuMjc0MUMtMC4wMzg1NTA4IDQxLjg0MzUgMi4wNjI0IDQzLjk2MDcgNC42NzE0OSA0My45OTkySDQuNzg4NzVDNy4zODgwNiA0NC4wNDczIDkuNTM3ODcgNDEuOTk3NSA5LjU4NjczIDM5LjQzNzdWNi41OTMySDMzLjMxMjhMNDIgLTEuOTIzNDZDNDEuNjQ4MiAtMi4yNjk5IDQxLjE3OTIgLTIuNDYyMzcgNDAuNjgwOCAtMi40NDMxMloiIGZpbGw9IiM1OEUxQTUiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xXzI0NjEiPgo8cmVjdCB3aWR0aD0iNDIiIGhlaWdodD0iNDQiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==\",\n defaultWallet:\n \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTIyLjg4IDEwMS4zMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTIyLjg4IDEwMS4zMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHN0eWxlIHR5cGU9InRleHQvY3NzIj4uc3Qwe2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO308L3N0eWxlPjxnPjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik05MC42MiwzMy4zMmgxOC40di0yLjc5Yy0yLjg4LTEwLjczLTEwLjItMTAuNjYtMTkuMjUtMTAuNTdjLTEuNDksMC4wMi0yLjg0LDAuMDMtMi45MiwwLjAzSDE4LjA3IGMtMS41OCwwLTIuODYtMS4yOC0yLjg2LTIuODZjMC0xLjU4LDEuMjgtMi44NiwyLjg2LTIuODZoNjguNzhjMi4wMywwLDIuNDYsMCwyLjg3LTAuMDFjNy43NC0wLjA4LDE0LjUtMC4xNSwxOS4zLDQuMzh2LTEuMzEgYzAtMy4yLTEuMzEtNi4xLTMuNDItOC4yMWMtMi4xMS0yLjExLTUuMDItMy40Mi04LjIxLTMuNDJIMTcuMzRjLTMuMiwwLTYuMSwxLjMxLTguMjEsMy40MmMtMi4xMSwyLjExLTMuNDIsNS4wMi0zLjQyLDguMjF2NjYuNjQgYzAsMy4yLDEuMzEsNi4xLDMuNDIsOC4yMWMyLjExLDIuMTEsNS4wMiwzLjQyLDguMjEsMy40Mmg4MC4wNGMzLjIsMCw2LjEtMS4zMSw4LjIxLTMuNDJjMi4xMS0yLjExLDMuNDItNS4wMiwzLjQyLTguMjF2LTkuNDYgaC0xOC40Yy01LjU1LDAtMTAuNi0yLjI3LTE0LjI1LTUuOTJjLTMuNjUtMy42NS01LjkyLTguNy01LjkyLTE0LjI1di0wLjg3YzAtNS41NSwyLjI3LTEwLjYsNS45Mi0xNC4yNSBDODAuMDIsMzUuNTksODUuMDYsMzMuMzIsOTAuNjIsMzMuMzJMOTAuNjIsMzMuMzJ6IE0xMTQuNzMsMzMuNDNjMi4wNywwLjMxLDMuOTIsMS4yOSw1LjMzLDIuNzFjMS43NCwxLjc0LDIuODEsNC4xNCwyLjgxLDYuNzggdjIxLjZjMCwyLjc2LTEuMTIsNS4yNi0yLjkzLDcuMDdjLTEuMzksMS4zOS0zLjIsMi4zOC01LjIxLDIuNzZ2OS42M2MwLDQuNzctMS45NSw5LjExLTUuMDksMTIuMjUgYy0zLjE0LDMuMTQtNy40OCw1LjA5LTEyLjI1LDUuMDlIMTcuMzRjLTQuNzcsMC05LjExLTEuOTUtMTIuMjUtNS4wOUMxLjk1LDkzLjEsMCw4OC43NiwwLDgzLjk5VjE3LjM0IGMwLTQuNzcsMS45NS05LjExLDUuMDktMTIuMjVDOC4yMywxLjk1LDEyLjU3LDAsMTcuMzQsMGg4MC4wNGM0Ljc3LDAsOS4xMSwxLjk1LDEyLjI1LDUuMDljMy4xNCwzLjE0LDUuMDksNy40OCw1LjA5LDEyLjI1VjMzLjQzIEwxMTQuNzMsMzMuNDN6IE04OC4xNCw0Ni4xMWM0LjA1LDAsNy4zMywzLjI4LDcuMzMsNy4zM2MwLDQuMDUtMy4yOCw3LjMzLTcuMzMsNy4zM2MtNC4wNSwwLTcuMzMtMy4yOC03LjMzLTcuMzMgQzgwLjgxLDQ5LjM5LDg0LjA5LDQ2LjExLDg4LjE0LDQ2LjExTDg4LjE0LDQ2LjExeiIvPjwvZz48L3N2Zz4=\",\n};\n",
+ "content": "export const RN_API_URL =\n (typeof process !== \"undefined\" && process.env.NEXT_PUBLIC_REQUEST_API_URL) ||\n \"https://api.request.network\";\nexport const ICONS = {\n metamask:\n \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzUiIGhlaWdodD0iMzQiIHZpZXdCb3g9IjAgMCAzNSAzNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTMyLjcwNzcgMzIuNzUyMkwyNS4xNjg4IDMwLjUxNzRMMTkuNDgzMyAzMy45MDA4TDE1LjUxNjcgMzMuODk5MUw5LjgyNzkzIDMwLjUxNzRMMi4yOTIyNSAzMi43NTIyTDAgMjUuMDQ4OUwyLjI5MjI1IDE2LjQ5OTNMMCA5LjI3MDk0TDIuMjkyMjUgMC4zMTIyNTZMMTQuMDY3NCA3LjMxNTU0SDIwLjkzMjZMMzIuNzA3NyAwLjMxMjI1NkwzNSA5LjI3MDk0TDMyLjcwNzcgMTYuNDk5M0wzNSAyNS4wNDg5TDMyLjcwNzcgMzIuNzUyMloiIGZpbGw9IiNGRjVDMTYiLz4KPHBhdGggZD0iTTIuMjkzOTUgMC4zMTIyNTZMMTQuMDY5MSA3LjMyMDQ3TDEzLjYwMDggMTIuMTMwMUwyLjI5Mzk1IDAuMzEyMjU2WiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNOS44Mjk1OSAyNS4wNTIyTDE1LjAxMDYgMjguOTgxMUw5LjgyOTU5IDMwLjUxNzVWMjUuMDUyMloiIGZpbGw9IiNGRjVDMTYiLz4KPHBhdGggZD0iTTE0LjU5NjYgMTguNTU2NUwxMy42MDA5IDEyLjEzMzNMNy4yMjY5MiAxNi41MDA5TDcuMjIzNjMgMTYuNDk5M1YxNi41MDI1TDcuMjQzMzUgMjAuOTk4M0w5LjgyODA5IDE4LjU1NjVIOS44Mjk3NEgxNC41OTY2WiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNMzIuNzA3NyAwLjMxMjI1NkwyMC45MzI2IDcuMzIwNDdMMjEuMzk5MyAxMi4xMzAxTDMyLjcwNzcgMC4zMTIyNTZaIiBmaWxsPSIjRkY1QzE2Ii8+CjxwYXRoIGQ9Ik0yNS4xNzIyIDI1LjA1MjJMMTkuOTkxMiAyOC45ODExTDI1LjE3MjIgMzAuNTE3NVYyNS4wNTIyWiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNMjcuNzc2NiAxNi41MDI1SDI3Ljc3ODNIMjcuNzc2NlYxNi40OTkzTDI3Ljc3NSAxNi41MDA5TDIxLjQwMSAxMi4xMzMzTDIwLjQwNTMgMTguNTU2NUgyNS4xNzIyTDI3Ljc1ODYgMjAuOTk4M0wyNy43NzY2IDE2LjUwMjVaIiBmaWxsPSIjRkY1QzE2Ii8+CjxwYXRoIGQ9Ik05LjgyNzkzIDMwLjUxNzVMMi4yOTIyNSAzMi43NTIyTDAgMjUuMDUyMkg5LjgyNzkzVjMwLjUxNzVaIiBmaWxsPSIjRTM0ODA3Ii8+CjxwYXRoIGQ9Ik0xNC41OTQ3IDE4LjU1NDlMMTYuMDM0MSAyNy44NDA2TDE0LjAzOTMgMjIuNjc3N0w3LjIzOTc1IDIwLjk5ODRMOS44MjYxMyAxOC41NTQ5SDE0LjU5M0gxNC41OTQ3WiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMjUuMTcyMSAzMC41MTc1TDMyLjcwNzggMzIuNzUyMkwzNS4wMDAxIDI1LjA1MjJIMjUuMTcyMVYzMC41MTc1WiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMjAuNDA1MyAxOC41NTQ5TDE4Ljk2NTggMjcuODQwNkwyMC45NjA3IDIyLjY3NzdMMjcuNzYwMiAyMC45OTg0TDI1LjE3MjIgMTguNTU0OUgyMC40MDUzWiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMCAyNS4wNDg4TDIuMjkyMjUgMTYuNDk5M0g3LjIyMTgzTDcuMjM5OTEgMjAuOTk2N0wxNC4wMzk0IDIyLjY3NkwxNi4wMzQzIDI3LjgzODlMMTUuMDA4OSAyOC45NzZMOS44Mjc5MyAyNS4wNDcySDBWMjUuMDQ4OFoiIGZpbGw9IiNGRjhENUQiLz4KPHBhdGggZD0iTTM1LjAwMDEgMjUuMDQ4OEwzMi43MDc4IDE2LjQ5OTNIMjcuNzc4M0wyNy43NjAyIDIwLjk5NjdMMjAuOTYwNyAyMi42NzZMMTguOTY1OCAyNy44Mzg5TDE5Ljk5MTIgMjguOTc2TDI1LjE3MjIgMjUuMDQ3MkgzNS4wMDAxVjI1LjA0ODhaIiBmaWxsPSIjRkY4RDVEIi8+CjxwYXRoIGQ9Ik0yMC45MzI1IDcuMzE1NDNIMTcuNDk5OUgxNC4wNjczTDEzLjYwMDYgMTIuMTI1MUwxNi4wMzQyIDI3LjgzNEgxOC45NjU2TDIxLjQwMDggMTIuMTI1MUwyMC45MzI1IDcuMzE1NDNaIiBmaWxsPSIjRkY4RDVEIi8+CjxwYXRoIGQ9Ik0yLjI5MjI1IDAuMzEyMjU2TDAgOS4yNzA5NEwyLjI5MjI1IDE2LjQ5OTNINy4yMjE4M0wxMy41OTkxIDEyLjEzMDFMMi4yOTIyNSAwLjMxMjI1NloiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTEzLjE3IDIwLjQxOTlIMTAuOTM2OUw5LjcyMDk1IDIxLjYwNjJMMTQuMDQwOSAyMi42NzI3TDEzLjE3IDIwLjQxODJWMjAuNDE5OVoiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTMyLjcwNzcgMC4zMTIyNTZMMzQuOTk5OSA5LjI3MDk0TDMyLjcwNzcgMTYuNDk5M0gyNy43NzgxTDIxLjQwMDkgMTIuMTMwMUwzMi43MDc3IDAuMzEyMjU2WiIgZmlsbD0iIzY2MTgwMCIvPgo8cGF0aCBkPSJNMjEuODMzIDIwLjQxOTlIMjQuMDY5NEwyNS4yODUzIDIxLjYwNzlMMjAuOTYwNCAyMi42NzZMMjEuODMzIDIwLjQxODJWMjAuNDE5OVoiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTE5LjQ4MTcgMzAuODM2MkwxOS45OTExIDI4Ljk3OTRMMTguOTY1OCAyNy44NDIzSDE2LjAzMjdMMTUuMDA3MyAyOC45Nzk0TDE1LjUxNjcgMzAuODM2MiIgZmlsbD0iIzY2MTgwMCIvPgo8cGF0aCBkPSJNMTkuNDgxNiAzMC44MzU5VjMzLjkwMjFIMTUuNTE2NlYzMC44MzU5SDE5LjQ4MTZaIiBmaWxsPSIjQzBDNENEIi8+CjxwYXRoIGQ9Ik05LjgyOTU5IDMwLjUxNDJMMTUuNTIgMzMuOTAwOFYzMC44MzQ2TDE1LjAxMDYgMjguOTc3OEw5LjgyOTU5IDMwLjUxNDJaIiBmaWxsPSIjRTdFQkY2Ii8+CjxwYXRoIGQ9Ik0yNS4xNzIxIDMwLjUxNDJMMTkuNDgxNyAzMy45MDA4VjMwLjgzNDZMMTkuOTkxMSAyOC45Nzc4TDI1LjE3MjEgMzAuNTE0MloiIGZpbGw9IiNFN0VCRjYiLz4KPC9zdmc+\",\n walletConnect:\n \"data:image/webp;base64,UklGRoQFAABXRUJQVlA4IHgFAAAQHwCdASp4AHgAPkkkjUSioiEVDGQYKASEsQBrTdi9V/GD8mflPpT9Q+7PEdo66nv5H8k/qfWK8wD+Afwn/AfxP+d9gDzAfar7wHoY/4vqAf6DqAOfE9iL+2/679o/Zd9QD//+oB//5lWRX6GuwHaY3eTHXFDyf94MY8zXvW3sC+Ux7Ff3I9lUnxBszMzMi+F8vttSqgutQquCB9o/6xQmMK42Q/WbdImEUpOqAd6u9h29GS1GgtbyEG2sDVzO4ZMHRCs1Z+SDbXLhIp6P3MCv8qbQY5kqbkHw1whDyDmZDXnO2N0BG0kh4OJlQEVeNeizP7fD34527u7u7u7u7u7sAAD+6Isf/94lW/WpT/f/+3OgFGaTgAAGDhlRYjGqZQcAAT9yh1vvJ8zrnfD5w65SpgvO8JdZndGf06eAg4abEWdwfNrhPidbW62baoeoW6QKe6Po6a3SfDgUxTkBiyfa6aQUtIUGi1uBLXPRARZxxk1RzVeP7tX4YVlU46ZT4F21rfa+L9KZhfgsVYoGdfucm3vMShtAjG1KrxQXIerWSCwntsXHunyedtYaZoKtyPde5PXpW7xpVP32KCdQU5pFOncjUdeC5f0rzo6/PnTKPTrdy16JvvX7zEKrr+WqEZb5hXukMwo6N8Ih6/Emwxd8FgnLe8b5Bnn7k/uM+szg3FzzTeXQeufvevm98uB2YZN/M5Yjadke6+6hxX9Yg6K84Ph+iAklF9vUUs3R38v6EYfsZZ6EyjnjxA+y+Bgr94Mgjuv9vmgfQkpNGC9xgu3TSWRVS0wC37go5DrovSTTs3E8ZiKPerDFKcBNvadynIfa6hFM3wvSQ0SodZsHlh7j6vPa+mtK2EuUN+I0VTKuwuGpPTIo/jrtfdXtSHgjEfm94ypk3Wor04YhlhPB766ggMJBtI4m9/Y6zTBgcG7c28W7SB5v8Vub8I3wuLcqJLP3kAaoVHwAoydvZojNzfsmP6MwbhR1Lr5tsZVH31IUEbg76DrQ+i2ZOTl9qCKDwiQhV5k35/DZGlQDNQg5gXrPDhFUBpIBmyhJize4Z13x54n2sf/RDdrj/51FBrl0d8sNmUeP7FeNGkz5/T7W0NEWBZwxHn2MhV02YkbceGkR583AW98Gvxj5OMHAgfNi39ttJOupfJ7/NOz8W/Z0k5d/sxuyfqQhs7yPDgqxdIWvW0493pAePUIxY0eW2x7bZxfF6l2CSpAuTZQ4/7F7nU0Jc5q2qruF453uW5aGcDv7hb73x1LFBj/w6PxrMjACi/3FWRhZACmvX8Ec5wdWI0K3QsCTSmc7bpk0f17rrgqv/U0iLrqrUuLOc9InkhsxB/5jVsTG37gbkbFFqZSyXBksIuJ0aKHo4MFkTr8N9Tg7d3RRRDNQHtpm+4eG9hpNK+fkJEoQ6Ne3VYoe8sg114BI9eVPqXK3xestyFZNeS16I2gewgFTLNcar8155bz4WSLgTGccxGOSbNv0d1zuTd5o8wdP7oEb4tnT+KMdnjrgVHcvhhUnHFeaFJLXUYdUKzgN37EDPajnygrfldc0NOoNj5Bamnxlq9EEDwGeLI5wJbXCYjOU4Dz5pbDtJsM2MgPQEUfMwRvocWG+DdXe6ETfjIX0H6zr0n3fasZGGmMqFCNZcVMctEx5+mqAG+JnWvd54aoOizkzCMhmYb14IcfiocgQ3KsD3gIC4rQV9E2XaUImdbfMpGF61/wqGvda6aRDxtHd1ncqrv43wQX3Fx21uN1knQwmrwi62tw4Wowsd5IbrPWsl1jMbRdwDkvscxEqzllnBxtHcAVIz5yzQ3NTCMmOkWVm77vmiJmqtx2MBoXs3Y6uVDgNkhNfu7Mvc3jMAAAAAAAAAA==\",\n coinbase:\n \"data:image/webp;base64,UklGRuAFAABXRUJQVlA4INQFAACQIACdASp4AHgAPkkijkQioiGXCyQcKASEsQN8c2HiuAn9m/FXv/s0eA/mf7Vfkz2fu/fw79J/jxmb/G/+n8yf+d/kHuA8wD9Ff9h1AP1g/s3sA/UT/Vf4z3hP4j/kv4B7gP83/KP7p7gH8+/wvWAfoB7AH6SelL+3Xwb/rh+1vtCXRH0ve8f5zhjlYsENIFMv8jf0t0t3mAeyB+wBaFRSEFJ85QT4wMY//h5Wlu4TPcVFeRjNCzkZt45BD1kFBoRy1sqh8qFDgfeX/D+cIlLM5yIHMXM8Z04AaLr6xJpiCnmNrbtii3uMOSRTYDj+6dtl939Kx23Ixh5BAMaZ1HngzO3U+1xDyiUA4ss2AAD+7/246f8RO+IneImyhtikVuTJl5GdRXejyZ8eT2bkasC6C1DoHzZdbsY0DSZv+qv0P6G5Cv37u5VmTK2jd8/KvSxtw1SdX+UjD6wZIJ6Q8Gcn+eMa0OP894J3M/WTO1v9TC2+VmPxrIhgiAzDs0TSGbNMi0psELMxuCu+LqgNGr2bjt3G/F63cM86fE9c7LHp8ply/wO+uxQtcEr1A7z4gpBiif/KPZiwfrKkrtAAmE4jX7Si6sFt0JjSnQfFfuQc7oKqj2/x2Ten6iTJFnTIRPBcR5nzCqT8fjzfyyCeg2HDSldW87YMEGYBLbS1FuNCaMTcSze0IRjPjjAjteIYwemmxRR4qNe2bpYMjG8EvKyiexULpmlU4EKgLmHGBy3JqfnSboHVNqYMBO97kx2eQbzoEgvIEPT1wSMvi5q8BZ0OX4uv1brvwNQfE8RLiON0wdvoivhnCOhx1P+CSsxH9L7flNZfQ2y9/nwSW9C9H5d14rSK2t/vtIarPTQEmHe6naSPkZ5+GcbUQ/SwTA5EkBL+9Z+UJIylYB8dk4Ha+y6YLL9nCclJPz1+D9KWUfJhK380kkFyZ/I4NRJ9P6XdEpx8Z3f/zdeQUI2AZMU/Pf+Ly/99QQpQIlrhOL3DagEdnJVYSYEr3Z1BrUv4Wpm2H1gdpCS3sS4fYkFlbewzr+jjCeYRgXs+d/hmba33VlmyxrnJg9ujTJj1QxoG4utbk1Tf9Tg8/b3B/zgMCRRANPCC9wUx81zORgfDst3wZhOq3qS8XlwfH3uHfLREQ8gaUsY3P/j0Vo/6pihVGc9Coqxxn3zLYcdrqoav/MyEPcXxNschwk15gRP0Omh7i/3LaWr/aAwcCU5jxZ8e/O2/zQZ8ENjpOLgZAy52G6QhYFXVjdoK28DQnB9zM/1gICr7tsxUPxfyUy0aliEZs8Mkc7Pwjq2tJcVNnwTMTAkiH4jdfKHedXwcHMAoMEhTd6gTkOUCz7hCzDguvX0Wk5inBSHo3W6M1kmPcFBHhty4Uz31qBkhWsktj0gEmgF1fWmMAEVw/y7C2Sm8kKekWMpEzpDTqqwoXaY8YyFWnBK+akFoIAgTCttUBDvbFdn2Xx9P1/SjkXxJg2G+aK4vIDQD1tobM/VmoTFnNc+uXqSOyyo8Ip0uQWU2XY+cG2nyR9RmdutY2YqiMOPdm2uTQxDyQ8gn5nUz6pEz4nJ/eeXJBuXeilVON+E6wFfox4EACd6DvukaUif9FJ0L+DWCFwDNzBVr9kCQH7tq65j5WpOfl1n68fEsGnzM/xLJX4Z5TmkumyI7BnHdEMHuFWVqxkHucNuS6qjBnUY0LzFY/eMO7Yn/JM1tBo4nrauNryL+MdwTQXJBooDQLYKw4x6gPfyyPqE6meMdl3CR0L0zUSnkmnMBLoPDDZW+cxrkZ+yTpU1ekcTWQCBLC/nbu7K2C8FdCxqv02mQaWTJW0IWNGLh+39CcdGNIKNJ43wCyMyfuH49jg64SEKZpEE8JXljz1I/XR3PuGM1snokHQomCSXb4cm2fBI4jWTeoxhDus89Wk1ODk50ZWp6DihAMecO3Ul630ci/tqcLfGj2oOrhkRIhFeCA3ZN1dBudQAfeZE6eOk/Hb9PkAAA\",\n safe: \"data:image/webp;base64,UklGRmwEAABXRUJQVlA4IGAEAAAQHQCdASp4AHgAPkkijUQioqEXTPQAKASEoA1XnRfpv4k+DZc/rX4v/lH00e+XfT9pfcBlxn2j8o/7d86f4B/gP41+gHyA8wD9AP7R9oHCA/YD/Jf4D3dP8B/gP8B2AH6ucAB+uXpT/7T3V/2p/0H9m9krNM/7B1cq5zK976PADTDTNfHc+PLcMbk9SFTCv8ZK3c2kA4wxWaJEMekH7chiyjmezfBnGX7l6ZLEA/jDMtgB36h24aGBseH81LSvxBz2xwNujw/xoPLk6GGfe7gLfF2YZ3Zkq3NMEt21YwfxpCxuKElykJJrRDqMJGuBLfCAAP7wYCBRUOxLnuefomvomJgo+dAB3q+0mhgl9zX5m2b0zxrt/3BrCkvWTPpubP4mKKCrfGVI184WtIxifdw//FbCgy0ECDSgBGTtg6PQ1me430CfaGQwNqFjiuMWV381BhXRq7hzE2rtUWjMvT7W3oWJUfPOnY1KIThNxn2cBTQ5owd5fdSenGZckR6V43Ipk9FB7X6MGSRUo3wpoq5VUm+JQsgi/VkAAD3vyuWknHeTw/rP0cKYmZHkuVoA9E0xdcWDQdZO0Qt1wrosPu5BSsFF5osjLFb/v+n5jVASMrMZz/tW35s3GmY9cALgOWXNzgkWXb30BEciwVYgxAvPmnHrQRPGHXt8GBYM7Gi2dXm3RGyHtWpHgSO3jPN0CyGqrECxvuYOkX/hVNBo1KPWsQvgdEKbmw2x50cEOuJqy0E+VKMAfoYM7SeflkVUSiMpNdcA9zEeidL4Sc42m2blDop7c61kxw4ynW6aIgMdAzW/zWiDtILZSgyKk/Ee/ksyY6hmsFrTkxyBLG4KQgrlFXZR/3YLaC3FfH0pL5yUlo/FdRG4lxBm8pHASmePW/oASzAMaHfI8xBv04BjOavXJCxxlQ5/j7F6ShqFhTo+0u5HrJII7Ab25IiXUTSo5MvT42erBSNNo+gV9vqv0kNieeGlqh++PmE2ONC8Ozay7U/NCCTjVgQEMCJ1zEDdc6qkAT9yBTFGShGh/8LUqqhem5Ln2ZT1+SX79SkUP+xubo3cJsCVNxtQtZ6oG1xOCZaNiOdAdx0hcWKmGPTRfJpR+7DqQ9vpc6Sx4G4NCsyB9VpvLoiwZwKw4Aq8AcLwJ4h8geguxsBGaT0BJKGTpOb/JFaHLfDPiCVIrgezK7WFRxtSKKWun4bVDF5YUupz620xC6639ore7sZXvYN7ZneX2vMmR1a+Usq9Z5gfqVNvXOHD3a96PqvHj47SeZ9bQFy1n570OcCrj8fkfNT6jLcdF1iTbK7i9/Yw+5oTz7M4Koda9E8jKW4obgrb2EwkgMYIm0D+/6+O8s+jRgHhxLW/mDExy8avr0XAUtZjnBiWM/nEc5zMBckPwsSsg9tm8RqxV4bV3U6kNDC9FvAjFjDv9BdpHGCJiGaj9vFM7x7poRHF9vAqKGkuIkSTP/tx+Wj/80cBl4DUhdykoAAAAAAA\",\n requestNetwork:\n \"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDQiIHZpZXdCb3g9IjAgMCA0MiA0NCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzFfMjQ2MSkiPgo8cGF0aCBkPSJNMjguMjIzMyAyNC43NjIxTDM5LjU2NTMgMTMuOTMxM0M0MC40MTc1IDEzLjEyNDMgNDAuODk1MiAxMi4wMTI2IDQwLjg5NTIgMTAuODY0OUw0MC45MTM5IDIuMTE0MjNDNDAuOTEzOSAxLjY2NTkzIDQwLjczNiAxLjIzNTU3IDQwLjQwODIgMC45MTI3OTZMMTguNjUxNiAyMS43MDQ3QzE2Ljg4MTQgMjMuMzgxMyAxNi44NzIxIDI2LjExNTkgMTguNjIzNSAyNy44MDE1TDE4LjY1MTYgMjcuODI4NEwzMy45OTI2IDQyLjQ2OTdDMzUuNzYyOCA0NC4xNTUzIDM4LjYyODcgNDQuMTQ2MyA0MC4zODAxIDQyLjQ1MThDNDIuMTQwOCA0MC43NTcyIDQyLjEzMTUgMzguMDEzNiA0MC4zNjEzIDM2LjMzN0wyOC4yMjMzIDI0Ljc2MjFaIiBmaWxsPSIjMzI4OTY1Ii8+CjxwYXRoIGQ9Ik00MC42ODA4IC0yLjQ0MzEySDUuMDEzNUMyLjEzMDggLTIuNDQzMTIgMC4wMDA1MzY2OTQgLTAuMjk3MTE3IDAuMDAwNTM2Njk0IDIuNjI4MzhWMzkuMjc0MUMtMC4wMzg1NTA4IDQxLjg0MzUgMi4wNjI0IDQzLjk2MDcgNC42NzE0OSA0My45OTkySDQuNzg4NzVDNy4zODgwNiA0NC4wNDczIDkuNTM3ODcgNDEuOTk3NSA5LjU4NjczIDM5LjQzNzdWNi41OTMySDMzLjMxMjhMNDIgLTEuOTIzNDZDNDEuNjQ4MiAtMi4yNjk5IDQxLjE3OTIgLTIuNDYyMzcgNDAuNjgwOCAtMi40NDMxMloiIGZpbGw9IiM1OEUxQTUiLz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF8xXzI0NjEiPgo8cmVjdCB3aWR0aD0iNDIiIGhlaWdodD0iNDQiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg==\",\n defaultWallet:\n \"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTIyLjg4IDEwMS4zMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTIyLjg4IDEwMS4zMyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PHN0eWxlIHR5cGU9InRleHQvY3NzIj4uc3Qwe2ZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkO308L3N0eWxlPjxnPjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik05MC42MiwzMy4zMmgxOC40di0yLjc5Yy0yLjg4LTEwLjczLTEwLjItMTAuNjYtMTkuMjUtMTAuNTdjLTEuNDksMC4wMi0yLjg0LDAuMDMtMi45MiwwLjAzSDE4LjA3IGMtMS41OCwwLTIuODYtMS4yOC0yLjg2LTIuODZjMC0xLjU4LDEuMjgtMi44NiwyLjg2LTIuODZoNjguNzhjMi4wMywwLDIuNDYsMCwyLjg3LTAuMDFjNy43NC0wLjA4LDE0LjUtMC4xNSwxOS4zLDQuMzh2LTEuMzEgYzAtMy4yLTEuMzEtNi4xLTMuNDItOC4yMWMtMi4xMS0yLjExLTUuMDItMy40Mi04LjIxLTMuNDJIMTcuMzRjLTMuMiwwLTYuMSwxLjMxLTguMjEsMy40MmMtMi4xMSwyLjExLTMuNDIsNS4wMi0zLjQyLDguMjF2NjYuNjQgYzAsMy4yLDEuMzEsNi4xLDMuNDIsOC4yMWMyLjExLDIuMTEsNS4wMiwzLjQyLDguMjEsMy40Mmg4MC4wNGMzLjIsMCw2LjEtMS4zMSw4LjIxLTMuNDJjMi4xMS0yLjExLDMuNDItNS4wMiwzLjQyLTguMjF2LTkuNDYgaC0xOC40Yy01LjU1LDAtMTAuNi0yLjI3LTE0LjI1LTUuOTJjLTMuNjUtMy42NS01LjkyLTguNy01LjkyLTE0LjI1di0wLjg3YzAtNS41NSwyLjI3LTEwLjYsNS45Mi0xNC4yNSBDODAuMDIsMzUuNTksODUuMDYsMzMuMzIsOTAuNjIsMzMuMzJMOTAuNjIsMzMuMzJ6IE0xMTQuNzMsMzMuNDNjMi4wNywwLjMxLDMuOTIsMS4yOSw1LjMzLDIuNzFjMS43NCwxLjc0LDIuODEsNC4xNCwyLjgxLDYuNzggdjIxLjZjMCwyLjc2LTEuMTIsNS4yNi0yLjkzLDcuMDdjLTEuMzksMS4zOS0zLjIsMi4zOC01LjIxLDIuNzZ2OS42M2MwLDQuNzctMS45NSw5LjExLTUuMDksMTIuMjUgYy0zLjE0LDMuMTQtNy40OCw1LjA5LTEyLjI1LDUuMDlIMTcuMzRjLTQuNzcsMC05LjExLTEuOTUtMTIuMjUtNS4wOUMxLjk1LDkzLjEsMCw4OC43NiwwLDgzLjk5VjE3LjM0IGMwLTQuNzcsMS45NS05LjExLDUuMDktMTIuMjVDOC4yMywxLjk1LDEyLjU3LDAsMTcuMzQsMGg4MC4wNGM0Ljc3LDAsOS4xMSwxLjk1LDEyLjI1LDUuMDljMy4xNCwzLjE0LDUuMDksNy40OCw1LjA5LDEyLjI1VjMzLjQzIEwxMTQuNzMsMzMuNDN6IE04OC4xNCw0Ni4xMWM0LjA1LDAsNy4zMywzLjI4LDcuMzMsNy4zM2MwLDQuMDUtMy4yOCw3LjMzLTcuMzMsNy4zM2MtNC4wNSwwLTcuMzMtMy4yOC03LjMzLTcuMzMgQzgwLjgxLDQ5LjM5LDg0LjA5LDQ2LjExLDg4LjE0LDQ2LjExTDg4LjE0LDQ2LjExeiIvPjwvZz48L3N2Zz4=\",\n};\n",
"type": "registry:lib",
"target": "components/payment-widget/constants.ts"
},
{
"path": "registry/default/payment-widget/utils/wagmi.ts",
- "content": "import { createConfig, http } from \"wagmi\";\nimport {\n arbitrum,\n base,\n mainnet,\n optimism,\n polygon,\n sepolia,\n} from \"wagmi/chains\";\nimport {\n injected,\n coinbaseWallet,\n metaMask,\n safe,\n walletConnect,\n} from \"wagmi/connectors\";\n\nexport const getWagmiConfig = (walletConnectProjectId?: string) => {\n const connectors = [\n injected(),\n coinbaseWallet({\n appName: \"Request Network Payment\",\n }),\n metaMask(),\n safe(),\n ];\n\n if (walletConnectProjectId && walletConnectProjectId.length > 0) {\n try {\n const connector = walletConnect({\n projectId: walletConnectProjectId,\n metadata: {\n name: \"Request Network Payment\",\n description: \"Pay with cryptocurrency using Request Network\",\n url: \"https://request.network\",\n icons: [\"https://request.network/favicon.ico\"],\n },\n showQrModal: true,\n });\n\n connectors.push(connector as any);\n } catch (error) {\n console.error(\"WalletConnect creation failed:\", error);\n }\n }\n\n const config = createConfig({\n chains: [mainnet, sepolia, arbitrum, optimism, polygon, base],\n connectors,\n transports: {\n [mainnet.id]: http(),\n [sepolia.id]: http(),\n [arbitrum.id]: http(),\n [optimism.id]: http(),\n [polygon.id]: http(),\n [base.id]: http(),\n },\n });\n\n return config;\n};\n",
+ "content": "import { createConfig, http, type CreateConnectorFn } from \"wagmi\";\nimport {\n arbitrum,\n base,\n mainnet,\n optimism,\n polygon,\n sepolia,\n} from \"wagmi/chains\";\nimport {\n injected,\n coinbaseWallet,\n metaMask,\n safe,\n walletConnect,\n} from \"wagmi/connectors\";\n\nexport const getWagmiConfig = (walletConnectProjectId?: string) => {\n const connectors: CreateConnectorFn[] = [\n injected(),\n coinbaseWallet({\n appName: \"Request Network Payment\",\n }),\n metaMask(),\n safe(),\n ];\n\n if (walletConnectProjectId && walletConnectProjectId.length > 0) {\n try {\n const connector = walletConnect({\n projectId: walletConnectProjectId,\n metadata: {\n name: \"Request Network Payment\",\n description: \"Pay with cryptocurrency using Request Network\",\n url: \"https://request.network\",\n icons: [\"https://request.network/favicon.ico\"],\n },\n showQrModal: true,\n });\n\n connectors.push(connector);\n } catch (error) {\n console.error(\"WalletConnect creation failed:\", error);\n }\n }\n\n const config = createConfig({\n chains: [mainnet, sepolia, arbitrum, optimism, polygon, base],\n connectors,\n transports: {\n [mainnet.id]: http(),\n [sepolia.id]: http(),\n [arbitrum.id]: http(),\n [optimism.id]: http(),\n [polygon.id]: http(),\n [base.id]: http(),\n },\n });\n\n return config;\n};\n",
"type": "registry:lib",
"target": "components/payment-widget/utils/wagmi.ts"
},
{
"path": "registry/default/payment-widget/utils/payment.ts",
- "content": "import { TransactionReceipt } from \"viem\";\nimport { RN_API_URL } from \"../constants\";\nimport type { FeeInfo, PaymentError } from \"../types\";\n\nexport interface PaymentParams {\n amountInUsd: string;\n payerWallet: string;\n recipientWallet: string;\n paymentCurrency: string;\n feeInfo?: FeeInfo;\n customerInfo: {\n // This matches the API spec\n firstName?: string;\n lastName?: string;\n email?: string;\n address?: {\n street?: string;\n city?: string;\n state?: string;\n postalCode?: string;\n country?: string;\n };\n };\n}\n\ninterface PayoutAPITransaction {\n to: string;\n data: string;\n value: number | string | { type: string; hex: string };\n}\nexport interface PayoutAPIResponse {\n requestId: string;\n paymentReference: string;\n transactions: PayoutAPITransaction[];\n metadata: {\n stepsRequired: number;\n needsApproval: boolean;\n approvalTransactionIndex: number;\n paymentTransactionIndex: number;\n };\n}\n\nexport type TxParams = {\n to: `0x${string}`;\n data: `0x${string}`;\n value: bigint;\n};\n\nexport type SendTransactionFunction = (tx: TxParams) => Promise<`0x${string}`>;\n\nexport type WaitForTransactionFunction = (\n hash: `0x${string}`,\n) => Promise;\n\nexport const isPaymentError = (error: any): error is PaymentError => {\n return (\n error && typeof error === \"object\" && \"type\" in error && \"error\" in error\n );\n};\n\nexport const normalizeValue = (\n value: PayoutAPITransaction[\"value\"],\n): bigint => {\n // ERC20 tokens don't have a bignumber returned\n if (typeof value === \"number\") {\n if (!Number.isSafeInteger(value)) {\n throw new Error(\n \"Unsafe numeric value for tx.value; expected string/hex BigInt.\",\n );\n }\n return BigInt(value);\n }\n\n if (typeof value === \"string\") {\n return BigInt(value);\n }\n\n if (typeof value === \"object\" && value !== null && \"hex\" in value) {\n return BigInt(value.hex);\n }\n\n console.warn(\"Unknown value format, defaulting to 0:\", value);\n throw new Error(\"Unknown value format\");\n};\n\nexport const executeTransactions = async (\n transactions: PayoutAPITransaction[],\n sendTransaction: SendTransactionFunction,\n waitForTransaction: WaitForTransactionFunction,\n): Promise => {\n const receipts: TransactionReceipt[] = [];\n\n try {\n for (const tx of transactions) {\n const hash = await sendTransaction({\n to: tx.to as `0x${string}`,\n data: tx.data as `0x${string}`,\n value: normalizeValue(tx.value),\n });\n\n const receipt = await waitForTransaction(hash);\n if (receipt.status !== \"success\") {\n throw new Error(`Transaction reverted: ${hash}`);\n }\n\n receipts.push(receipt);\n }\n\n return receipts;\n } catch (error) {\n console.error(\"Transaction execution failed:\", error);\n throw { type: \"transaction\", error: error as Error } as PaymentError;\n }\n};\n\nexport const createPayout = async (\n rnApiClientId: string,\n params: PaymentParams,\n): Promise => {\n const {\n amountInUsd,\n payerWallet,\n recipientWallet,\n paymentCurrency,\n feeInfo,\n } = params;\n\n const response = await fetch(`${RN_API_URL}/v2/payouts`, {\n method: \"POST\",\n headers: {\n \"x-client-id\": rnApiClientId,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n amount: amountInUsd,\n payerWallet: payerWallet,\n payee: recipientWallet,\n invoiceCurrency: \"USD\",\n paymentCurrency: paymentCurrency,\n feePercentage: feeInfo?.feePercentage || undefined,\n feeAddress: feeInfo?.feeAddress || undefined,\n customerInfo: params.customerInfo,\n }),\n });\n\n return response;\n};\n\nexport interface PaymentResponse {\n requestId: string;\n transactionReceipts: TransactionReceipt[];\n}\n\nexport const executePayment = async ({\n paymentParams,\n rnApiClientId,\n sendTransaction,\n waitForTransaction,\n}: {\n rnApiClientId: string;\n paymentParams: PaymentParams;\n sendTransaction: SendTransactionFunction;\n waitForTransaction: WaitForTransactionFunction;\n}): Promise => {\n try {\n const response = await createPayout(rnApiClientId, paymentParams);\n\n if (!response.ok) {\n let errorMessage = \"Failed to create payment\";\n\n try {\n const errorData = await response.json();\n errorMessage = errorData.error || errorData.message || errorMessage;\n } catch (_parsingError) {\n // If we can't parse the error response, just use status text\n errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n }\n\n const error = new Error(errorMessage);\n throw { type: \"api\", error } as PaymentError;\n }\n\n const data: PayoutAPIResponse = await response.json();\n\n if (Array.isArray(data?.transactions) && data.transactions.length > 0) {\n const transactionReceipts = await executeTransactions(\n data.transactions,\n sendTransaction,\n waitForTransaction,\n );\n\n return { requestId: data.requestId, transactionReceipts };\n } else {\n const error = new Error(\"No transaction data received from backend\");\n throw { type: \"api\", error } as PaymentError;\n }\n } catch (error) {\n console.error(\"Error in payment flow:\", error);\n if (isPaymentError(error)) {\n throw error;\n } else {\n throw { type: \"unknown\", error: error as Error } as PaymentError;\n }\n }\n};\n",
+ "content": "import type { TransactionReceipt } from \"viem\";\nimport { RN_API_URL } from \"../constants\";\nimport type { FeeInfo, PaymentError } from \"../types\";\n\nexport interface PaymentParams {\n amountInUsd: string;\n payerWallet: string;\n recipientWallet: string;\n paymentCurrency: string;\n feeInfo?: FeeInfo;\n customerInfo: {\n // This matches the API spec\n firstName?: string;\n lastName?: string;\n email?: string;\n address?: {\n street?: string;\n city?: string;\n state?: string;\n postalCode?: string;\n country?: string;\n };\n };\n}\n\ninterface PayoutAPITransaction {\n to: string;\n data: string;\n value: number | string | { type: string; hex: string };\n}\nexport interface PayoutAPIResponse {\n requestId: string;\n paymentReference: string;\n transactions: PayoutAPITransaction[];\n metadata: {\n stepsRequired: number;\n needsApproval: boolean;\n approvalTransactionIndex: number;\n paymentTransactionIndex: number;\n };\n}\n\nexport type TxParams = {\n to: `0x${string}`;\n data: `0x${string}`;\n value: bigint;\n};\n\nexport type SendTransactionFunction = (tx: TxParams) => Promise<`0x${string}`>;\n\nexport type WaitForTransactionFunction = (\n hash: `0x${string}`,\n) => Promise;\n\nexport const isPaymentError = (error: unknown): error is PaymentError => {\n return (\n error !== null &&\n typeof error === \"object\" &&\n \"type\" in error &&\n \"error\" in error\n );\n};\n\nexport const normalizeValue = (\n value: PayoutAPITransaction[\"value\"],\n): bigint => {\n // ERC20 tokens don't have a bignumber returned\n if (typeof value === \"number\") {\n if (!Number.isSafeInteger(value)) {\n throw new Error(\n \"Unsafe numeric value for tx.value; expected string/hex BigInt.\",\n );\n }\n return BigInt(value);\n }\n\n if (typeof value === \"string\") {\n return BigInt(value);\n }\n\n if (typeof value === \"object\" && value !== null && \"hex\" in value) {\n return BigInt(value.hex);\n }\n\n console.warn(\"Unknown value format, defaulting to 0:\", value);\n throw new Error(\"Unknown value format\");\n};\n\nexport const executeTransactions = async (\n transactions: PayoutAPITransaction[],\n sendTransaction: SendTransactionFunction,\n waitForTransaction: WaitForTransactionFunction,\n): Promise => {\n const receipts: TransactionReceipt[] = [];\n\n try {\n for (const tx of transactions) {\n const hash = await sendTransaction({\n to: tx.to as `0x${string}`,\n data: tx.data as `0x${string}`,\n value: normalizeValue(tx.value),\n });\n\n const receipt = await waitForTransaction(hash);\n if (receipt.status !== \"success\") {\n throw new Error(`Transaction reverted: ${hash}`);\n }\n\n receipts.push(receipt);\n }\n\n return receipts;\n } catch (error) {\n console.error(\"Transaction execution failed:\", error);\n throw { type: \"transaction\", error: error as Error } as PaymentError;\n }\n};\n\nexport const createPayout = async (\n rnApiClientId: string,\n params: PaymentParams,\n): Promise => {\n const {\n amountInUsd,\n payerWallet,\n recipientWallet,\n paymentCurrency,\n feeInfo,\n } = params;\n\n const response = await fetch(`${RN_API_URL}/v2/payouts`, {\n method: \"POST\",\n headers: {\n \"x-client-id\": rnApiClientId,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n amount: amountInUsd,\n payerWallet: payerWallet,\n payee: recipientWallet,\n invoiceCurrency: \"USD\",\n paymentCurrency: paymentCurrency,\n feePercentage: feeInfo?.feePercentage || undefined,\n feeAddress: feeInfo?.feeAddress || undefined,\n customerInfo: params.customerInfo,\n }),\n });\n\n return response;\n};\n\nexport interface PaymentResponse {\n requestId: string;\n transactionReceipts: TransactionReceipt[];\n}\n\nexport const executePayment = async ({\n paymentParams,\n rnApiClientId,\n sendTransaction,\n waitForTransaction,\n}: {\n rnApiClientId: string;\n paymentParams: PaymentParams;\n sendTransaction: SendTransactionFunction;\n waitForTransaction: WaitForTransactionFunction;\n}): Promise => {\n try {\n const response = await createPayout(rnApiClientId, paymentParams);\n\n if (!response.ok) {\n let errorMessage = \"Failed to create payment\";\n\n try {\n const errorData = await response.json();\n errorMessage = errorData.error || errorData.message || errorMessage;\n } catch {\n // If we can't parse the error response, just use status text\n errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n }\n\n const error = new Error(errorMessage);\n throw { type: \"api\", error } as PaymentError;\n }\n\n const data: PayoutAPIResponse = await response.json();\n\n if (Array.isArray(data?.transactions) && data.transactions.length > 0) {\n const transactionReceipts = await executeTransactions(\n data.transactions,\n sendTransaction,\n waitForTransaction,\n );\n\n return { requestId: data.requestId, transactionReceipts };\n } else {\n const error = new Error(\"No transaction data received from backend\");\n throw { type: \"api\", error } as PaymentError;\n }\n } catch (error) {\n console.error(\"Error in payment flow:\", error);\n if (isPaymentError(error)) {\n throw error;\n } else {\n throw { type: \"unknown\", error: error as Error } as PaymentError;\n }\n }\n};\n",
"type": "registry:lib",
"target": "components/payment-widget/utils/payment.ts"
},
@@ -134,12 +147,6 @@
"type": "registry:lib",
"target": "components/payment-widget/types/index.ts"
},
- {
- "path": "registry/default/payment-widget/types/html2pdf.d.ts",
- "content": "// Type definitions for html2pdf.js\n// Project: https://github.com/eKoopmans/html2pdf.js\n\ndeclare module \"html2pdf.js\" {\n interface Html2PdfOptions {\n margin?: number | [number, number, number, number];\n filename?: string;\n image?: {\n type?: \"jpeg\" | \"png\" | \"webp\";\n quality?: number;\n };\n html2canvas?: {\n scale?: number;\n backgroundColor?: string;\n useCORS?: boolean;\n allowTaint?: boolean;\n logging?: boolean;\n debug?: boolean;\n width?: number;\n height?: number;\n scrollX?: number;\n scrollY?: number;\n x?: number;\n y?: number;\n };\n jsPDF?: {\n unit?: \"pt\" | \"mm\" | \"cm\" | \"in\";\n format?: \"a4\" | \"letter\" | \"legal\" | [number, number];\n orientation?: \"portrait\" | \"landscape\";\n compress?: boolean;\n };\n pagebreak?: {\n mode?: string | string[];\n before?: string | string[];\n after?: string | string[];\n avoid?: string | string[];\n };\n }\n\n interface Html2PdfWorker {\n from(element: HTMLElement | string): Html2PdfWorker;\n to(target: string): Html2PdfWorker;\n set(options: Html2PdfOptions): Html2PdfWorker;\n save(filename?: string): Promise;\n outputPdf(type?: string): Promise;\n then(\n onFulfilled?: (value: any) => any,\n onRejected?: (reason: any) => any,\n ): Promise;\n catch(onRejected?: (reason: any) => any): Promise;\n }\n\n function html2pdf(): Html2PdfWorker;\n function html2pdf(\n element: HTMLElement,\n options?: Html2PdfOptions,\n ): Promise;\n\n namespace html2pdf {\n class Worker implements Html2PdfWorker {\n from(element: HTMLElement | string): Html2PdfWorker;\n to(target: string): Html2PdfWorker;\n set(options: Html2PdfOptions): Html2PdfWorker;\n save(filename?: string): Promise;\n outputPdf(type?: string): Promise;\n // biome-ignore lint/suspicious/noThenProperty: \n then(\n onFulfilled?: (value: any) => any,\n onRejected?: (reason: any) => any,\n ): Promise;\n catch(onRejected?: (reason: any) => any): Promise;\n }\n }\n\n export = html2pdf;\n}\n",
- "type": "registry:lib",
- "target": "components/payment-widget/types/html2pdf.d.ts"
- },
{
"path": "registry/default/payment-widget/context/web3-context.tsx",
"content": "\"use client\";\n\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { WagmiProvider } from \"wagmi\";\nimport { getWagmiConfig } from \"../utils/wagmi\";\nimport { type ReactNode, useMemo, useRef } from \"react\";\n\nconst queryClient = new QueryClient();\n\nexport function Web3Provider({\n children,\n walletConnectProjectId,\n}: {\n children: ReactNode;\n walletConnectProjectId?: string;\n}) {\n // @NOTE this may seem weird, but walletConnect doesn't handle strict mode initializing it twice, so we explicitly use a ref to store the config\n const configRef = useRef | null>(null);\n\n const wagmiConfig = useMemo(() => {\n if (!configRef.current) {\n configRef.current = getWagmiConfig(walletConnectProjectId);\n }\n return configRef.current;\n }, [walletConnectProjectId]);\n\n return (\n \n {children} \n \n );\n}\n",
@@ -154,7 +161,7 @@
},
{
"path": "registry/default/payment-widget/components/receipt/styles.css",
- "content": ".receipt-container {\n background-color: #ffffff;\n padding: 20px;\n max-width: 800px;\n margin: 0 auto;\n font-family:\n -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-size: 12px;\n line-height: 1.4;\n color: #2d3748;\n\n .receipt-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 20px;\n padding-bottom: 15px;\n border-bottom: 2px solid #e2e8f0;\n }\n\n .company-info {\n flex: 1;\n }\n\n .company-name {\n font-size: 24px;\n font-weight: 700;\n color: #1a202c;\n margin: 0 0 8px 0;\n }\n\n .company-details {\n font-size: 11px;\n color: #4a5568;\n line-height: 1.3;\n }\n\n .company-details > div {\n margin-bottom: 2px;\n }\n\n .receipt-title-section {\n text-align: right;\n }\n\n .receipt-title {\n font-size: 28px;\n font-weight: 700;\n color: #2d3748;\n margin: 0 0 4px 0;\n }\n\n .receipt-number {\n font-size: 16px;\n font-weight: 600;\n color: #4a5568;\n margin-bottom: 4px;\n }\n\n .receipt-date {\n font-size: 11px;\n color: #718096;\n }\n\n .party-info-section {\n display: flex;\n gap: 20px;\n margin-bottom: 16px;\n }\n\n .info-box {\n flex: 1;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n background-color: #f7fafc;\n }\n\n .info-header {\n font-size: 11px;\n font-weight: 700;\n color: #2d3748;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin: 0 0 8px 0;\n }\n\n .party-name {\n font-weight: 600;\n font-size: 14px;\n color: #1a202c;\n margin-bottom: 4px;\n }\n\n .wallet-address {\n font-size: 10px;\n color: #718096;\n font-family: \"Monaco\", \"Courier New\", monospace;\n margin-bottom: 6px;\n word-break: break-all;\n }\n\n .email {\n font-size: 11px;\n color: #4a5568;\n margin-bottom: 4px;\n }\n\n .address-info {\n font-size: 11px;\n color: #4a5568;\n line-height: 1.3;\n }\n\n .address-info > div {\n margin-bottom: 2px;\n }\n\n .payment-info {\n background-color: #edf2f7;\n padding: 8px 12px;\n border-radius: 4px;\n margin-bottom: 16px;\n font-size: 11px;\n color: #2d3748;\n }\n\n .payment-info > div {\n margin-bottom: 2px;\n }\n\n .payment-info > div:last-child {\n margin-bottom: 0;\n }\n\n .transaction-hash {\n font-family: \"Monaco\", \"Courier New\", monospace;\n word-break: break-all;\n }\n\n .items-table {\n width: 100%;\n border-collapse: collapse;\n border: 1px solid #e2e8f0;\n margin-bottom: 16px;\n font-size: 11px;\n }\n\n .items-table th {\n background-color: #edf2f7;\n border: 1px solid #e2e8f0;\n padding: 8px 6px;\n font-weight: 600;\n text-align: left;\n color: #2d3748;\n }\n\n .items-table th.center {\n text-align: center;\n width: 40px;\n }\n\n .items-table th.right {\n text-align: right;\n width: 80px;\n }\n\n .items-table td {\n border: 1px solid #e2e8f0;\n padding: 6px;\n vertical-align: top;\n }\n\n .items-table td.center {\n text-align: center;\n }\n\n .items-table td.right {\n text-align: right;\n font-family: \"Monaco\", \"Courier New\", monospace;\n }\n\n .items-table .row-even {\n background-color: #ffffff;\n }\n\n .items-table .row-odd {\n background-color: #f7fafc;\n }\n\n .items-table .amount {\n font-weight: 600;\n }\n\n .totals-section {\n display: flex;\n justify-content: flex-end;\n margin-bottom: 16px;\n }\n\n .totals-box {\n background-color: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n min-width: 240px;\n }\n\n .total-line {\n display: flex;\n justify-content: space-between;\n margin-bottom: 4px;\n font-size: 11px;\n }\n\n .total-line.subtotal {\n padding-top: 6px;\n border-top: 1px solid #e2e8f0;\n font-weight: 600;\n }\n\n .total-line.final {\n font-size: 14px;\n font-weight: 700;\n color: #1a202c;\n padding-top: 6px;\n border-top: 2px solid #2d3748;\n }\n\n .total-amount {\n font-family: \"Monaco\", \"Courier New\", monospace;\n font-size: inherit;\n }\n\n .notes-section {\n border-top: 1px solid #e2e8f0;\n font-size: 11px;\n color: #4a5568;\n background-color: #f7fafc;\n padding: 8px 12px;\n border-radius: 4px;\n }\n}\n",
+ "content": ".receipt-container {\n background-color: #ffffff;\n padding: 32px;\n max-width: 800px;\n margin: 0 auto;\n font-family:\n -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-size: 12px;\n line-height: 1.4;\n color: #2d3748;\n\n .receipt-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 20px;\n padding-bottom: 15px;\n border-bottom: 2px solid #e2e8f0;\n }\n\n .company-info {\n flex: 1;\n }\n\n .company-name {\n font-size: 24px;\n font-weight: 700;\n color: #1a202c;\n margin: 0 0 8px 0;\n }\n\n .company-details {\n font-size: 11px;\n color: #4a5568;\n line-height: 1.3;\n }\n\n .company-details > div {\n margin-bottom: 2px;\n }\n\n .receipt-title-section {\n text-align: right;\n }\n\n .receipt-title {\n font-size: 28px;\n font-weight: 700;\n color: #2d3748;\n margin: 0 0 4px 0;\n }\n\n .receipt-number {\n font-size: 16px;\n font-weight: 600;\n color: #4a5568;\n margin-bottom: 4px;\n }\n\n .receipt-date {\n font-size: 11px;\n color: #718096;\n }\n\n .party-info-section {\n display: flex;\n gap: 20px;\n margin-bottom: 16px;\n }\n\n .info-box {\n flex: 1;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n background-color: #f7fafc;\n }\n\n .info-header {\n font-size: 11px;\n font-weight: 700;\n color: #2d3748;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin: 0 0 8px 0;\n }\n\n .party-name {\n font-weight: 600;\n font-size: 14px;\n color: #1a202c;\n margin-bottom: 4px;\n }\n\n .wallet-address {\n font-size: 10px;\n color: #718096;\n font-family: \"Monaco\", \"Courier New\", monospace;\n margin-bottom: 6px;\n word-break: break-all;\n }\n\n .email {\n font-size: 11px;\n color: #4a5568;\n margin-bottom: 4px;\n }\n\n .address-info {\n font-size: 11px;\n color: #4a5568;\n line-height: 1.3;\n }\n\n .address-info > div {\n margin-bottom: 2px;\n }\n\n .payment-info {\n background-color: #edf2f7;\n padding: 8px 12px;\n border-radius: 4px;\n margin-bottom: 16px;\n font-size: 11px;\n color: #2d3748;\n }\n\n .payment-info > div {\n margin-bottom: 2px;\n }\n\n .payment-info > div:last-child {\n margin-bottom: 0;\n }\n\n .transaction-hash {\n font-family: \"Monaco\", \"Courier New\", monospace;\n word-break: break-all;\n }\n\n .items-table {\n width: 100%;\n border-collapse: collapse;\n border: 1px solid #e2e8f0;\n margin-bottom: 16px;\n font-size: 11px;\n }\n\n .items-table th {\n background-color: #edf2f7;\n border: 1px solid #e2e8f0;\n padding: 8px 6px;\n font-weight: 600;\n text-align: left;\n color: #2d3748;\n }\n\n .items-table th.center {\n text-align: center;\n width: 40px;\n }\n\n .items-table th.right {\n text-align: right;\n width: 80px;\n }\n\n .items-table td {\n border: 1px solid #e2e8f0;\n padding: 6px;\n vertical-align: top;\n }\n\n .items-table td.center {\n text-align: center;\n }\n\n .items-table td.right {\n text-align: right;\n font-family: \"Monaco\", \"Courier New\", monospace;\n }\n\n .items-table .row-even {\n background-color: #ffffff;\n }\n\n .items-table .row-odd {\n background-color: #f7fafc;\n }\n\n .items-table .amount {\n font-weight: 600;\n }\n\n .totals-section {\n display: flex;\n justify-content: flex-end;\n margin-bottom: 16px;\n }\n\n .totals-box {\n background-color: #f7fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n padding: 12px;\n min-width: 240px;\n }\n\n .total-line {\n display: flex;\n justify-content: space-between;\n margin-bottom: 4px;\n font-size: 11px;\n }\n\n .total-line.subtotal {\n padding-top: 6px;\n border-top: 1px solid #e2e8f0;\n font-weight: 600;\n }\n\n .total-line.final {\n font-size: 14px;\n font-weight: 700;\n color: #1a202c;\n padding-top: 6px;\n border-top: 2px solid #2d3748;\n }\n\n .total-amount {\n font-family: \"Monaco\", \"Courier New\", monospace;\n font-size: inherit;\n }\n\n .notes-section {\n border-top: 1px solid #e2e8f0;\n font-size: 11px;\n color: #4a5568;\n background-color: #f7fafc;\n padding: 8px 12px;\n border-radius: 4px;\n }\n}\n",
"type": "registry:component",
"target": "components/payment-widget/components/receipt/styles.css"
},
diff --git a/public/r/registry.json b/public/r/registry.json
index 4fe0dd7..422cfbf 100644
--- a/public/r/registry.json
+++ b/public/r/registry.json
@@ -21,7 +21,8 @@
"@tanstack/react-query@^5.86.0",
"react-hook-form@^7.62.0",
"lucide-react@^0.542.0",
- "html2pdf.js@^0.12.0"
+ "html2canvas-pro@^1.5.11",
+ "jspdf@^3.0.3"
],
"files": [
{
@@ -30,9 +31,19 @@
"target": "components/payment-widget/payment-widget.tsx"
},
{
- "path": "registry/default/payment-widget/context/payment-widget-context.tsx",
+ "path": "registry/default/payment-widget/context/payment-widget-context/index.ts",
"type": "registry:component",
- "target": "components/payment-widget/context/payment-widget-context.tsx"
+ "target": "components/payment-widget/context/payment-widget-context/index.ts"
+ },
+ {
+ "path": "registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx",
+ "type": "registry:component",
+ "target": "components/payment-widget/context/payment-widget-context/payment-widget-provider.tsx"
+ },
+ {
+ "path": "registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts",
+ "type": "registry:component",
+ "target": "components/payment-widget/context/payment-widget-context/use-payment-widget-context.ts"
},
{
"path": "registry/default/payment-widget/components/connection-handler.tsx",
@@ -119,11 +130,6 @@
"type": "registry:lib",
"target": "components/payment-widget/types/index.ts"
},
- {
- "path": "registry/default/payment-widget/types/html2pdf.d.ts",
- "type": "registry:lib",
- "target": "components/payment-widget/types/html2pdf.d.ts"
- },
{
"path": "registry/default/payment-widget/context/web3-context.tsx",
"type": "registry:component",
diff --git a/registry.json b/registry.json
index 4fe0dd7..422cfbf 100644
--- a/registry.json
+++ b/registry.json
@@ -21,7 +21,8 @@
"@tanstack/react-query@^5.86.0",
"react-hook-form@^7.62.0",
"lucide-react@^0.542.0",
- "html2pdf.js@^0.12.0"
+ "html2canvas-pro@^1.5.11",
+ "jspdf@^3.0.3"
],
"files": [
{
@@ -30,9 +31,19 @@
"target": "components/payment-widget/payment-widget.tsx"
},
{
- "path": "registry/default/payment-widget/context/payment-widget-context.tsx",
+ "path": "registry/default/payment-widget/context/payment-widget-context/index.ts",
"type": "registry:component",
- "target": "components/payment-widget/context/payment-widget-context.tsx"
+ "target": "components/payment-widget/context/payment-widget-context/index.ts"
+ },
+ {
+ "path": "registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx",
+ "type": "registry:component",
+ "target": "components/payment-widget/context/payment-widget-context/payment-widget-provider.tsx"
+ },
+ {
+ "path": "registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts",
+ "type": "registry:component",
+ "target": "components/payment-widget/context/payment-widget-context/use-payment-widget-context.ts"
},
{
"path": "registry/default/payment-widget/components/connection-handler.tsx",
@@ -119,11 +130,6 @@
"type": "registry:lib",
"target": "components/payment-widget/types/index.ts"
},
- {
- "path": "registry/default/payment-widget/types/html2pdf.d.ts",
- "type": "registry:lib",
- "target": "components/payment-widget/types/html2pdf.d.ts"
- },
{
"path": "registry/default/payment-widget/context/web3-context.tsx",
"type": "registry:component",
diff --git a/registry/default/payment-widget/components/currency-select.tsx b/registry/default/payment-widget/components/currency-select.tsx
index 96afe11..094c754 100644
--- a/registry/default/payment-widget/components/currency-select.tsx
+++ b/registry/default/payment-widget/components/currency-select.tsx
@@ -10,7 +10,7 @@ import {
type ConversionCurrency,
} from "../utils/currencies";
import { Check } from "lucide-react";
-import { usePaymentWidgetContext } from "../context/payment-widget-context";
+import { usePaymentWidgetContext } from "../context/payment-widget-context/use-payment-widget-context";
interface CurrencySelectProps {
onSubmit: (currency: ConversionCurrency) => void;
diff --git a/registry/default/payment-widget/components/payment-confirmation.tsx b/registry/default/payment-widget/components/payment-confirmation.tsx
index bdafcbe..d5ee24c 100644
--- a/registry/default/payment-widget/components/payment-confirmation.tsx
+++ b/registry/default/payment-widget/components/payment-confirmation.tsx
@@ -7,10 +7,10 @@ import {
getSymbolOverride,
type ConversionCurrency,
} from "../utils/currencies";
-import { usePaymentWidgetContext } from "../context/payment-widget-context";
import type { BuyerInfo, PaymentError } from "../types/index";
import { useState } from "react";
import type { TransactionReceipt } from "viem";
+import { usePaymentWidgetContext } from "../context/payment-widget-context/use-payment-widget-context";
interface PaymentConfirmationProps {
selectedCurrency: ConversionCurrency;
diff --git a/registry/default/payment-widget/components/payment-modal.tsx b/registry/default/payment-widget/components/payment-modal.tsx
index 5907a1d..c6d1f46 100644
--- a/registry/default/payment-widget/components/payment-modal.tsx
+++ b/registry/default/payment-widget/components/payment-modal.tsx
@@ -13,10 +13,10 @@ import { BuyerInfoForm } from "./buyer-info-form";
import { PaymentConfirmation } from "./payment-confirmation";
import { PaymentSuccess } from "./payment-success";
import { DisconnectWallet } from "./disconnect-wallet";
-import { usePaymentWidgetContext } from "../context/payment-widget-context";
import type { BuyerInfo } from "../types/index";
import type { ConversionCurrency } from "../utils/currencies";
import type { TransactionReceipt } from "viem";
+import { usePaymentWidgetContext } from "../context/payment-widget-context/use-payment-widget-context";
interface PaymentModalProps {
isOpen: boolean;
diff --git a/registry/default/payment-widget/components/payment-success.tsx b/registry/default/payment-widget/components/payment-success.tsx
index aaa95e0..1e6fe76 100644
--- a/registry/default/payment-widget/components/payment-success.tsx
+++ b/registry/default/payment-widget/components/payment-success.tsx
@@ -9,9 +9,9 @@ import {
} from "../utils/receipt";
import { useRef } from "react";
import { ReceiptPDFTemplate } from "../components/receipt/receipt-template";
-import { usePaymentWidgetContext } from "../context/payment-widget-context";
import type { BuyerInfo } from "../types/index";
import type { ConversionCurrency } from "../utils/currencies";
+import { usePaymentWidgetContext } from "../context/payment-widget-context/use-payment-widget-context";
interface PaymentSuccessProps {
requestId: string;
@@ -45,7 +45,7 @@ export function PaymentSuccess({
walletAddress: connectedWalletAddress || "",
},
payment: {
- amount: amountInUsd, // TODO connect to actual payout and exchange rate
+ amount: amountInUsd,
chain: selectedCurrency.network,
currency: selectedCurrency.symbol,
exchangeRate: "1",
@@ -69,18 +69,37 @@ export function PaymentSuccess({
return;
}
- const html2pdf = (await import("html2pdf.js")).default;
+ const html2canvas = (await import("html2canvas-pro")).default;
+ const jsPDF = (await import("jspdf")).default;
- html2pdf()
- .set({
- margin: 1,
- filename: `receipt-${receiptParams.metadata?.receiptNumber || "payment"}.pdf`,
- image: { type: "jpeg", quality: 0.98 },
- html2canvas: { scale: 2 },
- jsPDF: { unit: "in", format: "a4", orientation: "portrait" },
- })
- .from(element)
- .save();
+ const canvas = await html2canvas(element, {
+ scale: 2,
+ useCORS: true,
+ backgroundColor: "#ffffff",
+ width: element.scrollWidth,
+ height: element.scrollHeight,
+ windowWidth: element.scrollWidth,
+ windowHeight: element.scrollHeight,
+ });
+
+ const pdf = new jsPDF("p", "mm", "a4");
+ const imgData = canvas.toDataURL("image/png");
+
+ const pageWidth = 210;
+ const pageHeight = 297;
+ const margin = 10;
+ let imgWidth = pageWidth - margin * 2;
+ let imgHeight = (canvas.height * imgWidth) / canvas.width;
+ const maxHeight = pageHeight - margin * 2;
+ if (imgHeight > maxHeight) {
+ const ratio = maxHeight / imgHeight;
+ imgWidth = imgWidth * ratio;
+ imgHeight = imgHeight * ratio;
+ }
+ pdf.addImage(imgData, "PNG", margin, margin, imgWidth, imgHeight);
+ pdf.save(
+ `receipt-${receiptParams.metadata?.receiptNumber || "payment"}.pdf`,
+ );
} catch (error) {
console.error("Failed to download receipt:", error);
alert("Failed to download receipt. Please try again.");
@@ -118,6 +137,8 @@ export function PaymentSuccess({
top: 0,
opacity: 0,
pointerEvents: "none",
+ width: "800px",
+ backgroundColor: "white",
}}
>
diff --git a/registry/default/payment-widget/components/receipt/styles.css b/registry/default/payment-widget/components/receipt/styles.css
index ea2faf0..494bb36 100644
--- a/registry/default/payment-widget/components/receipt/styles.css
+++ b/registry/default/payment-widget/components/receipt/styles.css
@@ -1,6 +1,6 @@
.receipt-container {
background-color: #ffffff;
- padding: 20px;
+ padding: 32px;
max-width: 800px;
margin: 0 auto;
font-family:
diff --git a/registry/default/payment-widget/constants.ts b/registry/default/payment-widget/constants.ts
index 20e2ef1..84b920d 100644
--- a/registry/default/payment-widget/constants.ts
+++ b/registry/default/payment-widget/constants.ts
@@ -1,5 +1,6 @@
export const RN_API_URL =
- process.env.NEXT_PUBLIC_REQUEST_API_URL || "https://api.request.network";
+ (typeof process !== "undefined" && process.env.NEXT_PUBLIC_REQUEST_API_URL) ||
+ "https://api.request.network";
export const ICONS = {
metamask:
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzUiIGhlaWdodD0iMzQiIHZpZXdCb3g9IjAgMCAzNSAzNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTMyLjcwNzcgMzIuNzUyMkwyNS4xNjg4IDMwLjUxNzRMMTkuNDgzMyAzMy45MDA4TDE1LjUxNjcgMzMuODk5MUw5LjgyNzkzIDMwLjUxNzRMMi4yOTIyNSAzMi43NTIyTDAgMjUuMDQ4OUwyLjI5MjI1IDE2LjQ5OTNMMCA5LjI3MDk0TDIuMjkyMjUgMC4zMTIyNTZMMTQuMDY3NCA3LjMxNTU0SDIwLjkzMjZMMzIuNzA3NyAwLjMxMjI1NkwzNSA5LjI3MDk0TDMyLjcwNzcgMTYuNDk5M0wzNSAyNS4wNDg5TDMyLjcwNzcgMzIuNzUyMloiIGZpbGw9IiNGRjVDMTYiLz4KPHBhdGggZD0iTTIuMjkzOTUgMC4zMTIyNTZMMTQuMDY5MSA3LjMyMDQ3TDEzLjYwMDggMTIuMTMwMUwyLjI5Mzk1IDAuMzEyMjU2WiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNOS44Mjk1OSAyNS4wNTIyTDE1LjAxMDYgMjguOTgxMUw5LjgyOTU5IDMwLjUxNzVWMjUuMDUyMloiIGZpbGw9IiNGRjVDMTYiLz4KPHBhdGggZD0iTTE0LjU5NjYgMTguNTU2NUwxMy42MDA5IDEyLjEzMzNMNy4yMjY5MiAxNi41MDA5TDcuMjIzNjMgMTYuNDk5M1YxNi41MDI1TDcuMjQzMzUgMjAuOTk4M0w5LjgyODA5IDE4LjU1NjVIOS44Mjk3NEgxNC41OTY2WiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNMzIuNzA3NyAwLjMxMjI1NkwyMC45MzI2IDcuMzIwNDdMMjEuMzk5MyAxMi4xMzAxTDMyLjcwNzcgMC4zMTIyNTZaIiBmaWxsPSIjRkY1QzE2Ii8+CjxwYXRoIGQ9Ik0yNS4xNzIyIDI1LjA1MjJMMTkuOTkxMiAyOC45ODExTDI1LjE3MjIgMzAuNTE3NVYyNS4wNTIyWiIgZmlsbD0iI0ZGNUMxNiIvPgo8cGF0aCBkPSJNMjcuNzc2NiAxNi41MDI1SDI3Ljc3ODNIMjcuNzc2NlYxNi40OTkzTDI3Ljc3NSAxNi41MDA5TDIxLjQwMSAxMi4xMzMzTDIwLjQwNTMgMTguNTU2NUgyNS4xNzIyTDI3Ljc1ODYgMjAuOTk4M0wyNy43NzY2IDE2LjUwMjVaIiBmaWxsPSIjRkY1QzE2Ii8+CjxwYXRoIGQ9Ik05LjgyNzkzIDMwLjUxNzVMMi4yOTIyNSAzMi43NTIyTDAgMjUuMDUyMkg5LjgyNzkzVjMwLjUxNzVaIiBmaWxsPSIjRTM0ODA3Ii8+CjxwYXRoIGQ9Ik0xNC41OTQ3IDE4LjU1NDlMMTYuMDM0MSAyNy44NDA2TDE0LjAzOTMgMjIuNjc3N0w3LjIzOTc1IDIwLjk5ODRMOS44MjYxMyAxOC41NTQ5SDE0LjU5M0gxNC41OTQ3WiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMjUuMTcyMSAzMC41MTc1TDMyLjcwNzggMzIuNzUyMkwzNS4wMDAxIDI1LjA1MjJIMjUuMTcyMVYzMC41MTc1WiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMjAuNDA1MyAxOC41NTQ5TDE4Ljk2NTggMjcuODQwNkwyMC45NjA3IDIyLjY3NzdMMjcuNzYwMiAyMC45OTg0TDI1LjE3MjIgMTguNTU0OUgyMC40MDUzWiIgZmlsbD0iI0UzNDgwNyIvPgo8cGF0aCBkPSJNMCAyNS4wNDg4TDIuMjkyMjUgMTYuNDk5M0g3LjIyMTgzTDcuMjM5OTEgMjAuOTk2N0wxNC4wMzk0IDIyLjY3NkwxNi4wMzQzIDI3LjgzODlMMTUuMDA4OSAyOC45NzZMOS44Mjc5MyAyNS4wNDcySDBWMjUuMDQ4OFoiIGZpbGw9IiNGRjhENUQiLz4KPHBhdGggZD0iTTM1LjAwMDEgMjUuMDQ4OEwzMi43MDc4IDE2LjQ5OTNIMjcuNzc4M0wyNy43NjAyIDIwLjk5NjdMMjAuOTYwNyAyMi42NzZMMTguOTY1OCAyNy44Mzg5TDE5Ljk5MTIgMjguOTc2TDI1LjE3MjIgMjUuMDQ3MkgzNS4wMDAxVjI1LjA0ODhaIiBmaWxsPSIjRkY4RDVEIi8+CjxwYXRoIGQ9Ik0yMC45MzI1IDcuMzE1NDNIMTcuNDk5OUgxNC4wNjczTDEzLjYwMDYgMTIuMTI1MUwxNi4wMzQyIDI3LjgzNEgxOC45NjU2TDIxLjQwMDggMTIuMTI1MUwyMC45MzI1IDcuMzE1NDNaIiBmaWxsPSIjRkY4RDVEIi8+CjxwYXRoIGQ9Ik0yLjI5MjI1IDAuMzEyMjU2TDAgOS4yNzA5NEwyLjI5MjI1IDE2LjQ5OTNINy4yMjE4M0wxMy41OTkxIDEyLjEzMDFMMi4yOTIyNSAwLjMxMjI1NloiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTEzLjE3IDIwLjQxOTlIMTAuOTM2OUw5LjcyMDk1IDIxLjYwNjJMMTQuMDQwOSAyMi42NzI3TDEzLjE3IDIwLjQxODJWMjAuNDE5OVoiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTMyLjcwNzcgMC4zMTIyNTZMMzQuOTk5OSA5LjI3MDk0TDMyLjcwNzcgMTYuNDk5M0gyNy43NzgxTDIxLjQwMDkgMTIuMTMwMUwzMi43MDc3IDAuMzEyMjU2WiIgZmlsbD0iIzY2MTgwMCIvPgo8cGF0aCBkPSJNMjEuODMzIDIwLjQxOTlIMjQuMDY5NEwyNS4yODUzIDIxLjYwNzlMMjAuOTYwNCAyMi42NzZMMjEuODMzIDIwLjQxODJWMjAuNDE5OVoiIGZpbGw9IiM2NjE4MDAiLz4KPHBhdGggZD0iTTE5LjQ4MTcgMzAuODM2MkwxOS45OTExIDI4Ljk3OTRMMTguOTY1OCAyNy44NDIzSDE2LjAzMjdMMTUuMDA3MyAyOC45Nzk0TDE1LjUxNjcgMzAuODM2MiIgZmlsbD0iIzY2MTgwMCIvPgo8cGF0aCBkPSJNMTkuNDgxNiAzMC44MzU5VjMzLjkwMjFIMTUuNTE2NlYzMC44MzU5SDE5LjQ4MTZaIiBmaWxsPSIjQzBDNENEIi8+CjxwYXRoIGQ9Ik05LjgyOTU5IDMwLjUxNDJMMTUuNTIgMzMuOTAwOFYzMC44MzQ2TDE1LjAxMDYgMjguOTc3OEw5LjgyOTU5IDMwLjUxNDJaIiBmaWxsPSIjRTdFQkY2Ii8+CjxwYXRoIGQ9Ik0yNS4xNzIxIDMwLjUxNDJMMTkuNDgxNyAzMy45MDA4VjMwLjgzNDZMMTkuOTkxMSAyOC45Nzc4TDI1LjE3MjEgMzAuNTE0MloiIGZpbGw9IiNFN0VCRjYiLz4KPC9zdmc+",
diff --git a/registry/default/payment-widget/context/payment-widget-context/index.ts b/registry/default/payment-widget/context/payment-widget-context/index.ts
new file mode 100644
index 0000000..70c5b19
--- /dev/null
+++ b/registry/default/payment-widget/context/payment-widget-context/index.ts
@@ -0,0 +1,34 @@
+import { createContext } from "react";
+import type { ReceiptInfo, FeeInfo, PaymentError } from "../../types/index";
+import type { TransactionReceipt, WalletClient } from "viem";
+
+export interface PaymentWidgetContextValue {
+ amountInUsd: string;
+ recipientWallet: string;
+
+ walletAccount?: WalletClient;
+ connectedWalletAddress?: string;
+ isWalletOverride: boolean;
+
+ paymentConfig: {
+ rnApiClientId: string;
+ feeInfo?: FeeInfo;
+ supportedCurrencies: string[];
+ };
+
+ uiConfig: {
+ showRequestScanUrl: boolean;
+ showReceiptDownload: boolean;
+ };
+
+ receiptInfo: ReceiptInfo;
+
+ onSuccess?: (
+ requestId: string,
+ transactionReceipts: TransactionReceipt[],
+ ) => void | Promise
;
+ onError?: (error: PaymentError) => void | Promise;
+}
+
+export const PaymentWidgetContext =
+ createContext(null);
diff --git a/registry/default/payment-widget/context/payment-widget-context.tsx b/registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx
similarity index 64%
rename from registry/default/payment-widget/context/payment-widget-context.tsx
rename to registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx
index f33b79a..122f34f 100644
--- a/registry/default/payment-widget/context/payment-widget-context.tsx
+++ b/registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx
@@ -1,42 +1,11 @@
"use client";
-import { createContext, useContext, useMemo, type ReactNode } from "react";
+import { useMemo, type ReactNode } from "react";
import { useAccount } from "wagmi";
-import type { ReceiptInfo, FeeInfo, PaymentError } from "../types/index";
+import type { ReceiptInfo, PaymentError } from "../../types/index";
import type { TransactionReceipt, WalletClient } from "viem";
-import type { PaymentWidgetProps } from "../payment-widget.types";
-
-export interface PaymentWidgetContextValue {
- amountInUsd: string;
- recipientWallet: string;
-
- walletAccount?: WalletClient;
- connectedWalletAddress?: string;
- isWalletOverride: boolean;
-
- paymentConfig: {
- rnApiClientId: string;
- feeInfo?: FeeInfo;
- supportedCurrencies: string[];
- };
-
- uiConfig: {
- showRequestScanUrl: boolean;
- showReceiptDownload: boolean;
- };
-
- receiptInfo: ReceiptInfo;
-
- onSuccess?: (
- requestId: string,
- transactionReceipts: TransactionReceipt[],
- ) => void | Promise;
- onError?: (error: PaymentError) => void | Promise;
-}
-
-const PaymentWidgetContext = createContext(
- null,
-);
+import type { PaymentWidgetProps } from "../../payment-widget.types";
+import { PaymentWidgetContext, type PaymentWidgetContextValue } from "./index";
interface PaymentWidgetProviderProps {
children: ReactNode;
@@ -117,15 +86,3 @@ export function PaymentWidgetProvider({
);
}
-
-export function usePaymentWidgetContext(): PaymentWidgetContextValue {
- const context = useContext(PaymentWidgetContext);
-
- if (!context) {
- throw new Error(
- "usePaymentWidgetContext must be used within a PaymentWidgetProvider",
- );
- }
-
- return context;
-}
diff --git a/registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts b/registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts
new file mode 100644
index 0000000..632e89a
--- /dev/null
+++ b/registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts
@@ -0,0 +1,14 @@
+import { useContext } from "react";
+import { type PaymentWidgetContextValue, PaymentWidgetContext } from "./index";
+
+export function usePaymentWidgetContext(): PaymentWidgetContextValue {
+ const context = useContext(PaymentWidgetContext);
+
+ if (!context) {
+ throw new Error(
+ "usePaymentWidgetContext must be used within a PaymentWidgetProvider",
+ );
+ }
+
+ return context;
+}
diff --git a/registry/default/payment-widget/payment-widget.tsx b/registry/default/payment-widget/payment-widget.tsx
index 3fb07ca..40ecc2d 100644
--- a/registry/default/payment-widget/payment-widget.tsx
+++ b/registry/default/payment-widget/payment-widget.tsx
@@ -5,12 +5,10 @@ import { Button } from "@/components/ui/button";
import { ConnectionHandler } from "./components/connection-handler";
import { Web3Provider } from "./context/web3-context";
import { PaymentModal } from "./components/payment-modal";
-import {
- PaymentWidgetProvider,
- usePaymentWidgetContext,
-} from "./context/payment-widget-context";
+import { PaymentWidgetProvider } from "./context/payment-widget-context/payment-widget-provider";
import type { PaymentWidgetProps } from "./payment-widget.types";
import { ICONS } from "./constants";
+import { usePaymentWidgetContext } from "./context/payment-widget-context/use-payment-widget-context";
function PaymentWidgetInner({ children }: PropsWithChildren) {
const [isModalOpen, setIsModalOpen] = useState(false);
diff --git a/registry/default/payment-widget/types/html2pdf.d.ts b/registry/default/payment-widget/types/html2pdf.d.ts
deleted file mode 100644
index 9c36907..0000000
--- a/registry/default/payment-widget/types/html2pdf.d.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-// Type definitions for html2pdf.js
-// Project: https://github.com/eKoopmans/html2pdf.js
-
-declare module "html2pdf.js" {
- interface Html2PdfOptions {
- margin?: number | [number, number, number, number];
- filename?: string;
- image?: {
- type?: "jpeg" | "png" | "webp";
- quality?: number;
- };
- html2canvas?: {
- scale?: number;
- backgroundColor?: string;
- useCORS?: boolean;
- allowTaint?: boolean;
- logging?: boolean;
- debug?: boolean;
- width?: number;
- height?: number;
- scrollX?: number;
- scrollY?: number;
- x?: number;
- y?: number;
- };
- jsPDF?: {
- unit?: "pt" | "mm" | "cm" | "in";
- format?: "a4" | "letter" | "legal" | [number, number];
- orientation?: "portrait" | "landscape";
- compress?: boolean;
- };
- pagebreak?: {
- mode?: string | string[];
- before?: string | string[];
- after?: string | string[];
- avoid?: string | string[];
- };
- }
-
- interface Html2PdfWorker {
- from(element: HTMLElement | string): Html2PdfWorker;
- to(target: string): Html2PdfWorker;
- set(options: Html2PdfOptions): Html2PdfWorker;
- save(filename?: string): Promise;
- outputPdf(type?: string): Promise;
- then(
- onFulfilled?: (value: any) => any,
- onRejected?: (reason: any) => any,
- ): Promise;
- catch(onRejected?: (reason: any) => any): Promise;
- }
-
- function html2pdf(): Html2PdfWorker;
- function html2pdf(
- element: HTMLElement,
- options?: Html2PdfOptions,
- ): Promise;
-
- namespace html2pdf {
- class Worker implements Html2PdfWorker {
- from(element: HTMLElement | string): Html2PdfWorker;
- to(target: string): Html2PdfWorker;
- set(options: Html2PdfOptions): Html2PdfWorker;
- save(filename?: string): Promise;
- outputPdf(type?: string): Promise;
- // biome-ignore lint/suspicious/noThenProperty:
- then(
- onFulfilled?: (value: any) => any,
- onRejected?: (reason: any) => any,
- ): Promise;
- catch(onRejected?: (reason: any) => any): Promise;
- }
- }
-
- export = html2pdf;
-}
diff --git a/registry/default/payment-widget/utils/payment.ts b/registry/default/payment-widget/utils/payment.ts
index c769c86..6f72b5e 100644
--- a/registry/default/payment-widget/utils/payment.ts
+++ b/registry/default/payment-widget/utils/payment.ts
@@ -1,4 +1,4 @@
-import { TransactionReceipt } from "viem";
+import type { TransactionReceipt } from "viem";
import { RN_API_URL } from "../constants";
import type { FeeInfo, PaymentError } from "../types";
@@ -52,9 +52,12 @@ export type WaitForTransactionFunction = (
hash: `0x${string}`,
) => Promise;
-export const isPaymentError = (error: any): error is PaymentError => {
+export const isPaymentError = (error: unknown): error is PaymentError => {
return (
- error && typeof error === "object" && "type" in error && "error" in error
+ error !== null &&
+ typeof error === "object" &&
+ "type" in error &&
+ "error" in error
);
};
@@ -171,7 +174,7 @@ export const executePayment = async ({
try {
const errorData = await response.json();
errorMessage = errorData.error || errorData.message || errorMessage;
- } catch (_parsingError) {
+ } catch {
// If we can't parse the error response, just use status text
errorMessage = `HTTP ${response.status}: ${response.statusText}`;
}
diff --git a/registry/default/payment-widget/utils/wagmi.ts b/registry/default/payment-widget/utils/wagmi.ts
index 2ce3897..489ffb6 100644
--- a/registry/default/payment-widget/utils/wagmi.ts
+++ b/registry/default/payment-widget/utils/wagmi.ts
@@ -1,4 +1,4 @@
-import { createConfig, http } from "wagmi";
+import { createConfig, http, type CreateConnectorFn } from "wagmi";
import {
arbitrum,
base,
@@ -16,7 +16,7 @@ import {
} from "wagmi/connectors";
export const getWagmiConfig = (walletConnectProjectId?: string) => {
- const connectors = [
+ const connectors: CreateConnectorFn[] = [
injected(),
coinbaseWallet({
appName: "Request Network Payment",
@@ -38,7 +38,7 @@ export const getWagmiConfig = (walletConnectProjectId?: string) => {
showQrModal: true,
});
- connectors.push(connector as any);
+ connectors.push(connector);
} catch (error) {
console.error("WalletConnect creation failed:", error);
}