Compare commits

..
Author SHA1 Message Date
github-actions[bot]andGitHub bbc2e092b8 Version Packages (#2120) 2024-01-06 07:18:21 +13:00
iketwandGitHub 4c3c840d88 [RN] Export WC modal component (#2092) 2024-01-05 16:27:27 +00:00
Manan TankandGitHub cf606665fc Update Docs links (#2119) 2024-01-05 16:07:39 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
3871068aa6 Version Packages (#2116)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-03 15:59:09 +13:00
Isaac DubuqueandGitHub d00ca7fd2a add patch bump to payments package (#2115) 2024-01-03 02:51:11 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
9ee3c21eac Version Packages (#2102)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-01-03 14:34:19 +13:00
ed3afd418c Isaac/react sdk migration (#1913)
Co-authored-by: gerrysaporito <[email protected]>
2024-01-03 00:55:16 +00:00
iketwandGitHub b90fa2cc29 [RN] Adds hideSendButtonand hideReceiveButton to ConnectWallet (#2108) 2024-01-02 20:02:44 +00:00
EimanandGitHub 2ab517aa49 changing Avalanche tether USD address mapping (#2109) 2023-12-28 16:34:34 +00:00
Manan TankandGitHub e46dd0254f [wallets] JSDoc improvements for autogenerated docs (#2103) 2023-12-22 10:06:51 +00:00
Gerry SaporitoandGitHub 1a5627f72d chore: added payments to scope (#2107) 2023-12-20 18:39:51 +00:00
YashandGitHub b808e942fd [SDK] Fix verification (#2105) 2023-12-20 15:02:04 +00:00
iketwandGitHub 7fdb1715e4 [RN] Export types to improve references (#2101) 2023-12-19 17:02:08 +00:00
195 changed files with 7057 additions and 415 deletions
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/auth
## 4.1.21
### Patch Changes
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
- @thirdweb-dev/wallets@2.3.4
## 4.1.20
### Patch Changes
- Updated dependencies [[`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8)]:
- @thirdweb-dev/wallets@2.3.3
## 4.1.19
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "4.1.19",
"version": "4.1.21",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+18
View File
@@ -1,5 +1,23 @@
# thirdweb
## 0.13.29
### Patch Changes
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
- @thirdweb-dev/wallets@2.3.4
- @thirdweb-dev/sdk@4.0.25
- @thirdweb-dev/auth@4.1.21
## 0.13.28
### Patch Changes
- Updated dependencies [[`b808e942`](https://github.com/thirdweb-dev/js/commit/b808e942fdaa81b5de61dcb3f2083081cb99ea11), [`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8)]:
- @thirdweb-dev/sdk@4.0.24
- @thirdweb-dev/wallets@2.3.3
- @thirdweb-dev/auth@4.1.20
## 0.13.27
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.13.27",
"version": "0.13.29",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+11
View File
@@ -0,0 +1,11 @@
{
"presets": [
"@babel/preset-typescript",
["@babel/preset-env", { "targets": "defaults, not ie 11" }]
],
"plugins": [
"@babel/plugin-transform-flow-strip-types",
"@babel/plugin-transform-private-methods",
"@babel/plugin-proposal-class-properties"
]
}
+4
View File
@@ -0,0 +1,4 @@
# folders
dist/
# files
+21
View File
@@ -0,0 +1,21 @@
module.exports = {
root: true,
extends: ["thirdweb"],
plugins: ["better-tree-shaking"],
rules: {
"better-tree-shaking/no-top-level-side-effects": "error",
"no-restricted-imports": [
"error",
{
paths: [
{
name: "eventemitter3",
importNames: ["EventEmitter"],
message:
"Do not use named import for importing EventEmitter, Use default import instead.",
},
],
},
],
},
};
+18
View File
@@ -0,0 +1,18 @@
# @thirdweb-dev/payments
## 1.0.1
### Patch Changes
- [#2115](https://github.com/thirdweb-dev/js/pull/2115) [`d00ca7fd`](https://github.com/thirdweb-dev/js/commit/d00ca7fd2a0fa3e694bb37d1f486d3d4cf1ad79e) Thanks [@IDubuque](https://github.com/IDubuque)! - Update exports with new payments interface
## 1.0.0
### Major Changes
- [#1913](https://github.com/thirdweb-dev/js/pull/1913) [`ed3afd41`](https://github.com/thirdweb-dev/js/commit/ed3afd418cb6e0d711814a1c52afb302460751ba) Thanks [@IDubuque](https://github.com/IDubuque)! - The `@thirdweb-dev/payments` package is a continuation of the `@paperxyz/js-client-sdk`, aligning with the new branding and expanded capabilities under thirdweb. It inherits the core functionality of Paper's developer-first NFT checkout solution.
### Changed
- Rebranded package from `@paperxyz/js-client-sdk` to `@thirdweb-dev/payments`. Users of the old package should migrate to this new package name.
- Changed PaperSdkError typename to PaymentsSdkError
+14
View File
@@ -0,0 +1,14 @@
<p align="center">
<br />
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
<br />
</p>
<h1 align="center">thirdweb Checkouts SDK</h1>
<p align="center">
<a href="https://www.npmjs.com/package/@thirdweb-dev/payments"><img src="https://img.shields.io/npm/v/@thirdweb-dev/payments?color=red&label=npm&logo=npm" alt="npm version"/></a>
<a href="https://github.com/thirdweb-dev/js/actions/workflows/build-test-lint.yml"><img alt="Build Status" src="https://github.com/thirdweb-dev/js/actions/workflows/build-test-lint.yml/badge.svg"/></a>
<a href="https://discord.gg/thirdweb"><img alt="Join our Discord!" src="https://img.shields.io/discord/834227967404146718.svg?color=7289da&label=discord&logo=discord&style=flat"/></a>
</p>
<p align="center"><strong>web3 Checkouts library to integrate any NFT checkout into your applications.</strong></p>
<br />
+58
View File
@@ -0,0 +1,58 @@
{
"name": "@thirdweb-dev/payments",
"version": "1.0.1",
"main": "dist/thirdweb-dev-payments.cjs.js",
"module": "dist/thirdweb-dev-payments.esm.js",
"types": "dist/thirdweb-dev-payments.cjs.d.ts",
"browser": {
"./dist/thirdweb-dev-payments.esm.js": "./dist/thirdweb-dev-payments.browser.esm.js"
},
"exports": {
".": {
"module": {
"browser": "./dist/thirdweb-dev-payments.browser.esm.js",
"default": "./dist/thirdweb-dev-payments.esm.js"
},
"default": "./dist/thirdweb-dev-payments.cjs.js"
},
"./package.json": "./package.json"
},
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/payments",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
"author": "thirdweb eng <[email protected]>",
"files": [
"dist/"
],
"preconstruct": {
"entrypoints": [
"index.ts"
],
"exports": {
"envConditions": [
"browser"
]
}
},
"sideEffects": false,
"dependencies": {
"ethers": "^5.7.2"
},
"devDependencies": {
"@thirdweb-dev/tsconfig": "workspace:*",
"eslint-config-thirdweb": "workspace:*",
"typescript": "^5.1.6",
"@preconstruct/cli": "2.7.0"
},
"scripts": {
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"build": "tsc && preconstruct build",
"preconstruct:fix": "preconstruct fix",
"push": "yalc push"
}
}
@@ -0,0 +1,56 @@
// UNCHANGED: MERGED FROM sdk-common-utilities/blockchain
export const ChainToPublicRpc: Record<Chain, string> = {
Ethereum: "https://ethereum.rpc.thirdweb.com",
Goerli: "https://goerli.rpc.thirdweb.com",
Mumbai: "https://mumbai.rpc.thirdweb.com",
Polygon: "https://polygon.rpc.thirdweb.com",
Avalanche: "https://avalanche.rpc.thirdweb.com",
Optimism: "https://optimism.rpc.thirdweb.com",
OptimismGoerli: "https://optimism-goerli.rpc.thirdweb.com",
BSC: "https://binance.rpc.thirdweb.com",
BSCTestnet: "https://binance-testnet.rpc.thirdweb.com",
ArbitrumOne: "https://arbitrum.rpc.thirdweb.com",
ArbitrumGoerli: "https://arbitrum-goerli.rpc.thirdweb.com",
Fantom: "https://fantom.rpc.thirdweb.com",
FantomTestnet: "https://fantom-testnet.rpc.thirdweb.com",
Sepolia: "https://sepolia.rpc.thirdweb.com",
AvalancheFuji: "https://avalanche-fuji.rpc.thirdweb.com",
};
export const ChainIdToChain: Record<number, Chain> = {
1: "Ethereum",
5: "Goerli",
80001: "Mumbai",
137: "Polygon",
43114: "Avalanche",
10: "Optimism",
420: "OptimismGoerli",
56: "BSC",
97: "BSCTestnet",
42161: "ArbitrumOne",
421613: "ArbitrumGoerli",
250: "Fantom",
4002: "FantomTestnet",
11155111: "Sepolia",
43113: "AvalancheFuji",
};
// General Embedded wallet types
export type Chain =
| "Polygon"
| "Mumbai"
| "Goerli"
| "Ethereum"
| "Avalanche"
| "Optimism"
| "OptimismGoerli"
| "BSC"
| "BSCTestnet"
| "ArbitrumOne"
| "ArbitrumGoerli"
| "Fantom"
| "FantomTestnet"
| "Sepolia"
| "AvalancheFuji";
export type SupportedChainName = Chain | "Rinkeby";
@@ -0,0 +1,12 @@
// UNCHANGED: MERGED FROM sdk-common-utilities/iframeMessages
export type MessageType<T> =
| {
eventType: string;
success: true;
data: T;
}
| {
eventType: string;
success: false;
error: string;
};
+4
View File
@@ -0,0 +1,4 @@
// CHANGED: export merged sdk-common-utilities/constants & js-client-sdk/constants
export * from "./settings";
export * from "./blockchain";
export * from "./style";
@@ -0,0 +1,43 @@
// UNCHANGED: MERGE BETWEEN sdk-common-utilities/settings and js-client-sdk/settings
const isDev = (): boolean => {
return !!(
typeof window !== "undefined" &&
window.localStorage.getItem("IS_PAPER_DEV") === "true"
);
};
const isOldPaperDomain = (): boolean =>
typeof window !== "undefined" &&
(window.location.hostname === "paper.xyz" ||
window.location.hostname.endsWith(".paper.xyz"));
const isThirdwebDomain = (): boolean =>
typeof window !== "undefined" &&
(window.location.hostname === "thirdweb.com" ||
window.location.hostname.endsWith(".thirdweb.com"));
export const getPaperOriginUrl = (): string => {
if (isDev()) {
return (
window.localStorage.getItem("PAPER_DEV_URL") ?? "http://localhost:3000"
);
}
if (isOldPaperDomain()) {
return window.location.origin;
}
if (isThirdwebDomain()) {
return window.location.origin;
}
return "https://payments.thirdweb.com";
};
// eslint-disable-next-line better-tree-shaking/no-top-level-side-effects
export const PAPER_APP_URL = getPaperOriginUrl();
export const CHECKOUT_WITH_ETH_IFRAME_URL = "/sdk/2022-08-12/checkout-with-eth";
export const CHECKOUT_WITH_CARD_IFRAME_URL =
"/sdk/2022-08-12/checkout-with-card";
export const CREATE_WALLET_IFRAME_URL = "/sdk/v2/verify-email";
+25
View File
@@ -0,0 +1,25 @@
// // UNCHANGED: MERGED FROM sdk-common-utilities/style
export type StyleObject = Partial<CSSStyleDeclaration>;
export interface ICustomizationOptions {
colorPrimary?: string;
colorBackground?: string;
colorText?: string;
borderRadius?: number;
fontFamily?: string;
inputBackgroundColor?: string;
inputBorderColor?: string;
}
export type CustomizationOptionsType = {
[key in keyof ICustomizationOptions]: string;
};
export type Locale = "en" | "fr" | "es" | "it" | "de" | "ja" | "ko" | "zh";
export const DEFAULT_BRAND_OPTIONS = {
colorPrimary: "#cf3781",
colorBackground: "#ffffff",
colorText: "#1a202c",
borderRadius: 12,
fontFamily: "Open Sans",
};
+8
View File
@@ -0,0 +1,8 @@
// CHANGED: exports
// Customization options and base urls
export * from "./constants";
// Types
export * from "./interfaces";
// Components
export * from "./lib";
@@ -0,0 +1,67 @@
// UNCHANGED
export interface ReviewResult {
/**
* A unique ID for this purchase.
*/
id: string;
/**
* The cardholder's full name provided by the buyer.
*/
cardholderName: string;
}
export interface KycModal {
iframeLink: string;
}
/**
* The ICheckoutWithCardConfigs interface defines the configuration options for the checkoutWithCard component provided by Paper.
* This component allows users to generate a card payment component for NFTs without needing any backend calls.
*
* The configuration options include:
*
* - `contractId`: The id of your contract (not the address) found in you seller dashboard.
* - `walletAddress`: The wallet address to which the payment will be sent (AKA the receiving wallet).
* - `email`: (Optional) Email address of the buyer.
* - `quantity`: (Optional) The number of items to be purchased.
* - `metadata`: (Optional) A record of additional information about the transaction.
* - `mintMethod`: (Optional, Only required for Custom Contract Types) The contract function name, function args, and payment information which will be sent to your contract.
* - `contractArgs`: (Optional) Depending on the contract type, this can defined additional args to call your contract with. See [Thirdweb/Reservoir docs](https://docs.withpaper.com/reference/thirdweb-contracts) for more details.
* - `feeBearer`: (Optional) Who will bear the transaction fee, either 'BUYER' or 'SELLER'.
* - `capturePaymentLater`: (Optional) If set to true, the payment is authorized but not captured immediately.
* - `fiatCurrency`: (Optional) The type of fiat currency for the payment.
* - `title`: (Optional) The title to be displayed on the Stripe receipt.
* - `sendEmailOnTransferSucceeded`: (Optional) If set to true, an email is sent when the transfer succeeds.
* - `postPurchaseMessageMarkdown`: (Optional) A post-purchase message in Markdown format.
* - `postPurchaseButtonText`: (Optional) The text for the post-purchase button.
* - `successCallbackUrl`: (Optional) The URL to be called back when the transaction is successful.
*
* Note: This interface is part of an open-source npm package. Please ensure you understand the implications of each field before using this interface.
*/
export interface ICheckoutWithCardConfigs {
contractId: string;
walletAddress: string;
email?: string;
quantity?: number;
mintMethod?: {
name: string;
args: Record<string, any>;
payment: { value: string; currency: string };
};
contractArgs?: Record<string, any>;
// payment customizations
capturePaymentLater?: boolean;
fiatCurrency?: string;
// stripe receipt
title?: string;
// email
sendEmailOnTransferSucceeded?: boolean;
postPurchaseMessageMarkdown?: string;
postPurchaseButtonText?: string;
successCallbackUrl?: string;
}
+17
View File
@@ -0,0 +1,17 @@
// UNCHANGED
import type { StyleObject } from "../constants/style";
export interface ModalStyles {
main: StyleObject;
overlay: StyleObject;
body: StyleObject;
iframe: StyleObject;
closeButton?: StyleObject;
spinner: StyleObject;
}
export interface ModalInterface {
modalContainer?: HTMLElement;
modalStyles?: Partial<ModalStyles>;
}
@@ -0,0 +1,19 @@
// UNCHANGED
export type PaperUser = {
/**
* The user's email address.
* This address is case-insensitive (i.e. different capitalizations map to the same wallet).
*/
emailAddress: string;
/**
* The Paper Wallet address associated with this user's email address.
*/
walletAddress: string;
/**
* The user's access code. Will only be set if [clientId] was provided. This can be used to query about the user details.
*/
accessCode?: string;
};
@@ -0,0 +1,31 @@
// CHANGED: Updated name to Payments
export type PaymentsSDKError = {
/**
* An enum representing the error encountered.
* The value is a human-readable, English message describing the error.
*/
code: PaymentsSDKErrorCode | PayWithCryptoErrorCode;
error: Error;
};
export enum PaymentsSDKErrorCode {
UserAbandonedCheckout = "User abandoned the checkout",
UserLoginFailed = "User login failed",
InvalidProps = "The props you passed in to this component are not valid.",
InvalidCard = "The card information is invalid. Please double check that the Card, CVC, and Zip code are all correct.",
EmailNotVerified = "The email was unable to be verified.",
NotEnoughSupply = "There is not enough supply to claim.",
AddressNotAllowed = "This address is not on the allowlist.",
NoActiveClaimPhase = "There is no active claim phase at the moment.",
}
export enum PayWithCryptoErrorCode {
ErrorConnectingToWallet = "Error connecting to wallet",
ErrorSendingTransaction = "Something went wrong sending transaction",
InsufficientBalance = `Insufficient ETH`,
TransactionCancelled = "Transaction Cancelled",
WrongChain = `Wrong Chain Detected`,
ChainSwitchUnderway = "There is a network switch already underway",
PendingSignature = "Pending Signature",
}
@@ -0,0 +1,28 @@
// UNCHANGED
export interface PriceSummary {
quantity: number;
unitPrice: PriceDetail;
networkFees: PriceDetail;
serviceFees: PriceDetail;
total: PriceDetail;
cryptoToFiatConversionRate?: number;
}
export interface PriceDetail {
/**
* A human-readable string to display.
* Example: $123.45 CAD
*/
display: string;
/**
* This is the value in smallest units (cents, yen, wei).
* Example: 12345
*/
valueInSubunits: number;
/**
* The currency associated with the value.
* Example: USD, ETH
*/
currency: string;
}
@@ -0,0 +1,5 @@
// CHANGED: Added exports
export * from "./CheckoutWithCard";
export * from "./PaymentsSdkError";
export * from "./PaperUser";
export * from "./PriceSummary";
@@ -0,0 +1,256 @@
// CHANGED: Added clientId to interface
import type { ICustomizationOptions, Locale } from "../constants/style";
import { DEFAULT_BRAND_OPTIONS } from "../constants/style";
import { getPaperOriginUrl } from "../constants/settings";
import {
CHECKOUT_WITH_CARD_IFRAME_URL,
PAPER_APP_URL,
} from "../constants/settings";
import type {
ICheckoutWithCardConfigs,
KycModal,
ReviewResult,
} from "../interfaces/CheckoutWithCard";
import type {
PaymentsSDKError,
PaymentsSDKErrorCode,
} from "../interfaces/PaymentsSdkError";
import type { PriceSummary } from "../interfaces/PriceSummary";
import { LinksManager } from "../utils/LinksManager";
import { openCenteredPopup } from "../utils/device";
import { postMessageToIframe } from "../utils/postMessageToIframe";
import type { PaperPaymentElementConstructorArgs } from "./CreatePaymentElement";
import { PaperPaymentElement } from "./CreatePaymentElement";
import { Modal } from "./Modal";
export interface CheckoutWithCardLinkArgs {
clientId?: string;
sdkClientSecret?: string;
appName?: string;
options?: ICustomizationOptions;
locale?: Locale;
configs?: ICheckoutWithCardConfigs;
/**
* @deprecated: No longer used. Domain is set to "withpaper.com".
*/
useAltDomain?: boolean;
}
export function createCheckoutWithCardLink({
clientId,
sdkClientSecret,
appName,
options = { ...DEFAULT_BRAND_OPTIONS },
locale,
configs,
}: CheckoutWithCardLinkArgs): URL {
const CheckoutWithCardUrlBase = new URL(
CHECKOUT_WITH_CARD_IFRAME_URL,
PAPER_APP_URL,
);
let clientSecret = sdkClientSecret;
if (!clientSecret && configs) {
clientSecret = btoa(JSON.stringify(configs));
}
if (!clientSecret) {
const error = `Must have either sdkClientSecret or configs field set. Received neither`;
const destination = `/error?errorMessage=${error}`;
const domain = getPaperOriginUrl();
return new URL(destination, domain);
}
if (!clientId) {
const error = `Must have clientId field set. Please add clientId`;
const destination = `/error?errorMessage=${error}`;
const domain = getPaperOriginUrl();
return new URL(destination, domain);
}
const checkoutWithCardLink = new LinksManager(CheckoutWithCardUrlBase);
checkoutWithCardLink.addClientId(clientId ?? "");
checkoutWithCardLink.addClientSecret(clientSecret ?? "");
checkoutWithCardLink.addStylingOptions(options);
checkoutWithCardLink.addLocale(locale);
checkoutWithCardLink.addAppName(appName);
return checkoutWithCardLink.getLink();
}
export interface CheckoutWithCardMessageHandlerArgs {
iframe: HTMLIFrameElement;
onPaymentSuccess?: (props: {
transactionId: string;
/** @deprecated */
id: string;
}) => void;
onReview?: (result: ReviewResult) => void;
onError?: (error: PaymentsSDKError) => void;
onOpenKycModal?: (props: KycModal) => void;
onCloseKycModal?: () => void;
onBeforeModalOpen?: (props: { url: string }) => void;
onPriceUpdate?: (props: PriceSummary) => void;
useAltDomain?: boolean;
}
export function createCheckoutWithCardMessageHandler({
iframe,
onError,
onReview,
onPaymentSuccess,
onBeforeModalOpen,
onPriceUpdate,
}: CheckoutWithCardMessageHandlerArgs) {
let modal: Modal;
return (event: MessageEvent) => {
if (!event.origin.startsWith(PAPER_APP_URL)) {
return;
}
const { data } = event;
switch (data.eventType) {
case "checkoutWithCardError":
if (onError) {
onError({
code: data.code as PaymentsSDKErrorCode,
error: data.error,
});
}
break;
case "paymentSuccess":
if (onPaymentSuccess) {
onPaymentSuccess({ transactionId: data.id, id: data.id });
}
if (data.postToIframe) {
postMessageToIframe(iframe, data.eventType, data);
}
break;
case "reviewComplete":
if (onReview) {
onReview({
id: data.id,
cardholderName: data.cardholderName,
});
}
break;
case "openModalWithUrl":
if (
onBeforeModalOpen &&
data.url &&
data.url.includes("promptKYCModal")
) {
onBeforeModalOpen({
url: data.url,
});
} else {
modal = new Modal(undefined, {
body: {
colorScheme: "light",
},
});
modal.open({ iframeUrl: data.url });
}
break;
case "completedSDKModal":
modal.close();
if (data.postToIframe) {
postMessageToIframe(iframe, data.eventType, data);
}
break;
case "requestedPopup": {
// The iframe requested a popup.
// The reference to this window is not stored so the popup cannot
// be programmatically closed.
const popupRef = openCenteredPopup({
url: data.url,
width: data.width,
height: data.height,
});
if (!popupRef) {
console.error("CheckoutWithCard: Unable to open popup.");
}
break;
}
case "sizing":
iframe.style.height = data.height + "px";
iframe.style.maxHeight = data.height + "px";
break;
case "onPriceUpdate": {
onPriceUpdate?.(data);
break;
}
default:
// Ignore unrecognized event
}
};
}
export type CheckoutWithCardElementArgs = Omit<
CheckoutWithCardMessageHandlerArgs,
"iframe"
> &
CheckoutWithCardLinkArgs &
PaperPaymentElementConstructorArgs;
export function createCheckoutWithCardElement({
clientId,
onCloseKycModal,
onOpenKycModal,
sdkClientSecret,
appName,
elementOrId,
onLoad,
onError,
locale,
options,
onPaymentSuccess,
onReview,
onBeforeModalOpen,
onPriceUpdate,
useAltDomain = true,
configs,
}: CheckoutWithCardElementArgs) {
const checkoutWithCardId = "checkout-with-card-iframe";
const checkoutWithCardMessageHandler = (iframe: HTMLIFrameElement) =>
createCheckoutWithCardMessageHandler({
iframe,
onCloseKycModal,
onOpenKycModal,
onError,
onPaymentSuccess,
onReview,
onBeforeModalOpen,
onPriceUpdate,
useAltDomain,
});
const checkoutWithCardUrl = createCheckoutWithCardLink({
clientId,
sdkClientSecret,
appName,
locale,
options,
useAltDomain,
configs,
});
const paymentElement = new PaperPaymentElement({
onLoad,
elementOrId,
});
return paymentElement.createPaymentElement({
handler: checkoutWithCardMessageHandler,
iframeId: checkoutWithCardId,
link: checkoutWithCardUrl,
});
}
@@ -0,0 +1,334 @@
// CHANGED: Imports only
import type { ethers } from "ethers";
import {
CHECKOUT_WITH_ETH_IFRAME_URL,
PAPER_APP_URL,
getPaperOriginUrl,
} from "../constants/settings";
import type { ICustomizationOptions, Locale } from "../constants/style";
import { DEFAULT_BRAND_OPTIONS } from "../constants/style";
import type { PaymentsSDKError } from "../interfaces/PaymentsSdkError";
import { PayWithCryptoErrorCode } from "../interfaces/PaymentsSdkError";
import type { PriceSummary } from "../interfaces/PriceSummary";
import { LinksManager } from "../utils/LinksManager";
import { handlePayWithCryptoError } from "../utils/handleCheckoutWithEthError";
import { postMessageToIframe } from "../utils/postMessageToIframe";
import type { PaperPaymentElementConstructorArgs } from "./CreatePaymentElement";
import { PaperPaymentElement } from "./CreatePaymentElement";
export const PAY_WITH_ETH_ERROR = "payWithEthError";
export async function checkAndSendEth({
data,
iframe,
payingWalletSigner,
suppressErrorToast,
onError,
onPaymentSuccess,
}: {
payingWalletSigner: ethers.Signer;
data: {
chainId: number;
chainName: string;
blob: string;
paymentAddress: string;
value: string;
transactionId: string;
};
suppressErrorToast: boolean;
iframe: HTMLIFrameElement;
onPaymentSuccess?:
| CheckoutWithEthMessageHandlerArgs["onPaymentSuccess"]
| CheckoutWithEthMessageHandlerArgs["onSuccess"];
onError?: (error: PaymentsSDKError) => Promise<void> | void;
}) {
try {
const chainId = await payingWalletSigner.getChainId();
if (chainId !== data.chainId) {
throw {
isErrorObject: true,
title: PayWithCryptoErrorCode.WrongChain,
description: `Please change to ${data.chainName} to proceed.`,
};
}
} catch (e) {
await handlePayWithCryptoError(e as Error, onError, (errorObject) => {
postMessageToIframe(iframe, PAY_WITH_ETH_ERROR, {
error: errorObject,
suppressErrorToast,
});
});
return;
}
// send the transaction
try {
console.log("sending funds...", data);
const result = await payingWalletSigner.sendTransaction({
chainId: data.chainId,
data: data.blob,
to: data.paymentAddress,
value: data.value,
});
const receipt = await result.wait();
if (onPaymentSuccess && result) {
await onPaymentSuccess({
onChainTxResponse: result,
onChainTxReceipt: receipt,
transactionId: data.transactionId,
});
}
if (result) {
postMessageToIframe(iframe, "paymentSuccess", {
suppressErrorToast,
transactionHash: result.hash,
});
}
} catch (error) {
console.log("error sending funds", error);
await handlePayWithCryptoError(error as Error, onError, (errorObject) => {
postMessageToIframe(iframe, PAY_WITH_ETH_ERROR, {
error: errorObject,
suppressErrorToast,
});
});
}
}
export interface CheckoutWithEthMessageHandlerArgs {
iframe: HTMLIFrameElement;
onPaymentSuccess?: (props: {
onChainTxReceipt: ethers.providers.TransactionReceipt;
transactionId: string;
}) => Promise<void> | void;
/** @deprecated */
onSuccess?: (props: {
onChainTxResponse: ethers.providers.TransactionResponse;
transactionId: string;
}) => Promise<void> | void;
onPriceUpdate?: (props: PriceSummary) => void;
onError?: (error: PaymentsSDKError) => void;
suppressErrorToast?: boolean;
setUpUserPayingWalletSigner?: (args: {
chainId: number;
chainName?: string;
}) => void | Promise<void>;
payingWalletSigner: ethers.Signer;
}
export function createCheckoutWithEthMessageHandler({
iframe,
onError,
onPaymentSuccess,
onPriceUpdate,
payingWalletSigner,
suppressErrorToast = false,
setUpUserPayingWalletSigner,
}: CheckoutWithEthMessageHandlerArgs) {
return async (event: MessageEvent) => {
if (!event.origin.startsWith(PAPER_APP_URL)) {
return;
}
const data = event.data;
switch (data.eventType) {
case "payWithEth": {
if (data.error) {
await handlePayWithCryptoError(
new Error(data.error),
onError,
(errorObject) => {
postMessageToIframe(iframe, PAY_WITH_ETH_ERROR, {
error: errorObject,
suppressErrorToast,
});
},
);
return;
}
// Allows Dev's to inject any chain switching for their custom signer here.
if (setUpUserPayingWalletSigner) {
try {
console.log("setting up signer");
await setUpUserPayingWalletSigner({
chainId: data.chainId,
chainName: data.chainName,
});
} catch (error) {
console.log("error setting up signer", error);
await handlePayWithCryptoError(
error as Error,
onError,
(errorObject) => {
postMessageToIframe(iframe, PAY_WITH_ETH_ERROR, {
error: errorObject,
suppressErrorToast,
});
},
);
return;
}
}
await checkAndSendEth({
data,
iframe,
payingWalletSigner,
suppressErrorToast,
onError,
onPaymentSuccess,
});
break;
}
case "checkout-with-eth-sizing": {
iframe.style.height = data.height + "px";
iframe.style.maxHeight = data.height + "px";
break;
}
case "onPriceUpdate": {
onPriceUpdate?.(data as PriceSummary);
break;
}
default:
break;
}
};
}
export interface ICheckoutWithEthConfigs {
contractId: string;
walletAddress: string;
email?: string;
quantity?: number;
mintMethod?: {
name: string;
args: Record<string, any>[];
payment: { value: string; currency: string };
};
contractArgs?: Record<string, any>;
// payment customizations
capturePaymentLater?: boolean;
fiatCurrency?: string;
// stripe receipt
title?: string;
// email
sendEmailOnTransferSucceeded?: boolean;
postPurchaseMessageMarkdown?: string;
postPurchaseButtonText?: string;
successCallbackUrl?: string;
}
export interface CheckoutWithEthLinkArgs {
sdkClientSecret?: string;
appName?: string;
payingWalletSigner: ethers.Signer;
receivingWalletType?:
| "WalletConnect"
| "MetaMask"
| "Coinbase Wallet"
| string;
showConnectWalletOptions?: boolean;
locale?: Locale;
options?: ICustomizationOptions;
configs?: ICheckoutWithEthConfigs;
}
export async function createCheckoutWithEthLink({
sdkClientSecret,
payingWalletSigner,
receivingWalletType,
showConnectWalletOptions = false,
appName,
locale,
options = {
...DEFAULT_BRAND_OPTIONS,
},
configs,
}: CheckoutWithEthLinkArgs) {
const checkoutWithEthUrlBase = new URL(
CHECKOUT_WITH_ETH_IFRAME_URL,
PAPER_APP_URL,
);
const address = await payingWalletSigner.getAddress();
let clientSecret = sdkClientSecret;
if (!clientSecret && configs) {
clientSecret = btoa(JSON.stringify(configs));
}
if (!clientSecret) {
const error = `Must have either sdkClientSecret or configs field set. Received neither`;
const destination = `/error?errorMessage=${error}`;
const domain = getPaperOriginUrl();
return new URL(destination, domain);
}
const checkoutWithEthLink = new LinksManager(checkoutWithEthUrlBase);
checkoutWithEthLink.addClientSecret(clientSecret ?? "");
checkoutWithEthLink.addRecipientWalletAddress(address);
checkoutWithEthLink.addPayerWalletAddress(address);
checkoutWithEthLink.addReceivingWalletType(receivingWalletType);
checkoutWithEthLink.addAppName(appName);
checkoutWithEthLink.addShowConnectWalletOptions(showConnectWalletOptions);
checkoutWithEthLink.addStylingOptions(options);
checkoutWithEthLink.addLocale(locale);
return checkoutWithEthLink.getLink();
}
export type CheckoutWithEthElementArgs = Omit<
Omit<CheckoutWithEthMessageHandlerArgs, "iframe">,
"setUpUserPayingWalletSigner"
> &
CheckoutWithEthLinkArgs &
PaperPaymentElementConstructorArgs;
export async function createCheckoutWithEthElement({
sdkClientSecret,
suppressErrorToast,
onError,
onLoad,
payingWalletSigner,
receivingWalletType,
appName,
showConnectWalletOptions,
locale,
options,
elementOrId,
onPaymentSuccess: _onPaymentSuccess,
onSuccess,
}: CheckoutWithEthElementArgs): Promise<HTMLIFrameElement> {
const onPaymentSuccess =
_onPaymentSuccess ??
(onSuccess as CheckoutWithEthMessageHandlerArgs["onPaymentSuccess"]);
const checkoutWithEthId = "checkout-with-eth-iframe";
const checkoutWithEthMessageHandler = (iframe: HTMLIFrameElement) =>
createCheckoutWithEthMessageHandler({
iframe,
onError,
payingWalletSigner,
suppressErrorToast,
onPaymentSuccess,
});
const checkoutWithEthUrl = await createCheckoutWithEthLink({
payingWalletSigner,
sdkClientSecret,
appName,
locale,
options,
receivingWalletType,
showConnectWalletOptions,
});
const paymentElement = new PaperPaymentElement({
onLoad,
elementOrId,
});
return paymentElement.createPaymentElement({
handler: checkoutWithEthMessageHandler,
iframeId: checkoutWithEthId,
link: checkoutWithEthUrl,
});
}
@@ -0,0 +1,76 @@
// CHANGED: packageJson import + version string
// eslint-disable-next-line @typescript-eslint/no-var-requires, better-tree-shaking/no-top-level-side-effects
const packageJson = require("../package.json");
export interface PaperPaymentElementConstructorArgs {
onLoad?: (event?: Event) => void;
elementOrId?: string | HTMLElement;
}
export class PaperPaymentElement {
private elementOrId?: PaperPaymentElementConstructorArgs["elementOrId"];
private onLoad?: PaperPaymentElementConstructorArgs["onLoad"];
constructor({ elementOrId, onLoad }: PaperPaymentElementConstructorArgs) {
this.elementOrId = elementOrId;
this.onLoad = onLoad;
}
createPaymentElement({
handler,
link,
iframeId,
}: {
handler: (
iframe: HTMLIFrameElement,
) => (event: MessageEvent) => void | Promise<void>;
link: URL;
iframeId: string;
}) {
const iframe = document.createElement("iframe");
iframe.src = link.href;
iframe.id = iframeId;
iframe.allow = "payment";
iframe.setAttribute(
"style",
"margin-left:auto; margin-right:auto; width:100%; height: 100%; min-height:375px; transition-property:all; transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1); transition-duration:150ms; color-scheme: light;",
);
iframe.onload = (event: Event) => {
if (this.onLoad) {
this.onLoad(event);
}
};
iframe.setAttribute(
"data-thirdweb-sdk-version",
`${packageJson.name}@${packageJson.version}`,
);
if (!this.elementOrId) {
window.addEventListener("message", handler(iframe));
return iframe;
}
let container: HTMLElement | string = this.elementOrId;
if (typeof container === "string") {
const domElement = document.getElementById(container);
if (!domElement) {
throw new Error("Invalid id given");
}
container = domElement;
}
const existing: HTMLIFrameElement | null = container.querySelector(
"#" + iframeId,
);
// if we already created an iframe, consider updating the iframe link if it's new
if (existing) {
if (existing.src === link.href) {
return existing;
}
existing.src = link.href;
return existing;
}
window.addEventListener("message", handler(iframe));
return container.appendChild(iframe);
}
}
+146
View File
@@ -0,0 +1,146 @@
import type { StyleObject } from "../../constants/style";
import type { ModalStyles } from "../../interfaces/Modal";
import { getDefaultModalStyles } from "./styles";
import packageJson from "../../../package.json";
const MAIN_CLASSNAME = "paper--drawer-main";
const OVERLAY_CLASSNAME = "paper--drawer-overlay";
const BODY_CLASSNAME = "paper--drawer-body";
const CLOSE_CLASSNAME = "paper--drawer-close";
export class Drawer {
protected container: HTMLElement;
protected main: HTMLDivElement;
protected overlay: HTMLDivElement;
protected closeButton: HTMLButtonElement;
protected iframe: HTMLIFrameElement;
protected onCloseCallback: (() => void) | undefined;
protected closeTimeout: number | undefined;
// eslint-disable-next-line better-tree-shaking/no-top-level-side-effects
styles = getDefaultModalStyles();
body: HTMLDivElement;
constructor(container?: HTMLElement, styles?: Partial<ModalStyles>) {
this.container = container || document.body;
if (styles) {
this.mergeStyles(styles);
}
this.main = document.createElement("div");
this.main.className = MAIN_CLASSNAME;
this.overlay = document.createElement("div");
this.overlay.className = OVERLAY_CLASSNAME;
this.body = document.createElement("div");
this.body.className = BODY_CLASSNAME;
this.closeButton = document.createElement("button");
this.closeButton.className = CLOSE_CLASSNAME;
this.closeButton.innerHTML = "&#x2715;";
this.closeButton.onclick = () => {
this.close();
};
this.iframe = document.createElement("iframe");
this.iframe.allow = "camera; microphone; payment";
this.iframe.setAttribute(
"data-thirdweb-sdk-version",
`${packageJson.name}@${packageJson.version}`,
);
this.assignStyles(this.main, this.styles.main);
this.assignStyles(this.overlay, this.styles.overlay);
this.assignStyles(this.body, this.styles.body);
this.assignStyles(this.iframe, this.styles.iframe);
if (this.styles.closeButton) {
this.assignStyles(this.closeButton, this.styles.closeButton);
}
}
open({ iframeUrl }: { iframeUrl?: string } = {}) {
if (iframeUrl) {
this.iframe.src = iframeUrl;
this.body.appendChild(this.iframe);
}
this.addAccessibility();
this.main.appendChild(this.overlay);
this.main.appendChild(this.body);
this.main.appendChild(this.closeButton);
this.container.appendChild(this.main);
document.body.style.overflow = "hidden";
// Animate in.
this.overlay.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
this.body.style.right = "0px";
this.body.style.opacity = "1";
return this.iframe;
}
close() {
this.closeButton.remove();
// Animate out.
this.overlay.style.backgroundColor = "rgba(0, 0, 0, 0)";
this.body.style.right = "-100px";
this.body.style.opacity = "0";
// Remove drawer from DOM.
this.closeTimeout = window.setTimeout(() => {
document.body.style.overflow = "visible";
this.main.remove();
window.clearTimeout(this.closeTimeout);
this.onCloseCallback?.();
}, 250);
}
setOnCloseCallback(callback: () => void) {
this.onCloseCallback = callback;
}
protected mergeStyles(styles: Partial<ModalStyles>) {
this.styles.body = {
...this.styles.body,
...(styles.body || {}),
};
this.styles.overlay = {
...this.styles.overlay,
...(styles.overlay || {}),
};
this.styles.main = {
...this.styles.main,
...(styles.main || {}),
};
this.styles.iframe = {
...this.styles.iframe,
...(styles.iframe || {}),
};
this.styles.closeButton = {
...this.styles.closeButton,
...(styles.closeButton || {}),
};
}
protected addAccessibility() {
this.main.setAttribute("aria-hidden", "true");
this.overlay.setAttribute("aria-hidden", "true");
this.body.setAttribute("aria-modal", "true");
this.body.setAttribute("role", "dialog");
}
protected assignStyles(el: HTMLElement, styles: StyleObject) {
Object.assign(el.style, styles);
}
}
@@ -0,0 +1,67 @@
import type { ModalStyles } from "../../interfaces/Modal";
const fullScreen = {
position: "fixed",
top: "0px",
left: "0px",
right: "0px",
bottom: "0px",
};
export const getDefaultModalStyles = (): ModalStyles => ({
main: {
...fullScreen,
zIndex: "2147483646",
display: "flex",
alignItems: "center",
justifyContent: "end",
},
overlay: {
...fullScreen,
backgroundColor: "rgba(0, 0, 0, 0)",
transition: "background-color ease-out 0.2s",
},
body: {
position: "fixed",
width: "100%",
maxWidth: "420px",
top: "0px",
bottom: "0px",
right: "-100px",
opacity: "0",
boxShadow: "0px 10px 20px rgba(0, 0, 0, 0.25)",
backgroundColor: window.matchMedia("(prefers-color-scheme: dark)").matches
? "#2F2F2F"
: "white",
overflow: "hidden",
transition: "all ease-out 0.2s",
},
spinner: {
position: "absolute",
top: "0",
bottom: "0",
left: "0",
right: "0",
margin: "auto",
borderWidth: "3px",
borderColor: "#2D3748 #2D3748 transparent transparent",
borderRadius: "50%",
width: "30px",
height: "30px",
animation: "spin 1s linear infinite",
},
iframe: {
height: "100%",
width: "100%",
border: "none",
backgroundColor: "transparent",
},
closeButton: {
position: "fixed",
cursor: "pointer",
top: "0.75rem",
right: "1rem",
color: "#888",
padding: "2px 8px",
},
});
+116
View File
@@ -0,0 +1,116 @@
import type { StyleObject } from "../../constants/style";
import type { ModalStyles } from "../../interfaces/Modal";
import { getDefaultModalStyles, modalKeyframeAnimations } from "./styles";
import packageJson from "../../../package.json";
export const MODAL_ID = "paper-js-sdk-modal";
export class Modal {
protected container: HTMLElement;
protected main: HTMLDivElement;
protected overlay: HTMLDivElement;
protected iframe: HTMLIFrameElement;
protected spinner: HTMLDivElement;
protected style: HTMLStyleElement;
// eslint-disable-next-line better-tree-shaking/no-top-level-side-effects
styles = getDefaultModalStyles();
body: HTMLDivElement;
constructor(container?: HTMLElement, styles?: Partial<ModalStyles>) {
this.container = container || document.body;
if (styles) {
this.mergeStyles(styles);
}
this.main = document.createElement("div");
this.main.id = MODAL_ID;
this.overlay = document.createElement("div");
this.overlay.id = `${MODAL_ID}-overlay`;
this.body = document.createElement("div");
this.body.id = `${MODAL_ID}-body`;
this.spinner = document.createElement("div");
this.spinner.id = `${MODAL_ID}-spinner`;
this.iframe = document.createElement("iframe");
this.iframe.id = `${MODAL_ID}-iframe`;
this.iframe.allow = "camera; microphone; payment";
this.iframe.setAttribute(
"data-thirdweb-sdk-version",
`${packageJson.name}@${packageJson.version}`,
);
this.style = document.createElement("style");
this.style.innerHTML = modalKeyframeAnimations;
this.assignStyles(this.main, this.styles.main);
this.assignStyles(this.overlay, this.styles.overlay);
this.assignStyles(this.body, this.styles.body);
this.assignStyles(this.spinner, this.styles.spinner);
this.assignStyles(this.iframe, this.styles.iframe);
}
open({ iframeUrl }: { iframeUrl?: string } = {}) {
if (iframeUrl) {
this.body.appendChild(this.spinner);
this.iframe.src = iframeUrl;
// Remove the spinner when the iframe loads.
this.iframe.onload = () => this.body.removeChild(this.spinner);
this.body.appendChild(this.iframe);
}
this.addAccessibility();
this.main.appendChild(this.overlay);
this.main.appendChild(this.style);
this.main.appendChild(this.body);
this.container.appendChild(this.main);
document.body.style.overflow = "hidden";
}
close() {
this.body.style.animation = "pew-modal-slideOut 0.2s forwards";
window.setTimeout(() => this.main.remove(), 250);
}
protected mergeStyles(styles: Partial<ModalStyles>) {
this.styles.body = {
...this.styles.body,
...(styles.body || {}),
};
this.styles.spinner = {
...this.styles.spinner,
...(styles.spinner || {}),
};
this.styles.overlay = {
...this.styles.overlay,
...(styles.overlay || {}),
};
this.styles.main = {
...this.styles.main,
...(styles.main || {}),
};
this.styles.iframe = {
...this.styles.iframe,
...(styles.iframe || {}),
};
}
protected addAccessibility() {
this.main.setAttribute("aria-hidden", "true");
this.overlay.setAttribute("aria-hidden", "true");
this.body.setAttribute("aria-modal", "true");
this.body.setAttribute("role", "dialog");
}
protected assignStyles(el: HTMLElement, styles: StyleObject) {
Object.assign(el.style, styles);
}
}
+75
View File
@@ -0,0 +1,75 @@
import type { ModalStyles } from "../../interfaces/Modal";
const fullScreen = {
top: "0px",
left: "0px",
right: "0px",
bottom: "0px",
};
export const getDefaultModalStyles = (): ModalStyles => ({
main: {
...fullScreen,
position: "fixed",
zIndex: "10000",
display: "flex",
alignItems: "center",
justifyContent: "center",
pointerEvents: "auto",
},
overlay: {
...fullScreen,
position: "absolute",
backgroundColor: "rgba(0, 0, 0, 0.33)",
pointerEvents: "auto",
},
body: {
background: "transparent",
borderRadius: "12px",
position: "relative",
overflow: "hidden",
width: "100%",
maxWidth: "500px",
height: "700px",
maxHeight: "80%",
animation: "pew-modal-slideIn 0.2s forwards",
pointerEvents: "auto",
},
spinner: {
position: "absolute",
top: "0",
bottom: "0",
left: "0",
right: "0",
margin: "auto",
borderWidth: "3px",
borderColor: "#2D3748 #2D3748 transparent transparent",
borderRadius: "50%",
width: "30px",
height: "30px",
animation: "spin 1s linear infinite",
},
iframe: {
position: "relative",
height: "100%",
width: "100%",
border: "none",
background: "transparent",
},
});
export const modalKeyframeAnimations = `
@keyframes pew-modal-slideIn {
from {opacity: 0; transform: translate3d(0, 20px, 0);}
to {opacity: 1; transform: translate3d(0, 0, 0);}
}
@keyframes pew-modal-slideOut {
from {opacity: 1; transform: translate3d(0, 0, 0);}
to {opacity: 0; transform: translate3d(0, 20px, 0);}
}
@keyframes spin {
to { transform: rotate(360deg); }
}
`;
+4
View File
@@ -0,0 +1,4 @@
// CHANGED: ADDED
export * from "./CheckoutWithCard";
export * from "./CheckoutWithEth";
export * from "./renderPaperCheckoutLink";
@@ -0,0 +1,82 @@
// UNCHANGED
import { PAPER_APP_URL } from "../constants/settings";
import { Drawer } from "./Drawer";
async function sleepForSeconds(seconds: number) {
return new Promise((res) => {
setTimeout(() => {
res(0);
}, seconds * 1000);
});
}
export function renderPaperCheckoutLink({
checkoutLinkUrl,
onPaymentSucceeded,
onPaymentFailed,
onTransferSucceeded,
onModalClosed,
}: {
checkoutLinkUrl: string;
onPaymentSucceeded?: ({ transactionId }: { transactionId: string }) => void;
onPaymentFailed?: ({ transactionId }: { transactionId: string }) => void;
onTransferSucceeded?: ({
transactionId,
claimedTokens,
}: {
transactionId: string;
claimedTokens: any;
}) => void;
onModalClosed?: () => void;
}) {
const drawer = new Drawer();
const formattedCheckoutLinkUrl = new URL(checkoutLinkUrl);
formattedCheckoutLinkUrl.searchParams.set("display", "DRAWER");
drawer.open({ iframeUrl: formattedCheckoutLinkUrl.href });
if (onModalClosed) {
drawer.setOnCloseCallback(onModalClosed);
}
const messageHandler = async (e: MessageEvent) => {
if (e.origin !== PAPER_APP_URL) {
return;
}
const result = e.data;
if (!result.eventType) {
return;
}
switch (result.eventType) {
case "paymentSuccess": {
const transactionId = e.data.id;
onPaymentSucceeded?.({ transactionId });
break;
}
case "claimSuccessful": {
const { id: transactionId, claimedTokens } = e.data;
onTransferSucceeded?.({ transactionId, claimedTokens });
await sleepForSeconds(3.5);
drawer.close();
break;
}
case "redirectAfterSuccess": {
const redirectUrl = e.data.redirectUrl;
window.location.assign(redirectUrl);
break;
}
case "paymentFailed": {
const transactionId = e.data.id;
onPaymentFailed?.({ transactionId });
break;
}
case "modalClosed": {
onModalClosed?.();
break;
}
default:
throw new Error(`Unsupported eventType ${result.eventType}`);
}
};
window.addEventListener("message", messageHandler);
}
@@ -0,0 +1,95 @@
// CHANGED: added clientId to link
import type { ICustomizationOptions, Locale } from "../constants/style";
export class LinksManager {
private link: URL;
constructor(baseLink: URL) {
this.link = baseLink;
}
addStylingOptions(options: ICustomizationOptions) {
if (options.colorPrimary) {
this.link.searchParams.set("colorPrimary", options.colorPrimary);
}
if (options.colorBackground) {
this.link.searchParams.set("colorBackground", options.colorBackground);
}
if (options.colorText) {
this.link.searchParams.set("colorText", options.colorText);
}
if (options.borderRadius !== undefined) {
this.link.searchParams.set(
"borderRadius",
options.borderRadius.toString(),
);
}
if (options.fontFamily) {
this.link.searchParams.set("fontFamily", options.fontFamily);
}
if (options.inputBackgroundColor) {
this.link.searchParams.set(
"inputBackgroundColor",
options.inputBackgroundColor,
);
}
if (options.inputBorderColor) {
this.link.searchParams.set("inputBorderColor", options.inputBorderColor);
}
}
addClientSecret(sdkClientSecret: string) {
this.link.searchParams.set("sdkClientSecret", sdkClientSecret);
}
addClientId(clientId: string) {
this.link.searchParams.set("clientId", clientId);
}
addLocale(locale?: Locale) {
if (locale) {
this.link.searchParams.set("locale", locale.toString());
}
}
addOTP() {
this.link.searchParams.set("withOTP", "true");
}
addAppName(appName?: string) {
if (appName) {
this.link.searchParams.set("appName", appName);
}
}
addShowConnectWalletOptions(showConnectWalletOptions: boolean) {
this.link.searchParams.append(
"showConnectWalletOptions",
showConnectWalletOptions.toString(),
);
}
addReceivingWalletType(walletType?: string) {
this.link.searchParams.append("walletType", walletType || "Preset");
}
addRecipientWalletAddress(address: string) {
this.link.searchParams.set("recipientWalletAddress", address);
}
addPayerWalletAddress(address: string) {
this.link.searchParams.append("payerWalletAddress", address);
}
addDate(date?: Date) {
this.link.searchParams.set(
"date",
date ? date.toString() : Date.now().toString(),
);
}
getLink(): URL {
return this.link;
}
}
+29
View File
@@ -0,0 +1,29 @@
// UNCHANGED
/**
* Opens a popup centered on the current window.
* Note that modern browsers may prevent popups from opening automatically,
* so try to handle the case where the window returned is null.
*
* @returns Window | null - The Window object that was opened. If null is returned, the popup failed to open.
*/
export const openCenteredPopup = ({
url,
width = 440,
height = 700,
}: {
url: string;
width?: number;
height?: number;
}): Window | null => {
if (!window?.top) {
return null;
}
const y = window.top.outerHeight / 2 + window.top.screenY - height / 2;
const x = window.top.outerWidth / 2 + window.top.screenX - width / 2;
return window.open(
url,
"_blank",
`popup=true,width=${width},height=${height},top=${y},left=${x}`,
);
};
@@ -0,0 +1,95 @@
// Changed: Updated PaperSDKError
import type { PaymentsSDKError } from "../interfaces/PaymentsSdkError";
import { PayWithCryptoErrorCode } from "../interfaces/PaymentsSdkError";
export interface IErrorObject {
isErrorObject: boolean;
title: PayWithCryptoErrorCode;
description: string;
}
export const handlePayWithCryptoError = async (
error: Error | IErrorObject,
onError?: (code: PaymentsSDKError) => Promise<void> | void,
postToParent?: (errorObject: Omit<IErrorObject, "isErrorObject">) => void,
) => {
if ("isErrorObject" in error) {
if (onError) {
await onError({ code: error.title, error: new Error(error.title) });
}
if (postToParent) {
postToParent({ ...error });
}
} else if (!("message" in error)) {
if (onError) {
await onError({
code: PayWithCryptoErrorCode.ErrorSendingTransaction,
error: new Error(JSON.stringify(error)),
});
}
if (postToParent) {
postToParent({
description: `${error}`,
title: PayWithCryptoErrorCode.ErrorSendingTransaction,
});
}
} else {
if (
error.message.includes("rejected") ||
error.message.includes("denied transaction")
) {
if (onError) {
await onError({
code: PayWithCryptoErrorCode.TransactionCancelled,
error,
});
}
if (postToParent) {
postToParent({
description: "",
title: PayWithCryptoErrorCode.TransactionCancelled,
});
}
} else if (error.message.includes("insufficient funds")) {
if (onError) {
await onError({
code: PayWithCryptoErrorCode.InsufficientBalance,
error,
});
}
if (postToParent) {
postToParent({
description:
"Check your wallet's ETH balance to make sure you have enough!",
title: PayWithCryptoErrorCode.InsufficientBalance,
});
}
} else if (error.message.includes("Error switching chain")) {
if (onError) {
await onError({
code: PayWithCryptoErrorCode.ChainSwitchUnderway,
error,
});
}
if (postToParent) {
postToParent({
description: "Check your wallet app",
title: PayWithCryptoErrorCode.ChainSwitchUnderway,
});
}
} else {
if (onError) {
await onError({
code: PayWithCryptoErrorCode.ErrorSendingTransaction,
error,
});
}
if (postToParent) {
postToParent({
description: `${error.message}`,
title: PayWithCryptoErrorCode.ErrorSendingTransaction,
});
}
}
}
};
@@ -0,0 +1,8 @@
// UNCHANGED
export function postMessageToIframe<T>(
frame: HTMLIFrameElement,
eventType: string,
data: T,
) {
frame.contentWindow?.postMessage({ eventType, ...data }, "*");
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "@thirdweb-dev/tsconfig/sdk.json",
"include": ["src", "types"],
"exclude": ["dist", "build", "node_modules"],
"compilerOptions": {
"noUncheckedIndexedAccess": true
}
}
+22
View File
@@ -1,5 +1,27 @@
# @thirdweb-dev/react-core
## 4.2.2
### Patch Changes
- [#2119](https://github.com/thirdweb-dev/js/pull/2119) [`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc) Thanks [@MananTank](https://github.com/MananTank)! - Update Links to Docs website
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
- @thirdweb-dev/wallets@2.3.4
- @thirdweb-dev/sdk@4.0.25
- @thirdweb-dev/auth@4.1.21
## 4.2.1
## 4.2.0
### Patch Changes
- Updated dependencies [[`b808e942`](https://github.com/thirdweb-dev/js/commit/b808e942fdaa81b5de61dcb3f2083081cb99ea11), [`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8)]:
- @thirdweb-dev/sdk@4.0.24
- @thirdweb-dev/wallets@2.3.3
- @thirdweb-dev/auth@4.1.20
## 4.1.17
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "4.1.17",
"version": "4.2.2",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -204,7 +204,7 @@ export function useWallets() {
* #### walletConfig
* The wallet to connect. Must be of type `WalletConfig`.
*
* [Learn more about the available wallet options](https://portal.thirdweb.com/react/connecting-wallets).
* [Learn more about the available wallet options](https://portal.thirdweb.com/react/v4/connecting-wallets).
*
* #### connectOptions
* The typeof `connectOptions` object depends on the wallet you are connecting. For some wallets, it may be optional
@@ -288,7 +288,7 @@ export function useConnectionStatus() {
/**
* Hook for setting the `connectionStatus` of the wallet which is returned by the `useConnectionStatus` hook
*
* This is only useful if you are manually connecting a wallet instance as mentioned in [Build your Wallet](https://portal.thirdweb.com/wallet/build-a-wallet)
* This is only useful if you are manually connecting a wallet instance as mentioned in [Build your Wallet](https://portal.thirdweb.com/wallet-sdk/v2/build)
*
* @example
*
@@ -418,7 +418,7 @@ export function useSwitchChain() {
/**
* Hook for setting a wallet instance as "connected" - once done, the wallet connection hooks like `useWallet`, `useAddress`, `useSigner`, `useConnectionStatus` etc will return the data for that wallet instance
*
* This is only useful if you are manually connecting a wallet instance as mentioned in [Build your Wallet](https://portal.thirdweb.com/wallet/build-a-wallet)
* This is only useful if you are manually connecting a wallet instance as mentioned in [Build your Wallet](https://portal.thirdweb.com/wallet-sdk/v2/build)
*
* @example
*
@@ -13,7 +13,7 @@ export interface ISecureStorage {
}
/**
* The configuration to use by the React and React Native SDKs with an [auth](https://portal.thirdweb.com/auth) server.
* The configuration to use by the React and React Native SDKs with an [auth](https://portal.thirdweb.com/wallets/auth) server.
* @auth
*/
export interface ThirdwebAuthConfig {
@@ -172,7 +172,7 @@ export function useActiveClaimCondition(
}
/**
* Hook to get the claimer proofs for an address for [ERC20](https://portal.thirdweb.com/solidity/base-contracts/erc20drop), [ERC721](https://portal.thirdweb.com/solidity/base-contracts/erc721drop), and [ERC1155](https://portal.thirdweb.com/solidity/base-contracts/erc1155drop) drop contracts.
* Hook to get the claimer proofs for an address for [ERC20](https://portal.thirdweb.com/contracts/build/base-contracts/erc-20/drop), [ERC721](https://portal.thirdweb.com/contracts/build/base-contracts/erc-721/drop), and [ERC1155](https://portal.thirdweb.com/contracts/build/base-contracts/erc-1155/drop) drop contracts.
*
* A claimer proof is a set of data about a claimer's claim condition, including the claimer's address, the claimer's proof, and the claimer's max claimable amount, price, and currency address.
*
@@ -875,9 +875,9 @@ export function useSetClaimConditions(
* Hook for resetting the claim conditions on a drop contract.
*
* Available to use on contracts that implement
* [`ERC721ClaimableWithConditions`](https://portal.thirdweb.com/solidity/extensions/erc721claimconditions),
* [`ERC1155ClaimableWithConditions`](https://portal.thirdweb.com/solidity/extensions/erc1155claimconditions) or
* [`ERC20ClaimableWithConditions`](https://portal.thirdweb.com/solidity/extensions/erc20claimconditions).
* [`ERC721ClaimableWithConditions`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721ClaimConditions),
* [`ERC1155ClaimableWithConditions`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155ClaimConditions) or
* [`ERC20ClaimableWithConditions`](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20ClaimConditions).
*
* @example
* ```jsx
@@ -27,7 +27,7 @@ import invariant from "tiny-invariant";
/**
* Hook for getting the primary sales recipient of a smart contract.
*
* Available to use on contracts that implement the [`PrimarySale`](https://portal.thirdweb.com/solidity/extensions/primarysale) interface.
* Available to use on contracts that implement the [`PrimarySale`](https://portal.thirdweb.com/contracts/build/extensions/general/PrimarySale) interface.
*
* @example
* ```jsx
@@ -67,7 +67,7 @@ export function usePrimarySaleRecipient(
/**
* Hook for updating the primary sale recipient on a smart contract.
*
* Available to use on smart contracts that implement the [`PrimarySale`](https://portal.thirdweb.com/solidity/extensions/primarysale) interface.
* Available to use on smart contracts that implement the [`PrimarySale`](https://portal.thirdweb.com/contracts/build/extensions/general/PrimarySale) interface.
*
* The wallet that initiates this transaction must have the required permissions to change the primary sale recipient (defaults to `"admin"` level).
*
@@ -156,7 +156,7 @@ export function useUpdatePrimarySaleRecipient(
/**
* Hook for retrieving royalty settings of a smart contract.
*
* Available to use on contracts that implement the [`Royalty`](https://portal.thirdweb.com/solidity/extensions/royalty) interface.
* Available to use on contracts that implement the [`Royalty`](https://portal.thirdweb.com/contracts/build/extensions/general/Royalty) interface.
*
* @example
* ```jsx
@@ -214,7 +214,7 @@ export function useRoyaltySettings(
/**
* Hook for updating royalty settings on a smart contract.
*
* Available to use on smart contracts that implement the [`Royalty`](https://portal.thirdweb.com/solidity/extensions/royalty) interface.
* Available to use on smart contracts that implement the [`Royalty`](https://portal.thirdweb.com/contracts/build/extensions/general/Royalty) interface.
*
* @example
* ```jsx
@@ -310,7 +310,7 @@ export function useUpdateRoyaltySettings(
/**
* Hook for getting the platform fee settings of a contract.
*
* Available to use on contracts that implement the [`PlatformFee`](https://portal.thirdweb.com/solidity/extensions/platformfee) interface.
* Available to use on contracts that implement the [`PlatformFee`](https://portal.thirdweb.com/contracts/build/extensions/general/PlatformFee) interface.
*
* @example
*
@@ -370,7 +370,7 @@ export function usePlatformFees(
/**
* Hook for updating platform fees on a smart contract.
*
* Available to use on smart contracts that implement the [`PlatformFee`](https://portal.thirdweb.com/solidity/extensions/platformfee) interface.
* Available to use on smart contracts that implement the [`PlatformFee`](https://portal.thirdweb.com/contracts/build/extensions/general/PlatformFee) interface.
*
* ```jsx
* import {
@@ -466,7 +466,7 @@ export function useUpdatePlatformFees(
/**
* Hook for getting the metadata associated with a smart contract.
*
* Available to use on contracts that implement the [Contract Metadata](https://portal.thirdweb.com/solidity/extensions/contractmetadata) interface.
* Available to use on contracts that implement the [Contract Metadata](https://portal.thirdweb.com/contracts/build/extensions/general/ContractMetadata) interface.
*
* @example
* ```jsx
@@ -107,7 +107,7 @@ export function useUnclaimedNFTs(
/**
* Hook for fetching all claimed NFTs from a given NFT Drop contract.
*
* Available to use on contracts that implement [`ERC721Claimable`](https://portal.thirdweb.com/solidity/extensions/erc721claimable),
* Available to use on contracts that implement [`ERC721Claimable`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Claimable),
* such as the [NFT Drop](https://thirdweb.com/thirdweb.eth/DropERC721).
*
* @example
@@ -174,7 +174,7 @@ export function useClaimedNFTs(
*
* Unclaimed NFTs are tokens that were lazy-minted but have not yet been claimed by a user.
*
* Available to use on contracts that implement the [`LazyMint`](https://portal.thirdweb.com/solidity/extensions/lazymint) extension;
* Available to use on contracts that implement the [`LazyMint`](https://portal.thirdweb.com/contracts/build/extensions/general/LazyMint) extension;
* such as the [NFT Drop](https://thirdweb.com/thirdweb.eth/DropERC721) contract.
*
* @example
@@ -220,7 +220,7 @@ export function useUnclaimedNFTSupply(
/**
* Hook for retrieving the total supply of NFTs claimed from an NFT Drop contract.
*
* Available to use on contracts that implement [`ERC721Claimable`](https://portal.thirdweb.com/solidity/extensions/erc721claimable).
* Available to use on contracts that implement [`ERC721Claimable`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Claimable).
*
* @example
*
@@ -262,8 +262,8 @@ export function useClaimedNFTSupply(
/**
* Hook for fetching batches of lazy-minted NFTs that were set to be revealed at a later date, but have not yet been revealed.
*
* Available to use on contracts that implement the [ERC721Revealable](https://portal.thirdweb.com/solidity/extensions/erc721revealable)
* or [ERC1155Revealable](https://portal.thirdweb.com/solidity/extensions/erc1155revealable) interfaces,
* Available to use on contracts that implement the [ERC721Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Revealable)
* or [ERC1155Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155Revealable) interfaces,
* such as the [NFT Drop](https://thirdweb.com/thirdweb.eth/DropERC721)
* and [Edition Drop](https://thirdweb.com/thirdweb.eth/DropERC1155) smart contracts.
*
@@ -605,8 +605,8 @@ export function useLazyMint<TContract extends DropContract>(
* allowing the owner to set placeholder metadata and reveal the metadata of the NFTs at a later time.
*
* Available to use on contracts that implement the
* [ERC721Revealable](https://portal.thirdweb.com/solidity/extensions/erc721revealable)
* or [ERC1155Revealable](https://portal.thirdweb.com/solidity/extensions/erc1155revealable)
* [ERC721Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Revealable)
* or [ERC1155Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155Revealable)
* interfaces.
*
* @example
@@ -725,8 +725,8 @@ export function useDelayedRevealLazyMint<TContract extends RevealableContract>(
* Hook for revealing a batch of delayed reveal NFTs using [delayed reveal](https://portal.thirdweb.com/glossary/delayed-reveal).
*
* Available to use on contracts that implement the
* [ERC721Revealable](https://portal.thirdweb.com/solidity/extensions/erc721revealable)
* or [ERC1155Revealable](https://portal.thirdweb.com/solidity/extensions/erc1155revealable)
* [ERC721Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Revealable)
* or [ERC1155Revealable](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155Revealable)
* interfaces.
*
* ```jsx
+18 -18
View File
@@ -37,8 +37,8 @@ import invariant from "tiny-invariant";
/**
* Hook for fetching information about an NFT from a smart contract.
*
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
*
* NFT metadata is automatically fetched from where the `tokenUri` is hosted (e.g. IPFS), and makes the `image`
* property available as a URL through our IPFS gateway (if the image is hosted on IPFS).
@@ -122,8 +122,8 @@ export function useNFT<TContract extends NFTContract>(
/**
* Hook to query all NFTs associated with a smart contract.
*
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
*
* NFT metadata is automatically fetched from where the `tokenUri` is hosted (e.g. IPFS), and makes the `image`
* property available as a URL through our IPFS gateway (if the image is hosted on IPFS).
@@ -184,8 +184,8 @@ export function useNFTs<TContract extends NFTContract>(
/**
* Hook to get the total count of **unique** NFTs minted on a smart contract.
*
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
*
* When used for ERC1155 contracts, the total count is the number of unique token IDs minted, _not_ the total supply of all tokens in circulation.
*
@@ -242,8 +242,8 @@ export function useTotalCount<TContract extends NFTContract>(
*
* This takes into account the increase in supply due to minting and the decrease in supply due to burning.
*
* Available to use on contracts that implement either the [ERC721](https://portal.thirdweb.com/solidity/extensions/erc721)
* or [ERC1155](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
* Available to use on contracts that implement either the [ERC721](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
* or [ERC1155](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
*
* @example
*
@@ -370,9 +370,9 @@ export function useOwnedNFTs<TContract extends NFTContract>(
}
/**
* Hook to get the quantity a user owns of a specific [ERC1155](https://portal.thirdweb.com/solidity/extensions/erc1155) NFT.
* Hook to get the quantity a user owns of a specific [ERC1155](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) NFT.
*
* Available to use on smart contracts that implement the [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
* Available to use on smart contracts that implement the [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
*
* @example
* ```jsx
@@ -479,8 +479,8 @@ export function useSharedMetadata(
/**
* Hook for minting a new NFT on a smart contract.
*
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
*
* By default, the process uploads and pins the NFT metadata to IPFS before minting.
*
@@ -608,7 +608,7 @@ export function useMintNFT<TContract extends NFTContract>(
* Hook for minting additional supply to an _existing_ ERC-1155 token.
*
* Available to use on contracts that implement the
* [ERC1155Mintable](https://portal.thirdweb.com/solidity/extensions/erc1155mintable)
* [ERC1155Mintable](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155Mintable)
* interface, such as the [Edition](https://thirdweb.com/thirdweb.eth/TokenERC1155) or [Edition Drop](https://thirdweb.com/thirdweb.eth/DropERC1155).
*
* The wallet address that initiates this transaction must have minting permissions on the contract.
@@ -717,8 +717,8 @@ export function useMintNFTSupply(contract: Erc1155) {
* Hook for transferring ERC721 or ERC1155 NFTs to another wallet address.
*
* Available to use on contracts that implement either the
* [ERC721](https://portal.thirdweb.com/solidity/extensions/erc721)
* and [ERC1155](https://portal.thirdweb.com/solidity/extensions/erc1155)
* [ERC721](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
* and [ERC1155](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155)
* interfaces, such as the [Edition](https://thirdweb.com/thirdweb.eth/TokenERC1155)
* or [NFT Collection](https://thirdweb.com/thirdweb.eth/TokenERC721).
*
@@ -834,7 +834,7 @@ export function useTransferNFT<TContract extends NFTContract>(
}
/**
* Hook for airdropping [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) NFT tokens to multiple wallet addresses at once.
* Hook for airdropping [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) NFT tokens to multiple wallet addresses at once.
*
* Available to use on smart contracts that implement the `ERC1155` standard.
*
@@ -915,8 +915,8 @@ export function useAirdropNFT(contract: Erc1155) {
/**
* Hook for burning a NFT on a smart contract.
*
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/solidity/extensions/erc721)
* or [`ERC1155`](https://portal.thirdweb.com/solidity/extensions/erc1155) standard.
* Available to use on smart contracts that implement the [`ERC721`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721)
* or [`ERC1155`](https://portal.thirdweb.com/contracts/build/extensions/erc-1155/ERC1155) standard.
*
* @example
*
@@ -48,7 +48,7 @@ type GetAllReturnType<TContract extends ContractWithRoles> = Promise<
/**
* Hook for getting all wallet addresses that have a role in a smart contract.
*
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions).
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions).
*
* @example
*
@@ -116,7 +116,7 @@ export function useAllRoleMembers<TContract extends ContractWithRoles>(
/**
* Hook for getting all wallet addresses that have a specific role in a smart contract.
*
* Available to use on contracts that implement the [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions) interface.
* Available to use on contracts that implement the [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions) interface.
*
* @example
* ```jsx
@@ -173,7 +173,7 @@ export function useRoleMembers<TContract extends ContractWithRoles>(
/**
* Hook to check if an address is a member of a role on a smart contract.
*
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions) interface
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions) interface
*
* Provide the following arguments to the hook:
*
@@ -305,7 +305,7 @@ export function useSetAllRoleMembers<TContract extends ContractWithRoles>(
/**
* Hook for granting a role on a smart contract.
*
* Available to use on smart contracts that implement the [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions) interface.
* Available to use on smart contracts that implement the [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions) interface.
*
* @example
* ```jsx
@@ -403,7 +403,7 @@ export function useGrantRole<TContract extends ContractWithRoles>(
/**
* Hook for revoking a wallet address from a role on a smart contract.
*
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/solidity/extensions/permissions) interface
* Available to use on contracts that implement [`Permissions`](https://portal.thirdweb.com/contracts/build/extensions/general/Permissions) interface
*
* The wallet address that initiates this transaction must have the relevant permissions on the contract to remove the role from the wallet address (typically `"admin"` level required).
*
@@ -138,7 +138,7 @@ export function useTokenBalance(
/**
* Hook for fetching the [decimals](https://docs.openzeppelin.com/contracts/3.x/erc20#a-note-on-decimals)
* of an [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20) token.
* of an [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20) token.
*
* Tokens usually opt for a value of `18`, imitating the relationship between Ether and Wei.
* Therefore, `18` is the default value returned by this function, unless your ERC20 contract explicitly overrides it.
@@ -187,7 +187,7 @@ export function useTokenDecimals(contract: RequiredParam<TokenContract>) {
/**
* Hook for minting new tokens in an ERC20 smart contract.
*
* Available to use on contracts that implement the [ERC20Mintable](https://portal.thirdweb.com/solidity/extensions/erc20mintable)
* Available to use on contracts that implement the [ERC20Mintable](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20BatchMintable)
* interface, such as the [Token](https://thirdweb.com/thirdweb.eth/TokenERC20) contract.
*
* The wallet address that initiates this transaction must have minting permissions on the contract.
@@ -286,8 +286,8 @@ export function useMintToken(
/**
* Hook for claiming a ERC20 tokens from a smart contract.
*
* Available to use on smart contracts that implement both the [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20) interface
* and the [`claim`](https://portal.thirdweb.com/solidity/extensions/erc721claimable) function,
* Available to use on smart contracts that implement both the [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20) interface
* and the [`claim`](https://portal.thirdweb.com/contracts/build/extensions/erc-721/ERC721Claimable) function,
* such as the [Token Drop](https://thirdweb.com/thirdweb.eth/DropERC20).
*
* @example
@@ -374,7 +374,7 @@ export function useClaimToken(contract: RequiredParam<TokenContract>) {
/**
* Hook for transferring tokens on an ERC20 contract.
*
* Available to use on contracts that implement the [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20)
* Available to use on contracts that implement the [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20)
* interface, such as the [Token](https://thirdweb.com/thirdweb.eth/TokenERC20) contract.
*
* The wallet address that initiates this transaction must have a balance of tokens
@@ -518,7 +518,7 @@ export function useTransferNativeToken() {
/**
* Hook for transferring ERC20 tokens to multiple recipients in a single transaction (i.e. airdrop).
*
* Available to use on contracts that implement the [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20) interface.
* Available to use on contracts that implement the [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20) interface.
*
* The wallet that initiates this transaction must have sufficient balance to cover the total amount of tokens being transferred
* and must have transfer permissions on the contract, i.e. tokens are not soulbound.
@@ -620,7 +620,7 @@ export function useTransferBatchToken(contract: RequiredParam<TokenContract>) {
/**
* Hook for burning ERC20 tokens on a smart contract.
*
* Available to use on smart contracts that implement the [ERC20](https://portal.thirdweb.com/solidity/extensions/erc20) standard.
* Available to use on smart contracts that implement the [ERC20](https://portal.thirdweb.com/contracts/build/extensions/erc-20/ERC20) standard.
*
* @example
*
@@ -7,7 +7,7 @@ import invariant from "tiny-invariant";
import { AUTH_TOKEN_STORAGE_KEY } from "../../../core/constants/auth";
/**
* Hook to prompt the user to sign in with their wallet using [auth](https://portal.thirdweb.com/auth)
* Hook to prompt the user to sign in with their wallet using [auth](https://portal.thirdweb.com/wallets/auth)
*
* Requires the `authConfig` prop to be configured on the `ThirdwebProvider`
*
@@ -16,7 +16,7 @@ export interface UserWithData<
}
/**
* Hook for retrieving information about the currently signed-in user using [auth](https://portal.thirdweb.com/auth).
* Hook for retrieving information about the currently signed-in user using [auth](https://portal.thirdweb.com/wallets/auth).
*
* Useful to get the user's address and session data, or `undefined` if no user is signed in.
*
@@ -3,7 +3,7 @@ import { useSDK } from "../useSDK";
/**
* Get the instance of the `ThirdwebStorage` class being used by the `ThirdwebProvider`
*
* Allows you to use the TypeScript SDK functionality of [Storage](https://portal.thirdweb.com/storage) in your React app.
* Allows you to use the TypeScript SDK functionality of [Storage](https://portal.thirdweb.com/infrastructure/storage/overview) in your React app.
*
* @example
*
@@ -226,7 +226,7 @@ const WrappedThirdwebSDKProvider = <TChains extends Chain[]>({
* This Overrides any of the default values for the SDK. If not provided, it uses sensible defaults.
*
* ### storageInterface (optional)
* Override the default [Storage](https://portal.thirdweb.com/storage) interface used by the SDK.
* Override the default [Storage](https://portal.thirdweb.com/infrastructure/storage/overview) interface used by the SDK.
*
* It allows you to create an instance of `ThirdwebStorage` with your own customized config, and pass it to the SDK.
*
@@ -265,7 +265,7 @@ const WrappedThirdwebSDKProvider = <TChains extends Chain[]>({
* ```
*
* ### authConfig (optional)
* The configuration object for setting up [Auth](https://portal.thirdweb.com/auth); allowing users to sign in with their wallet.
* The configuration object for setting up [Auth](https://portal.thirdweb.com/wallets/auth); allowing users to sign in with their wallet.
*
* ### secretKey (optional)
* secretKey for thirdweb services. This is only required if server side rendering is being used.
@@ -47,7 +47,7 @@ export interface ThirdwebSDKProviderProps<TChains extends Chain[]>
sdkOptions?: Omit<SDKOptions, "chains">;
/**
* Override the default [Storage](https://portal.thirdweb.com/storage) interface used by the SDK.
* Override the default [Storage](https://portal.thirdweb.com/infrastructure/storage/overview) interface used by the SDK.
*
* It allows you to create an instance of `ThirdwebStorage` with your own customized config, and pass it to the SDK.
*
@@ -89,7 +89,7 @@ export interface ThirdwebSDKProviderProps<TChains extends Chain[]>
storageInterface?: IThirdwebStorage;
/**
* The configuration object for setting up [Auth](https://portal.thirdweb.com/auth); allowing users to sign in with their wallet.
* The configuration object for setting up [Auth](https://portal.thirdweb.com/wallets/auth); allowing users to sign in with their wallet.
*/
authConfig?: ThirdwebAuthConfig;
@@ -1,5 +1,11 @@
# @thirdweb-dev/react-native-compat
## 0.5.5
## 0.5.4
## 0.5.3
## 0.5.2
## 0.5.1
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@thirdweb-dev/react-native-compat",
"description": "Shims for Thirdweb in React Native Projects",
"version": "0.5.2",
"version": "0.5.5",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+39
View File
@@ -1,5 +1,44 @@
# @thirdweb-dev/react-native
## 0.5.5
### Patch Changes
- [#2119](https://github.com/thirdweb-dev/js/pull/2119) [`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc) Thanks [@MananTank](https://github.com/MananTank)! - Update Links to Docs website
- [#2092](https://github.com/thirdweb-dev/js/pull/2092) [`4c3c840d`](https://github.com/thirdweb-dev/js/commit/4c3c840d8815915840d0819932aa0a858c5d3184) Thanks [@iketw](https://github.com/iketw)! - Export WalletConnect modal component
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
- @thirdweb-dev/react-core@4.2.2
- @thirdweb-dev/wallets@2.3.4
- @thirdweb-dev/sdk@4.0.25
## 0.5.4
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/react-core@4.2.1
## 0.5.3
### Patch Changes
- [#2101](https://github.com/thirdweb-dev/js/pull/2101) [`7fdb1715`](https://github.com/thirdweb-dev/js/commit/7fdb1715e4263e9b9c90370a1e70c7fa1fa0e95e) Thanks [@iketw](https://github.com/iketw)! - Export types for better reference docs
- [#2108](https://github.com/thirdweb-dev/js/pull/2108) [`b90fa2cc`](https://github.com/thirdweb-dev/js/commit/b90fa2cc298bf1a6deb0f4c0f927ab77f2716448) Thanks [@iketw](https://github.com/iketw)! - Adds `hideSendButton` and `hideReceiveButton` to the ConnectWallet component.
Both props default to `false`.
```js
<ConnectWallet hideReceiveButton={true} hideSendButton={true} />
```
- Updated dependencies [[`b808e942`](https://github.com/thirdweb-dev/js/commit/b808e942fdaa81b5de61dcb3f2083081cb99ea11), [`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.5.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.5.2",
"version": "0.5.5",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -24,15 +24,29 @@ import { SupportedTokens, defaultTokens } from "./SendFunds/defaultTokens";
export type ConnectWalletProps = {
/**
* render a custom button to display the connected wallet details instead of the default button
* Renders a custom button to display the connected wallet details instead of the default button
*/
detailsButton?: ConnectWalletDetailsProps["detailsButton"];
/**
* render custom rows in the Connect Wallet Details modal
* Renders custom rows in the Connect Wallet Details modal
*/
extraRows?: ConnectWalletDetailsProps["extraRows"];
/**
* Option to hide the Send button in the wallet details modal.
*
* The default is `false`
*/
hideSendButton?: boolean;
/**
* Option to hide the Receive button in the wallet details modal.
*
* The default is `false`
*/
hideReceiveButton?: boolean;
/**
* Hide option to request testnet funds for testnets in dropdown
*
@@ -42,7 +56,7 @@ export type ConnectWalletProps = {
/**
* Whether to show "Switch Network" button if the wallet is connected,
* but it is not connected to the `activeChain` provided in `ThirdwebProvider`
* but it is not connected to the `activeChain` provided in [`ThirdwebProvider`](https://portal.thirdweb.com/react-native/v0/ThirdwebProvider)
*
* Please, note that if you support multiple networks in your app this prop should
* be set to `false` to allow users to switch between networks.
@@ -84,6 +98,8 @@ export const ConnectWallet = ({
buttonTitle,
modalTitle,
modalTitleIconUrl,
hideReceiveButton,
hideSendButton,
extraRows,
hideTestnetFaucet,
displayBalanceToken,
@@ -169,6 +185,8 @@ export const ConnectWallet = ({
detailsButton={detailsButton}
extraRows={extraRows}
hideTestnetFaucet={hideTestnetFaucet}
hideReceiveButton={hideReceiveButton}
hideSendButton={hideSendButton}
supportedTokens={supportedTokensMemo}
displayBalanceToken={displayBalanceToken}
hideSwitchToPersonalWallet={hideSwitchToPersonalWallet}
@@ -45,6 +45,8 @@ export const ConnectWalletDetailsModal = ({
supportedTokens,
displayBalanceToken,
hideSwitchToPersonalWallet,
hideReceiveButton,
hideSendButton,
}: {
isVisible: boolean;
onClosePress: () => void;
@@ -54,6 +56,8 @@ export const ConnectWalletDetailsModal = ({
supportedTokens: SupportedTokens;
displayBalanceToken?: Record<number, string>;
hideSwitchToPersonalWallet?: boolean;
hideReceiveButton?: boolean;
hideSendButton?: boolean;
}) => {
const l = useLocale();
const [isExportModalVisible, setIsExportModalVisible] = useState(false);
@@ -143,7 +147,7 @@ export const ConnectWalletDetailsModal = ({
if (activeWallet?.walletId === walletIds.localWallet) {
return (
<>
<View style={styles.currentNetwork}>
<View style={styles.additionalActions}>
<Text variant="bodySmallSecondary">
{l.connect_wallet_details.additional_actions}
</Text>
@@ -267,14 +271,19 @@ export const ConnectWalletDetailsModal = ({
</Text>
</Box>
) : null}
<Box
flexDirection="row"
justifyContent="space-evenly"
marginVertical="md"
>
<SendButton supportedTokens={supportedTokens} />
<ReceiveButton />
</Box>
{hideReceiveButton && hideSendButton ? null : (
<Box
flexDirection="row"
justifyContent="space-evenly"
gap="xs"
marginTop="md"
>
{hideSendButton ? null : (
<SendButton supportedTokens={supportedTokens} />
)}
{hideReceiveButton ? null : <ReceiveButton />}
</Box>
)}
<View style={styles.currentNetwork}>
<Text variant="bodySmallSecondary">
{l.connect_wallet_details.current_network}
@@ -353,6 +362,15 @@ const styles = StyleSheet.create({
minWidth: 200,
},
currentNetwork: {
display: "flex",
flexDirection: "column",
alignContent: "flex-start",
alignItems: "flex-start",
justifyContent: "flex-start",
marginBottom: 8,
marginTop: 28,
},
additionalActions: {
display: "flex",
flexDirection: "column",
alignContent: "flex-start",
@@ -42,6 +42,20 @@ export type ConnectWalletDetailsProps = {
* The default is `false`
*/
hideSwitchToPersonalWallet?: boolean;
/**
* Option to hide the Send button in the wallet details modal.
*
* The default is `false`
*/
hideSendButton?: boolean;
/**
* Option to hide the Receive button in the wallet details modal.
*
* The default is `false`
*/
hideReceiveButton?: boolean;
};
export const WalletDetailsButton = ({
@@ -49,6 +63,8 @@ export const WalletDetailsButton = ({
detailsButton,
extraRows,
hideTestnetFaucet,
hideReceiveButton,
hideSendButton,
supportedTokens,
displayBalanceToken,
hideSwitchToPersonalWallet,
@@ -84,6 +100,8 @@ export const WalletDetailsButton = ({
supportedTokens={supportedTokens}
displayBalanceToken={displayBalanceToken}
hideSwitchToPersonalWallet={hideSwitchToPersonalWallet}
hideReceiveButton={hideReceiveButton}
hideSendButton={hideSendButton}
/>
{detailsButton ? (
detailsButton({ onPress })
@@ -120,7 +120,7 @@ export const ConnectWalletFlow = () => {
connected={handleClose}
isOpen={modalVisible}
show={onOpenModal}
hide={() => {}}
hide={handleClose}
walletConfig={activeWallet}
supportedWallets={supportedWallets}
selectionData={selectionData}
@@ -25,6 +25,7 @@ export const SmartWalletFlow = ({
goBack,
walletConfig,
personalWalletConfig,
hide,
...props
}: ConnectUIProps<SmartWallet> & { personalWalletConfig: WalletConfig }) => {
const l = useLocale();
@@ -124,6 +125,7 @@ export const SmartWalletFlow = ({
<PersonalWalletConfigUI
{...props}
connected={connected}
hide={hide}
goBack={goBack}
walletConfig={personalWalletConfig}
onLocallyConnected={onPersonalWalletConnected}
@@ -33,7 +33,6 @@ export const ReceiveButton = () => {
<>
<IconTextButton
flex={1}
ml="xs"
text={l.common.receive}
justifyContent="center"
icon={
@@ -22,7 +22,7 @@ import { useGlobalTheme, useUIContext } from "../providers/ui-context-provider";
export type ActionFn = (contract: SmartContract) => Promise<any>;
interface Web3ButtonProps<TActionFn extends ActionFn> {
export interface Web3ButtonProps<TActionFn extends ActionFn> {
contractAddress: `0x${string}` | `${string}.eth` | string;
contractAbi?: ContractInterface;
@@ -43,7 +43,7 @@ interface Web3ButtonProps<TActionFn extends ActionFn> {
/**
* A component that allows the user to call an on-chain function on a contract.
*
* The button has to be wrapped in a `ThirdwebProvider` in order to function.
* The button has to be wrapped in a [`ThirdwebProvider`](https://portal.thirdweb.com/react-native/v0/ThirdwebProvider) in order to function.
*
* @example
* ```javascript
@@ -14,6 +14,8 @@ export function TWModal(props: Partial<ModalProps>) {
useNativeDriver
hideModalContentWhileAnimating={true}
style={{ maxWidth: MAX_WIDTH }}
animationIn="slideInUp" // Slide in from bottom
animationOut="slideOutDown" // Slide out to bottom
>
{props.children}
</Modal>
+17 -3
View File
@@ -38,7 +38,13 @@ export {
ConnectWallet,
type ConnectWalletProps,
} from "./components/ConnectWallet";
export { Web3Button, type ActionFn } from "./components/Web3Button";
export {
Web3Button,
type Web3ButtonProps,
type ActionFn,
} from "./components/Web3Button";
export { WalletConnectUI } from "./wallets/wallets/wallet-connect/WalletConnectUI";
// utilities
export * from "./utils/uri";
@@ -61,11 +67,19 @@ export { ConnectWalletHeader } from "./components/ConnectWalletFlow/ConnectingWa
export * from "./assets";
// providers
// export * from "./providers/full";
export * from "./providers/thirdweb-provider";
export {
ThirdwebProvider,
type DefaultChains,
type ThirdwebProviderProps,
} from "./providers/thirdweb-provider";
// re-export everything from react-core
export * from "@thirdweb-dev/react-core";
// ThirdwebSDK RN
export { ThirdwebSDK } from "./sdk/ThirdwebSDK";
export {
type SupportedTokens,
defaultTokens,
} from "./components/SendFunds/defaultTokens";
@@ -18,7 +18,7 @@ import { walletIds } from "@thirdweb-dev/wallets";
import { ThirdwebStorage } from "../../core/storage/storage";
import type { Locale } from "../i18n/types";
interface ThirdwebProviderProps<TChains extends Chain[]>
export interface ThirdwebProviderProps<TChains extends Chain[]>
extends Omit<
ThirdwebProviderCoreProps<TChains>,
"supportedWallets" | "secretKey" | "signer" | "theme"
@@ -10,9 +10,7 @@ import { MagicConnector } from "../connectors/magic/magic-connector";
* @deprecated We have deprecated magicLink in favor of our embeddedWallet which adds support for more sign in methods.
* To learn more, please see:
*
* Our release notes: https://portal.thirdweb.com/embedded-wallet
*
* Our documentation: https://portal.thirdweb.com/react-native/react-native.embeddedwallet
* Checkout the [Release Notes](https://portal.thirdweb.com/wallets/embedded-wallet/overview) and [Documentation](https://portal.thirdweb.com/react-native/v0/wallets/embedded-wallet) for more information.
*/
export class MagicLink extends AbstractClientWallet<
MagicLinkOptions,
@@ -29,9 +29,7 @@ import { useGlobalTheme, useLocale } from "../../providers/ui-context-provider";
* @deprecated We have deprecated magicLink in favor of our embeddedWallet which adds support for more sign in methods.
* To learn more, please see:
*
* Our release notes: https://portal.thirdweb.com/embedded-wallet
*
* Our documentation: https://portal.thirdweb.com/react-native/react-native.embeddedwallet
* Check out [Release Note](https://portal.thirdweb.com/wallets/embedded-wallet/overview) and [Documentation](https://portal.thirdweb.com/react-native/v0/wallets/embedded-wallet) for more details.
*/
export const magicLink = (
magicLinkOptions: MagicLinkOptions & { recommended?: boolean },
@@ -43,8 +43,21 @@ export function WalletConnectUI({
walletConfig,
projectId,
goBack,
hide,
supportedWallets,
}: ConnectUIProps<WalletConnect> & { projectId: string }) {
isVisible,
}: Omit<
ConnectUIProps<WalletConnect>,
| "isOpen"
| "show"
| "theme"
| "selectionData"
| "setSelectionData"
| "modalSize"
> & {
projectId: string;
isVisible?: boolean;
}) {
const l = useLocale();
const theme = useGlobalTheme();
const [wallets, setWallets] = useState<WCWallet[]>([]);
@@ -55,6 +68,9 @@ export function WalletConnectUI({
const createWalletInstance = useCreateWalletInstance();
const setConnectedWallet = useSetConnectedWallet();
const setConnectionStatus = useSetConnectionStatus();
const [isVisibleInternal, setIsVisibleInternal] = useState<
boolean | undefined
>(isVisible);
const onChangeText = useDebounceCallback({ callback: setSearch });
@@ -118,6 +134,10 @@ export function WalletConnectUI({
};
}, [projectId]);
useEffect(() => {
setIsVisibleInternal(isVisible);
}, [isVisible]);
useEffect(() => {
if (wallets && search) {
setSearchWallets(
@@ -145,18 +165,24 @@ export function WalletConnectUI({
console.error(`Error connecting with WalletConnect: ${e}`);
})
.finally(() => {
connected();
setIsVisibleInternal(false);
setTimeout(() => {
connected();
});
});
};
const onClosePress = () => {
setConnectionStatus("disconnected");
connected();
setIsVisibleInternal(false);
setTimeout(() => {
hide();
});
};
return (
<TWModal
isVisible={true}
isVisible={isVisibleInternal ?? true}
onBackdropPress={onClosePress}
backdropOpacity={0.7}
>
+38
View File
@@ -1,5 +1,43 @@
# @thirdweb-dev/react
## 4.2.2
### Patch Changes
- [#2119](https://github.com/thirdweb-dev/js/pull/2119) [`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc) Thanks [@MananTank](https://github.com/MananTank)! - Update Links to Docs website
- Updated dependencies [[`cf606665`](https://github.com/thirdweb-dev/js/commit/cf606665fcfdad7b08259350d39b60313278a8dc)]:
- @thirdweb-dev/react-core@4.2.2
- @thirdweb-dev/wallets@2.3.4
- @thirdweb-dev/sdk@4.0.25
## 4.2.1
### Patch Changes
- Updated dependencies [[`d00ca7fd`](https://github.com/thirdweb-dev/js/commit/d00ca7fd2a0fa3e694bb37d1f486d3d4cf1ad79e)]:
- @thirdweb-dev/payments@1.0.1
- @thirdweb-dev/react-core@4.2.1
## 4.2.0
### Minor Changes
- [#1913](https://github.com/thirdweb-dev/js/pull/1913) [`ed3afd41`](https://github.com/thirdweb-dev/js/commit/ed3afd418cb6e0d711814a1c52afb302460751ba) Thanks [@IDubuque](https://github.com/IDubuque)! - Migrated `@paperxyz/react-client-sdk` into `@thirdweb-dev/react` to provide checkout components under the thirdweb react sdk. The `@paperxyz/react-client-sdk` package will be continued within the `@thirdweb-dev/react` package, aligning with the new branding and expanded capabilities under thirdweb. It inherits the core functionality and components of Paper's developer-first NFT checkout solution.
### Added
- Integrated the `@paper-xyz/react-client-sdk` components into `@thirdweb-dev/react`. This update brings all the Paper.xyz React components into the thirdweb ecosystem, providing a seamless developer experience for building NFT-powered applications.
- New components from Paper.xyz for NFT checkouts, enhancing the existing thirdweb SDK capabilities.
### Patch Changes
- Updated dependencies [[`b808e942`](https://github.com/thirdweb-dev/js/commit/b808e942fdaa81b5de61dcb3f2083081cb99ea11), [`e46dd025`](https://github.com/thirdweb-dev/js/commit/e46dd0254f157c3c4d50fe3e4f44e2e24eaf34a8), [`ed3afd41`](https://github.com/thirdweb-dev/js/commit/ed3afd418cb6e0d711814a1c52afb302460751ba)]:
- @thirdweb-dev/sdk@4.0.24
- @thirdweb-dev/wallets@2.3.3
- @thirdweb-dev/payments@1.0.0
- @thirdweb-dev/react-core@4.2.0
## 4.1.17
### Patch Changes
+5 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "4.1.17",
"version": "4.2.2",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -110,6 +110,8 @@
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@emotion/css": "11.10.5",
"@headlessui/react": "1.7.6",
"@google/model-viewer": "^2.1.1",
"@radix-ui/colors": "^0.1.9",
"@radix-ui/react-dialog": "^1.0.5",
@@ -122,8 +124,10 @@
"@react-icons/all-files": "^4.1.0",
"@tanstack/react-query": "^4.33.0",
"@thirdweb-dev/chains": "workspace:*",
"@thirdweb-dev/sdk": "workspace:*",
"@thirdweb-dev/react-core": "workspace:*",
"@thirdweb-dev/wallets": "workspace:*",
"@thirdweb-dev/payments": "workspace:*",
"buffer": "^6.0.3",
"copy-to-clipboard": "^3.3.2",
"detect-browser": "^5.3.0",
@@ -110,7 +110,7 @@ export interface Web3ButtonProps<TActionFn extends ActionFn> {
style?: React.CSSProperties;
/**
* Web3Button renders a `ConnectWallet` if no wallet is connected. You can pass props for that component by passing a `connectWallet` prop to Web3Button
* Web3Button renders a [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) if no wallet is connected. You can pass props for that component by passing a `connectWallet` prop to Web3Button
*
* @example
* ```tsx
@@ -136,7 +136,7 @@ export interface Web3ButtonProps<TActionFn extends ActionFn> {
btnTitle?: string;
/**
* title of the `ConnectWallet` Modal.
* title of the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) Modal.
*
* The default is `"Connect"`.
*/
@@ -193,9 +193,9 @@ export interface Web3ButtonProps<TActionFn extends ActionFn> {
*
* It ensures the following criteria before attempting to call the contract function:
*
* 1. There is a connected wallet (if there is not, it renders a `ConnectWallet` component instead.
* 1. There is a connected wallet (if there is not, it renders a [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component instead.
*
* 2. The connected wallet is on the correct network as specified in the `ThirdwebProvider`'s `activeChain` prop. if it is not, it renders a "switch network" button instead.
* 2. The connected wallet is on the correct network as specified in the [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `activeChain` prop. if it is not, it renders a "switch network" button instead.
*
* If the action you are performing is async, make sure to return a `Promise` from the action function so that the SDK knows when the action is complete. This can be done by either using async/await or by returning a `Promise`.
*
@@ -220,7 +220,7 @@ export interface Web3ButtonProps<TActionFn extends ActionFn> {
* @param props -
* The props for the component.
*
* Refer to [Web3ButtonProps](https://portal.thirdweb.com/references/react/Web3ButtonProps) for more details.
* Refer to [Web3ButtonProps](https://portal.thirdweb.com/references/react/v4/Web3ButtonProps) for more details.
*
*/
export const Web3Button = <TAction extends ActionFn>(
@@ -3,7 +3,7 @@ import type { SafeConnectionArgs } from "@thirdweb-dev/wallets";
import { useCallback } from "react";
/**
* @deprecated use `ConnectWallet` or `useConnect` instead
* @deprecated use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) instead
* @walletConnection
* @internal
*/
@@ -23,7 +23,7 @@ export function useSafe() {
// for backwards compatibility
/**
* @deprecated use `ConnectWallet` or `useConnect` instead
* @deprecated use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) instead
* @walletConnection
* @internal
*/
@@ -4,7 +4,7 @@ import { MagicLinkAdditionalOptions } from "@thirdweb-dev/wallets/evm/wallets/ma
import { MagicLinkConnectOptions } from "@thirdweb-dev/wallets";
/**
* @deprecated use `ConnectWallet` or `useConnect` instead
* @deprecated use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) instead
* @walletConnection
* @internal
*/
@@ -4,7 +4,7 @@ import { BloctoWalletConfigOptions } from "../../../wallet/wallets/blocto/blocto
/**
* @internal
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
* @walletConnection
*/
export function useBloctoWallet() {
@@ -3,7 +3,7 @@ import { useCallback } from "react";
/**
* @walletConnection
* @deprecated use `ConnectWallet` component or `useConnect` hook instead
* @deprecated use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
* @internal
*/
export function useCoinbaseWallet() {
@@ -22,7 +22,7 @@ import {
/**
* Hook to connect `EmbeddedWallet` which allows users to login via Email or social logins
*
* The `embeddedWallet()` should be added to `ThirdwebProvider`'s `supportedWallets` prop to enable auto-connection on page load
* The `embeddedWallet()` should be added to [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `supportedWallets` prop to enable auto-connection on page load
*
* @example
*
@@ -2,7 +2,7 @@ import { useConnect } from "@thirdweb-dev/react-core";
import { useCallback } from "react";
/**
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
* @internal
*/
export function useFrameWallet() {
@@ -2,7 +2,7 @@ import { useConnect } from "@thirdweb-dev/react-core";
import { useCallback } from "react";
/**
* @deprecated Use `ConnectWallet` component or `useConnect` hook instead
* @deprecated Use [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
* @internal
*/
export function useMetamask() {
@@ -15,7 +15,7 @@ type PaperConfig = Omit<PaperWalletAdditionalOptions, "chain">;
/**
* @internal
* @deprecated We have deprecated `PaperWallet` in favor of our `EmbeddedWallet` which adds support for more sign in methods. use the `useEmbeddedWallet` hook instead
* @deprecated We have deprecated `PaperWallet` in favor of our `EmbeddedWallet` which adds support for more sign in methods. use the [`useEmbeddedWallet`](https://portal.thirdweb.com/references/react/v4/useEmbeddedWallet) hook instead
*/
export function usePaperWallet() {
const connect = useConnect();
@@ -35,7 +35,7 @@ export function usePaperWallet() {
/**
* @internal
* @deprecated We have deprecated `PaperWallet` in favor of our `EmbeddedWallet` which adds support for more sign in methods. use the `useEmbeddedWallet` hook instead
* @deprecated We have deprecated `PaperWallet` in favor of our `EmbeddedWallet` which adds support for more sign in methods. use the [`useEmbeddedWallet`](https://portal.thirdweb.com/references/react/v4/useEmbeddedWallet) hook instead
*/
export function usePaperWalletUserEmail(): UseQueryResult<
string | undefined,
@@ -2,7 +2,7 @@ import { useConnect } from "@thirdweb-dev/react-core";
import { useCallback } from "react";
/**
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
* @internal
* @walletConnection
*/
@@ -16,11 +16,11 @@ import { useCallback } from "react";
import type { BytesLike } from "ethers";
/**
* Hook to connect [Smart wallet](https://portal.thirdweb.com/wallet/smart-wallet)
* Hook to connect [Smart wallet](https://portal.thirdweb.com/references/wallets/v2/SmartWallet)
*
* `useSmartWallet()` handles connecting both the personal wallet and the Smart Wallet.
*
* The `smartWallet()` also need to be added in `ThirdwebProvider`'s `supportedWallets` prop to enable auto-connection on page load
* The `smartWallet()` also need to be added in [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `supportedWallets` prop to enable auto-connection on page load
*
* ### Example
*
@@ -2,7 +2,7 @@ import { useConnect } from "@thirdweb-dev/react-core";
import { useCallback } from "react";
/**
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
* @internal
* @walletConnection
*/
@@ -22,7 +22,7 @@ export function useWalletConnectV1() {
/**
* @internal
* @walletConnection
* @deprecated use the `ConnectWallet` component or `useConnect` hook instead
* @deprecated use the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) component or [`useConnect`](https://portal.thirdweb.com/references/react/v4/useConnect) hook instead
*/
export function useWalletConnect() {
const connect = useConnect();
+2 -2
View File
@@ -320,7 +320,7 @@ export function enDefault() {
}
/**
* Calling this function will return the default English locale object to be set on `ThirdwebProvider` to localize the thirdweb components.
* Calling this function will return the default English locale object to be set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider) to localize the thirdweb components.
*
* You can also overrides parts of the default locale object by passing an object with the same structure as the default locale object and only those parts will be overridden.
*
@@ -339,7 +339,7 @@ export function enDefault() {
* })
* ```
*
* Pass it to `ThirdwebProvider`'s `locale` prop to localize the thirdweb components.
* Pass it to [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `locale` prop to localize the thirdweb components.
*
* ```tsx
* function Example() {
+2 -2
View File
@@ -328,7 +328,7 @@ export function esDefault(): ThirdwebLocale {
}
/**
* Calling this function will return the default Spanish locale object to be set on `ThirdwebProvider` to localize the thirdweb components.
* Calling this function will return the default Spanish locale object to be set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider) to localize the thirdweb components.
*
* You can also overrides parts of the default locale object by passing an object with the same structure as the default locale object and only those parts will be overridden.
*
@@ -347,7 +347,7 @@ export function esDefault(): ThirdwebLocale {
* })
* ```
*
* Pass it to `ThirdwebProvider`'s `locale` prop to localize the thirdweb components.
* Pass it to [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `locale` prop to localize the thirdweb components.
*
* ```tsx
* function Example() {
+2 -2
View File
@@ -322,7 +322,7 @@ export function jaDefault(): ThirdwebLocale {
}
/**
* Calling this function will return the default Japanese locale object to be set on `ThirdwebProvider` to localize the thirdweb components.
* Calling this function will return the default Japanese locale object to be set on [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider) to localize the thirdweb components.
*
* You can also overrides parts of the default locale object by passing an object with the same structure as the default locale object and only those parts will be overridden.
*
@@ -341,7 +341,7 @@ export function jaDefault(): ThirdwebLocale {
* })
* ```
*
* Pass it to `ThirdwebProvider`'s `locale` prop to localize the thirdweb components.
* Pass it to [`ThirdwebProvider`](https://portal.thirdweb.com/react/v4/ThirdwebProvider)'s `locale` prop to localize the thirdweb components.
*
* ```tsx
* function Example() {
@@ -381,7 +381,7 @@ export type DefaultChains = typeof defaultChains;
* ```
*
* ### authConfig (optional)
* The configuration object for setting up [Auth](https://portal.thirdweb.com/auth); allowing users to sign in with their wallet.
* The configuration object for setting up [Auth](https://portal.thirdweb.com/wallets/auth); allowing users to sign in with their wallet.
* It takes an object with the following properties:
* - `authUrl` - The backend URL of the authentication endpoints. For example, if your endpoints are at `/api/auth/login`, `/api/auth/logout`, etc. then this should be set to `/api/auth`
* - `domain` - The frontend domain used to generate the login payload. This domain should match the domain used on your auth backend.
@@ -412,7 +412,7 @@ export type DefaultChains = typeof defaultChains;
*
* Allows you to create an instance of `ThirdwebStorage` with your own customized config, and pass it to the SDK. This requires the `@thirdweb-dev/storage` package to be installed.
*
* [Learn more aboout storage](https://portal.thirdweb.com/storage)
* [Learn more about storage](https://portal.thirdweb.com/infrastructure/storage/overview)
*
* ```jsx
* import { ThirdwebProvider } from "@thirdweb-dev/react";
@@ -78,14 +78,14 @@ export const WalletUIStatesProvider = (
};
/**
* Check if the `ConnectWallet` Modal is open or not
* Check if the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) Modal is open or not
*
* @example
* ```tsx
* const isOpen = useIsWalletModalOpen();
* ```
*
* @returns `true` if the `ConnectWallet` modal is open, `false` otherwise
* @returns `true` if the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) modal is open, `false` otherwise
* @connectWallet
*/
export const useIsWalletModalOpen = () => {
@@ -93,7 +93,7 @@ export const useIsWalletModalOpen = () => {
};
/**
* Open or close the `ConnectWallet` Modal
* Open or close the [`ConnectWallet`](https://portal.thirdweb.com/react/v4/components/ConnectWallet) Modal
*
* @example
* ```tsx
+1
View File
@@ -113,3 +113,4 @@ export type {
// at the moment we'll re-export everything from the evm package
export * from "./evm";
export * from "./payments";
+54
View File
@@ -0,0 +1,54 @@
// UNCHANGED
import type { SupportedChainName } from "@thirdweb-dev/payments";
import type { Dispatch, SetStateAction } from "react";
import React, { createContext, useContext, useMemo, useState } from "react";
interface SDKContext {
chainName: SupportedChainName;
setChainName: Dispatch<SetStateAction<SupportedChainName>>;
clientId: string;
appName: string;
}
const defaultContextValue: SDKContext = {
chainName: "Polygon",
setChainName: () => {},
clientId: "",
appName: "",
};
const PaymentsSDKContext = /* @__PURE__ */ createContext(defaultContextValue);
export interface PaymentsProviderProps {
chainName?: SupportedChainName;
appName?: string;
clientId?: string;
}
export const PaymentsSDKProvider = ({
appName = "",
chainName = "Polygon",
clientId = "",
children,
}: React.PropsWithChildren<PaymentsProviderProps>) => {
const [chainName_, setChainName] = useState<SupportedChainName>(chainName);
const contextValue = useMemo(
() => ({
chainName: chainName_,
setChainName,
appName: appName,
clientId: clientId,
}),
[chainName_, appName, clientId],
);
return (
<PaymentsSDKContext.Provider value={contextValue}>
{children}
</PaymentsSDKContext.Provider>
);
};
export const usePaymentsSDKContext = (): SDKContext => {
return useContext(PaymentsSDKContext);
};
+8
View File
@@ -0,0 +1,8 @@
// UNCHANGED
declare module '*.png';
declare module '*.webp';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.svg';
declare module '*.gif';
declare module '*.css';
@@ -0,0 +1,114 @@
// CHANGED: Import packageJSON + sdk-version only
import type {
ICheckoutWithCardConfigs,
PaymentsSDKError,
PriceSummary,
ReviewResult,
ICustomizationOptions,
Locale,
} from "@thirdweb-dev/payments";
import {
createCheckoutWithCardElement,
DEFAULT_BRAND_OPTIONS,
} from "@thirdweb-dev/payments";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { usePaymentsSDKContext } from "../Provider";
import { iframeContainer } from "../lib/utils/styles";
import { SpinnerWrapper } from "./common/SpinnerWrapper";
import packageJson from "../../../package.json";
interface CheckoutWithCardProps {
clientId?: string;
sdkClientSecret?: string;
onPaymentSuccess: (result: { transactionId: string }) => void;
appName?: string;
options?: ICustomizationOptions;
onReview?: (result: ReviewResult) => void;
onError?: (error: PaymentsSDKError) => void;
onBeforeModalOpen?: (props: { url: string }) => void;
onPriceUpdate?: (priceSummary: PriceSummary) => void;
configs?: ICheckoutWithCardConfigs;
/**
* @deprecated No longer used.
*/
experimentalUseAltDomain?: boolean;
/**
* Sets the locale to a supported language.
* NOTE: Localization is in early alpha and many languages are not yet supported.
*
* Defaults to English.
*/
locale?: Locale;
}
export const CheckoutWithCard = ({
clientId,
sdkClientSecret,
appName,
options = {
...DEFAULT_BRAND_OPTIONS,
},
onPaymentSuccess,
onReview,
onError,
onBeforeModalOpen,
onPriceUpdate,
locale,
configs,
}: CheckoutWithCardProps): React.ReactElement => {
const { appName: appNameContext } = usePaymentsSDKContext();
const [isCardDetailIframeLoading, setIsCardDetailIframeLoading] =
useState<boolean>(true);
const onCardDetailLoad = useCallback(() => {
setIsCardDetailIframeLoading(false);
}, []);
const CheckoutWithCardIframeContainerRef = useRef<HTMLDivElement>(null);
const appNameToUse = appName || appNameContext;
// force hide spinner if iframe taking too long
useEffect(() => {
const timer = setTimeout(() => {
setIsCardDetailIframeLoading(false);
}, 1000);
return () => {
clearTimeout(timer); // Clear the timer if the component unmounts before the delay
};
}, []);
// Handle message events from the popup. Pass along the message to the iframe as well
useEffect(() => {
if (!CheckoutWithCardIframeContainerRef.current) {
return;
}
createCheckoutWithCardElement({
clientId,
sdkClientSecret,
appName: appNameToUse,
elementOrId: CheckoutWithCardIframeContainerRef.current,
locale,
onError,
onLoad: onCardDetailLoad,
onPaymentSuccess,
onReview,
onBeforeModalOpen,
onPriceUpdate,
options,
configs,
});
}, [CheckoutWithCardIframeContainerRef.current]);
return (
<>
<div
className={iframeContainer}
ref={CheckoutWithCardIframeContainerRef}
// Label the package version.
data-thirdweb-sdk-version={`${packageJson.name}@${packageJson.version}`}
>
{isCardDetailIframeLoading && <SpinnerWrapper />}
</div>
</>
);
};
@@ -0,0 +1,4 @@
// UNCHANGED
import { VerifyOwnershipWithPaper } from "./VerifyOwnershipWithPaper";
export { VerifyOwnershipWithPaper as LoginWithPaper };
@@ -0,0 +1,490 @@
// CHANGED: imports only
import { PAPER_APP_URL, DEFAULT_BRAND_OPTIONS } from "@thirdweb-dev/payments";
import React, { useEffect, useState } from "react";
import type {
ContractType,
CustomContractArgWrapper,
ReadMethodCallType,
WriteMethodCallType,
} from "../interfaces/CustomContract";
import { fetchCustomContractArgsFromProps } from "../interfaces/CustomContract";
import type { PaymentSuccessResult } from "../interfaces/PaymentSuccessResult";
import type { TransferSuccessResult } from "../interfaces/TransferSuccessResult";
import { openCenteredPopup } from "../lib/utils/popup";
export enum PaperCheckoutDisplay {
/**
* Open the checkout in a new popup centered over the parent window.
*/
POPUP = "POPUP",
/**
* Open the checkout in a new browser tab.
*/
NEW_TAB = "NEW_TAB",
/**
* Open the checkout in a modal on the parent page with a darkened background.
*
* NOTE: Pay with Crypto is disabled in this display mode.
*/
MODAL = "MODAL",
/**
* Open the checkout in a drawer on the right side of the parent page with a darkened background.
*
* NOTE: Pay with Crypto is disabled in this display mode.
*/
DRAWER = "DRAWER",
/**
* Embed the checkout directly on the parent page.
*
* NOTE: Pay with Crypto is disabled in this display mode.
*/
EMBED = "EMBED",
}
export type PaperCheckoutProps<T extends ContractType> =
CustomContractArgWrapper<
{
checkoutId: string;
display?: PaperCheckoutDisplay;
recipientWalletAddress?: string;
emailAddress?: string;
quantity?: number;
mintMethod?: WriteMethodCallType;
eligibilityMethod?: ReadMethodCallType;
metadata?: Record<string, any>;
appName?: string;
onOpenCheckout?: () => void;
onCloseCheckout?: () => void;
onPaymentSuccess?: (result: PaymentSuccessResult) => void;
onTransferSuccess?: (result: TransferSuccessResult) => void;
options?: {
width: number;
height: number;
colorPrimary: string;
colorBackground: string;
colorText: string;
borderRadius: number;
fontFamily: string;
};
children?: React.ReactNode;
},
T
>;
export const PaperCheckout = <T extends ContractType>({
checkoutId,
display = PaperCheckoutDisplay.POPUP,
recipientWalletAddress,
emailAddress,
quantity,
eligibilityMethod,
mintMethod,
metadata,
appName,
options = {
width: 400,
height: 800,
...DEFAULT_BRAND_OPTIONS,
},
onOpenCheckout,
onCloseCheckout,
onPaymentSuccess,
onTransferSuccess,
children,
...props
}: PaperCheckoutProps<T>) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const { contractType, contractArgs } =
fetchCustomContractArgsFromProps(props);
// Handle message events from iframe.
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
const data = event.data;
switch (data.eventType) {
case "paymentSuccess":
if (onPaymentSuccess) {
onPaymentSuccess({ id: data.id });
}
break;
case "transferSuccess":
if (onTransferSuccess) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
onTransferSuccess({
id: data.id,
// ...
});
}
break;
case "modalClosed":
// Emitted when the iframe decides to close, such as when the purchase is completed.
setIsOpen(false);
if (onCloseCheckout) {
onCloseCheckout();
}
break;
default:
// Ignore unrecognized event
}
};
window.addEventListener("message", handleMessage);
return () => {
window.removeEventListener("message", handleMessage);
};
}, []);
// Build iframe URL with query params.
const checkoutUrl = new URL(`/checkout/${checkoutId}`, PAPER_APP_URL);
checkoutUrl.searchParams.append("display", display);
const mintMethodStringified = JSON.stringify(mintMethod);
const eligibilityMethodStringified = JSON.stringify(eligibilityMethod);
const metadataStringified = JSON.stringify(metadata);
const contractArgsStringified = JSON.stringify(contractArgs);
if (options.colorPrimary) {
checkoutUrl.searchParams.append("colorPrimary", options.colorPrimary);
}
if (options.colorBackground) {
checkoutUrl.searchParams.append("colorBackground", options.colorBackground);
}
if (options.colorText) {
checkoutUrl.searchParams.append("colorText", options.colorText);
}
if (options.borderRadius !== undefined) {
checkoutUrl.searchParams.append(
"borderRadius",
options.borderRadius.toString(),
);
}
if (options.fontFamily) {
checkoutUrl.searchParams.append("fontFamily", options.fontFamily);
}
if (mintMethod) {
checkoutUrl.searchParams.append(
"mintMethod",
Buffer.from(mintMethodStringified, "utf-8").toString("base64"),
);
}
if (eligibilityMethod) {
checkoutUrl.searchParams.append(
"eligibilityMethod",
Buffer.from(eligibilityMethodStringified, "utf-8").toString("base64"),
);
}
if (contractType) {
checkoutUrl.searchParams.append("contractType", contractType);
}
if (contractArgs) {
checkoutUrl.searchParams.append(
"contractArgs",
// Base 64 encode
Buffer.from(contractArgsStringified, "utf-8").toString("base64"),
);
}
if (appName) {
checkoutUrl.searchParams.append("appName", appName);
}
if (recipientWalletAddress) {
checkoutUrl.searchParams.append("wallet", recipientWalletAddress);
}
if (emailAddress) {
checkoutUrl.searchParams.append("username", emailAddress);
}
if (quantity) {
checkoutUrl.searchParams.append("quantity", quantity.toString());
}
if (metadata) {
checkoutUrl.searchParams.append(
"metadata",
encodeURIComponent(metadataStringified),
);
}
// Default button if the app doesn't pass one in.
const clickableElement = children || (
<button
style={{
backgroundColor: "#cf3781",
padding: "8px 20px 8px 20px",
borderRadius: "8px",
color: "white",
fontWeight: "bold",
}}
>
{"Buy Now"}
</button>
);
switch (display) {
case PaperCheckoutDisplay.POPUP: {
const onClick = () => {
openCenteredPopup({
url: checkoutUrl.href,
h: options.height,
w: options.width,
win: window,
target: "_blank",
});
if (onOpenCheckout) {
onOpenCheckout();
}
};
return <a onClick={onClick}>{clickableElement}</a>;
}
case PaperCheckoutDisplay.NEW_TAB: {
const onClick = () => {
window.open(checkoutUrl, "_blank");
if (onOpenCheckout) {
onOpenCheckout();
}
};
return <a onClick={onClick}>{clickableElement}</a>;
}
case PaperCheckoutDisplay.MODAL: {
const onOpen = () => {
setIsOpen(true);
if (onOpenCheckout) {
onOpenCheckout();
}
};
const onClose = () => {
setIsOpen(false);
if (onCloseCheckout) {
onCloseCheckout();
}
};
return (
<PaperCheckoutModal
clickableElement={clickableElement}
checkoutUrl={checkoutUrl.href}
width={options.width}
height={options.height}
isOpen={isOpen}
onOpen={onOpen}
onClose={onClose}
/>
);
}
case PaperCheckoutDisplay.DRAWER: {
const onOpen = () => {
setIsOpen(true);
if (onOpenCheckout) {
onOpenCheckout();
}
};
const onClose = () => {
setIsOpen(false);
if (onCloseCheckout) {
onCloseCheckout();
}
};
return (
<PaperCheckoutDrawer
clickableElement={clickableElement}
checkoutUrl={checkoutUrl.href}
width={options.width}
isOpen={isOpen}
onOpen={onOpen}
onClose={onClose}
/>
);
}
case PaperCheckoutDisplay.EMBED: {
return (
<iframe
src={checkoutUrl.href}
width={options.width}
height={options.height}
/>
);
}
default:
console.error(`Invalid or unimplemented display type: ${display}`);
return <></>;
}
};
const inlineStyles: { [name: string]: any } = {
overlay: {
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
margin: 0,
zIndex: 1000,
overflow: "hidden",
display: "flex",
visibility: "hidden",
opacity: 0,
transition: "all 0.2s ease",
},
overlayIsVisible: {
visibility: "visible",
opacity: 1,
background: "#0008",
},
modalOverlay: {
alignItems: "center",
justifyContent: "center",
},
drawerOverlay: {
justifyContent: "flex-end",
},
modalDialog: {
position: "relative",
overflow: "hidden",
borderRadius: "8px",
visibility: "hidden",
opacity: 0,
top: "5%",
transition: "all 0.2s ease",
maxWidth: "100vw",
},
modalDialogIsVisible: {
visibility: "visible",
opacity: 1,
top: 0,
},
drawerDialog: {
position: "relative",
visibility: "hidden",
opacity: 0,
right: "-10%",
transition: "all 0.2s ease",
},
drawerDialogIsVisible: {
visibility: "visible",
opacity: 1,
right: 0,
},
modalCloseButton: {
position: "absolute",
top: "0.1em",
right: "0.2em",
borderRadius: "8px",
fontSize: "x-large",
padding: "0 0.4em",
color: "#888",
},
};
const PaperCheckoutDrawer = ({
clickableElement,
checkoutUrl,
width,
isOpen,
onOpen,
onClose,
}: {
clickableElement: React.ReactNode;
checkoutUrl: string;
width: number;
isOpen: boolean;
onOpen: () => void;
onClose: () => void;
}) => {
return (
<>
<a onClick={onOpen}>{clickableElement}</a>
<div
className="paper-overlay"
style={{
...inlineStyles.overlay,
...(isOpen ? inlineStyles.overlayIsVisible : {}),
...inlineStyles.drawerOverlay,
}}
>
<div
className="paper-drawer"
style={{
...inlineStyles.drawerDialog,
...(isOpen ? inlineStyles.drawerDialogIsVisible : {}),
width,
}}
>
<button onClick={onClose} style={inlineStyles.modalCloseButton}>
&times;
</button>
{isOpen && <iframe src={checkoutUrl} width="100%" height="100%" />}
</div>
</div>
</>
);
};
const PaperCheckoutModal = ({
clickableElement,
checkoutUrl,
width,
height,
isOpen,
onOpen,
onClose,
}: {
clickableElement: React.ReactNode;
checkoutUrl: string;
width: number;
height: number;
isOpen: boolean;
onOpen: () => void;
onClose: () => void;
}) => {
// Set the max height to the window's inner height.
// This method handles mobile browser heights more reliably than 100vh.
const [innerHeight, setInnerHeight] = useState<number>(height);
useEffect(() => setInnerHeight(window.innerHeight), []);
return (
<>
<a onClick={onOpen}>{clickableElement}</a>
<div
className="paper-overlay"
style={{
...inlineStyles.overlay,
...(isOpen ? inlineStyles.overlayIsVisible : {}),
...inlineStyles.modalOverlay,
}}
>
<div
className="paper-modal"
style={{
...inlineStyles.modalDialog,
...(isOpen ? inlineStyles.modalDialogIsVisible : {}),
width,
height,
maxHeight: innerHeight,
}}
>
<button onClick={onClose} style={inlineStyles.modalCloseButton}>
&times;
</button>
{isOpen && <iframe src={checkoutUrl} width="100%" height="100%" />}
</div>
</div>
</>
);
};
@@ -0,0 +1,156 @@
// CHANGED: packageJson import + sdk-version + clientId
import { css } from "@emotion/css";
import type {
PaymentsSDKError,
SupportedChainName,
} from "@thirdweb-dev/payments";
import { PAPER_APP_URL, PaymentsSDKErrorCode } from "@thirdweb-dev/payments";
import React, { useEffect } from "react";
import { usePaymentsSDKContext } from "../Provider";
import { openCenteredPopup } from "../lib/utils/popup";
import { Button } from "./common/Button";
import packageJson from "../../../package.json";
interface VerifyOwnershipWithPaperProps {
onSuccess?: (code: string) => void;
onError?: (error: PaymentsSDKError) => void;
onWindowClose?: () => void;
children?: ({
onClick,
}: {
onClick: () => void;
}) => React.ReactNode | React.ReactNode;
redirectUrl?: string;
className?: string;
chainName?: SupportedChainName;
clientId?: string;
}
const enum VERIFY_OWNERSHIP_WITH_PAPER_EVENT_TYPE {
USER_LOGIN_SUCCESS = "userLoginSuccess",
USER_LOGIN_FAILED = "userLoginFailed",
USER_CLOSE_LOGIN_PAGE = "userCloseLoginPage",
}
export const VerifyOwnershipWithPaper: React.FC<
VerifyOwnershipWithPaperProps
> = ({
onSuccess,
onError,
onWindowClose,
className,
children,
redirectUrl,
chainName,
clientId,
}) => {
const { chainName: chainNameContext, clientId: clientIdContext } =
usePaymentsSDKContext();
const isChildrenFunction = typeof children === "function";
const chainNameToUse = chainName || chainNameContext;
const clientIdToUse = clientId || clientIdContext;
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
const data = event.data;
switch (data.eventType) {
case VERIFY_OWNERSHIP_WITH_PAPER_EVENT_TYPE.USER_LOGIN_SUCCESS:
if (onSuccess) {
onSuccess(data.values.accessCode);
}
break;
case VERIFY_OWNERSHIP_WITH_PAPER_EVENT_TYPE.USER_CLOSE_LOGIN_PAGE: {
if (onWindowClose) {
onWindowClose();
}
break;
}
case VERIFY_OWNERSHIP_WITH_PAPER_EVENT_TYPE.USER_LOGIN_FAILED:
if (onError) {
onError({
code: PaymentsSDKErrorCode.UserLoginFailed,
error: new Error(PaymentsSDKErrorCode.UserLoginFailed),
});
}
break;
default:
// Ignore unrecognized event
}
};
window.addEventListener("message", handleMessage);
return () => {
window.removeEventListener("message", handleMessage);
};
}, []);
const url = new URL("/sdk/v1/login-with-paper", PAPER_APP_URL);
url.searchParams.append("chainName", chainNameToUse);
url.searchParams.append("clientId", clientIdToUse);
url.searchParams.append("redirectUrl", redirectUrl || "");
const onClick = () => {
const loginWindow = openCenteredPopup({
url: url.href,
target: "_blank",
win: window,
w: 400,
h: 600,
});
loginWindow?.focus();
};
return (
<>
{children && isChildrenFunction ? (
children({ onClick })
) : children ? (
<a
onClick={onClick}
data-thirdweb-sdk-version={`${packageJson.name}@${packageJson.version}`}
>
{children}
</a>
) : (
<Button
onClick={onClick}
className={className}
data-thirdweb-sdk-version={`${packageJson.name}@${packageJson.version}`}
>
<div
className={css`
display: flex;
align-items: center;
`}
>
<span style={{ marginRight: "8px" }}>{"Login with"}</span>{" "}
<svg
width="15"
height="30"
viewBox="0 0 26 49"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M25.8824 8.91421C25.8824 8.47803 25.5996 8.09218 25.1838 7.96071L0 0V11.7963L14.7899 16.276L2.77311 24.5V34.4815L5.58074 37.3767L3.1009 39.6283C2.89214 39.8178 2.77311 40.0867 2.77311 40.3687V49L11.0924 42.6481V32.6667L8.60325 30.5207L25.8824 19.963V8.91421Z"
fill="#19A8D6"
/>
<path
d="M25.8824 8.91421C25.8824 8.47803 25.5996 8.09218 25.1838 7.96071L0 0V11.7963L25.8824 19.963V8.91421Z"
fill="#39D0FF"
/>
<path
d="M11.0924 32.6667L2.77311 24.5V34.4815L11.0924 42.6481V32.6667Z"
fill="#39D0FF"
/>
</svg>{" "}
<span style={{ marginLeft: "5px" }}>{"Paper"}</span>
</div>
</Button>
)}
</>
);
};
@@ -0,0 +1,63 @@
// Unchanged
import { css, cx } from "@emotion/css";
import React from "react";
import { Spinner } from "./Spinner";
export const Button = ({
isLoading = false,
loadingText = "",
...props
}: {
isLoading?: boolean;
loadingText?: string;
} & React.HTMLProps<HTMLButtonElement>): React.ReactElement => {
return (
<button
{...props}
type={props.type as "button" | "submit" | "reset" | undefined}
disabled={isLoading || props.disabled}
className={cx(buttonClass, props.className)}
>
{isLoading ? (
<div
className={css`
display: flex;
justify-content: center;
align-items: center;
width: 100%;
`}
>
<Spinner
className={
loadingText
? css`
margin-right: 0.5rem;
`
: ""
}
/>
{loadingText}
</div>
) : (
props.children
)}
</button>
);
};
const buttonClass = css`
padding-top: 0.625rem;
padding-bottom: 0.625rem;
padding-left: 1.25rem;
padding-right: 1.25rem;
background-color: #1f2937;
color: #ffffff;
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 600;
justify-content: flex-start;
align-items: center;
border-radius: 0.5rem;
border: none;
cursor: pointer;
`;
@@ -0,0 +1,13 @@
// Unchanged
import React, { forwardRef, memo } from "react";
const IFrameBase = /* @__PURE__ */ forwardRef(
(
props: React.IframeHTMLAttributes<HTMLIFrameElement>,
ref: React.ForwardedRef<HTMLIFrameElement>,
) => {
return <iframe ref={ref} {...props} />;
},
);
IFrameBase.displayName = "IFrameBase";
export const IFrameWrapper = /* @__PURE__ */ memo(IFrameBase);
@@ -0,0 +1,209 @@
// UNCHANGED
import { css, cx } from "@emotion/css";
import { Dialog, Transition } from "@headlessui/react";
import React, { Fragment, useEffect } from "react";
import { opacity0, opacity1 } from "../../lib/utils/styles";
export const Modal: React.FC<{
isOpen: boolean;
onClose: () => void;
escapeToClose?: boolean;
clickOutsideModalToClose?: boolean;
bgColor?: string;
isFullScreen?: boolean;
hasCloseButton?: boolean;
children: React.ReactNode;
}> = ({
isOpen,
onClose,
escapeToClose = true,
clickOutsideModalToClose = false,
bgColor = "#FAFAFA",
isFullScreen,
hasCloseButton = true,
children,
}) => {
useEffect(() => {
const keyDownHandler = (event: KeyboardEvent) => {
if (event.key === "Escape") {
onClose();
}
};
if (escapeToClose) {
document.addEventListener("keydown", keyDownHandler);
}
return () => {
document.removeEventListener("keydown", keyDownHandler);
};
}, []);
const additionalDialogClasses = isFullScreen ? "" : dialogContainedClasses;
const dialogPanelBg = isFullScreen ? "transparent" : bgColor;
return (
<Transition appear show={isOpen} as={Fragment}>
<Dialog
as="div"
className={cx(
"paper-modal",
css`
position: relative;
z-index: 1000;
`,
)}
onClose={clickOutsideModalToClose ? onClose : () => {}}
>
{/* Overlay */}
<Transition.Child
as={Fragment}
enter={enterClasses}
enterFrom={opacity0}
enterTo={opacity1}
leave={leaveClasses}
leaveFrom={opacity1}
leaveTo={opacity0}
>
<div
className={cx(
"paper-modal-overlay",
css`
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.5);
`,
)}
/>
</Transition.Child>
<div
className={css`
overflow-y: auto;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
`}
>
<div
className={css`
display: flex;
justify-content: center;
align-items: center;
min-height: 100%;
`}
>
<Transition.Child
as={Fragment}
enter={enterClasses}
enterFrom={cx(opacity0, scale95)}
enterTo={cx(opacity1, scale100)}
leave={leaveClasses}
leaveFrom={cx(opacity1, scale100)}
leaveTo={cx(opacity0, scale95)}
>
<Dialog.Panel
className={cx(
"paper-modal-content",
dialogClasses,
additionalDialogClasses,
)}
style={{ backgroundColor: dialogPanelBg }}
>
{hasCloseButton && <CloseButton onClose={onClose} />}
{children}
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
);
};
const CloseButton: React.FC<{ onClose: () => void }> = ({ onClose }) => {
return (
<button
aria-label="close modal"
className={css`
position: absolute;
top: 0.5rem;
right: 0.5rem;
padding: 0.5rem;
border-radius: 9999px;
cursor: pointer;
&:hover {
background-color: rgba(160, 174, 192, 0.1);
}
&:active {
background-color: rgba(160, 174, 192, 0.2);
}
`}
>
<svg
className={css`
width: 1.25rem;
height: 1.25rem;
`}
onClick={onClose}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
></path>
</svg>
</button>
);
};
const enterClasses = css`
transition-duration: 300ms;
transition-timing-function: ease-out;
`;
const leaveClasses = css`
transition-duration: 200ms;
transition-timing-function: ease-in;
`;
const scale95 = css`
transform: scale(0.95);
`;
const scale100 = css`
transform: scale(1);
`;
const dialogClasses = css`
overflow-x: auto;
overflow-y: hidden;
position: relative;
transition-property: all;
text-align: left;
vertical-align: middle;
max-width: 100%;
max-height: 100%;
border-radius: 0.5rem;
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 10px 10px -5px rgba(0, 0, 0, 0.04);
`;
const dialogContainedClasses = css`
padding: 1.25rem;
@media (min-width: 640px) {
margin: 1rem;
}
`;
@@ -0,0 +1,30 @@
// UNCHANGED
import { css, cx, keyframes } from "@emotion/css";
import React from "react";
export const Spinner = ({
className,
}: {
className?: string;
}): React.ReactElement => {
return <div id="loader" className={cx(loader, className)}></div>;
};
const Spin = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const loader = css`
border: 0.15rem solid #f3f3f300;
border-top: 0.15rem solid #000000;
border-left: 0.15rem solid #000000;
border-radius: 50%;
width: 4rem;
height: 4rem;
animation: ${Spin} 1s linear infinite;
`;
@@ -0,0 +1,24 @@
// UNCHANGED
import { css } from "@emotion/css";
import { Spinner } from "./Spinner";
export const SpinnerWrapper: React.FC = () => {
return (
<div className={wrapperStyle}>
<Spinner className={spinnerStyle} />
</div>
);
};
const wrapperStyle = css`
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
`;
const spinnerStyle = css`
color: #000000;
width: 2rem;
height: 2rem;
`;

Some files were not shown because too many files have changed in this diff Show More