Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/api/src/app/routes/proxies/socket/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const proxy: FastifyPluginAsync = async (fastify, opts): Promise<void> => {
replyOptions: {
rewriteRequestHeaders: (request, headers) => ({
'x-api-key': apiKey,
origin: 'https://swap.cow.fi',
origin: 'https://swap.cow.finance',
Affiliate: affiliateCode,
}),
},
Expand Down
2 changes: 1 addition & 1 deletion apps/notification-producer/src/sendPush.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This \`message\` has been generated by [sendPush.test.ts](https://github.com/cow
title: 'My little test',
message: markdown,
account: POST_TO_QUEUE_ACCOUNT,
url: 'https://swap.cow.fi/#/1/limit/WETH/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48?tab=all&page=1',
url: 'https://swap.cow.finance/#/1/limit/WETH/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48?tab=all&page=1',
};

await pushNotificationsRepository.send([message]);
Expand Down
2 changes: 1 addition & 1 deletion apps/twap/src/app/utils/getApiBaseUrl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SupportedChainId } from '@cowprotocol/cow-sdk';
import { COW_API_NETWORK_NAMES } from '@cowprotocol/shared';

const COW_API_BASE_URL = 'https://api.cow.fi';
const COW_API_BASE_URL = 'https://api.cow.finance';

export function getApiBaseUrl(chainId: SupportedChainId): string {
const chainName = COW_API_NETWORK_NAMES[chainId];
Expand Down
12 changes: 9 additions & 3 deletions libs/repositories/src/datasources/coingecko.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { getCoingeckoProClient } from './coingecko';

describe('getCoingeckoProClient', () => {
const originalApiKey = process.env.COINGECKO_API_KEY;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Node.js, what happens when assigning process.env.MY_VAR = undefined? Does it delete the variable or set it to the string "undefined"?

💡 Result:

Assigning process.env.MY_VAR = undefined sets the value to the string "undefined", not deleting the variable. To delete it, use delete process.env.MY_VAR. This is the documented behavior in the official Node.js documentation (v25.9.0), which states: "Assigning a property on process.env will implicitly convert the value to a string... env.test = undefined; console.log(env.test); // => 'undefined'". This behavior is deprecated, and future versions may throw an error for non-string/number/boolean values, but currently, it converts undefined to "undefined". Confirmed in Node.js GitHub issues #48875 and #9248, where developers note that assignment always stringifies the value, and delete is required to unset.

Citations:


🏁 Script executed:

cat -n libs/repositories/src/datasources/coingecko.test.ts | head -50

Repository: cowprotocol/bff

Length of output: 1781


Avoid restoring env vars via direct undefined assignment.

At lines 9 and 13, restoring with direct assignment sets "undefined" (string) when originalApiKey is absent, polluting the environment for other tests. In Node.js, assigning undefined to process.env.* coerces to the string "undefined" rather than deleting it; use delete instead, as shown on line 34.

Suggested fix
 describe('getCoingeckoProClient', () => {
   const originalApiKey = process.env.COINGECKO_API_KEY;
+
+  const restoreApiKey = () => {
+    if (originalApiKey === undefined) {
+      delete process.env.COINGECKO_API_KEY;
+    } else {
+      process.env.COINGECKO_API_KEY = originalApiKey;
+    }
+  };

   beforeEach(() => {
     jest.resetModules();
     jest.clearAllMocks();
-    process.env.COINGECKO_API_KEY = originalApiKey;
+    restoreApiKey();
   });

   afterAll(() => {
-    process.env.COINGECKO_API_KEY = originalApiKey;
+    restoreApiKey();
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@libs/repositories/src/datasources/coingecko.test.ts` at line 4, The test
restores COINGECKO_API_KEY by assigning process.env.COINGECKO_API_KEY =
originalApiKey which can set the literal string "undefined"; instead, update the
teardown/restore logic around the originalApiKey variable so that if
originalApiKey is undefined you delete process.env.COINGECKO_API_KEY, otherwise
set process.env.COINGECKO_API_KEY = originalApiKey; locate the restoration code
referencing originalApiKey and process.env.COINGECKO_API_KEY in the test file
and replace the direct assignment with this conditional delete-or-assign
behavior.


beforeEach(() => {
jest.resetModules();

// Clear the singleton instance by re-requiring the module
jest.clearAllMocks();
process.env.COINGECKO_API_KEY = originalApiKey;
});

afterAll(() => {
process.env.COINGECKO_API_KEY = originalApiKey;
});

it('should create and return a client when COINGECKO_API_KEY is set', () => {
Expand All @@ -27,6 +31,8 @@ describe('getCoingeckoProClient', () => {
});

it('should throw an error when COINGECKO_API_KEY is not set', () => {
delete process.env.COINGECKO_API_KEY;

expect(() => {
getCoingeckoProClient();
}).toThrow('COINGECKO_API_KEY is not set');
Expand All @@ -37,4 +43,4 @@ describe('getCoingeckoProClient', () => {
getCoingeckoProClient('');
}).toThrow('COINGECKO_API_KEY is not set');
});
});
});
3 changes: 2 additions & 1 deletion libs/repositories/src/datasources/cowApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import createClient from 'openapi-fetch';

