Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 52 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

This library was built to make it easier to build cross-domain applications in JS. It unites ecosystem-specific tooling, provides some helpful abstractions but gives you the flexibility to do anything more specific, and offers a configurable modal to manage connections across one or more domains you would like to support in your app.

Valence Protocol TS currently has support for these ecosystems:

1. Ethereum
2. Solana
3. Cosmos

Libraries are in their early stage. APIs subject to change.

For developer docs, see `docs` folder.
Expand All @@ -15,21 +21,21 @@ pnpm install @valence-protocol/domain-clients-core @valence-protocol/domain-clie
```

2. Install peer dependencies
Note: for the short term you need to install dependencies for all three supported domains (solana, evm, cosmos). There is a little more work required to allow you to install peer dependencies only for the domains that you wantt. You can still choose which domains show up in the modal in the config.
You will need to install the required libraries for the ecosystem you would like to support.

```bash
# EVM
# for EVM
pnpm install wagmi viem

# Solana
# for Solana
pnpm install gill @wallet-ui/react
## when interacting with frameworks that have not moved to @solana/kit (used by gill under the hood and importable through gill), you may need to install @solana/web3.js @solana/compat
## note: when interacting with frameworks that have not moved to @solana/kit (used by gill under the hood and importable through gill), you may need to also install @solana/web3.js and @solana/compat

