Skip to content
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(backend): get_historical_balance endpoint #54

Closed
wants to merge 52 commits into from
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
1b80451
feat(backend): get_historical_balance endpoint
EjembiEmmanuel Apr 13, 2024
f08465a
test(backend): get_historical_balance route
EjembiEmmanuel Apr 13, 2024
5de234b
test(backend): remove console.log() statement
EjembiEmmanuel Apr 13, 2024
4afeacf
refac(backend): modify sql query
EjembiEmmanuel Apr 13, 2024
fe33f97
test(backend): modify input and output values
EjembiEmmanuel Apr 13, 2024
7b79001
refac(backend): modify error message
EjembiEmmanuel Apr 13, 2024
bb00d02
fix(backend): fix linting issues
EjembiEmmanuel Apr 14, 2024
e5091f3
refac(backend): add primary key field to
EjembiEmmanuel Apr 14, 2024
d4240d7
fix(backend): fix linting issues
EjembiEmmanuel Apr 15, 2024
9144f9d
refac(backend): remove primary key field from
EjembiEmmanuel Apr 15, 2024
fb7471e
refac(backend): move address regex to
EjembiEmmanuel Apr 15, 2024
9a00423
feat(backend): add transaction history endpoint and tests (#53)
Xavek Apr 15, 2024
f185833
feat(backend): add register endpoint (#45)
0xLucqs Apr 15, 2024
0c3e83e
try coverage all (#57)
0xLucqs Apr 15, 2024
7f6bbf3
refac(backend): modify query
EjembiEmmanuel Apr 15, 2024
bbe66e5
fix: adjust drizzle-orm related types (#58)
fracek Apr 16, 2024
eaf1cf2
refac(backend): refactor historical balances
EjembiEmmanuel Apr 16, 2024
1d63f03
refac(backend): removed unncessary imports
EjembiEmmanuel Apr 16, 2024
6d23f65
refac(backend): handle error
EjembiEmmanuel Apr 16, 2024
27989e6
refac(backend): remove redundant error handling
EjembiEmmanuel Apr 16, 2024
db390b8
feat(backend): get_historical_balance endpoint
EjembiEmmanuel Apr 13, 2024
0762e04
test(backend): get_historical_balance route
EjembiEmmanuel Apr 13, 2024
bda917a
test(backend): remove console.log() statement
EjembiEmmanuel Apr 13, 2024
ca39d2e
refac(backend): modify sql query
EjembiEmmanuel Apr 13, 2024
b0daa92
test(backend): modify input and output values
EjembiEmmanuel Apr 13, 2024
89d75af
refac(backend): modify error message
EjembiEmmanuel Apr 13, 2024
1615454
fix(backend): fix linting issues
EjembiEmmanuel Apr 14, 2024
ee61dbe
refac(backend): add primary key field to
EjembiEmmanuel Apr 14, 2024
48e2e2a
fix(backend): fix linting issues
EjembiEmmanuel Apr 15, 2024
f18f462
refac(backend): remove primary key field from
EjembiEmmanuel Apr 15, 2024
3a2a790
refac(backend): move address regex to
EjembiEmmanuel Apr 15, 2024
ae87aab
refac(backend): modify query
EjembiEmmanuel Apr 15, 2024
6c4a5ca
refac(backend): refactor historical balances
EjembiEmmanuel Apr 16, 2024
f6144d0
refac(backend): removed unncessary imports
EjembiEmmanuel Apr 16, 2024
0619a69
refac(backend): handle error
EjembiEmmanuel Apr 16, 2024
8ac503e
refac(backend): remove redundant error handling
EjembiEmmanuel Apr 16, 2024
73856c2
Merge branch 'main' of github.com:EjembiEmmanuel/vault
EjembiEmmanuel Apr 16, 2024
c3232eb
fix(backend): fix liniting issues
EjembiEmmanuel Apr 16, 2024
89952ff
refac(backend): remove duplicate addres regex
EjembiEmmanuel Apr 16, 2024
2cb6d13
feat(backend): get_historical_balance endpoint
EjembiEmmanuel Apr 13, 2024
c9e8390
test(backend): get_historical_balance route
EjembiEmmanuel Apr 13, 2024
2438634
fix(backend): fix linting issues
EjembiEmmanuel Apr 14, 2024
e9e8abc
refac(backend): add primary key field to
EjembiEmmanuel Apr 14, 2024
06ec9fb
fix(backend): fix linting issues
EjembiEmmanuel Apr 15, 2024
4702fae
refac(backend): remove primary key field from
EjembiEmmanuel Apr 15, 2024
eb03992
refac(backend): move address regex to
EjembiEmmanuel Apr 15, 2024
63b4061
refac(backend): handle error
EjembiEmmanuel Apr 16, 2024
a408ff3
refac(backend): remove redundant error handling
EjembiEmmanuel Apr 16, 2024
80f0fba
fix(backend): fix liniting issues
EjembiEmmanuel Apr 16, 2024
29e8a0b
refac(backend): remove duplicate addres regex
EjembiEmmanuel Apr 16, 2024
6da1f66
Merge branch 'main' of github.com:EjembiEmmanuel/vault
EjembiEmmanuel Apr 16, 2024
58e502c
fix(backend): fix linting issues
EjembiEmmanuel Apr 16, 2024
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
1 change: 1 addition & 0 deletions backend/src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const usdcTransfer = pgTable('transfer_usdc', {
});

export const usdcBalance = pgTable('balance_usdc', {
balanceId: text('balance_id').primaryKey(),
Copy link
Contributor

Choose a reason for hiding this comment

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

remove that

network: text('network'),
blockNumber: bigint('block_number', { mode: 'number' }),
blockTimestamp: timestamp('block_timestamp', { withTimezone: false }),
Expand Down
46 changes: 46 additions & 0 deletions backend/src/routes/getHistoricalBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { sql } from 'drizzle-orm';
import type { FastifyInstance } from 'fastify';
import { usdcBalance } from '../db/schema';

const addressRegex = /^0x0[0-9a-fA-F]{63}$/;
Copy link
Contributor

Choose a reason for hiding this comment

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

move this in routes/index.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What am I suppose to move here. I don't quite get it

Copy link
Contributor

Choose a reason for hiding this comment

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

move the regex in routes/index.ts (as every endpoint uses it) and import it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright got it!


export function getHistoricalBalanceRoute(fastify: FastifyInstance) {
fastify.get('/get_historical_balance', async (request, reply) => {
const { address } = request.query as { address?: string };

if (!address) {
return reply.status(400).send({ error: 'Address is required' });
}

// Validate address format
if (!addressRegex.test(address)) {
return reply.status(400).send({ error: 'Invalid address format' });
}

try {
const historicalBalances = await fastify.db.execute(
sql`
SELECT balance, DATE(block_timestamp) AS date
FROM ${usdcBalance}
WHERE (address, block_timestamp) IN (
SELECT address, MAX(block_timestamp) AS max_timestamp
FROM ${usdcBalance}
WHERE address = ${address}
GROUP BY address, DATE(block_timestamp)
)
`,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

don't add the query as raw sql use the ts functions

Copy link
Contributor

Choose a reason for hiding this comment

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

also we want the last balance per day. If I had 100 usdc, i send 40 at 10 am then i receive 20 at 11pm my balance for that day would be 80 (lmk if it's not clear)

Copy link
Contributor

Choose a reason for hiding this comment

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

and we want that for the last 30 days

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay. I assumed each entry in the database has a timestamp and corresponding balance. So I grouped the rows by address and date then extracted the entry with the latest timestamp per day assuming the entry already has the latest balance for that day.

Copy link
Contributor

Choose a reason for hiding this comment

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

ah then this is correct, just rewrite this using the typescript functions plz

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright


if (!historicalBalances) {
return reply.status(404).send({ error: 'Error while retrieving historical balance' });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this can happen, or if it can can you please add a test case for this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright. Thought as much


return reply.send({
historicalBalances,
});
} catch (error) {
console.error(error);
return reply.status(500).send({ error: 'Internal server error' });
}
});
}
2 changes: 2 additions & 0 deletions backend/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { sql } from 'drizzle-orm';
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';

import { getBalanceRoute } from './getBalance';
import { getHistoricalBalanceRoute } from './getHistoricalBalance';

export function declareRoutes(fastify: FastifyInstance) {
getStatusRoute(fastify);
getBalanceRoute(fastify);
getHistoricalBalanceRoute(fastify);
}

function getStatusRoute(fastify: FastifyInstance) {
Expand Down
111 changes: 111 additions & 0 deletions backend/src/test/getHistoricalBalance.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { PostgreSqlContainer, type StartedPostgreSqlContainer } from '@testcontainers/postgresql';
import type { FastifyInstance } from 'fastify';
import { afterAll, beforeAll, describe, expect, test } from 'vitest';

import { buildApp } from '@/app';
import * as schema from '../db/schema';

describe('GET /get_historical_balance route', () => {
let container: StartedPostgreSqlContainer;
let app: FastifyInstance;
const testAddress = '0x064a24243f2aabae8d2148fa878276e6e6e452e3941b417f3c33b1649ea83e11';

beforeAll(async () => {
container = await new PostgreSqlContainer().start();
const connectionUri = container.getConnectionUri();
app = buildApp({
database: {
connectionString: connectionUri,
},
app: {
port: 8080,
},
});

await app.ready();
await app.db.insert(schema.usdcBalance).values([
{
balanceId: 'YfuanNvdfMfcmmdeklWDJO',
address: testAddress,
blockTimestamp: new Date('2024-04-10 13:03:05'),
balance: '3.8AE83A109D0635A426BB',
Copy link
Contributor

Choose a reason for hiding this comment

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

don't refer to the spec for the balance it's actually not hex but regular base 10

Copy link
Contributor

Choose a reason for hiding this comment

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

the numbers should be formatted like that 1.234567

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright

},
{
balanceId: 'jNEhskffmfscvdmdnQMrsz',
address: testAddress,
blockTimestamp: new Date('2024-04-10 14:03:05'),
balance: '3.8AE83A109D0635A426BB',
},
{
balanceId: 'ZkNhudkvfcfKdkgmYfkRj',
address: testAddress,
blockTimestamp: new Date('2024-04-11 13:03:05'),
balance: '3.8AE83A109D0635A426BB',
},
{
balanceId: 'ObZldfvkfgckmflKpXvrd',
address: testAddress,
blockTimestamp: new Date('2024-04-11 14:03:05'),
balance: '3.8AE83A109D0635A426BB',
},
]);
});

afterAll(async () => {
await app.close();
await container.stop();
});

test('should return the historical balances for a valid address', async () => {
const response = await app.inject({
method: 'GET',
url: `/get_historical_balance?address=${testAddress}`,
});

expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({
historicalBalances: [
{
date: '2024-04-10',
balance: '3.8AE83A109D0635A426BB',
},
{
date: '2024-04-11',
balance: '3.8AE83A109D0635A426BB',
},
],
});
});

test('should return error, invalid address format', async () => {
const invalidAddress = '0x0';
const response = await app.inject({
method: 'GET',
url: `/get_historical_balance?address=${invalidAddress}`,
});

expect(response.statusCode).toBe(400);
expect(response.json()).toHaveProperty('error', 'Invalid address format');
});

test('should return error, no address provided', async () => {
const response = await app.inject({
method: 'GET',
url: '/get_historical_balance',
});

expect(response.statusCode).toBe(400);
expect(response.json()).toHaveProperty('error', 'Address is required');
});

test('should return [], unknown address', async () => {
const unknownAddress = '0x064a24243f2aabae8d2148fa878276e6e6e452e3941b417f3c33b1649ea83e12';
const response = await app.inject({
method: 'GET',
url: `/get_historical_balance?address=${unknownAddress}`,
});

expect(response.statusCode).toBe(200);
expect(response.json()).toEqual({ historicalBalances: [] });
});
});
Loading