Conversation
Staging to Prod
Staging to prod
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a syncApi indexer wrapper and integrates it into campaign create/update and donation flows; refactors Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UI as UI Layer
participant WalletAPI as Wallet/Contract
participant Blockchain as Blockchain
participant EffectsHandler as Effects Handler
participant IndexerAPI as Indexer API
participant Backend as Backend
User->>UI: Initiate donation
UI->>WalletAPI: donate(args, depositAmount)
WalletAPI->>WalletAPI: Build transaction
WalletAPI->>Blockchain: signAndSendTransaction
Blockchain-->>WalletAPI: txHash, outcome
WalletAPI->>WalletAPI: Extract donation & txHash
WalletAPI-->>UI: DonateResult {donation, txHash}
UI->>EffectsHandler: Process donation result
EffectsHandler->>EffectsHandler: Extract donation & txHash
EffectsHandler->>IndexerAPI: syncApi.campaignDonation(campaignId, txHash, donor_id)
IndexerAPI->>Backend: POST /campaigns/{id}/donations/sync
Backend-->>IndexerAPI: {success: true}
IndexerAPI-->>EffectsHandler: Sync result
EffectsHandler->>EffectsHandler: Dispatch success (errors suppressed)
EffectsHandler-->>UI: Donation complete
sequenceDiagram
actor User
participant UI as UI/Forms
participant ContractAPI as Contract/API
participant IndexerAPI as Indexer API
participant Backend as Backend
User->>UI: Create/Update campaign
UI->>ContractAPI: Submit campaign
ContractAPI-->>UI: Campaign response (with id)
UI->>IndexerAPI: syncApi.campaign(campaignId)
IndexerAPI->>Backend: POST /campaigns/{id}/sync
Backend-->>IndexerAPI: {success: true}
IndexerAPI-->>UI: Sync result
UI->>UI: Reset form & show toast (errors suppressed)
UI-->>User: Campaign synced
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 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
🤖 Fix all issues with AI agents
In `@src/common/contracts/core/campaigns/client.ts`:
- Around line 169-172: providers.getTransactionLastResult(outcome) may return
undefined so you must validate its return before casting to CampaignDonation; in
the code around the symbols outcome, providers.getTransactionLastResult and the
returned DonateResult, check the result and if it's undefined either throw a
clear error (e.g. throw new Error("missing transaction result for txHash: ..."))
or return a value that matches the DonateResult contract (do not leave donation
undefined). Ensure the code uses the computed txHash when building the
error/return so the failure can be traced.
🧹 Nitpick comments (12)
src/entities/campaign/hooks/forms.ts (4)
8-8: Consider using the barrel export for consistency.The import uses a direct path, but
syncApiis re-exported from the barrel at@/common/api/indexer. Other files in this PR (e.g.,src/features/donation/models/effects/index.ts) import from the barrel.♻️ Suggested change
-import { syncApi } from "@/common/api/indexer/sync"; +import { syncApi } from "@/common/api/indexer";
314-317: Consider fire-and-forget for better UX.The
awaitonsyncApi.campaign()blocks the success toast and form reset until the sync completes. Since sync errors are already swallowed and the primary operation succeeded, making this non-blocking would improve perceived responsiveness.♻️ Fire-and-forget approach
.then(async () => { // Sync campaign to database - await syncApi.campaign(campaignId).catch(console.warn); + syncApi.campaign(campaignId).catch(console.warn); self.reset(values, { keepErrors: false });
354-362: Duplicated type guard logic.The same condition (
newCampaign && typeof newCampaign === "object" && "id" in newCampaign && newCampaign.id) appears twice. Consider consolidating into a single block or extracting a type guard function.♻️ Consolidated approach
.then(async (newCampaign) => { const startMs = values.start_ms ? timeToMilliseconds(values.start_ms) : undefined; + const campaignWithId = + newCampaign && typeof newCampaign === "object" && "id" in newCampaign && newCampaign.id + ? (newCampaign as Campaign) + : null; // Sync new campaign to database - if ( - newCampaign && - typeof newCampaign === "object" && - "id" in newCampaign && - newCampaign.id - ) { - await syncApi.campaign((newCampaign as Campaign).id).catch(console.warn); + if (campaignWithId) { + syncApi.campaign(campaignWithId.id).catch(console.warn); } toast({ title: `You've successfully created a campaign for ${values.name}.`, description: (() => { if (startMs && startMs > Date.now()) { return `Campaign starts on ${formatFullDateTime(startMs)}.`; } return "Campaign is live."; })(), }); - // Fix: Ensure newCampaign has an id before accessing it - console.log(newCampaign); - - if ( - newCampaign && - typeof newCampaign === "object" && - "id" in newCampaign && - newCampaign.id - ) { - router.push(routeSelectors.CAMPAIGN_BY_ID((newCampaign as Campaign).id)); + if (campaignWithId) { + router.push(routeSelectors.CAMPAIGN_BY_ID(campaignWithId.id)); } else { router.push(`/campaigns`); }Also applies to: 378-384
376-376: Remove debug console.log.This
console.log(newCampaign)appears to be leftover debug code.🧹 Remove debug statement
- // Fix: Ensure newCampaign has an id before accessing it - console.log(newCampaign);src/features/donation/models/effects/index.ts (2)
158-165: Add blank line before dispatch statement per linting rules.ESLint reports missing padding line before the
dispatch.donation.successstatement.🔧 Fix lint error
await syncApi .campaignDonation(campaignId, result.txHash, result.donation.donor_id) .catch(() => {}); } + dispatch.donation.success(result.donation);
183-190: Add blank line before dispatch statement per linting rules.Same ESLint padding line issue as the FT donation branch.
🔧 Fix lint error
await syncApi .campaignDonation(campaignId, result.txHash, result.donation.donor_id) .catch(() => {}); } + dispatch.donation.success(result.donation);src/features/donation/models/effects/campaign-ft-donation.ts (2)
255-259: Add blank line before const declaration per linting rules.ESLint reports missing padding line before the
txHashdeclaration.🔧 Fix lint error
.then((finalExecutionOutcomes) => { const lastOutcome = finalExecutionOutcomes?.at(-1); + const txHash = (lastOutcome as any)?.transaction?.hash || (lastOutcome as any)?.transaction_outcome?.id || null;
256-259: Consider extracting txHash extraction logic.The same
transaction?.hash || transaction_outcome?.idpattern appears insrc/common/contracts/core/campaigns/client.ts(line 169). Consider extracting to a shared utility to reduce duplication and ensure consistent behavior across donation flows.src/common/api/indexer/sync.ts (2)
21-22: Inconsistent success logging.The
campaignmethod logs success withconsole.log("Campaign synced:", result), butcampaignDonationdoesn't log on success. Consider making this consistent - either log both or neither.♻️ Option A: Add logging to campaignDonation
const result = await response.json(); + console.log("Campaign donation synced:", result); return { success: true, message: result.message };♻️ Option B: Remove logging from campaign
const result = await response.json(); - console.log("Campaign synced:", result); return { success: true, message: result.message };Also applies to: 57-58
10-13: Consider adding request timeouts.The fetch calls have no timeout configured. If the indexer API is slow or unresponsive, these requests could hang indefinitely, blocking the awaiting code in the donation/campaign flows.
♻️ Add AbortController timeout
async campaign(campaignId: number | string): Promise<{ success: boolean; message?: string }> { try { + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout + const response = await fetch( `${INDEXER_API_ENDPOINT_URL}/api/v1/campaigns/${campaignId}/sync`, - { method: "POST" }, + { method: "POST", signal: controller.signal }, ); + clearTimeout(timeoutId);Apply similar pattern to
campaignDonation.Also applies to: 42-49
src/common/contracts/core/campaigns/client.ts (2)
164-164: Add blank line before outcome assignment per linting rules.ESLint reports missing padding line.
🔧 Fix lint error
], }); + outcome = Array.isArray(results) ? results[0] : results;
146-167: Heavy use ofanytypes reduces type safety.The wallet interaction uses multiple
anycasts (outcome: any,walletAny,(lastOutcome as any)). While this is understandable given wallet API variations, it removes TypeScript's ability to catch errors.Consider defining a minimal interface for the expected wallet methods and outcome structure to restore some type safety without requiring full typing of all wallet implementations.
| const txHash = outcome?.transaction?.hash || outcome?.transaction_outcome?.id || null; | ||
| const donation = providers.getTransactionLastResult(outcome) as CampaignDonation; | ||
|
|
||
| return { donation, txHash }; |
There was a problem hiding this comment.
Missing null check on donation result.
providers.getTransactionLastResult(outcome) can return undefined if the transaction outcome doesn't have a successful result. This would cause donation to be undefined, which doesn't match the DonateResult type where donation is required (non-nullable CampaignDonation).
🛡️ Add validation
const txHash = outcome?.transaction?.hash || outcome?.transaction_outcome?.id || null;
const donation = providers.getTransactionLastResult(outcome) as CampaignDonation;
+ if (!donation) {
+ throw new Error("Unable to retrieve donation result from transaction.");
+ }
+
return { donation, txHash };📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const txHash = outcome?.transaction?.hash || outcome?.transaction_outcome?.id || null; | |
| const donation = providers.getTransactionLastResult(outcome) as CampaignDonation; | |
| return { donation, txHash }; | |
| const txHash = outcome?.transaction?.hash || outcome?.transaction_outcome?.id || null; | |
| const donation = providers.getTransactionLastResult(outcome) as CampaignDonation; | |
| if (!donation) { | |
| throw new Error("Unable to retrieve donation result from transaction."); | |
| } | |
| return { donation, txHash }; |
🤖 Prompt for AI Agents
In `@src/common/contracts/core/campaigns/client.ts` around lines 169 - 172,
providers.getTransactionLastResult(outcome) may return undefined so you must
validate its return before casting to CampaignDonation; in the code around the
symbols outcome, providers.getTransactionLastResult and the returned
DonateResult, check the result and if it's undefined either throw a clear error
(e.g. throw new Error("missing transaction result for txHash: ...")) or return a
value that matches the DonateResult contract (do not leave donation undefined).
Ensure the code uses the computed txHash when building the error/return so the
failure can be traced.
Summary by CodeRabbit