Skip to content

Dashboard, SDK: ERC20 assets #7321

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
Aug 15, 2025
Merged

Conversation

kumaryash90
Copy link
Member

@kumaryash90 kumaryash90 commented Jun 11, 2025


PR-Codex overview

This PR focuses on enhancing the thirdweb package by introducing new functionalities and updates for managing ERC20 assets, including the implementation of various events, functions, and utility methods for asset deployment and interaction.

Detailed summary

  • Added initialize functions to various asset-related contracts.
  • Introduced event handling for AssetDistributed, RouterUpdated, and RewardLockerUpdated.
  • Implemented methods for checking router and reward locker support.
  • Enhanced the distributeToken function to handle multiple recipients.
  • Updated ABI files for new function signatures and events.
  • Improved utility functions for encoding parameters and transaction preparation.

The following files were skipped due to too many changes: packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/initialize.ts, packages/thirdweb/src/extensions/assets/__generated__/FeeManager/write/initialize.ts, packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/listAsset.ts, packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/distributeAsset.ts, packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAsset.ts, packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/sellAsset.ts, packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/buyAsset.ts, packages/thirdweb/src/extensions/assets/__generated__/ERC20Asset/write/initialize.ts, packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/addImplementation.ts, packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAssetById.ts, packages/thirdweb/src/extensions/assets/__generated__/AssetInfraDeployer/write/deployInfraProxyDeterministic.ts, packages/thirdweb/package.json, packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAssetByImplementationConfig.ts, packages/thirdweb/src/assets/bootstrap.ts

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Added on-page Rewards workflow (Claim Rewards page, sidebar "Rewards" link) and expanded ERC20 asset creation flows (new coin/pool sale and airdrop options).
  • UI / UX

    • Improved token sale & distribution UIs, grouped recent transfers, SSR loading placeholders, refined spacing/styling, chain prioritization in selectors, customizable wallet fallback icon, and enhanced distribution chart legend/value display.
  • API / Props

    • Several component props and callbacks now include chain/contract info and router/support flags.
  • Chores

    • Release metadata added for an ERC20-assets patch.

@kumaryash90 kumaryash90 requested review from jnsdls, joaquim-verges and a team as code owners June 11, 2025 14:14
Copy link

changeset-bot bot commented Jun 11, 2025

🦋 Changeset detected

Latest commit: d227bc3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
thirdweb Patch
@thirdweb-dev/nebula Patch
@thirdweb-dev/wagmi-adapter Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

vercel bot commented Jun 11, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
docs-v2 Ready Ready Preview Comment Aug 15, 2025 10:41pm
login Ready Ready Preview Comment Aug 15, 2025 10:41pm
nebula Ready Ready Preview Comment Aug 15, 2025 10:41pm
thirdweb_playground Ready Ready Preview Comment Aug 15, 2025 10:41pm
thirdweb-www Ready Ready Preview Comment Aug 15, 2025 10:41pm
wallet-ui Ready Ready Preview Comment Aug 15, 2025 10:41pm

Copy link
Contributor

graphite-app bot commented Jun 11, 2025

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

Copy link
Contributor

coderabbitai bot commented Jun 11, 2025

Walkthrough

Adds ERC20 asset support and rewards features: new asset type "ERC20Asset", bifurcated token creation/launch flows (ERC20Asset vs DropERC20), form/schema and UI updates for dual sale modes, rewards page and helpers, a tokens SDK export barrel, and assorted component prop and UI tweaks. Includes a release changeset.

Changes

