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
41 changes: 38 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,38 @@ Refer to https://github.com/HathorNetwork/rfcs/blob/master/projects/wallet-servi

#### System dependencies

You need nodejs installed on your enviroment, we suggest the latest Active LTS version (v18.x.x).
```
Node: 20x
yarn: v4 (yarn-berry)
```

#### Install nix (preferred)

For a better developer experience we suggest nix usage for mananing the enviroment. Visit this [link](https://nixos.org/download/#download-nix) to download it.

To enable the commands `nix develop` and `nix build` using flakes, add the following to your `/etc/nix/nix.conf` file:

```
experimental-features = nix-command flakes
```

#### Clone the project and install dependencies

`git clone https://github.com/HathorNetwork/hathor-wallet-service-sync_daemon.git`
```sh
$ git clone https://github.com/HathorNetwork/hathor-wallet-service-sync_daemon.git
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you replace this with

git@github.com:HathorNetwork/hathor-wallet-service.git?

```

`npm install`
To initialize nix dev environment:

```sh
$ nix develop
```

then, install the depencies:

```sh
yarn
```

#### Add env variables or an .env file to the repository:

Expand Down Expand Up @@ -54,6 +79,16 @@ AWS_SECRET_ACCESS_KEY="..."

These are used for communicating with the alert SQS

#### Docker images

Some packages depends on some docker images. To build them you'll need to have Hathor VPN access configured, check this [link](https://github.com/HathorNetwork/ops-tools/blob/master/terraform/wireguard-vpn/SOP.md#adding-a-new-client-to-the-vpn) for it.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Which ones?


#### Db initialize

Before running the tests, make sure your database is already initialize by running the migrations.

`nix develop . -c yarn sequelize db:migrate`

## Reseeding the HTR Token After Database Reset

If you need to reset the database (for example, to re-sync it from scratch), you must re-insert the HTR token into the `token` table. This is handled by a seed script that will automatically calculate the correct transaction count for HTR based on the current state of the database.
Expand Down
15 changes: 15 additions & 0 deletions db/migrations/20250529233113-add-token-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"use strict";

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
queryInterface.addColumn("token", "version", {
type: Sequelize.INTEGER,
allowNull: true,
Copy link
Member

Choose a reason for hiding this comment

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

Why allow null?
We could create the column with a default value of 0 (or whatever value the tokenVersion.DEPOSIT is) so that all "current listed" tokens will be marked as DBTs (Deposit Based Token).

What do you think?

});
},

