Skip to content

Commit 78e247a

Browse files
authored
Support Solscan and other custom explorers (#11)
* use gill from catalog * docs(changeset): Support custom explorers (Solscan example) * Support custom explorers (solscan)
1 parent 0a3cfb6 commit 78e247a

File tree

12 files changed

+130
-31
lines changed

12 files changed

+130
-31
lines changed

.changeset/swift-sides-grin.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"example-dapp": patch
3+
"@macalinao/grill": patch
4+
---
5+
6+
Support custom explorers (Solscan example)

.claude/commands/checkandfix.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
allowed-tools:
3+
- Bash
4+
- Edit
5+
- MultiEdit
6+
- Read
7+
description: Run TypeScript checks and fix all errors
8+
---
9+
10+
Run the following commands in sequence and fix all TypeScript and linting errors:
11+
12+
1. First, run `bun run build` to identify all TypeScript errors
13+
2. Fix each TypeScript error found by:
14+
- Reading the affected files
15+
- Understanding the error context
16+
- Making the necessary code changes to resolve the errors
17+
3. After fixing TypeScript errors, run `bun run lint:fix` to automatically fix linting issues
18+
4. If there are any remaining linting errors that can't be auto-fixed, manually fix them
19+
5. Run both commands again to verify all errors are resolved. IF THE ERRORS ARE NOT RESOLVED, REPEAT THE ENTIRE PROCESS.
20+
6. Provide a summary of what was fixed
21+
22+
Be thorough and ensure all errors are resolved before completing the task.

apps/example-dapp/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
"@tanstack/react-router": "^1.131.2",
3939
"class-variance-authority": "^0.7.1",
4040
"clsx": "^2.1.1",
41-
"gill": "^0.10.3",
42-
"gill-react": "^0.5.0",
41+
"gill": "catalog:",
42+
"gill-react": "catalog:",
4343
"lucide-react": "^0.539.0",
4444
"react": "catalog:",
4545
"react-dom": "catalog:",

apps/example-dapp/src/App.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { GrillProvider } from "@macalinao/grill";
1+
import { GrillProvider, getSolscanExplorerLink } from "@macalinao/grill";
22
import { WalletAdapterCompatProvider } from "@macalinao/wallet-adapter-compat";
33
import {
44
ConnectionProvider,
@@ -12,7 +12,7 @@ import {
1212
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
1313
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
1414
import { createRouter, RouterProvider } from "@tanstack/react-router";
15-
import { createSolanaClient } from "gill";
15+
import { createSolanaClient, getPublicSolanaRpcUrl } from "gill";
1616
import { SolanaProvider } from "gill-react";
1717
import type * as React from "react";
1818
import { useMemo } from "react";
@@ -33,12 +33,12 @@ declare module "@tanstack/react-router" {
3333

3434
import "@solana/wallet-adapter-react-ui/styles.css";
3535

36-
console.log(import.meta.env);
37-
console.log(import.meta.env.VITE_SOLANA_RPC_URL ?? "mainnet-beta");
36+
const endpoint =
37+
import.meta.env.VITE_SOLANA_RPC_URL ?? getPublicSolanaRpcUrl("mainnet-beta");
3838

3939
const queryClient = new QueryClient();
4040
const solanaClient = createSolanaClient({
41-
urlOrMoniker: import.meta.env.VITE_SOLANA_RPC_URL ?? "mainnet-beta",
41+
urlOrMoniker: endpoint,
4242
});
4343

4444
export const App: React.FC = () => {
@@ -50,15 +50,15 @@ export const App: React.FC = () => {
5050
return (
5151
<ThemeProvider defaultTheme="dark" storageKey="grill-theme">
5252
<QueryClientProvider client={queryClient}>
53-
<ConnectionProvider endpoint={"https://api.mainnet-beta.solana.com"}>
53+
<ConnectionProvider endpoint={endpoint}>
5454
<WalletAdapterProvider wallets={wallets} autoConnect>
5555
<WalletModalProvider>
5656
<WalletAdapterCompatProvider>
5757
<SolanaProvider client={solanaClient}>
58-
<GrillProvider>
58+
<GrillProvider getExplorerLink={getSolscanExplorerLink}>
5959
<div className="min-h-screen bg-background">
6060
<RouterProvider router={router} />
61-
<Toaster position="bottom-right" />
61+
<Toaster richColors position="bottom-right" />
6262
<ReactQueryDevtools initialIsOpen={false} />
6363
</div>
6464
</GrillProvider>

bun.lock

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
"@tanstack/react-router": "^1.131.2",
3939
"class-variance-authority": "^0.7.1",
4040
"clsx": "^2.1.1",
41-
"gill": "^0.10.3",
42-
"gill-react": "^0.5.0",
41+
"gill": "catalog:",
42+
"gill-react": "catalog:",
4343
"lucide-react": "^0.539.0",
4444
"react": "catalog:",
4545
"react-dom": "catalog:",
@@ -90,8 +90,8 @@
9090
"@solana-program/token": "^0.5.1",
9191
"@solana/kit": "catalog:",
9292
"@tanstack/react-query": "catalog:",
93-
"gill": "^0.10.3",
94-
"gill-react": "^0.5.0",
93+
"gill": "catalog:",
94+
"gill-react": "catalog:",
9595
"react": "catalog:",
9696
},
9797
"devDependencies": {
@@ -105,7 +105,7 @@
105105
},
106106
"peerDependencies": {
107107
"react": "^18.0.0 || ^19.0.0",
108-
"sonner": "^1.7.0",
108+
"sonner": "^2",
109109
},
110110
},
111111
"packages/solana-batch-accounts-loader": {
@@ -171,6 +171,8 @@
171171
"@solana/wallet-adapter-react": "^0.15.39",
172172
"@tanstack/react-query": "^5.84.0",
173173
"eslint": "^9.29.0",
174+
"gill": "^0.10.3",
175+
"gill-react": "^0.5.0",
174176
"react": "^19.1.0",
175177
"react-dom": "^19.1.0",
176178
"tslib": "^2.8.1",

package.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@
44
"type": "module",
55
"license": "Apache-2.0",
66
"author": "Ian Macalinao <[email protected]>",
7-
"repository": {
8-
"type": "git",
9-
"url": "git+https://github.com/macalinao/grill.git"
10-
},
117
"workspaces": {
128
"packages": [
139
"packages/*",
@@ -25,7 +21,9 @@
2521
"@solana/wallet-adapter-base": "^0.9.27",
2622
"@solana/wallet-adapter-react": "^0.15.39",
2723
"react": "^19.1.0",
28-
"react-dom": "^19.1.0"
24+
"react-dom": "^19.1.0",
25+
"gill": "^0.10.3",
26+
"gill-react": "^0.5.0"
2927
}
3028
},
3129
"scripts": {

packages/grill/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
"@solana-program/token": "^0.5.1",
4949
"@solana/kit": "catalog:",
5050
"@tanstack/react-query": "catalog:",
51-
"gill": "^0.10.3",
52-
"gill-react": "^0.5.0",
51+
"gill": "catalog:",
52+
"gill-react": "catalog:",
5353
"react": "catalog:"
5454
},
5555
"devDependencies": {
@@ -63,7 +63,7 @@
6363
},
6464
"peerDependencies": {
6565
"react": "^18.0.0 || ^19.0.0",
66-
"sonner": "^1.7.0"
66+
"sonner": "^2"
6767
},
6868
"lint-staged": {
6969
"*.{js,jsx,ts,tsx,cjs,mjs,cts,mts}": [

packages/grill/src/contexts/grill-context.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import type { DataLoader } from "@macalinao/dataloader-es";
22
import type { Address, EncodedAccount } from "@solana/kit";
3+
import type { GetExplorerLinkArgs } from "gill";
34
import { createContext, useContext } from "react";
45
import type { SendTXFunction } from "../utils/internal/create-send-tx.js";
56

7+
export type GetExplorerLinkFunction = (args?: GetExplorerLinkArgs) => string;
8+
69
/**
710
* Context value interface for SolanaAccountProvider.
811
* Provides access to a batch account loader for efficient Solana account fetching.
@@ -16,6 +19,11 @@ export interface GrillContextValue {
1619
* Function to send transactions with batching and confirmation
1720
*/
1821
sendTX: SendTXFunction;
22+
23+
/**
24+
* Function to get explorer link for a transaction signature
25+
*/
26+
getExplorerLink: GetExplorerLinkFunction;
1927
}
2028

2129
/**

packages/grill/src/providers/grill-headless-provider.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { createBatchAccountsLoader } from "@macalinao/solana-batch-accounts-loader";
22
import { useQueryClient } from "@tanstack/react-query";
33
import type { Address } from "gill";
4+
import { getExplorerLink as defaultGetExplorerLink } from "gill";
45
import { useSolanaClient } from "gill-react";
56
import type { FC, ReactNode } from "react";
67
import { useCallback, useMemo } from "react";
8+
import type { GetExplorerLinkFunction } from "../contexts/grill-context.js";
79
import { GrillContext } from "../contexts/grill-context.js";
810
import { useKitWallet } from "../hooks/use-kit-wallet.js";
911
import type { TransactionStatusEventCallback } from "../types.js";
@@ -17,6 +19,8 @@ export interface GrillHeadlessProviderProps {
1719
/** Duration in milliseconds to wait before sending a batch. Defaults to 10ms. */
1820
batchDurationMs?: number;
1921
onTransactionStatusEvent?: TransactionStatusEventCallback;
22+
/** Custom function to get explorer link for a transaction signature. Defaults to gill's getExplorerLink. */
23+
getExplorerLink?: GetExplorerLinkFunction;
2024
}
2125

2226
/**
@@ -33,6 +37,7 @@ export const GrillHeadlessProvider: FC<GrillHeadlessProviderProps> = ({
3337
onTransactionStatusEvent = (e) => {
3438
console.log(e);
3539
},
40+
getExplorerLink = defaultGetExplorerLink,
3641
}) => {
3742
const { rpc } = useSolanaClient();
3843
const queryClient = useQueryClient();
@@ -66,8 +71,9 @@ export const GrillHeadlessProvider: FC<GrillHeadlessProviderProps> = ({
6671
rpc,
6772
refetchAccounts,
6873
onTransactionStatusEvent,
74+
getExplorerLink,
6975
}),
70-
[signer, rpc, refetchAccounts, onTransactionStatusEvent],
76+
[signer, rpc, refetchAccounts, onTransactionStatusEvent, getExplorerLink],
7177
);
7278

7379
return (
@@ -76,6 +82,7 @@ export const GrillHeadlessProvider: FC<GrillHeadlessProviderProps> = ({
7682
accountLoader,
7783
refetchAccounts,
7884
sendTX,
85+
getExplorerLink,
7986
}}
8087
>
8188
{children}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { GetExplorerLinkFunction } from "../contexts/grill-context.js";
2+
3+
/**
4+
* Creates a Solscan explorer link for viewing transactions, addresses, or blocks.
5+
*
6+
* @param args - The arguments for generating the explorer link
7+
* @returns A Solscan URL string for the specified resource
8+
*
9+
* @example
10+
* // For a transaction
11+
* getSolscanExplorerLink({ transaction: signature })
12+
*
13+
* @example
14+
* // For an address
15+
* getSolscanExplorerLink({ address: publicKey })
16+
*
17+
* @example
18+
* // For a specific cluster
19+
* getSolscanExplorerLink({ transaction: signature, cluster: "devnet" })
20+
*/
21+
export const getSolscanExplorerLink: GetExplorerLinkFunction = (args = {}) => {
22+
const baseUrl = "https://solscan.io";
23+
24+
// Determine the cluster suffix
25+
let clusterSuffix = "";
26+
if (args.cluster) {
27+
if (args.cluster === "devnet") {
28+
clusterSuffix = "?cluster=devnet";
29+
} else if (args.cluster === "testnet") {
30+
clusterSuffix = "?cluster=testnet";
31+
}
32+
// mainnet-beta and mainnet don't need a suffix
33+
}
34+
35+
// Generate the appropriate link based on the type
36+
if ("transaction" in args) {
37+
return `${baseUrl}/tx/${args.transaction}${clusterSuffix}`;
38+
}
39+
40+
if ("address" in args) {
41+
return `${baseUrl}/account/${args.address}${clusterSuffix}`;
42+
}
43+
44+
if ("block" in args) {
45+
return `${baseUrl}/block/${args.block}${clusterSuffix}`;
46+
}
47+
48+
// Default to homepage if no specific resource is provided
49+
return baseUrl;
50+
};

0 commit comments

Comments
 (0)