From a19cdfe31b072e3dcd92a240d27bd0153370e108 Mon Sep 17 00:00:00 2001 From: Johannes Lund Date: Wed, 4 Dec 2019 10:18:17 +0100 Subject: [PATCH 1/6] Remove redundant constraint --- .../test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs b/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs index 8e5a6b3fa7e..fc2ef179d06 100644 --- a/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs +++ b/lib/jormungandr/test/unit/Cardano/Wallet/Jormungandr/TransactionSpec.hs @@ -13,8 +13,6 @@ module Cardano.Wallet.Jormungandr.TransactionSpec import Prelude -import Cardano.Wallet.Jormungandr.Compatibility - ( Jormungandr ) import Cardano.Wallet.Jormungandr.Transaction ( ErrExceededInpsOrOuts (..), newTransactionLayer ) import Cardano.Wallet.Primitive.AddressDerivation @@ -509,7 +507,7 @@ goldenTestStdTx tl keystore inps outs bytes' = it title $ do title = "golden test mkStdTx: " <> show inps <> show outs goldenTestDelegationCertTx - :: forall t k. (t ~ Jormungandr, HasCallStack) + :: forall t k. (HasCallStack) => TransactionLayer t k -> (Address -> Maybe (k 'AddressK XPrv, Passphrase "encryption")) -> PoolId From 13448156b1c5451f7a12793318bda34c9277584e Mon Sep 17 00:00:00 2001 From: Johannes Lund Date: Thu, 5 Dec 2019 14:46:32 +0100 Subject: [PATCH 2/6] Fix incorrect swagger schema for tx fee estimation --- specifications/api/swagger.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index 22c04a97a9e..cf70e742d0a 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -1042,11 +1042,7 @@ x-responsesPostTransactionFee: &responsesPostTransactionFee 200: description: Ok schema: - type: object - required: - - amount - properties: - amount: *ApiFee + <<: *ApiFee x-responsesListAddresses: &responsesListAddresses <<: *responsesErr400 From 754a1c7e81ba0daca718789667097d53694e03e3 Mon Sep 17 00:00:00 2001 From: Johannes Lund Date: Thu, 5 Dec 2019 14:44:28 +0100 Subject: [PATCH 3/6] Add /stake-pools/{pid}/wallets/{wid}/fee to swagger.yaml --- specifications/api/swagger.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index cf70e742d0a..f85276ac667 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -1044,6 +1044,15 @@ x-responsesPostTransactionFee: &responsesPostTransactionFee schema: <<: *ApiFee +x-responsesGetJoinStakePoolFee: &responsesGetJoinStakePoolFee + <<: *responsesErr404 + <<: *responsesErr405 + <<: *responsesErr406 + 200: + description: Ok + schema: + <<: *ApiFee + x-responsesListAddresses: &responsesListAddresses <<: *responsesErr400 <<: *responsesErr404 @@ -1336,6 +1345,20 @@ paths: schema: *parametersQuitStakePool responses: *responsesQuitStakePool + /stake-pools/{stakePoolId}/wallets/{walletId}/fee: + get: + operationId: joinStakePoolFee + tags: ["Stake Pools"] + summary: Estimate + description: | +

status: stable

