Skip to content

Commit

Permalink
Merge pull request #507 from input-output-hk/paweljakubas/469/add-est…
Browse files Browse the repository at this point in the history
…imate-fee-endpoint

Add estimate fee endpoint
  • Loading branch information
KtorZ authored Jul 4, 2019
2 parents b4b922b + a30dbbb commit 2951956
Show file tree
Hide file tree
Showing 11 changed files with 1,908 additions and 126 deletions.
12 changes: 10 additions & 2 deletions lib/cli/src/Cardano/CLI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,13 @@ import Cardano.Wallet.Api.Server
import Cardano.Wallet.Api.Types
( AddressAmount
, ApiAddress
, ApiFee
, ApiMnemonicT (..)
, ApiT (..)
, ApiTransaction
, ApiWallet
, PostTransactionData (..)
, PostTransactionFeeData (..)
, WalletPostData (..)
, WalletPutData (..)
)
Expand Down Expand Up @@ -695,6 +697,10 @@ data WalletClient t = WalletClient
:: ApiT WalletId
-> PostTransactionData t
-> ClientM (ApiTransaction t)
, postTransactionFee
:: ApiT WalletId
-> PostTransactionFeeData t
-> ClientM ApiFee
}

walletClient :: forall t. (DecodeAddress t, EncodeAddress t) => WalletClient t
Expand All @@ -714,8 +720,9 @@ walletClient =
:<|> _ -- Put Wallet Passphrase
= wallets

_postTransaction =
transactions
_postTransaction
:<|> _postTransactionFee
= transactions
in
WalletClient
{ listAddresses = _listAddresses
Expand All @@ -725,6 +732,7 @@ walletClient =
, postWallet = _postWallet
, putWallet = _putWallet
, postTransaction = _postTransaction
, postTransactionFee = _postTransactionFee
}

runClient
Expand Down
33 changes: 33 additions & 0 deletions lib/core/src/Cardano/Wallet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module Cardano.Wallet

-- * Errors
, ErrCreateUnsignedTx (..)
, ErrEstimateTxFee (..)
, ErrNoSuchWallet (..)
, ErrSignTx (..)
, ErrSubmitTx (..)
Expand Down Expand Up @@ -78,6 +79,7 @@ import Cardano.Wallet.Primitive.CoinSelection
)
import Cardano.Wallet.Primitive.Fee
( ErrAdjustForFee (..)
, Fee (..)
, FeeOptions (..)
, FeePolicy
, adjustForFee
Expand Down Expand Up @@ -243,6 +245,15 @@ data WalletLayer s t = WalletLayer
-- coin selection for the given outputs. In order to construct (and
-- sign) an actual transaction, have a look at 'signTx'.

, estimateTxFee
:: (DefineTx t)
=> WalletId
-> CoinSelectionOptions
-> NonEmpty TxOut
-> ExceptT ErrEstimateTxFee IO Fee
-- ^ Estimate a transaction fee by automatically selecting inputs from the
-- wallet to cover the requested outputs.

, signTx
:: (Show s, NFData s, IsOwned s, GenChange s)
=> WalletId
Expand Down Expand Up @@ -278,6 +289,12 @@ data ErrCreateUnsignedTx
| ErrCreateUnsignedTxFee ErrAdjustForFee
deriving (Show, Eq)

-- | Errors occuring when estimating transaction fee
data ErrEstimateTxFee
= ErrEstimateTxFeeNoSuchWallet ErrNoSuchWallet
| ErrEstimateTxFeeCoinSelection ErrCoinSelection
deriving (Show, Eq)

-- | Errors occuring when signing a transaction
data ErrSignTx
= ErrSignTx ErrMkStdTx
Expand Down Expand Up @@ -361,6 +378,7 @@ newWalletLayer tracer block0 feePolicy db nw tl = do
, restoreWallet = _restoreWallet registry
, listAddresses = _listAddresses
, createUnsignedTx = _createUnsignedTx
, estimateTxFee = _estimateTxFee
, signTx = _signTx
, submitTx = _submitTx
, attachPrivateKey = _attachPrivateKey
Expand Down Expand Up @@ -607,6 +625,21 @@ newWalletLayer tracer block0 feePolicy db nw tl = do
}
debug "Coins after fee adjustment" =<< adjustForFee feeOpts utxo' sel


