Skip to content

Commit 2007df0

Browse files
committed
fixed config routes for cardano and changed api and folder structure for cardano support and fixed tokens routes for cardano and added three network mainnet, preprod and preview for tokens
1 parent 63caeeb commit 2007df0

Some content is hidden

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

48 files changed

+2408
-2725
lines changed

pnpm-lock.yaml

Lines changed: 1535 additions & 1558 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/chains/cardano/cardano.ts

Lines changed: 79 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
1-
import { Config, getCardanoConfig } from './cardano.config';
2-
import { Lucid, Blockfrost, C, UTxO } from '@aiquant/lucid-cardano';
3-
import { TokenListType, TokenValue } from '../../services/base';
1+
import crypto from 'crypto';
2+
3+
import { Lucid, Blockfrost, UTxO } from '@aiquant/lucid-cardano';
4+
import fse from 'fs-extra';
5+
6+
import { TokenService } from '#src/services/token-service';
7+
import { CardanoToken } from '#src/tokens/types';
8+
49
import { ConfigManagerCertPassphrase } from '../../services/config-manager-cert-passphrase';
10+
import { logger } from '../../services/logger';
511
import { walletPath } from '../../wallet/utils';
6-
import { promises as fs } from 'fs';
7-
import fse from 'fs-extra';
8-
import crypto from 'crypto';
12+
13+
import { Config, getCardanoConfig } from './cardano.config';
914
import {
1015
NETWORK_ERROR_CODE,
1116
NETWORK_ERROR_MESSAGE,
@@ -14,27 +19,13 @@ import {
1419
TOKEN_NOT_SUPPORTED_ERROR_MESSAGE,
1520
TransactionStatus,
1621
} from './cardano.utils';
17-
import { logger } from '../../services/logger';
18-
import { TokenListResolutionStrategy } from '../../services/token-list-resolution';
19-
20-
//import { Cardanoish } from "../../services/common-interfaces";
21-
export type CardanoTokenInfo = {
22-
policyId: string;
23-
assetName: string;
24-
decimals: number;
25-
name: string;
26-
symbol: string;
27-
logoURI: string;
28-
address: string;
29-
};
3022

3123
export class Cardano {
3224
private static _instances: { [name: string]: Cardano };
33-
public tokenList: CardanoTokenInfo[] = [];
25+
public tokenList: CardanoToken[] = [];
3426
public config: Config;
35-
public tokenMap: Record<string, CardanoTokenInfo> = {};
27+
public tokenMap: Record<string, CardanoToken> = {};
3628
private _tokenListSource: string;
37-
private _tokenListType: TokenListType;
3829
public lucidInstance: Lucid | null = null;
3930
public network: string;
4031
private _chain: string;
@@ -45,11 +36,7 @@ export class Cardano {
4536

4637
private constructor(network: string) {
4738
// Throw error if network is not 'mainnet' or 'preprod'
48-
if (
49-
network !== 'mainnet' &&
50-
network !== 'preprod' &&
51-
network !== 'preview'
52-
) {
39+
if (network !== 'mainnet' && network !== 'preprod' && network !== 'preview') {
5340
throw new HttpException(503, NETWORK_ERROR_MESSAGE, NETWORK_ERROR_CODE);
5441
}
5542
this.config = getCardanoConfig('cardano', network);
@@ -59,7 +46,6 @@ export class Cardano {
5946
this.network = this.config.network.name;
6047
this.nativeTokenSymbol = this.config.network.nativeCurrencySymbol;
6148
this._tokenListSource = this.config.network.tokenListSource;
62-
this._tokenListType = <TokenListType>this.config.network.tokenListType;
6349
this.projectId = this.config.network.projectId;
6450
}
6551
public static async getInstance(network: string): Promise<Cardano> {
@@ -70,21 +56,14 @@ export class Cardano {
7056
if (!Cardano._instances[network]) {
7157
const instance = new Cardano(network);
7258

73-
if (
74-
instance.projectId &&
75-
instance.projectId.toLowerCase().startsWith(network.toLowerCase())
76-
) {
59+
if (instance.projectId && instance.projectId.toLowerCase().startsWith(network.toLowerCase())) {
7760
try {
7861
await instance.init();
7962
} catch (err: any) {
80-
logger.warn(
81-
`[Cardano] initial init() skipped for network="${network}": ${err.message}`,
82-
);
63+
logger.warn(`[Cardano] initial init() skipped for network="${network}": ${err.message}`);
8364
}
8465
} else {
85-
logger.info(
86-
`[Cardano] skipped init() for network="${network}" because projectId is still placeholder`,
87-
);
66+
logger.info(`[Cardano] skipped init() for network="${network}" because projectId is still placeholder`);
8867
}
8968

9069
Cardano._instances[network] = instance;
@@ -109,18 +88,17 @@ export class Cardano {
10988
if (!this.lucidInstance) {
11089
this.lucidInstance = await Lucid.new(
11190
new Blockfrost(this.apiURL, this.projectId),
112-
this.network === 'preprod'
113-
? 'Preprod'
114-
: this.network === 'preview'
115-
? 'Preview'
116-
: 'Mainnet',
91+
this.network === 'preprod' ? 'Preprod' : this.network === 'preview' ? 'Preview' : 'Mainnet',
11792
);
11893
}
11994

120-
if (!this._ready) {
121-
// Ensure we only set ready once
95+
try {
96+
await this.loadTokens();
12297
this._ready = true;
123-
await this.loadTokens(this._tokenListSource, this._tokenListType);
98+
logger.info(`Cardano chain initialized for network=${this.network}`);
99+
} catch (e) {
100+
logger.error(`Failed to initialize Cardano chain: ${e}`);
101+
throw e;
124102
}
125103
}
126104

@@ -136,9 +114,7 @@ export class Cardano {
136114
address: string;
137115
}> {
138116
if (!this._ready) {
139-
throw new Error(
140-
'Cardano instance is not initialized. Call `init` first.',
141-
);
117+
throw new Error('Cardano instance is not initialized. Call `init` first.');
142118
}
143119

144120
try {
@@ -149,18 +125,13 @@ export class Cardano {
149125
const address = await lucid.wallet.address();
150126
return { address };
151127
} catch (error: any) {
152-
throw new Error(
153-
`Error retrieving wallet from private key: ${error.message}`,
154-
);
128+
throw new Error(`Error retrieving wallet from private key: ${error.message}`);
155129
}
156130
}
157131

158132
public async getWalletFromAddress(address: string): Promise<string> {
159133
const path = `${walletPath}/${this._chain}`;
160-
const encryptedPrivateKey: string = await fse.readFile(
161-
`${path}/${address}.json`,
162-
'utf8',
163-
);
134+
const encryptedPrivateKey: string = await fse.readFile(`${path}/${address}.json`, 'utf8');
164135
const passphrase = ConfigManagerCertPassphrase.readPassphrase();
165136
if (!passphrase) {
166137
throw new Error('missing passphrase');
@@ -182,29 +153,20 @@ export class Cardano {
182153
const utxos = await Lucid.utxosAt(address);
183154

184155
// Calculate total balance in ADA using BigInt
185-
const totalLovelace = utxos.reduce(
186-
(acc, utxo) => acc + (utxo.assets.lovelace || 0n),
187-
0n,
188-
);
156+
const totalLovelace = utxos.reduce((acc, utxo) => acc + (utxo.assets.lovelace || 0n), 0n);
189157

190158
// Convert Lovelace (BigInt) to ADA (Number)
191159
const balanceInADA = Number(totalLovelace) / 1_000_000;
192160

193161
return balanceInADA.toString();
194162
}
195163
// get Asset balance like MIN and LP
196-
async getAssetBalance(
197-
privateKey: string,
198-
token: CardanoTokenInfo,
199-
): Promise<string> {
200-
let tokenAdress: string;
201-
164+
async getAssetBalance(privateKey: string, token: CardanoToken): Promise<string> {
202165
// If token information is not found, throw an error
203166
if (!token || Object.keys(token).length === 0) {
204167
throw new Error(`Token ${token} is not supported.`);
205168
}
206-
207-
tokenAdress = token.policyId + token.assetName;
169+
const tokenAddress: string = token.policyId + token.assetName;
208170

209171
const Lucid = this.getLucid();
210172
Lucid.selectWalletFromPrivateKey(privateKey);
@@ -217,36 +179,24 @@ export class Cardano {
217179

218180
// Calculate token balance
219181
const calculatedTokenBalance = utxos.reduce((acc, utxo) => {
220-
if (utxo.assets[tokenAdress]) {
221-
return acc + Number(utxo.assets[tokenAdress]);
182+
if (utxo.assets[tokenAddress]) {
183+
return acc + Number(utxo.assets[tokenAddress]);
222184
}
223185
return acc;
224186
}, 0);
225187
// Divide raw balance by 10^decimals to get the actual amount
226188
const decimals = token.decimals;
227189
const actualTokenBalance = calculatedTokenBalance / Math.pow(10, decimals);
228-
logger.debug(
229-
`Token balance for ${address}: ${actualTokenBalance.toString()}`,
230-
);
190+
logger.debug(`Token balance for ${address}: ${actualTokenBalance.toString()}`);
231191
return actualTokenBalance.toString();
232192
}
233193

234194
async encrypt(secret: string, password: string): Promise<string> {
235195
const algorithm = 'aes-256-ctr';
236196
const iv = crypto.randomBytes(16);
237197
const salt = crypto.randomBytes(32);
238-
const key = crypto.pbkdf2Sync(
239-
password,
240-
new Uint8Array(salt),
241-
5000,
242-
32,
243-
'sha512',
244-
);
245-
const cipher = crypto.createCipheriv(
246-
algorithm,
247-
new Uint8Array(key),
248-
new Uint8Array(iv),
249-
);
198+
const key = crypto.pbkdf2Sync(password, new Uint8Array(salt), 5000, 32, 'sha512');
199+
const cipher = crypto.createCipheriv(algorithm, new Uint8Array(key), new Uint8Array(iv));
250200

251201
const encryptedBuffers = [
252202
new Uint8Array(cipher.update(new Uint8Array(Buffer.from(secret)))),
@@ -273,16 +223,10 @@ export class Cardano {
273223

274224
const key = crypto.pbkdf2Sync(password, salt, 5000, 32, 'sha512');
275225

276-
const decipher = crypto.createDecipheriv(
277-
hash.algorithm,
278-
new Uint8Array(key),
279-
iv,
280-
);
226+
const decipher = crypto.createDecipheriv(hash.algorithm, new Uint8Array(key), iv);
281227

282228
const decryptedBuffers = [
283-
new Uint8Array(
284-
decipher.update(new Uint8Array(Buffer.from(hash.encrypted, 'hex'))),
285-
),
229+
new Uint8Array(decipher.update(new Uint8Array(Buffer.from(hash.encrypted, 'hex')))),
286230
new Uint8Array(decipher.final()),
287231
];
288232
const decrypted = Buffer.concat(decryptedBuffers);
@@ -342,57 +286,62 @@ export class Cardano {
342286
/**
343287
* Load tokens from the token list source
344288
*/
345-
public async loadTokens(
346-
tokenListSource: string,
347-
tokenListType: TokenListType,
348-
): Promise<void> {
349-
logger.info(
350-
`Loading tokens for cardano from ${tokenListType} source: ${tokenListSource}`,
351-
);
289+
public async loadTokens(): Promise<void> {
290+
logger.info(`Loading tokens for cardano/${this.network} using TokenService`);
291+
352292
try {
353-
this.tokenList = await this.getTokenList(tokenListSource, tokenListType);
293+
// Use TokenService to load tokens
294+
const tokens = await TokenService.getInstance().loadTokenList('cardano', this.network);
295+
296+
// Transform to CardanoToken format with required Cardano-specific properties
297+
this.tokenList = tokens.map((token): CardanoToken => {
298+
let policyId: string;
299+
let assetName: string;
300+
301+
// Check if this is a Cardano native token (ADA)
302+
if (token.address === 'ada.lovelace' || token.symbol.toLowerCase() === 'ada') {
303+
// Native ADA token - empty policyId and assetName
304+
policyId = '';
305+
assetName = '';
306+
} else if (token.address.includes('.')) {
307+
// Custom token with format: policyId.assetName
308+
const addressParts = token.address.split('.');
309+
policyId = addressParts[0];
310+
assetName = addressParts[1] || '';
311+
} else {
312+
// Custom token with just policyId (no asset name)
313+
policyId = token.address;
314+
assetName = '';
315+
}
316+
return {
317+
...token,
318+
policyId: policyId,
319+
assetName: assetName,
320+
};
321+
});
354322

355323
if (this.tokenList) {
356-
logger.info(`Loaded ${this.tokenList.length} tokens for cardano`);
324+
logger.info(`Loaded ${this.tokenList.length} tokens for cardano/${this.network}`);
357325
// Build token map for faster lookups
358-
this.tokenList.forEach(
359-
(token: CardanoTokenInfo) => (this.tokenMap[token.symbol] = token),
360-
);
326+
this.tokenList.forEach((token: CardanoToken) => (this.tokenMap[token.symbol] = token));
361327
}
362328
} catch (error) {
363329
logger.error(`Failed to load token list: ${error.message}`);
364330
throw error;
365331
}
366332
}
367333

368-
/**
369-
* Get token list from source
370-
*/
371-
private async getTokenList(
372-
tokenListSource: string,
373-
tokenListType: TokenListType,
374-
): Promise<CardanoTokenInfo[]> {
375-
const tokensList = await new TokenListResolutionStrategy(
376-
tokenListSource,
377-
tokenListType,
378-
).resolve();
379-
380-
// Normalize addresses
381-
return tokensList.tokens;
382-
}
383-
384-
public get storedTokenList(): CardanoTokenInfo[] {
334+
public get storedTokenList(): CardanoToken[] {
385335
return this.tokenList;
386336
}
387337

388338
/**
389339
* Get token info by symbol or address
390340
*/
391-
public getTokenBySymbol(tokenSymbol: string): CardanoTokenInfo | undefined {
341+
public getTokenBySymbol(tokenSymbol: string): CardanoToken | undefined {
392342
// First try to find token by symbol
393343
const tokenBySymbol = this.tokenList.find(
394-
(token: CardanoTokenInfo) =>
395-
token.symbol.toUpperCase() === tokenSymbol.toUpperCase(),
344+
(token: CardanoToken) => token.symbol.toUpperCase() === tokenSymbol.toUpperCase(),
396345
);
397346

398347
if (tokenBySymbol) {
@@ -402,15 +351,11 @@ export class Cardano {
402351

403352
public getTokenAddress(symbol: string): string {
404353
let tokenAddress: string = '';
405-
let tokenInfo = this.getTokenBySymbol(symbol);
354+
const tokenInfo = this.getTokenBySymbol(symbol);
406355
// If token information is not found, throw an error
407356
if (!tokenInfo || Object.keys(tokenInfo).length === 0) {
408357
// Handle token not supported errors
409-
throw new HttpException(
410-
500,
411-
TOKEN_NOT_SUPPORTED_ERROR_MESSAGE,
412-
TOKEN_NOT_SUPPORTED_ERROR_CODE,
413-
);
358+
throw new HttpException(500, TOKEN_NOT_SUPPORTED_ERROR_MESSAGE, TOKEN_NOT_SUPPORTED_ERROR_CODE);
414359
}
415360

416361
tokenAddress = tokenInfo[0]?.policyId + tokenInfo[0]?.assetName;
@@ -441,9 +386,7 @@ export class Cardano {
441386

442387
// Validate it looks like an Cardano address
443388
if (!walletAddress.startsWith('addr')) {
444-
logger.warn(
445-
`Invalid Cardano address found in wallet directory: ${walletAddress}`,
446-
);
389+
logger.warn(`Invalid Cardano address found in wallet directory: ${walletAddress}`);
447390
return null;
448391
}
449392

@@ -474,13 +417,9 @@ export class Cardano {
474417
return utxos;
475418
} catch (error: any) {
476419
// 4) log the failure for debugging
477-
logger.error(
478-
`Cardano.getUtxos failed for address ${address}: ${error.message || error}`,
479-
);
420+
logger.error(`Cardano.getUtxos failed for address ${address}: ${error.message || error}`);
480421
// 5) rethrow a trimmed error
481-
throw new Error(
482-
`Unable to fetch UTxOs for ${address}: ${error.message || error}`,
483-
);
422+
throw new Error(`Unable to fetch UTxOs for ${address}: ${error.message || error}`);
484423
}
485424
}
486425

0 commit comments

Comments
 (0)