Skip to content

chore: add sdk token balance example #421

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

Merged
merged 1 commit into from
Mar 31, 2025
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
24 changes: 24 additions & 0 deletions examples/example-vite-token-balance/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
54 changes: 54 additions & 0 deletions examples/example-vite-token-balance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

```js
export default tseslint.config({
extends: [
// Remove ...tseslint.configs.recommended and replace with this
...tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
...tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
...tseslint.configs.stylisticTypeChecked,
],
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```

You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:

```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default tseslint.config({
plugins: {
// Add the react-x and react-dom plugins
'react-x': reactX,
'react-dom': reactDom,
},
rules: {
// other rules...
// Enable its recommended typescript rules
...reactX.configs['recommended-typescript'].rules,
...reactDom.configs.recommended.rules,
},
})
```
Comment on lines +34 to +54
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add installation and usage instructions

The README lacks instructions on how to install dependencies and run the example. Consider adding sections for:

  • Prerequisites
  • Installation
  • Running the example
  • Expected functionality

7 changes: 7 additions & 0 deletions examples/example-vite-token-balance/dojoConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createDojoConfig } from "@dojoengine/core";

import manifest from "../../worlds/dojo-starter/manifest_dev.json";

export const dojoConfig = createDojoConfig({
manifest,
});
28 changes: 28 additions & 0 deletions examples/example-vite-token-balance/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
13 changes: 13 additions & 0 deletions examples/example-vite-token-balance/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Example SDK Tokens</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
43 changes: 43 additions & 0 deletions examples/example-vite-token-balance/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "example-vite-token-balance",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@cartridge/connector": "^0.7.6",
"@cartridge/controller": "^0.7.6",
"@dojoengine/core": "workspace:*",
"@dojoengine/predeployed-connector": "workspace:*",
"@dojoengine/sdk": "workspace:*",
"@dojoengine/torii-client": "workspace:*",
"@dojoengine/utils": "workspace:*",
"@starknet-react/chains": "catalog:",
"@starknet-react/core": "catalog:",
"@tailwindcss/vite": "^4.0.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"starknet": "catalog:",
Comment on lines +20 to +25
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Specify exact versions instead of using "catalog:" references.

Using "catalog:" for version references may lead to unpredictable behavior if the catalog version changes. It's generally better to pin specific versions for production code.

    "@dojoengine/sdk": "workspace:*",
    "@dojoengine/torii-client": "workspace:*",
    "@dojoengine/utils": "workspace:*",
-    "@starknet-react/chains": "catalog:",
-    "@starknet-react/core": "catalog:",
+    "@starknet-react/chains": "^0.2.2",
+    "@starknet-react/core": "^2.1.0",
    "@tailwindcss/vite": "^4.0.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
-    "starknet": "catalog:",
+    "starknet": "^5.24.3",
    "tailwindcss": "^4.0.14"

🌐 Web query:

What are the latest versions of @starknet-react/chains, @starknet-react/core, and starknet packages?

💡 Result:

As of March 2025, the latest versions are:

@starknet-react/chains: 3.1.2
@starknet-react/core: 3.7.2
starknet (JS library): 6.24.1

Key details from sources:

  • @starknet-react/core 3.7.2 was last published 2 months ago (January 2025)[2][6]
  • starknet 6.24.1 received an update 10 days before the current date[12]
  • The types-js package (related to Starknet React) uses version 0.7.X alignment with JSON RPC 0.8[5]
  • Starknet React V3 requires Starknet.js v6[17], which matches the latest 6.24.1 version[12]

These versions support Starknet network upgrades up to v0.13.3[3][5], including new features like optimistic parallelization and applicative recursion[3]. When installing, ensure compatibility between package versions as shown in the Starknet React documentation[19].

Citations:


Update the version references to the latest stable releases in package.json

Please change the version references for the @starknet-react and starknet dependencies from "catalog:" to specific versions. Based on the recent query, the following versions should be used:

  • @starknet-react/chains: ^3.1.2
  • @starknet-react/core: ^3.7.2
  • starknet: ^6.24.1

