@@ -42,26 +40,6 @@ The available components are:
```
-```html [return html]
-
-```
-
-:::
-
@@ -87,6 +65,11 @@ pnpm add @coinbase/onchainkit
pnpm add react@18 react-dom@18 wagmi@2 @coinbase/wallet-sdk@4 permissionless
```
+```bash [bun]
+bun add @coinbase/onchainkit
+bun add react@18 react-dom@18 wagmi@2 @coinbase/wallet-sdk@4 permissionless
+```
+
:::
## Required providers
diff --git a/site/docs/pages/xmtp/introduction.mdx b/site/docs/pages/xmtp/introduction.mdx
index 4e91c9434d..afffd32ed6 100644
--- a/site/docs/pages/xmtp/introduction.mdx
+++ b/site/docs/pages/xmtp/introduction.mdx
@@ -53,6 +53,10 @@ yarn add @coinbase/onchainkit @xmtp/frames-validator
pnpm add @coinbase/onchainkit @xmtp/frames-validator
```
+```bash [bun]
+bun add @coinbase/onchainkit @xmtp/frames-validator
+```
+
:::
To assist you in handling interactions from XMTP, and extracting the `verifiedWalletAddress` from a POST payload, here is the XMTP Kit which includes:
diff --git a/site/docs/public/assets/copy-api-key.png b/site/docs/public/assets/copy-api-key.png
new file mode 100644
index 0000000000..e257c9f831
Binary files /dev/null and b/site/docs/public/assets/copy-api-key.png differ
diff --git a/site/docs/public/logo/v0-19.png b/site/docs/public/logo/v0-19.png
new file mode 100644
index 0000000000..f128c1260b
Binary files /dev/null and b/site/docs/public/logo/v0-19.png differ
diff --git a/site/package.json b/site/package.json
index 51d6d00a01..ab7b06aa5d 100644
--- a/site/package.json
+++ b/site/package.json
@@ -8,7 +8,7 @@
"preview": "vocs preview"
},
"dependencies": {
- "@coinbase/onchainkit": "0.18.6",
+ "@coinbase/onchainkit": "0.19.6",
"@coinbase/wallet-sdk": "^4.0.0",
"@tanstack/react-query": "^5.36.0",
"@types/react": "latest",
diff --git a/site/sidebar.ts b/site/sidebar.ts
index cfab10aee4..0fed9daae5 100644
--- a/site/sidebar.ts
+++ b/site/sidebar.ts
@@ -3,14 +3,26 @@ import type { Sidebar } from 'vocs';
export const sidebar = [
{
text: 'Introduction',
+ items: [{ text: 'Getting Started', link: '/getting-started' }],
+ },
+ {
+ text: 'Guides',
+ items: [
+ {
+ text: 'Contribution',
+ link: '/guides/contribution',
+ },
+ ],
+ },
+ {
+ text: 'Config',
items: [
- { text: 'Getting Started', link: '/getting-started' },
{
text: 'Components',
items: [
{
text: 'OnchainKitProvider',
- link: '/onchainkit-provider',
+ link: '/config/onchainkit-provider',
},
],
},
@@ -20,17 +32,13 @@ export const sidebar = [
items: [
{
text: 'isBase',
- link: '/is-base',
+ link: '/config/is-base',
},
],
},
{
text: 'Types',
- link: '/types',
- },
- {
- text: 'Contribution Guide',
- link: '/contribution-guide',
+ link: '/config/types',
},
],
},
diff --git a/site/yarn.lock b/site/yarn.lock
index e445af1ef6..7987a0fcf1 100644
--- a/site/yarn.lock
+++ b/site/yarn.lock
@@ -334,9 +334,9 @@ __metadata:
languageName: node
linkType: hard
-"@coinbase/onchainkit@npm:0.18.6":
- version: 0.18.6
- resolution: "@coinbase/onchainkit@npm:0.18.6"
+"@coinbase/onchainkit@npm:0.19.6":
+ version: 0.19.6
+ resolution: "@coinbase/onchainkit@npm:0.19.6"
peerDependencies:
"@coinbase/wallet-sdk": ^4
"@tanstack/react-query": ^5
@@ -348,7 +348,7 @@ __metadata:
react-dom: ^18
viem: ^2
wagmi: ^2
- checksum: 5feeb2c09bbec0549e26bc777238593e94ce1fa76f2b29c4bb7bf75ca46015a54a0fd284d26e4d5f3a66794118acf7a877830938a15add42825c85cb134fe694
+ checksum: 3732db3903b63db7c65670f570cffb4013329c7bf83027c1416f38996cfd76c41b23f054daec2e3fad05c5322c144a72f5fa9bbfde97a04205fbdad67805c36e
languageName: node
linkType: hard
@@ -7657,7 +7657,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "onchainkit@workspace:."
dependencies:
- "@coinbase/onchainkit": "npm:0.18.6"
+ "@coinbase/onchainkit": "npm:0.19.6"
"@coinbase/wallet-sdk": "npm:^4.0.0"
"@tanstack/react-query": "npm:^5.36.0"
"@types/react": "npm:latest"
diff --git a/src/OnchainKitConfig.test.ts b/src/OnchainKitConfig.test.ts
index 59c951e89c..3b8a27b33e 100644
--- a/src/OnchainKitConfig.test.ts
+++ b/src/OnchainKitConfig.test.ts
@@ -1,4 +1,3 @@
-import fs from 'fs';
import { baseSepolia } from 'viem/chains';
import { getOnchainKitConfig, setOnchainKitConfig } from './OnchainKitConfig';
import { getRPCUrl } from './getRPCUrl';
@@ -24,7 +23,7 @@ describe('OnchainKitConfig', () => {
const chain = baseSepolia;
const schemaId = '0x123';
const apiKey = 'test-api-key';
- const rpcUrl = `https://api.developer.coinbase.com/rpc/v1/base-sepolia/test-api-key`;
+ const rpcUrl = 'https://api.developer.coinbase.com/rpc/v1/base-sepolia/test-api-key';
setOnchainKitConfig({ chain, schemaId, apiKey });
expect(getOnchainKitConfig('chain')).toEqual(chain);
expect(getOnchainKitConfig('schemaId')).toEqual(schemaId);
@@ -36,7 +35,7 @@ describe('OnchainKitConfig', () => {
const chain = baseSepolia;
const schemaId = '0x123';
const apiKey = 'updated-api-key';
- const rpcUrl = `https://api.developer.coinbase.com/rpc/v1/base-sepolia/updated-api-key`;
+ const rpcUrl = 'https://api.developer.coinbase.com/rpc/v1/base-sepolia/updated-api-key';
setOnchainKitConfig({ chain, schemaId, apiKey, rpcUrl });
expect(getOnchainKitConfig('chain')).toEqual(chain);
expect(getOnchainKitConfig('schemaId')).toEqual(schemaId);
diff --git a/src/OnchainKitConfig.ts b/src/OnchainKitConfig.ts
index cad3ef2f55..d774eaffc3 100644
--- a/src/OnchainKitConfig.ts
+++ b/src/OnchainKitConfig.ts
@@ -1,5 +1,5 @@
import { baseSepolia } from 'viem/chains';
-import { OnchainKitConfig, SetOnchainKitConfig } from './types';
+import type { OnchainKitConfig, SetOnchainKitConfig } from './types';
// The ONCHAIN_KIT_CONFIG is not exported at index.ts,
// but only acccessed through the get and set functions.
diff --git a/src/OnchainKitProvider.test.tsx b/src/OnchainKitProvider.test.tsx
index f5a6db2f14..1b70d599c2 100644
--- a/src/OnchainKitProvider.test.tsx
+++ b/src/OnchainKitProvider.test.tsx
@@ -6,7 +6,8 @@ import { base } from 'viem/chains';
import { render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
-import { EASSchemaUid } from './identity/types';
+import type { EASSchemaUid } from './identity/types';
+import { setOnchainKitConfig, ONCHAIN_KIT_CONFIG } from './OnchainKitConfig';
import { OnchainKitProvider } from './OnchainKitProvider';
import { useOnchainKit } from './useOnchainKit';
@@ -20,6 +21,17 @@ const TestComponent = () => {
);
};
+jest.mock('./OnchainKitConfig', () => ({
+ setOnchainKitConfig: jest.fn(),
+ ONCHAIN_KIT_CONFIG: {
+ address: null,
+ apiKey: null,
+ chain: base,
+ rpcUrl: null,
+ schemaId: null,
+ },
+}));
+
describe('OnchainKitProvider', () => {
const schemaId: EASSchemaUid = `0x${'1'.repeat(64)}`;
const apiKey = 'test-api-key';
@@ -66,4 +78,19 @@ describe('OnchainKitProvider', () => {
),
).not.toThrow();
});
+
+ it('should call setOnchainKitConfig with the correct values', async () => {
+ render(
+
+
+ ,
+ );
+ expect(setOnchainKitConfig).toHaveBeenCalledWith({
+ address: null,
+ apiKey,
+ chain: base,
+ rpcUrl: null,
+ schemaId,
+ });
+ });
});
diff --git a/src/OnchainKitProvider.tsx b/src/OnchainKitProvider.tsx
index f84460f912..4924f97991 100644
--- a/src/OnchainKitProvider.tsx
+++ b/src/OnchainKitProvider.tsx
@@ -1,7 +1,7 @@
import { createContext, useMemo } from 'react';
import { checkHashLength } from './utils/checkHashLength';
-import { ONCHAIN_KIT_CONFIG } from './OnchainKitConfig';
-import { OnchainKitContextType, OnchainKitProviderReact } from './types';
+import { ONCHAIN_KIT_CONFIG, setOnchainKitConfig } from './OnchainKitConfig';
+import type { OnchainKitContextType, OnchainKitProviderReact } from './types';
export const OnchainKitContext = createContext
(ONCHAIN_KIT_CONFIG);
@@ -20,13 +20,15 @@ export function OnchainKitProvider({
throw Error('EAS schemaId must be 64 characters prefixed with "0x"');
}
const value = useMemo(() => {
- return {
+ const onchainKitConfig = {
address: address ?? null,
apiKey: apiKey ?? null,
chain: chain,
rpcUrl: rpcUrl ?? null,
schemaId: schemaId ?? null,
};
+ setOnchainKitConfig(onchainKitConfig);
+ return onchainKitConfig;
}, [address, chain, schemaId, apiKey, rpcUrl]);
return {children};
}
diff --git a/src/definitions/base.ts b/src/definitions/base.ts
index 72e0cec056..463586c3bc 100644
--- a/src/definitions/base.ts
+++ b/src/definitions/base.ts
@@ -1,5 +1,5 @@
import { base } from 'viem/chains';
-import { EASChainDefinition } from '../identity/types';
+import type { EASChainDefinition } from '../identity/types';
export const easChainBase: EASChainDefinition = {
id: base.id,
diff --git a/src/definitions/baseSepolia.ts b/src/definitions/baseSepolia.ts
index 386d23a9cb..85346bdf62 100644
--- a/src/definitions/baseSepolia.ts
+++ b/src/definitions/baseSepolia.ts
@@ -1,5 +1,5 @@
import { baseSepolia } from 'viem/chains';
-import { EASChainDefinition } from '../identity/types';
+import type { EASChainDefinition } from '../identity/types';
export const easChainBaseSepolia: EASChainDefinition = {
id: baseSepolia.id,
diff --git a/src/definitions/optimism.ts b/src/definitions/optimism.ts
index 674cf842f2..df31b3845c 100644
--- a/src/definitions/optimism.ts
+++ b/src/definitions/optimism.ts
@@ -1,5 +1,5 @@
import { optimism } from 'viem/chains';
-import { EASChainDefinition } from '../identity/types';
+import type { EASChainDefinition } from '../identity/types';
// More details in https://docs.optimism.io/chain/identity/schemas
export const easChainOptimism: EASChainDefinition = {
diff --git a/src/definitions/swap.ts b/src/definitions/swap.ts
index c128dc056a..0269eb5470 100644
--- a/src/definitions/swap.ts
+++ b/src/definitions/swap.ts
@@ -1 +1,2 @@
-export const ListSwapAssets = 'cdp_listSwapAssets';
+export const CDP_LISTSWAPASSETS = 'cdp_listSwapAssets';
+export const CDP_GETSWAPQUOTE = 'cdp_getSwapQuote';
diff --git a/src/farcaster/getFarcasterUserAddress.ts b/src/farcaster/getFarcasterUserAddress.ts
index 8a7820b9c4..9d20b377b3 100644
--- a/src/farcaster/getFarcasterUserAddress.ts
+++ b/src/farcaster/getFarcasterUserAddress.ts
@@ -1,6 +1,6 @@
import { getCustodyAddressForFidNeynar } from '../utils/neynar/user/getCustodyAddressForFidNeynar';
import { getVerifiedAddressesForFidNeynar } from '../utils/neynar/user/getVerifiedAddressesForFidNeynar';
-import { GetFarcasterUserAddressResponse } from './types';
+import type { GetFarcasterUserAddressResponse } from './types';
type GetFarcasterUserAddressOptions =
| {
diff --git a/src/frame/components/FrameMetadata.tsx b/src/frame/components/FrameMetadata.tsx
index 099b0492d5..5c80c39bb7 100644
--- a/src/frame/components/FrameMetadata.tsx
+++ b/src/frame/components/FrameMetadata.tsx
@@ -2,49 +2,7 @@ import { Fragment } from 'react';
import type { FrameMetadataReact } from '../types';
/**
- * FrameMetadata component
- *
- * @description
* This component is used to add React Frame Metadata to the page.
- *
- * @example
- * ```tsx
- *
- * ```
- *
- * @param {FrameMetadataReact} props - The metadata for the frame.
- * @param {{ [protocolIdentifier: string]: string; }} accepts - The types of protocol the frame accepts.
- * @param {Array<{ label: string, action?: string }>} props.buttons - The buttons.
- * @param {string | { src: string, aspectRatio?: string }} props.image - The image URL.
- * @param {string} props.input - The input text.
- * @param {boolean} props.isOpenFrame: Whether the frame uses the Open Frames standard.
- * @param {string} props.ogDescription - The Open Graph description.
- * @param {string} props.ogTitle - The Open Graph title.
- * @param {string} props.postUrl - The post URL.
- * @param {number} props.refreshPeriod - The refresh period.
- * @param {object} props.state - The serialized state (e.g. JSON) for the frame.
- * @param {React.ComponentType | undefined} props.wrapper - The wrapper component meta tags are rendered in.
- * @returns {React.ReactElement} The FrameMetadata component.
*/
export function FrameMetadata({
accepts = {},
diff --git a/src/getRPCUrl.test.ts b/src/getRPCUrl.test.ts
index e74392d1d0..73e1d03fed 100644
--- a/src/getRPCUrl.test.ts
+++ b/src/getRPCUrl.test.ts
@@ -14,7 +14,7 @@ describe('OnchainKitConfig RPC URL', () => {
it('should return the correct config value', () => {
const chain = baseSepolia;
const apiKey = 'test-api-key';
- const rpcUrl = `https://api.developer.coinbase.com/rpc/v1/base-sepolia/test-api-key`;
+ const rpcUrl = 'https://api.developer.coinbase.com/rpc/v1/base-sepolia/test-api-key';
setOnchainKitConfig({ chain, apiKey });
expect(getOnchainKitConfig('chain')).toEqual(chain);
expect(getOnchainKitConfig('apiKey')).toEqual(apiKey);
@@ -25,7 +25,7 @@ describe('OnchainKitConfig RPC URL', () => {
const chain = baseSepolia;
const apiKey = 'test-api-key';
const newApiKey = 'updated-api-key';
- const expectedRpcUrl = `https://api.developer.coinbase.com/rpc/v1/base-sepolia/updated-api-key`;
+ const expectedRpcUrl = 'https://api.developer.coinbase.com/rpc/v1/base-sepolia/updated-api-key';
setOnchainKitConfig({ chain, apiKey });
expect(getOnchainKitConfig('chain')).toEqual(chain);
expect(getOnchainKitConfig('apiKey')).toEqual(apiKey);
diff --git a/src/identity/components/Avatar.css b/src/identity/components/Avatar.css
new file mode 100644
index 0000000000..86cecc7352
--- /dev/null
+++ b/src/identity/components/Avatar.css
@@ -0,0 +1,3 @@
+.ock-avatar {
+ @apply rounded-[50%];
+}
diff --git a/src/identity/components/Avatar.test.tsx b/src/identity/components/Avatar.test.tsx
index a2a2967a73..20734d7113 100644
--- a/src/identity/components/Avatar.test.tsx
+++ b/src/identity/components/Avatar.test.tsx
@@ -108,7 +108,7 @@ describe('Avatar Component', () => {
render();
await waitFor(() => {
- const inner = screen.getByTestId('ockAvatarBadgeInner');
+ const inner = screen.getByTestId('ockAvatarBadgeContainer');
expect(inner).toBeInTheDocument();
const badge = screen.getByTestId('ockBadge');
expect(badge).toBeInTheDocument();
diff --git a/src/identity/components/Avatar.tsx b/src/identity/components/Avatar.tsx
index 6994684cdc..c60fb25a3e 100644
--- a/src/identity/components/Avatar.tsx
+++ b/src/identity/components/Avatar.tsx
@@ -80,14 +80,13 @@ export function Avatar({
return (
diff --git a/src/identity/components/Badge.css b/src/identity/components/Badge.css
new file mode 100644
index 0000000000..0d04c7eeab
--- /dev/null
+++ b/src/identity/components/Badge.css
@@ -0,0 +1,3 @@
+.ock-badge {
+ @apply rounded-[50%] border border-transparent;
+}
diff --git a/src/identity/components/Badge.test.tsx b/src/identity/components/Badge.test.tsx
index 28cb019085..c61b84ff97 100644
--- a/src/identity/components/Badge.test.tsx
+++ b/src/identity/components/Badge.test.tsx
@@ -21,7 +21,7 @@ describe('WithAvatarBadge Component', () => {
await waitFor(() => {
const badge = screen.queryByTestId('ockBadge');
- expect(badge).toHaveStyle('border-radius: 50%; height: 12px; width: 12px;');
+ expect(badge).toHaveStyle('height: 12px; width: 12px;');
const bg = screen.queryByTestId('ockBadgeBackground');
expect(bg).toHaveAttribute('fill', '#0052FF');
const ticker = screen.queryByTestId('ockBadgeTicker');
@@ -34,9 +34,7 @@ describe('WithAvatarBadge Component', () => {
await waitFor(() => {
const badge = screen.queryByTestId('ockBadge');
- expect(badge).toHaveStyle(
- 'border: 1px solid red; border-radius: 50%; height: 14px; width: 14px;',
- );
+ expect(badge).toHaveStyle('border-color: red; height: 14px; width: 14px;');
const bg = screen.queryByTestId('ockBadgeBackground');
expect(bg).toHaveAttribute('fill', '#0052FF');
const ticker = screen.queryByTestId('ockBadgeTicker');
@@ -49,7 +47,7 @@ describe('WithAvatarBadge Component', () => {
await waitFor(() => {
const badge = screen.queryByTestId('ockBadge');
- expect(badge).toHaveStyle('border-radius: 50%; height: 12px; width: 12px;');
+ expect(badge).toHaveStyle('height: 12px; width: 12px;');
const bg = screen.queryByTestId('ockBadgeBackground');
expect(bg).toHaveAttribute('fill', 'green');
const ticker = screen.queryByTestId('ockBadgeTicker');
@@ -62,7 +60,7 @@ describe('WithAvatarBadge Component', () => {
await waitFor(() => {
const badge = screen.queryByTestId('ockBadge');
- expect(badge).toHaveStyle('border-radius: 50%; height: 12px; width: 12px;');
+ expect(badge).toHaveStyle('height: 12px; width: 12px;');
const bg = screen.queryByTestId('ockBadgeBackground');
expect(bg).toHaveAttribute('fill', '#0052FF');
const ticker = screen.queryByTestId('ockBadgeTicker');
diff --git a/src/identity/components/Badge.tsx b/src/identity/components/Badge.tsx
index c0af5a99b7..c725d22ff1 100644
--- a/src/identity/components/Badge.tsx
+++ b/src/identity/components/Badge.tsx
@@ -2,8 +2,6 @@ import { BadgeReact } from '../types';
/**
* Badge component.
- *
- * @returns {JSX.Element} The JSX element representing the badge, which is a blue circle with a white checkmark.
*/
export function Badge({
backgroundColor = '#0052FF',
@@ -15,11 +13,10 @@ export function Badge({
const badgeSize = borderColor ? '14px' : '12px';
return (
{
);
await waitFor(() => {
- const inner = screen.queryByTestId('ockAvatarBadgeInner');
+ const inner = screen.queryByTestId('ockAvatarBadgeContainer');
expect(inner).toBeNull();
});
});
@@ -54,7 +54,7 @@ describe('WithAvatarBadge Component', () => {
);
await waitFor(() => {
- const inner = screen.getByTestId('ockAvatarBadgeInner');
+ const inner = screen.getByTestId('ockAvatarBadgeContainer');
expect(inner).toBeInTheDocument();
const badge = screen.queryByTestId('ockBadge');
expect(badge).toBeNull();
@@ -76,7 +76,7 @@ describe('WithAvatarBadge Component', () => {
);
await waitFor(() => {
- const inner = screen.getByTestId('ockAvatarBadgeInner');
+ const inner = screen.getByTestId('ockAvatarBadgeContainer');
expect(inner).toBeInTheDocument();
const badge = screen.getByTestId('ockBadge');
expect(badge).toBeInTheDocument();
diff --git a/src/identity/components/WithAvatarBadge.tsx b/src/identity/components/WithAvatarBadge.tsx
index 41b136a997..b3d5709500 100644
--- a/src/identity/components/WithAvatarBadge.tsx
+++ b/src/identity/components/WithAvatarBadge.tsx
@@ -1,24 +1,12 @@
-import type { Address } from 'viem';
-
import { useOnchainKit } from '../../useOnchainKit';
import { useAttestations } from '../hooks/useAttestations';
import { Badge } from './Badge';
-
-type WithAvatarBadgeInnerProps = {
- children: React.ReactNode;
- address: Address;
-};
-
-type WithAvatarBadgeProps = {
- children: React.ReactNode;
- showAttestation: boolean;
- address: Address;
-};
+import type { WithAvatarBadgeInnerReact, WithAvatarBadgeReact } from '../types';
const ERROR_MESSAGE =
'EAS schemaId must provided in OnchainKitProvider context when using WithNameBadge showAttestation is true.';
-function WithAvatarBadgeInner({ children, address }: WithAvatarBadgeInnerProps) {
+function WithAvatarBadgeInner({ children, address }: WithAvatarBadgeInnerReact) {
const onchainKitContext = useOnchainKit();
// SchemaId is required to fetch attestations
if (!onchainKitContext?.schemaId) {
@@ -31,35 +19,11 @@ function WithAvatarBadgeInner({ children, address }: WithAvatarBadgeInnerProps)
schemaId: onchainKitContext?.schemaId,
});
return (
-
+
{children}
{attestations && attestations[0] && (
-
-
+
@@ -68,7 +32,7 @@ function WithAvatarBadgeInner({ children, address }: WithAvatarBadgeInnerProps)
);
}
-export function WithAvatarBadge({ children, showAttestation, address }: WithAvatarBadgeProps) {
+export function WithAvatarBadge({ children, showAttestation, address }: WithAvatarBadgeReact) {
if (!showAttestation) {
return children;
}
diff --git a/src/identity/components/WithNameBadge.css b/src/identity/components/WithNameBadge.css
new file mode 100644
index 0000000000..46638e458d
--- /dev/null
+++ b/src/identity/components/WithNameBadge.css
@@ -0,0 +1,6 @@
+.ock-withnamebadge-container {
+ @apply flex items-center;
+}
+.ock-withnamebadge-inner {
+ @apply ml-1;
+}
diff --git a/src/identity/components/WithNameBadge.test.tsx b/src/identity/components/WithNameBadge.test.tsx
index 236d17b384..92ceb9d345 100644
--- a/src/identity/components/WithNameBadge.test.tsx
+++ b/src/identity/components/WithNameBadge.test.tsx
@@ -35,7 +35,7 @@ describe('WithNameBadge Component', () => {
);
await waitFor(() => {
- const inner = screen.queryByTestId('ockNameBadgeInner');
+ const inner = screen.queryByTestId('ockNameBadgeContainer');
expect(inner).toBeNull();
});
});
@@ -54,7 +54,7 @@ describe('WithNameBadge Component', () => {
);
await waitFor(() => {
- const inner = screen.getByTestId('ockNameBadgeInner');
+ const inner = screen.getByTestId('ockNameBadgeContainer');
expect(inner).toBeInTheDocument();
const badge = screen.queryByTestId('ockBadge');
expect(badge).toBeNull();
@@ -76,7 +76,7 @@ describe('WithNameBadge Component', () => {
);
await waitFor(() => {
- const inner = screen.getByTestId('ockNameBadgeInner');
+ const inner = screen.getByTestId('ockNameBadgeContainer');
expect(inner).toBeInTheDocument();
const badge = screen.getByTestId('ockBadge');
expect(badge).toBeInTheDocument();
diff --git a/src/identity/components/WithNameBadge.tsx b/src/identity/components/WithNameBadge.tsx
index 2acf751968..68e936c1db 100644
--- a/src/identity/components/WithNameBadge.tsx
+++ b/src/identity/components/WithNameBadge.tsx
@@ -1,24 +1,12 @@
-import type { Address } from 'viem';
-
import { useOnchainKit } from '../../useOnchainKit';
import { useAttestations } from '../hooks/useAttestations';
import { Badge } from './Badge';
-
-type WithNameBadgeInnerProps = {
- children: React.ReactNode;
- address: Address;
-};
-
-type WithNameBadgeProps = {
- children: React.ReactNode;
- showAttestation?: boolean;
- address: Address;
-};
+import type { WithNameBadgeInnerReact, WithNameBadgeReact } from '../types';
const ERROR_MESSAGE =
'EAS schemaId must provided in OnchainKitProvider context when using WithNameBadge showAttestation is true.';
-function WithNameBadgeInner({ children, address }: WithNameBadgeInnerProps) {
+function WithNameBadgeInner({ children, address }: WithNameBadgeInnerReact) {
const onchainKitContext = useOnchainKit();
// SchemaId is required to fetch attestations
if (!onchainKitContext?.schemaId) {
@@ -31,10 +19,10 @@ function WithNameBadgeInner({ children, address }: WithNameBadgeInnerProps) {
schemaId: onchainKitContext?.schemaId,
});
return (
-
+
{children}
{attestations && attestations[0] && (
-
+
)}
@@ -42,7 +30,7 @@ function WithNameBadgeInner({ children, address }: WithNameBadgeInnerProps) {
);
}
-export function WithNameBadge({ children, showAttestation, address }: WithNameBadgeProps) {
+export function WithNameBadge({ children, showAttestation, address }: WithNameBadgeReact) {
if (!showAttestation) {
return children;
}
diff --git a/src/identity/core/getAvatar.test.tsx b/src/identity/core/getAvatar.test.tsx
index 527aca4232..cc2a47698a 100644
--- a/src/identity/core/getAvatar.test.tsx
+++ b/src/identity/core/getAvatar.test.tsx
@@ -1,7 +1,6 @@
/**
* @jest-environment jsdom
*/
-
import { getAvatar } from './getAvatar';
import { publicClient } from '../../network/client';
@@ -20,7 +19,7 @@ describe('getAvatar', () => {
mockGetEnsAvatar.mockResolvedValue(expectedAvatarUrl);
- const avatarUrl = await getAvatar(ensName);
+ const avatarUrl = await getAvatar({ ensName });
expect(avatarUrl).toBe(expectedAvatarUrl);
expect(mockGetEnsAvatar).toHaveBeenCalledWith({ name: ensName });
@@ -31,6 +30,6 @@ describe('getAvatar', () => {
mockGetEnsAvatar.mockRejectedValue(new Error('This is an error'));
- await expect(getAvatar(ensName)).rejects.toThrow('This is an error');
+ await expect(getAvatar({ ensName })).rejects.toThrow('This is an error');
});
});
diff --git a/src/identity/core/getAvatar.ts b/src/identity/core/getAvatar.ts
index 2ada5ec53f..db3e64d428 100644
--- a/src/identity/core/getAvatar.ts
+++ b/src/identity/core/getAvatar.ts
@@ -1,9 +1,9 @@
import { normalize } from 'viem/ens';
import { publicClient } from '../../network/client';
-import { GetAvatarReturnType } from '../types';
+import { GetAvatar, GetAvatarReturnType } from '../types';
-export const getAvatar = async (ensName: string): Promise
=> {
+export const getAvatar = async (params: GetAvatar): Promise => {
return await publicClient.getEnsAvatar({
- name: normalize(ensName),
+ name: normalize(params.ensName),
});
};
diff --git a/src/identity/hooks/useAvatar.ts b/src/identity/hooks/useAvatar.ts
index 2bd22d62db..71af493077 100644
--- a/src/identity/hooks/useAvatar.ts
+++ b/src/identity/hooks/useAvatar.ts
@@ -20,7 +20,7 @@ export const useAvatar = ({ ensName }: UseAvatarOptions, queryOptions?: UseAvata
return useQuery({
queryKey: ['useAvatar', ensActionKey],
queryFn: async () => {
- return await getAvatar(ensName);
+ return getAvatar({ ensName });
},
gcTime: cacheTime,
enabled,
diff --git a/src/identity/index.ts b/src/identity/index.ts
index c4cce28dbb..5631195c68 100644
--- a/src/identity/index.ts
+++ b/src/identity/index.ts
@@ -15,6 +15,7 @@ export type {
EASSchemaUid,
EASChainDefinition,
GetAttestationsOptions,
+ GetAvatar,
GetAvatarReturnType,
GetNameReturnType,
} from './types';
diff --git a/src/identity/types.ts b/src/identity/types.ts
index f34214afce..a59c29acbe 100644
--- a/src/identity/types.ts
+++ b/src/identity/types.ts
@@ -72,6 +72,13 @@ export type GetAttestationsOptions = {
limit?: number;
};
+/**
+ * Note: exported as public Type
+ */
+export type GetAvatar = {
+ ensName: string; // The ENS name to fetch the avatar for.
+};
+
/**
* Note: exported as public Type
*/
@@ -107,3 +114,25 @@ export type UseAttestations = {
chain: Chain;
schemaId: Address;
};
+
+export type WithAvatarBadgeInnerReact = {
+ children: React.ReactNode;
+ address: Address;
+};
+
+export type WithAvatarBadgeReact = {
+ children: React.ReactNode;
+ showAttestation: boolean;
+ address: Address;
+};
+
+export type WithNameBadgeInnerReact = {
+ children: React.ReactNode;
+ address: Address;
+};
+
+export type WithNameBadgeReact = {
+ children: React.ReactNode;
+ showAttestation?: boolean;
+ address: Address;
+};
diff --git a/src/index.css b/src/index.css
index 56740ada68..904dd47b7b 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,2 +1,11 @@
+@import url('./identity/components/Avatar.css');
+@import url('./identity/components/Badge.css');
+@import url('./identity/components/WithAvatarBadge.css');
+@import url('./identity/components/WithNameBadge.css');
@import url('./token/components/TextInput.css');
@import url('./token/components/TokenChip.css');
+@import url('./token/components/TokenImage.css');
+@import url('./token/components/TokenRow.css');
+@import url('./token/components/TokenSelector.css');
+@import url('./token/components/TokenSelectorDropdown.css');
+@import url('./wallet/components/ConnectAccount.css');
diff --git a/src/isBase.ts b/src/isBase.ts
index 81147362fd..7415c18ad0 100644
--- a/src/isBase.ts
+++ b/src/isBase.ts
@@ -1,5 +1,5 @@
-import { isBaseOptions } from './types';
import { baseSepolia, base } from 'viem/chains';
+import type { isBaseOptions } from './types';
/**
* isBase
diff --git a/src/queries/attestations.test.ts b/src/queries/attestations.test.ts
index fe76cf145d..2c44331ee1 100644
--- a/src/queries/attestations.test.ts
+++ b/src/queries/attestations.test.ts
@@ -1,5 +1,5 @@
import {
- GetAttestationsByFilterOptions,
+ type GetAttestationsByFilterOptions,
getAttestationQueryVariables,
getAttestationsByFilter,
attestationQuery,
diff --git a/src/queries/attestations.ts b/src/queries/attestations.ts
index 9d118ab298..3737aea492 100644
--- a/src/queries/attestations.ts
+++ b/src/queries/attestations.ts
@@ -1,8 +1,8 @@
import { gql } from 'graphql-request';
-import type { Address, Chain } from 'viem';
import { getAddress } from 'viem';
-import { EASSchemaUid, Attestation } from '../identity/types';
import { createEasGraphQLClient } from '../network/createEasGraphQLClient';
+import type { Address, Chain } from 'viem';
+import type { EASSchemaUid, Attestation } from '../identity/types';
/**
* Type representing the filter options used for querying EAS Attestations.
diff --git a/src/queries/request.test.ts b/src/queries/request.test.ts
index 6b80bb6fe3..bdcf2c700f 100644
--- a/src/queries/request.test.ts
+++ b/src/queries/request.test.ts
@@ -1,5 +1,4 @@
import { setOnchainKitConfig } from '../OnchainKitConfig';
-import { version } from '../version';
import { buildRequestBody, sendRequest } from './request';
describe('request', () => {
@@ -47,7 +46,6 @@ describe('request', () => {
body: JSON.stringify(requestBody),
headers: {
'Content-Type': 'application/json',
- onchainkit_version: version,
},
});
expect(response).toEqual(mockResponse);
diff --git a/src/queries/request.ts b/src/queries/request.ts
index e042633d42..135106bf58 100644
--- a/src/queries/request.ts
+++ b/src/queries/request.ts
@@ -23,7 +23,6 @@ export type JSONRPCResult = {
const POST_METHOD = 'POST';
const JSON_HEADERS = {
'Content-Type': 'application/json',
- onchainkit_version: version,
};
const JSON_RPC_VERSION = '2.0';
diff --git a/src/swap/core/formatDecimals.test.ts b/src/swap/core/formatDecimals.test.ts
new file mode 100644
index 0000000000..7ee9463e1c
--- /dev/null
+++ b/src/swap/core/formatDecimals.test.ts
@@ -0,0 +1,63 @@
+import { formatDecimals } from './formatDecimals';
+
+describe('formatDecimals', () => {
+ it('should format the amount correctly with default decimals when inputInDecimals is true', () => {
+ const amount = '1500000000000000000';
+ const expectedFormattedAmount = '1.5';
+ const result = formatDecimals(amount, true, 18);
+ expect(result).toEqual(expectedFormattedAmount);
+ });
+
+ it('should format the amount correctly with custom decimals when inputInDecimals is true', () => {
+ const amount = '1500000000000000000';
+ const decimals = 9;
+ const expectedFormattedAmount = '1500000000';
+ const result = formatDecimals(amount, true, decimals);
+ expect(result).toEqual(expectedFormattedAmount);
+ });
+
+ it('should format the amount correctly with default decimals when inputInDecimals is false', () => {
+ const amount = '1.5';
+ const expectedFormattedAmount = '1500000000000000000';
+ const result = formatDecimals(amount, false, 18);
+ expect(result).toEqual(expectedFormattedAmount);
+ });
+
+ it('should format the amount correctly with custom decimals when inputInDecimals is false', () => {
+ const amount = '1.5';
+ const decimals = 9;
+ const expectedFormattedAmount = '1500000000';
+ const result = formatDecimals(amount, false, decimals);
+ expect(result).toEqual(expectedFormattedAmount);
+ });
+
+ it('should format the amount correctly with default decimals when inputInDecimals is true and decimals is not provided', () => {
+ const amount = '1500000000000000000';
+ const expectedFormattedAmount = '1.5';
+ const result = formatDecimals(amount);
+ expect(result).toEqual(expectedFormattedAmount);
+ });
+
+ it('should format the amount correctly with default decimals when inputInDecimals is true and decimals is provided', () => {
+ const amount = '1500000000';
+ const expectedFormattedAmount = '1.5';
+ const decimals = 9;
+ const result = formatDecimals(amount, true, decimals);
+ expect(result).toEqual(expectedFormattedAmount);
+ });
+
+ it('should format the amount correctly with default decimals when inputInDecimals is false and decimals is provided', () => {
+ const amount = '1.5';
+ const expectedFormattedAmount = '1500000000';
+ const decimals = 9;
+ const result = formatDecimals(amount, false, decimals);
+ expect(result).toEqual(expectedFormattedAmount);
+ });
+
+ it('should format the amount correctly with default decimals when inputInDecimals is false and decimals is not provided', () => {
+ const amount = '1.5';
+ const expectedFormattedAmount = '1.5e-18';
+ const result = formatDecimals(amount);
+ expect(result).toEqual(expectedFormattedAmount);
+ });
+});
diff --git a/src/swap/core/formatDecimals.ts b/src/swap/core/formatDecimals.ts
new file mode 100644
index 0000000000..0b4e4f74f9
--- /dev/null
+++ b/src/swap/core/formatDecimals.ts
@@ -0,0 +1,9 @@
+/**
+ * Formats an amount according to the decimals. Defaults to 18 decimals for ERC-20s.
+ */
+export function formatDecimals(amount: string, inputInDecimals = true, decimals = 18): string {
+ if (inputInDecimals) {
+ return (Number(amount) / 10 ** decimals).toString();
+ }
+ return (Number(amount) * 10 ** decimals).toString();
+}
diff --git a/src/swap/core/getParamsForToken.test.ts b/src/swap/core/getParamsForToken.test.ts
new file mode 100644
index 0000000000..19c9eecb50
--- /dev/null
+++ b/src/swap/core/getParamsForToken.test.ts
@@ -0,0 +1,150 @@
+import type { Token } from '../../token';
+import { getParamsForToken } from './getParamsForToken';
+
+describe('getParamsForToken', () => {
+ it('should return the correct GetQuoteAPIParams object', () => {
+ const from: Token = {
+ name: 'ETH',
+ address: '',
+ symbol: 'ETH',
+ decimals: 18,
+ image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ chainId: 8453,
+ };
+ const to: Token = {
+ name: 'DEGEN',
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ symbol: 'DEGEN',
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ chainId: 8453,
+ };
+ const amount = '1.5';
+ const amountReference = 'from';
+
+ const expectedParams = {
+ from: 'ETH',
+ to: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ amount: '1500000000000000000',
+ amountReference: 'from',
+ };
+
+ const result = getParamsForToken({
+ from,
+ to,
+ amount,
+ amountReference,
+ });
+
+ expect(result).toEqual(expectedParams);
+ });
+
+ it('should use the default for amountReference', () => {
+ const from: Token = {
+ name: 'ETH',
+ address: '',
+ symbol: 'ETH',
+ decimals: 18,
+ image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ chainId: 8453,
+ };
+ const to: Token = {
+ name: 'DEGEN',
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ symbol: 'DEGEN',
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ chainId: 8453,
+ };
+ const amount = '1.5';
+
+ const expectedParams = {
+ from: 'ETH',
+ to: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ amount: '1500000000000000000',
+ amountReference: 'from',
+ };
+
+ const result = getParamsForToken({
+ from,
+ to,
+ amount,
+ });
+
+ expect(result).toEqual(expectedParams);
+ });
+
+ it('should format the amount correctly with default decimals when isAmountInDecimals is true', () => {
+ const to: Token = {
+ name: 'ETH',
+ address: '',
+ symbol: 'ETH',
+ decimals: 18,
+ image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ chainId: 8453,
+ };
+ const from: Token = {
+ name: 'DEGEN',
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ symbol: 'DEGEN',
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ chainId: 8453,
+ };
+ const amount = '1500000000000000000';
+ const amountReference = 'from';
+ const isAmountInDecimals = true;
+ const expectedParams = {
+ from: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ to: 'ETH',
+ amount: '1500000000000000000',
+ amountReference: 'from',
+ };
+ const result = getParamsForToken({
+ from,
+ to,
+ amount,
+ amountReference,
+ isAmountInDecimals,
+ });
+ expect(result).toEqual(expectedParams);
+ });
+
+ it('should format the amount correctly with default decimals when isAmountInDecimals is false', () => {
+ const to: Token = {
+ name: 'ETH',
+ address: '',
+ symbol: 'ETH',
+ decimals: 18,
+ image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ chainId: 8453,
+ };
+ const from: Token = {
+ name: 'DEGEN',
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ symbol: 'DEGEN',
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ chainId: 8453,
+ };
+ const amount = '1.5';
+ const amountReference = 'from';
+ const expectedParams = {
+ from: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ to: 'ETH',
+ amount: '1500000000000000000',
+ amountReference: 'from',
+ };
+ const result = getParamsForToken({
+ from,
+ to,
+ amount,
+ amountReference,
+ });
+ expect(result).toEqual(expectedParams);
+ });
+});
diff --git a/src/swap/core/getParamsForToken.ts b/src/swap/core/getParamsForToken.ts
new file mode 100644
index 0000000000..b7e567f25d
--- /dev/null
+++ b/src/swap/core/getParamsForToken.ts
@@ -0,0 +1,18 @@
+import { formatDecimals } from './formatDecimals';
+import type { GetQuoteParams, GetQuoteAPIParams } from '../types';
+
+/**
+ * Converts parameters with `Token` to ones with address. Additionally adds default values for optional request fields.
+ */
+export function getParamsForToken(params: GetQuoteParams): GetQuoteAPIParams {
+ const { from, to, amount, amountReference, isAmountInDecimals } = params;
+
+ const decimals = amountReference === 'from' ? from.decimals : to.decimals;
+
+ return {
+ from: from.address || 'ETH',
+ to: to.address || 'ETH',
+ amount: isAmountInDecimals ? amount : formatDecimals(amount, false, decimals),
+ amountReference: amountReference || 'from',
+ };
+}
diff --git a/src/swap/core/getQuote.test.ts b/src/swap/core/getQuote.test.ts
new file mode 100644
index 0000000000..0afcc63863
--- /dev/null
+++ b/src/swap/core/getQuote.test.ts
@@ -0,0 +1,131 @@
+import { getQuote } from './getQuote';
+import { sendRequest } from '../../queries/request';
+import { CDP_GETSWAPQUOTE } from '../../definitions/swap';
+import type { Token } from '../../token/types';
+import { getParamsForToken } from './getParamsForToken';
+
+jest.mock('../../queries/request');
+
+const ETH: Token = {
+ name: 'ETH',
+ address: '',
+ symbol: 'ETH',
+ decimals: 18,
+ image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ chainId: 8453,
+};
+const DEGEN: Token = {
+ name: 'DEGEN',
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ symbol: 'DEGEN',
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ chainId: 8453,
+};
+const testAmount = '3305894409732200';
+const testAmountReference = 'from';
+
+describe('getQuote', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should return a quote for a swap', async () => {
+ const mockParams = {
+ amountReference: testAmountReference,
+ from: ETH,
+ to: DEGEN,
+ amount: testAmount,
+ };
+ const mockApiParams = getParamsForToken(mockParams);
+
+ const mockResponse = {
+ id: 1,
+ jsonrpc: '2.0',
+ result: {
+ from: {
+ address: '',
+ chainId: 8453,
+ decimals: 18,
+ image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ name: 'ETH',
+ symbol: 'ETH',
+ },
+ to: {
+ address: '0x4ed4e862860bed51a9570b96d89af5e1b0efefed',
+ chainId: 8453,
+ decimals: 18,
+ image:
+ 'https://d3r81g40ycuhqg.cloudfront.net/wallet/wais/3b/bf/3bbf118b5e6dc2f9e7fc607a6e7526647b4ba8f0bea87125f971446d57b296d2-MDNmNjY0MmEtNGFiZi00N2I0LWIwMTItMDUyMzg2ZDZhMWNm',
+ name: 'DEGEN',
+ symbol: 'DEGEN',
+ },
+ fromAmount: '100000000000000000',
+ toAmount: '16732157880511600003860',
+ amountReference: 'from',
+ priceImpact: '0.07',
+ chainId: 8453,
+ hasHighPriceImpact: false,
+ slippage: '3',
+ },
+ };
+
+ (sendRequest as jest.Mock).mockResolvedValue(mockResponse);
+
+ const quote = await getQuote(mockParams);
+
+ expect(quote).toEqual(mockResponse.result);
+
+ expect(sendRequest).toHaveBeenCalledTimes(1);
+ expect(sendRequest).toHaveBeenCalledWith(CDP_GETSWAPQUOTE, [mockApiParams]);
+ });
+
+ it('should throw an error if sendRequest fails', async () => {
+ const mockParams = {
+ amountReference: testAmountReference,
+ from: ETH,
+ to: DEGEN,
+ amount: testAmount,
+ };
+ const mockApiParams = getParamsForToken(mockParams);
+
+ const mockError = new Error('getQuote: Error: Failed to send request');
+ (sendRequest as jest.Mock).mockRejectedValue(mockError);
+
+ await expect(getQuote(mockParams)).rejects.toThrow('getQuote: Error: Failed to send request');
+
+ expect(sendRequest).toHaveBeenCalledTimes(1);
+ expect(sendRequest).toHaveBeenCalledWith(CDP_GETSWAPQUOTE, [mockApiParams]);
+ });
+
+ it('should return an error object from getQuote', async () => {
+ const mockParams = {
+ amountReference: testAmountReference,
+ from: ETH,
+ to: DEGEN,
+ amount: testAmount,
+ };
+ const mockApiParams = getParamsForToken(mockParams);
+
+ const mockResponse = {
+ id: 1,
+ jsonrpc: '2.0',
+ error: {
+ code: -1,
+ message: 'Invalid response',
+ },
+ };
+
+ (sendRequest as jest.Mock).mockResolvedValue(mockResponse);
+
+ const error = await getQuote(mockParams);
+ expect(error).toEqual({
+ code: -1,
+ error: 'Invalid response',
+ });
+
+ expect(sendRequest).toHaveBeenCalledTimes(1);
+ expect(sendRequest).toHaveBeenCalledWith(CDP_GETSWAPQUOTE, [mockApiParams]);
+ });
+});
diff --git a/src/swap/core/getQuote.ts b/src/swap/core/getQuote.ts
new file mode 100644
index 0000000000..facb088389
--- /dev/null
+++ b/src/swap/core/getQuote.ts
@@ -0,0 +1,38 @@
+import { CDP_GETSWAPQUOTE } from '../../definitions/swap';
+import { sendRequest } from '../../queries/request';
+import type {
+ GetQuoteResponse,
+ GetQuoteParams,
+ GetQuoteAPIParams,
+ Quote,
+ SwapError,
+} from '../types';
+import { getParamsForToken } from './getParamsForToken';
+
+/**
+ * Retrieves a quote for a swap from Token A to Token B.
+ */
+export async function getQuote(params: GetQuoteParams): Promise {
+ // Default parameters
+ const defaultParams = {
+ amountReference: 'from',
+ isAmountInDecimals: false,
+ };
+
+ const apiParams = getParamsForToken({ ...defaultParams, ...params });
+
+ try {
+ const res = await sendRequest(CDP_GETSWAPQUOTE, [apiParams]);
+
+ if (res.error) {
+ return {
+ code: res.error.code,
+ error: res.error.message,
+ } as SwapError;
+ }
+
+ return res.result;
+ } catch (error) {
+ throw new Error(`getQuote: ${error}`);
+ }
+}
diff --git a/src/swap/index.ts b/src/swap/index.ts
index d8b8c70a0d..e51725e16b 100644
--- a/src/swap/index.ts
+++ b/src/swap/index.ts
@@ -1 +1,10 @@
// 🌲☀️🌲
+export { getQuote } from './core/getQuote';
+export type {
+ Fee,
+ GetQuoteParams,
+ GetQuoteResponse,
+ Quote,
+ QuoteWarning,
+ SwapError,
+} from './types';
diff --git a/src/swap/types.ts b/src/swap/types.ts
index ddf5cb8cb5..5e73f0365b 100644
--- a/src/swap/types.ts
+++ b/src/swap/types.ts
@@ -1,49 +1,84 @@
-import { Address } from 'viem';
-import { Token } from '../token/types';
+import type { Address } from 'viem';
+import type { Token } from '../token/types';
export type AddressOrETH = Address | 'ETH';
+/**
+ * Note: exported as public Type
+ */
export type Fee = {
- amount: string;
- baseAsset: Token;
- percentage: string;
+ amount: string; // The amount of the fee
+ baseAsset: Token; // The base asset for the fee
+ percentage: string; // The percentage of the fee
};
+export type GetQuoteAPIParams = {
+ from: AddressOrETH | ''; // The source address or 'ETH' for Ethereum
+ to: AddressOrETH | ''; // The destination address or 'ETH' for Ethereum
+ amount: string; // The amount to be swapped
+ amountReference?: string; // The reference amount for the swap
+};
+
+/**
+ * Note: exported as public Type
+ */
+export type GetQuoteParams = {
+ from: Token; // The source token for the swap
+ to: Token; // The destination token for the swap
+ amount: string; // The amount to be swapped
+ amountReference?: string; // The reference amount for the swap
+ isAmountInDecimals?: boolean; // Whether the amount is in decimals
+};
+
+/**
+ * Note: exported as public Type
+ */
+export type GetQuoteResponse = Quote | SwapError;
+
+/**
+ * Note: exported as public Type
+ */
export type Quote = {
- amountReference: string;
- fromAmount: string;
- fromAsset: Token;
- highPriceImpact: boolean;
- priceImpact: string;
- slippage: string;
- toAmount: string;
- toAsset: Token;
- warning?: QuoteWarning;
+ amountReference: string; // The reference amount for the quote
+ from: Token; // The source token for the swap
+ fromAmount: string; // The amount of the source token
+ hasHighPriceImpact: boolean; // Whether the price impact is high
+ priceImpact: string; // The price impact of the swap
+ slippage: string; // The slippage of the swap
+ to: Token; // The destination token for the swap
+ toAmount: string; // The amount of the destination token
+ warning?: QuoteWarning; // The warning associated with the quote
};
+/**
+ * Note: exported as public Type
+ */
export type QuoteWarning = {
- description?: string;
- message?: string;
- type?: string;
+ description?: string; // The description of the warning
+ message?: string; // The message of the warning
+ type?: string; // The type of the warning
};
-export type Trade = {
- approveTx?: Transaction;
- chainId: string;
- fee: Fee;
- tx: Transaction;
+/**
+ * Note: exported as public Type
+ */
+export type SwapError = {
+ code: number; // The error code
+ error: string; // The error message
};
-export type Transaction = {
- data: string;
- from: string;
- gas: string;
- gasPrice: string;
- to: string;
- value: string;
+export type Trade = {
+ approveTx?: Transaction; // The approval transaction
+ chainId: string; // The chain ID
+ fee: Fee; // The fee for the trade
+ tx: Transaction; // The trade transaction
};
-export type TrendingToken = Token & {
- numOfBuys: number;
- numOfSells: number;
+export type Transaction = {
+ data: string; // The transaction data
+ from: string; // The sender address
+ gas: string; // The gas limit
+ gasPrice: string; // The gas price
+ to: string; // The recipient address
+ value: string; // The value of the transaction
};
diff --git a/src/token/components/TextInput.css b/src/token/components/TextInput.css
index f12da931ca..ebd147dbb4 100644
--- a/src/token/components/TextInput.css
+++ b/src/token/components/TextInput.css
@@ -1,31 +1,24 @@
.ock-textinput-container {
@apply relative flex items-center;
}
-
.ock-textinput-iconsearch {
@apply absolute left-4 top-2/4 -translate-y-2/4;
}
-
.ock-textinput-input {
@apply w-full rounded-full border-2 border-solid border-[#eef0f3] py-2 pl-12 pr-5 text-[#0A0B0D];
background: #eef0f3;
+ outline: none;
&::placeholder {
@apply text-[#5B616E];
}
&:hover {
- @apply border-[#cacbce];
background: #cacbce;
}
- &:focus {
- @apply border-[#0052FF];
- outline: none;
- }
&:hover:focus {
background: #eef0f3;
}
}
-
.ock-textinput-clearbutton {
@apply absolute right-4 top-2/4 -translate-y-2/4;
}
diff --git a/src/token/components/TokenChip.css b/src/token/components/TokenChip.css
index bda3edfc7c..e65dadaac1 100644
--- a/src/token/components/TokenChip.css
+++ b/src/token/components/TokenChip.css
@@ -5,16 +5,13 @@
&:hover {
background: #cacbce;
}
-
&:active {
background: #bfc1c3;
}
}
-
.ock-tokenchip-label {
@apply text-base font-medium leading-4 text-black;
}
-
.ock-tokenchip-image {
@apply mr-2 h-6 w-6;
}
diff --git a/src/token/components/TokenImage.css b/src/token/components/TokenImage.css
new file mode 100644
index 0000000000..5525f98c3c
--- /dev/null
+++ b/src/token/components/TokenImage.css
@@ -0,0 +1,3 @@
+.ock-tokenimage {
+ @apply overflow-hidden rounded-[50%];
+}
diff --git a/src/token/components/TokenImage.test.tsx b/src/token/components/TokenImage.test.tsx
index 1494bc3539..a268ec5ff5 100644
--- a/src/token/components/TokenImage.test.tsx
+++ b/src/token/components/TokenImage.test.tsx
@@ -5,14 +5,33 @@ import React from 'react';
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import { TokenImage } from './TokenImage';
+import { Token } from '../types';
+
+const tokenWithImage: Token = {
+ name: 'Ethereum',
+ address: '0x123',
+ symbol: 'ETH',
+ decimals: 18,
+ image: 'https://wallet-api-production.s3.amazonaws.com/uploads/tokens/eth_288.png',
+ chainId: 8453,
+};
+
+const tokenWithNoImage: Token = {
+ name: 'Ethereum',
+ address: '0x123',
+ symbol: 'ETH',
+ decimals: 18,
+ image: null,
+ chainId: 8453,
+};
describe('TokenImage Component', () => {
beforeEach(() => {
jest.clearAllMocks();
});
- it('should render with src prop', async () => {
- render();
+ it('should render token with image', async () => {
+ render();
const imgElement = screen.getByTestId('ockTokenImage_Image');
const noImgElement = screen.queryByTestId('ockTokenImage_NoImage');
@@ -20,8 +39,8 @@ describe('TokenImage Component', () => {
expect(noImgElement).toBeNull();
});
- it('should render with no src prop', async () => {
- render();
+ it('should render token with no image', async () => {
+ render();
const imgElement = screen.queryByTestId('ockTokenImage_Image');
const noImgElement = screen.getByTestId('ockTokenImage_NoImage');
@@ -30,7 +49,7 @@ describe('TokenImage Component', () => {
});
it('should render with size prop', async () => {
- render();
+ render();
const imgElement = screen.queryByTestId('ockTokenImage_Image');
const noImgElement = screen.getByTestId('ockTokenImage_NoImage');
diff --git a/src/token/components/TokenImage.tsx b/src/token/components/TokenImage.tsx
index 075fde97c9..632f2ed970 100644
--- a/src/token/components/TokenImage.tsx
+++ b/src/token/components/TokenImage.tsx
@@ -1,31 +1,42 @@
import { useMemo } from 'react';
import { TokenImageReact } from '../types';
+import { getTokenImageColor } from './getTokenImageColor';
+
+export function TokenImage({ className, size = 24, token }: TokenImageReact) {
+ const { image, name } = token;
-export function TokenImage({ src, size = 24 }: TokenImageReact) {
const styles = useMemo(() => {
return {
image: {
width: `${size}px`,
height: `${size}px`,
- borderRadius: '50%',
- overflow: 'hidden',
- background: 'blue',
},
placeholderImage: {
- background: 'blue',
+ background: getTokenImageColor(name),
width: `${size}px`,
height: `${size}px`,
},
};
}, [size]);
- if (!src) {
+ if (!image) {
return (
-
+
);
}
- return
;
+ return (
+
+ );
}
diff --git a/src/token/components/TokenRow.css b/src/token/components/TokenRow.css
new file mode 100644
index 0000000000..bb457f6f3c
--- /dev/null
+++ b/src/token/components/TokenRow.css
@@ -0,0 +1,26 @@
+.ock-tokenrow-button {
+ @apply flex h-16 w-full cursor-pointer items-center justify-between px-2 py-1;
+ background: #ffffff;
+
+ &:hover {
+ background: #cacbce;
+ }
+ &:active {
+ background: #bfc1c3;
+ }
+}
+.ock-tokenrow-left {
+ @apply flex items-center gap-3;
+}
+.ock-tokenrow-body {
+ @apply flex flex-col items-start;
+}
+.ock-tokenrow-name {
+ @apply text-base font-medium leading-normal text-[#0A0B0D];
+}
+.ock-tokenrow-symbol {
+ @apply text-base font-normal leading-normal text-[#5B616E];
+}
+.ock-tokenrow-data {
+ @apply text-base font-normal leading-normal text-[#5B616E];
+}
diff --git a/src/token/components/TokenRow.test.tsx b/src/token/components/TokenRow.test.tsx
index e01a86f37a..7d0bfbb04b 100644
--- a/src/token/components/TokenRow.test.tsx
+++ b/src/token/components/TokenRow.test.tsx
@@ -27,7 +27,7 @@ describe('TokenRow component', () => {
render(
);
await waitFor(() => {
- const circle = screen.getByTestId('ockTokenRow_PlaceholderImage');
+ const circle = screen.getByTestId('ockTokenImage_NoImage');
expect(circle).toBeInTheDocument();
});
});
@@ -36,7 +36,7 @@ describe('TokenRow component', () => {
render(
);
await waitFor(() => {
- const tokenImage = screen.getByTestId('ockTokenRow_Image');
+ const tokenImage = screen.getByTestId('ockTokenImage_Image');
expect(tokenImage).toBeInTheDocument();
});
});
diff --git a/src/token/components/TokenRow.tsx b/src/token/components/TokenRow.tsx
index 6a342cc809..10b401811c 100644
--- a/src/token/components/TokenRow.tsx
+++ b/src/token/components/TokenRow.tsx
@@ -1,66 +1,29 @@
-import { memo, CSSProperties } from 'react';
+import { memo } from 'react';
import { TokenRowReact } from '../types';
import { formatAmount } from '../core/formatAmount';
+import { TokenImage } from './TokenImage';
-const styles = {
- row: {
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'space-between',
- cursor: 'pointer',
- padding: '4px 8px',
- width: '100%',
- },
- left: {
- display: 'flex',
- alignItems: 'center',
- },
- image: {
- height: '32px',
- width: '32px',
- marginRight: '12px',
- },
- circle: {
- height: '32px',
- width: '32px',
- borderRadius: '99999px',
- background: 'gray',
- marginRight: '12px',
- },
- column: {
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'flex-start',
- },
- label1: {
- fontSize: '16px',
- lineHeight: '1.5',
- fontWeight: 500,
- color: '#0A0B0D',
- },
- label2: {
- fontSize: '16px',
- lineHeight: '1.5',
- fontWeight: 400,
- color: '#5B616E',
- },
-} as Record
;
-
-export const TokenRow = memo(function TokenRow({ token, amount, onClick }: TokenRowReact) {
+export const TokenRow = memo(function TokenRow({
+ token,
+ amount,
+ onClick,
+ hideImage,
+ hideSymbol,
+}: TokenRowReact) {
return (
-