Skip to content

Commit c38a5ff

Browse files
amateimaashwinravaNikolasHaimerlmelisaguevara
authored
feat: production release 🚀 (#445)
Co-authored-by: Ashwin <[email protected]> Co-authored-by: Nikolas Haimerl <[email protected]> Co-authored-by: Melisa Guevara <[email protected]>
1 parent 5b5edac commit c38a5ff

File tree

93 files changed

+10631
-314
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+10631
-314
lines changed

‎.github/workflows/ci.yml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ jobs:
2626
cache: 'pnpm'
2727
- name: Install dependencies
2828
run: pnpm install
29+
- name: Validate and inject secret into .env.test
30+
env:
31+
RPC_PROVIDER_URLS_421614: ${{ secrets.RPC_PROVIDER_URLS_421614 }}
32+
RPC_PROVIDER_URLS_998: ${{ secrets.RPC_PROVIDER_URLS_998 }}
33+
RPC_PROVIDER_URLS_42161: ${{ secrets.RPC_PROVIDER_URLS_42161 }}
34+
RPC_PROVIDER_URLS_999: ${{ secrets.RPC_PROVIDER_URLS_999 }}
35+
run: |
36+
# Safely append the secret, prefixed with a newline.
37+
# This prevents file corruption.
38+
printf '\n%s\n' "RPC_PROVIDER_URLS_421614=$RPC_PROVIDER_URLS_421614" >> .env.test
39+
printf '\n%s\n' "RPC_PROVIDER_URLS_998=$RPC_PROVIDER_URLS_998" >> .env.test
40+
printf '\n%s\n' "RPC_PROVIDER_URLS_42161=$RPC_PROVIDER_URLS_42161" >> .env.test
41+
printf '\n%s\n' "RPC_PROVIDER_URLS_999=$RPC_PROVIDER_URLS_999" >> .env.test
2942
- name: Run tests using docker
3043
run: pnpm test:e2e:docker
3144
build:

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ node_modules
99
# Local env files
1010
.env
1111
.env.local
12+
.env.test
1213
.env.development.local
1314
.env.test.local
1415
.env.production.local

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"dev": "turbo dev",
77
"fix": "turbo fix",
88
"check": "turbo check",
9-
"test": "turbo test",
9+
"test": "turbo test --concurrency=1",
1010
"test:e2e:docker": "docker compose --env-file .env.test -f docker-compose.e2e.yml up --abort-on-container-exit --exit-code-from indexer-scraper-e2e",
1111
"test:e2e:docker:prune": "docker compose --env-file .env.test -f docker-compose.e2e.yml down -v",
1212
"start:indexer": "APP=indexer pnpm --filter @repo/node-app start",

‎packages/indexer-api/package.json‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"author": "",
2323
"license": "ISC",
2424
"dependencies": {
25-
"@across-protocol/sdk": "^4.3.75",
25+
"@across-protocol/constants": "^3.1.89",
26+
"@across-protocol/sdk": "^4.3.97",
2627
"@repo/error-handling": "workspace:*",
2728
"@repo/indexer": "workspace:*",
2829
"@repo/indexer-database": "workspace:*",
@@ -48,15 +49,19 @@
4849
"@types/body-parser": "^1.19.5",
4950
"@types/chai": "^4.3.17",
5051
"@types/cors": "^2.8.17",
52+
"@types/ioredis-mock": "^8.2.6",
5153
"@types/mocha": "^10.0.7",
5254
"chai": "^4.5.0",
5355
"eslint": "^8.57.0",
56+
"ioredis-mock": "^8.13.1",
5457
"mocha": "^10.7.0",
5558
"nyc": "^17.0.0",
59+
"pg-mem": "^3.0.5",
5660
"prettier": "^3.3.3",
5761
"source-map-support": "^0.5.21",
5862
"supertest": "^7.0.0",
5963
"ts-node": "^10.9.2",
64+
"typeorm": "^0.3.20",
6065
"typescript": "^5.5.4"
6166
}
6267
}

‎packages/indexer-api/src/controllers/deposits.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DepositsParams,
66
DepositParams,
77
FilterDepositsParams,
8+
DepositStatusParams,
89
} from "../dtos/deposits.dto";
910