Below is the updated diff snippet for the file examples/example-vite-token-balance/package.json (lines 20-25):

    "@dojoengine/sdk": "workspace:*",
    "@dojoengine/torii-client": "workspace:*",
    "@dojoengine/utils": "workspace:*",
-    "@starknet-react/chains": "catalog:",
-    "@starknet-react/core": "catalog:",
+    "@starknet-react/chains": "^3.1.2",
+    "@starknet-react/core": "^3.7.2",
    "@tailwindcss/vite": "^4.0.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
-    "starknet": "catalog:",
+    "starknet": "^6.24.1",
    "tailwindcss": "^4.0.14"

Using specific version numbers will help avoid unpredictable behavior if the catalog versions change. Please update accordingly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@starknet-react/chains": "catalog:",
"@starknet-react/core": "catalog:",
"@tailwindcss/vite": "^4.0.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"starknet": "catalog:",
{
"dependencies": {
"@dojoengine/sdk": "workspace:*",
"@dojoengine/torii-client": "workspace:*",
"@dojoengine/utils": "workspace:*",
"@starknet-react/chains": "^3.1.2",
"@starknet-react/core": "^3.7.2",
"@tailwindcss/vite": "^4.0.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"starknet": "^6.24.1",
"tailwindcss": "^4.0.14"
}
}

"tailwindcss": "^4.0.14"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
"globals": "^15.15.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.24.1",
"vite": "^6.2.0",
"vite-plugin-top-level-await": "^1.5.0",
"vite-plugin-wasm": "^3.4.1"
}
}
1 change: 1 addition & 0 deletions examples/example-vite-token-balance/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions examples/example-vite-token-balance/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}
15 changes: 15 additions & 0 deletions examples/example-vite-token-balance/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import "./App.css";
import { Wallet } from "./components/wallet";
import TokenBalance from "./components/token-balance";

function App() {
return (
<>
<h1>Connect your wallet</h1>
<Wallet />
<TokenBalance />
</>
);
}

export default App;
1 change: 1 addition & 0 deletions examples/example-vite-token-balance/src/assets/react.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { PropsWithChildren } from "react";
import { mainnet } from "@starknet-react/chains";
import { jsonRpcProvider, StarknetConfig, voyager } from "@starknet-react/core";
import { dojoConfig } from "../../dojoConfig";
import { usePredeployedAccounts } from "@dojoengine/predeployed-connector/react";
import { ControllerConnector } from "@cartridge/connector";
import { shortString } from "starknet";

const controller = new ControllerConnector({
chains: [
{
rpcUrl: "http://localhost:5050",
},
],
defaultChainId: shortString.encodeShortString("KATANA"),
});
Comment on lines +9 to +16
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid hardcoding RPC URL in the controller configuration

The RPC URL is hardcoded to "http://localhost:5050". Consider using the URL from dojoConfig for consistency with the rest of the application.

const controller = new ControllerConnector({
    chains: [
        {
-            rpcUrl: "http://localhost:5050",
+            rpcUrl: dojoConfig.rpcUrl as string,
        },
    ],
    defaultChainId: shortString.encodeShortString("KATANA"),
});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const controller = new ControllerConnector({
chains: [
{
rpcUrl: "http://localhost:5050",
},
],
defaultChainId: shortString.encodeShortString("KATANA"),
});
const controller = new ControllerConnector({
chains: [
{
rpcUrl: dojoConfig.rpcUrl as string,
},
],
defaultChainId: shortString.encodeShortString("KATANA"),
});


