-
Notifications
You must be signed in to change notification settings - Fork 66
feat: add swing client #750
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
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: Gregory Hill <gregorydhill@outlook.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Summary of Changes
Hello @gregdhill, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request introduces the integration of the Swing SDK to facilitate cross-chain token swaps, particularly for BTC <> any chain swaps, by wrapping the functionality within a new client that extends the existing Gateway framework. The primary goal is to enable seamless asset movement between different blockchain networks, starting with support for the BOB mainnet.
Highlights
- New Dependency: The
@swing.xyz/sdkhas been added to thesdk/package.jsondependencies, enabling integration with the Swing cross-chain protocol. - Swing Client Implementation: A new
SwingClientclass has been introduced insdk/src/gateway/swing.ts. This client is responsible for interacting with the Swing API to fetch cross-chain quotes and execute token transfers. - Gateway Integration: The
SwingGatewayClienthas been added, extending theGatewayApiClient. This client leverages theSwingClientto provide cross-chain swap capabilities within the Gateway, currently supporting only the BOB mainnet. - Unit Tests: A new test file,
sdk/test/swing.test.ts, has been added to include unit tests for theSwingClient, specifically verifying its ability to retrieve quotes.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
WalkthroughAdded Swing SDK dependency. Introduced Swing-based gateway with quote and send flow restricted to BOB mainnet. Implemented SwingClient to call Swing cross-chain quote and send endpoints. Added a Vitest test that invokes getQuote with sample parameters and logs the response. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Dev as Caller
participant GW as SwingGatewayClient
participant SC as SwingClient
participant SDK as SwingSDK
participant API as Swing Cross-Chain API
Dev->>GW: new SwingGatewayClient(chainId=bob.id)
GW-->>Dev: instance (validates chainId)
Dev->>SC: getQuote(params)
SC->>SDK: crossChainAPI GET /v0/transfer/quote<br/>fromChain, toChain, token addresses, symbols, amount
SDK->>API: GET /v0/transfer/quote
API-->>SDK: quote response (routes)
SDK-->>SC: quote data
alt routes > 0
SC->>SDK: crossChainAPI POST /v0/transfer/send<br/>route, amounts, skipValidation=true
SDK->>API: POST /v0/transfer/send
API-->>SDK: send response
SDK-->>SC: data
SC-->>Dev: result
else no routes / error
note over SC: Throw error (non-ok, missing data, or zero routes)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
✨ 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. Comment |
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.
Code Review
This pull request introduces a new Swing client to handle cross-chain swaps. The implementation is a good start, but there are several critical issues that need to be addressed before this can be safely used. Specifically, user addresses are hardcoded to the zero address, which will cause transactions to fail or lead to loss of funds. The method for getting a quote also initiates a transfer, which is misleading. Additionally, the implementation has some risky shortcuts like skipping validation and always picking the first available route. The associated test file also lacks any assertions. I've left detailed comments on these points.
| interface SwingQuoteParams { | ||
| fromChain: string; | ||
| toChain: string; | ||
| fromTokenAddress: Hex; | ||
| toTokenAddress: Hex; | ||
| fromTokenSymbol: string; | ||
| toTokenSymbol: string; | ||
| tokenAmount: string; | ||
| } |
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.
This interface is missing fromUserAddress and toUserAddress. These are crucial for creating a valid transfer and should be included here to be passed to the getQuote method, instead of hardcoding zeroAddress.
interface SwingQuoteParams {
fromChain: string;
toChain: string;
fromTokenAddress: Hex;
toTokenAddress: Hex;
fromTokenSymbol: string;
toTokenSymbol: string;
tokenAmount: string;
fromUserAddress: Hex;
toUserAddress: Hex;
}| fromUserAddress: zeroAddress, | ||
| toChain: params.toChain, | ||
| toTokenSymbol: params.toTokenSymbol, | ||
| toTokenAddress: params.toTokenAddress, | ||
| toUserAddress: zeroAddress, |
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.
Hardcoding fromUserAddress and toUserAddress to zeroAddress is incorrect for real transactions. They should be passed in via params after updating the SwingQuoteParams interface.
fromUserAddress: params.fromUserAddress,
toChain: params.toChain,
toTokenSymbol: params.toTokenSymbol,
toTokenAddress: params.toTokenAddress,
toUserAddress: params.toUserAddress,| fromUserAddress: zeroAddress, | ||
| tokenSymbol: params.fromTokenSymbol, | ||
| toTokenAddress: params.toTokenAddress, | ||
| toChain: params.toChain, | ||
| toTokenAmount: transferRoute.quote.amount, | ||
| toTokenSymbol: params.toTokenSymbol, | ||
| toUserAddress: zeroAddress, |
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.
Hardcoding fromUserAddress and toUserAddress to zeroAddress is incorrect for real transactions. They should be passed in via params.
fromUserAddress: params.fromUserAddress,
tokenSymbol: params.fromTokenSymbol,
toTokenAddress: params.toTokenAddress,
toChain: params.toChain,
toTokenAmount: transferRoute.quote.amount,
toTokenSymbol: params.toTokenSymbol,
toUserAddress: params.toUserAddress,| }); | ||
| } | ||
|
|
||
| async getQuote(params: SwingQuoteParams) { |
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 method name getQuote is misleading because it doesn't just fetch a quote; it also makes a second API call to /v0/transfer/send to prepare a transfer. This can have unintended side effects and makes the code harder to understand. Consider renaming this method to something that better reflects its full functionality, like getTransferData, or splitting it into two separate methods: one to get a quote, and another to prepare the transfer using that quote.
| throw new Error('No routes found'); | ||
| } | ||
|
|
||
| const transferRoute = quoteResponse.data.routes[0]; |
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 implementation currently always selects the first route returned by the Swing API (quoteResponse.data.routes[0]). The API might return multiple routes with different fees, speeds, or final amounts. Hardcoding the first route might not be optimal for the user. Consider adding logic to select the best route based on some criteria (e.g., highest output amount) or allowing the caller to select a route.
| tokenAmount: '1000000', | ||
| }); | ||
|
|
||
| console.log('quote', quote); |
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.
This test case lacks assertions. A test should verify that the code behaves as expected by checking the output or state. Currently, it only calls the getQuote method and logs the result, but it doesn't validate if the returned quote object is correct. Please add assertions to check the properties of the quote object.
console.log('quote', quote);
assert.isDefined(quote, 'Quote should not be undefined');
assert.isObject(quote, 'Quote should be an object');
// TODO: Add more specific assertions based on the expected quote structure.| "@magiceden-oss/runestone-lib": "^1.0.2", | ||
| "@scure/base": "^1.2.4", | ||
| "@scure/btc-signer": "^1.6.0", | ||
| "@swing.xyz/sdk": "^0.67.7", | ||
| "bitcoin-address-validation": "^3.0.0", | ||
| "bitcoinjs-lib": "^6.1.7", | ||
| "viem": "^2.33.2", | ||
| "global": "^4.4.0" | ||
| "global": "^4.4.0", | ||
| "viem": "^2.33.2" | ||
| } |
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.
| constructor() { | ||
| this.swingSDK = new SwingSDK({ | ||
| projectId: 'bob-swap', | ||
| }); | ||
| } |
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 projectId is hardcoded. It would be more flexible to allow this to be passed in through the constructor, with 'bob-swap' as a default value. This would make the client more reusable.
constructor(options: { projectId?: string } = {}) {
this.swingSDK = new SwingSDK({
projectId: options.projectId || 'bob-swap',
});
}| if (!quoteResponse.response.ok) { | ||
| throw new Error(quoteResponse.response.statusText); | ||
| } else if (!quoteResponse.data) { |
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 error handling for a failed API response is minimal. Throwing just quoteResponse.response.statusText might not be enough for debugging. It would be helpful to include the status code and potentially the response body in the error message, as the API might provide more specific details about what went wrong.
if (!quoteResponse.response.ok) {
const errorBody = await quoteResponse.response.text().catch(() => 'Could not read error body.');
throw new Error(`API Error: ${quoteResponse.response.status} ${quoteResponse.response.statusText}. Body: ${errorBody}`);
}| export class SwingGatewayClient extends GatewayApiClient { | ||
| private swingClient: SwingClient; | ||
|
|
||
| constructor(chainId: number, options?: { rpcUrl?: string }) { | ||
| if (chainId !== bob.id) { | ||
| throw new Error('SwingGatewayClient only supports BOB mainnet'); | ||
| } | ||
| super(chainId, options); | ||
| this.swingClient = new SwingClient(); | ||
| } | ||
| } |
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 SwingGatewayClient class initializes a SwingClient instance, but it's never used. The class itself doesn't seem to add any new functionality. If this is a work-in-progress, it's fine, but as it stands, this is unused code. Consider adding methods that utilize swingClient or removing the class if it's not yet ready.
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 (4)
sdk/test/swing.test.ts (1)
19-19: Remove console.log from testsReplace with assertions; logs add noise in CI.
- console.log('quote', quote); + assert.ok(quote, 'expected a quote/send response')sdk/src/gateway/swing.ts (3)
19-23: Make projectId configurable and allow SDK injection for testingHardcoding 'bob-swap' hinders env-specific configs and mocking.
- constructor() { - this.swingSDK = new SwingSDK({ - projectId: 'bob-swap', - }); - } + constructor(opts?: { projectId?: string; sdk?: SwingSDK }) { + this.swingSDK = + opts?.sdk ?? + new SwingSDK({ + projectId: opts?.projectId ?? process.env.SWING_PROJECT_ID ?? 'bob-swap', + }); + }
42-48: Surface API error details for debuggabilityThrowing only statusText loses server error context.
- if (!quoteResponse.response.ok) { - throw new Error(quoteResponse.response.statusText); + if (!quoteResponse.response.ok) { + const body = await quoteResponse.response.text().catch(() => ''); + throw new Error(`Quote failed: ${quoteResponse.response.status} ${quoteResponse.response.statusText} ${body}`); } else if (!quoteResponse.data) { throw new Error('No quote data'); } else if (quoteResponse.data.routes.length === 0) { throw new Error('No routes found'); }
73-84: Gateway wrapper is incomplete; expose quote/send and centralize chain gatingThe class constructs SwingClient but doesn’t expose methods. Provide thin wrappers and reuse chain gating.
export class SwingGatewayClient extends GatewayApiClient { private swingClient: SwingClient; constructor(chainId: number, options?: { rpcUrl?: string }) { if (chainId !== bob.id) { throw new Error('SwingGatewayClient only supports BOB mainnet'); } super(chainId, options); this.swingClient = new SwingClient(); } + + quote(params: SwingQuoteParams) { + return this.swingClient.getQuote(params); + } + + send(args: Parameters<SwingClient['send']>[0]) { + return this.swingClient.send(args); + } }Also consider adding bob-sepolia support behind a feature flag.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
sdk/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
sdk/package.json(1 hunks)sdk/src/gateway/swing.ts(1 hunks)sdk/test/swing.test.ts(1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.27.2)
sdk/test/swing.test.ts
[high] 11-11: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
[high] 12-12: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 GitHub Check: Tests
sdk/src/gateway/swing.ts
[failure] 47-47: test/swing.test.ts > Swing Tests > should get quote
Error: No routes found
❯ SwingClient.getQuote src/gateway/swing.ts:47:19
❯ test/swing.test.ts:8:23
⏰ 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: test
🔇 Additional comments (5)
sdk/package.json (2)
53-54: Dependency order churn is fineReordering "global" before "viem" is harmless. No action needed.
50-50: Verified Swing SDK dependency: @swing.xyz/sdk@^0.67.7 is the current latest, licensed under GPL-3.0-or-later, requires Node >= 16, and has no known vulnerabilities.sdk/test/swing.test.ts (1)
11-12: Gitleaks false positive on EVM addressesThese 0x… token addresses tripped the “Generic API Key” rule. Consider allowlisting EVM address patterns in tests.
You can add to .gitleaks.toml:
[[allowlist.regexes]] description = "Allow EVM addresses in tests" regex = '''0x[a-fA-F0-9]{40}''' paths = ["sdk/test/"]sdk/src/gateway/swing.ts (2)
25-41: Ignore parameter naming suggestion
tokenSymbolis the correct key for the source token symbol (andtoTokenSymbolfor the destination) per the API spec—no changes required.Likely an incorrect or invalid review comment.
65-66: skipValidation is defined as a string per the Swing API docs, so leaving'true'is correct.Likely an incorrect or invalid review comment.
| const transferRoute = quoteResponse.data.routes[0]; | ||
|
|
||
| const sendResponse = await this.swingSDK.crossChainAPI.POST('/v0/transfer/send', { | ||
| body: { | ||
| fromChain: params.fromChain, | ||
| fromTokenAddress: params.fromTokenAddress, | ||
| fromUserAddress: zeroAddress, | ||
| tokenSymbol: params.fromTokenSymbol, | ||
| toTokenAddress: params.toTokenAddress, | ||
| toChain: params.toChain, | ||
| toTokenAmount: transferRoute.quote.amount, | ||
| toTokenSymbol: params.toTokenSymbol, | ||
| toUserAddress: zeroAddress, | ||
| tokenAmount: params.tokenAmount, | ||
| route: transferRoute.route, | ||
| skipValidation: 'true', | ||
| }, | ||
| }); | ||
|
|
||
| return sendResponse.data; | ||
| } |
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.
getQuote() performs a send; wrong semantics, unsafe defaults (zeroAddress), and skipValidation as string
- A method named getQuote must not initiate transfer side effects.
- Sending to zeroAddress risks funds loss if this ever executes against mainnet.
- skipValidation should be boolean unless the API requires string; verify.
Recommended split:
- async getQuote(params: SwingQuoteParams) {
+ async getQuote(params: SwingQuoteParams) {
const quoteResponse = await this.swingSDK.crossChainAPI.GET('/v0/transfer/quote', { ... });
...
- const transferRoute = quoteResponse.data.routes[0];
- const sendResponse = await this.swingSDK.crossChainAPI.POST('/v0/transfer/send', { body: { ... }});
- return sendResponse.data;
+ return quoteResponse.data;
}
+
+ async send(params: SwingQuoteParams & {
+ route: unknown; // use exact SDK type if available
+ fromUserAddress: Hex;
+ toUserAddress: Hex;
+ skipValidation?: boolean;
+ }) {
+ const sendResponse = await this.swingSDK.crossChainAPI.POST('/v0/transfer/send', {
+ body: {
+ fromChain: params.fromChain,
+ fromTokenAddress: params.fromTokenAddress,
+ fromUserAddress: params.fromUserAddress,
+ tokenSymbol: params.fromTokenSymbol,
+ toTokenAddress: params.toTokenAddress,
+ toChain: params.toChain,
+ toTokenAmount: (params as any).toTokenAmount ?? undefined,
+ toTokenSymbol: params.toTokenSymbol,
+ toUserAddress: params.toUserAddress,
+ tokenAmount: params.tokenAmount,
+ route: params.route,
+ skipValidation: params.skipValidation ?? false,
+ },
+ });
+ return sendResponse.data;
+ }Also, select the best route (e.g., max toTokenAmount or min fee) instead of always routes[0].
📝 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 transferRoute = quoteResponse.data.routes[0]; | |
| const sendResponse = await this.swingSDK.crossChainAPI.POST('/v0/transfer/send', { | |
| body: { | |
| fromChain: params.fromChain, | |
| fromTokenAddress: params.fromTokenAddress, | |
| fromUserAddress: zeroAddress, | |
| tokenSymbol: params.fromTokenSymbol, | |
| toTokenAddress: params.toTokenAddress, | |
| toChain: params.toChain, | |
| toTokenAmount: transferRoute.quote.amount, | |
| toTokenSymbol: params.toTokenSymbol, | |
| toUserAddress: zeroAddress, | |
| tokenAmount: params.tokenAmount, | |
| route: transferRoute.route, | |
| skipValidation: 'true', | |
| }, | |
| }); | |
| return sendResponse.data; | |
| } | |
| async getQuote(params: SwingQuoteParams) { | |
| const quoteResponse = await this.swingSDK.crossChainAPI.GET( | |
| '/v0/transfer/quote', | |
| { /* map params appropriately */ } | |
| ); | |
| return quoteResponse.data; | |
| } | |
| async send( | |
| params: SwingQuoteParams & { | |
| route: unknown; // TODO: replace with exact SDK route type | |
| fromUserAddress: Hex; | |
| toUserAddress: Hex; | |
| skipValidation?: boolean; | |
| } | |
| ) { | |
| const sendResponse = await this.swingSDK.crossChainAPI.POST( | |
| '/v0/transfer/send', | |
| { | |
| body: { | |
| fromChain: params.fromChain, | |
| fromTokenAddress: params.fromTokenAddress, | |
| fromUserAddress: params.fromUserAddress, | |
| tokenSymbol: params.fromTokenSymbol, | |
| toTokenAddress: params.toTokenAddress, | |
| toChain: params.toChain, | |
| toTokenAmount: (params as any).toTokenAmount ?? undefined, | |
| toTokenSymbol: params.toTokenSymbol, | |
| toUserAddress: params.toUserAddress, | |
| tokenAmount: params.tokenAmount, | |
| route: params.route, | |
| skipValidation: params.skipValidation ?? false, | |
| }, | |
| } | |
| ); | |
| return sendResponse.data; | |
| } |
| it('should get quote', async () => { | ||
| const swingClient = new SwingClient(); | ||
|
|
||
| const quote = await swingClient.getQuote({ | ||
| fromChain: 'bob', | ||
| toChain: 'base', | ||
| fromTokenAddress: '0xe75D0fB2C24A55cA1e3F96781a2bCC7bdba058F0', | ||
| toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', | ||
| fromTokenSymbol: 'USDC.e', | ||
| toTokenSymbol: 'USDC', | ||
| tokenAmount: '1000000', | ||
| }); | ||
|
|
||
| console.log('quote', quote); | ||
| }, 120000); |
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.
Test makes live network calls, has no assertions, and fails CI
This is flaky and currently breaks CI (“No routes found”). Either mock Swing or gate as opt-in E2E. Also add assertions.
Apply one of the following:
Option A — mock with nock (preferred):
+import nock from 'nock';
import { assert, describe, it } from 'vitest';
import { SwingClient } from '../src/gateway/swing';
describe('Swing Tests', () => {
- it('should get quote', async () => {
+ it('should get quote', async () => {
+ nock.cleanAll();
+ const api = nock('https://api.swing.xyz')
+ .get('/v0/transfer/quote')
+ .query(true)
+ .reply(200, { routes: [{ quote: { amount: '990000' }, route: { id: 'r1' } }] });
+ nock('https://api.swing.xyz')
+ .post('/v0/transfer/send')
+ .reply(200, { id: 'tx_123', status: 'queued' });
const swingClient = new SwingClient();
- const quote = await swingClient.getQuote({
+ const quote = await swingClient.getQuote({
...
});
- console.log('quote', quote);
+ assert.ok(quote);
+ assert.equal(quote?.status, 'queued');
+ assert.ok(api.isDone());
}, 120000);
});Option B — gate E2E and skip by default:
-describe('Swing Tests', () => {
- it('should get quote', async () => {
+describe('Swing Tests', () => {
+ const runE2E = !!process.env.SWING_E2E;
+ (runE2E ? it : it.skip)('should get quote', async () => {
const swingClient = new SwingClient();
const quote = await swingClient.getQuote({ ... });
- console.log('quote', quote);
+ assert.ok(quote);
}, 120000);
});📝 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.
| it('should get quote', async () => { | |
| const swingClient = new SwingClient(); | |
| const quote = await swingClient.getQuote({ | |
| fromChain: 'bob', | |
| toChain: 'base', | |
| fromTokenAddress: '0xe75D0fB2C24A55cA1e3F96781a2bCC7bdba058F0', | |
| toTokenAddress: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', | |
| fromTokenSymbol: 'USDC.e', | |
| toTokenSymbol: 'USDC', | |
| tokenAmount: '1000000', | |
| }); | |
| console.log('quote', quote); | |
| }, 120000); | |
| import nock from 'nock'; | |
| import { assert, describe, it } from 'vitest'; | |
| import { SwingClient } from '../src/gateway/swing'; | |
| describe('Swing Tests', () => { | |
| it('should get quote', async () => { | |
| nock.cleanAll(); | |
| const api = nock('https://api.swing.xyz') | |
| .get('/v0/transfer/quote') | |
| .query(true) | |
| .reply(200, { routes: [{ quote: { amount: '990000' }, route: { id: 'r1' } }] }); | |
| nock('https://api.swing.xyz') | |
| .post('/v0/transfer/send') | |
| .reply(200, { id: 'tx_123', status: 'queued' }); | |
| const swingClient = new SwingClient(); | |
| const quote = await swingClient.getQuote({ | |
| fromChain: 'bob', | |
| toChain: 'base', | |
| fromTokenAddress: '0x7C502F253124A88Bbb6a0Ad79D9BeD279d86E8f4', | |
| toTokenAddress: '0x5C068df7139aD2Dedb840ceC95C384F25b443275', | |
| fromTokenSymbol: 'USDC.e', | |
| toTokenSymbol: 'USDC', | |
| tokenAmount: '1000000', | |
| }); | |
| assert.ok(quote); | |
| assert.equal(quote?.status, 'queued'); | |
| assert.ok(api.isDone()); | |
| }, 120000); | |
| }); |
🧰 Tools
🪛 Gitleaks (8.27.2)
[high] 11-11: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
[high] 12-12: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
In sdk/test/swing.test.ts around lines 5 to 19, the test makes live network
calls, has no assertions and is flaky in CI; replace the live call with a mocked
HTTP response (preferred) or gate the test as opt-in E2E and add assertions:
implement a nock interceptor for the Swing API endpoint(s) used by SwingClient
returning a representative JSON payload, call swingClient.getQuote in the test
against the mocked endpoint, and assert expected fields/values (e.g., routes
exist, expected token amounts/types); alternatively, wrap the test in a
conditional that only runs when a CI/E2E env var (e.g., RUN_E2E=true) is set and
add the same assertions so the test is skipped by default.
Adding a wrapper around Gateway using Swing for BTC <> any chain swaps
Summary by CodeRabbit
New Features
Chores
Tests