plutus-pioneer-program/code/week10/app/uniswap-client.hs

208 lines
7.8 KiB
Haskell
Raw Normal View History

2021-06-07 22:49:01 +02:00
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main
( main
) where
import Control.Concurrent
import Control.Exception
2021-06-09 14:20:10 +02:00
import Control.Monad (forM_, when)
import Control.Monad.IO.Class (MonadIO (..))
2021-06-08 23:35:13 +02:00
import Data.Aeson (Result (..), ToJSON, decode, fromJSON)
2021-06-09 14:20:10 +02:00
import qualified Data.ByteString.Lazy as LB
import Data.Monoid (Last (..))
import Data.Proxy (Proxy (..))
2021-06-08 23:35:13 +02:00
import Data.String (IsString (..))
2021-06-08 10:38:44 +02:00
import Data.Text (Text, pack)
2021-06-08 23:35:13 +02:00
import Data.UUID hiding (fromString)
2021-06-09 15:51:57 +02:00
import Ledger.Value (AssetClass (..), CurrencySymbol, Value, flattenValue, TokenName)
import Network.HTTP.Req
2021-06-08 22:27:06 +02:00
import qualified Plutus.Contracts.Uniswap as US
import Plutus.PAB.Events.ContractInstanceState (PartiallyDecodedResponse (..))
import Plutus.PAB.Webserver.Types
import System.Environment (getArgs)
import System.Exit (exitFailure)
2021-06-09 14:20:10 +02:00
import Text.Printf (printf)
import Text.Read (readMaybe)
import Wallet.Emulator.Types (Wallet (..))
2021-06-08 10:38:44 +02:00
import Uniswap (cidFile, UniswapContracts)
2021-06-07 22:49:01 +02:00
main :: IO ()
main = do
w <- Wallet . read . head <$> getArgs
cid <- read <$> readFile (cidFile w)
2021-06-08 10:45:43 +02:00
mcs <- decode <$> LB.readFile "symbol.json"
2021-06-08 23:35:13 +02:00
case mcs of
Nothing -> putStrLn "invalid symbol.json" >> exitFailure
Just cs -> do
2021-06-08 10:38:44 +02:00
putStrLn $ "cid: " ++ show cid
2021-06-08 10:45:43 +02:00
putStrLn $ "symbol: " ++ show (cs :: CurrencySymbol)
2021-06-08 23:35:13 +02:00
go cid cs
2021-06-08 22:27:06 +02:00
where
2021-06-08 23:35:13 +02:00
go :: UUID -> CurrencySymbol -> IO a
go cid cs = do
2021-06-08 22:27:06 +02:00
cmd <- readCommandIO
case cmd of
2021-06-08 23:35:13 +02:00
Funds -> getFunds cid
Pools -> getPools cid
Create amtA tnA amtB tnB -> createPool cid $ toCreateParams cs amtA tnA amtB tnB
2021-06-09 15:51:57 +02:00
Add amtA tnA amtB tnB -> addLiquidity cid $ toAddParams cs amtA tnA amtB tnB
2021-06-09 15:57:14 +02:00
Remove amt tnA tnB -> removeLiquidity cid $ toRemoveParams cs amt tnA tnB
2021-06-09 13:54:29 +02:00
Swap amtA tnA tnB -> swap cid $ toSwapParams cs amtA tnA tnB
2021-06-08 23:35:13 +02:00
go cid cs
2021-06-08 22:27:06 +02:00
2021-06-08 23:35:13 +02:00
data Command =
Funds
| Pools
| Create Integer Char Integer Char
2021-06-09 15:51:57 +02:00
| Add Integer Char Integer Char
2021-06-09 15:57:14 +02:00
| Remove Integer Char Char
2021-06-09 13:54:29 +02:00
| Swap Integer Char Char
2021-06-08 22:27:06 +02:00
deriving (Show, Read, Eq, Ord)
readCommandIO :: IO Command
readCommandIO = do
2021-06-09 15:57:14 +02:00
putStrLn "Enter a command: Funds, Pools, Create amtA tnA amtB tnB, Add amtA tnA amtB tnB, Remove amt tnA tnB, Swap amtA tnA tnB"
2021-06-08 22:27:06 +02:00
s <- getLine
maybe readCommandIO return $ readMaybe s
2021-06-08 10:38:44 +02:00
2021-06-09 13:54:29 +02:00
toCoin :: CurrencySymbol -> Char -> US.Coin c
toCoin cs tn = US.Coin $ AssetClass (cs, fromString [tn])
2021-06-08 23:35:13 +02:00
toCreateParams :: CurrencySymbol -> Integer -> Char -> Integer -> Char -> US.CreateParams
2021-06-09 13:54:29 +02:00
toCreateParams cs amtA tnA amtB tnB = US.CreateParams (toCoin cs tnA) (toCoin cs tnB) (US.Amount amtA) (US.Amount amtB)
2021-06-09 15:51:57 +02:00
toAddParams :: CurrencySymbol -> Integer -> Char -> Integer -> Char -> US.AddParams
toAddParams cs amtA tnA amtB tnB = US.AddParams (toCoin cs tnA) (toCoin cs tnB) (US.Amount amtA) (US.Amount amtB)
2021-06-09 15:57:14 +02:00
toRemoveParams :: CurrencySymbol -> Integer -> Char -> Char -> US.RemoveParams
toRemoveParams cs amt tnA tnB = US.RemoveParams (toCoin cs tnA) (toCoin cs tnB) (US.Amount amt)
2021-06-09 13:54:29 +02:00
toSwapParams :: CurrencySymbol -> Integer -> Char -> Char -> US.SwapParams
toSwapParams cs amtA tnA tnB = US.SwapParams (toCoin cs tnA) (toCoin cs tnB) (US.Amount amtA) (US.Amount 0)
2021-06-08 23:35:13 +02:00
2021-06-09 15:45:52 +02:00
showCoinHeader :: IO ()
showCoinHeader = printf "\n currency symbol token name amount\n\n"
showCoin :: CurrencySymbol -> TokenName -> Integer -> IO ()
showCoin cs tn amt = printf "%64s %66s %15d\n" (show cs) (show tn) amt
2021-06-08 10:38:44 +02:00
getFunds :: UUID -> IO ()
2021-06-08 23:35:13 +02:00
getFunds cid = do
callEndpoint cid "funds" ()
threadDelay 2_000_000
go
2021-06-08 22:27:06 +02:00
where
2021-06-08 23:35:13 +02:00
go = do
e <- getStatus cid
case e of
2021-06-09 14:20:10 +02:00
Right (US.Funds v) -> showFunds v
2021-06-08 23:35:13 +02:00
_ -> go
2021-06-08 22:27:06 +02:00
2021-06-09 14:20:10 +02:00
showFunds :: Value -> IO ()
showFunds v = do
2021-06-09 15:45:52 +02:00
showCoinHeader
forM_ (flattenValue v) $ \(cs, tn, amt) -> showCoin cs tn amt
2021-06-09 14:20:10 +02:00
printf "\n"
2021-06-08 22:27:06 +02:00
getPools :: UUID -> IO ()
2021-06-08 23:35:13 +02:00
getPools cid = do
callEndpoint cid "pools" ()
threadDelay 2_000_000
go
2021-06-08 10:38:44 +02:00
where
2021-06-08 23:35:13 +02:00
go = do
e <- getStatus cid
case e of
2021-06-09 15:45:52 +02:00
Right (US.Pools ps) -> showPools ps
2021-06-08 23:35:13 +02:00
_ -> go
2021-06-07 22:49:01 +02:00
2021-06-09 15:45:52 +02:00
showPools :: [((US.Coin US.A, US.Amount US.A), (US.Coin US.B, US.Amount US.B))] -> IO ()
showPools ps = do
forM_ ps $ \((US.Coin (AssetClass (csA, tnA)), amtA), (US.Coin (AssetClass (csB, tnB)), amtB)) -> do
showCoinHeader
showCoin csA tnA (US.unAmount amtA)
showCoin csB tnB (US.unAmount amtB)
2021-06-08 23:35:13 +02:00
createPool :: UUID -> US.CreateParams -> IO ()
2021-06-09 13:54:29 +02:00
createPool cid cp = do
callEndpoint cid "create" cp
2021-06-08 23:35:13 +02:00
threadDelay 2_000_000
go
2021-06-07 22:49:01 +02:00
where
2021-06-08 23:35:13 +02:00
go = do
e <- getStatus cid
case e of
Right US.Created -> putStrLn "created"
Left err' -> putStrLn $ "error: " ++ show err'
_ -> go
2021-06-07 22:49:01 +02:00
2021-06-09 15:51:57 +02:00
addLiquidity :: UUID -> US.AddParams -> IO ()
addLiquidity cid ap = do
callEndpoint cid "add" ap
threadDelay 2_000_000
go
where
go = do
e <- getStatus cid
case e of
Right US.Added -> putStrLn "added"
Left err' -> putStrLn $ "error: " ++ show err'
_ -> go
2021-06-09 15:57:14 +02:00
removeLiquidity :: UUID -> US.RemoveParams -> IO ()
removeLiquidity cid rp = do
callEndpoint cid "remove" rp
threadDelay 2_000_000
go
where
go = do
e <- getStatus cid
case e of
Right US.Removed -> putStrLn "removed"
Left err' -> putStrLn $ "error: " ++ show err'
_ -> go
2021-06-09 13:54:29 +02:00
swap :: UUID -> US.SwapParams -> IO ()
swap cid sp = do
callEndpoint cid "swap" sp
threadDelay 2_000_000
go
where
go = do
e <- getStatus cid
case e of
Right US.Swapped -> putStrLn "swapped"
Left err' -> putStrLn $ "error: " ++ show err'
_ -> go
2021-06-08 23:35:13 +02:00
getStatus :: UUID -> IO (Either Text US.UserContractState)
getStatus cid = runReq defaultHttpConfig $ do
w <- req
GET
(http "127.0.0.1" /: "api" /: "new" /: "contract" /: "instance" /: pack (show cid) /: "status")
NoReqBody
(Proxy :: Proxy (JsonResponse (ContractInstanceClientState UniswapContracts)))
2021-06-07 22:49:01 +02:00
(port 8080)
2021-06-08 23:35:13 +02:00
case fromJSON $ observableState $ cicCurrentState $ responseBody w of
Success (Last Nothing) -> liftIO $ threadDelay 1_000_000 >> getStatus cid
Success (Last (Just e)) -> return e
_ -> liftIO $ ioError $ userError "error decoding state"
2021-06-07 22:49:01 +02:00
2021-06-08 23:35:13 +02:00
callEndpoint :: ToJSON a => UUID -> String -> a -> IO ()
callEndpoint cid name a = handle h $ runReq defaultHttpConfig $ do
2021-06-07 22:49:01 +02:00
v <- req
POST
2021-06-08 23:35:13 +02:00
(http "127.0.0.1" /: "api" /: "new" /: "contract" /: "instance" /: pack (show cid) /: "endpoint" /: pack name)
(ReqBodyJson a)
2021-06-07 22:49:01 +02:00
(Proxy :: Proxy (JsonResponse ()))
(port 8080)
2021-06-08 23:35:13 +02:00
when (responseStatusCode v /= 200) $
liftIO $ ioError $ userError $ "error calling endpoint " ++ name
2021-06-07 22:49:01 +02:00
where
h :: HttpException -> IO ()
2021-06-08 23:35:13 +02:00
h = ioError . userError . show