+ + Estimate fee for joining a stake pool. + parameters: + - *parametersStakePoolId + - *parametersWalletId + responses: *responsesGetJoinStakePoolFee + /byron-wallets: get: operationId: listByronWallets From e98f33f2f3689d841e52ad8f2fd09a56fdbb0782 Mon Sep 17 00:00:00 2001 From: Johannes Lund Date: Thu, 5 Dec 2019 11:54:52 +0100 Subject: [PATCH 4/6] Add feeBalance :: CoinSelection -> Word64 --- lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs index e2bc725e7e6..5c3fc1f2e51 100644 --- a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs +++ b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs @@ -92,6 +92,9 @@ outputBalance = foldl' addTxOut 0 . outputs changeBalance :: CoinSelection -> Word64 changeBalance = foldl' addCoin 0 . change +feeBalance :: CoinSelection -> Word64 +feeBalance sel = inputBalance sel - outputBalance sel - changeBalance sel + addTxOut :: Integral a => a -> TxOut -> a addTxOut total = addCoin total . coin From 402c67fb3ce7d631b55919897dae34372912c278 Mon Sep 17 00:00:00 2001 From: Johannes Lund Date: Wed, 4 Dec 2019 10:18:00 +0100 Subject: [PATCH 5/6] Add estimate join-stake-pool fee endpoint --- lib/cli/src/Cardano/CLI.hs | 1 + .../src/Test/Integration/Framework/DSL.hs | 21 +++++++ .../Test/Integration/Framework/TestData.hs | 21 +++++++ lib/core/src/Cardano/Wallet/Api.hs | 10 ++++ lib/core/src/Cardano/Wallet/Api/Server.hs | 24 +++++++- .../Cardano/Wallet/Primitive/CoinSelection.hs | 1 + .../Jormungandr/Scenario/API/StakePools.hs | 58 ++++++++++++++++++- 7 files changed, 134 insertions(+), 2 deletions(-) diff --git a/lib/cli/src/Cardano/CLI.hs b/lib/cli/src/Cardano/CLI.hs index bbaa18a5d3d..df10946eb1e 100644 --- a/lib/cli/src/Cardano/CLI.hs +++ b/lib/cli/src/Cardano/CLI.hs @@ -1120,6 +1120,7 @@ walletClient = _listPools :<|> _joinStakePool + :<|> _joinStakePoolFee :<|> _quitStakePool = pools diff --git a/lib/core-integration/src/Test/Integration/Framework/DSL.hs b/lib/core-integration/src/Test/Integration/Framework/DSL.hs index a80fa1797cd..35a4bd94e32 100644 --- a/lib/core-integration/src/Test/Integration/Framework/DSL.hs +++ b/lib/core-integration/src/Test/Integration/Framework/DSL.hs @@ -86,6 +86,7 @@ module Test.Integration.Framework.DSL , getFromResponseList , json , joinStakePool + , joinStakePoolFee , quitStakePool , listAddresses , listTransactions @@ -123,6 +124,7 @@ module Test.Integration.Framework.DSL , getAddressesEp , listStakePoolsEp , joinStakePoolEp + , joinStakePoolFeeEp , quitStakePoolEp , stakePoolEp , postTxEp @@ -162,6 +164,7 @@ import Cardano.Wallet.Api.Types , ApiByronWallet , ApiByronWalletBalance , ApiEpochInfo + , ApiFee , ApiStakePoolMetrics , ApiT (..) , ApiTransaction @@ -1154,6 +1157,15 @@ quitStakePool ctx p (w, pass) = do } |] request @(ApiTransaction 'Testnet) ctx (quitStakePoolEp p w) Default payload +joinStakePoolFee + :: forall t w. (HasType (ApiT WalletId) w) + => Context t + -> ApiT PoolId + -> w + -> IO (HTTP.Status, Either RequestException ApiFee) +joinStakePoolFee ctx p w = do + request @ApiFee ctx (joinStakePoolFeeEp p w) Default Empty + listAddresses :: Context t -> ApiWallet @@ -1352,6 +1364,15 @@ joinStakePoolEp -> (Method, Text) joinStakePoolEp = stakePoolEp "PUT" +joinStakePoolFeeEp + :: forall w. (HasType (ApiT WalletId) w) + => ApiT PoolId + -> w + -> (Method, Text) +joinStakePoolFeeEp pid w = (verb, path <> "/fee") + where + (verb, path) = stakePoolEp "GET" pid w + quitStakePoolEp :: forall w. (HasType (ApiT WalletId) w) => ApiT PoolId diff --git a/lib/core-integration/src/Test/Integration/Framework/TestData.hs b/lib/core-integration/src/Test/Integration/Framework/TestData.hs index f708e28d54e..b6fff524112 100644 --- a/lib/core-integration/src/Test/Integration/Framework/TestData.hs +++ b/lib/core-integration/src/Test/Integration/Framework/TestData.hs @@ -32,6 +32,10 @@ module Test.Integration.Framework.TestData , russianWalletName , wildcardsWalletName + -- * Stake pool ids + , nonExistingStakePool1 + , invalidPoolIds + -- * Helpers , addressPoolGapMax , addressPoolGapMin @@ -266,6 +270,23 @@ wildcardsWalletName :: Text wildcardsWalletName = "`~`!@#$%^&*()_+-=<>,./?;':\"\"'{}[]\\|❀️ πŸ’” πŸ’Œ πŸ’• πŸ’ž \ \πŸ’“ πŸ’— πŸ’– πŸ’˜ πŸ’ πŸ’Ÿ πŸ’œ πŸ’› πŸ’š πŸ’™0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ πŸ”ŸπŸ‡ΊπŸ‡ΈπŸ‡·πŸ‡ΊπŸ‡Έ πŸ‡¦πŸ‡«πŸ‡¦πŸ‡²πŸ‡Έ" + +-- +-- Stake Pools +-- + +nonExistingStakePool1 :: Text +nonExistingStakePool1 = "008d686a02c6e625b5a59cc9e234f32e5d72987012f9c25c9a6b60ddade197d1" + +invalidPoolIds :: [(String, String)] +invalidPoolIds = + [ ("64 chars non-hex", replicate 64 'Ε›') + , ("63 chars hex", replicate 63 '1') + , ("65 chars hex", replicate 65 '1') + , ("64 chars hex", replicate 64 '1') + , ("empty", "") + ] + --- --- Helpers --- diff --git a/lib/core/src/Cardano/Wallet/Api.hs b/lib/core/src/Cardano/Wallet/Api.hs index b3e7eb64f75..feab779d93e 100644 --- a/lib/core/src/Cardano/Wallet/Api.hs +++ b/lib/core/src/Cardano/Wallet/Api.hs @@ -138,6 +138,7 @@ type CoreApi t = type StakePoolApi t = ListStakePools :<|> JoinStakePool t + :<|> JoinStakePoolFee :<|> QuitStakePool t type CompatibilityApi n = @@ -282,6 +283,15 @@ type JoinStakePool t = "stake-pools" :> ReqBody '[JSON] ApiWalletPassphrase :> PutAcccepted '[JSON] (ApiTransaction t) + +-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/joinStakePool +type JoinStakePoolFee = "stake-pools" + :> Capture "stakePoolId" (ApiT PoolId) + :> "wallets" + :> Capture "walletId" (ApiT WalletId) + :> "fee" + :> Get '[JSON] ApiFee + -- | https://input-output-hk.github.io/cardano-wallet/api/#operation/quitStakePool type QuitStakePool t = "stake-pools" :> Capture "stakePoolId" (ApiT PoolId) diff --git a/lib/core/src/Cardano/Wallet/Api/Server.hs b/lib/core/src/Cardano/Wallet/Api/Server.hs index ada9f4ab76b..064d4a80685 100644 --- a/lib/core/src/Cardano/Wallet/Api/Server.hs +++ b/lib/core/src/Cardano/Wallet/Api/Server.hs @@ -149,7 +149,7 @@ import Cardano.Wallet.Primitive.AddressDiscovery.Random import Cardano.Wallet.Primitive.AddressDiscovery.Sequential ( SeqState (..), defaultAddressPoolGap, mkSeqState ) import Cardano.Wallet.Primitive.CoinSelection - ( CoinSelection, changeBalance, inputBalance ) + ( CoinSelection, changeBalance, feeBalance, inputBalance ) import Cardano.Wallet.Primitive.Fee ( Fee (..) ) import Cardano.Wallet.Primitive.Model @@ -787,6 +787,7 @@ stakePools stakePools ctx spl = listPools spl :<|> joinStakePool ctx spl + :<|> joinStakePoolFee ctx spl :<|> quitStakePool ctx listPools @@ -837,6 +838,27 @@ joinStakePool ctx spl (ApiT pid) (ApiT wid) (ApiWalletPassphrase (ApiT pwd)) = d where liftE = throwE . ErrJoinStakePoolNoSuchWallet +joinStakePoolFee + :: forall ctx s t n k. + ( DelegationAddress n k + , Buildable (ErrValidateSelection t) + , s ~ SeqState n k + , k ~ ShelleyKey + , HardDerivation k + , ctx ~ ApiLayer s t k + ) + => ctx + -> StakePoolLayer IO + -> ApiT PoolId + -> ApiT WalletId + -> Handler ApiFee +joinStakePoolFee ctx _spl (ApiT _pid) (ApiT wid) = do + liftHandler $ withWorkerCtx ctx wid liftE $ \wrk -> + apiFee <$> W.selectCoinsForDelegation @_ @s @t @k wrk wid + where + apiFee = ApiFee . Quantity . fromIntegral . feeBalance + liftE = throwE . ErrSelectForDelegationNoSuchWallet + quitStakePool :: forall ctx s t n k. ( DelegationAddress n k diff --git a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs index 5c3fc1f2e51..a42f65d4f10 100644 --- a/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs +++ b/lib/core/src/Cardano/Wallet/Primitive/CoinSelection.hs @@ -19,6 +19,7 @@ module Cardano.Wallet.Primitive.CoinSelection , inputBalance , outputBalance , changeBalance + , feeBalance , ErrCoinSelection (..) , CoinSelectionOptions (..) ) where diff --git a/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/StakePools.hs b/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/StakePools.hs index 6ea620476b9..719b58a9e62 100644 --- a/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/StakePools.hs +++ b/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/StakePools.hs @@ -13,7 +13,8 @@ module Test.Integration.Jormungandr.Scenario.API.StakePools import Prelude import Cardano.Wallet.Api.Types - ( ApiNetworkInformation + ( ApiFee + , ApiNetworkInformation , ApiStakePool , ApiT (..) , ApiTransaction @@ -40,6 +41,7 @@ import Test.Integration.Framework.DSL , Headers (..) , Payload (..) , TxDescription (..) + , amount , apparentPerformance , balanceAvailable , balanceReward @@ -61,6 +63,7 @@ import Test.Integration.Framework.DSL , expectResponseCode , faucetUtxoAmt , feeEstimator + , fixtureByronWallet , fixturePassphrase , fixtureWallet , fixtureWalletWith @@ -68,6 +71,8 @@ import Test.Integration.Framework.DSL , getWalletEp , joinStakePool , joinStakePoolEp + , joinStakePoolFee + , joinStakePoolFeeEp , json , listStakePoolsEp , listTxEp @@ -81,16 +86,21 @@ import Test.Integration.Framework.DSL , status , unsafeRequest , verify + , walletId ) import Test.Integration.Framework.TestData ( errMsg403DelegationFee , errMsg403PoolAlreadyJoined , errMsg403WrongPass , errMsg403WrongPool + , errMsg404NoEndpoint , errMsg404NoSuchPool + , errMsg404NoWallet , errMsg405 , errMsg406 , errMsg415 + , falseWalletIds + , invalidPoolIds , passphraseMaxLength , passphraseMinLength ) @@ -503,6 +513,52 @@ spec = do r <- joinStakePool ctx (p ^. #id) (w, "Secure Passprase") expectResponseCode HTTP.status404 r + it "STAKE_POOLS_ESTIMATE_FEE_01 - fee matches eventual cost" $ \ctx -> do + (_, p:_) <- eventually $ + unsafeRequest @[ApiStakePool] ctx listStakePoolsEp Empty + w <- fixtureWallet ctx + fee <- getFromResponse amount <$> joinStakePoolFee ctx (p ^. #id) w + r <- joinStakePool ctx (p ^. #id) (w, fixturePassphrase) + verify r + [ expectFieldEqual amount fee + ] + + it "STAKE_POOLS_ESTIMATE_FEE_02 - \ + \empty wallet cannot estimate fee" $ \ctx -> do + (_, p:_) <- eventually $ + unsafeRequest @[ApiStakePool] ctx listStakePoolsEp Empty + w <- emptyWallet ctx + joinStakePoolFee ctx (p ^. #id) w >>= flip verify + [ expectResponseCode HTTP.status403 + , expectErrorMessage $ errMsg403DelegationFee stakeDelegationFee + ] + + it "STAKE_POOLS_ESTIMATE_FEE_03 - can't use byron wallets" $ \ctx -> do + (_, p:_) <- eventually $ + unsafeRequest @[ApiStakePool] ctx listStakePoolsEp Empty + w <- fixtureByronWallet ctx + let ep = joinStakePoolFeeEp (p ^. #id) w + r <- request @(ApiTransaction 'Mainnet) ctx ep Default Empty + verify r + [ expectResponseCode HTTP.status404 -- should fail + , expectErrorMessage $ errMsg404NoWallet (w ^. walletId) + ] + + describe "STAKE_POOLS_ESTIMATE_FEE_04 - invalid pool and wallet ids" $ do + forM_ invalidPoolIds $ \(pDesc, poolId) -> + forM_ falseWalletIds $ \(wDesc, walId) -> do + let path = poolId <> "/wallets/" <> walId + it ("pool:" ++ pDesc ++ ", wallet:" ++ wDesc) $ \ctx -> do + let endpoint = "v2/stake-pools/" + <> T.pack path + <> "/fee" + rg <- request @ApiFee ctx ("GET", endpoint) + Default Empty + expectResponseCode @IO HTTP.status404 rg + if pDesc == "64 chars hex" && wDesc == "40 chars hex" + then expectErrorMessage (errMsg404NoWallet $ T.pack walId) rg + else expectErrorMessage errMsg404NoEndpoint rg + describe "STAKE_POOLS_JOIN/QUIT_05 - Bad request" $ do let verifyIt ctx sPoolEndp = do w <- emptyWallet ctx From decdad3e649ae9278d47815926d729ee56e25941 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Mon, 9 Dec 2019 16:21:29 +0100 Subject: [PATCH 6/6] adjust according to review comments - remove the pool id parameter from the endpoint - use fee estimator in the tests - rename 'joinStakePoolFee' to 'delegationFee' --- lib/cli/src/Cardano/CLI.hs | 3 +- .../src/Test/Integration/Framework/DSL.hs | 24 +++++------ .../Test/Integration/Framework/TestData.hs | 20 --------- lib/core/src/Cardano/Wallet/Api.hs | 11 +++-- lib/core/src/Cardano/Wallet/Api/Server.hs | 8 ++-- .../Jormungandr/Scenario/API/StakePools.hs | 42 +++++++++---------- specifications/api/swagger.yaml | 26 +++++++----- 7 files changed, 56 insertions(+), 78 deletions(-) diff --git a/lib/cli/src/Cardano/CLI.hs b/lib/cli/src/Cardano/CLI.hs index df10946eb1e..d1c021cf2b8 100644 --- a/lib/cli/src/Cardano/CLI.hs +++ b/lib/cli/src/Cardano/CLI.hs @@ -1120,11 +1120,10 @@ walletClient = _listPools :<|> _joinStakePool - :<|> _joinStakePoolFee :<|> _quitStakePool + :<|> _delegationFee = pools - _networkInformation = network in WalletClient diff --git a/lib/core-integration/src/Test/Integration/Framework/DSL.hs b/lib/core-integration/src/Test/Integration/Framework/DSL.hs index 35a4bd94e32..aac0a349570 100644 --- a/lib/core-integration/src/Test/Integration/Framework/DSL.hs +++ b/lib/core-integration/src/Test/Integration/Framework/DSL.hs @@ -86,7 +86,7 @@ module Test.Integration.Framework.DSL , getFromResponseList , json , joinStakePool - , joinStakePoolFee + , delegationFee , quitStakePool , listAddresses , listTransactions @@ -124,7 +124,7 @@ module Test.Integration.Framework.DSL , getAddressesEp , listStakePoolsEp , joinStakePoolEp - , joinStakePoolFeeEp + , delegationFeeEp , quitStakePoolEp , stakePoolEp , postTxEp @@ -1157,14 +1157,13 @@ quitStakePool ctx p (w, pass) = do } |] request @(ApiTransaction 'Testnet) ctx (quitStakePoolEp p w) Default payload -joinStakePoolFee +delegationFee :: forall t w. (HasType (ApiT WalletId) w) => Context t - -> ApiT PoolId -> w -> IO (HTTP.Status, Either RequestException ApiFee) -joinStakePoolFee ctx p w = do - request @ApiFee ctx (joinStakePoolFeeEp p w) Default Empty +delegationFee ctx w = do + request @ApiFee ctx (delegationFeeEp w) Default Empty listAddresses :: Context t @@ -1364,14 +1363,15 @@ joinStakePoolEp -> (Method, Text) joinStakePoolEp = stakePoolEp "PUT" -joinStakePoolFeeEp +delegationFeeEp :: forall w. (HasType (ApiT WalletId) w) - => ApiT PoolId - -> w + => w -> (Method, Text) -joinStakePoolFeeEp pid w = (verb, path <> "/fee") - where - (verb, path) = stakePoolEp "GET" pid w +delegationFeeEp w = + ( "GET" + , "v2/wallets/" <> w ^. walletId <> "/delegations/fees" + ) + quitStakePoolEp :: forall w. (HasType (ApiT WalletId) w) diff --git a/lib/core-integration/src/Test/Integration/Framework/TestData.hs b/lib/core-integration/src/Test/Integration/Framework/TestData.hs index b6fff524112..3b3409e5a92 100644 --- a/lib/core-integration/src/Test/Integration/Framework/TestData.hs +++ b/lib/core-integration/src/Test/Integration/Framework/TestData.hs @@ -32,10 +32,6 @@ module Test.Integration.Framework.TestData , russianWalletName , wildcardsWalletName - -- * Stake pool ids - , nonExistingStakePool1 - , invalidPoolIds - -- * Helpers , addressPoolGapMax , addressPoolGapMin @@ -271,22 +267,6 @@ wildcardsWalletName = "`~`!@#$%^&*()_+-=<>,./?;':\"\"'{}[]\\|❀️ πŸ’” πŸ’Œ \πŸ’“ πŸ’— πŸ’– πŸ’˜ πŸ’ πŸ’Ÿ πŸ’œ πŸ’› πŸ’š πŸ’™0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ πŸ”ŸπŸ‡ΊπŸ‡ΈπŸ‡·πŸ‡ΊπŸ‡Έ πŸ‡¦πŸ‡«πŸ‡¦πŸ‡²πŸ‡Έ" --- --- Stake Pools --- - -nonExistingStakePool1 :: Text -nonExistingStakePool1 = "008d686a02c6e625b5a59cc9e234f32e5d72987012f9c25c9a6b60ddade197d1" - -invalidPoolIds :: [(String, String)] -invalidPoolIds = - [ ("64 chars non-hex", replicate 64 'Ε›') - , ("63 chars hex", replicate 63 '1') - , ("65 chars hex", replicate 65 '1') - , ("64 chars hex", replicate 64 '1') - , ("empty", "") - ] - --- --- Helpers --- diff --git a/lib/core/src/Cardano/Wallet/Api.hs b/lib/core/src/Cardano/Wallet/Api.hs index feab779d93e..f13443fcf94 100644 --- a/lib/core/src/Cardano/Wallet/Api.hs +++ b/lib/core/src/Cardano/Wallet/Api.hs @@ -138,8 +138,8 @@ type CoreApi t = type StakePoolApi t = ListStakePools :<|> JoinStakePool t - :<|> JoinStakePoolFee :<|> QuitStakePool t + :<|> DelegationFee type CompatibilityApi n = DeleteByronWallet @@ -284,12 +284,11 @@ type JoinStakePool t = "stake-pools" :> PutAcccepted '[JSON] (ApiTransaction t) --- | https://input-output-hk.github.io/cardano-wallet/api/#operation/joinStakePool -type JoinStakePoolFee = "stake-pools" - :> Capture "stakePoolId" (ApiT PoolId) - :> "wallets" +-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/delegationFee +type DelegationFee = "wallets" :> Capture "walletId" (ApiT WalletId) - :> "fee" + :> "delegations" + :> "fees" :> Get '[JSON] ApiFee -- | https://input-output-hk.github.io/cardano-wallet/api/#operation/quitStakePool diff --git a/lib/core/src/Cardano/Wallet/Api/Server.hs b/lib/core/src/Cardano/Wallet/Api/Server.hs index 064d4a80685..26ecd92ef03 100644 --- a/lib/core/src/Cardano/Wallet/Api/Server.hs +++ b/lib/core/src/Cardano/Wallet/Api/Server.hs @@ -787,8 +787,8 @@ stakePools stakePools ctx spl = listPools spl :<|> joinStakePool ctx spl - :<|> joinStakePoolFee ctx spl :<|> quitStakePool ctx + :<|> delegationFee ctx listPools :: StakePoolLayer IO @@ -838,7 +838,7 @@ joinStakePool ctx spl (ApiT pid) (ApiT wid) (ApiWalletPassphrase (ApiT pwd)) = d where liftE = throwE . ErrJoinStakePoolNoSuchWallet -joinStakePoolFee +delegationFee :: forall ctx s t n k. ( DelegationAddress n k , Buildable (ErrValidateSelection t) @@ -848,11 +848,9 @@ joinStakePoolFee , ctx ~ ApiLayer s t k ) => ctx - -> StakePoolLayer IO - -> ApiT PoolId -> ApiT WalletId -> Handler ApiFee -joinStakePoolFee ctx _spl (ApiT _pid) (ApiT wid) = do +delegationFee ctx (ApiT wid) = do liftHandler $ withWorkerCtx ctx wid liftE $ \wrk -> apiFee <$> W.selectCoinsForDelegation @_ @s @t @k wrk wid where diff --git a/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/StakePools.hs b/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/StakePools.hs index 719b58a9e62..6bc02067480 100644 --- a/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/StakePools.hs +++ b/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/StakePools.hs @@ -48,6 +48,8 @@ import Test.Integration.Framework.DSL , balanceTotal , blocks , delegation + , delegationFee + , delegationFeeEp , direction , emptyByronWallet , emptyWallet @@ -71,8 +73,6 @@ import Test.Integration.Framework.DSL , getWalletEp , joinStakePool , joinStakePoolEp - , joinStakePoolFee - , joinStakePoolFeeEp , json , listStakePoolsEp , listTxEp @@ -100,7 +100,6 @@ import Test.Integration.Framework.TestData , errMsg406 , errMsg415 , falseWalletIds - , invalidPoolIds , passphraseMaxLength , passphraseMinLength ) @@ -513,11 +512,17 @@ spec = do r <- joinStakePool ctx (p ^. #id) (w, "Secure Passprase") expectResponseCode HTTP.status404 r + -- NOTE + -- This is only true because: + -- + -- 1/ We are in JΓΆrmungandr scenario were fees can be known exactly + -- 2/ Fixture wallets are made of homogeneous UTxOs (all equal to the same + -- value) and therefore, the random selection has no influence. it "STAKE_POOLS_ESTIMATE_FEE_01 - fee matches eventual cost" $ \ctx -> do (_, p:_) <- eventually $ unsafeRequest @[ApiStakePool] ctx listStakePoolsEp Empty w <- fixtureWallet ctx - fee <- getFromResponse amount <$> joinStakePoolFee ctx (p ^. #id) w + fee <- getFromResponse amount <$> delegationFee ctx w r <- joinStakePool ctx (p ^. #id) (w, fixturePassphrase) verify r [ expectFieldEqual amount fee @@ -525,37 +530,30 @@ spec = do it "STAKE_POOLS_ESTIMATE_FEE_02 - \ \empty wallet cannot estimate fee" $ \ctx -> do - (_, p:_) <- eventually $ - unsafeRequest @[ApiStakePool] ctx listStakePoolsEp Empty w <- emptyWallet ctx - joinStakePoolFee ctx (p ^. #id) w >>= flip verify + let (fee, _) = ctx ^. feeEstimator $ DelegDescription 0 0 1 + delegationFee ctx w >>= flip verify [ expectResponseCode HTTP.status403 - , expectErrorMessage $ errMsg403DelegationFee stakeDelegationFee + , expectErrorMessage $ errMsg403DelegationFee fee ] it "STAKE_POOLS_ESTIMATE_FEE_03 - can't use byron wallets" $ \ctx -> do - (_, p:_) <- eventually $ - unsafeRequest @[ApiStakePool] ctx listStakePoolsEp Empty w <- fixtureByronWallet ctx - let ep = joinStakePoolFeeEp (p ^. #id) w + let ep = delegationFeeEp w r <- request @(ApiTransaction 'Mainnet) ctx ep Default Empty verify r [ expectResponseCode HTTP.status404 -- should fail , expectErrorMessage $ errMsg404NoWallet (w ^. walletId) ] - describe "STAKE_POOLS_ESTIMATE_FEE_04 - invalid pool and wallet ids" $ do - forM_ invalidPoolIds $ \(pDesc, poolId) -> - forM_ falseWalletIds $ \(wDesc, walId) -> do - let path = poolId <> "/wallets/" <> walId - it ("pool:" ++ pDesc ++ ", wallet:" ++ wDesc) $ \ctx -> do - let endpoint = "v2/stake-pools/" - <> T.pack path - <> "/fee" - rg <- request @ApiFee ctx ("GET", endpoint) - Default Empty + describe "STAKE_POOLS_ESTIMATE_FEE_04 - wallet ids" $ do + forM_ falseWalletIds $ \(wDesc, walId) -> do + let path = "wallets/" <> walId + it ("wallet:" ++ wDesc) $ \ctx -> do + let endpoint = "v2/" <> T.pack path <> "/delegations/fees" + rg <- request @ApiFee ctx ("GET", endpoint) Default Empty expectResponseCode @IO HTTP.status404 rg - if pDesc == "64 chars hex" && wDesc == "40 chars hex" + if wDesc == "40 chars hex" then expectErrorMessage (errMsg404NoWallet $ T.pack walId) rg else expectErrorMessage errMsg404NoEndpoint rg diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index f85276ac667..08192a1fe70 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -1035,6 +1035,7 @@ x-responsesPostExternalTransaction: &responsesPostExternalTransaction x-responsesPostTransactionFee: &responsesPostTransactionFee <<: *responsesErr400 + <<: *responsesErr403 <<: *responsesErr404 <<: *responsesErr405 <<: *responsesErr406 @@ -1044,7 +1045,8 @@ x-responsesPostTransactionFee: &responsesPostTransactionFee schema: <<: *ApiFee -x-responsesGetJoinStakePoolFee: &responsesGetJoinStakePoolFee +x-responsesGetDelegationFee: &responsesGetDelegationFee + <<: *responsesErr403 <<: *responsesErr404 <<: *responsesErr405 <<: *responsesErr406 @@ -1072,7 +1074,7 @@ x-responsesListStakePools: &responsesListStakePools type: array items: *ApiStakePool -x-responsesJoinStakePool: &responsesJoinStakePool +x-: &responsesJoinStakePool <<: *responsesErr400 <<: *responsesErr403 <<: *responsesErr404 @@ -1084,7 +1086,7 @@ x-responsesJoinStakePool: &responsesJoinStakePool schema: *ApiTransaction x-responsesQuitStakePool: &responsesQuitStakePool - <<: *responsesJoinStakePool + <<: * x-responsesGetNetworkInformation: &responsesGetNetworkInformation <<: *responsesErr405 @@ -1252,7 +1254,7 @@ paths: post: operationId: postTransactionFee tags: ["Transactions"] - summary: Estimate + summary: Estimate Fee description: |

status: stable

@@ -1328,7 +1330,7 @@ paths: - *parametersWalletId - <<: *parametersBody schema: *parametersJoinStakePool - responses: *responsesJoinStakePool + responses: * delete: operationId: quitStakePool @@ -1345,19 +1347,21 @@ paths: schema: *parametersQuitStakePool responses: *responsesQuitStakePool - /stake-pools/{stakePoolId}/wallets/{walletId}/fee: + /wallets/{walletId}/delegations/fees: get: - operationId: joinStakePoolFee + operationId: getDelegationFee tags: ["Stake Pools"] - summary: Estimate + summary: Estimate Fee description: |

status: stable

- Estimate fee for joining a stake pool. + Estimate fee for joining or leaving a stake pool. Note that it is an + estimation because a delegation induces a transaction for which coins + have to be selected randomly within the wallet. Because of this randomness, + fees can only be estimated. parameters: - - *parametersStakePoolId - *parametersWalletId - responses: *responsesGetJoinStakePoolFee + responses: *responsesGetDelegationFee /byron-wallets: get: