azimuth-hs 0.2.0 → 0.2.1
raw patch · 7 files changed
+103/−25 lines, 7 files
Files
- CHANGELOG +6/−0
- azimuth-hs.cabal +1/−1
- lib/Urbit/Azimuth/Account.hs +9/−7
- lib/Urbit/Azimuth/Contract.hs +52/−12
- lib/Urbit/Azimuth/Transaction.hs +21/−0
- test/Doc.hs +5/−2
- test/Main.hs +9/−3
CHANGELOG view
@@ -1,5 +1,11 @@ # Changelog +- 0.2.1 (2020-08-13)+ * Allows the specification of optional transaction parameters via+ Urbit.Azimuth.runAzimuth' (note the apostrophe).+ * The chain ID must now be passed explicitly to the+ Urbit.Azimuth.Account.toPrivateKey helper (previously it defaulted to 1).+ - 0.2.0 (2020-08-13) * Generalizes account types to include hs-web3's 'DefaultAccount' and 'PersonalAccount', in addition to the existing 'LocalKeyAccount'.
azimuth-hs.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: azimuth-hs-version: 0.2.0+version: 0.2.1 synopsis: Interact with Azimuth from Haskell description: Haskell bindings for Azimuth (https://github.com/urbit/azimuth) homepage: https://github.com/urbit/azimuth-hs
lib/Urbit/Azimuth/Account.hs view
@@ -34,23 +34,25 @@ type Unlockable p m = (JsonRpc m, A.Account p (AI.AccountT p)) --- | Recover a private key from a BIP39 mnemonic, passphrase, and HD derivation--- path.+-- | Recover a private key from a BIP39 mnemonic, passphrase, HD derivation+-- path, and chain ID. -- -- Note that you can use the monoid unit, 'mempty', to denote an empty or--- absent passphrase.+-- absent passphrase. It's also worth noting that you'll typically want to+-- use a chain ID of '1' (i.e., the Ethereum mainnet). -- -- >>> let mnem = "benefit crew supreme gesture quantum web media hazard theory mercy wing kitten"--- >>> let hdpath = Deriv :| 44 :| 60 :| 0 :/ 0 :/ 0 -- m/44'/60'/0'/0/0--- >>> toPrivateKey mnem mempty hdpath+-- >>> let hdpath = "m/44'/60'/0'/0/0" :: DerivPath+-- >>> toPrivateKey mnem mempty hdpath 1 toPrivateKey :: K.Mnemonic -> K.Passphrase -> K.DerivPath+ -> Integer -> Either String A.LocalKey-toPrivateKey mnem pass path = do+toPrivateKey mnem pass path chainId = do seed <- K.mnemonicToSeed pass mnem- pure . flip A.LocalKey 1 -- NB this targets mainnet only+ pure . flip A.LocalKey chainId . C.importKey . K.getSecKey . K.xPrvKey
lib/Urbit/Azimuth/Contract.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} @@ -12,16 +11,18 @@ , Azimuth(..) , runAzimuth+ , runAzimuth' ) where import Control.Monad.Trans (lift)-import Control.Monad.Reader (ReaderT(..), ask)+import Control.Monad.Reader (ReaderT(..), asks) import Data.Solidity.Prim.Address (Address) import qualified Network.Ethereum.Account as Ethereum.Account import qualified Network.Ethereum.Account.Internal as AI import qualified Network.Ethereum.Ens as Ethereum.Ens import Network.JsonRpc.TinyClient (JsonRpc) import Urbit.Azimuth.Account+import Urbit.Azimuth.Transaction -- | Supported Azimuth contracts. data Contracts = Contracts {@@ -29,13 +30,19 @@ , ecliptic :: Address } deriving stock Show +-- | The environment for any given Azimuth action.+data AzimuthEnv = AzimuthEnv {+ azimuthContracts :: Contracts+ , azimuthTxnParams :: Maybe TxnParams+ } deriving stock Show+ -- | The Azimuth type represents an authenticated connection to a JSON RPC by -- way of a local private key, and has access to a 'Contracts' object. newtype Azimuth p m a =- Azimuth (ReaderT Contracts (AI.AccountT p m) a)+ Azimuth (ReaderT AzimuthEnv (AI.AccountT p m) a) deriving newtype (Functor, Applicative, Monad) --- | Run an Azimuth action.+-- | Run an Azimuth action using default optional transaction parameters. runAzimuth :: Unlockable p m => Contracts@@ -43,8 +50,21 @@ -> Azimuth p m a -> m a runAzimuth contracts account (Azimuth action) = withAccount account $- runReaderT action contracts+ runReaderT action (AzimuthEnv contracts Nothing) +-- | Run an Azimuth action, specifying optional transaction parameters+-- explicitly.+runAzimuth'+ :: Unlockable p m+ => Contracts+ -> TxnParams+ -> p+ -> Azimuth p m a+ -> m a+runAzimuth' contracts params account (Azimuth action) =+ withAccount account $+ runReaderT action (AzimuthEnv contracts (Just params))+ -- | Fetch the Azimuth and Ecliptic contracts by way of their ENS records. getContracts :: JsonRpc m => m Contracts getContracts = withAccount () $ do@@ -59,11 +79,31 @@ -> AI.AccountT p m a -> Azimuth p m a withContract selector action = Azimuth $ do- contracts <- ask- lift $ Ethereum.Account.withParam (\param -> param {- AI._to = Just (selector contracts)- -- NB hardcoded to 40 gwei at present- -- going to want a better way to specify gas price- , AI._gasPrice = Just 40_000_000_000- }) action+ contracts <- asks azimuthContracts+ txnParams <- asks azimuthTxnParams + let contract = Just (selector contracts)++ TxnParams {..} = case txnParams of+ Nothing -> defaultTxnParams+ Just ps -> ps++ -- NB this can probably be improved with a better understanding of+ -- Network.Ethereum.Api.Types.DefaultBlock+ --+ param pars = case txnBlock of++ Just block -> pars {+ AI._to = contract+ , AI._gasLimit = fmap fromIntegral txnGasLimit+ , AI._gasPrice = fmap fromIntegral txnGasPrice+ , AI._block = block+ }++ Nothing -> pars {+ AI._to = contract+ , AI._gasLimit = fmap fromIntegral txnGasLimit+ , AI._gasPrice = fmap fromIntegral txnGasPrice+ }++ lift $ Ethereum.Account.withParam param action
lib/Urbit/Azimuth/Transaction.hs view
@@ -1,7 +1,28 @@+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE NumericUnderscores #-} module Urbit.Azimuth.Transaction ( Api.TxReceipt(..)++ , TxnParams(..)+ , defaultTxnParams ) where import qualified Network.Ethereum.Api.Types as Api+import Numeric.Natural++-- | Optional transaction parameters for Azimuth actions.+data TxnParams = TxnParams {+ txnGasLimit :: Maybe Natural+ , txnGasPrice :: Maybe Natural+ , txnBlock :: Maybe Api.DefaultBlock+ } deriving stock Show++-- | Default optional transaction parameters (i.e., a gas price of 80 Gwei).+defaultTxnParams :: TxnParams+defaultTxnParams = TxnParams {+ txnGasLimit = Nothing+ , txnGasPrice = Just 80_000_000_000+ , txnBlock = Nothing+ }
test/Doc.hs view
@@ -30,10 +30,13 @@ let mnem = "benefit crew supreme gesture quantum " <> "web media hazard theory mercy wing kitten" - -- and a standard HD path+ -- a standard HD path let hdpath = "m/44'/60'/0'/0/0" :: A.DerivPath - let account = case A.toPrivateKey mnem mempty hdpath of+ -- and ethereum mainnet+ let chainId = 1++ let account = case A.toPrivateKey mnem mempty hdpath chainId of Left _ -> error "bogus creds" Right acc -> acc
test/Main.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE RecordWildCards #-} module Main where@@ -52,9 +53,9 @@ testConfig = Config {..} where endpt = "http://localhost:8545" pbase = A.Deriv A.:| 44 A.:| 60 A.:| 0 A.:/ 0- Right pk0 = A.toPrivateKey mnem mempty (pbase A.:/ 0)- Right pk1 = A.toPrivateKey mnem mempty (pbase A.:/ 1)- Right pk2 = A.toPrivateKey mnem mempty (pbase A.:/ 2)+ Right pk0 = A.toPrivateKey mnem mempty (pbase A.:/ 0) 1+ Right pk1 = A.toPrivateKey mnem mempty (pbase A.:/ 1) 1+ Right pk2 = A.toPrivateKey mnem mempty (pbase A.:/ 2) 1 mnem = "benefit crew supreme gesture quantum web media hazard " <> "theory mercy wing kitten"@@ -165,3 +166,8 @@ getRift z1 `shouldSatisfy` (> (getRift z0)) getRift n1 `shouldSatisfy` (> (getRift n0)) + it "works with optional transaction parameters" $ do+ let txnParams = A.defaultTxnParams { A.txnGasPrice = 100_000_000_000 }+ A.runWeb3 endpoint $+ A.runAzimuth' contracts txnParams pk0 $+ A.configureKeys zod keys A.Rotate