diff --git a/playground/nextjs-app-router/components/AppProvider.tsx b/playground/nextjs-app-router/components/AppProvider.tsx
index f8add83c5..b67e7f967 100644
--- a/playground/nextjs-app-router/components/AppProvider.tsx
+++ b/playground/nextjs-app-router/components/AppProvider.tsx
@@ -8,6 +8,7 @@ import { WalletPreference } from './form/wallet-type';
export enum OnchainKitComponent {
Identity = 'identity',
Swap = 'swap',
+ SwapDefault = 'swap-default',
Transaction = 'transaction',
Wallet = 'wallet',
}
diff --git a/playground/nextjs-app-router/components/Demo.tsx b/playground/nextjs-app-router/components/Demo.tsx
index ce6b68e77..1ddb5aa39 100644
--- a/playground/nextjs-app-router/components/Demo.tsx
+++ b/playground/nextjs-app-router/components/Demo.tsx
@@ -7,6 +7,7 @@ import { WalletType } from '@/components/form/wallet-type';
import { useContext, useEffect, useState } from 'react';
import IdentityDemo from './demo/Identity';
import SwapDemo from './demo/Swap';
+import SwapDefaultDemo from './demo/SwapDefault';
import TransactionDemo from './demo/Transaction';
import WalletDemo from './demo/Wallet';
import { ActiveComponent } from './form/active-component';
@@ -56,6 +57,10 @@ function Demo() {
return ;
}
+ if (activeComponent === OnchainKitComponent.SwapDefault) {
+ return ;
+ }
+
return <>>;
}
diff --git a/playground/nextjs-app-router/components/demo/SwapDefault.tsx b/playground/nextjs-app-router/components/demo/SwapDefault.tsx
new file mode 100644
index 000000000..30e1f0d64
--- /dev/null
+++ b/playground/nextjs-app-router/components/demo/SwapDefault.tsx
@@ -0,0 +1,118 @@
+import { ENVIRONMENT, ENVIRONMENT_VARIABLES } from '@/lib/constants';
+import { type LifecycleStatus, SwapDefault } from '@coinbase/onchainkit/swap';
+import type { SwapError } from '@coinbase/onchainkit/swap';
+import type { Token } from '@coinbase/onchainkit/token';
+import { useCallback, useContext } from 'react';
+import type { TransactionReceipt } from 'viem';
+import { base } from 'viem/chains';
+import { AppContext } from '../AppProvider';
+
+const DEFAULT_MAX_SLIPPAGE = 3;
+
+function SwapDefaultComponent() {
+ const { chainId, defaultMaxSlippage, paymasters } = useContext(AppContext);
+
+ const degenToken: Token = {
+ name: 'DEGEN',
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ symbol: 'DEGEN',
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ chainId: base.id,
+ };
+
+ const ethToken: Token = {
+ name: 'ETH',
+ address: '',
+ symbol: 'ETH',
+ decimals: 18,
+ image:
+ 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ chainId: base.id,
+ };
+
+ const usdcToken: Token = {
+ name: 'USDC',
+ address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
+ symbol: 'USDC',
+ decimals: 6,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/44/2b/442b80bd16af0c0d9b22e03a16753823fe826e5bfd457292b55fa0ba8c1ba213-ZWUzYjJmZGUtMDYxNy00NDcyLTg0NjQtMWI4OGEwYjBiODE2',
+ chainId: base.id,
+ };
+
+ const wethToken: Token = {
+ name: 'Wrapped Ether',
+ address: '0x4200000000000000000000000000000000000006',
+ symbol: 'WETH',
+ decimals: 6,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/47/bc/47bc3593c2dec7c846b66b7ba5f6fa6bd69ec34f8ebb931f2a43072e5aaac7a8-YmUwNmRjZDUtMjczYy00NDFiLWJhZDUtMzgwNjFmYWM0Njkx',
+ chainId: base.id,
+ };
+
+ const to = [ethToken, degenToken, usdcToken, wethToken];
+ const from = [usdcToken, degenToken, ethToken, wethToken];
+
+ const handleOnStatus = useCallback((lifecycleStatus: LifecycleStatus) => {
+ console.log('Status:', lifecycleStatus);
+ }, []);
+
+ const handleOnSuccess = useCallback(
+ (transactionReceipt: TransactionReceipt) => {
+ console.log('Success:', transactionReceipt);
+ },
+ [],
+ );
+
+ const handleOnError = useCallback((swapError: SwapError) => {
+ console.log('Error:', swapError);
+ }, []);
+
+ return (
+
+ {chainId !== base.id ? (
+
+
+ Swap Demo is only available on Base.
+
+ You're connected to a different network. Switch to Base to continue
+ using the app.
+
+
+ ) : (
+ <>>
+ )}
+
+ {ENVIRONMENT_VARIABLES[ENVIRONMENT.ENVIRONMENT] === 'production' &&
+ chainId === base.id ? (
+
+ Note: Swap is disabled on production. To test, run the app locally.
+
+ ) : null}
+
+
+
+ );
+}
+
+export default function SwapDefaultDemo() {
+ return (
+
+
+
+ );
+}
diff --git a/playground/nextjs-app-router/components/form/active-component.tsx b/playground/nextjs-app-router/components/form/active-component.tsx
index 00e4da4f5..fbcdb9af6 100644
--- a/playground/nextjs-app-router/components/form/active-component.tsx
+++ b/playground/nextjs-app-router/components/form/active-component.tsx
@@ -30,6 +30,9 @@ export function ActiveComponent() {
Transaction
Swap
+
+ SwapDefault
+
Wallet
diff --git a/src/swap/components/SwapButton.tsx b/src/swap/components/SwapButton.tsx
index 24ab8d8da..d616ab561 100644
--- a/src/swap/components/SwapButton.tsx
+++ b/src/swap/components/SwapButton.tsx
@@ -27,6 +27,9 @@ export function SwapButton({ className, disabled = false }: SwapButtonReact) {
disabled ||
isLoading;
+ // disable swap if to and from token are the same
+ const isSwapInvalid = to.token?.address === from.token?.address;
+
// prompt user to connect wallet
if (!isDisabled && !address) {
return ;
@@ -44,7 +47,7 @@ export function SwapButton({ className, disabled = false }: SwapButtonReact) {
className,
)}
onClick={() => handleSubmit()}
- disabled={isDisabled}
+ disabled={isDisabled || isSwapInvalid}
data-testid="ockSwapButton_Button"
>
{isLoading ? (
diff --git a/src/swap/components/SwapDefault.tsx b/src/swap/components/SwapDefault.tsx
new file mode 100644
index 000000000..f788845e6
--- /dev/null
+++ b/src/swap/components/SwapDefault.tsx
@@ -0,0 +1,63 @@
+import { SwapDefaultReact } from '../types';
+import { Swap } from './Swap';
+import { SwapAmountInput } from './SwapAmountInput';
+import { SwapButton } from './SwapButton';
+import { SwapMessage } from './SwapMessage';
+import { SwapSettings } from './SwapSettings';
+import { SwapSettingsSlippageDescription } from './SwapSettingsSlippageDescription';
+import { SwapSettingsSlippageInput } from './SwapSettingsSlippageInput';
+import { SwapSettingsSlippageTitle } from './SwapSettingsSlippageTitle';
+import { SwapToast } from './SwapToast';
+import { SwapToggleButton } from './SwapToggleButton';
+
+export function SwapDefault({
+ config,
+ className,
+ disabled,
+ experimental,
+ from,
+ isSponsored = false,
+ onError,
+ onStatus,
+ onSuccess,
+ title = 'Swap',
+ to,
+}: SwapDefaultReact) {
+ return (
+
+
+ Max. slippage
+
+ Your swap will revert if the prices change by more than the selected
+ percentage.
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/swap/index.ts b/src/swap/index.ts
index e65691192..56f645247 100644
--- a/src/swap/index.ts
+++ b/src/swap/index.ts
@@ -2,6 +2,7 @@
export { Swap } from './components/Swap';
export { SwapAmountInput } from './components/SwapAmountInput';
export { SwapButton } from './components/SwapButton';
+export { SwapDefault } from './components/SwapDefault';
export { SwapMessage } from './components/SwapMessage';
export { SwapSettings } from './components/SwapSettings';
export { SwapSettingsSlippageDescription } from './components/SwapSettingsSlippageDescription';
diff --git a/src/swap/types.ts b/src/swap/types.ts
index 9124efb48..bd179238c 100644
--- a/src/swap/types.ts
+++ b/src/swap/types.ts
@@ -296,6 +296,15 @@ export type SwapReact = {
title?: string; // Title for the Swap component. (default: "Swap")
};
+/**
+ * Note: exported as public Type
+ */
+export type SwapDefaultReact = {
+ to: Token[]; // Swappable tokens
+ from: Token[]; // Swappable tokens
+ disabled?: boolean; // Disables swap button
+} & Omit;
+
/**
* Note: exported as public Type
*/