async down(queryInterface) {
queryInterface.removeColumn("token", "version");
},
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"packages/wallet-service"
],
"engines": {
"node": ">=18"
"node": ">=20"
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we should upgrade to 22, since the current lib version already requires node 22

},
"nohoist": [
"**"
Expand Down
1 change: 1 addition & 0 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "dist/index.js",
"scripts": {
"build": "tsc --declaration",
"check-types": "tsc --noemit --skipLibCheck",
"test": "jest --runInBand --collectCoverage --detectOpenHandles --forceExit"
},
"peerDependencies": {
Expand Down
59 changes: 38 additions & 21 deletions packages/daemon/__tests__/db/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import {
XPUBKEY,
} from '../utils';
import { isAuthority } from '@wallet-service/common';
import { DbTxOutput, StringMap, TokenInfo, WalletStatus } from '../../src/types';
import { DbTxOutput, StringMap, TokenInfo, TokenInfoVersion, WalletStatus } from '../../src/types';
import { Authorities, TokenBalanceMap } from '@wallet-service/common';
import { constants } from '@hathor/wallet-lib';
import { generateAddresses } from '../../src/utils';
Expand Down Expand Up @@ -1061,18 +1061,41 @@ describe('token methods', () => {

expect(await getTokenInformation(mysql, 'invalid')).toBeNull();

const info = new TokenInfo('tokenId', 'tokenName', 'TKNS');
storeTokenInformation(mysql, info.id, info.name, info.symbol);
const info = new TokenInfo({
id: 'tokenId',
name: 'tokenName',
symbol: 'TKNS',
version: TokenInfoVersion.DEPOSIT
});
storeTokenInformation(mysql, info.id, info.name, info.symbol, info.version);

expect(info).toStrictEqual(await getTokenInformation(mysql, info.id));
});

test('incrementTokensTxCount', async () => {
expect.hasAssertions();

const htr = new TokenInfo('00', 'Hathor', 'HTR', 5);
const token1 = new TokenInfo('token1', 'MyToken1', 'MT1', 10);
const token2 = new TokenInfo('token2', 'MyToken2', 'MT2', 15);
const htr = new TokenInfo({
id: '00',
name: 'Hathor',
symbol: 'HTR',
transactions: 5,
version: TokenInfoVersion.DEPOSIT
});
const token1 = new TokenInfo({
id: 'token1',
name: 'MyToken1',
symbol: 'MT1',
transactions: 10,
version: TokenInfoVersion.DEPOSIT
});
const token2 = new TokenInfo({
id: 'token2',
name: 'MyToken2',
symbol: 'MT2',
transactions: 15,
version: TokenInfoVersion.DEPOSIT
});

await addToTokenTable(mysql, [
{ id: htr.id, name: htr.name, symbol: htr.symbol, transactions: htr.transactions },
Expand Down Expand Up @@ -1111,23 +1134,23 @@ describe('sync metadata', () => {
});
});

const generateMockTokens = (qty: number = 5) => new Array(qty).map((_, i) => new TokenInfo({
id: `token${i + 1}`,
name: `tokenName${i + 1}`,
symbol: `TKN${i + 1}`,
}))

// TODO: This test is duplicated from the wallet-service package, we should
// have methods shared between the two projects
describe('getTokenSymbols', () => {
it('should return a map of token symbol by token id', async () => {
expect.hasAssertions();

const tokensToPersist = [
new TokenInfo('token1', 'tokenName1', 'TKN1'),
new TokenInfo('token2', 'tokenName2', 'TKN2'),
new TokenInfo('token3', 'tokenName3', 'TKN3'),
new TokenInfo('token4', 'tokenName4', 'TKN4'),
new TokenInfo('token5', 'tokenName5', 'TKN5'),
];
const tokensToPersist = generateMockTokens();

// persist tokens
for (const eachToken of tokensToPersist) {
await storeTokenInformation(mysql, eachToken.id, eachToken.name, eachToken.symbol);
await storeTokenInformation(mysql, eachToken.id, eachToken.name, eachToken.symbol, eachToken.version);
}

const tokenIdList = tokensToPersist.map((each: TokenInfo) => each.id);
Expand All @@ -1145,13 +1168,7 @@ describe('getTokenSymbols', () => {
it('should return null when no token is found', async () => {
expect.hasAssertions();

const tokensToPersist = [
new TokenInfo('token1', 'tokenName1', 'TKN1'),
new TokenInfo('token2', 'tokenName2', 'TKN2'),
new TokenInfo('token3', 'tokenName3', 'TKN3'),
new TokenInfo('token4', 'tokenName4', 'TKN4'),
new TokenInfo('token5', 'tokenName5', 'TKN5'),
];
const tokensToPersist = generateMockTokens();

// no token persistence

Expand Down
1 change: 1 addition & 0 deletions packages/daemon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"build": "tsc -b",
"start": "node dist/index.js",
"watch": "tsc -w",
"check-types": "tsc --noemit --skipLibCheck",
"test_images_up": "docker compose -f ./__tests__/integration/scripts/docker-compose.yml up -d",
"test_images_down": "docker compose -f ./__tests__/integration/scripts/docker-compose.yml down",
"test_images_integration": "jest --config ./jest_integration.config.js --runInBand --forceExit",
Expand Down
12 changes: 10 additions & 2 deletions packages/daemon/src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
Miner,
TokenSymbolsRow,
MaxAddressIndexRow,
TokenInfoVersion,
} from '../types';
import {
TxInput,
Expand Down Expand Up @@ -974,14 +975,16 @@ export const mapDbResultToDbTxOutput = (result: TxOutputRow): DbTxOutput => ({
* @param tokenId - The token's id
* @param tokenName - The token's name
* @param tokenSymbol - The token's symbol
* @param tokenVersion - The token's version
*/
export const storeTokenInformation = async (
mysql: MysqlConnection,
tokenId: string,
tokenName: string,
tokenSymbol: string,
tokenVersion?: TokenInfoVersion | null
): Promise<void> => {
const entry = { id: tokenId, name: tokenName, symbol: tokenSymbol };
const entry = { id: tokenId, name: tokenName, symbol: tokenSymbol, version: tokenVersion };
await mysql.query(
'INSERT INTO `token` SET ?',
[entry],
Expand Down Expand Up @@ -1462,7 +1465,12 @@ export const getTokenInformation = async (

if (results.length === 0) return null;

return new TokenInfo(tokenId, results[0].name as string, results[0].symbol as string);
return new TokenInfo({
id: tokenId,
name: results[0].name as string,
symbol: results[0].symbol as string,
version: results[0].version as (TokenInfoVersion | null),
});
};

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/daemon/src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
tokens,
token_name,
token_symbol,
token_info_version,
parents,
} = fullNodeData;

Expand Down Expand Up @@ -258,7 +259,7 @@ export const handleVertexAccepted = async (context: Context, _event: Event) => {
if (!token_name || !token_symbol) {
throw new Error('Processed a token creation event but it did not come with token name and symbol');
}
await storeTokenInformation(mysql, hash, token_name, token_symbol);
await storeTokenInformation(mysql, hash, token_name, token_symbol, token_info_version);
}

// check if any of the inputs are still marked as locked and update tables accordingly.
Expand Down
1 change: 1 addition & 0 deletions packages/daemon/src/types/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ export interface TokenInformationRow extends RowDataPacket {
name: string;
symbol: string;
transactions: number;
version?: number | null;
Copy link
Member

Choose a reason for hiding this comment

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

Why null?

created_at: number;
updated_at: number;
}
Expand Down
1 change: 1 addition & 0 deletions packages/daemon/src/types/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export type StandardFullNodeEvent = FullNodeEventBase & {
tokens: string[];
token_name: null | string;
token_symbol: null | string;
token_info_version: null | number;
signal_bits: number;
metadata: {
hash: string;
Expand Down
30 changes: 26 additions & 4 deletions packages/daemon/src/types/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,26 @@
* LICENSE file in the root directory of this source tree.
*/

import { constants } from '@hathor/wallet-lib';
import { constants } from "@hathor/wallet-lib";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why?


export class TokenInfo {
export enum TokenInfoVersion {
DEPOSIT = 1,

FEE = 2,
}

export interface ITokenInfo {
id: string;
name: string;
symbol: string;
version?: TokenInfoVersion | null;
}

export interface ITokenInfoOptions extends ITokenInfo {
transactions?: number;
}

export class TokenInfo implements ITokenInfo {
id: string;

name: string;
Expand All @@ -16,26 +33,31 @@ export class TokenInfo {

transactions: number;

constructor(id: string, name: string, symbol: string, transactions?: number) {
version?: TokenInfoVersion | null;

constructor({ id, name, symbol, version, transactions }: ITokenInfoOptions) {
this.id = id;
this.name = name;
this.symbol = symbol;
this.transactions = transactions || 0;
this.version = version || TokenInfoVersion.DEPOSIT;

// XXX: get config from settings?
const hathorConfig = constants.DEFAULT_NATIVE_TOKEN_CONFIG;

if (this.id === constants.NATIVE_TOKEN_UID) {
this.name = hathorConfig.name;
this.symbol = hathorConfig.symbol;
this.version = null;
}
}

toJSON(): Record<string, unknown> {
toJSON(): ITokenInfo {
return {
id: this.id,
name: this.name,
symbol: this.symbol,
version: this.version,
};
}
}
1 change: 1 addition & 0 deletions packages/daemon/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"composite": true,
"target": "ES2022",
"module": "CommonJS",
"sourceMap": true,
Expand Down
Loading
Loading