-
Notifications
You must be signed in to change notification settings - Fork 3
feat: referral code #555
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
feat: referral code #555
Changes from all commits
5311b22
d53d0d7
c430ed1
f3a3149
839f93f
cdfc058
69c663d
52d21d3
7564273
9cd6872
79ee309
90ea1d7
efddfd6
420ad2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import { type NextRequest, NextResponse } from 'next/server'; | ||
| import { isAddress } from 'viem'; | ||
| import { callDataApiInternal } from '@/utils/dataApiInternal'; | ||
|
|
||
| const TX_HASH_PATTERN = /^0x[0-9a-fA-F]{64}$/; | ||
|
|
||
| export async function POST(request: NextRequest) { | ||
| let body: unknown; | ||
|
|
||
| try { | ||
| body = await request.json(); | ||
| } catch { | ||
| return NextResponse.json({ error: 'Invalid JSON body.' }, { status: 400 }); | ||
| } | ||
|
|
||
| if (!isRecord(body)) { | ||
| return NextResponse.json({ error: 'Invalid JSON body.' }, { status: 400 }); | ||
| } | ||
|
|
||
| const userWallet = readString(body.userWallet); | ||
| const chainId = typeof body.chainId === 'number' ? body.chainId : Number.NaN; | ||
| const txHash = readString(body.txHash); | ||
| const source = readString(body.source); | ||
| const tokenAddress = readString(body.tokenAddress); | ||
| const amountRaw = readString(body.amountRaw); | ||
|
|
||
| if ( | ||
| !userWallet || | ||
| !isAddress(userWallet) || | ||
| !Number.isInteger(chainId) || | ||
| !txHash || | ||
| !TX_HASH_PATTERN.test(txHash) || | ||
| !source || | ||
| !tokenAddress || | ||
| !isAddress(tokenAddress) || | ||
| !amountRaw || | ||
| !/^[0-9]+$/.test(amountRaw) || | ||
| BigInt(amountRaw) <= 0n | ||
| ) { | ||
| return NextResponse.json({ error: 'Invalid platform fee request.' }, { status: 400 }); | ||
| } | ||
|
|
||
| try { | ||
| const response = await callDataApiInternal('/internal/platform-fees', { | ||
| userWallet, | ||
| chainId, | ||
| txHash, | ||
| source, | ||
| tokenAddress, | ||
| amountRaw, | ||
| }); | ||
| const data = (await response.json().catch(() => ({}))) as { error?: unknown }; | ||
|
|
||
| if (!response.ok) { | ||
| return NextResponse.json( | ||
| { error: typeof data.error === 'string' ? data.error : 'Failed to record platform fee.' }, | ||
| { status: response.status || 502 }, | ||
| ); | ||
| } | ||
|
|
||
| return NextResponse.json(data); | ||
| } catch { | ||
| return NextResponse.json({ error: 'Failed to record platform fee.' }, { status: 500 }); | ||
| } | ||
| } | ||
|
|
||
| function readString(value: unknown): string | null { | ||
| return typeof value === 'string' && value.trim() ? value.trim() : null; | ||
| } | ||
|
|
||
| function isRecord(value: unknown): value is Record<string, unknown> { | ||
| return typeof value === 'object' && value !== null && !Array.isArray(value); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,64 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { type NextRequest, NextResponse } from 'next/server'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { isAddress } from 'viem'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { callDataApiInternal } from '@/utils/dataApiInternal'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+3
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Import
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const TX_HASH_PATTERN = /^0x[0-9a-fA-F]{64}$/; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function POST(request: NextRequest) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let body: unknown; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body = await request.json(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: 'Invalid JSON body.' }, { status: 400 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isRecord(body)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: 'Invalid JSON body.' }, { status: 400 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const referredWallet = readString(body.referredWallet); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const referralCode = readString(body.referralCode); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const chainId = typeof body.chainId === 'number' ? body.chainId : Number.NaN; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const txHash = readString(body.txHash); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !referredWallet || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !isAddress(referredWallet) || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !referralCode || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !Number.isInteger(chainId) || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !txHash || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| !TX_HASH_PATTERN.test(txHash) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: 'Invalid referral attribution request.' }, { status: 400 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+20
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Normalize the referred wallet and transaction hash to lowercase. Use the shared
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await callDataApiInternal('/internal/referrals/attribute', { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| referredWallet, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| referralCode, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chainId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| txHash, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const data = (await response.json().catch(() => ({}))) as { error?: unknown }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!response.ok) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { error: typeof data.error === 'string' ? data.error : 'Failed to record referral attribution.' }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { status: response.status || 502 }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json(data); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return NextResponse.json({ error: 'Failed to record referral attribution.' }, { status: 500 }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function readString(value: unknown): string | null { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return typeof value === 'string' && value.trim() ? value.trim() : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function isRecord(value: unknown): value is Record<string, unknown> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return typeof value === 'object' && value !== null && !Array.isArray(value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
Normalize the wallet, token address, and transaction hash to lowercase to prevent case-sensitivity mismatches in the database. Additionally, validate the length of the
sourceparameter to prevent excessively long strings from being processed.