Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: remove allowlist & denylist functionality #284

Merged
merged 1 commit into from
Oct 22, 2024
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
6 changes: 6 additions & 0 deletions .changeset/friendly-files-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@reactive-dot/react": minor
"@reactive-dot/vue": minor
---

BREAKING: Removed `allowlist` and `denylist` functionality. This feature was too specific, and it’s now recommended for users to implement it as a recipe in their own applications if needed.
70 changes: 55 additions & 15 deletions apps/docs/docs/guides/multichain.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@ function Component() {

## Chain narrowing

By default, ReactiveDOT provides type definitions based on the merged definitions of all chains in the config. For example, if your DApp is set up to be used with Polkadot, Kusama, and Westend, the following code will not work because the Bounties pallet only exists on Polkadot and Kusama, not on Westend:
By default, ReactiveDOT merges type definitions from all the chains in the config. For instance, if your DApp is set up to work with Polkadot, Kusama, and Westend, the following code will fail because the Bounties pallet is available only on Polkadot and Kusama, not on Westend:

```tsx
function Component() {
// Since `Bounties` pallet doesn't exist on Westend, this will:
// 1. Raise a TypeScript error
// 2. Throw an error during runtime if Westend is selected
// Since the `Bounties` pallet doesn't exist on Westend, this will:
// 1. Trigger a TypeScript error
// 2. Cause a runtime error if Westend is selected
const bountyCount = useLazyLoadQuery((builder) =>
builder.readStorage("Bounties", "BountyCount", []),
);
Expand All @@ -134,7 +134,7 @@ function Component() {
}
```

You have the option of either explicitly specifying the chain to query, which will override the chain ID provided via context:
To resolve this, you can explicitly specify the chain to query, which will override the chain ID provided by context:

```tsx
function Component() {
Expand All @@ -147,21 +147,31 @@ function Component() {
}
```

Or, to continue using the chain ID provided via context, you can use the [`useChainId`](/api/react/function/useChainId) hook along with its allowlist/denylist functionality:
Alternatively, if you want to keep using the chain ID provided by context, you can use the following pattern:

```tsx
function useBountiesChainId() {
const chainId = useChainId();

switch (chainId) {
case "polkadot":
case "kusama":
return chainId;
default:
throw new Error("This chain does not support bounties", {
cause: chainId,
});
}
}

function BountiesPalletRequiredComponent() {
const bountyCount = useLazyLoadQuery(
(builder) => builder.readStorage("Bounties", "BountyCount", []),
{
// `useChainId` with the allow/deny list will:
// 1. Throw an error if the context's chain ID conflicts with the list(s)
// 2. Restrict descriptors used by `useLazyLoadQuery` to provide correct intellisense
chainId: useChainId({
allowlist: ["polkadot", "kusama"],
// Or
denylist: ["westend"],
}),
// This will:
// 1. Throw an error if the chain ID does not support bounties
// 2. Restrict the possible chain types for better intellisense
chainId: useBountiesChainId(),
},
);

Expand All @@ -171,7 +181,7 @@ function BountiesPalletRequiredComponent() {
function App() {
// ...

// Only use compatible chain IDs, else an error will be thrown
// Use only compatible chain IDs, otherwise an error will be thrown
const bountiesEnabledChainIds = ["polkadot", "kusama"] satisfies ChainId[];

return (
Expand All @@ -186,3 +196,33 @@ function App() {
);
}
```

Finally, if your application primarily uses a few chains but interacts with many other supporting chains, you can use the `targetChains` option:

```ts
import { defineConfig } from "@reactive-dot/core";

const config = defineConfig({
chains: {
polkadot: {
// ...
},
polkadot_asset_hub: {
// ...
},
polkadot_people: {
// ...
},
polkadot_collectives: {
// ...
},
polkadot_bridge_hub: {
// ...
},
},
// This will restrict the default chain types used by hooks
// to just Polkadot when no explicit `chainId` is provided
targetChains: ["polkadot"],
// ...
});
```
19 changes: 2 additions & 17 deletions packages/react/src/hooks/use-chain-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,16 @@ export function useChainIds() {
/**
* Hook for getting the current chain ID.
*
* @param options - Additional options
* @returns The current chain ID
*/
export function useChainId<
const TAllowList extends ChainId[],
const TDenylist extends ChainId[] = [],
>(options?: { allowlist?: TAllowList; denylist?: TDenylist }) {
export function useChainId() {
const chainId = useContext(ChainIdContext);

if (chainId === undefined) {
throw new ReactiveDotError("No chain ID provided");
}

if (options?.allowlist?.includes(chainId) === false) {
throw new ReactiveDotError("Chain ID not allowed", { cause: chainId });
}

if (options?.denylist?.includes(chainId)) {
throw new ReactiveDotError("Chain ID denied", { cause: chainId });
}

return chainId as Exclude<
Extract<ChainId, TAllowList[number]>,
TDenylist[number]
>;
return chainId as ChainId;
}

/**
Expand Down
19 changes: 2 additions & 17 deletions packages/vue/src/composables/use-chain-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,9 @@ export function useChainIds() {
/**
* Composable for getting the current chain ID.
*
* @param options - Additional options
* @returns The current chain ID
*/
export function useChainId<
const TAllowList extends ChainId[],
const TDenylist extends ChainId[] = [],
>(options?: { allowlist?: TAllowList; denylist?: TDenylist }) {
export function useChainId() {
const injectedChainId = inject(chainIdKey);

return computed(() => {
Expand All @@ -32,18 +28,7 @@ export function useChainId<
throw new ReactiveDotError("No chain ID provided");
}

if (options?.allowlist?.includes(chainId) === false) {
throw new ReactiveDotError("Chain ID not allowed", { cause: chainId });
}

if (options?.denylist?.includes(chainId)) {
throw new ReactiveDotError("Chain ID denied", { cause: chainId });
}

return chainId as Exclude<
Extract<ChainId, TAllowList[number]>,
TDenylist[number]
>;
return chainId as ChainId;
});
}

Expand Down