Skip to content

Commit

Permalink
feat: default chain (#515)
Browse files Browse the repository at this point in the history
Adds a `defaultChain` arg to `StarknetConfig` that allows changing the
default chain used when no wallet is connected
  • Loading branch information
fracek authored Oct 18, 2024
2 parents b459136 + 9d7fc85 commit 29096d4
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Add provider option to select default chain when no wallet is connected",
"packageName": "@starknet-react/core",
"email": "[email protected]",
"dependentChangeType": "patch"
}
70 changes: 70 additions & 0 deletions docs/components/demo/change-default-network.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { type Chain, mainnet, sepolia } from "@starknet-react/chains";
import { publicProvider, useAccount, useNetwork } from "@starknet-react/core";
import { useState } from "react";
import { Button } from "../ui/button";
import { DemoContainer } from "../starknet";

export function ChangeDefaultNetwork() {
const [defaultChain, setDefaultChain] = useState<Chain>(sepolia);
const chains = [mainnet, sepolia];
const provider = publicProvider();

return (
<DemoContainer hasWallet defaultChainId={defaultChain.id}>
<ChangeNetworkInner
defaultChain={defaultChain}
setDefaultChain={setDefaultChain}
/>
</DemoContainer>
);
}

function ChangeNetworkInner({
defaultChain,
setDefaultChain,
}: {
defaultChain: Chain;
setDefaultChain: (chain: Chain) => void;
}) {
const { chain } = useNetwork();
const chains = [sepolia, mainnet];
const { isConnected } = useAccount();

return (
<div className="flex flex-col gap-4">
<div className="h-full flex flex-col justify-center">
<p className="font-medium">Current Chain: </p>
<pre>{chain.name}</pre>
</div>

<div className="h-full flex flex-col justify-center">
<p className="font-medium">Default Chain: </p>
<pre>{defaultChain.name}</pre>
</div>

<div>
<p className="font-medium">Change Default Chain:</p>
<div className="flex gap-2 my-2">
{chains.map((chain) => (
<Button
key={chain.id}
onClick={() => setDefaultChain(chain)}
variant={defaultChain.id === chain.id ? "default" : "outline"}
>
{chain.name}
</Button>
))}
</div>
</div>

<div className="h-full flex flex-col justify-center">
<p className="font-medium">Behavior Explanation: </p>
<pre>
{isConnected
? "Connected: Changing default chain won't affect current chain"
: "Not Connected: Changing default chain will update current chain"}
</pre>
</div>
</div>
);
}
2 changes: 2 additions & 0 deletions docs/components/demo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { StarkProfile } from "./stark-profile";
import { StarknetKit } from "./starknetkit";
import { SwitchChain } from "./switch-chain";
import { WalletPermission } from "./wallet-permission";
import { ChangeDefaultNetwork } from "./change-default-network";

