import { CoinSelectionWallet } from './wallet/coin-selection-wallet' ; import { generateMnemonic , mnemonicToEntropy } from 'bip39' ; import { Address , AssetName , Assets , AuxiliaryData , AuxiliaryDataHash , BaseAddress , BigNum , Bip32PrivateKey , Bip32PublicKey , Ed25519KeyHash , Ed25519Signature , EnterpriseAddress , GeneralTransactionMetadata , hash_auxiliary_data , hash_transaction , Int , LinearFee , make_vkey_witness , MetadataList , MetadataMap , Mint , MintAssets , min_ada_required , min_fee , MultiAsset , NativeScript , NativeScripts , NetworkInfo , PrivateKey , PublicKey , ScriptAll , ScriptAny , ScriptHash , ScriptHashNamespace , ScriptNOfK , ScriptPubkey , StakeCredential , TimelockExpiry , TimelockStart , Transaction , TransactionBody , TransactionBuilder , TransactionHash , TransactionInput , TransactionInputs , TransactionMetadatum , TransactionOutput , TransactionOutputs , TransactionWitnessSet , Value , Vkeywitnesses } from '@emurgo/cardano-serialization-lib-nodejs' ; import { Mainnet , Testnet } from './config/network.config' ; import { TokenWallet } from './wallet/token-wallet' ; import { ApiCoinSelectionChange , WalletsAssetsAvailable } from './models' ; import { AssetWallet } from './wallet/asset-wallet' ; import { Script } from './models/script.model' ; import { JsonScript , ScriptTypeEnum , scriptTypes } from './models/json-script.model' ; import { ExtendedSigningKey } from './models/payment-extended-signing-key' ; import { MultisigTransaction } from './models/multisig-transaction' ; const phrasesLengthMap = { 12 : 128 , 15 : 160 , 18 : 192 , 21 : 224 , 24 : 256 } ; export class Seed { static generateRecoveryPhrase ( size = 15 ) { let strength = phrasesLengthMap [ size ] || phrasesLengthMap [ 15 ] ; return generateMnemonic ( strength ) . trim ( ) ; } static toMnemonicList ( phrase ) { return phrase . trim ( ) . split ( /\s+/g ) ; } static deriveRootKey ( phrase ) { let mnemonic = Array . isArray ( phrase ) ? phrase . join ( " " ) : phrase ; const entropy = mnemonicToEntropy ( mnemonic ) ; const rootKey = Bip32PrivateKey . from_bip39_entropy ( Buffer . from ( entropy , 'hex' ) , Buffer . from ( '' ) ) ; return rootKey ; } static deriveAccountKey ( key , index = 0 ) { return key . derive ( Seed . harden ( CARDANO_PUROPOSE ) ) . derive ( Seed . harden ( CARDANO_COIN_TYPE ) ) . derive ( Seed . harden ( index ) ) ; } static deriveKey ( key , path ) { let result = key ; path . forEach ( p => { result = result . derive ( p . endsWith ( 'H' ) || p . endsWith ( "'" ) ? Seed . harden ( Number . parseInt ( p . substr ( 0 , p . length - 1 ) ) ) : Number . parseInt ( p ) ) ; } ) ; return result ; } static buildTransaction ( coinSelection , ttl , opts = { changeAddress : "" , metadata : null as any , startSlot : 0 , config : Mainnet } ) { let config = opts . config || Mainnet ; let metadata = opts . metadata ; let startSlot = opts . startSlot || 0 ; let txBuilder = TransactionBuilder . new ( LinearFee . new ( BigNum . from_str ( config . protocols . txFeePerByte . toString ( ) ) , BigNum . from_str ( config . protocols . txFeeFixed . toString ( ) ) ) , BigNum . from_str ( config . protocols . minUTxOValue . toString ( ) ) , BigNum . from_str ( config . protocols . stakePoolDeposit . toString ( ) ) , BigNum . from_str ( config . protocols . stakeAddressDeposit . toString ( ) ) , config . protocols . maxValueSize , config . protocols . maxTxSize ) ; coinSelection . inputs . forEach ( ( input , i ) => { let address = Address . from_bech32 ( input . address ) ; let txInput = TransactionInput . new ( TransactionHash . from_bytes ( Buffer . from ( input . id , 'hex' ) ) , input . index ) ; let amount = Value . new ( BigNum . from_str ( input . amount . quantity . toString ( ) ) ) ; txBuilder . add_input ( address , txInput , amount ) ; } ) ; coinSelection . outputs . forEach ( output => { let address = Address . from_bech32 ( output . address ) ; let amount = Value . new ( BigNum . from_str ( output . amount . quantity . toString ( ) ) ) ; if ( output . assets && output . assets . length > 0 ) { let multiAsset = Seed . buildMultiAssets ( output . assets ) ; amount . set_multiasset ( multiAsset ) ; } let txOutput = TransactionOutput . new ( address , amount ) ; txBuilder . add_output ( txOutput ) ; } ) ; coinSelection . change . forEach ( change => { let address = Address . from_bech32 ( change . address ) ; let amount = Value . new ( BigNum . from_str ( change . amount . quantity . toString ( ) ) ) ; if ( change . assets && change . assets . length > 0 ) { let multiAsset = Seed . buildMultiAssets ( change . assets ) ; amount . set_multiasset ( multiAsset ) ; } let txOutput = TransactionOutput . new ( address , amount ) ; txBuilder . add_output ( txOutput ) ; } ) ; if ( metadata ) { txBuilder . set_auxiliary_data ( metadata ) ; } txBuilder . set_validity_start_interval ( startSlot ) ; txBuilder . set_ttl ( ttl ) ; if ( opts . changeAddress ) { let address = Address . from_bech32 ( opts . changeAddress ) ; txBuilder . add_change_if_needed ( address ) ; } else { let fee = opts . fee || coinSelection . inputs . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) + ( coinSelection . withdrawals ?. reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) || 0 ) - coinSelection . outputs . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) - coinSelection . change . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) - ( coinSelection . deposits ?. reduce ( ( acc , c ) => c . quantity + acc , 0 ) || 0 ) ; txBuilder . set_fee ( BigNum . from_str ( fee . toString ( ) ) ) ; } let txBody = txBuilder . build ( ) ; return txBody ; } static buildTransactionWithToken ( coinSelection , ttl , tokens , signingKeys , opts = { changeAddress : "" , data : null as any , startSlot : 0 , config : Mainnet } , encoding = 'hex' ) { let metadata = opts . data ? Seed . buildTransactionMetadata ( opts . data ) : null ; opts . config = opts . config || Mainnet ; const fee = parseInt ( opts . config . protocols . maxTxSize * opts . config . protocols . txFeePerByte + opts . config . protocols . txFeeFixed ) ; if ( ! opts . fee ) { opts . fee = fee ; if ( coinSelection . change && coinSelection . change . length > 0 ) { const selectionfee = coinSelection . inputs . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) + ( coinSelection . withdrawals ?. reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) || 0 ) - coinSelection . outputs . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) - coinSelection . change . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) - ( coinSelection . deposits ?. reduce ( ( acc , c ) => c . quantity + acc , 0 ) || 0 ) ; const feePerChange = Math . ceil ( ( opts . fee - selectionfee ) / coinSelection . change . length ) ; coinSelection . change = coinSelection . change . map ( change => { change . amount . quantity -= feePerChange ; return change ; } ) ; } } let buildOpts = Object . assign ( { } , { metadata : metadata , ... opts } ) ; let mint = Seed . buildTransactionMint ( tokens , encoding ) ; let scripts = tokens . map ( t => t . script ) ; let txBody = Seed . buildTransaction ( coinSelection , ttl , buildOpts ) ; txBody . set_mint ( mint ) ; let tx = Seed . sign ( txBody , signingKeys , metadata , scripts ) ; const txFee = parseInt ( Seed . getTransactionFee ( tx , opts . config ) . to_str ( ) ) ; const finalFee = txFee ; opts . fee = finalFee ; const feeDiff = fee - finalFee ; if ( coinSelection . change && coinSelection . change . length > 0 ) { const feeDiffPerChange = Math . ceil ( feeDiff / coinSelection . change . length ) ; coinSelection . change = coinSelection . change . map ( c => { c . amount . quantity += feeDiffPerChange ; return c ; } ) ; } metadata = opts . data ? Seed . buildTransactionMetadata ( opts . data ) : null ; buildOpts = Object . assign ( { } , { metadata : metadata , ... opts } ) ; txBody = Seed . buildTransaction ( coinSelection , ttl , buildOpts ) ; txBody . set_mint ( mint ) ; return txBody ; } static buildTransactionMultisig ( coinSelection , ttl , scripts , tokens = null , signingKeys = null , opts = { changeAddress : "" , data : null , startSlot : 0 , config : Mainnet } , encoding = 'hex' ) { const config = opts . config || Mainnet ; let metadata = opts . data ? Seed . buildTransactionMetadata ( opts . data ) : null ; const startSlot = opts . startSlot || 0 ; const selectionfee = coinSelection . inputs . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) + ( coinSelection . withdrawals ?. reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) || 0 ) - coinSelection . outputs . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) - coinSelection . change . reduce ( ( acc , c ) => c . amount . quantity + acc , 0 ) - ( coinSelection . deposits ?. reduce ( ( acc , c ) => c . quantity + acc , 0 ) || 0 ) ; const inputs = coinSelection . inputs . map ( ( input , i ) => { return TransactionInput . new ( TransactionHash . from_bytes ( Buffer . from ( input . id , 'hex' ) ) , input . index ) ; } ) ; let outputs = coinSelection . outputs . map ( output => { let address = Address . from_bech32 ( output . address ) ; let amount = Value . new ( BigNum . from_str ( output . amount . quantity . toString ( ) ) ) ; if ( output . assets && output . assets . length > 0 ) { let multiAsset = Seed . buildMultiAssets ( output . assets , encoding ) ; amount . set_multiasset ( multiAsset ) ; } return TransactionOutput . new ( address , amount ) ; } ) ; if ( coinSelection . change && coinSelection . change . length > 0 ) { outputs . push ( ... coinSelection . change . map ( change => { let address = Address . from_bech32 ( change . address ) ; let amount = Value . new ( BigNum . from_str ( change . amount . quantity . toString ( ) ) ) ; if ( change . assets && change . assets . length > 0 ) { let multiAsset = Seed . buildMultiAssets ( change . assets , encoding ) ; amount . set_multiasset ( multiAsset ) ; } return TransactionOutput . new ( address , amount ) ; } ) ) ; } const txInputs = TransactionInputs . new ( ) ; inputs . forEach ( txin => txInputs . add ( txin ) ) ; let txOutputs = TransactionOutputs . new ( ) ; outputs . forEach ( txout => txOutputs . add ( txout ) ) ; const txBody = TransactionBody . new ( txInputs , txOutputs , BigNum . from_str ( selectionfee . toString ( ) ) , ttl ) ; if ( metadata ) { const dataHash = hash_auxiliary_data ( metadata ) ; txBody . set_auxiliary_data_hash ( dataHash ) ; } if ( tokens ) { const mint = Seed . buildTransactionMint ( tokens , encoding ) ; txBody . set_mint ( mint ) ; } txBody . set_validity_start_interval ( startSlot ) ; return new MultisigTransaction ( coinSelection , txBody , scripts , signingKeys , config , encoding , metadata , tokens ) ; } static buildMultiAssets ( assets , encoding = 'hex' ) { let multiAsset = MultiAsset . new ( ) ; const groups = assets . reduce ( ( dict , asset ) => { ( dict [ asset . policy_id ] = dict [ asset . policy_id ] || [ ] ) . push ( asset ) ; return dict ; } , { } ) ; for ( const policy_id in groups ) { const scriptHash = Seed . getScriptHashFromPolicy ( policy_id ) ; let asset = Assets . new ( ) ; const assetGroups = groups [ policy_id ] . reduce ( ( dict , asset ) => { dict [ asset . asset_name ] = ( dict [ asset . asset_name ] || 0 ) + + asset . quantity ; return dict ; } , { } ) ; for ( const asset_name in assetGroups ) { const quantity = assetGroups [ asset_name ] ; const assetName = AssetName . new ( Buffer . from ( asset_name , encoding ) ) ; asset . insert ( assetName , BigNum . from_str ( quantity . toString ( ) ) ) ; } multiAsset . insert ( scriptHash , asset ) ; } return multiAsset ; } static buildTransactionMint ( tokens , encoding = 'utf8' ) { let mint = Mint . new ( ) ; const groups = tokens . reduce ( ( dict , asset ) => { ( dict [ asset . asset . policy_id ] = dict [ asset . asset . policy_id ] || [ ] ) . push ( asset ) ; return dict ; } , { } ) ; for ( const policy_id in groups ) { const scriptHash = Seed . getScriptHashFromPolicy ( policy_id ) ; let mintAssets = MintAssets . new ( ) ; const assetGroups = groups [ policy_id ] . reduce ( ( dict , asset ) => { dict [ asset . asset . asset_name ] = ( dict [ asset . asset . asset_name ] || 0 ) + + asset . asset . quantity ; return dict ; } , { } ) ; for ( const asset_name in assetGroups ) { const quantity = assetGroups [ asset_name ] ; const assetName = AssetName . new ( Buffer . from ( asset_name , encoding ) ) ; mintAssets . insert ( assetName , Int . new_i32 ( quantity ) ) ; } mint . insert ( scriptHash , mintAssets ) ; } return mint ; } static getTransactionFee ( tx , config = Mainnet ) { return min_fee ( tx , LinearFee . new ( BigNum . from_str ( config . protocols . txFeePerByte . toString ( ) ) , BigNum . from_str ( config . protocols . txFeeFixed . toString ( ) ) ) ) ; } static addKeyWitness ( transaction , prvKey ) { const vkeyWitnesses = Vkeywitnesses . new ( ) ; const txBody = transaction . body ( ) ; const txHash = hash_transaction ( txBody ) ; const vkeyWitness = make_vkey_witness ( txHash , prvKey ) ; vkeyWitnesses . add ( vkeyWitness ) ; const witnesses = transaction . witness_set ( ) ; witnesses . set_vkeys ( vkeyWitnesses ) ; return Transaction . new ( txBody , witnesses , transaction . auxiliary_data ( ) ) ; } static addScriptWitness ( transaction , script ) { const txBody = transaction . body ( ) ; const nativeScripts = NativeScripts . new ( ) ; nativeScripts . add ( script ) ; const witnesses = transaction . witness_set ( ) ; witnesses . set_native_scripts ( nativeScripts ) ; return Transaction . new ( txBody , witnesses , transaction . auxiliary_data ( ) ) ; } static sign ( txBody , privateKeys , transactionMetadata ? , scripts ? ) { const txHash = hash_transaction ( txBody ) ; const witnesses = TransactionWitnessSet . new ( ) ; const vkeyWitnesses = Vkeywitnesses . new ( ) ; if ( privateKeys ) { privateKeys . forEach ( prvKey => { const vkeyWitness = make_vkey_witness ( txHash , prvKey ) ; vkeyWitnesses . add ( vkeyWitness ) ; } ) ; } witnesses . set_vkeys ( vkeyWitnesses ) ; if ( scripts ) { let nativeScripts = NativeScripts . new ( ) ; scripts . forEach ( s => { nativeScripts . add ( s ) ; } ) ; witnesses . set_native_scripts ( nativeScripts ) ; } const transaction = Transaction . new ( txBody , witnesses , transactionMetadata ) ; return transaction ; } static signMessage ( key , message ) { return key . sign ( Buffer . from ( message ) ) . to_hex ( ) ; } static verifyMessage ( key , message , signed ) { return key . verify ( Buffer . from ( message ) , Ed25519Signature . from_hex ( signed ) ) ; } static getTxId ( transaction ) { const txBody = transaction . body ( ) ; const txHash = hash_transaction ( txBody ) ; const txId = Buffer . from ( txHash . to_bytes ( ) ) . toString ( 'hex' ) ; return txId ; } static convertPrivateKeyToSignKey ( prkKey ) { console . log ( prkKey . to_bech32 ( ) ) ; const cborHex = "5880" + Buffer . from ( prkKey . to_128_xprv ( ) ) . toString ( 'hex' ) ; return new ExtendedSigningKey ( cborHex ) ; } static harden ( num ) { return 0x80000000 + num ; } static constructMetadata ( data ) { let metadata = { } ; if ( Array . isArray ( data ) ) { for ( let i = 0 ; i < data . length ; i ++ ) { const value = data [ i ] ; metadata [ i ] = Seed . getMetadataObject ( value ) ; } } else { let keys = Object . keys ( data ) ; for ( let i = 0 ; i < keys . length ; i ++ ) { const key = keys [ i ] ; if ( this . isInteger ( key ) ) { let index = parseInt ( key ) ; metadata [ index ] = Seed . getMetadataObject ( data [ key ] ) ; } } } return metadata ; } static getMetadataObject ( data ) { let result = { } ; let type = typeof data ; if ( type == "number" ) { result [ MetadateTypesEnum . Number ] = data ; } else if ( type == "string" && Buffer . byteLength ( data , 'utf-8' ) <= 64 ) { result [ MetadateTypesEnum . String ] = data ; } else if ( Buffer . isBuffer ( data ) && Buffer . byteLength ( data , "hex" ) <= 64 ) { result [ MetadateTypesEnum . Bytes ] = data . toString ( "hex" ) ; } else if ( type == "boolean" ) { result [ MetadateTypesEnum . String ] = data . toString ( ) ; } else if ( type == "undefined" ) { result [ MetadateTypesEnum . String ] = "undefined" ; } else if ( Array . isArray ( data ) ) { result [ MetadateTypesEnum . List ] = data . map ( a => this . getMetadataObject ( a ) ) ; } else if ( type == "object" ) { if ( data ) { result [ MetadateTypesEnum . Map ] = Object . keys ( data ) . map ( k => { return { "k" : this . getMetadataObject ( k ) , "v" : this . getMetadataObject ( data [ k ] ) } ; } ) ; } else { result [ MetadateTypesEnum . String ] = "null" ; } } return result ; } static reverseMetadata ( data , type = "object" ) { if ( ! data ) { return null ; } let metadata = type == "object" ? { } : [ ] ; let keys = Object . keys ( data ) ; for ( let i = 0 ; i < keys . length ; i ++ ) { const key = keys [ i ] ; let index = parseInt ( key ) ; metadata [ index ] = Seed . reverseMetadataObject ( data [ key ] ) ; } return metadata ; } static reverseMetadataObject ( data ) { let result = [ ] ; let keys = Object . keys ( data ) ; for ( let i = 0 ; i < keys . length ; i ++ ) { const key = keys [ i ] ; let value = data [ key ] ; if ( key == "string" ) { result . push ( value ) ; } else if ( key == "int" ) { result . push ( new Number ( value ) ) ; } else if ( key == "bytes" ) { result . push ( Buffer . from ( value , 'hex' ) ) ; } else if ( key == "list" ) { result . push ( value . map ( ( d ) => Seed . reverseMetadataObject ( d ) ) ) ; } else if ( key == "map" ) { let map = value . reduce ( ( acc , obj ) => { let k = Seed . reverseMetadataObject ( obj [ "k" ] ) ; let v = Seed . reverseMetadataObject ( obj [ "v" ] ) ; acc [ k ] = v ; return acc ; } , { } ) ; result . push ( map ) ; } else { result . push ( null ) ; } } return result . length == 1 ? result [ 0 ] : result ; } static buildTransactionMetadata ( data ) { let metadata = Seed . constructMetadata ( data ) ; let generalMetatada = GeneralTransactionMetadata . new ( ) ; for ( const key in metadata ) { let value = metadata [ key ] ; generalMetatada . insert ( BigNum . from_str ( key ) , Seed . getTransactionMetadatum ( value ) ) ; } let auxiliaryData = AuxiliaryData . new ( ) ; auxiliaryData . set_metadata ( generalMetatada ) ; return auxiliaryData ; } static getTransactionMetadatum ( value ) { if ( value . hasOwnProperty ( MetadateTypesEnum . Number ) ) { return TransactionMetadatum . new_int ( Int . new_i32 ( value [ MetadateTypesEnum . Number ] ) ) ; } if ( value . hasOwnProperty ( MetadateTypesEnum . String ) ) { return TransactionMetadatum . new_text ( value [ MetadateTypesEnum . String ] ) ; } if ( value . hasOwnProperty ( MetadateTypesEnum . Bytes ) ) { return TransactionMetadatum . new_bytes ( Buffer . from ( value [ MetadateTypesEnum . Bytes ] , 'hex' ) ) ; } if ( value . hasOwnProperty ( MetadateTypesEnum . List ) ) { let list = value [ MetadateTypesEnum . List ] ; let metalist = MetadataList . new ( ) ; for ( let i = 0 ; i < list . length ; i ++ ) { metalist . add ( Seed . getTransactionMetadatum ( list [ i ] ) ) ; } return TransactionMetadatum . new_list ( metalist ) ; } if ( value . hasOwnProperty ( MetadateTypesEnum . Map ) ) { let map = value [ MetadateTypesEnum . Map ] ; let metamap = MetadataMap . new ( ) ; for ( let i = 0 ; i < map . length ; i ++ ) { let { k , v } = map [ i ] ; metamap . insert ( Seed . getTransactionMetadatum ( k ) , Seed . getTransactionMetadatum ( v ) ) ; } return TransactionMetadatum . new_map ( metamap ) ; } } static generateKeyPair ( ) { let prvKey = Bip32PrivateKey . generate_ed25519_bip32 ( ) ; let pubKey = prvKey . to_public ( ) ; let pair = { privateKey : prvKey , publicKey : pubKey } ; return pair ; } static generateEnterpriseAddress ( pubKey , network = 'mainnet' ) { let networkId = network == 'mainnet' ? NetworkInfo . mainnet ( ) . network_id ( ) : NetworkInfo . testnet ( ) . network_id ( ) ; return EnterpriseAddress . new ( networkId , StakeCredential . from_keyhash ( pubKey . to_raw_key ( ) . hash ( ) ) ) . to_address ( ) ; } static getKeyHash ( key ) { return key . to_raw_key ( ) . hash ( ) ; } static buildSingleIssuerScript ( keyHash ) { let scriptPubKey = ScriptPubkey . new ( keyHash ) ; return NativeScript . new_script_pubkey ( scriptPubKey ) ; } static buildMultiIssuerAllScript ( scripts ) { let nativeScripts = this . buildNativeScripts ( scripts ) ; let scriptAll = ScriptAll . new ( nativeScripts ) ; return NativeScript . new_script_all ( scriptAll ) ; } static buildMultiIssuerAnyScript ( scripts ) { let nativeScripts = this . buildNativeScripts ( scripts ) ; let scriptAny = ScriptAny . new ( nativeScripts ) ; return NativeScript . new_script_any ( scriptAny ) ; } static buildMultiIssuerAtLeastScript ( n , scripts ) { let nativeScripts = this . buildNativeScripts ( scripts ) ; let scriptAtLeast = ScriptNOfK . new ( n , nativeScripts ) ; return NativeScript . new_script_n_of_k ( scriptAtLeast ) ; } static buildAfterScript ( slot ) { let scriptAfter = TimelockStart . new ( slot ) ; return NativeScript . new_timelock_start ( scriptAfter ) ; } static buildBeforeScript ( slot ) { let scriptBefore = TimelockExpiry . new ( slot ) ; return NativeScript . new_timelock_expiry ( scriptBefore ) ; } static getNativeScripts ( script ) { const result = [ ] ; const kind = script . root . kind ( ) ; if ( kind == 0 ) { result . push ( script . root ) ; } else if ( kind == 1 || kind == 2 || kind == 3 ) { result . push ( ... script . scripts . map ( s => s . root ) ) ; } return result ; } private static buildNativeScripts ( scripts ) { let nativeScripts = NativeScripts . new ( ) ; scripts . forEach ( script => { nativeScripts . add ( script ) ; } ) ; return nativeScripts ; } static getScriptHash ( script ) { let keyHash = script . hash ( ScriptHashNamespace . NativeScript ) ; let scriptHash = ScriptHash . from_bytes ( keyHash . to_bytes ( ) ) ; return scriptHash ; } static getPolicyId ( scriptHash ) { return Buffer . from ( scriptHash . to_bytes ( ) ) . toString ( 'hex' ) ; } static getScriptHashFromPolicy ( policyId ) { return ScriptHash . from_bytes ( Buffer . from ( policyId , 'hex' ) ) ; } static getMinUtxoValueWithAssets ( tokenAssets , config = Mainnet , encoding = 'utf8' ) { let assets = Value . new ( BigNum . from_str ( '1000000' ) ) ; let multiAsset = MultiAsset . new ( ) ; const groups = tokenAssets . reduce ( ( dict , asset ) => { ( dict [ asset . policy_id ] = dict [ asset . policy_id ] || [ ] ) . push ( asset ) ; return dict ; } , { } ) ; for ( const policy_id in groups ) { const scriptHash = Seed . getScriptHashFromPolicy ( policy_id ) ; let asset = Assets . new ( ) ; groups [ policy_id ] . forEach ( a => { asset . insert ( AssetName . new ( Buffer . from ( a . asset_name , encoding ) ) , BigNum . from_str ( a . quantity . toString ( ) ) ) ; } ) ; multiAsset . insert ( scriptHash , asset ) ; } assets . set_multiasset ( multiAsset ) ; let min = min_ada_required ( assets , BigNum . from_str ( config . protocols . minUTxOValue . toString ( ) ) ) ; return Number . parseInt ( min . to_str ( ) ) ; } static buildScript ( json , currentSlot ? ) { if ( json . type === ScriptTypeEnum . Sig ) { let keyPair ; let keyHash ; if ( ! json . keyHash ) { keyPair = Seed . generateKeyPair ( ) ; keyHash = Seed . getKeyHash ( keyPair . publicKey ) ; } else { keyHash = Ed25519KeyHash . from_bytes ( Buffer . from ( json . keyHash , 'hex' ) ) ; } return { root : Seed . buildSingleIssuerScript ( keyHash ) , keyHash : Buffer . from ( keyHash . to_bytes ( ) ) . toString ( 'hex' ) , keyPair : keyPair , scripts : [ ] } ; } if ( json . type === ScriptTypeEnum . All ) { let scripts = json . scripts . map ( s => Seed . buildScript ( s , currentSlot ) ) ; return { root : Seed . buildMultiIssuerAllScript ( scripts . map ( s => s . root ) ) , scripts : scripts } ; } if ( json . type === ScriptTypeEnum . Any ) { let scripts = json . scripts . map ( s => Seed . buildScript ( s , currentSlot ) ) ; return { root : Seed . buildMultiIssuerAnyScript ( scripts . map ( s => s . root ) ) , scripts : scripts } ; } if ( json . type === ScriptTypeEnum . AtLeast ) { let scripts = json . scripts . map ( s => Seed . buildScript ( s , currentSlot ) ) ; let n = json . require ; return { root : Seed . buildMultiIssuerAtLeastScript ( n , scripts . map ( s => s . root ) ) , scripts : scripts } ; } if ( json . type === ScriptTypeEnum . After ) { let slot = 0 ; if ( ! json . slot ) { slot = currentSlot ; let lockTime = json . lockTime ; if ( lockTime != 'now' ) { let now = Date . now ( ) ; let datetime = new Date ( lockTime ) . getTime ( ) ; slot = currentSlot + Math . floor ( ( datetime - now ) / 1000 ) ; } } else { slot = json . slot ; } return { root : Seed . buildAfterScript ( slot ) , slot : slot , scripts : [ ] } ; } if ( json . type === ScriptTypeEnum . Before ) { let slot = 0 ; if ( ! json . slot ) { let lockTime = json . lockTime ; slot = currentSlot + 180 ; if ( lockTime != 'now' ) { let now = Date . now ( ) ; let datetime = new Date ( lockTime ) . getTime ( ) ; slot = currentSlot + Math . floor ( ( datetime - now ) / 1000 ) ; } } else { slot = json . slot ; } return { root : Seed . buildBeforeScript ( slot ) , slot : slot , scripts : [ ] } ; } } static scriptToJson ( script ) { let result = { } ; result . type = scriptTypes [ script . root . kind ( ) ] ; if ( script . keyHash ) { result . keyHash = script . keyHash ; } if ( result . type === 'any' ) { result . require = script . root . as_script_n_of_k ( ) . n ( ) ; } if ( result . type === 'after' || result . type === 'before' ) { result . slot = script . slot ; } if ( script . scripts && script . scripts . length > 0 ) { result . scripts = script . scripts . map ( s => Seed . scriptToJson ( s ) ) ; } return result ; } static getScriptKeys ( script ) { let result = [ ] ; if ( script . keyPair ) { result . push ( script . keyPair . privateKey ) ; } script . scripts . forEach ( s => { result . push ( ... Seed . getScriptKeys ( s ) ) ; } ) ; return result ; } static getScriptAddress ( script , network = 'mainnet' ) { let networkId = network == 'mainnet' ? NetworkInfo . mainnet ( ) . network_id ( ) : NetworkInfo . testnet ( ) . network_id ( ) ; const scriptHash = this . getScriptHash ( script . root ) ; const credential = StakeCredential . from_scripthash ( scriptHash ) ; return BaseAddress . new ( networkId , credential , credential ) . to_address ( ) ; } static getPolicyScriptId ( script ) { let scriptHash = Seed . getScriptHash ( script . root ) ; return Buffer . from ( scriptHash . to_bytes ( ) ) . toString ( 'hex' ) ; } static findSlots ( script ) { let result = { } ; let type = script . root . kind ( ) ; if ( type === 4 ) { result . start = script . slot ; } else if ( type === 5 ) { result . end = script . slot ; } else { let slots = script . scripts . map ( s => Seed . findSlots ( s ) ) ; result . start = slots . reduce ( ( max , act ) => ! act . start && ! max ? max : Math . max ( act . start , max ) , result . start ) ; result . end = slots . reduce ( ( min , act ) => ! act . end ? min : ! min ? act . end : Math . min ( act . end , min ) , result . end ) ; } return result ; } private static isInteger ( value ) { return Number . isInteger ( Number ( value ) ) ; } } export class Bip32KeyPair { privateKey : Bip32PrivateKey ; publicKey : Bip32PublicKey ; } export enum MetadateTypesEnum { Number = "int" , String = "string" , Bytes = "bytes" , List = "list" , Map = "map" } export const CARDANO_PUROPOSE = 1852 ; export const CARDANO_COIN_TYPE = 1815 ; export const CARDANO_EXTERNAL = 0 ; export const CARDANO_CHANGE = 1 ; export const CARDANO_CHIMERIC = 2 ;