web3-polkadot-1.0.1.0: src/Network/Polkadot/Extrinsic.hs
{-# LANGUAGE FlexibleContexts #-}
-- |
-- Module : Network.Polkadot.Extrinsic
-- Copyright : Aleksandr Krupenkin 2016-2024
-- License : Apache-2.0
--
-- Maintainer : mail@akru.me
-- Stability : experimental
-- Portability : unportable
--
-- Extrinsic is a piece of data from external world.
--
module Network.Polkadot.Extrinsic
( Extrinsic
, SignedExtra
, sign_and_send
, mortal_max
, new_extra'
, new_extra
) where
import Codec.Scale (Compact (..),
Encode,
encode)
import Data.ByteArray.HexString (HexString)
import Data.Maybe (fromJust)
import Data.Text.Encoding (decodeUtf8)
import Network.JsonRpc.TinyClient (JsonRpc)
import Network.Polkadot.Account (AccountId,
IdentifyAccount (..),
Ss58Codec (to_ss58check))
import Network.Polkadot.Crypto (MultiPair (..))
import Network.Polkadot.Extrinsic.Era (Era (..))
import Network.Polkadot.Extrinsic.SignedExtension.System
import Network.Polkadot.Extrinsic.SignedExtension.TransactionPayment
import Network.Polkadot.Extrinsic.Unchecked (UncheckedExtrinsic,
sign_extrinsic)
import Network.Polkadot.Primitives (Balance,
Index)
import qualified Network.Polkadot.Primitives as P (MultiAddress,
MultiSignature)
import Network.Polkadot.Rpc.Account (nextIndex)
import Network.Polkadot.Rpc.Author (submitExtrinsic)
import Network.Polkadot.Rpc.Chain (getHeader)
import Network.Polkadot.Rpc.Types (headerNumber,
unBlockNumber)
-- | Default Polkadot compatible extrinsic type.
type Extrinsic a = UncheckedExtrinsic a P.MultiAddress P.MultiSignature SignedExtra
-- | Default Polkadot signed extra.
type SignedExtra =
( CheckSpecVersion
, CheckTxVersion
, CheckGenesis
, CheckEra
, CheckNonce
, CheckWeight
, ChargeTransactionPayment
)
new_extra :: (Ss58Codec a, JsonRpc m)
=> a
-- ^ Transaction sender address.
-> Balance
-- ^ Transaction tips, or set zero for no tips.
-> m SignedExtra
-- ^ Returns Polkadot transaction extra.
new_extra account_id tip = do
nonce <- fromIntegral <$> nextIndex ss58account
era <- mortal_max
return $ new_extra' era nonce tip
where
ss58account = decodeUtf8 $ to_ss58check account_id
-- | Create signed extra from general data.
new_extra' :: Era
-- ^ Transaction mortality.
-> Index
-- ^ Transaction nonce value.
-> Balance
-- ^ Transaction tips, or set zero for no tips.
-> SignedExtra
-- ^ Returns Polkadot transaction extra.
new_extra' era nonce tip =
( CheckSpecVersion
, CheckTxVersion
, CheckGenesis
, CheckEra era
, CheckNonce (Compact nonce)
, CheckWeight
, ChargeTransactionPayment (Compact tip)
)
-- | Create a mortal 'Era' with biggest lifetime period.
--
-- Note: The assumption is runtime has `BlockHashCount` = 2400. This is common
-- for Polkadot runtimes.
mortal_max :: JsonRpc m => m Era
mortal_max = do
current <- (unBlockNumber . headerNumber . fromJust) <$> getHeader Nothing
return $ MortalEra 2048 $ fromIntegral (current - 1)
-- | Sign extrinsic and send it using node RPC call.
sign_and_send :: ( MultiPair pair
, IdentifyAccount (MultiSigner pair)
, Ss58Codec (AccountId (MultiSigner pair))
, Encode (MultiAddress pair)
, Encode (MultiSignature pair)
, Encode call
, JsonRpc m
)
=> pair
-- ^ Sender account pair.
-> call
-- ^ Runtime function to call.
-> Balance
-- ^ Tips for speedup transaction (set zero for no boost).
-> m HexString
-- ^ Transaction hash.
sign_and_send pair call tip = do
extra <- new_extra account_id tip
xt <- sign_extrinsic pair call extra
submitExtrinsic (encode xt)
where
account_id = into_account (multi_signer pair)