feat: add contractAddresses override and restyle custom tokens UI#610
Conversation
…rowser testing Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…loyments Allow overriding default contract addresses (e.g., B3TR, VOT3) via a new contractAddresses prop on the provider. This enables apps deploying on solo or testnet with different contract instances to pass their addresses without modifying the kit's built-in config. Also adds useAppConfig() hook and appConfig context field for accessing the merged configuration. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Replace the full-width footer button with a compact icon button next to the search input for easier reachability. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
- Add helper text explaining what to do - Move add button inline with input as a compact icon button - Show empty state when no custom tokens exist - Stack token symbol and address vertically for clearer hierarchy - Remove separate footer, keeping all actions near the input - Bump version to 2.7.0 Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThis PR refactors configuration management in vechain-kit by introducing a centralized Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/vechain-kit/src/components/AccountModal/Contents/Swap/SwapTokenContent.tsx (1)
174-201:⚠️ Potential issue | 🟡 MinorStale-closure risk and dead try/catch around
appConfig.b3trContractAddress.Two related issues here and at the symmetric block in
handleSelectFromToken(lines 551–575):
- The dependency arrays include
network.typebut the actual value being read is nowappConfig.b3trContractAddress. If a consumer changescontractAddresses(or any override) without changingnetwork.type, this effect/callback won't re-run with the new B3TR address.- The
try/catchmade sense when this calledgetConfig(network.type)(which throws on unsupported network), but a plain property read onappConfigcannot throw — it's now dead code.♻️ Proposed cleanup (apply to both occurrences)
// If not found by symbol, try finding by address from config if (!b3trToken) { - try { - const b3trAddress = appConfig.b3trContractAddress; - if (b3trAddress) { - b3trToken = sortedTokens.find((t) => - compareAddresses(t.address, b3trAddress), - ); - } - } catch (error) { - console.warn( - 'Failed to get B3TR address from config:', - error, - ); - } + const b3trAddress = appConfig.b3trContractAddress; + if (b3trAddress) { + b3trToken = sortedTokens.find((t) => + compareAddresses(t.address, b3trAddress), + ); + } }And update the deps:
- }, [ - sortedTokens, - fromToken, - toToken, - fromTokenAddress, - toTokenAddress, - network.type, - ]); + }, [ + sortedTokens, + fromToken, + toToken, + fromTokenAddress, + toTokenAddress, + appConfig.b3trContractAddress, + ]);- [toToken, sortedTokens, network.type], + [toToken, sortedTokens, appConfig.b3trContractAddress],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vechain-kit/src/components/AccountModal/Contents/Swap/SwapTokenContent.tsx` around lines 174 - 201, The effect reads appConfig.b3trContractAddress but the dependency array only includes network.type, and the try/catch around reading appConfig.b3trContractAddress is dead code; update the effect (and the symmetric block in handleSelectFromToken) to remove the unnecessary try/catch and instead include the B3TR address source in the dependency array (e.g. appConfig.b3trContractAddress or the broader appConfig/contractAddresses object) so the effect/callback re-runs when the address changes, and keep the existing logic that finds b3trToken in sortedTokens and calls setToToken when found.
🧹 Nitpick comments (5)
packages/vechain-kit/src/hooks/signing/useSignTypedData.ts (2)
36-100: Consider addingsignerto the dependency array.The
signTypedDatacallback usessigner(line 55) but doesn't include it in the dependency array (line 99). While this is pre-existing code, addingsignerto the dependencies would align with React's exhaustive-deps best practice.♻️ Suggested fix
}, - [connection, privyWalletProvider], + [connection, privyWalletProvider, signer], );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vechain-kit/src/hooks/signing/useSignTypedData.ts` around lines 36 - 100, The useSignTypedData hook's signTypedData callback references the signer variable but the useCallback dependency array only lists connection and privyWalletProvider; update the dependency array for the useCallback that defines signTypedData to include signer (in addition to connection and privyWalletProvider) so the callback is recreated when signer changes, ensuring correct behavior and aligning with React's exhaustive-deps rule.
3-3: Track the TODO in an issue.The TODO suggests an investigation is needed for VeWorld in-app browser compatibility with
signTypedData. Consider creating a tracking issue to ensure this investigation isn't forgotten.Would you like me to help draft an issue description for tracking this investigation?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vechain-kit/src/hooks/signing/useSignTypedData.ts` at line 3, The TODO comment in useSignTypedData.ts ("investigate signTypedData support in VeWorld in-app browser") needs a tracked issue; create a ticket in your issue tracker referencing the file and symbol useSignTypedData (and the TODO text) that outlines investigation steps: reproduce signTypedData behavior in VeWorld in-app browser, test different EIP-712 payloads, document which APIs are supported or failing, capture console/errors and environment (browser version, wallet), and propose fallback or polyfill approaches; link the issue ID back in the TODO comment for traceability.packages/vechain-kit/src/providers/VeChainKitProvider.tsx (1)
149-165: Consider narrowingcontractAddressesto actual address fields.
Partial<AppConfig>allows overriding non-address fields likenodeUrl,explorerUrl,b3trIndexerUrl,network,ipfsFetchingService, etc. The prop name implies addresses only, and overriding e.g.nodeUrlhere would diverge fromnetwork.nodeUrl(which is independently resolved at line 417 and is whatDAppKitProvider/PrivyWalletProvideractually use), producing silent inconsistencies betweenappConfig.nodeUrland the active node URL.Consider tightening the type to a contract-address-only subset:
♻️ Proposed refactor
+type ContractAddressKeys = + | 'vthoContractAddress' + | 'b3trContractAddress' + | 'vot3ContractAddress' + | 'b3trGovernorAddress' + | 'timelockContractAddress' + | 'xAllocationPoolContractAddress' + | 'xAllocationVotingContractAddress' + | 'emissionsContractAddress' + | 'voterRewardsContractAddress' + | 'galaxyMemberContractAddress' + | 'treasuryContractAddress' + | 'x2EarnAppsContractAddress' + | 'x2EarnCreatorContractAddress' + | 'x2EarnRewardsPoolContractAddress' + | 'nodeManagementContractAddress' + | 'veBetterPassportContractAddress' + | 'veDelegate' + | 'veDelegateVotes' + | 'veDelegateTokenContractAddress' + | 'oracleContractAddress' + | 'accountFactoryAddress' + | 'cleanifyCampaignsContractAddress' + | 'cleanifyChallengesContractAddress' + | 'veWorldSubdomainClaimerContractAddress' + | 'vetDomainsContractAddress' + | 'vetDomainsPublicResolverAddress' + | 'vetDomainsReverseRegistrarAddress' + | 'vnsResolverAddress' + | 'sassContractAddress'; + - contractAddresses?: Partial<AppConfig>; + contractAddresses?: Partial<Pick<AppConfig, ContractAddressKeys>>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vechain-kit/src/providers/VeChainKitProvider.tsx` around lines 149 - 165, The contractAddresses prop on VeChainKitProvider is typed as Partial<AppConfig>, which lets callers override non-address fields (e.g., nodeUrl, explorerUrl, network) and can silently diverge from the resolved network.nodeUrl used by DAppKitProvider/PrivyWalletProvider; change contractAddresses to a narrower type that only includes the contract address fields (e.g., b3trContractAddress, vot3ContractAddress, and any other on-chain contract address keys in AppConfig) instead of Partial<AppConfig>, update the VeChainKitProvider prop type and any related usages to that new address-only subset type, and ensure consumers and internal merges only accept/merge those address fields so nodeUrl/explorerUrl/network remain resolved from network.packages/vechain-kit/src/components/AccountModal/Contents/Assets/ManageCustomTokenContent.tsx (2)
156-164: Use Chakra'sFormErrorMessageinstead of a hard‑coded redText.Hard‑coding
color="#ef4444"bypasses the theme/FormControlinvalid state and won't respond to dark‑mode token overrides used elsewhere in this file. Since you already wrap the input inFormControl isInvalid={...}, Chakra v2 shipsFormErrorMessagewhich is the idiomatic counterpart and will be wired to the input via aria-describedby automatically.♻️ Suggested change
- {errors.newTokenAddress && ( - <Text - color="#ef4444" - fontSize="sm" - mt={1} - > - {errors.newTokenAddress.message} - </Text> - )} + <FormErrorMessage> + {errors.newTokenAddress?.message} + </FormErrorMessage>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vechain-kit/src/components/AccountModal/Contents/Assets/ManageCustomTokenContent.tsx` around lines 156 - 164, Replace the hard-coded red Text used for validation feedback with Chakra's FormErrorMessage so the message follows FormControl's isInvalid state and theme (e.g., dark mode). In ManageCustomTokenContent (the component rendering the input with FormControl isInvalid={...}), swap the conditional that renders errors.newTokenAddress Text to render <FormErrorMessage>{errors.newTokenAddress.message}</FormErrorMessage> and remove the inline color/fontSize styling; also add an import for FormErrorMessage from `@chakra-ui/react` so the error message is correctly wired to the input's aria-describedby.
112-142: Trim logic overridesregister'sonChangeand is non‑idiomatic.Spreading
{...register(...)}and then declaringonChangereplaces RHF's own change handler; you compensate with a manualsetValue(..., { shouldValidate: true })plus a direct mutation ofe.target.value. This works but is fragile (DOM mutation on a controlled-by-RHF input) and easy to break in future edits. PrefersetValueAs(oronChangevia the register options) so RHF still owns the change pipeline:♻️ Cleaner alternative
<Input {...register('newTokenAddress', { required: t('Address is required'), + setValueAs: (v: string) => + typeof v === 'string' + ? v.trim() + : v, pattern: { value: /^0x[a-fA-F0-9]{40}$/, message: t( 'Please enter a valid contract address', ), }, validate: (value) => /^0x[a-fA-F0-9]{40}$/.test( value, ) || t('Invalid contract address'), })} - onChange={(e) => { - const trimmed = - e.target.value.trim(); - e.target.value = trimmed; - setValue( - 'newTokenAddress', - trimmed, - { shouldValidate: true }, - ); - }} placeholder="0x..."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/vechain-kit/src/components/AccountModal/Contents/Assets/ManageCustomTokenContent.tsx` around lines 112 - 142, The current onChange prop on the Input in ManageCustomTokenContent overrides the React Hook Form handler from register('newTokenAddress') and mutates e.target.value; remove the external onChange and DOM mutation and instead perform trimming inside RHF by using register's options (e.g., provide setValueAs or an onChange sanitizer in the register call for 'newTokenAddress') so RHF owns the change pipeline and validation (keep the pattern/validate messages and ensure shouldValidate behavior via RHF config).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@packages/vechain-kit/src/components/AccountModal/Contents/Assets/ManageCustomTokenContent.tsx`:
- Around line 110-155: The address input isn't inside a native form so Enter
doesn't submit; wrap the FormControl/HStack block in a form element (e.g., Box
as="form") with onSubmit={handleSubmit(onSubmit)} to enable keyboard submission,
ensure you keep using register('newTokenAddress') and setValue for input
handling, and change the IconButton to type="submit" (removing or keeping
onClick as redundant) so pressing Enter or clicking the button both trigger
handleSubmit(onSubmit).
---
Outside diff comments:
In
`@packages/vechain-kit/src/components/AccountModal/Contents/Swap/SwapTokenContent.tsx`:
- Around line 174-201: The effect reads appConfig.b3trContractAddress but the
dependency array only includes network.type, and the try/catch around reading
appConfig.b3trContractAddress is dead code; update the effect (and the symmetric
block in handleSelectFromToken) to remove the unnecessary try/catch and instead
include the B3TR address source in the dependency array (e.g.
appConfig.b3trContractAddress or the broader appConfig/contractAddresses object)
so the effect/callback re-runs when the address changes, and keep the existing
logic that finds b3trToken in sortedTokens and calls setToToken when found.
---
Nitpick comments:
In
`@packages/vechain-kit/src/components/AccountModal/Contents/Assets/ManageCustomTokenContent.tsx`:
- Around line 156-164: Replace the hard-coded red Text used for validation
feedback with Chakra's FormErrorMessage so the message follows FormControl's
isInvalid state and theme (e.g., dark mode). In ManageCustomTokenContent (the
component rendering the input with FormControl isInvalid={...}), swap the
conditional that renders errors.newTokenAddress Text to render
<FormErrorMessage>{errors.newTokenAddress.message}</FormErrorMessage> and remove
the inline color/fontSize styling; also add an import for FormErrorMessage from
`@chakra-ui/react` so the error message is correctly wired to the input's
aria-describedby.
- Around line 112-142: The current onChange prop on the Input in
ManageCustomTokenContent overrides the React Hook Form handler from
register('newTokenAddress') and mutates e.target.value; remove the external
onChange and DOM mutation and instead perform trimming inside RHF by using
register's options (e.g., provide setValueAs or an onChange sanitizer in the
register call for 'newTokenAddress') so RHF owns the change pipeline and
validation (keep the pattern/validate messages and ensure shouldValidate
behavior via RHF config).
In `@packages/vechain-kit/src/hooks/signing/useSignTypedData.ts`:
- Around line 36-100: The useSignTypedData hook's signTypedData callback
references the signer variable but the useCallback dependency array only lists
connection and privyWalletProvider; update the dependency array for the
useCallback that defines signTypedData to include signer (in addition to
connection and privyWalletProvider) so the callback is recreated when signer
changes, ensuring correct behavior and aligning with React's exhaustive-deps
rule.
- Line 3: The TODO comment in useSignTypedData.ts ("investigate signTypedData
support in VeWorld in-app browser") needs a tracked issue; create a ticket in
your issue tracker referencing the file and symbol useSignTypedData (and the
TODO text) that outlines investigation steps: reproduce signTypedData behavior
in VeWorld in-app browser, test different EIP-712 payloads, document which APIs
are supported or failing, capture console/errors and environment (browser
version, wallet), and propose fallback or polyfill approaches; link the issue ID
back in the TODO comment for traceability.
In `@packages/vechain-kit/src/providers/VeChainKitProvider.tsx`:
- Around line 149-165: The contractAddresses prop on VeChainKitProvider is typed
as Partial<AppConfig>, which lets callers override non-address fields (e.g.,
nodeUrl, explorerUrl, network) and can silently diverge from the resolved
network.nodeUrl used by DAppKitProvider/PrivyWalletProvider; change
contractAddresses to a narrower type that only includes the contract address
fields (e.g., b3trContractAddress, vot3ContractAddress, and any other on-chain
contract address keys in AppConfig) instead of Partial<AppConfig>, update the
VeChainKitProvider prop type and any related usages to that new address-only
subset type, and ensure consumers and internal merges only accept/merge those
address fields so nodeUrl/explorerUrl/network remain resolved from network.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0691991a-7623-4ed9-a540-22c69c66df07
📒 Files selected for processing (14)
examples/homepage/src/app/pages/Home.tsxexamples/next-template/next.config.jspackages/vechain-kit/package.jsonpackages/vechain-kit/src/components/AccountModal/Contents/Assets/AssetsContent.tsxpackages/vechain-kit/src/components/AccountModal/Contents/Assets/ManageCustomTokenContent.tsxpackages/vechain-kit/src/components/AccountModal/Contents/Swap/SwapTokenContent.tsxpackages/vechain-kit/src/hooks/api/wallet/useCustomTokens.tspackages/vechain-kit/src/hooks/api/wallet/useGetB3trBalance.tspackages/vechain-kit/src/hooks/api/wallet/useGetVot3Balance.tspackages/vechain-kit/src/hooks/api/wallet/useTokenBalances.tspackages/vechain-kit/src/hooks/api/wallet/useTokenPrices.tspackages/vechain-kit/src/hooks/signing/useSignTypedData.tspackages/vechain-kit/src/languages/en.jsonpackages/vechain-kit/src/providers/VeChainKitProvider.tsx
|
Size Change: +12.7 kB (+0.17%) Total Size: 7.63 MB
ℹ️ View Unchanged
|
Summary
contractAddressesprop toVeChainKitProviderto override default contract addresses (e.g., B3TR, VOT3) for custom deployments on solo/testnetuseAppConfig()hook andappConfigcontext field for accessing the merged configTest plan
contractAddressesprop overrides B3TR/VOT3 addresses on testnet/solouseAppConfig()returns merged config with overrides🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Improvements
Chores