-
Notifications
You must be signed in to change notification settings - Fork 9
Allow once-off sponsored transactions #550
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
Conversation
WalkthroughAdds per-call fee delegation support by threading a new Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant UI as DApp UI
participant Hook as useSendTransaction / useBuildTransaction
participant Privy as Privy Wallet Service
participant Provider as PrivyWalletProvider
participant Thor as Thor SDK / Node
UI->>Hook: user triggers sendTransaction(..., delegationUrl?)
Hook->>Hook: build clauses / include delegationUrl in payload
Hook->>Provider: request signAndSend(payload, delegationUrl)
alt delegationUrl provided
Provider->>Privy: call privyWalletProvider.sendTransaction(payload with delegationUrl)
Privy->>Provider: txid / response
else no delegationUrl (generic delegator)
Provider->>Provider: resolve genericDelegatorUrl
Provider->>Privy: call privyWalletProvider.sendTransaction(payload with genericDelegatorUrl)
Privy->>Provider: txid / response
end
Provider->>Thor: send signed tx / buildTransactionBody (isDelegated flag)
Thor->>Provider: tx receipt / txid
Provider->>Hook: resolve promise with txid/receipt
Hook->>UI: update UI (toasts/modals)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 |
packages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
Outdated
Show resolved
Hide resolved
|
Size Change: -87.1 kB (-1.59%) Total Size: 5.39 MB
ℹ️ View Unchanged
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
.github/workflows/deploy-preview.yaml (1)
128-137: Condition prevents comment updates; comment will never be edited after initial creation.The conditional
if: steps.find-comment.outputs.comment-id == ''causes the step to run only when no existing comment is found. This means existing comments are never updated, defeating the purpose of the Find Comment step.Remove the condition or always run the step so that
create-or-update-commentcan update existing deployment comments. The action handles both scenarios: ifcomment-idis provided, it updates; if empty, it creates.- name: Create Deployment Comment uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 - if: steps.find-comment.outputs.comment-id == '' with: comment-id: ${{ steps.find-comment.outputs.comment-id }}packages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts (1)
168-189:dAppSponsoredUrlparameter is accepted but unused in the VeChain wallet (non-Privy) code path.The parameter is passed to
sendTransaction(line 132) but only used when connected via Privy (line 145). In the VeChain wallet path (lines 168-188), the parameter is ignored andbuildTransactionBodyis called with only anisDelegatedflag, which doesn't support per-transaction sponsored URLs. The TODO comment at line 172 ("kit-migration check how to pass the delegator url") confirms this is incomplete. Either this feature needs to be implemented for VeChain wallets, or the parameter should be removed from the non-Privy code path to avoid confusion about its availability.packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.ts (1)
62-79: Remove unnecessarydAppSponsoredUrlparameter fromuseSendTransactionhook props.The
dAppSponsoredUrlparameter destructured at the hook level (line 112 of useSendTransaction.ts) is never used—it appears only in the dependency array but not in any actual logic. ThesendTransactionAdapterfunction accepts and usesdAppSponsoredUrlas a call-time parameter instead. In useBuildTransaction, passingdAppSponsoredUrltouseSendTransactionat line 68 is unnecessary; only the call-time pass at line 77 has effect. Remove the hook-level parameter to eliminate dead code and the apparent redundancy.
🧹 Nitpick comments (1)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx (1)
48-66: Demo placeholder fordAppSponsoredUrlis commented out.The
dAppSponsoredUrlis commented out, which means this demo doesn't actually exercise the sponsored transaction flow. Consider either:
- Adding a placeholder URL that developers can see in action, or
- Adding a comment explaining how to test with a real delegator URL.
suggestedMaxGas: undefined, - // dAppSponsoredUrl: "", <--- YOUR DELEGATOR URL HERE + // To test sponsored transactions, uncomment and provide your delegator URL: + // dAppSponsoredUrl: "https://sponsor.example.com/delegate", });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
.github/workflows/deploy-preview.yaml(4 hunks).github/workflows/destroy-preview-env.yaml(2 hunks)examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx(4 hunks)packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.ts(5 hunks)packages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts(10 hunks)packages/vechain-kit/src/providers/PrivyWalletProvider.tsx(6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)
**/*.{ts,tsx}: In VeChain Kit Version 2, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.tsexamples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsxpackages/vechain-kit/src/providers/PrivyWalletProvider.tsxpackages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
🧠 Learnings (3)
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For transaction building and sending, use the `useBuildTransaction` hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Applied to files:
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.tsexamples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsxpackages/vechain-kit/src/providers/PrivyWalletProvider.tsxpackages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : In VeChain Kit Version 2, use `useThor` instead of `useConnex` for contract interactions
Applied to files:
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.tspackages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For multiple parallel contract calls, use the `executeMultipleClausesCall` utility wrapped in a `useQuery` hook with the pattern: define query key function, use `executeMultipleClausesCall` in queryFn mapping items to clause objects, and transform results
Applied to files:
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.tspackages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
🧬 Code graph analysis (1)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx (2)
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.ts (1)
useBuildTransaction(30-83)examples/next-template/src/app/constants.ts (1)
b3trMainnetAddress(1-1)
🪛 Biome (2.1.2)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
[error] 95-95: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Lint & Build
- GitHub Check: e2e-test
- GitHub Check: build-size-check
- GitHub Check: deploy
🔇 Additional comments (6)
.github/workflows/destroy-preview-env.yaml (2)
4-4: Clarify the security implications ofpull_request_targettrigger.The switch from
pull_requesttopull_request_targetchanges workflow execution to the base branch context (main) with full access to secrets. While this provides better permission isolation for untrusted external PRs, it should be explicitly documented in your PR or ADR to ensure the team understands the security posture change and its implications.Is this change intended for security hardening? If so, please confirm it's aligned with your organization's GitHub Actions security guidelines.
45-51: LGTM: Appropriate scope reduction forBRANCH_NAME.Moving
BRANCH_NAMEfrom top-level env to step-level env (line 48) is good practice—it limits variable visibility to where it's used and reduces potential for accidental reuse in unintended contexts.packages/vechain-kit/src/providers/PrivyWalletProvider.tsx (2)
189-195: Logic correctly bypasses generic delegator whendAppSponsoredUrlis provided.The condition
genericDelegator && !dAppSponsoredUrlensures that when a per-transaction sponsor URL is provided, it takes precedence over the generic delegator path. This aligns with the PR objective of allowing one-off sponsored transactions.
104-125: LGTM:signAndSendcorrectly prioritizesdAppSponsoredUrl.The
dAppSponsoredUrl ?? delegatorUrlfallback ensures the per-transaction sponsor URL takes precedence, falling back to the default delegator URL when not provided. This is the correct behavior for enabling one-off sponsored transactions.packages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts (2)
214-241: Adapter signature updated correctly to forwarddAppSponsoredUrl.The
sendTransactionAdapternow accepts_dAppSponsoredUrlas a second parameter and forwards it to the internalsendTransaction. This enables callers to specify a per-transaction sponsor URL.
52-60: Public API correctly updated fordAppSponsoredUrlparameter.The
UseSendTransactionReturnValue.sendTransactionsignature now includesdAppSponsoredUrlas an optional second parameter, aligning with the implementation and enabling consumers to pass the sponsor URL per transaction.
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
Outdated
Show resolved
Hide resolved
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
Outdated
Show resolved
Hide resolved
…n-kit into mike/sponsor-specific-txs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx (3)
48-66: Consider extracting the shared clause builder to reduce duplication.The
buildTransactionWithDAppSponsoredhook duplicates the entire configuration from the firstuseBuildTransactionhook (lines 22-46), differing only in the commented-outdAppSponsoredUrl. While this duplication may aid clarity in a demo, extracting the commonclauseBuilderfunction would demonstrate best practices and improve maintainability.Apply this diff to extract the shared logic:
+ const buildDummyTransferClause = useCallback(() => { + if (!account?.address) return []; + + return [ + { + ...thor.contracts + .load(b3trMainnetAddress, IB3TR__factory.abi) + .clause.transfer(account.address, BigInt('0')).clause, + comment: `This is a dummy transaction to test the transaction modal. Confirm to transfer 0 B3TR to ${account?.address}`, + }, + ]; + }, [account?.address, thor.contracts]); + const { sendTransaction, status, txReceipt, isTransactionPending, error, resetStatus, } = useBuildTransaction({ - clauseBuilder: () => { - if (!account?.address) return []; - - return [ - { - ...thor.contracts - .load(b3trMainnetAddress, IB3TR__factory.abi) - .clause.transfer(account.address, BigInt('0')).clause, - comment: `This is a dummy transaction to test the transaction modal. Confirm to transfer 0 B3TR to ${account?.address}`, - }, - ]; - }, + clauseBuilder: buildDummyTransferClause, refetchQueryKeys: [], onSuccess: () => {}, onFailure: () => {}, suggestedMaxGas: undefined, }); const buildTransactionWithDAppSponsored = useBuildTransaction({ - clauseBuilder: () => { - if (!account?.address) return []; - - return [ - { - ...thor.contracts - .load(b3trMainnetAddress, IB3TR__factory.abi) - .clause.transfer(account.address, BigInt('0')).clause, - comment: `This is a dummy transaction to test the transaction modal. Confirm to transfer 0 B3TR to ${account?.address}`, - }, - ]; - }, + clauseBuilder: buildDummyTransferClause, refetchQueryKeys: [], onSuccess: () => {}, onFailure: () => {}, suggestedMaxGas: undefined, // dAppSponsoredUrl: "", <--- YOUR DELEGATOR URL HERE });
117-118: Optional: Simplify the fallback logic.Since
buildTransactionWithDAppSponsoredis always returned byuseBuildTransaction, itsisTransactionPendingproperty should always be defined (true or false), making the nullish coalescing fallback unnecessary. However, this defensive pattern is reasonable for a demo and doesn't cause any functional issues.If you prefer to simplify:
- isLoading={buildTransactionWithDAppSponsored.isTransactionPending ?? isTransactionPending} - isDisabled={buildTransactionWithDAppSponsored.isTransactionPending ?? isTransactionPending} + isLoading={buildTransactionWithDAppSponsored.isTransactionPending} + isDisabled={buildTransactionWithDAppSponsored.isTransactionPending}
141-144: Optional: Consider simplifying modal prop fallbacks.Similar to the button states, the nullish coalescing fallbacks for
status,txReceipt,error, andonTryAgainare likely unnecessary sincebuildTransactionWithDAppSponsoredis always defined. However, this pattern is acceptable for demonstrating both transaction flows in an example.If you prefer to use only the sponsored flow:
- status={buildTransactionWithDAppSponsored.status ?? status} - txReceipt={buildTransactionWithDAppSponsored.txReceipt ?? txReceipt} - txError={buildTransactionWithDAppSponsored.error ?? error} - onTryAgain={handleTryAgainWithSponsoredModal ?? handleTryAgain} + status={buildTransactionWithDAppSponsored.status} + txReceipt={buildTransactionWithDAppSponsored.txReceipt} + txError={buildTransactionWithDAppSponsored.error} + onTryAgain={handleTryAgainWithSponsoredModal}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx(4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)
**/*.{ts,tsx}: In VeChain Kit Version 2, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For transaction building and sending, use the `useBuildTransaction` hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For transaction building and sending, use the `useBuildTransaction` hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Applied to files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : Always conditionally enable queries using the `enabled` property to prevent unnecessary contract calls, checking for all required parameters: `enabled: !!requiredParam && !!otherRequiredParam`
Applied to files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
🧬 Code graph analysis (1)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx (2)
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.ts (1)
useBuildTransaction(30-83)examples/next-template/src/app/constants.ts (1)
b3trMainnetAddress(1-1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build-size-check
🔇 Additional comments (3)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx (3)
85-88: Good fix for the Promise fallback logic.The previous incorrect use of nullish coalescing with Promises has been properly resolved. The modal now directly invokes the sponsored transaction flow, which correctly demonstrates the feature.
95-98: Excellent fix for the Rules of Hooks violation.The conditional hook call has been properly resolved.
useCallbackis now invoked unconditionally, which complies with React's Rules of Hooks. The retry logic correctly resets and re-sends the sponsored transaction.
146-146: Clear and appropriate title update.The modal title correctly indicates this demonstrates a DApp-sponsored transaction, helping developers understand which flow they're testing.
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.ts
Outdated
Show resolved
Hide resolved
|
Can you update also the docs? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.ts (1)
31-31: Stale closure:balancesnot in queryKey may cause incorrect token selection.The
balancesarray is accessed insidequeryFnbut isn't included inqueryKey. When balances update (e.g., after a transaction), the query won't refetch, and the closure will use stale balance data, potentially selecting an incorrect gas token.Consider adding a stable representation of balances to the queryKey:
- const queryKey = ['gas-estimation', JSON.stringify(clauses), JSON.stringify(tokens), sendingAmount, sendingTokenSymbol]; + const queryKey = ['gas-estimation', JSON.stringify(clauses), JSON.stringify(tokens), sendingAmount, sendingTokenSymbol, JSON.stringify(balances.map(b => ({ symbol: b.symbol, balance: b.balance })))];Alternatively, if you want to avoid frequent refetches, ensure the
enabledcondition and manual invalidation handle this correctly.Also applies to: 49-49
packages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts (1)
208-234: Signature mismatch between adapter and exported type.The exported
UseSendTransactionReturnValue.sendTransactiontype (lines 53-61) acceptsprivyUIOptionsas the third parameter, butsendTransactionAdapter(line 209) only accepts two parameters and always usesprivyUIOptionsfrom the hook's closure. Callers passingprivyUIOptionsat call time will have their value ignored.Either update the adapter to accept and use the third parameter, or update the exported type to reflect the actual behavior.
If the intent is to allow per-call
privyUIOptions:const sendTransactionAdapter = useCallback( - async (_clauses?: TransactionClause[], _delegationUrl?: string): Promise<void> => { + async (_clauses?: TransactionClause[], _delegationUrl?: string, _privyUIOptions?: { + title?: string; + description?: string; + buttonText?: string; + }): Promise<void> => { if (!_clauses && !clauses) throw new Error('clauses are required'); try { // ... - const response = await sendTransaction(_clauses ?? [], _delegationUrl, { - ...privyUIOptions, - }); + const response = await sendTransaction(_clauses ?? [], _delegationUrl, _privyUIOptions ?? privyUIOptions);
🧹 Nitpick comments (7)
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.ts (2)
62-79: RedundantdelegationUrlpassing pattern.
delegationUrlis passed touseSendTransactionat line 68, and then passed again tosendTransactionat line 77. If the intent is to allow per-call overrides, this is acceptable but could benefit from documentation. Otherwise, one of these could be removed.If per-call override is not needed, simplify by removing the second parameter:
const sendTransaction = useCallback( async (props: ClausesParams) => { - result.sendTransaction(clauseBuilder(props), delegationUrl); + result.sendTransaction(clauseBuilder(props)); }, - [clauseBuilder, result, delegationUrl], + [clauseBuilder, result], );If per-call override IS the intended design (allowing different delegation URLs for different calls), consider documenting this in the JSDoc and updating the function signature to make it explicit:
const sendTransaction = useCallback( async (props: ClausesParams, overrideDelegationUrl?: string) => { result.sendTransaction(clauseBuilder(props), overrideDelegationUrl ?? delegationUrl); }, [clauseBuilder, result, delegationUrl], );
15-15: Standardize parameter naming for fee delegation across the transaction hooks API.The transaction hooks (
useBuildTransaction,useSendTransaction) usedelegationUrlwhile the provider configuration usesdelegatorUrl. Although this distinction may be intentional (override vs. config), the inconsistency creates API confusion. Consider aligning the naming with VeChain SDK conventions—either standardizing ondelegatorUrlor consistently prefixing override parameters (e.g.,delegatorUrlOverride).packages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.ts (3)
8-14: Interface naming convention: use PascalCase.The interface name
useGenericDelegatorFeeEstimationParamsuses camelCase, which is unconventional for TypeScript interfaces. Interface names typically use PascalCase.-export interface useGenericDelegatorFeeEstimationParams { +export interface UseGenericDelegatorFeeEstimationParams { clauses: EnhancedClause[]; enabled?: boolean; tokens: string[]; // Array of tokens to try in order sendingAmount?: string; // Amount being sent sendingTokenSymbol?: string; // Symbol of token being sent }
76-77: RedundantretryDelaywhenretry: false.
retryDelay: 1000has no effect sinceretry: falsedisables retries entirely. Consider removing it to avoid confusion.retry: false, - retryDelay: 1000,
59-59: Side effect insidequeryFn:updatePreferencesmay cause unexpected behavior.Calling
updatePreferencesinsidequeryFnis a side effect that will execute each time the query runs (including refetches, retries if enabled, or cache invalidation). This could cause unintended preference updates. Consider moving this side effect to auseEffectthat reacts to the query result'susedToken.// In the consuming component or a wrapper: const { data } = useGenericDelegatorFeeEstimation({...}); useEffect(() => { if (data?.usedToken) { updatePreferences({ gasTokenToUse: data.usedToken as GasTokenType }); } }, [data?.usedToken]);packages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts (2)
234-234: Unnecessary dependency:delegationUrlin adapter's deps.
sendTransactionAdapterreceives_delegationUrlas a parameter and doesn't use thedelegationUrlprop from closure. Including it in the dependency array causes unnecessary recreation of the callback when the prop changes.- [sendTransaction, clauses, privyUIOptions, delegationUrl], + [sendTransaction, clauses, privyUIOptions],
362-364: Unnecessary dependency inuseMemo.
sendTransactionPendingis listed in the dependency array but isn't used in the memo body (onlystatusis checked). This doesn't cause bugs but is misleading.const isWaitingForWalletConfirmation = useMemo(() => { return status === 'pending'; - }, [sendTransactionPending, status]); + }, [status]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
packages/vechain-kit/src/components/AccountModal/Contents/ChooseName/ChooseNameSummaryContent.tsx(2 hunks)packages/vechain-kit/src/components/AccountModal/Contents/Profile/Customization/CustomizationSummaryContent.tsx(2 hunks)packages/vechain-kit/src/components/AccountModal/Contents/SendToken/SendTokenSummaryContent.tsx(2 hunks)packages/vechain-kit/src/components/AccountModal/Contents/Swap/SwapTokenContent.tsx(2 hunks)packages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.ts(1 hunks)packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegator.ts(0 hunks)packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.ts(5 hunks)packages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts(11 hunks)packages/vechain-kit/src/providers/PrivyWalletProvider.tsx(6 hunks)
💤 Files with no reviewable changes (1)
- packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegator.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)
**/*.{ts,tsx}: In VeChain Kit Version 2, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
packages/vechain-kit/src/components/AccountModal/Contents/Swap/SwapTokenContent.tsxpackages/vechain-kit/src/components/AccountModal/Contents/SendToken/SendTokenSummaryContent.tsxpackages/vechain-kit/src/components/AccountModal/Contents/Profile/Customization/CustomizationSummaryContent.tsxpackages/vechain-kit/src/providers/PrivyWalletProvider.tsxpackages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.tspackages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.tspackages/vechain-kit/src/components/AccountModal/Contents/ChooseName/ChooseNameSummaryContent.tsxpackages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
🧠 Learnings (4)
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : In VeChain Kit Version 2, use `useThor` instead of `useConnex` for contract interactions
Applied to files:
packages/vechain-kit/src/components/AccountModal/Contents/Swap/SwapTokenContent.tsxpackages/vechain-kit/src/components/AccountModal/Contents/SendToken/SendTokenSummaryContent.tsxpackages/vechain-kit/src/components/AccountModal/Contents/Profile/Customization/CustomizationSummaryContent.tsxpackages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.tspackages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.tspackages/vechain-kit/src/components/AccountModal/Contents/ChooseName/ChooseNameSummaryContent.tsxpackages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For single contract read operations, use the `useCallClause` hook with the pattern: import dependencies, define ABI and method as const, create query key function using `getCallClauseQueryKeyWithArgs`, and wrap with useCallClause including data transformation in the select option
Applied to files:
packages/vechain-kit/src/components/AccountModal/Contents/Profile/Customization/CustomizationSummaryContent.tsx
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For transaction building and sending, use the `useBuildTransaction` hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Applied to files:
packages/vechain-kit/src/providers/PrivyWalletProvider.tsxpackages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.tspackages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For multiple parallel contract calls, use the `executeMultipleClausesCall` utility wrapped in a `useQuery` hook with the pattern: define query key function, use `executeMultipleClausesCall` in queryFn mapping items to clause objects, and transform results
Applied to files:
packages/vechain-kit/src/hooks/thor/transactions/useBuildTransaction.tspackages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts
🧬 Code graph analysis (4)
packages/vechain-kit/src/components/AccountModal/Contents/SendToken/SendTokenSummaryContent.tsx (1)
packages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.ts (1)
useGenericDelegatorFeeEstimation(16-79)
packages/vechain-kit/src/components/AccountModal/Contents/Profile/Customization/CustomizationSummaryContent.tsx (1)
packages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.ts (1)
useGenericDelegatorFeeEstimation(16-79)
packages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.ts (1)
packages/vechain-kit/src/types/types.ts (1)
EnhancedClause(130-130)
packages/vechain-kit/src/components/AccountModal/Contents/ChooseName/ChooseNameSummaryContent.tsx (1)
packages/vechain-kit/src/hooks/generic-delegator/useGasEstimation.ts (1)
useGenericDelegatorFeeEstimation(16-79)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build-size-check
- GitHub Check: Lint & Build
- GitHub Check: e2e-test
- GitHub Check: deploy
🔇 Additional comments (9)
packages/vechain-kit/src/providers/PrivyWalletProvider.tsx (3)
241-251: MissingdelegationUrlinsendTransactiondependency array.The
sendTransactioncallback usesdelegationUrlin the conditional check at line 190, but it's not included in theuseCallbackdependency array. This could cause stale closure issues ifdelegationUrlchanges.}, [ sendTransactionUsingGenericDelegator, genericDelegator, smartAccount, connectedWallet, delegatorUrl, buildClausesWithAuth, hasV1SmartAccount, smartAccountVersion, thor, + // Note: delegationUrl is passed per-call, not as a hook dependency ]);However, since
delegationUrlis passed per-call via the payload (line 170) and not from the outer scope, this is actually correct. The callback receivesdelegationUrlfrom its parameter, not from closure.
103-152: Well-structured delegation URL resolution insignAndSend.The fallback chain
delegationUrl ?? delegatorUrlat line 124 correctly prioritizes per-call delegation over the global delegator URL, enabling the one-off sponsored transaction feature.
189-195: Correct conditional logic for generic delegator bypass.The condition
genericDelegator && !delegationUrlcorrectly ensures that when a per-calldelegationUrlis provided, the generic delegator path is bypassed in favor of the explicit delegation endpoint.packages/vechain-kit/src/components/AccountModal/Contents/Profile/Customization/CustomizationSummaryContent.tsx (1)
28-28: Hook rename applied correctly.The migration from
useGasEstimationtouseGenericDelegatorFeeEstimationis consistent with the broader refactoring across the codebase. The hook invocation signature and return value destructuring remain unchanged.Also applies to: 190-196
packages/vechain-kit/src/components/AccountModal/Contents/ChooseName/ChooseNameSummaryContent.tsx (1)
27-28: Hook rename applied correctly.The migration to
useGenericDelegatorFeeEstimationis consistent with the codebase-wide refactoring. Theclauses()function call pattern aligns with how domain claim hooks expose their clause builders.Also applies to: 174-180
packages/vechain-kit/src/components/AccountModal/Contents/Swap/SwapTokenContent.tsx (1)
45-46: Hook rename with swap-specific parameters applied correctly.The migration to
useGenericDelegatorFeeEstimationcorrectly preserves the swap-specific parameters (sendingAmount,sendingTokenSymbol) which the hook uses to calculate whether the user has sufficient balance when the gas token matches the sending token.Also applies to: 344-355
packages/vechain-kit/src/components/AccountModal/Contents/SendToken/SendTokenSummaryContent.tsx (1)
29-30: Hook rename with send-specific parameters applied correctly.The migration to
useGenericDelegatorFeeEstimationcorrectly includessendingAmountandsendingTokenSymbolparameters, enabling the hook to verify the user has sufficient balance for both the transfer amount and gas fees when using the same token.Also applies to: 204-212
packages/vechain-kit/src/hooks/thor/transactions/useSendTransaction.ts (2)
169-182: LGTM!The
requestTransactionintegration with conditional delegator configuration correctly handles both global fee delegation (delegateAllTransactions) and per-transaction delegation (delegationUrl). The fallback chaindelegationUrl ?? feeDelegation?.delegatorUrlproperly prioritizes the per-call URL.
53-61: TheprivyWalletProvider.sendTransactioninterface already supportsdelegationUrl. The PrivyWalletProviderContextType interface inpackages/vechain-kit/src/providers/PrivyWalletProvider.tsx(line 41) includesdelegationUrl?: stringas a parameter in the sendTransaction method signature, so the delegation will work correctly for Privy-connected wallets.
|
|
||
| return useQuery<EstimationResponse & { usedToken: string }, Error>({ | ||
| queryKey, | ||
| queryFn: async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The queryFn in useGenericDelegatorFeeEstimation nests a for -> try -> if return path and accumulates lastError instead of using early continues/guard clauses to simplify loop control.
Details
🔧 How do I fix it?
Place parameter validation and guard clauses at the function start. Use early returns to reduce nesting levels and improve readability.
More info - Comment @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
There was a problem hiding this 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 (1)
packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.ts (1)
8-14: Type tokens andusedTokenasGasTokenTypeto avoid unsafe castsRight now
tokensisstring[]and you repeatedly cast toGasTokenType; this loses type safety and can hide invalid values until runtime. Narrowing the API toGasTokenType[](andusedToken: GasTokenType) lets TS catch mistakes at call sites and removes the need forascasts.-export interface useGenericDelegatorFeeEstimationParams { - clauses: EnhancedClause[]; - enabled?: boolean; - tokens: string[]; // Array of tokens to try in order - sendingAmount?: string; // Amount being sent - sendingTokenSymbol?: string; // Symbol of token being sent -} +export interface useGenericDelegatorFeeEstimationParams { + clauses: EnhancedClause[]; + enabled?: boolean; + tokens: GasTokenType[]; // Array of tokens to try in order + sendingAmount?: string; // Amount being sent + sendingTokenSymbol?: GasTokenType; // Symbol of token being sent +} @@ - return useQuery<EstimationResponse & { usedToken: string }, Error>({ + return useQuery<EstimationResponse & { usedToken: GasTokenType }, Error>({ @@ - for (const token of tokens) { + for (const token of tokens) { @@ - clauses as TransactionClause[], - token as GasTokenType, + clauses as TransactionClause[], + token, @@ - updatePreferences({ gasTokenToUse: token as GasTokenType }); - return { ...estimation, usedToken: token }; + updatePreferences({ gasTokenToUse: token }); + return { ...estimation, usedToken: token };Also applies to: 33-34, 39-45, 57-60
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/vechain-kit/src/hooks/generic-delegator/index.ts(1 hunks)packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.ts(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)
**/*.{ts,tsx}: In VeChain Kit Version 2, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.tspackages/vechain-kit/src/hooks/generic-delegator/index.ts
🧠 Learnings (3)
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For single contract read operations, use the `useCallClause` hook with the pattern: import dependencies, define ABI and method as const, create query key function using `getCallClauseQueryKeyWithArgs`, and wrap with useCallClause including data transformation in the select option
Applied to files:
packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.ts
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For transaction building and sending, use the `useBuildTransaction` hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Applied to files:
packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.ts
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For multiple parallel contract calls, use the `executeMultipleClausesCall` utility wrapped in a `useQuery` hook with the pattern: define query key function, use `executeMultipleClausesCall` in queryFn mapping items to clause objects, and transform results
Applied to files:
packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.ts
🧬 Code graph analysis (1)
packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.ts (7)
packages/vechain-kit/src/types/types.ts (1)
EnhancedClause(130-130)packages/vechain-kit/src/hooks/thor/smartAccounts/useSmartAccount.ts (1)
useSmartAccount(51-60)packages/vechain-kit/src/providers/VeChainKitProvider.tsx (1)
useVeChainKitConfig(204-210)packages/vechain-kit/src/hooks/generic-delegator/useGasTokenSelection.ts (1)
useGasTokenSelection(9-84)packages/vechain-kit/src/types/gasEstimation.ts (1)
EstimationResponse(36-43)packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegator.ts (1)
estimateGas(17-30)packages/vechain-kit/src/types/gasToken.ts (1)
GasTokenType(1-1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Lint & Build
- GitHub Check: e2e-test
- GitHub Check: deploy
- GitHub Check: build-size-check
🔇 Additional comments (1)
packages/vechain-kit/src/hooks/generic-delegator/index.ts (1)
1-4: Re-export ofuseGenericDelegatorFeeEstimationlooks correctThe new barrel export aligns with the new hook implementation and lets consumers switch from
useGasEstimationcleanly.
| const { connectedWallet, account } = useWallet(); | ||
| const { data: smartAccount } = useSmartAccount( | ||
| connectedWallet?.address ?? '', | ||
| ); | ||
| const { feeDelegation } = useVeChainKitConfig(); | ||
| const { balances } = useTokenBalances(account?.address ?? ''); | ||
| const { updatePreferences } = useGasTokenSelection(); | ||
| // Only include essential data in query key to prevent unnecessary refetches | ||
| const queryKey = ['gas-estimation', JSON.stringify(clauses), JSON.stringify(tokens), sendingAmount, sendingTokenSymbol]; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Include signer/delegator context in the query key to avoid stale estimations
queryKey currently ignores the signer (smart account) and delegator URL, so gas estimations can be incorrectly reused when the connected wallet, smart account, or network changes while clauses/tokens stay the same. This can surface the wrong gas token or cost for a new account.
Consider folding the relevant context into the key (and keeping it simple rather than JSON.stringify-ing large objects):
- // Only include essential data in query key to prevent unnecessary refetches
- const queryKey = ['gas-estimation', JSON.stringify(clauses), JSON.stringify(tokens), sendingAmount, sendingTokenSymbol];
+ // Include signer and delegator context so estimations change when account/network changes
+ const queryKey = [
+ 'gas-estimation',
+ smartAccount?.address,
+ feeDelegation?.genericDelegatorUrl,
+ tokens,
+ clauses,
+ sendingAmount,
+ sendingTokenSymbol,
+ ] as const;
@@
- enabled: enabled && clauses.length > 0 && !!smartAccount?.address && !!feeDelegation?.genericDelegatorUrl && tokens.length > 0 && balances.length > 0,
+ enabled:
+ enabled &&
+ clauses.length > 0 &&
+ !!smartAccount?.address &&
+ !!feeDelegation?.genericDelegatorUrl &&
+ tokens.length > 0 &&
+ balances.length > 0,Also, please confirm that useTokenBalances always returns balances as an array (defaulting to []), otherwise balances.length could throw. Based on learnings, conditional enabled guards are important to prevent unnecessary/invalid queries.
Also applies to: 35-37, 70-78
🤖 Prompt for AI Agents
packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.ts
lines 23-32 (and replicate same changes at 35-37 and 70-78): the query key
currently omits signer/smart-account and delegator context and uses
JSON.stringify on large objects; update the key to include simple identifiers
such as connectedWallet?.address, smartAccount?.address, and feeDelegation?.url
or id (avoid stringifying full objects), e.g. ['gas-estimation', clausesId or
clauses.length, tokensId or tokens.map(t=>t.symbol).join(','), sendingAmount,
sendingTokenSymbol, connectedWallet?.address, smartAccount?.address,
feeDelegation?.url]; also ensure useTokenBalances returns a safe default
(balances ?? []) or guard enabled checks with balances?.length to prevent
accessing length of undefined and add conditional enabled guards so queries only
run when wallet/smartAccount/delegator context is available.
| const estimation = await estimateGas( | ||
| smartAccount?.address ?? '', | ||
| feeDelegation?.genericDelegatorUrl ?? '', | ||
| clauses as TransactionClause[], | ||
| token as GasTokenType, | ||
| 'medium', | ||
| ); | ||
| // Check if user has enough balance for this token | ||
| const gasCost = estimation.transactionCost; | ||
| const tokenBalance = Number(balances.find(t => t.symbol === token)?.balance || 0); | ||
| // If sending the same token as gas token, need balance for both | ||
| // If no sendingAmount is provided, we're only checking for gas fees | ||
| const additionalAmount = (sendingAmount && sendingTokenSymbol && token === sendingTokenSymbol) | ||
| ? Number(sendingAmount) | ||
| : 0; | ||
| const requiredBalance = gasCost + additionalAmount; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle missing/invalid transactionCost and avoid Number-based precision loss
EstimationResponse.transactionCost is optional; if it’s undefined, gasCost + additionalAmount becomes NaN, the balance check always fails, and the error message logs needs NaN, which is misleading. On top of that, Number(...) may lose precision for large token balances or amounts.
A more defensive approach could be:
- const estimation = await estimateGas(
+ const estimation = await estimateGas(
@@
- // Check if user has enough balance for this token
- const gasCost = estimation.transactionCost;
- const tokenBalance = Number(balances.find(t => t.symbol === token)?.balance || 0);
+ // Check if user has enough balance for this token
+ const gasCost = estimation.transactionCost ?? 0;
+ if (!Number.isFinite(gasCost) || gasCost <= 0) {
+ lastError = new Error(
+ `Delegator returned invalid transactionCost for ${token}: ${gasCost}`,
+ );
+ continue;
+ }
+
+ // TODO: if balances/sendingAmount are big-int/wei strings, prefer a bignumber util instead of Number()
+ const tokenBalance = Number(
+ balances.find((t) => t.symbol === token)?.balance ?? 0,
+ );
@@
- const additionalAmount = (sendingAmount && sendingTokenSymbol && token === sendingTokenSymbol)
- ? Number(sendingAmount)
- : 0;
+ const additionalAmount =
+ sendingAmount &&
+ sendingTokenSymbol &&
+ token === sendingTokenSymbol
+ ? Number(sendingAmount)
+ : 0;
@@
- const requiredBalance = gasCost + additionalAmount;
+ const requiredBalance = gasCost + additionalAmount;If balances and amounts are in wei/large integer space, consider switching to a shared bigint/bignumber helper instead of Number() to avoid rounding issues; that can be a follow-up refactor.
Also applies to: 63-68
🤖 Prompt for AI Agents
In
packages/vechain-kit/src/hooks/generic-delegator/useGenericDelegatorFeeEstimation.ts
around lines 40 to 56 (also apply same fix to 63-68): guard against undefined
estimation.transactionCost and stop using Number() for monetary values; convert
transactionCost and balance/amount strings to a safe big integer/bignumber
representation (or BigInt) before arithmetic, treat a missing transactionCost
explicitly (either default to 0 with a clear log/flag or return early with an
error), compute requiredBalance using the big-int/bignumber types, and use that
same type to compare against token balances so you never produce NaN or incur
precision loss.
There was a problem hiding this 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)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx (2)
90-126: Consider using stable object references in dependency arrays.The useCallback hooks reference individual methods like
buildTransactionWithDelegation.sendTransactionin dependency arrays. While this works if the hooks return stable method references, it's more idiomatic to depend on the entire hook result object and trust React's memoization.const handleBuildTransactionDelegatedWithModal = useCallback(async () => { setCurrentModalType('buildTransactionWithDelegation'); openTransactionModal(); await (buildTransactionWithDelegation.sendTransaction({})); - }, [buildTransactionWithDelegation.sendTransaction, openTransactionModal]); + }, [buildTransactionWithDelegation, openTransactionModal]);Apply similar changes to other callbacks (
handleUseSendTransactionWithToast,handleSendTransactionDelegatedWithModal, retry handlers).
202-221: Misleading nullish coalescing fallback for modal props.The
??fallback (e.g.,buildTransactionWithDelegation.status ?? buildTransactionNoDelegation.status) won't execute as expected because hook results likestatusare always defined (even if the value is'idle'or similar). The fallback implies a safety net that doesn't actually work.Since
currentModalTypealready determines which flow is active, simplify by removing the fallback:status={ currentModalType === 'buildTransactionWithDelegation' - ? buildTransactionWithDelegation.status ?? buildTransactionNoDelegation.status - : sendTransactionWithDelegation.status ?? sendTransactionNoDelegation.status + ? buildTransactionWithDelegation.status + : sendTransactionWithDelegation.status } txReceipt={ currentModalType === 'buildTransactionWithDelegation' - ? buildTransactionWithDelegation.txReceipt ?? buildTransactionNoDelegation.txReceipt - : sendTransactionWithDelegation.txReceipt ?? sendTransactionNoDelegation.txReceipt + ? buildTransactionWithDelegation.txReceipt + : sendTransactionWithDelegation.txReceipt } txError={ currentModalType === 'buildTransactionWithDelegation' - ? buildTransactionWithDelegation.error ?? buildTransactionNoDelegation.error - : sendTransactionWithDelegation.error ?? sendTransactionNoDelegation.error + ? buildTransactionWithDelegation.error + : sendTransactionWithDelegation.error }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)
**/*.{ts,tsx}: In VeChain Kit Version 2, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For transaction building and sending, use the `useBuildTransaction` hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Applied to files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : Always conditionally enable queries using the `enabled` property to prevent unnecessary contract calls, checking for all required parameters: `enabled: !!requiredParam && !!otherRequiredParam`
Applied to files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
🪛 Biome (2.1.2)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
[error] 33-33: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 47-47: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: e2e-test
- GitHub Check: Lint & Build
- GitHub Check: build-size-check
- GitHub Check: deploy
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx (1)
32-61: Critical: Hooks called inside nested functions violate React's Rules of Hooks.The
callUseBuildTransactionandcallUseSendTransactionfunctions wrap hook calls inside regular functions. This violates React's Rules of Hooks which require hooks to be called unconditionally at the top level of the component. React cannot statically verify the call order, leading to potential bugs and broken linting.Refactor to call hooks directly at the top level. If you need to reduce duplication, extract the shared configuration into objects/constants rather than wrapping hooks:
- const callUseBuildTransaction = (delegationUrl?: string) => { - return useBuildTransaction({ + const buildTransactionConfig = { clauseBuilder: () => { if (!account?.address) return []; - return clauses; }, refetchQueryKeys: [], onSuccess: () => {}, onFailure: () => {}, suggestedMaxGas: undefined, - delegationUrl: delegationUrl, - });} + }; - const callUseSendTransaction = (delegationUrl?: string) => { - return useSendTransaction({ - signerAccountAddress: account?.address, - delegationUrl: delegationUrl, - }); - } + const buildTransactionNoDelegation = useBuildTransaction({ + ...buildTransactionConfig, + delegationUrl: undefined, + }); - const buildTransactionNoDelegation = callUseBuildTransaction(); + const delegationUrl = ""; // YOUR DELEGATION URL HERE - const delegationUrl = ""; // YOUR DELEGATION URL HERE - - const buildTransactionWithDelegation = callUseBuildTransaction(delegationUrl); + const buildTransactionWithDelegation = useBuildTransaction({ + ...buildTransactionConfig, + delegationUrl: delegationUrl, + }); - const sendTransactionNoDelegation = callUseSendTransaction(); + const sendTransactionNoDelegation = useSendTransaction({ + signerAccountAddress: account?.address, + delegationUrl: undefined, + }); - const sendTransactionWithDelegation = callUseSendTransaction(); + const sendTransactionWithDelegation = useSendTransaction({ + signerAccountAddress: account?.address, + delegationUrl: delegationUrl, + });
🧹 Nitpick comments (2)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx (2)
84-88: Non-idiomatic dependency array: prefer whole object over method reference.Referencing
buildTransactionWithDelegation.sendTransactionin the dependency array can cause issues with referential stability. React's ESLint plugin typically warns against this pattern.const handleBuildTransactionDelegatedWithModal = useCallback(async () => { setCurrentModalType('useBuildTxWithDelegation'); openTransactionModal(); await (buildTransactionWithDelegation.sendTransaction({})); - }, [buildTransactionWithDelegation.sendTransaction, openTransactionModal]); + }, [buildTransactionWithDelegation, openTransactionModal]);Apply the same pattern to other callbacks at lines 90-94, 96-100, 102-105, 112-115.
55-55: Placeholder delegation URL needs attention.The empty string for
delegationUrlmeans the "delegated" transaction flows won't actually perform fee delegation. Ensure this is intentional for demo purposes and consider adding a more prominent comment or TODO to guide developers.- const delegationUrl = ""; // YOUR DELEGATION URL HERE + // TODO: Replace with your actual delegation endpoint URL to enable fee delegation + // Example: "https://sponsor.vechain.energy/by/123" + const delegationUrl = ""; // YOUR DELEGATION URL HERE
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/migration-guide-to-v2.mdc)
**/*.{ts,tsx}: In VeChain Kit Version 2, useuseThorinstead ofuseConnexfor contract interactions
For single contract read operations, use theuseCallClausehook with the pattern: import dependencies, define ABI and method as const, create query key function usinggetCallClauseQueryKeyWithArgs, and wrap with useCallClause including data transformation in the select option
For multiple parallel contract calls, use theexecuteMultipleClausesCallutility wrapped in auseQueryhook with the pattern: define query key function, useexecuteMultipleClausesCallin queryFn mapping items to clause objects, and transform results
For transaction building and sending, use theuseBuildTransactionhook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Always provide an arguments array for contract calls, even when no parameters are required - use an empty array for parameter-less functions to enable TypeScript type checking
Always conditionally enable queries using theenabledproperty to prevent unnecessary contract calls, checking for all required parameters:enabled: !!requiredParam && !!otherRequiredParam
Use theselectoption in useCallClause or transform data in queryFn to handle data transformation, particularly for converting BigInt values to strings and normalizing contract return data
Maintain consistent query key patterns: usegetCallClauseQueryKeyWithArgsfor contract calls with arguments andgetCallClauseQueryKeyfor calls without arguments to ensure proper caching and invalidation
Use TypeScriptas constassertions for method names andas0x${string}`` assertions for Ethereum addresses to ensure type safety in contract interactions
Files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
🧠 Learnings (3)
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For transaction building and sending, use the `useBuildTransaction` hook with a clauseBuilder function that returns an array of clauses with optional comment fields describing the transaction action
Applied to files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For multiple parallel contract calls, use the `executeMultipleClausesCall` utility wrapped in a `useQuery` hook with the pattern: define query key function, use `executeMultipleClausesCall` in queryFn mapping items to clause objects, and transform results
Applied to files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
📚 Learning: 2025-12-01T13:01:33.771Z
Learnt from: CR
Repo: vechain/vechain-kit PR: 0
File: .cursor/rules/migration-guide-to-v2.mdc:0-0
Timestamp: 2025-12-01T13:01:33.771Z
Learning: Applies to **/*.{ts,tsx} : For single contract read operations, use the `useCallClause` hook with the pattern: import dependencies, define ABI and method as const, create query key function using `getCallClauseQueryKeyWithArgs`, and wrap with useCallClause including data transformation in the select option
Applied to files:
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
🪛 Biome (2.1.2)
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
[error] 33-33: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 47-47: This hook is being called from a nested function, but all hooks must be called unconditionally from the top-level component.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Lint & Build
- GitHub Check: build-size-check
- GitHub Check: e2e-test
- GitHub Check: deploy
|
|
||
| const sendTransactionNoDelegation = callUseSendTransaction(); | ||
|
|
||
| const sendTransactionWithDelegation = callUseSendTransaction(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: sendTransactionWithDelegation is missing the delegation URL.
callUseSendTransaction() is called without the delegationUrl argument, so sendTransactionWithDelegation is functionally identical to sendTransactionNoDelegation. The delegation URL should be passed for this transaction to use fee delegation.
- const sendTransactionWithDelegation = callUseSendTransaction();
+ const sendTransactionWithDelegation = callUseSendTransaction(delegationUrl);🤖 Prompt for AI Agents
In
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
around line 61, the callUseSendTransaction() invocation for
sendTransactionWithDelegation is missing the delegationUrl argument so it
behaves the same as the no-delegation variant; update the call to pass the
proper delegation URL (e.g., callUseSendTransaction(delegationUrl) or the
correct variable/constant in scope) so this instance uses fee delegation.
| <Button | ||
| onClick={handleTransactionWithToast} | ||
| isLoading={isTransactionPending} | ||
| isDisabled={isTransactionPending} | ||
| isLoading={buildTransactionNoDelegation.isTransactionPending} | ||
| isDisabled={buildTransactionNoDelegation.isTransactionPending} | ||
| data-testid="tx-with-toast-button" | ||
| > | ||
| useBuildTransaction with toast (no delegation) | ||
| </Button> | ||
| <Button | ||
| onClick={handleBuildTransactionDelegatedWithModal} | ||
| isLoading={buildTransactionWithDelegation.isTransactionPending} | ||
| isDisabled={buildTransactionWithDelegation.isTransactionPending} | ||
| data-testid="tx-with-modal-button" | ||
| > | ||
| useBuildTransaction with modal (delegated) | ||
| </Button> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate data-testid values across buttons.
Lines 138 and 146 both use data-testid="tx-with-toast-button" and data-testid="tx-with-modal-button" respectively, which are also used in lines 156 and 164. This will cause ambiguity in test selectors.
<Button
onClick={handleTransactionWithToast}
isLoading={buildTransactionNoDelegation.isTransactionPending}
isDisabled={buildTransactionNoDelegation.isTransactionPending}
- data-testid="tx-with-toast-button"
+ data-testid="build-tx-with-toast-button"
>
useBuildTransaction with toast (no delegation)
</Button>
<Button
onClick={handleBuildTransactionDelegatedWithModal}
isLoading={buildTransactionWithDelegation.isTransactionPending}
isDisabled={buildTransactionWithDelegation.isTransactionPending}
- data-testid="tx-with-modal-button"
+ data-testid="build-tx-with-modal-button"
>
useBuildTransaction with modal (delegated)
</Button>Similarly for lines 152-167:
<Button
onClick={handleUseSendTransactionWithToast}
isLoading={sendTransactionNoDelegation.isTransactionPending}
isDisabled={sendTransactionNoDelegation.isTransactionPending}
- data-testid="tx-with-toast-button"
+ data-testid="send-tx-with-toast-button"
>
useSendTransaction with toast (no delegation)
</Button>
<Button
onClick={handleSendTransactionDelegatedWithModal}
isLoading={sendTransactionWithDelegation.isTransactionPending}
isDisabled={sendTransactionWithDelegation.isTransactionPending}
- data-testid="tx-with-modal-button"
+ data-testid="send-tx-with-modal-button"
>
useSendTransaction with modal (delegated)
</Button>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
around lines 134 to 149 (and similarly for lines 152 to 167), multiple Button
elements reuse the same data-testid values causing ambiguous test selectors;
change each Button to have a unique data-testid (e.g., append a descriptive
suffix like -no-delegation, -delegated, -with-modal, -with-toast or include
index/context) so every test id is unique and then update any tests that assert
on the old ids to use the new unique ids.
| status={ | ||
| currentModalType === 'useBuildTxWithDelegation' | ||
| ? buildTransactionWithDelegation.status ?? buildTransactionNoDelegation.status | ||
| : sendTransactionWithDelegation.status ?? sendTransactionNoDelegation.status | ||
| } | ||
| txReceipt={ | ||
| currentModalType === 'useBuildTxWithDelegation' | ||
| ? buildTransactionWithDelegation.txReceipt ?? buildTransactionNoDelegation.txReceipt | ||
| : sendTransactionWithDelegation.txReceipt ?? sendTransactionNoDelegation.txReceipt | ||
| } | ||
| txError={ | ||
| currentModalType === 'useBuildTxWithDelegation' | ||
| ? buildTransactionWithDelegation.error ?? buildTransactionNoDelegation.error | ||
| : sendTransactionWithDelegation.error ?? sendTransactionNoDelegation.error | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fallback logic with ?? may not work as intended for status values.
The ?? operator only falls back when the left operand is null or undefined. If status is a defined string like 'idle' or 'error', the fallback won't trigger. Since currentModalType already determines which flow is active, the fallback to buildTransactionNoDelegation / sendTransactionNoDelegation seems unintentional and could display stale state from a different transaction.
Consider removing the fallback or using explicit conditional logic:
status={
currentModalType === 'useBuildTxWithDelegation'
- ? buildTransactionWithDelegation.status ?? buildTransactionNoDelegation.status
- : sendTransactionWithDelegation.status ?? sendTransactionNoDelegation.status
+ ? buildTransactionWithDelegation.status
+ : sendTransactionWithDelegation.status
}
txReceipt={
currentModalType === 'useBuildTxWithDelegation'
- ? buildTransactionWithDelegation.txReceipt ?? buildTransactionNoDelegation.txReceipt
- : sendTransactionWithDelegation.txReceipt ?? sendTransactionNoDelegation.txReceipt
+ ? buildTransactionWithDelegation.txReceipt
+ : sendTransactionWithDelegation.txReceipt
}
txError={
currentModalType === 'useBuildTxWithDelegation'
- ? buildTransactionWithDelegation.error ?? buildTransactionNoDelegation.error
- : sendTransactionWithDelegation.error ?? sendTransactionNoDelegation.error
+ ? buildTransactionWithDelegation.error
+ : sendTransactionWithDelegation.error
}🤖 Prompt for AI Agents
In
examples/next-template/src/app/components/features/TransactionExamples/TransactionExamples.tsx
around lines 203 to 217, the code uses the nullish coalescing operator (??) to
fall back to the other transaction object’s fields, which will not trigger for
defined status strings (e.g., 'idle' or 'error') and can surface stale state
from the non-active flow; remove the fallback and select fields exclusively
based on currentModalType (i.e., return
buildTransactionWithDelegation.status/txReceipt/error when currentModalType ===
'useBuildTxWithDelegation', otherwise return sendTransactionWithDelegation.*),
or if you must keep a fallback only use an explicit undefined/null check (e.g.,
status === undefined || status === null ? other.status : status) so only truly
missing values fall back.
Description
This pull request introduces support for dApp-sponsored (delegated fee) transactions in the transaction hooks and UI, and improves the GitHub Actions workflows for preview environment deployment and teardown. The main changes add an optional
dAppSponsoredUrlparameter throughout the transaction building and sending flow, and update the preview deployment workflow to enforce branch naming conventions for external pull requests.dApp-Sponsored Transaction Support:
Added an optional
dAppSponsoredUrlparameter touseBuildTransaction,useSendTransaction, and thePrivyWalletProviderContextType, allowing transactions to be sent via a delegator for sponsored fees. This includes updates to type definitions, function signatures, and transaction sending logic.Updated the
TransactionExamplescomponent to demonstrate and handle dApp-sponsored transactions, including new button logic, modal state handling, and user feedback.Preview Environment Workflow Improvements:
Enforced branch naming convention for external pull requests in the
deploy-preview.yamlworkflow, requiring branches to be prefixed with the GitHub username (e.g.,username/feature-branch). The workflow now fails early if this is not met, and user-facing messages were updated accordingly.Improved deployment comment logic in the preview deployment workflow to avoid duplicate comments by checking for an existing deployment comment before posting a new one.
Changed the destroy preview environment workflow trigger from
pull_requesttopull_request_targetfor better permission handling, and improved the logic for finding and removing deployment comments.Demo
Screen.Recording.2025-12-02.at.11.27.33.mov
Closes #471
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.