Feat: Create Backend API Endpoint for Fundraising Campaign Creation#13
Feat: Create Backend API Endpoint for Fundraising Campaign Creation#13gelluisaac wants to merge 5 commits intoFundable-Protocol:devfrom
Conversation
WalkthroughThis update introduces a comprehensive campaign creation feature, including blockchain integration with StarkNet, input validation, rate limiting, authentication, and database persistence. New middleware, utility modules, entity definitions, and TypeScript types are added. The routing structure is refactored to support campaign endpoints, and several supporting packages and ABI files are introduced. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as Express API
participant Auth as Auth Middleware
participant Rate as Rate Limit Middleware
participant Validator as Validator
participant DB as Database
participant Blockchain as StarkNet Service
Client->>API: POST /campaigns (with JWT)
API->>Auth: Authenticate JWT
Auth-->>API: Attach req.user or 401 error
API->>Rate: Check rate limit (user ID)
Rate-->>API: Allow or 429 error
API->>Validator: Validate input (Joi)
Validator-->>API: Validation result
API->>DB: Check campaign_ref uniqueness
DB-->>API: Uniqueness result
API->>Blockchain: Verify donation_token contract
Blockchain-->>API: Verification result
API->>Blockchain: Get user wallet balance
Blockchain-->>API: Balance result
API->>Blockchain: Create campaign on-chain
Blockchain-->>API: Transaction receipt or error
API->>DB: Save campaign metadata
DB-->>API: Save result
API->>DB: Log user campaign request
DB-->>API: Log result
API-->>Client: 201 Created (campaign details) or error
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related issues
Suggested reviewers
Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 18
🧹 Nitpick comments (3)
src/components/v1/routes.v1.ts (1)
5-5: Fix the typo in distributions import path.There's a typo in the existing import:
distrubtion.routesshould bedistribution.routes.-import distributionRoutes from "./distribution/distrubtion.routes" +import distributionRoutes from "./distribution/distribution.routes"src/components/v1/campaigns.controller.ts (1)
75-75: Initialize variables with proper typesVariables should be initialized or properly typed to avoid potential issues.
- let campaign_id, transaction_hash; + let campaign_id: string; + let transaction_hash: string;src/utils/starknetService.ts (1)
11-11: Consider making RPC endpoint configurableThe hardcoded public RPC endpoint may have rate limits and reliability issues. Consider using an environment variable.
const provider = new RpcProvider({ - nodeUrl: 'https://starknet-mainnet.public.blastapi.io/rpc/v0_6', // Example public mainnet RPC endpoint + nodeUrl: process.env.STARKNET_RPC_URL || 'https://starknet-mainnet.public.blastapi.io/rpc/v0_6', });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (10)
package.json(1 hunks)src/components/v1/campaigns.controller.ts(1 hunks)src/components/v1/campaigns.db.ts(1 hunks)src/components/v1/campaigns.routes.ts(1 hunks)src/components/v1/campaigns.validation.ts(1 hunks)src/components/v1/routes.v1.ts(1 hunks)src/types/express/index.d.ts(1 hunks)src/utils/blockchainUtils.ts(1 hunks)src/utils/helper.ts(1 hunks)src/utils/starknetService.ts(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/types/express/index.d.ts (4)
src/types/global.ts (2)
IRequest(79-89)IToken(23-31)src/components/v1/user/user.entity.ts (1)
UserEntity(6-36)src/migrations/CreateCoreEntities.js (1)
up(6-94)src/components/v1/wallet/wallet.entity.ts (1)
WalletEntity(8-55)
src/utils/blockchainUtils.ts (2)
src/components/v1/wallet/wallet.entity.ts (1)
WalletEntity(8-55)src/migrations/CreateCoreEntities.js (1)
up(6-94)
🔇 Additional comments (7)
src/components/v1/routes.v1.ts (2)
6-6: LGTM: Campaign routes import follows established pattern.The import statement is correctly structured and consistent with other route imports in the file.
13-13: LGTM: Campaign routes registration is properly implemented.The route registration follows the same pattern as other routes and correctly maps campaigns to the
/campaignspath prefix.src/types/express/index.d.ts (1)
10-12: No Conflicts: GlobalRequestAugmentation andIRequestCoexist CleanlyThe declaration in
src/types/express/index.d.tsaugmentsExpress.Requestwithuser: User, which lets untyped middleware (e.g. rate-limit’skeyGenerator) safely accessreq.user. Your existingIRequestinsrc/types/global.tsthen extends that already-augmentedRequestwith its own properties (decoded?,fingerprint?,Wallet?).–
src/types/express/index.d.ts: mergesuser: Userinto everyExpress.Request
–src/types/global.ts: exportsIRequest extends Requestto add session-specific fields
– Middleware/controllers correctly typereq: IRequestand non-typed code still seesreq.userThese two patterns layer without conflict. No changes needed here.
src/components/v1/campaigns.routes.ts (1)
24-24: Route path looks correctThe route path '/campaigns' is correct as it will be mounted under the version prefix in the main router, resulting in the full path '/api/v1/campaigns' as specified in the PR objectives.
src/components/v1/campaigns.validation.ts (1)
36-36: Contract address validation pattern is correctThe regex pattern correctly validates StarkNet contract addresses with 0x prefix and 64 hexadecimal characters.
src/components/v1/campaigns.controller.ts (1)
119-124: Audit log implementation looks goodThe audit logging for campaign creation with relevant details is well-implemented and follows best practices.
src/utils/starknetService.ts (1)
35-39: Verify the contract method call parametersThe contract call should match the expected Cairo contract interface exactly.
Please verify that the
create_campaignmethod in your Cairo contract expects these exact parameters in this order:
campaign_ref(string)target_amount(U256 struct)donation_token(contract address)Also ensure the method name matches exactly (case-sensitive).
|
@mubarak23 can i get the contract abi so i can push with the code rabbit review? with the contract address so i can test |
There was a problem hiding this comment.
Actionable comments posted: 12
♻️ Duplicate comments (2)
src/types/express/index.d.ts (1)
1-12: User type consolidation still needed.This implementation correctly extends the Express Request interface, but the previous review comment about consolidating multiple user types across the codebase remains valid.
src/utils/starknetService.ts (1)
120-124: Private key management still needs implementationThis placeholder must be replaced with secure key management before production deployment.
🧹 Nitpick comments (2)
src/components/v1/campaigns/campaigns.db.ts (2)
4-4: Fix import formatting and organization.Missing space after comma in import statement, and imports should be organized at the top of the file.
-import { Campaign} from './campaign.entity' +import { Campaign } from './campaign.entity';
14-15: Move imports to the top of the file.Imports should be organized at the beginning of the file for better readability.
+import { CampaignData } from '../../../types/campaign'; // Check if campaign_ref is unique export async function isCampaignRefUnique(campaign_ref: string): Promise<boolean> { // ... function body } // Save campaign metadata to DB -import { CampaignData } from '../../../types/campaign';
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (16)
package.json(1 hunks)src/appMiddlewares/auth.middleware.ts(1 hunks)src/appMiddlewares/rateLimit.middleware.ts(1 hunks)src/components/v1/campaigns/campaign.entity.ts(1 hunks)src/components/v1/campaigns/campaigns.controller.ts(1 hunks)src/components/v1/campaigns/campaigns.db.ts(1 hunks)src/components/v1/campaigns/campaigns.routes.ts(1 hunks)src/components/v1/campaigns/campaigns.validation.ts(1 hunks)src/components/v1/campaigns/routes.v1.ts(1 hunks)src/index.ts(1 hunks)src/types/auth.ts(1 hunks)src/types/campaign/index.ts(1 hunks)src/types/express/index.d.ts(1 hunks)src/utils/blockchainUtils.ts(1 hunks)src/utils/distributor.abi.json(1 hunks)src/utils/starknetService.ts(1 hunks)
✅ Files skipped from review due to trivial changes (6)
- src/types/auth.ts
- src/index.ts
- src/components/v1/campaigns/routes.v1.ts
- src/components/v1/campaigns/campaign.entity.ts
- src/types/campaign/index.ts
- src/utils/distributor.abi.json
🚧 Files skipped from review as they are similar to previous changes (1)
- package.json
🧰 Additional context used
🧬 Code Graph Analysis (5)
src/appMiddlewares/rateLimit.middleware.ts (1)
src/components/v1/campaigns/campaigns.db.ts (2)
getUserCampaignCountLastHour(57-62)saveUserCampaignRequest(51-55)
src/types/express/index.d.ts (1)
src/appMiddlewares/auth.middleware.ts (1)
AuthUser(6-9)
src/utils/starknetService.ts (1)
src/types/campaign/index.ts (3)
CreateCampaignParams(16-21)TransactionReceipt(28-31)StarknetError(33-37)
src/appMiddlewares/auth.middleware.ts (1)
src/types/auth.ts (1)
AuthUser(2-6)
src/components/v1/campaigns/campaigns.db.ts (2)
dist/config/persistence/data-source.js (1)
AppDataSource(18-40)src/types/campaign/index.ts (1)
CampaignData(1-9)
🔇 Additional comments (14)
src/components/v1/campaigns/campaigns.controller.ts (5)
12-24: Input validation implementation looks good!The validation is properly structured with clear error responses and appropriate status codes.
26-38: Campaign reference uniqueness check is well implemented!Proper use of 409 Conflict status for duplicate references with clear error messaging.
62-82: Excellent two-stage token validation!Good practice to validate address format before making network calls to verify the contract on-chain.
84-119: Robust blockchain interaction with comprehensive error handling!Excellent error mapping from contract-specific errors to appropriate HTTP status codes.
140-151: Proper success response structure!Correct use of HTTP 201 status with comprehensive campaign data in the response.
src/components/v1/campaigns/campaigns.validation.ts (3)
4-15: Well-structured campaign reference validation!Good use of trim, length validation, and regex to ensure no whitespace.
35-42: Correct StarkNet address validation!The regex pattern accurately validates StarkNet contract address format.
16-34: Add error handling for BigInt conversionThe BigInt conversion could throw if the string is invalid, even with the pattern check.
.custom((value, helpers) => { + try { if (BigInt(value) <= 0n) { return helpers.error('any.invalid'); } // Optionally, check if value fits u256 if (BigInt(value) > (2n ** 256n - 1n)) { return helpers.error('any.invalid'); } return value; + } catch (error) { + return helpers.error('any.invalid'); + } }, 'u256 validation')Likely an incorrect or invalid review comment.
src/utils/starknetService.ts (4)
5-19: Great job addressing the previous review comments!The CONTRACT_ADDRESS environment variable validation and ABI loading error handling have been properly implemented.
37-75: Excellent retry logic implementation!Well-structured exponential backoff with jitter and selective retry based on error types.
102-117: Error mapping properly implemented!Good coverage of common StarkNet error patterns with structured error responses.
90-99: I don’t see any custom parsing of raw StarkNet event data into named fields likecampaign_id. TheTransactionReceiptandCampaignEventtypes assumedata.campaign_idis already a string, but StarkNet’s raw receipts encode event data as hex‐felt arrays. You’ll need to surface where you deserialize those raw events into yourCampaignEventshape and ensure you convert any hex/felt values to human‐readable strings (e.g. via BigInt(hex).toString()). Otherwise,extractCampaignIdFromReceiptmay return hex prefixed IDs or throw if the raw data isn’t pre‐converted.Please:
• Locate the code that transforms StarkNet’s raweventsarray into yourCampaignEvent.dataobject.
• Add explicit handling for hex/felt values (e.g.BigInt(hexValue).toString()).
• Update tests (or add new ones) to cover a hex‐encodedcampaign_idcase.src/utils/blockchainUtils.ts (2)
30-47: Excellent implementation addressing previous concerns!The function now properly queries the blockchain for balance with safe error handling that returns zero balance on failure.
50-59: Token verification properly implemented!Good approach using the symbol method call to verify ERC20 contracts, addressing the previous security concern.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/appMiddlewares/rateLimit.middleware.ts (1)
16-30: Race condition still exists despite previous feedback.While the premature logging issue has been addressed, the race condition mentioned in previous reviews still exists. Multiple concurrent requests can pass the count check simultaneously before any database updates occur.
Consider implementing atomic operations or using Redis with
INCRandEXPIREfor more reliable rate limiting:// Alternative approach using database-level atomic operations const count = await AppDataSource.transaction(async manager => { const repo = manager.getRepository(CampaignRequest); const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000); const currentCount = await repo.count({ where: { user_id: userId, created_at: MoreThan(oneHourAgo) } }); if (currentCount >= 5) { throw new Error('RATE_LIMIT_EXCEEDED'); } return currentCount; });
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/appMiddlewares/auth.middleware.ts(1 hunks)src/appMiddlewares/rateLimit.middleware.ts(1 hunks)src/components/v1/campaigns/campaignRequest.entity.ts(1 hunks)src/components/v1/campaigns/campaigns.controller.ts(1 hunks)src/components/v1/campaigns/campaigns.db.ts(1 hunks)src/components/v1/campaigns/campaigns.routes.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- src/appMiddlewares/auth.middleware.ts
- src/components/v1/campaigns/campaigns.routes.ts
- src/components/v1/campaigns/campaigns.db.ts
- src/components/v1/campaigns/campaigns.controller.ts
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/appMiddlewares/rateLimit.middleware.ts (1)
src/components/v1/campaigns/campaigns.db.ts (1)
getUserCampaignCountLastHour(64-73)
🔇 Additional comments (2)
src/appMiddlewares/rateLimit.middleware.ts (2)
31-41: LGTM - Error handling is properly implemented.The error handling correctly catches database failures and returns appropriate error responses with consistent structure.
6-15: LGTM - Authentication check is properly implemented.The middleware correctly validates user authentication and returns appropriate 401 responses when authentication is missing.
|
@mubarak23 Pr is ready for review |
This PR implements the backend API endpoint that interfaces with our Cairo smart contract's create_campaign function. The new endpoint handles campaign creation requests from the frontend and interacts with the StarkNet blockchain to deploy new fundraising campaigns.
Features Implemented
API Endpoint
Method: POST
Path: /api/v1/campaigns
Authentication: JWT token required
Rate Limiting: 5 campaigns per user per hour
Request/Response Schema
Request Body:
{
"campaign_ref": "string",
"target_amount": "string",
"donation_token": "string"
}
Success Response:
{
"success": true,
"data": {
"campaign_id": "string",
"campaign_ref": "string",
"target_amount": "string",
"donation_token": "string",
"transaction_hash": "string",
"created_at": "ISO 8601 timestamp"
}
}
Error Response:
{
"success": false,
"error": {
"code": "string",
"message": "string",
"details": {}
}
}
Acceptance Criteria Fulfilled
Core Functionality
API endpoint accepts valid campaign creation requests
Successfully interfaces with Cairo smart contract
Returns proper campaign ID from blockchain transaction
Implements all required validations
Handles all error scenarios gracefully
Includes comprehensive logging
Rate limiting implemented (5 campaigns per user per hour)
Summary by CodeRabbit
New Features
Chores
Documentation