const COW_API_BASE_URL = process.env.COW_API_BASE_URL || 'https://api.cow.fi';
const COW_API_BASE_URL =
process.env.COW_API_BASE_URL || 'https://api.cow.finance';

import { AllChainIds, COW_API_NETWORK_NAMES } from '@cowprotocol/shared';
import { SupportedChainId } from '@cowprotocol/cow-sdk';
Expand Down
118 changes: 116 additions & 2 deletions libs/repositories/src/gen/cow/cow-api-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,27 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/v1/debug/simulation": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/**
* Simulate an arbitrary order.
* @description Simulates an arbitrary order specified in the request body and returns the Tenderly simulation request, along with any simulation error if applicable.
*
*/
post: operations["debugSimulationPost"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/debug/simulation/{uid}": {
parameters: {
query?: never;
Expand Down Expand Up @@ -1472,6 +1493,40 @@ export interface components {
/** @description EIP-2930 access list for the transaction. */
access_list?: components["schemas"]["AccessListItem"][];
};
/** @description Request body for simulating an arbitrary order without it being stored in the orderbook.
* */
SimulationRequest: {
/** @description The token being sold. */
sellToken: components["schemas"]["Address"];
/** @description The token being bought. */
buyToken: components["schemas"]["Address"];
/** @description Amount of sell token (hex- or decimal-encoded uint256). Must be greater than zero.
* */
sellAmount: components["schemas"]["TokenAmount"];
/** @description Amount of buy token (hex- or decimal-encoded uint256). */
buyAmount: components["schemas"]["TokenAmount"];
/** @description Whether this is a sell or buy order. */
kind: components["schemas"]["OrderKind"];
/** @description The address of the order owner. */
owner: components["schemas"]["Address"];
/** @description The address that will receive the buy tokens. Defaults to the owner if omitted.
* */
receiver?: components["schemas"]["Address"] | null;
/**
* @description Where the sell token should be drawn from.
* @default erc20
*/
sellTokenBalance: components["schemas"]["SellTokenSource"];
/**
* @description Where the buy token should be transferred to.
* @default erc20
*/
buyTokenBalance: components["schemas"]["BuyTokenDestination"];
/** @description Full app data JSON string. Defaults to `"{}"` if omitted.
* */
appData?: string | null;
blockNumber?: number;
};
/** @description The Tenderly simulation request for an order, along with any simulation error.
* */
OrderSimulation: {
Expand Down Expand Up @@ -2437,10 +2492,68 @@ export interface operations {
};
};
};
debugSimulation: {
debugSimulationPost: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["SimulationRequest"];
};
};
responses: {
/** @description Simulation request returned. */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["OrderSimulation"];
};
};
/** @description Invalid JSON body, or `appData` is not valid JSON.
* */
400: {
headers: {
[name: string]: unknown;
};
content?: never;
};
/** @description Order simulation endpoint is not enabled. */
405: {
headers: {
[name: string]: unknown;
};
content?: never;
};
/** @description Request body failed schema validation: missing required field, wrong field type, zero `sellAmount`, or unrecognised `kind` value.
* */
422: {
headers: {
[name: string]: unknown;
};
content?: never;
};
/** @description Internal error. */
500: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
debugSimulation: {
parameters: {
query?: {
/** @description Block number to simulate the order at. If not specified, the simulation uses the latest block.
* */
block_number?: number;
};
header?: never;
path: {
uid: components["schemas"]["UID"];
};
Expand All @@ -2457,7 +2570,8 @@ export interface operations {
"application/json": components["schemas"]["OrderSimulation"];
};
};
/** @description Invalid order UID. */
/** @description Malformed path parameter (invalid order UID format) or invalid `block_number` query parameter.
* */
400: {
headers: {
[name: string]: unknown;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const mockApi: CowApiClient = {
} as unknown as jest.Mocked<CowApiClient>;

const NATIVE_PRICE_ENDPOINT = '/api/v1/token/{token}/native_price';
const WETH_NATIVE_PRICE = 1; // See https://api.cow.fi/mainnet/api/v1/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/native_price
const USDC_PRICE = 288778763.042292; // USD price: 3,462.8585200136 (calculated 1e12 / 288778763.042292). See https://api.cow.fi/mainnet/api/v1/token/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/native_price
const WETH_NATIVE_PRICE = 1; // See https://api.cow.finance/mainnet/api/v1/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/native_price
const USDC_PRICE = 288778763.042292; // USD price: 3,462.8585200136 (calculated 1e12 / 288778763.042292). See https://api.cow.finance/mainnet/api/v1/token/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/native_price
const CHAIN_ID = SupportedChainId.MAINNET.toString();

const mockErc20Repository = {
Expand Down Expand Up @@ -133,7 +133,7 @@ describe('UsdRepositoryCow', () => {
// Get USD price for a not supported token
const price = await usdRepositoryCow.getUsdPrice(
CHAIN_ID,
NULL_ADDRESS // See https://api.cow.fi/mainnet/api/v1/token/0x0000000000000000000000000000000000000000/native_price
NULL_ADDRESS // See https://api.cow.finance/mainnet/api/v1/token/0x0000000000000000000000000000000000000000/native_price
);

// USD calculation based on native price is correct
Expand Down Expand Up @@ -176,7 +176,7 @@ describe('UsdRepositoryCow', () => {
// Get USD price for something is not even an address
const price = await usdRepositoryCow.getUsdPrice(
CHAIN_ID,
'this-is-not-a-token' // See https://api.cow.fi/mainnet/api/v1/token/this-is-not-a-token/native_price
'this-is-not-a-token' // See https://api.cow.finance/mainnet/api/v1/token/this-is-not-a-token/native_price
);

// USD calculation based on native price is correct
Expand Down
4 changes: 2 additions & 2 deletions libs/repositories/src/repos/UsdRepository/UsdRepositoryCow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,12 @@ export class UsdRepositoryCow extends UsdRepositoryNoop {
},
});

// If tokens is not found, return null. See See https://api.cow.fi/mainnet/api/v1/token/this-is-not-a-token/native_price
// If tokens is not found, return null. See See https://api.cow.finance/mainnet/api/v1/token/this-is-not-a-token/native_price
if (response.status === 404) {
return null;
}

// Unsupported tokens return undefined. See https://api.cow.fi/mainnet/api/v1/token/0x0000000000000000000000000000000000000000/native_price
// Unsupported tokens return undefined. See https://api.cow.finance/mainnet/api/v1/token/0x0000000000000000000000000000000000000000/native_price
if (response.status === 400) {
const errorType = (error as any)?.errorType;
const description = (error as any)?.description;
Expand Down
2 changes: 1 addition & 1 deletion libs/shared/src/utils/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function getExplorerBaseUrl(chainId: SupportedChainId) {
chainId === SupportedChainId.MAINNET
? ''
: `/${EXPLORER_NETWORK_NAMES[chainId]}`;
return `https://explorer.cow.fi${suffix}`;
return `https://explorer.cow.finance${suffix}`;
}

export function formatAmount(amount: bigint, decimals: number | undefined) {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"private": true,
"dependencies": {
"@cowprotocol/cms": "^0.3.0-RC.4",
"@cowprotocol/cow-sdk": "^8.0.0",
"@cowprotocol/cow-sdk": "8.1.0",
"@fastify/autoload": "~5.7.1",
"@fastify/caching": "^8.3.0",
"@fastify/cors": "^8.2.1",
Expand Down Expand Up @@ -112,4 +112,4 @@
"vite-plugin-dts": "^3.0.3",
"vite-tsconfig-paths": "^4.2.0"
}
}
}
Loading
Loading