export default function StarknetProvider({ children }: PropsWithChildren) {
const { connectors } = usePredeployedAccounts({
rpc: dojoConfig.rpcUrl as string,
id: "katana",
name: "Katana",
});

const provider = jsonRpcProvider({
rpc: () => ({ nodeUrl: dojoConfig.rpcUrl as string }),
});

return (
<StarknetConfig
chains={[mainnet]}
provider={provider}
connectors={[...connectors, controller]}
explorer={voyager}
autoConnect
>
{/* @ts-ignore react version mismatch */}
{children}
Comment on lines +37 to +38
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Remove TypeScript ignore comment and fix type issue properly

The code uses a TypeScript ignore comment to suppress a React version mismatch error. Instead of ignoring the error, consider addressing the underlying issue by ensuring compatible versions of dependencies.

-        {/* @ts-ignore react version mismatch */}
         {children}

Then update your package.json to ensure compatible versions of React dependencies.


🏁 Script executed:

#!/bin/bash
# Check React versions in package.json
grep -r "\"react\":" examples/example-vite-token-balance/package.json
grep -r "\"@starknet-react" examples/example-vite-token-balance/package.json

Length of output: 265


Fix dependency mismatches and remove the TS ignore directive

  • Action Required:

    • Remove the @ts-ignore comment in examples/example-vite-token-balance/src/components/starknet-provider.tsx (lines 37–38) so that TypeScript can catch the version mismatch issues.
    • Update your dependency versions in examples/example-vite-token-balance/package.json. The output shows "react": "^19.0.0", which likely isn’t what your @starknet-react packages expect. Adjust React (and, if needed, the @starknet-react/* dependencies) so they are compatible—for example, consider downgrading React to a version that aligns with the expectations of your other packages.
  • Diff Suggestion:

    -        {/* @ts-ignore react version mismatch */}
             {children}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{/* @ts-ignore react version mismatch */}
{children}
{children}

</StarknetConfig>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useTokens, WithAccount } from "@dojoengine/sdk/react";

function TokenBalance({ address }: { address: `0x${string}` }) {
const { tokens, getBalance, toDecimal } = useTokens({
accountAddresses: [address],
});

return (
<div>
Token Balance: {address}
<div>
{tokens.map((token, idx) => (
<div key={idx}>
Comment on lines +12 to +13
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using array index as key in React lists

Using array indices as keys can lead to rendering issues if the order of items changes. Consider using a more stable unique identifier like token.contract_address as the key.

-{tokens.map((token, idx) => (
-    <div key={idx}>
+{tokens.map((token) => (
+    <div key={token.contract_address}>

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 13-13: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)

{token.symbol}
&nbsp;
{toDecimal(token, getBalance(token))}
</div>
))}
</div>
</div>
);
}

export default WithAccount(TokenBalance);
Empty file.
68 changes: 68 additions & 0 deletions examples/example-vite-token-balance/src/components/wallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {
Connector,
useAccount,
useConnect,
useDisconnect,
} from "@starknet-react/core";
import { useCallback, useState } from "react";

export function Wallet() {
const { connectAsync, connectors } = useConnect();
const { address } = useAccount();
const { disconnect } = useDisconnect();
const [pendingConnectorId, setPendingConnectorId] = useState<
string | undefined
>(undefined);

const connect = useCallback(
async (connector: Connector) => {
setPendingConnectorId(connector.id);
try {
await connectAsync({ connector });
} catch (error) {
console.error(error);
}
setPendingConnectorId(undefined);
},
[connectAsync]
);

function isWalletConnecting(connectorId: string) {
return pendingConnectorId === connectorId;
}

if (address) {
return (
<div className="mb-6 flex justify-center">
<div style={{ display: "flex", gap: "1rem" }}>
<button
onClick={() => disconnect()}
className="text-white border border-white p-3"
>
Disconnect
</button>
</div>
</div>
);
}

return (
<div className="mb-6">
<h2 className="text-white">Connect Wallet</h2>
<div style={{ display: "flex", gap: "1rem" }}>
{}
{connectors.map((connector) => (
<button
key={connector.id}
onClick={() => connect(connector)}
disabled={!connector.available()}
className="text-white border border-white p-3"
>
{connector.name}
{isWalletConnecting(connector.id) && "Connecting"}
</button>
))}
</div>
</div>
);
}
Loading
Loading