diff --git a/packages/checkout/sdk/src/widgets/definitions/configurations/checkout.ts b/packages/checkout/sdk/src/widgets/definitions/configurations/checkout.ts new file mode 100644 index 0000000000..cab1fa1947 --- /dev/null +++ b/packages/checkout/sdk/src/widgets/definitions/configurations/checkout.ts @@ -0,0 +1,6 @@ +/* eslint-disable max-len */ +import { WidgetConfiguration } from './widget'; + +export type CheckoutWidgetConfiguration = { + +} & WidgetConfiguration; diff --git a/packages/checkout/sdk/src/widgets/definitions/configurations/index.ts b/packages/checkout/sdk/src/widgets/definitions/configurations/index.ts index 4fc7348e4e..1927000b2a 100644 --- a/packages/checkout/sdk/src/widgets/definitions/configurations/index.ts +++ b/packages/checkout/sdk/src/widgets/definitions/configurations/index.ts @@ -6,3 +6,4 @@ export * from './onramp'; export * from './sale'; export * from './theme'; export * from './widget'; +export * from './checkout'; diff --git a/packages/checkout/sdk/src/widgets/definitions/events/checkout.ts b/packages/checkout/sdk/src/widgets/definitions/events/checkout.ts new file mode 100644 index 0000000000..d22666b0a3 --- /dev/null +++ b/packages/checkout/sdk/src/widgets/definitions/events/checkout.ts @@ -0,0 +1,3 @@ +export enum CheckoutEventType { + +} diff --git a/packages/checkout/sdk/src/widgets/definitions/events/index.ts b/packages/checkout/sdk/src/widgets/definitions/events/index.ts index ba4c92cefe..60579cdead 100644 --- a/packages/checkout/sdk/src/widgets/definitions/events/index.ts +++ b/packages/checkout/sdk/src/widgets/definitions/events/index.ts @@ -6,3 +6,4 @@ export * from './sale'; export * from './bridge'; export * from './orchestration'; export * from './onramp'; +export * from './checkout'; diff --git a/packages/checkout/sdk/src/widgets/definitions/events/widgets.ts b/packages/checkout/sdk/src/widgets/definitions/events/widgets.ts index d34cfadd5c..e790a72a00 100644 --- a/packages/checkout/sdk/src/widgets/definitions/events/widgets.ts +++ b/packages/checkout/sdk/src/widgets/definitions/events/widgets.ts @@ -12,6 +12,7 @@ export enum IMTBLWidgetEvents { IMTBL_BRIDGE_WIDGET_EVENT = 'imtbl-bridge-widget', IMTBL_ONRAMP_WIDGET_EVENT = 'imtbl-onramp-widget', IMTBL_SALE_WIDGET_EVENT = 'imtbl-sale-widget', + IMTBL_CHECKOUT_WIDGET_EVENT = 'imtbl-checkout-widget', } /** diff --git a/packages/checkout/sdk/src/widgets/definitions/parameters/checkout.ts b/packages/checkout/sdk/src/widgets/definitions/parameters/checkout.ts new file mode 100644 index 0000000000..d816bc49dc --- /dev/null +++ b/packages/checkout/sdk/src/widgets/definitions/parameters/checkout.ts @@ -0,0 +1,7 @@ +/* eslint-disable max-len */ +import { WidgetLanguage } from '../configurations'; + +export type CheckoutWidgetParams = { + /** The language to use for the checkout widget */ + language?: WidgetLanguage; +}; diff --git a/packages/checkout/sdk/src/widgets/definitions/parameters/index.ts b/packages/checkout/sdk/src/widgets/definitions/parameters/index.ts index 61b3c99b4f..1af32c0e81 100644 --- a/packages/checkout/sdk/src/widgets/definitions/parameters/index.ts +++ b/packages/checkout/sdk/src/widgets/definitions/parameters/index.ts @@ -4,3 +4,4 @@ export * from './wallet'; export * from './swap'; export * from './onramp'; export * from './sale'; +export * from './checkout'; diff --git a/packages/checkout/sdk/src/widgets/definitions/types.ts b/packages/checkout/sdk/src/widgets/definitions/types.ts index 854dc2465d..544185cdc3 100644 --- a/packages/checkout/sdk/src/widgets/definitions/types.ts +++ b/packages/checkout/sdk/src/widgets/definitions/types.ts @@ -34,6 +34,7 @@ import { WalletDisconnect, WalletEventType, WalletNetworkSwitch, + CheckoutEventType, } from './events'; import { BridgeWidgetParams, @@ -41,6 +42,7 @@ import { SwapWidgetParams, WalletWidgetParams, OnRampWidgetParams, + CheckoutWidgetParams, } from './parameters'; import { SaleWidgetParams } from './parameters/sale'; import { @@ -50,6 +52,7 @@ import { SaleWidgetConfiguration, SwapWidgetConfiguration, WalletWidgetConfiguration, + CheckoutWidgetConfiguration, } from './configurations'; import { WidgetTheme } from './configurations/theme'; @@ -63,6 +66,7 @@ export enum WidgetType { BRIDGE = 'bridge', ONRAMP = 'onramp', SALE = 'sale', + CHECKOUT = 'checkout', } /** @@ -79,7 +83,8 @@ export type WidgetConfigurations = { [WidgetType.SWAP]: SwapWidgetConfiguration, [WidgetType.BRIDGE]: BridgeWidgetConfiguration, [WidgetType.ONRAMP]: OnrampWidgetConfiguration, - [WidgetType.SALE]: SaleWidgetConfiguration + [WidgetType.SALE]: SaleWidgetConfiguration, + [WidgetType.CHECKOUT]: CheckoutWidgetConfiguration, }; // Mapping each widget type to their parameters @@ -89,7 +94,8 @@ export type WidgetParameters = { [WidgetType.SWAP]: SwapWidgetParams, [WidgetType.BRIDGE]: BridgeWidgetParams, [WidgetType.ONRAMP]: OnRampWidgetParams, - [WidgetType.SALE]: SaleWidgetParams + [WidgetType.SALE]: SaleWidgetParams, + [WidgetType.CHECKOUT]: CheckoutWidgetParams, }; /** @@ -101,7 +107,8 @@ export type WidgetEventTypes = { [WidgetType.SWAP]: SwapEventType | OrchestrationEventType, [WidgetType.BRIDGE]: BridgeEventType | OrchestrationEventType, [WidgetType.ONRAMP]: OnRampEventType | OrchestrationEventType, - [WidgetType.SALE]: SaleEventType | OrchestrationEventType + [WidgetType.SALE]: SaleEventType | OrchestrationEventType, + [WidgetType.CHECKOUT]: CheckoutEventType | OrchestrationEventType, }; // Mapping of Orchestration events to their payloads @@ -168,7 +175,10 @@ export type WidgetEventData = { [SaleEventType.REQUEST_BRIDGE]: {}, [SaleEventType.REQUEST_SWAP]: {}, [SaleEventType.REQUEST_ONRAMP]: {}, - } & OrchestrationMapping & ProviderEventMapping + } & OrchestrationMapping & ProviderEventMapping, + + [WidgetType.CHECKOUT]: { + } & OrchestrationMapping & ProviderEventMapping, }; /** diff --git a/packages/checkout/widgets-lib/src/factory.ts b/packages/checkout/widgets-lib/src/factory.ts index dae6f65ea7..9bb2cd36c8 100644 --- a/packages/checkout/widgets-lib/src/factory.ts +++ b/packages/checkout/widgets-lib/src/factory.ts @@ -8,6 +8,7 @@ import { WidgetConfigurations, } from '@imtbl/checkout-sdk'; import { Web3Provider } from '@ethersproject/providers'; +import { Environment } from '@imtbl/config'; import { Connect } from './widgets/connect/ConnectWidgetRoot'; import { Swap } from './widgets/swap/SwapWidgetRoot'; import { OnRamp } from './widgets/on-ramp/OnRampWidgetRoot'; @@ -21,6 +22,7 @@ import { DEFAULT_THEME, } from './lib'; import './i18n'; +import { CheckoutWidgetRoot } from './widgets/checkout/CheckoutWidgetRoot'; export class WidgetsFactory implements IWidgetsFactory { private sdk: Checkout; @@ -92,6 +94,15 @@ export class WidgetsFactory implements IWidgetsFactory { provider, }) as Widget as Widget; } + case WidgetType.CHECKOUT: { + if (this.sdk.config.environment === Environment.PRODUCTION) { + throw new Error('Checkout widget is not supported in production'); + } + return new CheckoutWidgetRoot(this.sdk, { + config: { ...this.widgetConfig, ...(config) }, + provider, + }) as Widget as Widget; + } default: throw new Error('widget type not supported'); } diff --git a/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidget.tsx b/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidget.tsx new file mode 100644 index 0000000000..7eca30ee61 --- /dev/null +++ b/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidget.tsx @@ -0,0 +1,26 @@ +import { CheckoutWidgetParams } from '@imtbl/checkout-sdk'; +import { + useContext, +} from 'react'; +import { ConnectLoaderContext } from '../../context/connect-loader-context/ConnectLoaderContext'; +import { StrongCheckoutWidgetsConfig } from '../../lib/withDefaultWidgetConfig'; + +export type CheckoutWidgetInputs = CheckoutWidgetParams & { + config: StrongCheckoutWidgetsConfig, +}; + +export default function CheckoutWidget(props: CheckoutWidgetInputs) { + const { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + config, + } = props; + + const { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + connectLoaderState: { checkout, provider }, + } = useContext(ConnectLoaderContext); + + return ( +
+ ); +} diff --git a/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidgetRoot.tsx b/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidgetRoot.tsx new file mode 100644 index 0000000000..5b23576f0c --- /dev/null +++ b/packages/checkout/widgets-lib/src/widgets/checkout/CheckoutWidgetRoot.tsx @@ -0,0 +1,87 @@ +import { + ChainId, + IMTBLWidgetEvents, + CheckoutWidgetConfiguration, + CheckoutWidgetParams, + WidgetProperties, + WidgetTheme, + WidgetType, +} from '@imtbl/checkout-sdk'; +import React, { Suspense } from 'react'; +import { ConnectLoader, ConnectLoaderParams } from '../../components/ConnectLoader/ConnectLoader'; +import { ThemeProvider } from '../../components/ThemeProvider/ThemeProvider'; +import { CustomAnalyticsProvider } from '../../context/analytics-provider/CustomAnalyticsProvider'; +import { HandoverProvider } from '../../context/handover-context/HandoverProvider'; +import i18n from '../../i18n'; +import { getL1ChainId, getL2ChainId } from '../../lib'; +import { LoadingView } from '../../views/loading/LoadingView'; +import { Base } from '../BaseWidgetRoot'; + +const CheckoutWidget = React.lazy(() => import('./CheckoutWidget')); + +export class CheckoutWidgetRoot extends Base { + protected eventTopic: IMTBLWidgetEvents = IMTBLWidgetEvents.IMTBL_CHECKOUT_WIDGET_EVENT; + + protected getValidatedProperties( + { config }: WidgetProperties, + ): WidgetProperties { + let validatedConfig: CheckoutWidgetConfiguration | undefined; + + if (config) { + validatedConfig = config; + if (config.theme === WidgetTheme.LIGHT) validatedConfig.theme = WidgetTheme.LIGHT; + else validatedConfig.theme = WidgetTheme.DARK; + } + + return { + config: validatedConfig, + }; + } + + protected getValidatedParameters(params: CheckoutWidgetParams): CheckoutWidgetParams { + const validatedParams = params; + + return validatedParams; + } + + protected render() { + if (!this.reactRoot) return; + + const { t } = i18n; + const connectLoaderParams: ConnectLoaderParams = { + targetChainId: this.checkout.config.isProduction + ? ChainId.IMTBL_ZKEVM_MAINNET + : ChainId.IMTBL_ZKEVM_TESTNET, + // walletProviderName: this.parameters?.walletProviderName, + web3Provider: this.web3Provider, + checkout: this.checkout, + allowedChains: [getL1ChainId(this.checkout.config), getL2ChainId(this.checkout.config)], + }; + + this.reactRoot.render( + + + + + { }} + > + + } + > + + + + + + + , + ); + } +} diff --git a/packages/checkout/widgets-sample-app/src/App.tsx b/packages/checkout/widgets-sample-app/src/App.tsx index f8091a47cd..605d251aaf 100644 --- a/packages/checkout/widgets-sample-app/src/App.tsx +++ b/packages/checkout/widgets-sample-app/src/App.tsx @@ -27,6 +27,10 @@ function App() { On-ramp Widget

+
+ Checkout Widget +
+
new Checkout({ baseConfig: { environment: Environment.SANDBOX } }), []); + const factory = useMemo(() => new WidgetsFactory(checkout, { theme: WidgetTheme.DARK }), [checkout]); + const checkoutWidget = useMemo(() => factory.create(WidgetType.CHECKOUT), [checkout]) + + const unmount = () => { checkoutWidget.unmount() } + const mount = () => { checkoutWidget.mount('checkout') } + const update = (theme: WidgetTheme) => { checkoutWidget.update({ config: { theme } }) } + + useEffect(() => { + mount(); + }, []); + + return ( +
+

Checkout Widget

+
+ + + + + +
+ ); +} + +export default CheckoutUI; diff --git a/packages/checkout/widgets-sample-app/src/index.tsx b/packages/checkout/widgets-sample-app/src/index.tsx index d1ad9a25fa..b75af754ef 100644 --- a/packages/checkout/widgets-sample-app/src/index.tsx +++ b/packages/checkout/widgets-sample-app/src/index.tsx @@ -11,6 +11,7 @@ import OnRampUI from "./components/ui/on-ramp/onRamp"; import { PassportLoginCallback } from './components/ui/marketplace-orchestrator/PassportLoginCallback'; import { Marketplace } from './components/ui/marketplace-orchestrator'; import { SaleUI } from './components/ui/sale/sale'; +import CheckoutUI from './components/ui/checkout/checkout'; const router = createBrowserRouter([ { @@ -41,6 +42,10 @@ const router = createBrowserRouter([ path: '/sale', element: , }, + { + path: '/checkout', + element: , + }, { path: '/marketplace-orchestrator', element: