import { TSMap } from 'typescript-map' ; import { Subject , of , Observable , Subscription } from 'rxjs' ; import { filter , share } from 'rxjs/operators' ; import BN from 'bn.js' ; import { RadixAccountSystem , RadixAtomUpdate , RadixAtomObservation , RadixAtomStatusIsInsert , logger } from '../..' ; import { RadixTokenDefinition , RadixTokenSupplyType } from '../token/RadixTokenDefinition' ; import { RadixSpin , RadixAddress , RadixTokenPermissionsValues , RadixUnallocatedTokensParticle , RRI , RadixTransferrableTokensParticle , RadixFixedSupplyTokenDefinitionParticle , RadixMutableSupplyTokenDefinitionParticle , } from '../atommodel' ; export class RadixTokenDefinitionAccountSystem implements RadixAccountSystem { public name = 'TOKENS' ; public tokenDefinitions = new TSMap < string , RadixTokenDefinition > ( ) ; private tokenDefinitionSubject : Subject < RadixTokenDefinition > = new Subject ( ) ; private processedAtomHIDs = new TSMap < string , boolean > ( ) ; private subs = new Subscription ( ) ; constructor ( readonly address ) { } public unsubscribeSubscribers ( ) { this . subs . unsubscribe ( ) ; } public processAtomUpdate ( atomUpdate ) { if ( ! atomUpdate . atom . containsParticle ( RadixFixedSupplyTokenDefinitionParticle , RadixMutableSupplyTokenDefinitionParticle , RadixUnallocatedTokensParticle ) ) { return ; } if ( RadixAtomStatusIsInsert [ atomUpdate . status . status ] ) { this . processStoreAtom ( atomUpdate ) ; } else { this . processDeleteAtom ( atomUpdate ) ; } } public processStoreAtom ( atomUpdate ) { const atom = atomUpdate . atom ; if ( this . processedAtomHIDs . has ( atom . getAidString ( ) ) ) { return ; } this . processedAtomHIDs . set ( atom . getAidString ( ) , true ) ; for ( const particleGroup of atom . getParticleGroups ( ) ) { let tokenDefinition ; if ( particleGroup . containsParticle ( RadixFixedSupplyTokenDefinitionParticle ) ) { for ( const spunParticle of particleGroup . getParticles ( ) ) { if ( spunParticle . particle instanceof RadixFixedSupplyTokenDefinitionParticle && spunParticle . spin === RadixSpin . UP ) { this . createOrUpdateFixedTokenDefinition ( spunParticle . particle ) ; } } } else if ( particleGroup . containsParticle ( RadixMutableSupplyTokenDefinitionParticle ) ) { for ( const spunParticle of particleGroup . getParticles ( ) ) { if ( spunParticle . particle instanceof RadixMutableSupplyTokenDefinitionParticle && spunParticle . spin === RadixSpin . UP ) { this . createOrUpdateMutableTokenDefinition ( spunParticle . particle ) ; } else if ( spunParticle . particle instanceof RadixUnallocatedTokensParticle ) { const particle = ( spunParticle . particle as RadixUnallocatedTokensParticle ) ; tokenDefinition = this . getOrCreateTokenDefinition ( particle . getTokenDefinitionReference ( ) ) ; if ( spunParticle . spin === RadixSpin . UP ) { tokenDefinition . unallocatedTokens . set ( particle . getHidString ( ) , particle ) ; } else { tokenDefinition . unallocatedTokens . delete ( particle . getHidString ( ) ) ; } } } } else if ( particleGroup . containsParticle ( RadixUnallocatedTokensParticle ) && particleGroup . containsParticle ( RadixTransferrableTokensParticle ) ) { for ( const spunParticle of particleGroup . getParticles ( ) ) { if ( spunParticle . particle instanceof RadixUnallocatedTokensParticle ) { const particle = ( spunParticle . particle as RadixUnallocatedTokensParticle ) ; tokenDefinition = this . getOrCreateTokenDefinition ( particle . getTokenDefinitionReference ( ) ) ; if ( spunParticle . spin === RadixSpin . UP ) { tokenDefinition . unallocatedTokens . set ( particle . getHidString ( ) , particle ) ; tokenDefinition . addTotalSupply ( particle . getAmount ( ) . neg ( ) ) ; } else { tokenDefinition . unallocatedTokens . delete ( particle . getHidString ( ) ) ; tokenDefinition . addTotalSupply ( particle . getAmount ( ) ) ; } } } } if ( tokenDefinition ) { if ( ! this . tokenDefinitionSubject . closed && ! this . tokenDefinitionSubject . isStopped ) { this . tokenDefinitionSubject . next ( tokenDefinition ) ; } else { logger . error ( `☢️ tokenDefinitionSubject closed or stopped` ) ; } } } } public processDeleteAtom ( atomUpdate ) { const atom = atomUpdate . atom ; if ( ! this . processedAtomHIDs . has ( atom . getAidString ( ) ) ) { return ; } this . processedAtomHIDs . delete ( atom . getAidString ( ) ) ; for ( const particleGroup of atom . getParticleGroups ( ) ) { let tokenDefinition ; if ( particleGroup . containsParticle ( RadixFixedSupplyTokenDefinitionParticle ) ) { for ( const spunParticle of particleGroup . getParticles ( ) ) { if ( spunParticle . particle instanceof RadixFixedSupplyTokenDefinitionParticle && spunParticle . spin === RadixSpin . DOWN ) { this . createOrUpdateFixedTokenDefinition ( spunParticle . particle ) ; } } } else if ( particleGroup . containsParticle ( RadixMutableSupplyTokenDefinitionParticle ) ) { for ( const spunParticle of particleGroup . getParticles ( ) ) { if ( spunParticle . particle instanceof RadixMutableSupplyTokenDefinitionParticle && spunParticle . spin === RadixSpin . DOWN ) { this . createOrUpdateMutableTokenDefinition ( spunParticle . particle ) ; } else if ( spunParticle . particle instanceof RadixUnallocatedTokensParticle ) { const particle = ( spunParticle . particle as RadixUnallocatedTokensParticle ) ; tokenDefinition = this . getOrCreateTokenDefinition ( particle . getTokenDefinitionReference ( ) ) ; if ( spunParticle . spin === RadixSpin . DOWN ) { tokenDefinition . unallocatedTokens . set ( particle . getHidString ( ) , particle ) ; } else { tokenDefinition . unallocatedTokens . delete ( particle . getHidString ( ) ) ; } } } } else if ( particleGroup . containsParticle ( RadixUnallocatedTokensParticle ) && particleGroup . containsParticle ( RadixTransferrableTokensParticle ) ) { for ( const spunParticle of particleGroup . getParticles ( ) ) { if ( spunParticle . particle instanceof RadixUnallocatedTokensParticle ) { const particle = ( spunParticle . particle as RadixUnallocatedTokensParticle ) ; tokenDefinition = this . getOrCreateTokenDefinition ( particle . getTokenDefinitionReference ( ) ) ; if ( spunParticle . spin === RadixSpin . DOWN ) { tokenDefinition . unallocatedTokens . set ( particle . getHidString ( ) , particle ) ; tokenDefinition . addTotalSupply ( particle . getAmount ( ) . neg ( ) ) ; } else { tokenDefinition . unallocatedTokens . delete ( particle . getHidString ( ) ) ; tokenDefinition . addTotalSupply ( particle . getAmount ( ) ) ; } } } } if ( tokenDefinition ) { if ( ! this . tokenDefinitionSubject . closed && ! this . tokenDefinitionSubject . isStopped ) { this . tokenDefinitionSubject . next ( tokenDefinition ) ; } else { logger . error ( `☢️ tokenDefinitionSubject closed or stopped` ) ; } } } } private createOrUpdateFixedTokenDefinition ( particle ) { const reference = particle . getRRI ( ) ; const tokenDefinition = this . getOrCreateTokenDefinition ( reference ) ; tokenDefinition . symbol = reference . getName ( ) ; tokenDefinition . name = particle . name ; tokenDefinition . description = particle . description ; tokenDefinition . granularity = particle . granularity ; tokenDefinition . iconUrl = particle . iconUrl ; tokenDefinition . tokenSupplyType = RadixTokenSupplyType . FIXED ; tokenDefinition . totalSupply = particle . getSupply ( ) ; if ( ! this . tokenDefinitionSubject . closed && ! this . tokenDefinitionSubject . isStopped ) { this . tokenDefinitionSubject . next ( tokenDefinition ) ; } else { logger . error ( `☢️ tokenDefinitionSubject closed or stopped` ) ; } } private createOrUpdateMutableTokenDefinition ( particle ) { const reference = particle . getRRI ( ) ; const tokenDefinition = this . getOrCreateTokenDefinition ( reference ) ; tokenDefinition . symbol = reference . getName ( ) ; tokenDefinition . name = particle . name ; tokenDefinition . description = particle . description ; tokenDefinition . granularity = particle . granularity ; tokenDefinition . iconUrl = particle . iconUrl ; tokenDefinition . tokenSupplyType = RadixTokenSupplyType . MUTABLE ; if ( ! this . tokenDefinitionSubject . closed && ! this . tokenDefinitionSubject . isStopped ) { this . tokenDefinitionSubject . next ( tokenDefinition ) ; } else { logger . error ( `☢️ tokenDefinitionSubject closed or stopped` ) ; } } private getOrCreateTokenDefinition ( reference ) { if ( ! this . tokenDefinitions . has ( reference . getName ( ) ) ) { this . tokenDefinitions . set ( reference . getName ( ) , new RadixTokenDefinition ( reference . address , reference . getName ( ) ) ) ; } return this . tokenDefinitions . get ( reference . getName ( ) ) ; } public getTokenDefinition ( symbol ) { if ( this . tokenDefinitions . has ( symbol ) ) { return this . tokenDefinitions . get ( symbol ) ; } return null ; } public getTokenDefinitionObservable ( symbol ) { return Observable . create ( ( observer ) => { if ( this . tokenDefinitions . has ( symbol ) ) { observer . next ( this . tokenDefinitions . get ( symbol ) ) ; } this . subs . add ( this . tokenDefinitionSubject . pipe ( filter ( x => x . symbol === symbol ) ) . subscribe ( observer ) ) ; } ) ; } public getAllTokenDefinitionObservable ( ) { return this . tokenDefinitionSubject . pipe ( share ( ) ) ; } }