# Cosmos
# for Cosmos
pnpm install graz
```

For graz, you will also need to add a post-install script to install chains locally. They will not be packaged with you app unless you import them.
With graz, you will also need to add a post-install script to generate chain info locally. The generated chain infos will not be packaged with your app unless you import them.

```json
{
Expand All @@ -41,10 +47,9 @@ For graz, you will also need to add a post-install script to install chains loca
```

3. Define config.
Copy [these](https://github.com/timewave-computer/valence-protocol-ts/tree/next/apps/domain-clients-example/src/config/domainClientsConfig) config files as an example. Only include the domains you are interested in working with.

- Note:\* for the short term you MUST fill some config for each domain. This will be addressed soon. If you don't want the modal to support this domain, set `hide=true` in the config. You can copy [these](https://github.com/timewave-computer/valence-protocol-ts/tree/next/apps/domain-clients-example/src/config/domainClientsConfig) config files as an example, and have minimal input for the domains you don't want to support.

The root config must be
Example of the root config:

```javascript
import { DomainClientsConfig } from '@valence-protocol/domain-clients-react';
Expand All @@ -56,30 +61,29 @@ export const domainClientsConfig: DomainClientsConfig = {
};
```

4. Create app providers component with `DomainClientsProvider` and `DomainModalProvider`. They are kept separate in case you want to use a custom modal altogether.
4. Wrap your app root in a `DomainModalProvider` and import css. For Next.js 14+, you will need to wrap the providers in a client component.

```javascript
'use client' // for Next.js 14+
// AppProvider.tsx (for Next.js 14+)

'use client'
import { domainClientsConfig } from '@/config';
import { ReactQueryProvider } from '@/context';
import { DomainClientsProvider } from '@valence-protocol/domain-clients-react';
import { DomainModalProvider } from '@valence-protocol/domain-modal-react';

export const AppProviders = ({ children }: { children: React.ReactNode }) => {
return (
<ReactQueryProvider>
<DomainClientsProvider config={domainClientsConfig}>
<DomainModalProvider>{children}</DomainModalProvider>
</DomainClientsProvider>
</ReactQueryProvider>
<...OtherProviders>
<DomainModalProvider config={domainClientsConfig}>
{children}
</DomainModalProvider>
<...OtherProviders>
);
};
```

5. Wrap root project in providers and import css

```javascript
// layout.tsx or similar application root

import '@valence-protocol/domain-modal-react/styles.css';

const Root = () => {
Expand All @@ -105,21 +109,37 @@ const { showModal } = useDomainModal();

### `domain-clients-core`

Pure-JS code that abstract common, implementation-specific methods away. They have a few pre-built operations for you (such as transferring tokens and reading balances). To do anything not pre-built, you can call `getClient` on the respective client and perform anything else with it. More common use patterns will be added over time.
Pure-JS code that abstract common, implementation-specific methods. There are a few pre-built operations (such as transferring tokens and reading balances). To do anything not pre-built, you can call `getClient` on the respective client and perform anything you need. Commonly used patterns will be added as client functions over time.

The imports per-domain are tree-shakeable.
Imports per-domain are tree-shakeable. For example:

```javascript
import { EvmClient } from '@valence-protocol/domain-clients-core/evm';
```

### `domain-clients-react`

A React provider and hooks to access the config via context within the app, and use domain clients via react hooks.
React hooks and context provider to access the config and use domain clients in a react app.

The imports per-domain are tree-shakeable.
The imports per-domain are tree-shakeable. For example:

```javascript
import {
useSigningSolanaClient,
} from '@valence-protocol/domain-clients-react/solana';

// component
const solanaSigningClient = useSolanaSigningClient({clusterId:'solana:devent'})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There are a couple of typos in this code example that could confuse users:

  1. The imported hook is useSigningSolanaClient, but it's called as useSolanaSigningClient.
  2. The clusterId is 'solana:devent', which is likely a typo for 'solana:devnet'.

Suggesting a fix for consistency and correctness.

Suggested change
const solanaSigningClient = useSolanaSigningClient({clusterId:'solana:devent'})
const solanaSigningClient = useSigningSolanaClient({clusterId:'solana:devnet'})

solanaSigningClient.getSolBalance({...})
solanaSigningClient.transfer({...})

```

### `domain-modal-react`

A React provider and component that allows you to support connecting wallets and accessing the state. It is a loose wrapper around the domain-specific UI hooks.
A React UI component and context provider that allows you to manage wallet connections across multiple domains. It is a loose wrapper around domain-specific UI libraries. Based on the domain clients configuration, it will render the appropriate wallet modal.

This is NOT YET tree shakeable. It will be in the future.
The modal will omit dependencies for ecosystems excluded from in the configuration.

## How to use the libraries

Expand Down Expand Up @@ -151,13 +171,13 @@ If your app is wrapped in the DomainClientsProvider, you can simply call `useXSi

```javascript
import {
solanaClient,
useSolanaClient,
useSigningSolanaClient,
useCosmosClient,
useSigningCosmosClient,
useEvmClient,
useSigningEvmClient,
} from '@valence-protocol/domain-clients-react/solana';
} from '@valence-protocol/domain-clients-react';

const solananClient = useSolanaClient({ clusterId });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's a typo in the variable name. It should be solanaClient instead of solananClient.

Suggested change
const solananClient = useSolanaClient({ clusterId });
const solanaClient = useSolanaClient({ clusterId });

const signingSolanaClient = useSigningSolanaClient({ clusterId });
Expand Down Expand Up @@ -199,12 +219,14 @@ const config = useDomainConfig();

### Domain-specific hooks

Use various domain-specific utils as needed. They will work as expected because we render their providers under the hod. The implementation and naming for each varies. You can dig into the tool-specific docs as needed.
Use various domain-specific hooks and utils as needed. They will work as expected because we render their providers under the hod. The implementation and naming for each varies. You can find some examples of how these are used in the example app, but it is suggested to consult the individual docs.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There's a typo here. It should be under the hood instead of under the hod.

Suggested change
Use various domain-specific hooks and utils as needed. They will work as expected because we render their providers under the hod. The implementation and naming for each varies. You can find some examples of how these are used in the example app, but it is suggested to consult the individual docs.
Use various domain-specific hooks and utils as needed. They will work as expected because we render their providers under the hood. The implementation and naming for each varies. You can find some examples of how these are used in the example app, but it is suggested to consult the individual docs.


```javascript
import { useAccount as useEvmAccount } from 'wagmi';
import { useAccount as useCosmosAccount } from 'graz';
import { useWalletUi as useSolanaAccount } from '@wallet-ui/react';
```

This is about it! Feel free to raise issues.
## Contact us

Feel free to raise issues, or contact the team on [Telegram](https://t.me/+Sig6DYQn-Ec0NTZh).
7 changes: 4 additions & 3 deletions apps/domain-clients-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@next/bundle-analyzer": "^15.4.6",
"tailwind-merge": "^3.3.1",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-tabs": "^1.1.13",
Expand All @@ -21,9 +22,9 @@
"@solana/web3.js": "^1.98.4",
"@tanstack/react-query": "^5.84.1",
"@uiw/react-json-view": "2.0.0-alpha.34",
"@valence-protocol/domain-clients-core": "0.3.0",
"@valence-protocol/domain-clients-react": "0.3.0",
"@valence-protocol/domain-modal-react": "0.3.0",
"@valence-protocol/domain-clients-core": "workspace:*",
"@valence-protocol/domain-clients-react": "workspace:*",
"@valence-protocol/domain-modal-react": "workspace:*",
"@wallet-ui/react": "^1.1.1",
"bn.js": "^5.2.2",
"class-variance-authority": "^0.7.1",
Expand Down
21 changes: 17 additions & 4 deletions apps/domain-clients-example/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
CosmosOps,
SolanaOps,
} from '@/components';
import { domainClientsConfig } from '@/config';

export default async function Home() {
return (
Expand All @@ -34,25 +35,37 @@ export default async function Home() {
</div>
<div>
<h2 className='font-semibold text-xl pb-2 '>Domain Operations</h2>
<Tabs className='md:max-w-[400px]' defaultValue='solana'>
<Tabs className='md:max-w-[400px]' defaultValue='evm'>
<TabsList>
<TabsTrigger value='evm'>Ethereum</TabsTrigger>
<TabsTrigger value='cosmos'>Cosmos</TabsTrigger>
<TabsTrigger value='solana'>Solana</TabsTrigger>
</TabsList>
<TabsContent value='evm'>
<Suspense fallback={<div>Loading...</div>}>
<EthereumOps />
{domainClientsConfig.evm ? (
<EthereumOps />
) : (
<div>No Ethereum operations available</div>
)}
</Suspense>
</TabsContent>
<TabsContent value='cosmos'>
<Suspense fallback={<div>Loading...</div>}>
<CosmosOps />
{domainClientsConfig.cosmos ? (
<CosmosOps />
) : (
<div>No Cosmos operations available</div>
)}
</Suspense>
</TabsContent>
<TabsContent value='solana'>
<Suspense fallback={<div>Loading...</div>}>
<SolanaOps />
{domainClientsConfig.solana ? (
<SolanaOps />
) : (
<div>No Solana operations available</div>
)}
</Suspense>
</TabsContent>
</Tabs>
Expand Down
5 changes: 2 additions & 3 deletions apps/domain-clients-example/src/components/ui/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/components/ui';

const buttonVariants = cva(
'px-2 py-1 rounded-sm font-semibold transition-colors duration-200 border outline-none rounded-xs border text-sm ',
Expand All @@ -7,8 +8,6 @@ const buttonVariants = cva(
variant: {
default: 'bg-gray-900 text-white hover:bg-gray-900/80 cursor-pointer',
secondary: 'border-gray-700 hover:bg-gray-100 cursor-pointer',
ghost:
'bg-gray-200 hover:bg-gray-300 cursor-pointer border-transparent',
disabled:
'border-gray-300 cursor-not-allowed text-gray-900 bg-gray-300',
},
Expand All @@ -31,7 +30,7 @@ export const Button = ({
}
return (
<button
className={buttonVariants({ variant, className })}
className={cn(buttonVariants({ variant, className }))}
disabled={disabled ?? false}
{...rest}
>
Expand Down
3 changes: 2 additions & 1 deletion apps/domain-clients-example/src/components/ui/cn.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export const cn = (...inputs: ClassValue[]) => {
return clsx(inputs);
return twMerge(clsx(inputs));
};
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ export const cosmosConfig: CosmosConfig = {
},
},
defaultChainId: neutrontestnet.chainId,
hide: false,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { evmConfig } from './evm.config';
import { cosmosConfig } from './cosmos.config';
import { solanaConfig } from './solana.config';
import { DomainClientsConfig } from '@valence-protocol/domain-clients-react';

export const domainClientsConfig: DomainClientsConfig = {
evm: evmConfig,
cosmos: cosmosConfig,
solana: solanaConfig,
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ const wagmiConfig = createEvmConfig({
export const evmConfig = {
wagmiConfig,
defaultChainId: sepolia.id,
hide: false,
};
Original file line number Diff line number Diff line change
@@ -1,10 +1 @@
import { evmConfig } from './evm.config';
import { cosmosConfig } from './cosmos.config';
import { solanaConfig } from './solana.config';
import { DomainClientsConfig } from '@valence-protocol/domain-clients-react';

export const domainClientsConfig: DomainClientsConfig = {
evm: evmConfig,
cosmos: cosmosConfig,
solana: solanaConfig,
};
export * from './domainClients.config';
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,4 @@ export const mainnet: SolanaCluster = {
export const solanaConfig = createSolanaDomainClientsConfig({
clusters: [devnet, mainnet],
defaultClusterId: devnet.id,
hide: false,
});
7 changes: 3 additions & 4 deletions apps/domain-clients-example/src/context/AppProviders.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
'use client';
import { domainClientsConfig } from '@/config';
import { ReactQueryProvider } from '@/context';
import { DomainClientsProvider } from '@valence-protocol/domain-clients-react';
import { DomainModalProvider } from '@valence-protocol/domain-modal-react';

export const AppProviders = ({ children }: { children: React.ReactNode }) => {
return (
<ReactQueryProvider>
<DomainClientsProvider config={domainClientsConfig}>
<DomainModalProvider>{children}</DomainModalProvider>
</DomainClientsProvider>
<DomainModalProvider config={domainClientsConfig}>
{children}
</DomainModalProvider>
</ReactQueryProvider>
);
};
4 changes: 4 additions & 0 deletions packages/domain-clients-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased] YYYY-MM-DD

## Changed

- made `domainClientsConfig.hide` an optional argument

## [0.3.0] 2025-09-03

### Changed
Expand Down
2 changes: 1 addition & 1 deletion packages/domain-clients-core/src/common/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export type DomainClientConfig = {
hide: boolean;
hide?: boolean;
};
2 changes: 1 addition & 1 deletion packages/domain-clients-core/src/solana/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const createSolanaDomainClientsConfig = ({
}: {
clusters: SolanaCluster[];
defaultClusterId: string;
hide: boolean;
hide?: boolean;
}): SolanaConfig => {
if (!isSolanaClusterId(defaultClusterId)) {
throw new Error('Default cluster id must start with "solana:"');
Expand Down
2 changes: 1 addition & 1 deletion packages/domain-clients-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"wagmi": "^2.16.0",
"@wallet-ui/react": "^1.1.1",
"graz": "^0.3.3",
"@valence-protocol/domain-clients-core": "0.3.0"
"@valence-protocol/domain-clients-core": "workspace:*"
},
"devDependencies": {
"@types/react": "18.3.1",
Expand Down
5 changes: 5 additions & 0 deletions packages/domain-modal-react/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased] YYYY-MM-DD

### Fixed

- You no longer need to install peer dependencies for domains you are not using. If they are omitted from the config, the modal will not attempt to instantiate them.
- You no longer need to instantiate a `DomainClientsProvider`, the modal provider handles this internally.

## [0.3.0] 2025-09-03

### Added
Expand Down
4 changes: 2 additions & 2 deletions packages/domain-modal-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"author": "",
"license": "MIT",
"peerDependencies": {
"@valence-protocol/domain-clients-core": "0.3.0",
"@valence-protocol/domain-clients-react": "0.3.0",
"@valence-protocol/domain-clients-core": "workspace:*",
"@valence-protocol/domain-clients-react": "workspace:*",
"@wallet-ui/react": "^1.1.1",
"graz": "^0.3.3",
"react": "18.3.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const ConnectDomainButton = ({
export const ConnectDomainButtonRoot = ({
onClick,
children,
}: {
Expand Down
Loading