diff --git a/cardano-wasm/cardano-wasm.cabal b/cardano-wasm/cardano-wasm.cabal index 026f0c2b10..38900fba43 100644 --- a/cardano-wasm/cardano-wasm.cabal +++ b/cardano-wasm/cardano-wasm.cabal @@ -30,6 +30,7 @@ library cardano-wasi-lib src-lib exposed-modules: + Cardano.Wasm.Api.Certificate.StakeCertificate Cardano.Wasm.Api.GRPC Cardano.Wasm.Api.Info Cardano.Wasm.Api.InfoToTypeScript @@ -39,6 +40,7 @@ library cardano-wasi-lib Cardano.Wasm.ExceptionHandling other-modules: + Cardano.Wasm.Internal.Api.Era Cardano.Wasm.Internal.Api.Random build-depends: @@ -77,8 +79,9 @@ executable cardano-wasi if arch(wasm32) ghc-options: -no-hs-main - "-optl-Wl,--strip-all,--export=hs_init,--export=newTx,--export=newExperimentalEraTx,--export=newConwayTx,--export=addTxInput,--export=addSimpleTxOut,--export=appendCertificateToTx,--export=setFee,--export=estimateMinFee,--export=signWithPaymentKey,--export=alsoSignWithPaymentKey,--export=toCbor,--export=generatePaymentWallet,--export=generateStakeWallet,--export=restorePaymentWalletFromSigningKeyBech32,--export=restoreStakeWalletFromSigningKeyBech32,--export=generateTestnetPaymentWallet,--export=generateTestnetStakeWallet,--export=restoreTestnetPaymentWalletFromSigningKeyBech32,--export=restoreTestnetStakeWalletFromSigningKeyBech32,--export=getAddressBech32,--export=getBech32ForPaymentVerificationKey,--export=getBech32ForPaymentSigningKey,--export=getBech32ForStakeVerificationKey,--export=getBech32ForStakeSigningKey,--export=getBase16ForPaymentVerificationKeyHash,--export=getBase16ForStakeVerificationKeyHash,--export=mallocNBytes,--export=getStrLen,--export=freeMemory" + "-optl-Wl,--strip-all,--export=hs_init,--export=newTx,--export=newUpcomingEraTx,--export=addTxInput,--export=addSimpleTxOut,--export=appendCertificateToTx,--export=setFee,--export=estimateMinFee,--export=signWithPaymentKey,--export=alsoSignWithPaymentKey,--export=toCbor,--export=makeStakeAddressStakeDelegationCertificate,--export=makeStakeAddressStakeDelegationCertificateUpcomingEra,--export=makeStakeAddressRegistrationCertificate,--export=makeStakeAddressRegistrationCertificateUpcomingEra,--export=makeStakeAddressUnregistrationCertificate,--export=makeStakeAddressUnregistrationCertificateUpcomingEra,--export=generatePaymentWallet,--export=generateStakeWallet,--export=restorePaymentWalletFromSigningKeyBech32,--export=restoreStakeWalletFromSigningKeyBech32,--export=generateTestnetPaymentWallet,--export=generateTestnetStakeWallet,--export=restoreTestnetPaymentWalletFromSigningKeyBech32,--export=restoreTestnetStakeWalletFromSigningKeyBech32,--export=getAddressBech32,--export=getBech32ForPaymentVerificationKey,--export=getBech32ForPaymentSigningKey,--export=getBech32ForStakeVerificationKey,--export=getBech32ForStakeSigningKey,--export=getBase16ForPaymentVerificationKeyHash,--export=getBase16ForStakeVerificationKeyHash,--export=mallocNBytes,--export=getStrLen,--export=freeMemory" other-modules: + Cardano.Wasi.Internal.Api.Certificate.StakeCertificate Cardano.Wasi.Internal.Api.GRPC Cardano.Wasi.Internal.Api.Memory Cardano.Wasi.Internal.Api.Tx diff --git a/cardano-wasm/js-test/basic-test.spec.ts b/cardano-wasm/js-test/basic-test.spec.ts index 13a5cde6f3..ff747a4b6a 100644 --- a/cardano-wasm/js-test/basic-test.spec.ts +++ b/cardano-wasm/js-test/basic-test.spec.ts @@ -8,5 +8,5 @@ test('test output matches', async ({ page }) => { // Wait for the test to finish running (we signal this by creating a tag with id "finish-tag" and text "Finished test!") await expect(page.locator('#finish-tag')).toHaveText("Finished test!"); // Check the output of the test (from the example folder), which is displayed in the code element with id "test-output". The output contains information about the various objects and results of trying some of the functions. - await expect(page.locator('#test-output')).toHaveText("> \"Api object:\"> [object] { objectType: cardano-api tx: [object Object] newGrpcConnection: async function (...args) wallet: [object Object] }> \"Bech32 of address:\"> \"addr_test1vp93p9em3regvgylxuvet6fgr3e9sn259pcejgrk4ykystcs7v8j6\"> \"UnsignedTx object:\"> [object] { objectType: UnsignedTx addTxInput: function (txId,txIx) addSimpleTxOut: function (destAddr,lovelaceAmount) appendCertificateToTx: function (certCbor) setFee: function (lovelaceAmount) estimateMinFee: function (protocolParams,numKeyWitnesses,numByronKeyWitnesses,totalRefScriptSize) signWithPaymentKey: function (signingKey) }> \"Estimated fee:\"> 164005n> \"SignedTx object:\"> [object] { objectType: SignedTx alsoSignWithPaymentKey: function (signingKey) txToCbor: function () }> \"Tx CBOR:\"> \"84a300d9010281825820be6efd42a3d7b9a00d09d77a5d41e55ceaf0bd093a8aa8a893ce70d9caafd97800018182581d6082935e44937e8b530f32ce672b5d600d0a286b4e8a52c6555f659b871a00989680021a000280a5a100d9010281825820adfc1c30385916da87db1ba3328f0690a57ebb2a6ac9f6f86b2d97f943adae005840a49259b5977aea523b46f01261fbff93e0899e8700319e11f5ab96b67eb628fca1a233ce2d50ee3227b591b84f27237d920d63974d65728362382f751c4d9400f5f6\""); + await expect(page.locator('#test-output')).toHaveText("> \"Api object:\"> [object] {    objectType: cardano-api    tx: [object Object]    newGrpcConnection: async function (...args)    certificate: [object Object]    wallet: [object Object]  }> \"Bech32 of address:\"> \"addr_test1vp93p9em3regvgylxuvet6fgr3e9sn259pcejgrk4ykystcs7v8j6\"> \"UnsignedTx object:\"> [object] {    objectType: UnsignedTx    addTxInput: function (txId,txIx)    addSimpleTxOut: function (destAddr,lovelaceAmount)    appendCertificateToTx: function (certCbor)    setFee: function (lovelaceAmount)    estimateMinFee: function (protocolParams,numKeyWitnesses,numByronKeyWitnesses,totalRefScriptSize)    signWithPaymentKey: function (signingKey)  }> \"Estimated fee:\"> 164005n> \"SignedTx object:\"> [object] {    objectType: SignedTx    alsoSignWithPaymentKey: function (signingKey)    txToCbor: function ()  }> \"Tx CBOR:\"> \"84a300d9010281825820be6efd42a3d7b9a00d09d77a5d41e55ceaf0bd093a8aa8a893ce70d9caafd97800018182581d6082935e44937e8b530f32ce672b5d600d0a286b4e8a52c6555f659b871a00989680021a000280a5a100d9010281825820adfc1c30385916da87db1ba3328f0690a57ebb2a6ac9f6f86b2d97f943adae005840a49259b5977aea523b46f01261fbff93e0899e8700319e11f5ab96b67eb628fca1a233ce2d50ee3227b591b84f27237d920d63974d65728362382f751c4d9400f5f6\""); }); diff --git a/cardano-wasm/lib-wrapper/cardano-api.d.ts b/cardano-wasm/lib-wrapper/cardano-api.d.ts index a9cae31b6e..2c14564952 100644 --- a/cardano-wasm/lib-wrapper/cardano-api.d.ts +++ b/cardano-wasm/lib-wrapper/cardano-api.d.ts @@ -26,16 +26,10 @@ declare interface CardanoApi { newTx(): Promise; /** - * Create a new unsigned transaction in the current experimental era (currently unavailable). + * Create a new unsigned transaction in the current upcoming era (currently Dijkstra). * @returns A promise that resolves to a new `UnsignedTx` object. */ - newExperimentalEraTx(): Promise; - - /** - * Create a new unsigned transaction in the Conway era. - * @returns A promise that resolves to a new `UnsignedTx` object. - */ - newConwayTx(): Promise; + newUpcomingEraTx(): Promise; } /** @@ -45,6 +39,69 @@ declare interface CardanoApi { */ newGrpcConnection(webGrpcUrl: string): Promise; + /** + * Methods for creating certificates. + */ + certificate: { + /** + * Methods for creating certificates in the current era (currently Conway). + */ + currentEra: { + /** + * Make a certificate that delegates a stake address to a stake pool in the current era (currently Conway). + * @param stakeKeyHash The stake key hash in base16 format. + * @param poolId The pool ID in base16 format. + * @returns A promise that resolves to the CBOR-encoded certificate as a hex string. + */ + makeStakeAddressStakeDelegationCertificate(stakeKeyHash: string, poolId: string): Promise; + + /** + * Make a stake address registration certificate in the current era (currently Conway). + * @param stakeKeyHash The stake key hash in base16 format. + * @param deposit The deposit amount in lovelaces. + * @returns A promise that resolves to the CBOR-encoded certificate as a hex string. + */ + makeStakeAddressRegistrationCertificate(stakeKeyHash: string, deposit: bigint): Promise; + + /** + * Make a stake address unregistration certificate in the current era (currently Conway). + * @param stakeKeyHash The stake key hash in base16 format. + * @param deposit The deposit amount in lovelaces. + * @returns A promise that resolves to the CBOR-encoded certificate as a hex string. + */ + makeStakeAddressUnregistrationCertificate(stakeKeyHash: string, deposit: bigint): Promise; + } + + /** + * Methods for creating certificates in the current upcoming era (currently Dijkstra). + */ + upcomingEra: { + /** + * Make a certificate that delegates a stake address to a stake pool in the current upcoming era (currently Dijkstra). + * @param stakeKeyHash The stake key hash in base16 format. + * @param poolId The pool ID in base16 format. + * @returns A promise that resolves to the CBOR-encoded certificate as a hex string. + */ + makeStakeAddressStakeDelegationCertificateUpcomingEra(stakeKeyHash: string, poolId: string): Promise; + + /** + * Make a stake address registration certificate in the current upcoming era (currently Dijkstra). + * @param stakeKeyHash The stake key hash in base16 format. + * @param deposit The deposit amount in lovelaces. + * @returns A promise that resolves to the CBOR-encoded certificate as a hex string. + */ + makeStakeAddressRegistrationCertificateUpcomingEra(stakeKeyHash: string, deposit: bigint): Promise; + + /** + * Make a stake address unregistration certificate in the current upcoming era (currently Dijkstra). + * @param stakeKeyHash The stake key hash in base16 format. + * @param deposit The deposit amount in lovelaces. + * @returns A promise that resolves to the CBOR-encoded certificate as a hex string. + */ + makeStakeAddressUnregistrationCertificateUpcomingEra(stakeKeyHash: string, deposit: bigint): Promise; + } + } + /** * Methods for generating and restoring wallets. */ diff --git a/cardano-wasm/src-lib/Cardano/Wasm/Api/Certificate/StakeCertificate.hs b/cardano-wasm/src-lib/Cardano/Wasm/Api/Certificate/StakeCertificate.hs new file mode 100644 index 0000000000..719dcc4289 --- /dev/null +++ b/cardano-wasm/src-lib/Cardano/Wasm/Api/Certificate/StakeCertificate.hs @@ -0,0 +1,151 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Cardano.Wasm.Api.Certificate.StakeCertificate + ( makeStakeAddressStakeDelegationCertificateImpl + , makeStakeAddressStakeDelegationCertificateUpcomingEraImpl + , makeStakeAddressRegistrationCertificateImpl + , makeStakeAddressRegistrationCertificateUpcomingEraImpl + , makeStakeAddressUnregistrationCertificateImpl + , makeStakeAddressUnregistrationCertificateUpcomingEraImpl + ) +where + +import Cardano.Api + ( Coin (..) + , Hash + , PoolId + , StakeKey + , serialiseToCBOR + , unStakePoolKeyHash + ) +import Cardano.Api.Address (StakeCredential (..)) +import Cardano.Api.Experimental (Era (..), obtainCommonConstraints) +import Cardano.Api.Experimental qualified as Exp +import Cardano.Api.Experimental.Certificate (Certificate (..)) +import Cardano.Api.Serialise.Raw qualified as Api + +import Cardano.Ledger.Api (Delegatee (DelegStake)) +import Cardano.Wasm.ExceptionHandling (justOrError, rightOrError) +import Cardano.Wasm.Internal.Api.Era (currentEra, upcomingEra) + +import Control.Monad.Catch (MonadThrow) +import Data.ByteString.Base16 qualified as Base16 +import Data.Text qualified as Text +import Data.Text.Encoding qualified as Text + +-- * Type aliases for clarity + +-- | A stake key hash represented as a base16-encoded string. +type StakeKeyHashBase16 = String + +-- | A pool ID represented as a base16-encoded string. +type PoolIdBase16 = String + +-- | Deposit amount in lovelace. +type DepositLovelace = Integer + +-- | Certificate serialized to CBOR as a base16-encoded string. +type CertificateCBORBase16 = String + +-- * Stake Certificate function implementation + +-- | Make a certificate that delegates a stake address to a stake pool in the current era. +makeStakeAddressStakeDelegationCertificateImpl + :: MonadThrow m => StakeKeyHashBase16 -> PoolIdBase16 -> m CertificateCBORBase16 +makeStakeAddressStakeDelegationCertificateImpl skHashStr poolIdStr = do + stakeCertHash <- readHash skHashStr + poolId <- readPoolId poolIdStr + makeStakeAddressStakeDelegationCertificate currentEra stakeCertHash poolId + +-- | Make a certificate that delegates a stake address to a stake pool in the current upcoming era. +makeStakeAddressStakeDelegationCertificateUpcomingEraImpl + :: MonadThrow m => StakeKeyHashBase16 -> PoolIdBase16 -> m CertificateCBORBase16 +makeStakeAddressStakeDelegationCertificateUpcomingEraImpl skHashStr poolIdStr = do + stakeCertHash <- readHash skHashStr + poolId <- readPoolId poolIdStr + era <- justOrError "No upcoming era available" upcomingEra + makeStakeAddressStakeDelegationCertificate era stakeCertHash poolId + +makeStakeAddressStakeDelegationCertificate + :: forall era m. MonadThrow m => Exp.Era era -> Hash StakeKey -> PoolId -> m CertificateCBORBase16 +makeStakeAddressStakeDelegationCertificate era stakeCertHash poolId = + obtainCommonConstraints era $ do + let cert :: Certificate (Exp.LedgerEra era) = + Exp.makeStakeAddressDelegationCertificate + (StakeCredentialByKey stakeCertHash) + ( case era of + ConwayEra -> DelegStake $ unStakePoolKeyHash poolId + DijkstraEra -> DelegStake $ unStakePoolKeyHash poolId + ) + return $ serialiseCertificateToCBOR era cert + +-- | Make a stake address registration certificate in the current era. +makeStakeAddressRegistrationCertificateImpl + :: MonadThrow m => StakeKeyHashBase16 -> DepositLovelace -> m CertificateCBORBase16 +makeStakeAddressRegistrationCertificateImpl skHashStr deposit = do + skHash <- readHash skHashStr + makeStakeAddressRegistrationCertificateWrapper currentEra skHash deposit + +--  | Make a stake address registration certificate in the upcoming era. +makeStakeAddressRegistrationCertificateUpcomingEraImpl + :: MonadThrow m => StakeKeyHashBase16 -> DepositLovelace -> m CertificateCBORBase16 +makeStakeAddressRegistrationCertificateUpcomingEraImpl skHashStr deposit = do + skHash <- readHash skHashStr + era <- justOrError "No upcoming era available" upcomingEra + makeStakeAddressRegistrationCertificateWrapper era skHash deposit + +makeStakeAddressRegistrationCertificateWrapper + :: forall era m. MonadThrow m => Era era -> Hash StakeKey -> DepositLovelace -> m CertificateCBORBase16 +makeStakeAddressRegistrationCertificateWrapper era skHash deposit = + obtainCommonConstraints era $ do + let cert :: Certificate (Exp.LedgerEra era) = + Exp.makeStakeAddressRegistrationCertificate + (StakeCredentialByKey skHash) + (Coin deposit) + return $ serialiseCertificateToCBOR era cert + +-- | Make a stake address unregistration certificate in the current era. +makeStakeAddressUnregistrationCertificateImpl + :: MonadThrow m => StakeKeyHashBase16 -> DepositLovelace -> m CertificateCBORBase16 +makeStakeAddressUnregistrationCertificateImpl skHashStr deposit = do + skHash <- readHash skHashStr + makeStakeAddressUnregistrationCertificateWrapper currentEra skHash deposit + +-- | Make a stake address unregistration certificate in the upcoming era. +makeStakeAddressUnregistrationCertificateUpcomingEraImpl + :: MonadThrow m => StakeKeyHashBase16 -> DepositLovelace -> m CertificateCBORBase16 +makeStakeAddressUnregistrationCertificateUpcomingEraImpl skHashStr deposit = do + skHash <- readHash skHashStr + era <- justOrError "No upcoming era available" upcomingEra + makeStakeAddressUnregistrationCertificateWrapper era skHash deposit + +makeStakeAddressUnregistrationCertificateWrapper + :: forall era m. MonadThrow m => Era era -> Hash StakeKey -> DepositLovelace -> m CertificateCBORBase16 +makeStakeAddressUnregistrationCertificateWrapper era skHash deposit = + obtainCommonConstraints era $ do + let cert :: Certificate (Exp.LedgerEra era) = + Exp.makeStakeAddressUnregistrationCertificate + (StakeCredentialByKey skHash) + (Coin deposit) + return $ serialiseCertificateToCBOR era cert + +serialiseCertificateToCBOR + :: Exp.Era era -> Certificate (Exp.LedgerEra era) -> CertificateCBORBase16 +serialiseCertificateToCBOR era cert = + obtainCommonConstraints era $ do + Text.unpack $ + Text.decodeUtf8 $ + Base16.encode $ + serialiseToCBOR + cert + +readHash :: MonadThrow m => StakeKeyHashBase16 -> m (Hash StakeKey) +readHash = rightOrError . Api.deserialiseFromRawBytesHex . Text.encodeUtf8 . Text.pack + +readPoolId :: MonadThrow m => PoolIdBase16 -> m PoolId +readPoolId = rightOrError . Api.deserialiseFromRawBytesHex . Text.encodeUtf8 . Text.pack diff --git a/cardano-wasm/src-lib/Cardano/Wasm/Api/GRPC.hs b/cardano-wasm/src-lib/Cardano/Wasm/Api/GRPC.hs index 91bebe7ce9..a39fa16448 100644 --- a/cardano-wasm/src-lib/Cardano/Wasm/Api/GRPC.hs +++ b/cardano-wasm/src-lib/Cardano/Wasm/Api/GRPC.hs @@ -12,7 +12,7 @@ import Data.ByteString.Char8 qualified as BS newtype GrpcObject grpcClient = GrpcObject grpcClient --- | Create a new unsigned transaction object for making a Conway era transaction. +-- | Create a new gRPC or GRPC-web connection to the Cardano Node. newGrpcConnectionImpl :: (String -> IO grpcClient) -> String -> IO (GrpcObject grpcClient) newGrpcConnectionImpl createClientFunc host = GrpcObject <$> createClientFunc host diff --git a/cardano-wasm/src-lib/Cardano/Wasm/Api/Info.hs b/cardano-wasm/src-lib/Cardano/Wasm/Api/Info.hs index 2a68eee2f7..5ebfbc3e2b 100644 --- a/cardano-wasm/src-lib/Cardano/Wasm/Api/Info.hs +++ b/cardano-wasm/src-lib/Cardano/Wasm/Api/Info.hs @@ -15,8 +15,9 @@ module Cardano.Wasm.Api.Info where import Cardano.Api (pretty) +import Cardano.Api.Experimental.Era qualified as Exp -import Cardano.Wasm.Api.Tx (UnsignedTxObject (..), newExperimentalEraTxImpl, newTxImpl) +import Cardano.Wasm.Internal.Api.Era (currentEra, upcomingEra) import Data.Aeson qualified as Aeson import Data.Text qualified as Text @@ -196,11 +197,12 @@ instance Aeson.ToJSON ApiInfo where ] -- | Get a comment about the era for unsigned transaction creation methods. -getEraCommentForUnsignedTx :: Maybe UnsignedTxObject -> String -getEraCommentForUnsignedTx utxMonad = - case utxMonad of - Just (UnsignedTxObject era _) -> "(currently " ++ show (pretty era) ++ ")" - Nothing -> "(currently unavailable)" +getEraCommentFor :: Maybe (Exp.Era era) -> String +getEraCommentFor era = + case era of + Just era' -> "(currently " ++ show (pretty era') ++ ")" + Nothing -> + "(currently unavailable, upcoming era will only be available during late development and testing phases)" -- | Provides metadata about the "virtual objects" and their methods. -- This is intended to help generate JavaScript wrappers. @@ -441,7 +443,7 @@ apiInfo = { methodName = "newTx" , methodDoc = "Create a new unsigned transaction in the current era " - ++ getEraCommentForUnsignedTx (Just newTxImpl) + ++ getEraCommentFor (Just currentEra) ++ "." , methodParams = [] , methodReturnType = NewObject (virtualObjectName unsignedTxObj) @@ -449,23 +451,15 @@ apiInfo = } , MethodInfoEntry $ MethodInfo - { methodName = "newExperimentalEraTx" + { methodName = "newUpcomingEraTx" , methodDoc = - "Create a new unsigned transaction in the current experimental era " - ++ getEraCommentForUnsignedTx newExperimentalEraTxImpl + "Create a new unsigned transaction in the current upcoming era " + ++ getEraCommentFor upcomingEra ++ "." , methodParams = [] , methodReturnType = NewObject (virtualObjectName unsignedTxObj) , methodReturnDoc = "A promise that resolves to a new `UnsignedTx` object." } - , MethodInfoEntry $ - MethodInfo - { methodName = "newConwayTx" - , methodDoc = "Create a new unsigned transaction in the Conway era." - , methodParams = [] - , methodReturnType = NewObject (virtualObjectName unsignedTxObj) - , methodReturnDoc = "A promise that resolves to a new `UnsignedTx` object." - } ] } , MethodInfoEntry $ @@ -476,6 +470,117 @@ apiInfo = , methodReturnType = NewObject (virtualObjectName grpcConnection) , methodReturnDoc = "A promise that resolves to a new `GrpcConnection`." } + , MethodGroupEntry $ + MethodGroup + { groupName = "certificate" + , groupDoc = ["Methods for creating certificates."] + , groupMethods = + [ MethodGroupEntry $ + MethodGroup + { groupName = "currentEra" + , groupDoc = + [ "Methods for creating certificates in the current era " ++ getEraCommentFor (Just currentEra) ++ "." + ] + , groupMethods = + [ MethodInfoEntry $ + MethodInfo + { methodName = "makeStakeAddressStakeDelegationCertificate" + , methodDoc = + "Make a certificate that delegates a stake address to a stake pool in the current era " + ++ getEraCommentFor (Just currentEra) + ++ "." + , methodParams = + [ ParamInfo "stakeKeyHash" TSString "The stake key hash in base16 format." + , ParamInfo "poolId" TSString "The pool ID in base16 format." + ] + , methodReturnType = OtherType TSString + , methodReturnDoc = "A promise that resolves to the CBOR-encoded certificate as a hex string." + } + , MethodInfoEntry $ + MethodInfo + { methodName = "makeStakeAddressRegistrationCertificate" + , methodDoc = + "Make a stake address registration certificate in the current era " + ++ getEraCommentFor (Just currentEra) + ++ "." + , methodParams = + [ ParamInfo "stakeKeyHash" TSString "The stake key hash in base16 format." + , ParamInfo "deposit" TSBigInt "The deposit amount in lovelaces." + ] + , methodReturnType = OtherType TSString + , methodReturnDoc = "A promise that resolves to the CBOR-encoded certificate as a hex string." + } + , MethodInfoEntry $ + MethodInfo + { methodName = "makeStakeAddressUnregistrationCertificate" + , methodDoc = + "Make a stake address unregistration certificate in the current era " + ++ getEraCommentFor (Just currentEra) + ++ "." + , methodParams = + [ ParamInfo "stakeKeyHash" TSString "The stake key hash in base16 format." + , ParamInfo "deposit" TSBigInt "The deposit amount in lovelaces." + ] + , methodReturnType = OtherType TSString + , methodReturnDoc = "A promise that resolves to the CBOR-encoded certificate as a hex string." + } + ] + } + , MethodGroupEntry $ + MethodGroup + { groupName = "upcomingEra" + , groupDoc = + [ "Methods for creating certificates in the current upcoming era " + ++ getEraCommentFor upcomingEra + ++ "." + ] + , groupMethods = + [ MethodInfoEntry $ + MethodInfo + { methodName = "makeStakeAddressStakeDelegationCertificateUpcomingEra" + , methodDoc = + "Make a certificate that delegates a stake address to a stake pool in the current upcoming era " + ++ getEraCommentFor upcomingEra + ++ "." + , methodParams = + [ ParamInfo "stakeKeyHash" TSString "The stake key hash in base16 format." + , ParamInfo "poolId" TSString "The pool ID in base16 format." + ] + , methodReturnType = OtherType TSString + , methodReturnDoc = "A promise that resolves to the CBOR-encoded certificate as a hex string." + } + , MethodInfoEntry $ + MethodInfo + { methodName = "makeStakeAddressRegistrationCertificateUpcomingEra" + , methodDoc = + "Make a stake address registration certificate in the current upcoming era " + ++ getEraCommentFor upcomingEra + ++ "." + , methodParams = + [ ParamInfo "stakeKeyHash" TSString "The stake key hash in base16 format." + , ParamInfo "deposit" TSBigInt "The deposit amount in lovelaces." + ] + , methodReturnType = OtherType TSString + , methodReturnDoc = "A promise that resolves to the CBOR-encoded certificate as a hex string." + } + , MethodInfoEntry $ + MethodInfo + { methodName = "makeStakeAddressUnregistrationCertificateUpcomingEra" + , methodDoc = + "Make a stake address unregistration certificate in the current upcoming era " + ++ getEraCommentFor upcomingEra + ++ "." + , methodParams = + [ ParamInfo "stakeKeyHash" TSString "The stake key hash in base16 format." + , ParamInfo "deposit" TSBigInt "The deposit amount in lovelaces." + ] + , methodReturnType = OtherType TSString + , methodReturnDoc = "A promise that resolves to the CBOR-encoded certificate as a hex string." + } + ] + } + ] + } , MethodGroupEntry $ MethodGroup { groupName = "wallet" diff --git a/cardano-wasm/src-lib/Cardano/Wasm/Api/Tx.hs b/cardano-wasm/src-lib/Cardano/Wasm/Api/Tx.hs index ce9346f4e5..5fabec28a7 100644 --- a/cardano-wasm/src-lib/Cardano/Wasm/Api/Tx.hs +++ b/cardano-wasm/src-lib/Cardano/Wasm/Api/Tx.hs @@ -13,8 +13,7 @@ module Cardano.Wasm.Api.Tx , SignedTxObject (..) , ProtocolParamsJSON (..) , newTxImpl - , newExperimentalEraTxImpl - , newConwayTxImpl + , newUpcomingEraTxImpl , addTxInputImpl , addSimpleTxOutImpl , appendCertificateToTxImpl @@ -35,7 +34,8 @@ import Cardano.Api.Plutus qualified as Shelley import Cardano.Api.Tx qualified as TxBody import Cardano.Ledger.Api qualified as Ledger -import Cardano.Wasm.ExceptionHandling (justOrError, rightOrError, throwError, toMonadFail) +import Cardano.Wasm.ExceptionHandling (justOrError, rightOrError, toMonadFail) +import Cardano.Wasm.Internal.Api.Era (currentEra, upcomingEra) import Control.Monad.Catch (MonadThrow) import Data.Aeson (ToJSON (toJSON), (.=)) @@ -82,15 +82,13 @@ instance FromJSON UnsignedTxObject where -- | Create a new unsigned transaction object for making a transaction in the current era. newTxImpl :: UnsignedTxObject -newTxImpl = newConwayTxImpl +newTxImpl = UnsignedTxObject currentEra (Exp.UnsignedTx (Ledger.mkBasicTx Ledger.mkBasicTxBody)) --- | Create a new unsigned transaction object for making a transaction in the current experimental era. -newExperimentalEraTxImpl :: (HasCallStack, MonadThrow m) => m UnsignedTxObject -newExperimentalEraTxImpl = throwError "newExperimentalEraTxImpl: No experimental era available" - --- | Create a new unsigned transaction object for making a Conway era transaction. -newConwayTxImpl :: UnsignedTxObject -newConwayTxImpl = UnsignedTxObject Exp.ConwayEra (Exp.UnsignedTx (Ledger.mkBasicTx Ledger.mkBasicTxBody)) +-- | Create a new unsigned transaction object for making a transaction in the current upcoming era. +newUpcomingEraTxImpl :: MonadThrow m => m UnsignedTxObject +newUpcomingEraTxImpl = do + era <- justOrError "No upcoming era available" upcomingEra + return $ UnsignedTxObject era (Exp.UnsignedTx (Ledger.mkBasicTx Ledger.mkBasicTxBody)) -- | Add a simple transaction input to an unsigned transaction object. addTxInputImpl :: UnsignedTxObject -> Api.TxId -> Api.TxIx -> UnsignedTxObject diff --git a/cardano-wasm/src-lib/Cardano/Wasm/Internal/Api/Era.hs b/cardano-wasm/src-lib/Cardano/Wasm/Internal/Api/Era.hs new file mode 100644 index 0000000000..4161401b0d --- /dev/null +++ b/cardano-wasm/src-lib/Cardano/Wasm/Internal/Api/Era.hs @@ -0,0 +1,19 @@ +-- We disable missing signature because DijkstraEra type is not exported yet +{-# OPTIONS_GHC -Wno-missing-signatures #-} + +-- | Module providing constants for the current and upcoming eras +-- used throughout the cardano-wasm library. +module Cardano.Wasm.Internal.Api.Era + ( currentEra + , upcomingEra + ) +where + +import Cardano.Api.Experimental qualified as Exp + +-- | The current era used in mainnet. +currentEra :: Exp.Era Exp.ConwayEra +currentEra = Exp.ConwayEra + +-- | The upcoming era, still under development or testing. +upcomingEra = Just Exp.DijkstraEra diff --git a/cardano-wasm/src-wasi/Cardano/Wasi/Internal/Api/Certificate/StakeCertificate.hs b/cardano-wasm/src-wasi/Cardano/Wasi/Internal/Api/Certificate/StakeCertificate.hs new file mode 100644 index 0000000000..28d89a8958 --- /dev/null +++ b/cardano-wasm/src-wasi/Cardano/Wasi/Internal/Api/Certificate/StakeCertificate.hs @@ -0,0 +1,101 @@ +{-# LANGUAGE CPP #-} + +module Cardano.Wasi.Internal.Api.Certificate.StakeCertificate + ( makeStakeAddressStakeDelegationCertificate + , makeStakeAddressStakeDelegationCertificateUpcomingEra + , makeStakeAddressRegistrationCertificate + , makeStakeAddressRegistrationCertificateUpcomingEra + , makeStakeAddressUnregistrationCertificate + , makeStakeAddressUnregistrationCertificateUpcomingEra + ) +where + +import Cardano.Wasi.Internal.Conversion (cstrToInt) +import Cardano.Wasm.Api.Certificate.StakeCertificate qualified as Wasm + +import Control.Monad (join) + +import Foreign.C (CString) +import Foreign.C.String (newCString, peekCString) + +#if defined(wasm32_HOST_ARCH) + +foreign export ccall "makeStakeAddressStakeDelegationCertificate" + makeStakeAddressStakeDelegationCertificate :: CString -> CString -> IO CString + +foreign export ccall "makeStakeAddressStakeDelegationCertificateUpcomingEra" + makeStakeAddressStakeDelegationCertificateUpcomingEra :: CString -> CString -> IO CString + +foreign export ccall "makeStakeAddressRegistrationCertificate" + makeStakeAddressRegistrationCertificate :: CString -> CString -> IO CString + +foreign export ccall "makeStakeAddressRegistrationCertificateUpcomingEra" + makeStakeAddressRegistrationCertificateUpcomingEra :: CString -> CString -> IO CString + +foreign export ccall "makeStakeAddressUnregistrationCertificate" + makeStakeAddressUnregistrationCertificate :: CString -> CString -> IO CString + +foreign export ccall "makeStakeAddressUnregistrationCertificateUpcomingEra" + makeStakeAddressUnregistrationCertificateUpcomingEra :: CString -> CString -> IO CString + +#endif + +-- | Make a certificate that delegates a stake address to a stake pool in the current era. +makeStakeAddressStakeDelegationCertificate :: CString -> CString -> IO CString +makeStakeAddressStakeDelegationCertificate stakeKeyHashCStr poolIdCStr = + newCString + =<< join + ( Wasm.makeStakeAddressStakeDelegationCertificateImpl + <$> peekCString stakeKeyHashCStr + <*> peekCString poolIdCStr + ) + +-- | Make a certificate that delegates a stake address to a stake pool in the upcoming era. +makeStakeAddressStakeDelegationCertificateUpcomingEra :: CString -> CString -> IO CString +makeStakeAddressStakeDelegationCertificateUpcomingEra stakeKeyHashCStr poolIdCStr = + newCString + =<< join + ( Wasm.makeStakeAddressStakeDelegationCertificateUpcomingEraImpl + <$> peekCString stakeKeyHashCStr + <*> peekCString poolIdCStr + ) + +-- | Make a stake address registration certificate in the current era. +makeStakeAddressRegistrationCertificate :: CString -> CString -> IO CString +makeStakeAddressRegistrationCertificate stakeKeyHashCStr depositCStr = + newCString + =<< join + ( Wasm.makeStakeAddressRegistrationCertificateImpl + <$> peekCString stakeKeyHashCStr + <*> (toInteger <$> cstrToInt "deposit" depositCStr) + ) + +-- | Make a stake address registration certificate in the upcoming era. +makeStakeAddressRegistrationCertificateUpcomingEra :: CString -> CString -> IO CString +makeStakeAddressRegistrationCertificateUpcomingEra stakeKeyHashCStr depositCStr = + newCString + =<< join + ( Wasm.makeStakeAddressRegistrationCertificateUpcomingEraImpl + <$> peekCString stakeKeyHashCStr + <*> (toInteger <$> cstrToInt "deposit" depositCStr) + ) + +-- | Make a stake address unregistration certificate in the current era. +makeStakeAddressUnregistrationCertificate :: CString -> CString -> IO CString +makeStakeAddressUnregistrationCertificate stakeKeyHashCStr depositCStr = + newCString + =<< join + ( Wasm.makeStakeAddressUnregistrationCertificateImpl + <$> peekCString stakeKeyHashCStr + <*> (toInteger <$> cstrToInt "deposit" depositCStr) + ) + +-- | Make a stake address unregistration certificate in the upcoming era. +makeStakeAddressUnregistrationCertificateUpcomingEra :: CString -> CString -> IO CString +makeStakeAddressUnregistrationCertificateUpcomingEra stakeKeyHashCStr depositCStr = + newCString + =<< join + ( Wasm.makeStakeAddressUnregistrationCertificateUpcomingEraImpl + <$> peekCString stakeKeyHashCStr + <*> (toInteger <$> cstrToInt "deposit" depositCStr) + ) diff --git a/cardano-wasm/src-wasi/Cardano/Wasi/Internal/Api/Tx.hs b/cardano-wasm/src-wasi/Cardano/Wasi/Internal/Api/Tx.hs index e829c3b68a..0a1ce46216 100644 --- a/cardano-wasm/src-wasi/Cardano/Wasi/Internal/Api/Tx.hs +++ b/cardano-wasm/src-wasi/Cardano/Wasi/Internal/Api/Tx.hs @@ -2,8 +2,7 @@ module Cardano.Wasi.Internal.Api.Tx ( newTx - , newExperimentalEraTx - , newConwayTx + , newUpcomingEraTx , addTxInput , addSimpleTxOut , appendCertificateToTx @@ -39,11 +38,8 @@ import Foreign.C.String (newCString, peekCString) foreign export ccall "newTx" newTx :: IO UnsignedTxObjectJSON -foreign export ccall "newExperimentalEraTx" - newExperimentalEraTx :: IO UnsignedTxObjectJSON - -foreign export ccall "newConwayTx" - newConwayTx :: IO UnsignedTxObjectJSON +foreign export ccall "newUpcomingEraTx" + newUpcomingEraTx :: IO UnsignedTxObjectJSON foreign export ccall "addTxInput" addTxInput :: UnsignedTxObjectJSON -> CString -> Int -> IO UnsignedTxObjectJSON @@ -70,11 +66,8 @@ type UnsignedTxObjectJSON = CString newTx :: IO UnsignedTxObjectJSON newTx = toCJSON newTxImpl -newExperimentalEraTx :: IO UnsignedTxObjectJSON -newExperimentalEraTx = toCJSON =<< newExperimentalEraTxImpl - -newConwayTx :: IO UnsignedTxObjectJSON -newConwayTx = toCJSON newConwayTxImpl +newUpcomingEraTx :: IO UnsignedTxObjectJSON +newUpcomingEraTx = toCJSON =<< newUpcomingEraTxImpl addTxInput :: UnsignedTxObjectJSON -> CString -> Int -> IO UnsignedTxObjectJSON addTxInput unsignedTxObject txId txIx = @@ -113,15 +106,15 @@ setFee unsignedTxObject feeStr = ) estimateMinFee :: UnsignedTxObjectJSON -> CString -> Int -> Int -> Int -> IO CString -estimateMinFee ptrUnsignedTxObject pparams numInputs numOutputs numShelleyKeyWitnesses = do +estimateMinFee ptrUnsignedTxObject pparams numExtraKeyWitnesses numExtraByronKeyWitnesses totalRefScriptSize = do (intToCStr . Api.unCoin) =<< join ( estimateMinFeeImpl <$> fromCJSON False "UnsignedTx" ptrUnsignedTxObject <*> (ProtocolParamsJSON <$> fromCJSON False "ProtocolParameters" pparams) - <*> pure (fromIntegral numInputs) - <*> pure (fromIntegral numOutputs) - <*> pure (fromIntegral numShelleyKeyWitnesses) + <*> pure (fromIntegral numExtraKeyWitnesses) + <*> pure (fromIntegral numExtraByronKeyWitnesses) + <*> pure (fromIntegral totalRefScriptSize) ) signWithPaymentKey :: UnsignedTxObjectJSON -> CString -> IO SignedTxObjectJSON diff --git a/cardano-wasm/src-wasm/Cardano/Wasm/Internal/JavaScript/Bridge.hs b/cardano-wasm/src-wasm/Cardano/Wasm/Internal/JavaScript/Bridge.hs index 20a668d661..11b35b6f75 100644 --- a/cardano-wasm/src-wasm/Cardano/Wasm/Internal/JavaScript/Bridge.hs +++ b/cardano-wasm/src-wasm/Cardano/Wasm/Internal/JavaScript/Bridge.hs @@ -16,6 +16,7 @@ module Cardano.Wasm.Internal.JavaScript.Bridge where import Cardano.Api qualified as Api import Cardano.Api.Ledger qualified as Ledger +import Cardano.Wasm.Api.Certificate.StakeCertificate qualified as Wasm import Cardano.Wasm.Api.GRPC qualified as Wasm import Cardano.Wasm.Api.Info (apiInfo) import Cardano.Wasm.Api.Tx qualified as Wasm @@ -341,11 +342,8 @@ getBase16ForStakeVerificationKeyHash jsWallet = foreign export javascript "newTx" newTx :: IO JSUnsignedTx -foreign export javascript "newExperimentalEraTx" - newExperimentalEraTx :: IO JSUnsignedTx - -foreign export javascript "newConwayTx" - newConwayTx :: IO JSUnsignedTx +foreign export javascript "newUpcomingEraTx" + newUpcomingEraTx :: IO JSUnsignedTx foreign export javascript "addTxInput" addTxInput :: JSUnsignedTx -> JSTxId -> JSTxIx -> IO JSUnsignedTx @@ -356,6 +354,24 @@ foreign export javascript "addSimpleTxOut" foreign export javascript "appendCertificateToTx" appendCertificateToTx :: JSUnsignedTx -> JSString -> IO JSUnsignedTx +foreign export javascript "makeStakeAddressStakeDelegationCertificate" + makeStakeAddressStakeDelegationCertificate :: JSString -> JSString -> IO JSString + +foreign export javascript "makeStakeAddressStakeDelegationCertificateUpcomingEra" + makeStakeAddressStakeDelegationCertificateUpcomingEra :: JSString -> JSString -> IO JSString + +foreign export javascript "makeStakeAddressRegistrationCertificate" + makeStakeAddressRegistrationCertificate :: JSString -> JSCoin -> IO JSString + +foreign export javascript "makeStakeAddressRegistrationCertificateUpcomingEra" + makeStakeAddressRegistrationCertificateUpcomingEra :: JSString -> JSCoin -> IO JSString + +foreign export javascript "makeStakeAddressUnregistrationCertificate" + makeStakeAddressUnregistrationCertificate :: JSString -> JSCoin -> IO JSString + +foreign export javascript "makeStakeAddressUnregistrationCertificateUpcomingEra" + makeStakeAddressUnregistrationCertificateUpcomingEra :: JSString -> JSCoin -> IO JSString + foreign export javascript "setFee" setFee :: JSUnsignedTx -> JSCoin -> IO JSUnsignedTx @@ -365,17 +381,13 @@ foreign export javascript "estimateMinFee" foreign export javascript "signWithPaymentKey" signWithPaymentKey :: JSUnsignedTx -> JSSigningKey -> IO JSSignedTx --- | Create a new unsigned transaction. +-- | Create a new unsigned transaction in the current era. newTx :: HasCallStack => IO JSUnsignedTx newTx = toJSVal Wasm.newTxImpl --- | Create a new experimental era unsigned transaction. -newExperimentalEraTx :: HasCallStack => IO JSUnsignedTx -newExperimentalEraTx = toJSVal =<< Wasm.newExperimentalEraTxImpl - --- | Create a new Conway era unsigned transaction. -newConwayTx :: HasCallStack => IO JSUnsignedTx -newConwayTx = toJSVal Wasm.newConwayTxImpl +-- | Create a new upcoming era unsigned transaction. +newUpcomingEraTx :: HasCallStack => IO JSUnsignedTx +newUpcomingEraTx = toJSVal =<< Wasm.newUpcomingEraTxImpl -- | Add a transaction input to an unsigned transaction. addTxInput :: HasCallStack => JSUnsignedTx -> JSTxId -> JSTxIx -> IO JSUnsignedTx @@ -407,6 +419,66 @@ appendCertificateToTx jsUnsignedTx jsCertCbor = <*> fromJSVal jsCertCbor ) +-- | Make a certificate that delegates a stake address to a stake pool in the current era. +makeStakeAddressStakeDelegationCertificate :: HasCallStack => JSString -> JSString -> IO JSString +makeStakeAddressStakeDelegationCertificate jsStakeKeyHash jsPoolId = + toJSVal + =<< join + ( Wasm.makeStakeAddressStakeDelegationCertificateImpl + <$> fromJSVal jsStakeKeyHash + <*> fromJSVal jsPoolId + ) + +-- | Make a certificate that delegates a stake address to a stake pool in the upcoming era. +makeStakeAddressStakeDelegationCertificateUpcomingEra :: HasCallStack => JSString -> JSString -> IO JSString +makeStakeAddressStakeDelegationCertificateUpcomingEra jsStakeKeyHash jsPoolId = + toJSVal + =<< join + ( Wasm.makeStakeAddressStakeDelegationCertificateUpcomingEraImpl + <$> fromJSVal jsStakeKeyHash + <*> fromJSVal jsPoolId + ) + +-- | Make a stake address registration certificate in the current era. +makeStakeAddressRegistrationCertificate :: HasCallStack => JSString -> JSCoin -> IO JSString +makeStakeAddressRegistrationCertificate jsStakeKeyHash jsDeposit = + toJSVal + =<< join + ( Wasm.makeStakeAddressRegistrationCertificateImpl + <$> fromJSVal jsStakeKeyHash + <*> (fromInteger <$> fromJSBigInt jsDeposit) + ) + +-- | Make a stake address registration certificate in the upcoming era. +makeStakeAddressRegistrationCertificateUpcomingEra :: HasCallStack => JSString -> JSCoin -> IO JSString +makeStakeAddressRegistrationCertificateUpcomingEra jsStakeKeyHash jsDeposit = + toJSVal + =<< join + ( Wasm.makeStakeAddressRegistrationCertificateUpcomingEraImpl + <$> fromJSVal jsStakeKeyHash + <*> (fromInteger <$> fromJSBigInt jsDeposit) + ) + +-- | Make a stake address unregistration certificate in the current era. +makeStakeAddressUnregistrationCertificate :: HasCallStack => JSString -> JSCoin -> IO JSString +makeStakeAddressUnregistrationCertificate jsStakeKeyHash jsDeposit = + toJSVal + =<< join + ( Wasm.makeStakeAddressUnregistrationCertificateImpl + <$> fromJSVal jsStakeKeyHash + <*> (fromInteger <$> fromJSBigInt jsDeposit) + ) + +-- | Make a stake address unregistration certificate in the upcoming era. +makeStakeAddressUnregistrationCertificateUpcomingEra :: HasCallStack => JSString -> JSCoin -> IO JSString +makeStakeAddressUnregistrationCertificateUpcomingEra jsStakeKeyHash jsDeposit = + toJSVal + =<< join + ( Wasm.makeStakeAddressUnregistrationCertificateUpcomingEraImpl + <$> fromJSVal jsStakeKeyHash + <*> (fromInteger <$> fromJSBigInt jsDeposit) + ) + -- | Set the transaction fee for an unsigned transaction. setFee :: HasCallStack => JSUnsignedTx -> JSCoin -> IO JSUnsignedTx setFee jsUnsignedTx jsCoin = @@ -480,7 +552,7 @@ foreign export javascript "getUtxosForAddress" foreign export javascript "submitTx" submitTx :: JSGrpc -> JSString -> IO JSString --- | Create a new gRPC object for making Conway era transactions. +-- | Create a new gRPC object. newGrpcConnection :: HasCallStack => JSString -> IO JSGrpc newGrpcConnection webGrpcUrl = toJSVal =<< join (Wasm.newGrpcConnectionImpl js_newWebGrpcClient <$> fromJSVal webGrpcUrl)