Add referral fee support (Legacy Swap) + live E2E scripts, docs, and config#3
Add referral fee support (Legacy Swap) + live E2E scripts, docs, and config#3MonteCrypto999 wants to merge 17 commits intoelizaos-plugins:1.xfrom
Conversation
…ample; refine fee receiver ATA checks; ignore local-e2e
WalkthroughAdds referral-fee support and related tooling: environment-driven referral configuration, fee-mint selection and fee-account derivation, referral-aware quote and swap flows, tests, an E2E mainnet script, expanded docs, build entry, and CI/release workflow updates. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant E2EScript as E2E Script
participant JupiterService
participant Jupiter as Jupiter API
participant Solana as Solana RPC
User->>E2EScript: Run with env vars
E2EScript->>E2EScript: Load config (mints, amount, keys)
E2EScript->>JupiterService: Instantiate
rect rgb(200,220,255)
Note over E2EScript,JupiterService: Quote Phase
E2EScript->>JupiterService: getQuote()
JupiterService->>Jupiter: HTTP GET (includes platformFeeBps if enabled)
Jupiter-->>JupiterService: Quote response
JupiterService-->>E2EScript: Quote (outAmount, platformFee)
end
rect rgb(200,255,220)
Note over E2EScript,JupiterService: Swap Phase
E2EScript->>JupiterService: executeSwap(quote)
JupiterService->>JupiterService: selectFeeMint() / deriveFeeAccount()
JupiterService->>Jupiter: HTTP POST (swap + optional feeAccount)
Jupiter-->>JupiterService: Swap transaction (base64)
JupiterService-->>E2EScript: Swap transaction
end
rect rgb(255,240,200)
Note over E2EScript,Solana: Simulation & Signing
E2EScript->>Solana: simulateTransaction()
Solana-->>E2EScript: Simulation result/logs
alt SIGNER_SECRET_KEY provided
E2EScript->>E2EScript: Sign locally
alt SEND_TX enabled
E2EScript->>Solana: sendTransaction()
Solana-->>E2EScript: Tx signature
else
E2EScript->>E2EScript: Log signed tx only
end
else
E2EScript->>E2EScript: Log unsigned tx warning
end
end
sequenceDiagram
participant Client
participant JupiterService
participant Referral as Referral Logic
Client->>JupiterService: getQuote()/executeSwap()
JupiterService->>Referral: loadReferralConfig()
Referral-->>JupiterService: {enabled, feeBps, mode}
alt Referral enabled
JupiterService->>JupiterService: include platformFeeBps in requests
JupiterService->>Referral: selectFeeMint(pair, mode)
Referral-->>JupiterService: {mint, feeAccount?}
alt feeAccount needed
JupiterService->>Referral: deriveFeeAccount(mint)
Referral-->>JupiterService: feeAccount (ATA)
JupiterService->>JupiterService: attach feeAccount to swap
else
JupiterService->>JupiterService: proceed without feeAccount
end
else
JupiterService->>JupiterService: omit referral fields
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~70 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 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 |
There was a problem hiding this comment.
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)
src/service.ts (3)
68-94: Add HTTP timeouts to avoid hanging requests.fetch has no timeout. Use AbortController and pass a per-try timeout; reuse in swap as well.
-async function getQuoteWithRetry(url: string, retries = 3, delay = 2000) { +async function getQuoteWithRetry(url: string, retries = 3, delay = 2000, timeoutMs = 10_000) { for (let i = 0; i < retries; i++) { - const response = await fetch(url); + const c = new AbortController(); + const to = setTimeout(() => c.abort(), timeoutMs); + const response = await fetch(url, { signal: c.signal }).finally(() => clearTimeout(to));Replicate for getSwapWithRetry.
159-165: Avoid logging full swap payloads. Potential PII/log bloat.payload.body can be large and contain user public keys. Log minimal fields or gate under a DEBUG flag.
- console.log('jupSrv - swap', payload.body) + logger.info('jupSrv - swap request enqueued');
262-268: Don’t return false on invalid input; throw a typed error.getQuote currently returns false, creating a union type with objects and risking downstream crashes. Throw a clear error.
- if (isNaN(intAmount) || intAmount <= 0) { - console.warn('jupiter::getQuote - Amount in', amount, 'become', intAmount) - return false - } + if (!Number.isFinite(intAmount) || intAmount <= 0) { + throw new TypeError(`Invalid amount: ${amount}`); + }
🧹 Nitpick comments (19)
src/__tests__/referral.test.ts (1)
1-25: Consider enhancing or removing these basic tests.These tests only verify that
process.envcan get and set values, which is standard JavaScript behavior. The comments (lines 3-5) acknowledge that actual referral logic testing happens in integration tests.Consider either:
- Removing this file if integration tests already provide adequate coverage
- Expanding to test actual referral config parsing/validation logic (e.g., invalid BPS values, invalid modes, edge cases)
env.example (2)
4-5: Consider documenting the valid BPS range.While 20 BPS (0.2%) is a reasonable example, it would be helpful to document the valid range. Based on the PR description mentioning "0-10%", the valid range appears to be 0-1000 BPS.
Consider adding a comment:
# Platform fee in basis points. Example: 20 = 0.2% +# Valid range: 0-1000 (0% - 10%) REFERRAL_FEE_BPS=20
19-19: Security reminder for secret key handling.The example correctly shows the JSON array format for secret keys. However, consider adding a warning comment about never committing actual keys.
Consider adding a security note:
# If you implement the recommended backend endpoint, you may need: # SOLANA_RPC_URL=https://api.mainnet-beta.solana.com +# WARNING: Never commit actual secret keys to version control # FEE_PAYER_SECRET_KEY=[1,2,3,...,64]Also applies to: 29-29
README.md (2)
98-135: Verify error handling in the backend endpoint example.The backend endpoint example is helpful, but consider these improvements:
- Validation: The endpoint checks for missing
receiverandmintbut doesn't validate that they are valid Solana public keys- Error context: Generic error message
e?.message ?? 'unknown error'could be more informative- Security note: Missing warning about securing this endpoint (authentication, rate limiting)
Consider enhancing the example with:
app.post('/api/fee-ata/ensure', async (req, res) => { try { const { receiver, mint } = req.body; if (!receiver || !mint) return res.status(400).json({ error: 'receiver and mint are required' }); + + // Validate public keys + try { + new PublicKey(receiver); + new PublicKey(mint); + } catch { + return res.status(400).json({ error: 'Invalid public key format' }); + } const connection = new Connection(RPC_URL, 'confirmed');And add a security note before the endpoint code:
### Recommended backend endpoint (optional) For production setups, we recommend a backend endpoint to ensure the fee receiver has the required ATA before swaps: + +⚠️ **Security Note**: In production, add authentication and rate limiting to this endpoint.
173-200: Verify the E2E setup instructions are complete.The testing section is comprehensive, but the E2E instructions assume the user will have the necessary infrastructure:
- The E2E script requires a funded signer wallet (SIGNER_SECRET_KEY) but doesn't mention funding requirements
- No mention of how to obtain/set up HELIUS_RPC_URL (free tier? paid?)
- The example uses 0.005 SOL - might be helpful to mention approximate USD cost
Consider adding a note before the E2E instructions:
# Live E2E (example using .env.example), with optional simulation/signing +# Note: E2E requires a funded wallet (~0.005-0.01 SOL) and Helius RPC access (free tier available) # 1) Copy .env.example to .env and set:src/types.ts (1)
34-40: Consider adding JSDoc for the valid range offeeBps.The
ReferralConfiginterface looks good, but thefeeBpsfield would benefit from documentation about its valid range. Based on the PR description and README, the valid range is 0-1000 (representing 0% to 10%).Consider adding JSDoc:
+/** + * Fee configuration for referral rewards + * @property enabled - Whether referral fees are enabled + * @property feeBps - Fee in basis points (valid range: 0-1000, representing 0%-10%) + * @property mode - Fee collection mode + */ export interface ReferralConfig { enabled: boolean; feeBps: number; mode: FeeMode; }src/__tests__/integration.test.ts (5)
21-27: Gate LIVE tests to avoid flaky CI runs.These integration tests hit real Jupiter endpoints and mainnet. Gate the suite behind an env flag (e.g., RUN_LIVE_TESTS=1) to skip by default.
Apply:
-describe('Jupiter Service Integration (LIVE)', () => { +const RUN_LIVE = process.env.RUN_LIVE_TESTS === '1'; +(RUN_LIVE ? describe : describe.skip)('Jupiter Service Integration (LIVE)', () => {
49-61: Simplify the “referral disabled” assertion.Use a direct undefined/null check to avoid redundant comparison.
- expect(quote.platformFee == null || quote.platformFee === null).toBe(true); + expect(quote.platformFee == null).toBe(true);
84-96: Ensure env cleanup between tests.Some cases set REFERRAL_FEE_RECEIVER; others don’t. Add afterEach to restore a captured baseline env snapshot to avoid cross-test leakage.
+const ENV_SNAPSHOT = { ...process.env }; +afterEach(() => { + process.env = { ...ENV_SNAPSHOT }; +});
100-116: Strengthen fee-mode assertions.For sol_only, also assert that no feeAccount is attempted when SOL isn’t in the pair (or that logs indicate fallback). Today the test only checks swapTransaction truthiness.
- Consider mocking executeSwap’s HTTP call to assert feeAccount omission for USDC-USDT.
134-165: Live custom-mint test: guard and reduce blast radius.This calls mainnet with an arbitrary mint. Keep under RUN_LIVE_TESTS, lower the amount further (e.g., 10_000) and optionally add retries/backoff locally to reduce flakiness.
src/scripts/e2e-mainnet.ts (2)
27-37: Surface referral config explicitly in logs.Small UX: log whether referral is active and which mint mode is used to ease troubleshooting.
- console.log('E2E Mainnet - Params', { inputMint, outputMint, amount, slippageBps, feeBps, mode, receiver }); + const referralEnabled = feeBps > 0; + console.log('E2E Mainnet - Params', { inputMint, outputMint, amount, slippageBps, referralEnabled, feeBps, mode, receiver });
64-81: Harden secret parsing and guard log noise.Validate SIGNER_SECRET_KEY format and avoid printing large structures or stack traces by default. Keep SEND_TX hard-gated.
- const parsed = JSON.parse(signerSecret); + let parsed: number[]; + try { parsed = JSON.parse(signerSecret) } catch { throw new Error('SIGNER_SECRET_KEY must be a JSON array of 64 bytes'); } const signer = Keypair.fromSecretKey(Uint8Array.from(parsed));src/service.ts (6)
29-48: Fee mint selection: document exact behavior and ensure WSOL constant reuse.Logic looks fine; add brief docs on ExactIn/ExactOut behavior if relevant, and reuse WSOL_MINT constant consistently across the file to avoid hard-coded duplicates.
53-66: Optionally verify ATA existence to fail fast.Derivation succeeds even if the ATA doesn’t exist; swaps will later fail. Consider an optional preflight check against RPC to ensure the ATA exists when referral is enabled, and surface a clear warning/error.
Example (pseudo):
// if (checkExists) { const info = await connection.getAccountInfo(ata); if (!info) warn(...) }
96-142: Background queue starts at module load.checkQuoteQueues and checkSwapQueues are launched globally, which creates side effects on import and perpetual timers. Move queue management into JupiterService.start()/stop() or guard with a singleton start flag.
-// start checking queues -checkQuoteQueues() +let queuesStarted = false; +export function startQueues() { + if (!queuesStarted) { queuesStarted = true; checkQuoteQueues(); } +}Then call startQueues() from JupiterService.start().
295-324: Magic constants and duplicate WSOL literal.Hard-coded 2,039,280 lamports and repeated WSOL literal can drift. Hoist WSOL_MINT usage and make rent estimate configurable or documented with a reference.
- const isWrappedSol = initialQuote.inputMint === "So11111111111111111111111111111111111111112"; + const isWrappedSol = initialQuote.inputMint === WSOL_MINT;
347-374: feeAccount derivation: handle missing REFERRAL_FEE_RECEIVER and ExactOut constraints.You warn when receiver is missing, but still continue silently. Consider failing fast (or disabling fees) based on an env toggle. Also, if swap mode is ExactOut and the selected fee mint conflicts with constraints, skip adding feeAccount to honor constraints (documented in PR summary).
- Add: read quoteResponse.swapMode and skip feeAccount if ExactOut and selected mint isn’t compatible.
- Add: env flag REFERRAL_STRICT=1 to error when receiver is missing.
427-434: Propagate referral flag consistently through helper paths.getTokenPrice (and other helpers) call getQuote with default slippage; ensure referral config is respected (it is via getQuote), and consider allowing an override to disable referral fees in these utility methods to avoid polluting analytics.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (11)
.gitignore(1 hunks)README.md(1 hunks)env.example(1 hunks)package.json(1 hunks)src/__tests__/integration.test.ts(1 hunks)src/__tests__/referral.test.ts(1 hunks)src/index.ts(1 hunks)src/scripts/e2e-mainnet.ts(1 hunks)src/service.ts(17 hunks)src/types.ts(1 hunks)tsup.config.ts(1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md
[style] ~137-~137: ‘prior to’ might be wordy. Consider a shorter alternative.
Context: ...')); Call it from your ops/scripts prior to swaps, e.g.: bash curl -X POST http...
(EN_WORDINESS_PREMIUM_PRIOR_TO)
🔇 Additional comments (11)
.gitignore (1)
1-7: LGTM!The ignore patterns appropriately exclude build artifacts, dependencies, environment files, and local testing directories. The inclusion of various
.env*patterns follows security best practices for preventing sensitive configuration from being committed.package.json (2)
12-14: LGTM!The devDependencies additions are appropriate for the new E2E and simulation capabilities. Both
dotenvfor environment configuration andtsxfor executing TypeScript directly are standard choices.
21-22: LGTM!The new scripts are well-structured:
e2eruns the compiled E2E mainnet scriptlocal:simusestsxto execute TypeScript directly for faster local developmenttsup.config.ts (1)
4-4: LGTM!Adding the E2E mainnet script as a build entry point is appropriate and enables the
npm run e2escript to execute the compiled output.src/index.ts (1)
29-29: LGTM!Publicly exporting
JupiterServiceenables consumers to directly instantiate and use the service, which aligns with the enhanced API reference documentation in the README.README.md (4)
1-14: LGTM!The Features section provides a clear, concise overview of the plugin's capabilities, including the new referral fee functionality with well-defined fee modes.
37-62: LGTM!The configuration documentation clearly explains the referral fee setup with practical examples. The fee mode comparison (with ✅/❌ indicators) makes the behavior immediately understandable.
148-171: LGTM!The API Reference provides clear, practical examples with appropriate comments. The code snippets demonstrate both quote fetching and swap execution flows.
218-222: LGTM!The References section provides relevant links to Jupiter and ElizaOS documentation, giving users clear paths to deeper information.
src/types.ts (1)
42-45: LGTM!The
FeeMintSelectioninterface appropriately models the fee mint and optional fee account (ATA might not exist yet). The optionalfeeAccountaligns with the documented behavior where swaps proceed without fees if the account doesn't exist.src/__tests__/integration.test.ts (1)
28-47: The original review comment is incorrect—keep the existing assertion.Jupiter's lite quote endpoint does return
platformFee.feeBpsin the response whenplatformFeeBpsis passed in the query. The test assertionexpect(quote.platformFee?.feeBps).toBe(20)is not brittle; it reliably tests the guaranteed API contract. The suggested refactor to check only presence or amount is unnecessary. No changes are needed.Likely an incorrect or invalid review comment.
| describe('Swap with Fee Account', () => { | ||
| it('should include feeAccount when referral is enabled', async () => { | ||
| process.env.REFERRAL_FEE_BPS = '20'; | ||
| process.env.REFERRAL_MODE = 'smart'; | ||
| process.env.REFERRAL_FEE_RECEIVER = '11111111111111111111111111111111'; | ||
|
|
||
| const quote: any = await service.getQuote({ inputMint: WSOL_MINT, outputMint: USDC_MINT, amount: 100000, slippageBps: 50 }); | ||
|
|
||
| const swap = await service.executeSwap({ | ||
| quoteResponse: quote, | ||
| userPublicKey: '11111111111111111111111111111111', | ||
| slippageBps: 50, | ||
| }); | ||
| expect((swap as any).swapTransaction).toBeTruthy(); | ||
|
|
||
| delete process.env.REFERRAL_FEE_BPS; | ||
| delete process.env.REFERRAL_MODE; | ||
| delete process.env.REFERRAL_FEE_RECEIVER; | ||
| }); | ||
|
|
There was a problem hiding this comment.
Test name vs assertion mismatch.
Name says “include feeAccount when referral is enabled,” but the test doesn’t assert feeAccount presence. Either assert that the request body carried feeAccount (via a stubbed/mocked swap request) or rename the test to reflect what’s actually verified.
- Option A: stub swap endpoint to capture payload and assert feeAccount is set.
- Option B: rename to “should draft a swap when referral is enabled.”
🤖 Prompt for AI Agents
In src/__tests__/integration.test.ts around lines 64 to 83, the test title
claims it verifies a feeAccount is included when referral is enabled but the
assertions never check for feeAccount; either (A) stub or mock the swap endpoint
to capture the outgoing request payload and add an assertion that payload
contains the expected feeAccount (REFERRAL_FEE_RECEIVER) when REFERRAL_* env
vars are set, or (B) if you don’t want to assert payloads, change the test name
to something accurate like "should draft a swap when referral is enabled" and
keep the existing assertions; implement one of these fixes and remove the unused
env var cleanup or leave it as-is.
| const userPublicKey = process.env.E2E_USER_PUBLIC_KEY || receiver || '11111111111111111111111111111111'; | ||
|
|
||
| const swap = await service.executeSwap({ | ||
| quoteResponse: quote as any, | ||
| userPublicKey, | ||
| slippageBps, | ||
| }); |
There was a problem hiding this comment.
Require a realistic user public key or exit.
Defaulting to the system program (“111…”) for userPublicKey can yield unusable drafts. Fail early unless E2E_USER_PUBLIC_KEY is provided.
- const userPublicKey = process.env.E2E_USER_PUBLIC_KEY || receiver || '11111111111111111111111111111111';
+ const userPublicKey = process.env.E2E_USER_PUBLIC_KEY || receiver;
+ if (!userPublicKey) {
+ console.error('E2E_USER_PUBLIC_KEY (or REFERRAL_FEE_RECEIVER as fallback) is required.');
+ process.exit(1);
+ }📝 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 userPublicKey = process.env.E2E_USER_PUBLIC_KEY || receiver || '11111111111111111111111111111111'; | |
| const swap = await service.executeSwap({ | |
| quoteResponse: quote as any, | |
| userPublicKey, | |
| slippageBps, | |
| }); | |
| const userPublicKey = process.env.E2E_USER_PUBLIC_KEY || receiver; | |
| if (!userPublicKey) { | |
| console.error('E2E_USER_PUBLIC_KEY (or REFERRAL_FEE_RECEIVER as fallback) is required.'); | |
| process.exit(1); | |
| } | |
| const swap = await service.executeSwap({ | |
| quoteResponse: quote as any, | |
| userPublicKey, | |
| slippageBps, | |
| }); |
🤖 Prompt for AI Agents
In src/scripts/e2e-mainnet.ts around lines 39 to 45, the code currently falls
back to the system program key ('111...') when E2E_USER_PUBLIC_KEY is not set
which produces invalid drafts; change the logic to require a realistic user
public key by removing the '111...' fallback and the receiver fallback, and
instead check process.env.E2E_USER_PUBLIC_KEY at startup and if it's missing log
a clear error and exit (or throw) so the script fails early rather than
continuing with an invalid public key.
| function loadReferralConfig(): ReferralConfig { | ||
| const feeBps = parseInt(process.env.REFERRAL_FEE_BPS || '0', 10); | ||
| const mode = (process.env.REFERRAL_MODE || 'smart') as FeeMode; | ||
|
|
||
| return { | ||
| enabled: feeBps > 0, | ||
| feeBps, | ||
| mode, | ||
| }; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
Validate and clamp REFERRAL_FEE_BPS; sanitize mode.
Parse failures or out-of-range BPS can silently enable/disable fees. Clamp to a sane range and whitelist modes.
-function loadReferralConfig(): ReferralConfig {
- const feeBps = parseInt(process.env.REFERRAL_FEE_BPS || '0', 10);
- const mode = (process.env.REFERRAL_MODE || 'smart') as FeeMode;
+function loadReferralConfig(): ReferralConfig {
+ const raw = process.env.REFERRAL_FEE_BPS;
+ const parsed = Number.isFinite(Number(raw)) ? Math.floor(Number(raw)) : 0;
+ // Clamp to [1..1000]; adjust if Jupiter publishes a stricter cap.
+ const feeBps = Math.max(0, Math.min(1000, parsed));
+ const modeStr = (process.env.REFERRAL_MODE || 'smart').toLowerCase();
+ const mode = (modeStr === 'sol_only' ? 'sol_only' : 'smart') as FeeMode;
return {
enabled: feeBps > 0,
feeBps,
mode,
};
}🌐 Web query:
What are the documented limits (min/max) for `platformFeeBps` in Jupiter's swap/quote APIs?
💡 Result:
Short answer: Jupiter's public docs do not specify a numeric min/max for platformFeeBps. The docs show platformFeeBps as an optional integer (basis points) and give examples (e.g., 20 = 0.2%) but do not document a formal range — so the only documented constraint is that it's an integer BPS value and that passing it requires providing feeAccount in /swap. [1][2]
Sources:
- Jupiter Swap API (quote) — shows platformFee/platformFeeBps in request/response but no min/max range. [1]
- Jupiter docs "Add fees to swap" — example and notes on using platformFeeBps (no numeric bounds documented). [2]
Validate and clamp REFERRAL_FEE_BPS; sanitize mode.
Parse failures or out-of-range BPS can silently enable/disable fees. Although Jupiter's public docs don't specify a numeric limit for platformFeeBps, defensive clamping to a reasonable range and whitelisting modes prevents configuration errors.
-function loadReferralConfig(): ReferralConfig {
- const feeBps = parseInt(process.env.REFERRAL_FEE_BPS || '0', 10);
- const mode = (process.env.REFERRAL_MODE || 'smart') as FeeMode;
+function loadReferralConfig(): ReferralConfig {
+ const raw = process.env.REFERRAL_FEE_BPS;
+ const parsed = Number.isFinite(Number(raw)) ? Math.floor(Number(raw)) : 0;
+ // Clamp to [0..1000] as a reasonable defensive bound.
+ const feeBps = Math.max(0, Math.min(1000, parsed));
+ const modeStr = (process.env.REFERRAL_MODE || 'smart').toLowerCase();
+ const mode = (modeStr === 'sol_only' ? 'sol_only' : 'smart') as FeeMode;
return {
enabled: feeBps > 0,
feeBps,
mode,
};
}🤖 Prompt for AI Agents
In src/service.ts around lines 15 to 24, the REFERRAL_FEE_BPS parsing and
REFERRAL_MODE casting are unsafe: parse failures or out-of-range values can
silently misconfigure fees and untrusted mode strings may be accepted. Change
parsing to treat non-numeric input as 0, clamp feeBps to a safe range (e.g.,
0–10000 for basis points), and compute enabled from the clamped value; for mode,
implement a whitelist of allowed strings (e.g., 'smart' and 'fixed') and default
to 'smart' if the env value is not in the whitelist. Ensure the returned
ReferralConfig uses the sanitized feeBps and mode.
| // Load referral config and add platformFeeBps if enabled | ||
| const referralConfig = loadReferralConfig(); | ||
| let url = `https://lite-api.jup.ag/swap/v1/quote?inputMint=${inputMint}&outputMint=${outputMint}&amount=${intAmount}&slippageBps=${slippageBps}`; | ||
|
|
||
| if (referralConfig.enabled && referralConfig.feeBps > 0) { | ||
| url += `&platformFeeBps=${referralConfig.feeBps}`; | ||
| logger.info(`Referral fees enabled: ${referralConfig.feeBps} bps (${referralConfig.feeBps / 100}%)`); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Big‑number safety for “amount”.
Parsing via number risks precision loss for large atomics. Accept string|number, preserve as string in URL, and avoid parseInt unless necessary.
- const referralConfig = loadReferralConfig();
- let url = `https://lite-api.jup.ag/swap/v1/quote?inputMint=${inputMint}&outputMint=${outputMint}&amount=${intAmount}&slippageBps=${slippageBps}`;
+ const referralConfig = loadReferralConfig();
+ const amountStr = String(amount);
+ let url = `https://lite-api.jup.ag/swap/v1/quote?inputMint=${inputMint}&outputMint=${outputMint}&amount=${encodeURIComponent(amountStr)}&slippageBps=${slippageBps}`;Also update the method signature to allow amount: string | number.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/service.ts around lines 274 to 281, the code converts amount via parseInt
which can lose precision for large atomic values; update the method signature to
accept amount: string | number, remove parseInt usage, and build the URL using a
preserved string representation of the amount (e.g., const amountStr = typeof
amount === 'number' ? String(amount) : amount) so the exact atomic value is kept
when appended to the quote URL; keep referral platformFeeBps logic as-is and use
amountStr in place of intAmount in the URL.
feat(workflows): add worklfo deployment and release
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
.github/workflows/release.yml (1)
6-6: Clean up the branch condition comment.Line 6 contains a non-English comment fragment ("ou master") that appears to be incomplete or a copy-paste artifact. Recommend removing or replacing with a clear English comment.
on: push: branches: - - main # ou master + - main
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge base: Disabled due to data retention organization setting
📒 Files selected for processing (5)
.github/workflows/npm-deploy.yml(0 hunks).github/workflows/publish-npm.yaml(1 hunks).github/workflows/release.yml(1 hunks).release-please-manifest.json(1 hunks)release-please-config.json(1 hunks)
💤 Files with no reviewable changes (1)
- .github/workflows/npm-deploy.yml
🔇 Additional comments (4)
.release-please-manifest.json (1)
1-3: LGTM!Standard release-please manifest structure. The "0.0.0" initial version is appropriate for release-please automation.
release-please-config.json (1)
1-26: LGTM!Well-structured release-please configuration. Changelog sections are comprehensive, emoji formatting improves readability, and hidden sections (style, test, build, ci) appropriately exclude internal changes from public release notes. The simple release type and bump strategy are appropriate for this repository structure.
.github/workflows/publish-npm.yaml (2)
13-22: Verify explicit job permissions for npm publishing.Default GitHub Actions permissions are read-only and insufficient for npm publishing. To publish packages, you must grant write permissions in the workflow. This workflow calls an external reusable workflow (
sendo-labs/reusable-workflows) which may handle permissions internally, but this should be verified. Either add explicit permissions to this job:permissions: contents: readOr verify the reusable workflow documentation confirms it handles the necessary write permissions for npm publishing.
12-12: Yes, please search for the sendo-labs/reusable-workflows repository and the npm-publish-workflow-v1.0.0 workflow on GitHub.Verify external reusable workflow dependency and configuration.
The workflow delegates to
sendo-labs/reusable-workflows@npm-publish-workflow-v1.0.0, but this repository could not be confirmed via public sources. Ensure:
- The external repository exists and is from a trusted source
- The version tag
npm-publish-workflow-v1.0.0is correct and stable- The reusable workflow properly handles the
NPM_TOKENsecret (verify it's not logged or exposed)- Job permissions are appropriate for npm publishing (may need
contents:readif the reusable workflow requires it)
|
|
||
| jobs: | ||
| release: | ||
| uses: sendo-labs/reusable-workflows/.github/workflows/release.yml@release-workflow-v1.0.0 |
There was a problem hiding this comment.
🧩 Analysis chain
Verify external reusable workflow dependency.
The workflow delegates to sendo-labs/reusable-workflows@release-workflow-v1.0.0. Ensure this external repository and version tag are correct and from a trusted source.
🌐 Web query:
sendo-labs reusable-workflows GitHub release-workflow-v1.0.0
💡 Result:
I couldn't find a public GitHub repository or a release/tag named "sendo-labs reusable-workflows release-workflow-v1.0.0". (I searched GitHub/docs for reusable-workflow usage but found no matching sendo-labs repo or that tag.) (github.com)
Do you want me to:
- search GitHub again (I can try broader queries or GitHub's site search), or
- check a specific URL/repo you have, or
- show how to reference/use a reusable workflow tag like @v1.0.0 in a caller workflow?
Citations:
Reusable workflow reference cannot be verified—repository not found publicly.
The workflow references sendo-labs/reusable-workflows@release-workflow-v1.0.0, but no public GitHub repository with this name or tag exists. Verify:
- The organization name is correct (
sendo-labs) - The repository exists and you have access (if private)
- The tag/release version is correct
- The workflow path is correct (
.github/workflows/release.yml)
If this is a private repository, document this external dependency clearly for maintainers.
🤖 Prompt for AI Agents
.github/workflows/release.yml around line 10: the workflow references
sendo-labs/reusable-workflows/.github/workflows/release.yml@release-workflow-v1.0.0
which cannot be found publicly; verify and correct the reusable workflow
reference by confirming the organization and repo name, ensuring the
.github/workflows/release.yml path exists in that repo, and that the tag
release-workflow-v1.0.0 is valid; if the repository is private, either replace
the reference with a local or public workflow copy in this repo or add
documentation in the repo README or CONTRIBUTING noting the private external
dependency and required access permissions for maintainers.
PR: Optional Referral Fees on Jupiter Legacy Swap
This PR adds optional referral fees on Jupiter Legacy Swap via
platformFeeBpsandfeeAccount,with clean env-based config, tests, and runnable E2E examples (simulation/signing).
🧩 Features
Referral Fees
Adds
platformFeeBpsto/quotewhenREFERRAL_FEE_BPS > 0.Derives
feeAccountas the ATA for the chosen mint fromREFERRAL_FEE_RECEIVER.Modes:
sol_only: collect fees only if SOL/WSOL is in the pair.smart: prefer SOL if available, else input mint.Respects ExactIn / ExactOut rules and skips fees if constraints aren’t met.
🧠 Code Changes
src/service.tsplatformFeeBpsin quote andfeeAccountin swap.src/__tests__/integration.test.tssrc/scripts/e2e-mainnet.ts.env(can simulate and sign/send).local-e2e/(gitignored)Docs & Config
README.mdandenv.example: updated with clear config and E2E instructions..gitignore: ignoreslocal-e2eand env files.⚙️ How to Use
1. Setup Environment
Copy
.env.example→.envand set:2. Run E2E Example
npm run build && node dist/scripts/e2e-mainnet.js3. Local Simulator (ignored, for ops/dev)
Populate
local-e2e/.env.localas above, then run:🧩 Notes & Edge Cases
Error “Invalid token account / 0x1789 (6025)”
Usually a route/account issue.
Ensure the fee receiver’s ATA exists for the selected mint (e.g., WSOL in
sol_onlymode).If absent, create the ATA or let Jupiter create it idempotently.
Some routes may still fail in simulation—try higher slippage or another route.
No Referral Program needed for Legacy Swap.
(Still required for Trigger API.)
🔗 References
✅ Checklist
REFERRAL_FEE_BPS > 0and validfeeAccountfeeAccountderived fromREFERRAL_FEE_RECEIVERATA for selected mintREADMEand.env.exampleupdatedSummary by CodeRabbit
New Features
Documentation
Tests
Chores
CI