Cohort / File(s) Summary
Release metadata
\.changeset/young-carrots-burn.md
New changeset recording a patch release for package "thirdweb" with note "ERC20 assets".
Analytics / typings
apps/dashboard/src/@/analytics/report.ts
Add ERC20Asset to AssetContractType, introduce CoinCreationStep type, update reportAssetCreationFailed to use CoinCreationStep.
Network selectors
apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx
Stop mutating inputs; add chain prioritization and reordering; add priorityChains?: number[] prop to SingleNetworkSelector; default exclude deprecated chains.
Token selector & small UI
apps/dashboard/src/@/components/blocks/TokenSelector.tsx, apps/dashboard/src/@/components/ui/CopyAddressButton.tsx, apps/dashboard/src/@/components/ui/tabs.tsx, apps/dashboard/src/@/components/blocks/multi-step-status/multi-step-status.tsx, apps/dashboard/src/@/components/blocks/wallet-address.tsx
Make Select uncontrolled while loading; forward iconClassName in CopyAddressButton; add bottomLineClassName? to TabButtons; minor JSX/CSS tweaks; add fallbackIcon? to WalletAddress/WalletAvatar and update zero-address rendering.
Distribution chart
apps/dashboard/src/@/components/blocks/distribution-chart.tsx
Add value: string to Segment; make title optional and add titleClassName? and barClassName?; legend now shows segment.value; add segment borders.
Contract table & hooks
apps/dashboard/src/@/components/contract-components/tables/contract-table.tsx, apps/dashboard/src/@/hooks/project-contracts.ts
Map ERC20Asset → "Coin" in contract-type display; extend useAddContractToProject contractType union to include "ERC20Asset".
Contract page metadata & sidebar
apps/dashboard/src/app/.../_utils/getContractPageMetadata.ts, .../getContractPageMetadataSetup.ts, .../getContractPageSidebarLinks.ts
Add showClaimRewards: boolean to ContractPageMetadata; implement isClaimRewardsSupported check and propagate showClaimRewards; add "Rewards" sidebar link hidden when showClaimRewards is false.
Recent transfers UI
apps/dashboard/src/app/.../public-pages/erc20/_components/RecentTransfers.tsx
Group transfers by transaction_hash, render grouped rows (senders/recipients/amounts), add TokenAmount helper and relative timestamp UI.
Universal bridge API
apps/dashboard/src/app/.../tokens/create/_apis/create-token-on-bridge.ts
Add createTokenOnUniversalBridge({ chainId, tokenAddress, client }) to POST token registration to bridge endpoint (prod/dev domains).
Token creation schema & utils
apps/dashboard/src/app/.../tokens/create/_common/form.ts, .../utils/calculate-tick.ts
Restructure distribution schema into erc20Asset_poolMode and dropERC20Mode; add priceAmountSchema and tick validation using new getInitialTickValue and isValidTickValue.
Token creation pages & client logic
apps/dashboard/src/app/.../create-token-page-impl.tsx, .../create-token-page.client.tsx, .../create-token-page.stories.tsx
Split create functions into ERC20Asset and DropERC20 groups (new CreateTokenFunctions type); implement asset-specific deploy/approve/airdrop/set-claim/mint flows; add feature/chain checks; call createTokenOnUniversalBridge on success; update onLaunchSuccess to accept { chainId, contractAddress }.
Token creation UI fieldsets & small tweaks
apps/dashboard/src/app/.../token-info/*, .../nft-collection-info-fieldset.tsx, .../step-card.tsx, .../SocialUrls.tsx
Add SSR Skeleton placeholders for chain selector, pass priorityChains to selector, setValue now validates on change, spacing/padding and responsive layout adjustments.
Token distribution / sale UI
apps/dashboard/src/app/.../distribution/token-distribution.tsx, .../distribution/token-sale.tsx, .../distribution/drop-erc20-token-sale.tsx, .../distribution/token-airdrop.tsx
Support dual sale modes: new DropERC20_TokenSaleSection, router-aware gating (isRouterEnabled), new PoolConfig UI for ERC20 pool sales, compact number formatting, and mode-aware distribution calculations and chart values.
Launch flow / status
apps/dashboard/src/app/.../launch/launch-token.tsx
Replace generic step ids with asset-specific step IDs (erc20-asset:*, drop-erc20:*), bifurcate execution by saleMode, update onLaunchSuccess to pass { chainId, contractAddress }, track contractAddress state, and update analytics reporting.
Token distribution consumers & stories
apps/dashboard/src/app/.../token/create-token-page.stories.tsx, .../token-distribution/*
Update stories and mocks to the new nested distribution schema and CreateTokenFunctions shape; adapt consumers to sale-mode UI and new props.
Rewards feature: server page, UI, and utils
apps/dashboard/src/app/.../rewards/page.tsx, .../rewards/components/claim-rewards-page.tsx, .../rewards/components/claim-rewards-page.stories.tsx, .../rewards/utils/rewards.ts, .../rewards/utils/unclaimed-fees.ts
New server page and client UI for Claim Rewards, helpers getValidReward and getUnclaimedFees, client ClaimRewardsPageUI with distribution chart and transaction handling, and Storybook stories.
Thirdweb tokens barrel
packages/thirdweb/src/exports/tokens.ts, packages/thirdweb/package.json
New export barrel ./tokens aggregating token-related APIs/types (getRewards, claimRewards, createToken, distributeToken, getDeployedEntrypointERC20, isPoolRouterEnabled, constants, utils, types); package.json exports and typesVersions updated.
New token ABIs
packages/thirdweb/scripts/generate/abis/tokens/*.json
Add multiple new token-related ABI artifacts (ContractFactory, ERC20Asset, ERC20Entrypoint, FeeManager, PoolRouter, RewardLocker) and supporting ABI JSONs.
Small/no-op / formatting changes
apps/dashboard/src/@/contexts/error-handler.tsx, various small UI files
Minor formatting, spacing, skeleton placeholders, and className adjustments not changing functional APIs.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant UI as Dashboard UI
participant SDK as ThirdwebClient / SDK
participant Bridge as Universal Bridge
participant Explorer as Chain Explorer

UI->>UI: User selects saleMode (erc20-asset or drop-erc20)
alt erc20-asset:pool
UI->>SDK: ERC20Asset.deployContract(params)
SDK-->>UI: { contractAddress }
UI->>SDK: ERC20Asset.approveAirdropTokens(params) [optional]
SDK-->>UI: txReceipt
UI->>SDK: ERC20Asset.airdropTokens(params) [optional]
SDK-->>UI: txReceipt
else drop-erc20:token-drop
UI->>SDK: DropERC20.deployContract(params)
SDK-->>UI: { contractAddress }
UI->>SDK: DropERC20.setClaimConditions(params)
SDK-->>UI: ok
UI->>SDK: DropERC20.mintTokens(params) [conditional]
SDK-->>UI: txReceipt
UI->>SDK: DropERC20.airdropTokens(params) [optional]
SDK-->>UI: txReceipt
end
UI->>Bridge: createTokenOnUniversalBridge({ chainId, tokenAddress }) (async)
Bridge-->>UI: HTTP response
UI->>Explorer: show View Coin link (contractAddress)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • TEAM-0000: Entity not found: Issue - Could not find referenced Issue.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch yash/ocr-contracts-integration

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added packages SDK Involves changes to the thirdweb SDK labels Jun 11, 2025
@kumaryash90 kumaryash90 marked this pull request as draft June 11, 2025 14:15
@kumaryash90 kumaryash90 added the DO NOT MERGE This pull request is still in progress and is not ready to be merged. label Jun 11, 2025
@kumaryash90 kumaryash90 changed the title OCR ERC20 assets integration [WIP] OCR ERC20 assets integration Jun 11, 2025
@kumaryash90 kumaryash90 changed the title [WIP] OCR ERC20 assets integration [WIP] ERC20 assets Jun 11, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/thirdweb/src/assets/index.ts (1)

1-4: Same extension concern & future export growth

  1. Ensure the .js suffix plays nicely with the compiler/bundler (see previous note).
  2. As assets utilities grow, consider grouping exports in an index.ts barrel only once they stabilise; otherwise tree-shaking may pull in unfinished stubs.
packages/thirdweb/scripts/generate/abis/assets/AssetEntrypointERC20.json (1)

1-34: Nit: store ABI as generated artefact, not source

Committed raw ABI arrays bloat the repo (~4 KB here) and create noisy diffs. If this file is generated by a script in /scripts/generate, prefer ignoring it in Git and generating during build/publish instead.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 950bbae and 388c7a0.

⛔ Files ignored due to path filters (19)
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/AssetCreated.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/AssetDistributed.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/ImplementationAdded.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/RewardLockerUpdated.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/events/RouterUpdated.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/read/getImplementation.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/read/getRewardLocker.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/read/getRouter.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/addImplementation.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/buyAsset.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAsset.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAssetById.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAssetByImplementationConfig.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/distributeAsset.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/initialize.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/listAsset.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/sellAsset.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/setRewardLocker.ts is excluded by !**/__generated__/**
  • packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/setRouter.ts is excluded by !**/__generated__/**
📒 Files selected for processing (4)
  • packages/thirdweb/scripts/generate/abis/assets/AssetEntrypointERC20.json (1 hunks)
  • packages/thirdweb/src/assets/create-token.ts (1 hunks)
  • packages/thirdweb/src/assets/index.ts (1 hunks)
  • packages/thirdweb/src/exports/assets.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/thirdweb/src/assets/create-token.ts

[error] 1-1: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 3-3: This parameter is unused.

Unused variables usually are result of incomplete refactoring, typos and other source of bugs.
Unsafe fix: If this is intentional, prepend params with an underscore.

(lint/correctness/noUnusedVariables)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Lint Packages
  • GitHub Check: Build Packages
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
packages/thirdweb/src/exports/assets.ts (1)

1-1: Double-check relative path & extension

The re-export targets "../assets/index.js", but the source file is index.ts in the same tree.
This pattern is okay if tsc emits .js files beside .d.ts and the runtime resolution happens on compiled output. Otherwise you’ll hit module-not-found in tests or ts-node. Verify the build pipeline supports “import with .js extension from TS”.

packages/thirdweb/scripts/generate/abis/assets/AssetEntrypointERC20.json (1)

7-12:

❌ Incorrect review comment

Struct/event field size mismatch – potential ABI desync

Struct addImplementation.config uses bytes createHookData, but ImplementationAdded event declares bytes32 createHookData.
If the underlying Solidity contract really emits bytes32, calls encoding bytes will still compile but the event parser will truncate data.

Confirm the canonical Solidity source and align both the function parameter and event field to the same type.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for the ImplementationAdded event in Solidity files..."
rg -n "event ImplementationAdded" --glob "*.sol"
echo

echo "Context around event ImplementationAdded:"
rg -A3 -B3 "event ImplementationAdded" --glob "*.sol"
echo

echo "Searching for function addImplementation in Solidity files..."
rg -n "function addImplementation" --glob "*.sol"
echo

echo "Context around function addImplementation:"
rg -A3 -B3 "function addImplementation" --glob "*.sol"
echo

echo "Searching for structs related to ImplementationConfig or config:"
rg -n "struct .*Implementation.*" --glob "*.sol"
echo

echo "Context around those struct definitions:"
rg -A3 -B3 "struct .*Implementation.*" --glob "*.sol"

Length of output: 358


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for the ImplementationAdded event in JSON ABI files..."
rg -n '"name":\s*"ImplementationAdded"' --glob "*.json"

echo
echo "Context around the ImplementationAdded event:"
rg -A5 -B5 '"name":\s*"ImplementationAdded"' --glob "*.json"

echo
echo "Inspecting createHookData field in this event:"
rg -A2 -B2 '"createHookData"' --glob "*.json"

Length of output: 216


No ImplementationAdded event in ABI – review comment can be ignored

The ABI at packages/thirdweb/scripts/generate/abis/assets/AssetEntrypointERC20.json only contains function definitions and does not declare any ImplementationAdded event (nor a bytes32 createHookData field). There is no mismatch to resolve.

Likely an incorrect or invalid review comment.

Copy link
Contributor

github-actions bot commented Jun 11, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 64.06 KB (0%) 1.3 s (0%) 366 ms (+20.29% 🔺) 1.7 s
thirdweb (cjs) 357.05 KB (0%) 7.2 s (0%) 2.2 s (+0.46% 🔺) 9.4 s
thirdweb (minimal + tree-shaking) 5.73 KB (0%) 115 ms (0%) 52 ms (+264.35% 🔺) 166 ms
thirdweb/chains (tree-shaking) 526 B (0%) 11 ms (0%) 82 ms (+1315.05% 🔺) 92 ms
thirdweb/react (minimal + tree-shaking) 19.15 KB (0%) 383 ms (0%) 101 ms (+86.79% 🔺) 484 ms

Copy link

codecov bot commented Jun 11, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.33%. Comparing base (6c711e8) to head (d227bc3).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7321      +/-   ##
==========================================
- Coverage   56.34%   56.33%   -0.02%     
==========================================
  Files         905      907       +2     
  Lines       58834    58965     +131     
  Branches     4150     4159       +9     
==========================================
+ Hits        33151    33216      +65     
- Misses      25577    25643      +66     
  Partials      106      106              
Flag Coverage Δ
packages 56.33% <ø> (-0.02%) ⬇️
Files with missing lines Coverage Δ
packages/thirdweb/src/tokens/constants.ts 100.00% <ø> (ø)
packages/thirdweb/src/tokens/token-utils.ts 38.31% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions bot added the Dashboard Involves changes to the Dashboard. label Aug 15, 2025
@MananTank MananTank removed DO NOT MERGE This pull request is still in progress and is not ready to be merged. Dashboard Involves changes to the Dashboard. labels Aug 15, 2025
@MananTank MananTank changed the title [WIP] ERC20 assets Dashboard, SDK: ERC20 assets Aug 15, 2025
Copy link
Contributor

graphite-app bot commented Aug 15, 2025

Merge activity

<!--

## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes"

If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000):

## Notes for the reviewer

Anything important to call out? Be sure to also clarify these in your comments.

## How to test

Unit tests, playground, etc.

-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR focuses on enhancing the `thirdweb` package by introducing new functionalities and updates for managing ERC20 assets, including the implementation of various events, functions, and utility methods for asset deployment and interaction.

### Detailed summary
- Added `initialize` functions to various asset-related contracts.
- Introduced event handling for `AssetDistributed`, `RouterUpdated`, and `RewardLockerUpdated`.
- Implemented methods for checking router and reward locker support.
- Enhanced the `distributeToken` function to handle multiple recipients.
- Updated ABI files for new function signatures and events.
- Improved utility functions for encoding parameters and transaction preparation.

> The following files were skipped due to too many changes: `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/initialize.ts`, `packages/thirdweb/src/extensions/assets/__generated__/FeeManager/write/initialize.ts`, `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/listAsset.ts`, `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/distributeAsset.ts`, `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAsset.ts`, `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/sellAsset.ts`, `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/buyAsset.ts`, `packages/thirdweb/src/extensions/assets/__generated__/ERC20Asset/write/initialize.ts`, `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/addImplementation.ts`, `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAssetById.ts`, `packages/thirdweb/src/extensions/assets/__generated__/AssetInfraDeployer/write/deployInfraProxyDeterministic.ts`, `packages/thirdweb/package.json`, `packages/thirdweb/src/extensions/assets/__generated__/AssetEntrypointERC20/write/createAssetByImplementationConfig.ts`, `packages/thirdweb/src/assets/bootstrap.ts`

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}`

<!-- end pr-codex -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Added on-page Rewards workflow (Claim Rewards page, UI, and sidebar link) and expanded token creation flows for ERC20 assets with dedicated sale/airdrop options.

* **UI / UX**
  * Improved token sale & distribution UIs, grouped recent transfers, loading placeholders, styling tweaks, chain prioritization in network selectors, customizable wallet fallback icon, and chart legend/value display.

* **API / Props**
  * Several component props and callbacks updated to include chain/contract info and router availability.

* **Chores**
  * Release metadata added for an ERC20 assets patch.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🔭 Outside diff range comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/distribution/token-distribution.tsx (2)

108-116: Guard against invalid/zero supply in distribution calculations

If supply is 0 or NaN, downstream calculations produce NaN/Infinity and can let the Next button be enabled erroneously. Add an early validation error here (complements the schema change).

 function getDistributionError(form: TokenDistributionForm) {
-  const supply = Number(form.watch("supply"));
+  const supply = Number(form.watch("supply"));
+  if (!Number.isFinite(supply) || supply <= 0) {
+    return "Enter a valid total supply";
+  }

143-154: Harden bar chart math against NaN/Infinity and improve numeric coercion

Use SafeNumber consistently and avoid division by zero.

-  const totalSupply = Number(props.distributionFormValues.supply);
-  const totalAirdropSupply =
-    props.distributionFormValues.airdropAddresses.reduce(
-      (acc, curr) => acc + Number(curr.quantity),
-      0,
-    );
-  const airdropPercentage = (totalAirdropSupply / totalSupply) * 100;
+  const totalSupply = SafeNumber(props.distributionFormValues.supply as string);
+  const totalAirdropSupply =
+    props.distributionFormValues.airdropAddresses.reduce(
+      (acc, curr) => acc + SafeNumber(curr.quantity),
+      0,
+    );
+  const airdropPercentage =
+    totalSupply > 0 ? (totalAirdropSupply / totalSupply) * 100 : 0;
   const salePercentage = Number(
     props.distributionFormValues.saleMode === "erc20-asset:pool"
       ? props.distributionFormValues.erc20Asset_poolMode
           .saleAllocationPercentage
       : props.distributionFormValues.dropERC20Mode.saleAllocationPercentage,
   );
 
   const ownerPercentage = Math.max(100 - airdropPercentage - salePercentage, 0);
 
   const tokenAllocations: Segment[] = [
     {
       color: "hsl(var(--chart-1))",
       label: "Owner",
       percent: ownerPercentage,
-      value: `${ownerPercentage}%`,
+      value: `${ownerPercentage}%`,
     },
     {
       color: "hsl(var(--chart-3))",
       label: "Airdrop",
       percent: airdropPercentage,
-      value: `${airdropPercentage}%`,
+      value: `${airdropPercentage}%`,
     },
     {
       color: "hsl(var(--chart-4))",
       label: "Sale",
       percent: salePercentage,
-      value: `${salePercentage}%`,
+      value: `${salePercentage}%`,
     },
   ];

Optionally format to 2 decimals for cleaner display: use toFixed(2) before string interpolation.

Also applies to: 156-176

🧹 Nitpick comments (24)
apps/dashboard/src/@/hooks/project-contracts.ts (6)

14-19: Centralize contractType and make it optional instead of | undefined

Defining the union inline increases drift risk as more asset types are added. Prefer a local type alias (or reuse a shared type barrel if one exists) and mark the field optional.

Apply this diff here:

-      contractType:
-        | "ERC20Asset"
-        | "DropERC721"
-        | "DropERC1155"
-        | "DropERC20"
-        | undefined;
+      contractType?: ContractType;

And add a local type alias near the top of the file (or import from a shared types barrel if available):

type ContractType = "ERC20Asset" | "DropERC721" | "DropERC1155" | "DropERC20";

13-13: Use optional property instead of union-with-undefined for deploymentType

This is more idiomatic and signals the field can be omitted (JSON.stringify will skip it).

-      deploymentType: "asset" | undefined;
+      deploymentType?: "asset";

7-8: Add a mutationKey for React Query devtools clarity and deduping

Not required, but helpful for debugging and potential deduplication.

   return useMutation({
+    mutationKey: ["project-contracts", "add"],
     mutationFn: async (params: {

35-38: Avoid leaking raw server errors to the browser console

This hook runs on the client; logging raw backend errors can expose internals. Log only in dev and throw a user-safe message.

-      if (!res.ok) {
-        console.error(res.error);
-        throw new Error(res.error);
-      }
+      if (!res.ok) {
+        if (process.env.NODE_ENV !== "production") {
+          console.error(res.error);
+        }
+        throw new Error("Failed to add contract to project");
+      }

54-58: Add explicit return type to align with TS guidelines

The dashboard guidelines prefer explicit return types. Add the Promise shape to the signature.

-export async function removeContractFromProject(params: {
+export async function removeContractFromProject(params: {
   teamId: string;
   projectId: string;
   contractId: string;
-}) {
+}): Promise<{ result: { success: boolean } }> {

64-66: Mirror error handling pattern from the add hook and avoid exposing raw messages

For consistency and to avoid surfacing backend messages directly to users, guard logs in dev and throw a generic error.

-  if (!res.ok) {
-    throw new Error(res.error);
-  }
+  if (!res.ok) {
+    if (process.env.NODE_ENV !== "production") {
+      console.error(res.error);
+    }
+    throw new Error("Failed to remove contract from project");
+  }
.changeset/young-carrots-burn.md (1)

5-5: Make the changeset description user-facing and specific.

“ERC20 assets” is too terse for release notes. Briefly summarize the new surface (tokens export), key ABIs, and dashboard capability so downstream consumers understand what’s in this patch.

Apply this diff to improve the description:

-ERC20 assets
+Add ERC20 asset support:
+- expose a new SDK surface at `thirdweb/tokens`
+- add ERC20Entrypoint/RewardLocker/PoolRouter/FeeManager ABIs for generation
+- enable rewards claim flow in the dashboard
+- no breaking API changes
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/rewards/page.tsx (3)

1-1: Mark this as a Server Component.

Per dashboard guidelines, add import "server-only"; to ensure this page isn’t accidentally bundled for the client.

+import "server-only";
 import { notFound, redirect } from "next/navigation";

70-77: Avoid passing client instances across the server/client boundary.

To keep the payload lean and avoid serialization pitfalls, prefer passing primitives (address, chain slug/id) to the client component and creating the client-side contract within it. You’re already doing the server-side variant above for reward discovery; mirror that pattern client-side within ClaimRewardsPage.

Example direction (requires adjusting ClaimRewardsPage props):

-  <ClaimRewardsPage
-    assetContractClient={info.clientContract}
-    entrypointContractClient={getContract({
-      address: entrypointContractAddress,
-      chain: chain,
-      client: info.clientContract.client,
-    })}
-    reward={reward}
-    unclaimedFees={unclaimedFees}
-    chainSlug={info.chainMetadata.slug}
-  />
+  <ClaimRewardsPage
+    assetAddress={info.clientContract.address}
+    entrypointAddress={entrypointContractAddress}
+    chainSlug={info.chainMetadata.slug}
+    reward={reward}
+    unclaimedFees={unclaimedFees}
+  />

Then, inside ClaimRewardsPage (client), construct the contract via getContract using the client-side SDK.


35-39: Minor: handle missing entrypoint defensively.

If getDeployedEntrypointERC20 fails or returns no address on unsupported chains, consider short-circuiting to notFound() early to avoid downstream errors.

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page.stories.tsx (1)

30-54: DRY the async delay with a small sleep helper

You repeat the 1s delay snippet several times. Centralize to a tiny helper for readability.

Apply this diff within the selected ranges to use a helper:

-    airdropTokens: async () => {
-      await new Promise((resolve) => setTimeout(resolve, 1000));
-    },
+    airdropTokens: async () => {
+      await sleep(1000);
+    },
-    approveAirdropTokens: async () => {
-      await new Promise((resolve) => setTimeout(resolve, 1000));
-    },
+    approveAirdropTokens: async () => {
+      await sleep(1000);
+    },
-    deployContract: async () => {
-      await new Promise((resolve) => setTimeout(resolve, 1000));
+    deployContract: async () => {
+      await sleep(1000);
       return { contractAddress: "0x123" };
     },
-    airdropTokens: async () => {
-      await new Promise((resolve) => setTimeout(resolve, 1000));
-    },
+    airdropTokens: async () => {
+      await sleep(1000);
+    },
-    deployContract: async () => {
-      await new Promise((resolve) => setTimeout(resolve, 1000));
+    deployContract: async () => {
+      await sleep(1000);
       return { contractAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" };
     },
-    setClaimConditions: async () => {
-      await new Promise((resolve) => setTimeout(resolve, 1000));
-    },
+    setClaimConditions: async () => {
+      await sleep(1000);
+    },
-    mintTokens: async () => {
-      await new Promise((resolve) => setTimeout(resolve, 1000));
-    },
+    mintTokens: async () => {
+      await sleep(1000);
+    },
-        deployContract: async () => {
-          await new Promise((resolve) => setTimeout(resolve, 1000));
+        deployContract: async () => {
+          await sleep(1000);
           throw new Error("Failed to deploy contract");
         },
-        deployContract: async () => {
-          await new Promise((resolve) => setTimeout(resolve, 1000));
+        deployContract: async () => {
+          await sleep(1000);
           throw new Error(
             "You have reached your storage limit. Please add a valid payment method to continue using the service.",
           );
         },

Additionally add this helper near the top (outside the selected ranges):

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

Also applies to: 42-54, 78-81, 99-104

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/contract/[chainIdOrSlug]/[contractAddress]/rewards/components/claim-rewards-page.tsx (1)

209-214: Replace inline styles with Tailwind arbitrary values to match dashboard guidelines

Inline style is discouraged. Use Tailwind’s arbitrary value syntax with your existing CSS vars.

Apply this diff:

-                fallbackIcon={
-                  <div
-                    className="size-3 rounded-full"
-                    style={{
-                      backgroundColor: recipientColor,
-                    }}
-                  />
-                }
+                fallbackIcon={
+                  <div className="size-3 rounded-full bg-[hsl(var(--chart-1))]" />
+                }
-                fallbackIcon={
-                  <div
-                    className="size-3 rounded-full"
-                    style={{
-                      backgroundColor: developerColor,
-                    }}
-                  />
-                }
+                fallbackIcon={
+                  <div className="size-3 rounded-full bg-[hsl(var(--chart-2))]" />
+                }

Also applies to: 227-233

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/_common/form.ts (3)

25-26: Grammar nit in validation message

Minor copy edit for user-facing error.

-    message: "Amount must be number larger than or equal to 0",
+    message: "Amount must be a number greater than or equal to 0",

41-50: Avoid double Number() conversion; reuse parsed value

You already computed numValue. Reuse it for getInitialTickValue for clarity.

-      const tick = getInitialTickValue({
-        startingPricePerToken: Number(value),
-      });
+      const tick = getInitialTickValue({
+        startingPricePerToken: numValue,
+      });

31-36: Optional: validate airdrop quantities are numeric and non-negative

Right now quantity is any string; you later coerce with SafeNumber. A light schema guard improves UX.

   airdropAddresses: z.array(
     z.object({
       address: addressSchema,
-      quantity: z.string(),
+      quantity: z
+        .string()
+        .refine((v) => {
+          const n = Number(v);
+          return Number.isFinite(n) && n >= 0;
+        }, "Quantity must be a non-negative number"),
     }),
   ),
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/distribution/token-sale.tsx (2)

205-222: Constrain percentage input to non-negative values

You cap max at 100; also cap min at 0 to guard accidental negatives.

-            <DecimalInput
-              maxValue={100}
+            <DecimalInput
+              minValue={0}
+              maxValue={100}

149-157: Unused client prop inside PoolConfig

PoolConfig receives and forwards client but does not use it. Consider removing from the component signature and call to reduce surface area.

-function PoolConfig(props: {
-  form: TokenDistributionForm;
-  chainId: string;
-  client: ThirdwebClient;
-}) {
+function PoolConfig(props: {
+  form: TokenDistributionForm;
+  chainId: string;
+}) {
-            <PoolConfig
-              chainId={props.chainId}
-              client={props.client}
-              form={props.form}
-            />
+            <PoolConfig chainId={props.chainId} form={props.form} />

Also applies to: 94-98

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/launch/launch-token.tsx (2)

95-156: Step orchestration is correct; small async hygiene improvement

Both branches build steps deterministically and gate optional steps correctly (approve-airdrop before airdrop). To avoid unhandled rejections from the async runner, prefix the calls with void (and see the companion comment about not rethrowing inside executeSteps).

-      executeSteps(initialSteps, 0, isGasless);
+      void executeSteps(initialSteps, 0, isGasless);
@@
-      executeSteps(initialSteps, 0, isGasless);
+      void executeSteps(initialSteps, 0, isGasless);

217-233: Don’t rethrow inside executeSteps to prevent unhandled promise rejections

The error state is already surfaced via updateStatus and UI retry. Rethrowing here causes an unhandled rejection since callers don’t await/catch. Remove the throw.

       updateStatus(i, {
         message: errorMessage,
         type: "error",
       });

-      throw error;
+      // Do not rethrow; error is reflected in step status and UI handles retries.
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page-impl.tsx (5)

146-153: Avoid unhandled rejections on background addContractToProject

mutateAsync returns a Promise; if you don't await it, swallow/capture errors to prevent unhandled rejections.

-    addContractToProject.mutateAsync({
+    void addContractToProject
+      .mutateAsync({
         chainId: params.values.chain,
         contractAddress: contractAddress,
         contractType: "ERC20Asset",
         deploymentType: "asset",
         projectId: props.projectId,
         teamId: props.teamId,
-    });
+      })
+      .catch((err) => console.warn("addContractToProject failed", err));

272-279: Do the same for DropERC20 addContractToProject

Mirror the background error handling here as well.

-    addContractToProject.mutateAsync({
+    void addContractToProject
+      .mutateAsync({
         chainId: values.chain,
         contractAddress: contractAddress,
         contractType: "DropERC20",
         deploymentType: "asset",
         projectId: props.projectId,
         teamId: props.teamId,
-    });
+      })
+      .catch((err) => console.warn("addContractToProject failed", err));

443-449: Make bridge trigger fire-and-forget safely

createTokenOnUniversalBridge returns a Promise. If you’re intentionally not awaiting, prefix with void and catch to avoid unhandled rejections.

-        createTokenOnUniversalBridge({
+        void createTokenOnUniversalBridge({
           chainId: params.chainId,
           client: props.client,
           tokenAddress: params.contractAddress,
-        });
+        }).catch((err) =>
+          console.warn("createTokenOnUniversalBridge failed", err),
+        );

90-94: Name consistency: Erc20Asset_deployContract → ERC20Asset_deployContract

Nit but helps keep internal helpers consistent with the group name.

-  async function Erc20Asset_deployContract(params: {
+  async function ERC20Asset_deployContract(params: {
@@
-          deployContract: Erc20Asset_deployContract,
+          deployContract: ERC20Asset_deployContract,

Also applies to: 431-435


90-168: Consider adding explicit return types to exported helpers

For readability and to match the repo guidelines, annotate returns:

  • ERC20Asset_deployContract: Promise<{ contractAddress: string }>
  • ERC20Asset_airdropTokens: Promise
  • ERC20Asset_approveAirdropTokens: Promise
  • DropERC20_*: Promise except deployContract which returns Promise<{ contractAddress: string }>

Also applies to: 170-226, 248-292, 294-424

Comment on lines 82 to 83
supply: z.string().min(1, "Supply is required"),
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Validate supply is a positive numeric value to prevent NaN/zero edge cases downstream

Many consumers cast supply with Number(...) and do math, which can yield NaN/Infinity and bypass errors. Enforce numeric > 0 at the schema level.

-  supply: z.string().min(1, "Supply is required"),
+  supply: z
+    .string()
+    .min(1, "Supply is required")
+    .refine((v) => {
+      const n = Number(v);
+      return Number.isFinite(n) && n > 0;
+    }, "Supply must be a positive number"),
📝 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
supply: z.string().min(1, "Supply is required"),
});
supply: z
.string()
.min(1, "Supply is required")
.refine((v) => {
const n = Number(v);
return Number.isFinite(n) && n > 0;
}, "Supply must be a positive number"),
});
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/_common/form.ts
around lines 82-83, the schema currently accepts supply as a non-empty string
which allows consumers to cast to Number(...) and get NaN/0/Infinity later;
change the schema to coerce and validate a positive number: use z.preprocess to
convert incoming strings to Number and then validate with z.number().positive()
(with a clear error message like "Supply must be a number greater than 0") so
downstream math never receives NaN/zero.

Comment on lines +155 to +162
reportContractDeployed({
address: contractAddress,
chainId: Number(params.values.chain),
contractName: "DropERC20",
deploymentType: "asset",
publisher: account.address,
});

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix analytics: wrong contractName for ERC20Asset deployment

You're reporting ERC20Asset deployments as "DropERC20". This skews analytics and downstream reporting.

   reportContractDeployed({
     address: contractAddress,
     chainId: Number(params.values.chain),
-    contractName: "DropERC20",
+    contractName: "ERC20Asset",
     deploymentType: "asset",
     publisher: account.address,
   });
📝 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
reportContractDeployed({
address: contractAddress,
chainId: Number(params.values.chain),
contractName: "DropERC20",
deploymentType: "asset",
publisher: account.address,
});
reportContractDeployed({
address: contractAddress,
chainId: Number(params.values.chain),
contractName: "ERC20Asset",
deploymentType: "asset",
publisher: account.address,
});
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page-impl.tsx
around lines 155–162, the analytics event is reporting ERC20Asset deployments
with contractName "DropERC20"; change the contractName string to "ERC20Asset" so
the reportContractDeployed call uses contractName: "ERC20Asset" (keep chainId,
deploymentType and publisher unchanged) to correct analytics; run and verify any
affected analytics dashboards/tests after the change.

Comment on lines +170 to +192
async function ERC20Asset_airdropTokens(params: {
values: CreateAssetFormValues;
gasless: boolean;
}) {
const { values, gasless } = params;
const account = getAccount(gasless);
const contract = getDeployedContract({ chain: values.chain });

const airdropTx = await distributeToken({
chain: contract.chain,
client: props.client,
contents: values.airdropAddresses.map((recipient) => ({
amount: BigInt(recipient.quantity),
recipient: recipient.address,
})),
tokenAddress: contract.address,
});

await sendAndConfirmTransaction({
account,
transaction: airdropTx,
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm distributeToken expects base units vs human-readable amounts

Here, amount is built via BigInt(recipient.quantity). If the form captures human-readable quantities (e.g., "1.5"), this will throw or mis-scale. The tokens API may accept human-readable amounts as strings and convert internally (similar to transfer/transferBatch).

Run this to inspect local usage patterns and the function signature:

If distributeToken accepts human-readable amounts, prefer:

  • amount: recipient.quantity (string), letting the SDK handle decimals; or
  • if base units are required, fetch decimals and use toUnits(amount, decimals).

🏁 Script executed:

#!/bin/bash
# Find distributeToken usage and typical parameter shapes
rg -nP -C3 '\bdistributeToken\s*\('

# Also inspect approve usage to confirm preferred 'amount' vs 'amountWei'
rg -nP -C3 '\bapprove\s*\(\s*\{' | sed -n '1,200p'

Length of output: 18675


🏁 Script executed:

#!/bin/bash
# Display the beginning of distribute-token.ts to inspect types and parameter expectations
sed -n '1,200p' packages/thirdweb/src/tokens/distribute-token.ts

Length of output: 944


🏁 Script executed:

#!/bin/bash
# Locate DistributeContent type definition
rg -nP "export type DistributeContent" -n packages/thirdweb/src/tokens/types.ts

# Print the definition for context
sed -n '1,200p' packages/thirdweb/src/tokens/types.ts

Length of output: 1335


Align amount with distributeToken’s human-readable inputs

The SDK’s distributeToken implementation calls

toUnits(a.amount.toString(), 18)

internally, so you must pass the human-readable token amount (string or number)—not a base-unit BigInt. Wrapping recipient.quantity in BigInt:

  • Forbids fractional quantities (BigInt("1.5") throws)
  • If quantity were already in base units, it would be scaled again

Recommended fixes in ERC20Asset_airdropTokens (around lines 178–184):

• If your form’s quantity is an integer or decimal string, drop the BigInt and pass it directly:

- contents: values.airdropAddresses.map((recipient) => ({
-   amount: BigInt(recipient.quantity),
+ contents: values.airdropAddresses.map((recipient) => ({
+   amount: recipient.quantity,         // human-readable number or numeric string
    recipient: recipient.address,
})),

• If you need to support decimals but keep a bigint type locally, convert at the call site and cast:

import { toUnits } from "thirdweb/utils/units";

const decimals = 18;  // or fetch from the token metadata
contents: values.airdropAddresses.map((r) => ({
  amount: BigInt(toUnits(r.quantity.toString(), decimals)),
  recipient: r.address,
}));

• Verify your CreateAssetFormValues.airdropAddresses[].quantity type (string vs. number) and adjust accordingly.

Comment on lines +216 to +221
const approvalTx = approve({
amountWei: toWei(totalAmountToAirdrop.toString()),
contract: contract,
spender: entrypoint.address,
});

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Approve with amount, not amountWei, to avoid decimals mismatch

Using toWei assumes 18 decimals; ERC20 decimals can vary. The ERC20 approve helper supports human-readable amounts and performs conversion internally.

-    const approvalTx = approve({
-      amountWei: toWei(totalAmountToAirdrop.toString()),
-      contract: contract,
-      spender: entrypoint.address,
-    });
+    const approvalTx = approve({
+      amount: totalAmountToAirdrop.toString(),
+      contract,
+      spender: entrypoint.address,
+    });

If this is the only toWei usage, also remove the unused import:

-  toWei,
📝 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 approvalTx = approve({
amountWei: toWei(totalAmountToAirdrop.toString()),
contract: contract,
spender: entrypoint.address,
});
const approvalTx = approve({
amount: totalAmountToAirdrop.toString(),
contract,
spender: entrypoint.address,
});
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page-impl.tsx
around lines 216–221, the approve call incorrectly passes an amountWei (using
toWei) which assumes 18 decimals; change the call to pass a human-readable
amount instead (e.g., amount: totalAmountToAirdrop.toString()) so the ERC20
helper can convert using the token's actual decimals, and if toWei is now unused
remove its import to avoid dead code.

Comment on lines +176 to +187
<SelectItem value="bonding-curve">Bonding Curve</SelectItem>
<SelectItem value="fixed-price" disabled>
<span className="flex items-center gap-2">
Dynamic Bonding Curve{" "}
<Badge variant="secondary">Coming Soon</Badge>
</span>
</SelectItem>
<SelectItem value="dutch-auction" disabled>
<span className="flex items-center gap-2">
Fixed Price <Badge variant="secondary">Coming Soon</Badge>
</span>
</SelectItem>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix mislabeled pricing strategy options

“Dynamic Bonding Curve” is shown under value="fixed-price" and “Fixed Price” under value="dutch-auction". This is confusing for users.

-            <SelectItem value="fixed-price" disabled>
+            <SelectItem value="dynamic-bonding-curve" disabled>
               <span className="flex items-center gap-2">
-                Dynamic Bonding Curve{" "}
+                Dynamic Bonding Curve{" "}
                 <Badge variant="secondary">Coming Soon</Badge>
               </span>
             </SelectItem>
-            <SelectItem value="dutch-auction" disabled>
+            <SelectItem value="fixed-price" disabled>
               <span className="flex items-center gap-2">
-                Fixed Price <Badge variant="secondary">Coming Soon</Badge>
+                Fixed Price <Badge variant="secondary">Coming Soon</Badge>
               </span>
             </SelectItem>

If “dutch-auction” is planned, add it as a separate disabled option with the correct label.

📝 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
<SelectItem value="bonding-curve">Bonding Curve</SelectItem>
<SelectItem value="fixed-price" disabled>
<span className="flex items-center gap-2">
Dynamic Bonding Curve{" "}
<Badge variant="secondary">Coming Soon</Badge>
</span>
</SelectItem>
<SelectItem value="dutch-auction" disabled>
<span className="flex items-center gap-2">
Fixed Price <Badge variant="secondary">Coming Soon</Badge>
</span>
</SelectItem>
<SelectItem value="bonding-curve">Bonding Curve</SelectItem>
<SelectItem value="dynamic-bonding-curve" disabled>
<span className="flex items-center gap-2">
Dynamic Bonding Curve{" "}
<Badge variant="secondary">Coming Soon</Badge>
</span>
</SelectItem>
<SelectItem value="fixed-price" disabled>
<span className="flex items-center gap-2">
Fixed Price <Badge variant="secondary">Coming Soon</Badge>
</span>
</SelectItem>
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/distribution/token-sale.tsx
around lines 176 to 187, the SelectItem labels are swapped: the option with
value="fixed-price" is labeled "Dynamic Bonding Curve" and value="dutch-auction"
is labeled "Fixed Price". Fix by renaming the labels to match their values
(value="fixed-price" should show "Fixed Price" and value="dutch-auction" should
show "Dutch Auction" or similar), and if dutch-auction is planned but not
available, add a separate disabled SelectItem for it with the correct label and
an appropriate "Coming Soon" Badge so each value matches its displayed label.

Comment on lines +59 to +62
onLaunchSuccess: (params: {
chainId: number;
contractAddress: string;
}) => void;
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Prop signature change for onLaunchSuccess: verify all call sites are updated

The new signature is (params: { chainId: number; contractAddress: string }) => void. Ensure no downstream usages still expect the zero-arg callback.

Use this script to find and inspect call sites:


🏁 Script executed:

#!/bin/bash
# Find usages of onLaunchSuccess prop across the repo (and check param shape)
rg -nP -C3 '\bonLaunchSuccess\s*=\s*\(' --type ts --type tsx

Length of output: 90


To cover both .ts and .tsx files without relying on ripgrep’s file‐type detection, let’s broaden the search:


🏁 Script executed:

#!/bin/bash
# Find all usages of onLaunchSuccess across TS and TSX files
rg -nP -C3 'onLaunchSuccess' -g '*.ts' -g '*.tsx'

Length of output: 13512


Update Storybook stories to match the new onLaunchSuccess signature

All internal usages of onLaunchSuccess in the token launcher have been updated to accept a { chainId, contractAddress } parameter, but the Storybook examples still use the old zero-arg callback. Please update them to take (and ignore) the new params:

• apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page.stories.tsx
– Lines 63, 84, 107: change
onLaunchSuccess: () => {}
to
onLaunchSuccess: (_params) => {}

🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/tokens/create/token/create-token-page.stories.tsx
around lines 63, 84, and 107, the Storybook stories still use the old zero-arg
onLaunchSuccess signature; update each onLaunchSuccess: () => {} to accept the
new parameter shape by changing them to onLaunchSuccess: (_params) => {} so the
stories match the component's new signature and ignore the passed { chainId,
contractAddress } argument.

Comment on lines +27 to +37
token0: {
address: string;
amount: bigint;
symbol: string;
};
token1: {
address: string;
amount: bigint;
symbol: string;
};
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Token amount formatting assumes 18 decimals — add decimals to avoid misreporting values

Using toTokens(amount, 18) will display incorrect amounts for non-18-decimal tokens (e.g., USDC 6). This is a correctness issue in a financial UI.

Apply these diffs to thread decimals through and format correctly:

 export function ClaimRewardsPage(props: {
   assetContractClient: ThirdwebContract;
   entrypointContractClient: ThirdwebContract;
   reward: NonNullable<Awaited<ReturnType<typeof getValidReward>>>;
   unclaimedFees: {
     token0: {
       address: string;
       amount: bigint;
       symbol: string;
+      decimals: number;
     };
     token1: {
       address: string;
       amount: bigint;
       symbol: string;
+      decimals: number;
     };
   };
   chainSlug: string;
 }) {
 export function ClaimRewardsPageUI(props: {
   unclaimedFees: {
     token0: {
       address: string;
       amount: bigint;
       symbol: string;
+      decimals: number;
     };
     token1: {
       address: string;
       amount: bigint;
       symbol: string;
+      decimals: number;
     };
   };
 function TokenReward(props: {
   token: {
     address: string;
     amount: bigint;
     symbol: string;
+    decimals: number;
   };
   client: ThirdwebClient;
   chain: Chain;
   chainSlug: string;
 }) {
-        <p className="font-bold text-sm">
-          {toTokens(props.token.amount, 18)} {props.token.symbol}
-        </p>
+        <p className="font-bold text-sm">
+          {toTokens(props.token.amount, props.token.decimals)} {props.token.symbol}
+        </p>

Note: You’ll need to pass decimals from the data source (e.g., alongside address/symbol when constructing unclaimedFees).

Also applies to: 94-106, 272-281, 304-306

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dashboard Involves changes to the Dashboard. packages SDK Involves changes to the thirdweb SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants