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+
49import { ConfigManagerCertPassphrase } from '../../services/config-manager-cert-passphrase' ;
10+ import { logger } from '../../services/logger' ;
511import { 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' ;
914import {
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
3123export 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