import { FastifyRequest } from 'fastify' ; import { CardanoService } from '../services/cardano-services' ; import { ConstructionService } from '../services/construction-service' ; import { constructPayloadsForTransactionBody , decodeExtraData , encodeExtraData , getNetworkIdentifierByRequestParameters , mapAmount , mapToConstructionHashResponse } from '../utils/data-mapper' ; import { ErrorFactory , ErrorUtils } from '../utils/errors' ; import { withNetworkValidation } from './controllers-helper' ; import { CardanoCli } from '../utils/cardano/cli/cardanonode-cli' ; import { NetworkService } from '../services/network-service' ; import { AddressType } from '../utils/constants' ; import { isAddressTypeValid , isKeyValid } from '../utils/validations' ; import { BlockService } from '../services/block-service' ; import ApiError from '../api-error' ; export interface ConstructionController { constructionDerive ( request ) : Promise < Components . Schemas . ConstructionDeriveResponse | Components . Schemas . Error > ; constructionPreprocess ( request ) : Promise < Components . Schemas . ConstructionPreprocessResponse | Components . Schemas . Error > ; constructionMetadata ( request ) : Promise < Components . Schemas . ConstructionMetadataResponse | Components . Schemas . Error > ; constructionPayloads ( request ) : Promise < Components . Schemas . ConstructionPayloadsResponse | Components . Schemas . Error > ; constructionCombine ( request ) : Promise < Components . Schemas . ConstructionCombineResponse | Components . Schemas . Error > ; constructionParse ( request ) : Promise < Components . Schemas . ConstructionParseResponse | Components . Schemas . Error > ; constructionHash ( request ) : Promise < Components . Schemas . TransactionIdentifierResponse | Components . Schemas . Error > ; constructionSubmit ( request ) : Promise < Components . Schemas . TransactionIdentifierResponse | Components . Schemas . Error > ; } const configure = ( constructionService , cardanoService , cardanoCli , networkService , blockService ) : ConstructionController => ( { constructionDerive : async ( request ) => withNetworkValidation ( request . body . network_identifier , request , async ( ) => { const logger = request . log ; const publicKey = request . body . public_key ; const networkIdentifier = getNetworkIdentifierByRequestParameters ( request . body . network_identifier ) ; logger . info ( '[constructionDerive] About to check if public key has valid length and curve type' ) ; if ( ! isKeyValid ( publicKey . hex_bytes , publicKey . curve_type ) ) { logger . info ( '[constructionDerive] Public key has an invalid format' ) ; throw ErrorFactory . invalidPublicKeyFormat ( ) ; } logger . info ( '[constructionDerive] Public key has a valid format' ) ; const stakingCredential = request . body . metadata ?. staking_credential ; if ( stakingCredential ) { logger . info ( '[constructionDerive] About to check if staking credential has valid length and curve type' ) ; if ( ! isKeyValid ( stakingCredential . hex_bytes , stakingCredential . curve_type ) ) { logger . info ( '[constructionDerive] Staking credential has an invalid format' ) ; throw ErrorFactory . invalidStakingKeyFormat ( ) ; } logger . info ( '[constructionDerive] Staking credential key has a valid format' ) ; } const addressType = request . body . metadata ?. address_type ; if ( addressType ) { logger . info ( '[constructionDerive] About to check if address type is valid' ) ; if ( ! isAddressTypeValid ( addressType ) ) { logger . info ( '[constructionDerive] Address type has an invalid value' ) ; throw ErrorFactory . invalidAddressTypeError ( ) ; } logger . info ( '[constructionDerive] Address type has a valid value' ) ; } logger . info ( request . body , '[constructionDerive] About to generate address' ) ; const address = cardanoService . generateAddress ( logger , networkIdentifier , publicKey . hex_bytes , stakingCredential ?. hex_bytes , addressType as AddressType ) ; if ( ! address ) { logger . error ( '[constructionDerive] There was an error generating address' ) ; throw ErrorFactory . addressGenerationError ( ) ; } logger . info ( `[constructionDerive] new address is ${ address }` ) ; return { account_identifier : { address } } ; } , request . log , networkService ) , constructionHash : async ( request ) => withNetworkValidation ( request . body . network_identifier , request , async ( ) => { const logger = request . log ; const [ signedTransaction ] = await decodeExtraData ( request . body . signed_transaction ) ; logger . info ( '[constructionHash] About to get hash of signed transaction' ) ; const transactionHash = cardanoService . getHashOfSignedTransaction ( logger , signedTransaction ) ; logger . info ( '[constructionHash] About to return hash of signed transaction' ) ; return mapToConstructionHashResponse ( transactionHash ) ; } , request . log , networkService ) , constructionPreprocess : async ( request ) => withNetworkValidation ( request . body . network_identifier , request , async ( ) => { const networkIdentifier = getNetworkIdentifierByRequestParameters ( request . body . network_identifier ) ; const relativeTtl = constructionService . calculateRelativeTtl ( request . body . metadata ?. relative_ttl ) ; const transactionSize = cardanoService . calculateTxSize ( request . log , networkIdentifier , request . body . operations , 0 ) ; return { options : { relative_ttl : relativeTtl , transaction_size : transactionSize } } ; } , request . log , networkService ) , constructionMetadata : async ( request ) => withNetworkValidation ( request . body . network_identifier , request , async ( ) => { const logger = request . log ; const ttlOffset = request . body . options . relative_ttl ; const txSize = request . body . options . transaction_size ; logger . debug ( `[constructionMetadata] Calculating ttl based on ${ ttlOffset } relative ttl` ) ; const ttl = await constructionService . calculateTtl ( request . log , ttlOffset ) ; logger . debug ( `[constructionMetadata] ttl is ${ ttl }` ) ; logger . debug ( `[constructionMetadata] updating tx size from ${ txSize }` ) ; const updatedTxSize = cardanoService . updateTxSize ( txSize , 0 , ttl ) ; const linearFeeParameters = await blockService . getLinearFeeParameters ( logger ) ; logger . debug ( `[constructionMetadata] gotten linear fee parameters from block-service ${ linearFeeParameters }` ) ; logger . debug ( `[constructionMetadata] updated txSize size is ${ updatedTxSize }` ) ; const suggestedFee = cardanoService . calculateTxMinimumFee ( updatedTxSize , linearFeeParameters ) ; logger . debug ( `[constructionMetadata] suggested fee is ${ suggestedFee }` ) ; return { metadata : { ttl : ttl . toString ( ) } , suggested_fee : [ mapAmount ( suggestedFee . toString ( ) ) ] } ; } , request . log , networkService ) , constructionPayloads : async ( request ) => withNetworkValidation ( request . body . network_identifier , request , async ( ) => { const logger = request . log ; const ttl = request . body . metadata . ttl ; const operations = request . body . operations ; const networkIdentifier = getNetworkIdentifierByRequestParameters ( request . body . network_identifier ) ; logger . info ( operations , '[constuctionPayloads] Operations about to be processed' ) ; const unsignedTransaction = cardanoService . createUnsignedTransaction ( logger , networkIdentifier , operations , parseInt ( ttl ) ) ; const payloads = constructPayloadsForTransactionBody ( unsignedTransaction . hash , unsignedTransaction . addresses ) ; return { unsigned_transaction : await encodeExtraData ( unsignedTransaction . bytes , { operations : request . body . operations , transactionMetadataHex : unsignedTransaction . metadata } ) , payloads } ; } , request . log , networkService ) , constructionCombine : async ( request ) => withNetworkValidation ( request . body . network_identifier , request , async ( ) => { const logger = request . log ; logger . info ( '[constructionCombine] Request received to sign a transaction' ) ; const [ transaction , extraData ] = await decodeExtraData ( request . body . unsigned_transaction ) ; const { transactionMetadataHex } = extraData ; const signedTransaction = cardanoService . buildTransaction ( logger , transaction , request . body . signatures . map ( signature => ( { signature : signature . hex_bytes , publicKey : signature . public_key . hex_bytes , address : signature . signing_payload . account_identifier ?. address , chainCode : signature . signing_payload . account_identifier ?. metadata ?. chain_code } ) ) , transactionMetadataHex ) ; logger . info ( { signedTransaction } , '[constructionCombine] About to return signed transaction' ) ; return { signed_transaction : await encodeExtraData ( signedTransaction , extraData ) } ; } , request . log , networkService ) , constructionParse : async ( request ) => withNetworkValidation ( request . body . network_identifier , request , async ( ) => { const logger = request . log ; const signed = request . body . signed ; const networkIdentifier = getNetworkIdentifierByRequestParameters ( request . body . network_identifier ) ; logger . info ( request . body . transaction , '[constructionParse] Processing' ) ; const [ transaction , extraData ] = await decodeExtraData ( request . body . transaction ) ; logger . info ( { transaction , extraData } , '[constructionParse] Decoded' ) ; if ( signed ) { return { network_identifier : request . body . network_identifier , ... cardanoService . parseSignedTransaction ( logger , networkIdentifier , transaction , extraData ) } ; } return { network_identifier : request . body . network_identifier , ... cardanoService . parseUnsignedTransaction ( logger , networkIdentifier , transaction , extraData ) } ; } , request . log , networkService ) , constructionSubmit : async ( request ) => withNetworkValidation ( request . body . network_identifier , request , async ( ) => { try { const logger = request . log ; const [ signedTransaction ] = await decodeExtraData ( request . body . signed_transaction ) ; logger . info ( `[constructionSubmit] About to submit ${ signedTransaction }` ) ; await cardanoCli . submitTransaction ( logger , signedTransaction , request . body . network_identifier . network === 'mainnet' ) ; logger . info ( '[constructionHash] About to get hash of signed transaction' ) ; const transactionHash = cardanoService . getHashOfSignedTransaction ( logger , signedTransaction ) ; return { transaction_identifier : { hash : transactionHash } } ; } catch ( error ) { request . log . error ( error ) ; return ErrorUtils . resolveApiErrorFromNodeError ( error . message ) . then ( ( mappedError ) => mappedError ) . catch ( ( ) => ErrorFactory . sendTransactionError ( error . message ) ) ; } } , request . log , networkService ) } ) ; export default configure ;