_estimateTxFee
:: DefineTx t
=> WalletId
-> CoinSelectionOptions
-> NonEmpty TxOut
-> ExceptT ErrEstimateTxFee IO Fee
_estimateTxFee wid opts recipients = do
(w, _) <- withExceptT ErrEstimateTxFeeNoSuchWallet (_readWallet wid)
let utxo = availableUTxO @s @t w
(sel, _utxo') <- withExceptT ErrEstimateTxFeeCoinSelection $
CoinSelection.random opts recipients utxo
let estimateFee = computeFee feePolicy . estimateSize tl
pure $ estimateFee sel

_signTx
:: (Show s, NFData s, IsOwned s, GenChange s)
=> WalletId
Expand Down
12 changes: 12 additions & 0 deletions lib/core/src/Cardano/Wallet/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ module Cardano.Wallet.Api where

import Cardano.Wallet.Api.Types
( ApiAddress
, ApiFee
, ApiT
, ApiTransaction
, ApiWallet
, PostTransactionData
, PostTransactionFeeData
, WalletPostData
, WalletPutData
, WalletPutPassphraseData
Expand Down Expand Up @@ -107,6 +109,7 @@ type PutWalletPassphrase = "wallets"

type Transactions t =
CreateTransaction t
:<|> PostTransactionFee t

-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/postTransaction
type CreateTransaction t = "wallets"
Expand All @@ -115,6 +118,15 @@ type CreateTransaction t = "wallets"
:> ReqBody '[JSON] (PostTransactionData t)
:> PostAccepted '[JSON] (ApiTransaction t)


-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/postTransactionFee
type PostTransactionFee t = "wallets"
:> Capture "walletId" (ApiT WalletId)
:> "transactions"
:> "fees"
:> ReqBody '[JSON] (PostTransactionFeeData t)
:> PostAccepted '[JSON] ApiFee

{-------------------------------------------------------------------------------
Internals
-------------------------------------------------------------------------------}
Expand Down
37 changes: 33 additions & 4 deletions lib/core/src/Cardano/Wallet/Api/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import Cardano.Wallet
( ErrAdjustForFee (..)
, ErrCoinSelection (..)
, ErrCreateUnsignedTx (..)
, ErrEstimateTxFee (..)
, ErrMkStdTx (..)
, ErrNetworkUnreachable (..)
, ErrNoSuchWallet (..)
Expand All @@ -47,10 +48,12 @@ import Cardano.Wallet.Api.Types
( AddressAmount (..)
, ApiAddress (..)
, ApiErrorCode (..)
, ApiFee (..)
, ApiT (..)
, ApiTransaction (..)
, ApiWallet (..)
, PostTransactionData
, PostTransactionFeeData
, WalletBalance (..)
, WalletPostData (..)
, WalletPutData (..)
Expand All @@ -63,6 +66,8 @@ import Cardano.Wallet.Primitive.AddressDiscovery
( SeqState (..), defaultAddressPoolGap, mkSeqState )
import Cardano.Wallet.Primitive.CoinSelection
( CoinSelectionOptions (..) )
import Cardano.Wallet.Primitive.Fee
( Fee (..) )
import Cardano.Wallet.Primitive.Model
( availableBalance, getState, totalBalance )
import Cardano.Wallet.Primitive.Types
Expand Down Expand Up @@ -367,7 +372,9 @@ transactions
:: (DefineTx t, KeyToAddress t)
=> WalletLayer (SeqState t) t
-> Server (Transactions t)
transactions = createTransaction
transactions w =
createTransaction w
:<|> postTransactionFee w

createTransaction
:: forall t. (DefineTx t, KeyToAddress t)
Expand All @@ -394,13 +401,30 @@ createTransaction w (ApiT wid) body = do
, status = ApiT (meta ^. #status)
}
where
coerceCoin :: AddressAmount t -> TxOut
coerceCoin (AddressAmount (ApiT addr, _) (Quantity c)) =
TxOut addr (Coin $ fromIntegral c)
coerceTxOut :: TxOut -> AddressAmount t
coerceTxOut (TxOut addr (Coin c)) =
AddressAmount (ApiT addr, Proxy @t) (Quantity $ fromIntegral c)

coerceCoin :: AddressAmount t -> TxOut
coerceCoin (AddressAmount (ApiT addr, _) (Quantity c)) =
TxOut addr (Coin $ fromIntegral c)

postTransactionFee
:: forall t. (DefineTx t)
=> WalletLayer (SeqState t) t
-> ApiT WalletId
-> PostTransactionFeeData t
-> Handler ApiFee
postTransactionFee w (ApiT wid) body = do
-- FIXME Compute the options based on the transaction's size / inputs
let opts = CoinSelectionOptions { maximumNumberOfInputs = 10 }
let outs = coerceCoin <$> (body ^. #payments)
(Fee fee) <- liftHandler $ W.estimateTxFee w wid opts outs
return ApiFee
{ amount = Quantity (fromIntegral fee)
}


{-------------------------------------------------------------------------------
Error Handling
-------------------------------------------------------------------------------}
Expand Down Expand Up @@ -507,6 +531,11 @@ instance LiftHandler ErrCreateUnsignedTx where
ErrCreateUnsignedTxCoinSelection e -> handler e
ErrCreateUnsignedTxFee e -> handler e

instance LiftHandler ErrEstimateTxFee where
handler = \case
ErrEstimateTxFeeNoSuchWallet e -> handler e
ErrEstimateTxFeeCoinSelection e -> handler e

instance LiftHandler ErrSignTx where
handler = \case
ErrSignTx (ErrKeyNotFoundForAddress addr) ->
Expand Down
20 changes: 20 additions & 0 deletions lib/core/src/Cardano/Wallet/Api/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ module Cardano.Wallet.Api.Types
, WalletPutData (..)
, WalletPutPassphraseData (..)
, PostTransactionData (..)
, PostTransactionFeeData (..)
, ApiBlockData (..)
, ApiTransaction (..)
, ApiFee (..)
, AddressAmount (..)
, ApiErrorCode (..)

Expand Down Expand Up @@ -163,6 +165,14 @@ data PostTransactionData t = PostTransactionData
, passphrase :: !(ApiT (Passphrase "encryption"))
} deriving (Eq, Generic, Show)

newtype PostTransactionFeeData t = PostTransactionFeeData
{ payments :: (NonEmpty (AddressAmount t))
} deriving (Eq, Generic, Show)

newtype ApiFee = ApiFee
{ amount :: (Quantity "lovelace" Natural)
} deriving (Eq, Generic, Show)

data ApiTransaction t = ApiTransaction
{ id :: !(ApiT (Hash "Tx"))
, amount :: !(Quantity "lovelace" Natural)
Expand Down Expand Up @@ -292,6 +302,11 @@ instance FromJSON WalletPutPassphraseData where
instance ToJSON WalletPutPassphraseData where
toJSON = genericToJSON defaultRecordTypeOptions

instance FromJSON ApiFee where
parseJSON = genericParseJSON defaultRecordTypeOptions
instance ToJSON ApiFee where
toJSON = genericToJSON defaultRecordTypeOptions

instance (PassphraseMaxLength purpose, PassphraseMinLength purpose)
=> FromJSON (ApiT (Passphrase purpose)) where
parseJSON = parseJSON >=> eitherToParser . bimap ShowFmt ApiT . fromText
Expand Down Expand Up @@ -354,6 +369,11 @@ instance DecodeAddress t => FromJSON (PostTransactionData t) where
instance EncodeAddress t => ToJSON (PostTransactionData t) where
toJSON = genericToJSON defaultRecordTypeOptions

instance DecodeAddress t => FromJSON (PostTransactionFeeData t) where
parseJSON = genericParseJSON defaultRecordTypeOptions
instance EncodeAddress t => ToJSON (PostTransactionFeeData t) where
toJSON = genericToJSON defaultRecordTypeOptions

instance FromJSON (ApiT SlotId) where
parseJSON = fmap ApiT . genericParseJSON defaultRecordTypeOptions
instance ToJSON (ApiT SlotId) where
Expand Down
65 changes: 65 additions & 0 deletions lib/core/test/data/Cardano/Wallet/Api/ApiFee.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"seed": 1702885383116510579,
"samples": [
{
"amount": {
"quantity": 201,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 29,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 77,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 188,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 130,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 248,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 251,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 3,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 117,
"unit": "lovelace"
}
},
{
"amount": {
"quantity": 196,
"unit": "lovelace"
}
}
]
}
Loading

0 comments on commit 2951956

Please sign in to comment.