1011
export class DepositsController {
@@ -30,7 +31,7 @@ export class DepositsController {
3031
next: NextFunction,
3132
) => {
3233
try {
33-
const params = s.create(req.query, DepositParams);
34+
const params = s.create(req.query, DepositStatusParams);
3435
const result = await this.service.getDepositStatus(params);
3536
return res.json(result);
3637
} catch (err) {

‎packages/indexer-api/src/dtos/deposits.dto.ts‎

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,33 @@ export const DepositParams = s.object({
6868

6969
export type DepositParams = s.Infer<typeof DepositParams>;
7070

71+
export const DepositStatusParams = s.object({
72+
depositId: s.optional(s.string()),
73+
originChainId: s.optional(stringToInt),
74+
/**
75+
* @deprecated Use depositTxnRef instead.
76+
* Once with supporting SVM chains, all references
77+
* to EVM terminology is being deprecated for more general terms.
78+
*
79+
*/
80+
depositTxHash: s.optional(s.string()),
81+
depositTxnRef: s.optional(s.string()),
82+
relayDataHash: s.optional(s.string()),
83+
index: s.refine(
84+
s.defaulted(stringToInt, 0),
85+
"positiveIndex",
86+
(value) => value >= 0,
87+
),
88+
89+
/**
90+
* Hyperliquid Withdrawals
91+
*/
92+
from: s.optional(parseAddressField),
93+
hypercoreWithdrawalNonce: s.optional(s.string()),
94+
});
95+
96+
export type DepositStatusParams = s.Infer<typeof DepositStatusParams>;
97+
7198
export const FilterDepositsParams = s.object({
7299
originChainId: s.optional(stringToInt),
73100
destinationChainId: s.optional(stringToInt),
@@ -163,10 +190,10 @@ export type DepositStatusResponse = {
163190
status: string | entities.RelayStatus;
164191
originChainId: number;
165192
depositId: string;
166-
depositTxHash: string | null;
167-
fillTx: string | undefined;
193+
depositTxHash?: string | null;
194+
fillTx?: string | undefined;
168195
destinationChainId: number;
169-
depositRefundTxHash: string | undefined;
196+
depositRefundTxHash?: string | undefined;
170197
actionsSucceeded: boolean | null;
171198
pagination: PaginationInfo;
172199
};

‎packages/indexer-api/src/services/deposits.ts‎

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Redis } from "ioredis";
2+
import { CHAIN_IDs } from "@across-protocol/constants";
23
import { DataSource, entities } from "@repo/indexer-database";
34
import type {
45
DepositParams,
@@ -7,9 +8,11 @@ import type {
78
DepositReturnType,
89
ParsedDepositReturnType,
910
DepositStatusResponse,
11+
DepositStatusParams,
1012
} from "../dtos/deposits.dto";
1113
import {
1214
DepositNotFoundException,
15+
HyperliquidWithdrawalNotFoundException,
1316
IncorrectQueryParamsException,
1417
IndexParamOutOfRangeException,
1518
} from "./exceptions";
@@ -222,7 +225,7 @@ export class DepositsService {
222225
}
223226

224227
public async getDepositStatus(
225-
params: DepositParams,
228+
params: DepositStatusParams,
226229
): Promise<DepositStatusResponse> {
227230
// in the validation rules each of these params are marked as optional
228231
// but we need to check that at least one of them is present
@@ -231,7 +234,8 @@ export class DepositsService {
231234
(params.depositId && params.originChainId) ||
232235
params.depositTxHash ||
233236
params.depositTxnRef ||
234-
params.relayDataHash
237+
params.relayDataHash ||
238+
(params.from && params.hypercoreWithdrawalNonce)
235239
)
236240
) {
237241
throw new IncorrectQueryParamsException();
@@ -245,6 +249,11 @@ export class DepositsService {
245249
return JSON.parse(cachedData);
246250
}
247251

252+
if (params.from && params.hypercoreWithdrawalNonce) {
253+
// Hyperliquid Withdrawal status check
254+
return this.getHyperliquidWithdrawalStatus(params);
255+
}
256+
248257
// no cached data, so we need to query the database
249258
const repo = this.db.getRepository(entities.RelayHashInfo);
250259
const queryBuilder = repo.createQueryBuilder("rhi");
@@ -323,6 +332,46 @@ export class DepositsService {
323332
return result;
324333
}
325334

335+
private async getHyperliquidWithdrawalStatus(params: DepositStatusParams) {
336+
const cacheKey = this.getDepositStatusCacheKey(params);
337+
const repo = this.db.getRepository(entities.HypercoreCctpWithdraw);
338+
const withdrawal = await repo.findOne({
339+
where: {
340+
fromAddress: params.from,
341+
hypercoreNonce: params.hypercoreWithdrawalNonce,
342+
},
343+
});
344+
345+
if (!withdrawal) {
346+
throw new HyperliquidWithdrawalNotFoundException();
347+
}
348+
349+
const result = {
350+
status: "filled",
351+
originChainId: CHAIN_IDs.HYPERCORE,
352+
depositId: params.hypercoreWithdrawalNonce as string, // it cannot be undefined because of the query validation rules
353+
depositTxnRef: null,
354+
fillTxnRef: withdrawal.mintTxnHash,
355+
destinationChainId: parseInt(withdrawal.destinationChainId),
356+
depositRefundTxnRef: null,
357+
actionsSucceeded: null,
358+
pagination: {
359+
currentIndex: 0,
360+
maxIndex: 0,
361+
},
362+
};
363+
364+
const cacheTtlSeconds = 60 * 5; // 5 minutes
365+
await this.redis.set(
366+
cacheKey,
367+
JSON.stringify(result),
368+
"EX",
369+
cacheTtlSeconds,
370+
);
371+
372+
return result;
373+
}
374+
326375
public async getDeposit(params: DepositParams) {
327376
// in the validation rules each of these params are marked as optional
328377
// but we need to check that at least one of them is present
@@ -703,7 +752,7 @@ export class DepositsService {
703752
return false;
704753
}
705754

706-
private getDepositStatusCacheKey(params: DepositParams) {
755+
private getDepositStatusCacheKey(params: DepositStatusParams) {
707756
if (params.depositId && params.originChainId) {
708757
return `depositStatus-${params.depositId}-${params.originChainId}-${params.index}`;
709758
}
@@ -716,6 +765,10 @@ export class DepositsService {
716765
return `depositStatus-${params.relayDataHash}-${params.index}`;
717766
}
718767

768+
if (params.from && params.hypercoreWithdrawalNonce) {
769+
return `depositStatus-${params.from}-${params.hypercoreWithdrawalNonce}`;
770+
}
771+
719772
// in theory this should never happen because we have already checked
720773
// that at least one of the params is present
721774
throw new Error(

‎packages/indexer-api/src/services/exceptions.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ export class DepositNotFoundException extends IndexerHTTPError {
1010
}
1111
}
1212

13+
export class HyperliquidWithdrawalNotFoundException extends IndexerHTTPError {
14+
constructor() {
15+
super(
16+
StatusCodes.NOT_FOUND,
17+
HyperliquidWithdrawalNotFoundException.name,
18+
"Hyperliquid withdrawal not found given the provided constraints",
19+
);
20+
}
21+
}
22+
1323
export class IndexParamOutOfRangeException extends IndexerHTTPError {
1424
constructor(message: string) {
1525
super(StatusCodes.BAD_REQUEST, IndexParamOutOfRangeException.name, message);

‎packages/indexer-api/src/tests/deposit.test.ts‎

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,21 @@ import { expect } from "chai";
22
import request from "supertest";
33
import { ExpressApp } from "../express-app";
44
import express from "express";
5-
import {
6-
createDataSource,
7-
DataSource,
8-
entities,
9-
fixtures,
10-
} from "@repo/indexer-database";
5+
import { DataSource, entities, fixtures } from "@repo/indexer-database";
116
import Redis from "ioredis";
12-
import * as Indexer from "@repo/indexer";
13-
import * as utils from "../utils";
7+
import { getTestDataSource, getTestRedisInstance } from "./setup";
148
import * as routers from "../routers";
159

1610
describe("/deposit", () => {
1711
let app: express.Express;
1812
let dataSource: DataSource;
19-
let redis: Redis;
13+
let redisClient: Redis;
2014
let depositsFixture: fixtures.FundsDepositedFixture;
2115
let relayHashInfoFixture: fixtures.RelayHashInfoFixture;
2216

23-
before(async () => {
24-
// Set up database and Redis
25-
const databaseConfig = utils.getPostgresConfig(process.env);
26-
dataSource = await createDataSource(databaseConfig).initialize();
27-
28-
const redisConfig = Indexer.parseRedisConfig(process.env);
29-
redis = new Redis(redisConfig);
17+
beforeEach(async () => {
18+
dataSource = await getTestDataSource();
19+
redisClient = getTestRedisInstance();
3020

3121
// Initialize fixtures
3222
depositsFixture = new fixtures.FundsDepositedFixture(dataSource);
@@ -35,15 +25,14 @@ describe("/deposit", () => {
3525
await relayHashInfoFixture.deleteAllRelayHashInfoRows();
3626

3727
// Initialize the Express app with the deposits router
38-
const depositsRouter = routers.deposits.getRouter(dataSource, redis);
28+
const depositsRouter = routers.deposits.getRouter(dataSource, redisClient);
3929
app = ExpressApp({ deposits: depositsRouter });
4030
});
4131

42-
after(async () => {
32+
afterEach(async () => {
4333
// Clean up resources
44-
await depositsFixture.deleteAllDeposits();
4534
await dataSource.destroy();
46-
await redis.quit();
35+
await redisClient.quit();
4736
});
4837

4938
it("should return 200 and one deposit by depositId and originChainId", async () => {

0 commit comments

Comments
 (0)