export default {
Account,
Expand All @@ -30,4 +31,5 @@ export default {
DeclareContract,
NonceForAddress,
StarknetKit,
ChangeDefaultNetwork,
};
4 changes: 3 additions & 1 deletion docs/components/starknet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import { WalletBar } from "./bar";
import { StarknetProvider } from "./provider";

export function DemoContainer({
defaultChainId,
hasWallet,
children,
}: {
defaultChainId?: bigint;
hasWallet?: boolean;
children: React.ReactNode;
}) {
return (
<StarknetProvider>
<StarknetProvider defaultChainId={defaultChainId}>
<div className="flex flex-col px-4 border border-primary rounded-xl">
{hasWallet ? <WalletBar /> : null}
<div className="py-4">{children}</div>
Expand Down
3 changes: 3 additions & 0 deletions docs/components/starknet/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import {
} from "@starknet-react/core";

export function StarknetProvider({
defaultChainId,
children,
explorer,
}: {
children: React.ReactNode;
defaultChainId?: bigint;
explorer?: ExplorerFactory;
}) {
const chains = [sepolia, mainnet];
Expand All @@ -32,6 +34,7 @@ export function StarknetProvider({
provider={provider}
connectors={connectors}
explorer={explorer}
defaultChainId={defaultChainId}
>
{children}
</StarknetConfig>
Expand Down
5 changes: 5 additions & 0 deletions docs/pages/demo/change-default-network.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Demo from "../../components/demo";

# Change Default Network (Todo)

<Demo.ChangeDefaultNetwork />
2 changes: 2 additions & 0 deletions docs/pages/demo/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ You can find the source code for these demos [on GitHub](https://github.com/apib

**[Sign Typed Data](/demo/sign-typed-data)**: Shows how to request users to sign a piece of data.

**[Change Default Network](/demo/change-default-network)**: Shows how to change the default network.

## New APIs

**[Request wallet permissions](/demo/wallet-permission)**: Shows how to request wallet permissions.
Expand Down
65 changes: 65 additions & 0 deletions docs/pages/docs/starknet-config.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# StarknetConfig

The React Context provider for Starknet.

## Usage

```tsx twoslash
"use client";
import React from "react";

import { mainnet } from "@starknet-react/chains";
import { StarknetConfig, publicProvider } from "@starknet-react/core";

function App() {
return (
<StarknetConfig chains={[mainnet]} provider={publicProvider()}>
{/* your app here */}
</StarknetConfig>
);
}
```

## Arguments

### chains

- Type: `Chain[]`

List of supported chains.

### provider

- Type: `ChainProviderFactory`

The JSON-RPC provider you want to use. See [the RPC providers page](/docs/providers) for more information.

### connectors

- Type: `Connector[]`

List of wallet connectors you want to use. See [the wallets page](/docs/wallets) for more information.

### explorer

- Type: `ExplorerFactory`

Explorer factory to use. See [the explorers page](/docs/explorers) for more information.

### autoConnect

- Type: `boolean | undefined`

Whether to automatically connect to the first available wallet.

### queryClient

- Type: `QueryClient`

React Query client to use.

### defaultChainId

- Type: `bigint | undefined`

Default chain to use when no wallet is connected. This chain must be included in the `chains` array.
4 changes: 4 additions & 0 deletions docs/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export const sidebar = {
text: "Upgrading to V3",
link: "/docs/upgrading-to-v3",
},
{
text: "StarknetConfig",
link: "/docs/starknet-config",
},
{
text: "Wallets",
link: "/docs/wallets",
Expand Down
26 changes: 22 additions & 4 deletions packages/core/src/context/starknet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ interface UseStarknetManagerProps {
explorer?: ExplorerFactory;
connectors?: Connector[];
autoConnect?: boolean;
defaultChainId?: bigint;
}

function useStarknetManager({
Expand All @@ -110,17 +111,20 @@ function useStarknetManager({
explorer,
connectors = [],
autoConnect = false,
defaultChainId,
}: UseStarknetManagerProps): StarknetState & {
account?: AccountInterface;
address?: Address;
} {
const initialChain = chains[0];
if (initialChain === undefined) {
const defaultChain = defaultChainId
? (chains.find((c) => c.id === defaultChainId) ?? chains[0])
: chains[0];
if (defaultChain === undefined) {
throw new Error("Must provide at least one chain.");
}

const { chain: defaultChain, provider: defaultProvider } = providerForChain(
initialChain,
const { chain: _, provider: defaultProvider } = providerForChain(
defaultChain,
provider,
);

Expand Down Expand Up @@ -174,6 +178,16 @@ function useStarknetManager({
[updateChainAndProvider, state.currentProvider],
);

useEffect(() => {
if (!connectorRef.current) {
// Only update currentChain if no wallet is connected
setState((state) => ({
...state,
currentChain: defaultChain,
currentProvider: providerForChain(defaultChain, provider).provider,
}));
}
}, [defaultChain, provider]);
const connect = useCallback(
async ({ connector }: { connector?: Connector }) => {
if (!connector) {
Expand Down Expand Up @@ -311,6 +325,8 @@ export interface StarknetProviderProps {
queryClient?: QueryClient;
/** Application. */
children?: React.ReactNode;
/** Default chain to use when wallet is not connected */
defaultChainId?: bigint;
}

/** Root Starknet context provider. */
Expand All @@ -321,6 +337,7 @@ export function StarknetProvider({
explorer,
autoConnect,
queryClient,
defaultChainId,
children,
}: StarknetProviderProps): JSX.Element {
const { account, address, ...state } = useStarknetManager({
Expand All @@ -329,6 +346,7 @@ export function StarknetProvider({
explorer,
connectors,
autoConnect,
defaultChainId,
});

return (
Expand Down

0 comments on commit 29096d4

Please sign in to comment.