diff --git a/package.json b/package.json index 24a683b..ed12303 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "test": "spago -x test.dhall test" }, "dependencies": { - "bn.js": "^4.11.0", "ethjs-provider-http": "^0.1.6", "keccak": "^1.0.2", "rlp": "^2.0.0", diff --git a/packages.dhall b/packages.dhall index 8788b92..2f31ced 100644 --- a/packages.dhall +++ b/packages.dhall @@ -1,6 +1,9 @@ let upstream = https://raw.githubusercontent.com/f-o-a-m/package-sets/09b71674a327f7601276846c1afb537342bb57ff/purs-0.15.7-web3.dhall sha256:7e32f0c65a7b5d334ee98d7fda1d7d3a557b6b478421f545694bd8e1cd4d16ac + with eth-core.version = "v10.0.0" + with js-bigints.version = "v2.2.0" + let overrides = {=} diff --git a/spago.dhall b/spago.dhall index 2fe92e1..01ef064 100644 --- a/spago.dhall +++ b/spago.dhall @@ -17,6 +17,7 @@ , "foreign" , "foreign-object" , "fork" + , "gen" , "heterogeneous" , "maybe" , "newtype" diff --git a/src/Network/Ethereum/Web3.purs b/src/Network/Ethereum/Web3.purs index 4944864..f92f8e0 100644 --- a/src/Network/Ethereum/Web3.purs +++ b/src/Network/Ethereum/Web3.purs @@ -9,11 +9,7 @@ module Network.Ethereum.Web3 import Network.Ethereum.Web3.Contract (class EventFilter, event, eventFilter, call, sendTx, deployContract, mkDataField) import Network.Ethereum.Web3.Contract.Events (event', EventHandler, MultiFilterStreamState(..), FilterStreamState, ChangeReceipt) import Network.Ethereum.Web3.Solidity - ( class KnownSize - , sizeVal - , class IntSize - , class ByteSize - , Address + ( Address , BigNumber , ByteString , BytesN @@ -31,5 +27,5 @@ import Network.Ethereum.Web3.Solidity , vCons , (:<) ) -import Network.Ethereum.Web3.Types (forkWeb3, forkWeb3', runWeb3, Address, Babbage, BigNumber, Block(..), BlockNumber(..), ChainCursor(..), Change(..), Ether, EventAction(..), Filter, FilterId, Finney, HexString, KEther, Lovelace, CallError(..), Shannon, Szabo, Transaction(..), TransactionOptions(..), TransactionReceipt(..), TransactionStatus(..), Value, Web3, Web3Par, Web3Error(..), Wei, _address, _data, _from, _fromBlock, _gas, _gasPrice, _nonce, _to, _toBlock, _topics, _value, convert, defaultFilter, defaultTransactionOptions, embed, formatValue, fromMinorUnit, mkAddress, mkHexString, mkValue, toMinorUnit, throwWeb3, unAddress, unHex) +import Network.Ethereum.Web3.Types (forkWeb3, forkWeb3', runWeb3, Address, Babbage, BigNumber, Block(..), BlockNumber(..), ChainCursor(..), Change(..), Ether, EventAction(..), Filter, FilterId, Finney, HexString, KEther, Lovelace, CallError(..), Shannon, Szabo, Transaction(..), TransactionOptions(..), TransactionReceipt(..), TransactionStatus(..), Value, Web3, Web3Par, Web3Error(..), Wei, _address, _data, _from, _fromBlock, _gas, _gasPrice, _nonce, _to, _toBlock, _topics, _value, convert, defaultFilter, defaultTransactionOptions, fromInt, formatValue, fromMinorUnit, mkAddress, mkHexString, mkValue, toMinorUnit, throwWeb3, unAddress, unHex) import Network.Ethereum.Web3.Types.Provider (Provider, httpProvider, metamaskProvider) diff --git a/src/Network/Ethereum/Web3/Contract/Events.purs b/src/Network/Ethereum/Web3/Contract/Events.purs index 0a01b9d..e32ed34 100644 --- a/src/Network/Ethereum/Web3/Contract/Events.purs +++ b/src/Network/Ethereum/Web3/Contract/Events.purs @@ -41,7 +41,7 @@ import Effect.Aff (delay, Milliseconds(..)) import Effect.Aff.Class (liftAff) import Heterogeneous.Folding (class FoldingWithIndex, class FoldlRecord, hfoldlWithIndex) import Heterogeneous.Mapping (class MapRecordWithIndex, class Mapping, ConstMapping, hmap) -import Network.Ethereum.Core.BigNumber (BigNumber, embed) +import Network.Ethereum.Core.BigNumber (BigNumber, fromInt) import Network.Ethereum.Core.HexString (HexString) import Network.Ethereum.Web3.Api (eth_blockNumber, eth_getFilterChanges, eth_getLogs, eth_newFilter, eth_uninstallFilter) import Network.Ethereum.Web3.Solidity.Event (class DecodeEvent, decodeEvent) @@ -205,12 +205,12 @@ filterProducer cs@(MultiFilterStreamState currentState) = do -- otherwise try make progress else case hfoldlWithIndex MultiFilterMinToBlock Latest currentState.filters of -- consume as many as possible up to the chain head - Latest -> continueTo $ over BlockNumber (_ - embed currentState.trailBy) chainHead + Latest -> continueTo $ over BlockNumber (_ - fromInt currentState.trailBy) chainHead -- if the original fitler ends at a specific block, consume as many as possible up to that block -- or terminate if we're already past it BN targetEnd -> let - targetEnd' = min targetEnd $ over BlockNumber (_ - embed currentState.trailBy) chainHead + targetEnd' = min targetEnd $ over BlockNumber (_ - fromInt currentState.trailBy) chainHead in if currentState.currentBlock <= targetEnd' then continueTo targetEnd' @@ -218,7 +218,7 @@ filterProducer cs@(MultiFilterStreamState currentState) = do pure cs where newTo :: BlockNumber -> BlockNumber -> Int -> BlockNumber - newTo upper current window = min upper $ over BlockNumber (_ + embed window) current + newTo upper current window = min upper $ over BlockNumber (_ + fromInt window) current succ :: BlockNumber -> BlockNumber succ = over BlockNumber (_ + one) diff --git a/src/Network/Ethereum/Web3/Solidity.purs b/src/Network/Ethereum/Web3/Solidity.purs index bcf058b..c4dc1fd 100644 --- a/src/Network/Ethereum/Web3/Solidity.purs +++ b/src/Network/Ethereum/Web3/Solidity.purs @@ -1,6 +1,5 @@ module Network.Ethereum.Web3.Solidity - ( module Network.Ethereum.Web3.Solidity.Size - , module Network.Ethereum.Web3.Solidity.Vector + ( module Network.Ethereum.Web3.Solidity.Vector , module Network.Ethereum.Web3.Solidity.Bytes , module Network.Ethereum.Web3.Solidity.Tuple , module Network.Ethereum.Web3.Solidity.Generic @@ -12,12 +11,6 @@ module Network.Ethereum.Web3.Solidity , module Data.ByteString ) where -import Network.Ethereum.Web3.Solidity.Size - ( class KnownSize - , sizeVal - , class IntSize - , class ByteSize - ) import Network.Ethereum.Web3.Solidity.Vector (Vector, unVector, nilVector, vCons, (:<), vectorLength, toVector) import Network.Ethereum.Web3.Solidity.Bytes (BytesN, unBytesN, proxyBytesN, update, fromByteString) import Network.Ethereum.Web3.Solidity.Tuple diff --git a/src/Network/Ethereum/Web3/Solidity/AbiEncoding.purs b/src/Network/Ethereum/Web3/Solidity/AbiEncoding.purs index 2e71a1e..5f5437b 100644 --- a/src/Network/Ethereum/Web3/Solidity/AbiEncoding.purs +++ b/src/Network/Ethereum/Web3/Solidity/AbiEncoding.purs @@ -1,29 +1,42 @@ -module Network.Ethereum.Web3.Solidity.AbiEncoding where +module Network.Ethereum.Web3.Solidity.AbiEncoding + ( bytesBuilder + , class ABIDecode + , fromDataParser + , class ABIEncode + , toDataBuilder + , fromBool + , fromData + , int256HexBuilder + , parseBytes + , toBool + , uInt256HexBuilder + , uInt256HexParser + ) where import Prelude + import Data.Array (cons, fold, foldMap, length) import Data.Array.Partial (init) import Data.ByteString (ByteString) -import Data.ByteString (toUTF8, fromUTF8, toString, fromString, length, Encoding(Hex)) as BS +import Data.ByteString (toUTF8, fromUTF8, length) as BS import Data.Either (Either) import Data.Functor.Tagged (Tagged, tagged, untagged) -import Data.Maybe (maybe, fromJust) -import Data.String (splitAt) -import Data.Tuple (Tuple(..)) +import Data.Maybe (fromJust, maybe) +import Data.Reflectable (class Reflectable, reflectType) import Data.Traversable (for, scanl) +import Data.Tuple (Tuple(..)) import Data.Unfoldable (replicateA) -import Network.Ethereum.Core.BigNumber (toTwosComplement, unsafeToInt) -import Network.Ethereum.Core.HexString (HexString, Signed(..), mkHexString, numberOfBytes, padLeft, padLeftSigned, padRight, toBigNumber, toBigNumberFromSignedHexString, toSignedHexString, unHex) -import Network.Ethereum.Types (Address, BigNumber, embed, mkAddress, unAddress) +import Network.Ethereum.Core.BigNumber (fromString, fromTwosComplement, toString, toTwosComplement, unsafeToInt) +import Network.Ethereum.Core.HexString (HexString, PadByte(..), fromByteString, mkHexString, numberOfBytes, padLeft, padRight, splitAtByteOffset, toByteString, unHex) +import Network.Ethereum.Types (Address, BigNumber, fromInt, mkAddress, unAddress) import Network.Ethereum.Web3.Solidity.Bytes (BytesN, unBytesN, update, proxyBytesN) import Network.Ethereum.Web3.Solidity.EncodingType (class EncodingType, isDynamic) import Network.Ethereum.Web3.Solidity.Int (IntN, unIntN, intNFromBigNumber) -import Network.Ethereum.Web3.Solidity.Size (class ByteSize, class IntSize, class KnownSize, sizeVal) import Network.Ethereum.Web3.Solidity.UInt (UIntN, unUIntN, uIntNFromBigNumber) import Network.Ethereum.Web3.Solidity.Vector (Vector, unVector) -import Partial.Unsafe (unsafePartial) import Parsing (ParseError, Parser, ParseState(..), Position(..), ParserT, fail, getParserT, stateParserT, runParser) import Parsing.Combinators (lookAhead) +import Partial.Unsafe (unsafeCrashWith, unsafePartial) import Type.Proxy (Proxy(..)) -- | Class representing values that have an encoding and decoding instance to/from a solidity type. @@ -50,13 +63,13 @@ instance abiDecodeBool :: ABIDecode Boolean where fromDataParser = toBool <$> uInt256HexParser instance abiEncodeInt :: ABIEncode Int where - toDataBuilder = int256HexBuilder <<< embed + toDataBuilder = int256HexBuilder <<< fromInt instance abiDecodeInt :: ABIDecode Int where fromDataParser = unsafeToInt <$> int256HexParser instance abiEncodeAddress :: ABIEncode Address where - toDataBuilder addr = padLeft <<< unAddress $ addr + toDataBuilder addr = padLeft Zero <<< unAddress $ addr instance abiDecodeAddress :: ABIDecode Address where fromDataParser = do @@ -65,12 +78,12 @@ instance abiDecodeAddress :: ABIDecode Address where maybe (fail "Address is 20 bytes, receieved more") pure maddr instance abiEncodeBytesD :: ABIEncode ByteString where - toDataBuilder bytes = uInt256HexBuilder (embed $ BS.length bytes) <> bytesBuilder bytes + toDataBuilder bytes = uInt256HexBuilder (fromInt $ BS.length bytes) <> bytesBuilder bytes instance abiDecodeBytesD :: ABIDecode ByteString where fromDataParser = do len <- fromDataParser - bytesDecode <<< unHex <$> parseBytes (unsafeToInt len) + toByteString <$> parseBytes (unsafeToInt len) instance abiEncodeString :: ABIEncode String where toDataBuilder = toDataBuilder <<< BS.toUTF8 @@ -78,26 +91,26 @@ instance abiEncodeString :: ABIEncode String where instance abiDecodeString :: ABIDecode String where fromDataParser = BS.fromUTF8 <$> fromDataParser -instance abiEncodeBytesN :: ByteSize n => ABIEncode (BytesN n) where +instance abiEncodeBytesN :: Reflectable n Int => ABIEncode (BytesN n) where toDataBuilder bs = bytesBuilder <<< unBytesN $ bs -instance abiDecodeBytesN :: ByteSize n => ABIDecode (BytesN n) where +instance abiDecodeBytesN :: Reflectable n Int => ABIDecode (BytesN n) where fromDataParser = do let - len = sizeVal (Proxy :: Proxy n) + len = reflectType (Proxy :: Proxy n) zeroBytes = 32 - len raw <- parseBytes len _ <- parseBytes zeroBytes - pure <<< update proxyBytesN <<< bytesDecode <<< unHex $ raw + pure <<< update proxyBytesN <<< toByteString $ raw -instance abiEncodeVec :: (EncodingType a, ABIEncode a, KnownSize n) => ABIEncode (Vector n a) where +instance abiEncodeVec :: (EncodingType a, ABIEncode a, Reflectable n Int) => ABIEncode (Vector n a) where toDataBuilder l = if isDynamic (Proxy :: Proxy a) then do let encs = map toDataBuilder (unVector l) lengths = map numberOfBytes encs - len = sizeVal (Proxy :: Proxy n) + len = reflectType (Proxy :: Proxy n) offsets = let seed = 32 * len @@ -107,10 +120,10 @@ instance abiEncodeVec :: (EncodingType a, ABIEncode a, KnownSize n) => ABIEncode else foldMap toDataBuilder $ (unVector l :: Array a) -instance abiDecodeVec :: (EncodingType a, KnownSize n, ABIDecode a) => ABIDecode (Vector n a) where +instance abiDecodeVec :: (EncodingType a, Reflectable n Int, ABIDecode a) => ABIDecode (Vector n a) where fromDataParser = do let - len = sizeVal (Proxy :: Proxy n) + len = reflectType (Proxy :: Proxy n) if isDynamic (Proxy :: Proxy a) then do offsets <- replicateA len uInt256HexParser let @@ -126,7 +139,7 @@ instance abiDecodeVec :: (EncodingType a, KnownSize n, ABIDecode a) => ABIDecode instance abiEncodeAray :: (EncodingType a, ABIEncode a) => ABIEncode (Array a) where toDataBuilder l = do - uInt256HexBuilder (embed $ length l) + uInt256HexBuilder (fromInt $ length l) <> if isDynamic (Proxy :: Proxy a) then do let @@ -139,7 +152,7 @@ instance abiEncodeAray :: (EncodingType a, ABIEncode a) => ABIEncode (Array a) w seed = 32 * length l in seed `cons` (unsafePartial $ init $ scanl (+) seed lengths) - foldMap (uInt256HexBuilder <<< embed) offsets <> fold encs + foldMap (uInt256HexBuilder <<< fromInt) offsets <> fold encs else foldMap toDataBuilder l @@ -159,31 +172,31 @@ instance abiDecodeArray :: (EncodingType a, ABIDecode a) => ABIDecode (Array a) else replicateA len fromDataParser -instance abiEncodeUint :: IntSize n => ABIEncode (UIntN n) where +instance abiEncodeUint :: Reflectable n Int => ABIEncode (UIntN n) where toDataBuilder a = uInt256HexBuilder <<< unUIntN $ a -instance abiDecodeUint :: IntSize n => ABIDecode (UIntN n) where +instance abiDecodeUint :: Reflectable n Int => ABIDecode (UIntN n) where fromDataParser = do a <- uInt256HexParser maybe (fail $ msg a) pure <<< uIntNFromBigNumber (Proxy :: Proxy n) $ a where msg n = let - size = sizeVal (Proxy :: Proxy n) + size = reflectType (Proxy :: Proxy n) in "Couldn't parse as uint" <> show size <> " : " <> show n -instance abiEncodeIntN :: IntSize n => ABIEncode (IntN n) where +instance abiEncodeIntN :: Reflectable n Int => ABIEncode (IntN n) where toDataBuilder a = int256HexBuilder <<< unIntN $ a -instance abiDecodeIntN :: IntSize n => ABIDecode (IntN n) where +instance abiDecodeIntN :: Reflectable n Int => ABIDecode (IntN n) where fromDataParser = do a <- int256HexParser maybe (fail $ msg a) pure <<< intNFromBigNumber (Proxy :: Proxy n) $ a where msg n = let - size = sizeVal (Proxy :: Proxy n) + size = reflectType (Proxy :: Proxy n) in "Couldn't parse as int" <> show size <> " : " <> show n @@ -196,37 +209,41 @@ instance abiDecodeTagged :: ABIDecode a => ABIDecode (Tagged s a) where -------------------------------------------------------------------------------- -- | Special Builders and Parsers -------------------------------------------------------------------------------- + -- | base16 encode, then utf8 encode, then pad bytesBuilder :: ByteString -> HexString -bytesBuilder = padRight <<< unsafePartial fromJust <<< mkHexString <<< flip BS.toString BS.Hex - --- | unsafe utfDecode -bytesDecode :: String -> ByteString -bytesDecode s = unsafePartial $ fromJust $ flip BS.fromString BS.Hex s +bytesBuilder = padRight Zero <<< fromByteString -- | Encode something that is essentaially a signed integer. int256HexBuilder :: BigNumber -> HexString int256HexBuilder x = - if x < zero then - int256HexBuilder <<< toTwosComplement $ x - else - padLeftSigned <<< toSignedHexString $ x + let + a = toTwosComplement 256 x + x' = + if a < zero then unsafeCrashWith $ "FUCK " <> show a + else unsafePartial $ fromJust $ mkHexString (toString a) + in + if x < zero then padLeft FF x' + else padLeft Zero x' -- | Encode something that is essentially an unsigned integer. uInt256HexBuilder :: BigNumber -> HexString -uInt256HexBuilder x = - let - Signed _ x' = toSignedHexString x - in - padLeft x' +uInt256HexBuilder x = unsafePartial $ fromJust $ + padLeft Zero <$> mkHexString (toString x) -- | Parse as a signed `BigNumber` int256HexParser :: forall m. Monad m => ParserT HexString m BigNumber -int256HexParser = toBigNumberFromSignedHexString <$> parseBytes 32 +int256HexParser = do + bs <- unHex <$> parseBytes 32 + a <- maybe (fail $ "Failed to parse bytes as BigNumber " <> bs) pure (fromString bs) + pure $ fromTwosComplement 256 a -- | Parse an unsigned `BigNumber` uInt256HexParser :: forall m. Monad m => ParserT HexString m BigNumber -uInt256HexParser = toBigNumber <$> parseBytes 32 +uInt256HexParser = do + bs <- unHex <$> parseBytes 32 + a <- maybe (fail $ "Failed to parse bytes as BigNumber " <> bs) pure (fromString bs) + pure a -- | Decode a `Boolean` as a BigNumber fromBool :: Boolean -> BigNumber @@ -238,26 +255,15 @@ toBool bn = not $ bn == zero -- | Read any number of HexDigits parseBytes :: forall m. Monad m => Int -> ParserT HexString m HexString -parseBytes n = fold <$> replicateA n parseByte - -parseByte :: forall m. Monad m => ParserT HexString m HexString -parseByte = do +parseBytes n = do ParseState input (Position position) _ <- getParserT - if numberOfBytes input < 1 then + if numberOfBytes input < n then fail "Unexpected EOF" else do let - { after, before } = splitAt 2 (unHex input) + { after, before } = splitAtByteOffset n input - unsafeMkHex s = unsafePartial $ fromJust $ mkHexString s - - position' = Position $ position { column = position.column + 1 } - - let - newState = ParseState (unsafeMkHex after) position' true - ret = unsafeMkHex before + position' = Position $ position { column = position.column + n } - -- equivalent to: do - -- put newState -- ParserT is no longer it's own MonadState and theres no putParserT - -- pure ret - stateParserT $ const (Tuple ret newState) + let newState = ParseState after position' true + stateParserT $ const (Tuple before newState) diff --git a/src/Network/Ethereum/Web3/Solidity/Bytes.purs b/src/Network/Ethereum/Web3/Solidity/Bytes.purs index fdac21e..1afae38 100644 --- a/src/Network/Ethereum/Web3/Solidity/Bytes.purs +++ b/src/Network/Ethereum/Web3/Solidity/Bytes.purs @@ -4,13 +4,17 @@ module Network.Ethereum.Web3.Solidity.Bytes , proxyBytesN , update , fromByteString + , generator ) where import Prelude + +import Control.Monad.Gen (class MonadGen) import Data.ByteString (empty, ByteString, Encoding(Hex)) import Data.ByteString as BS import Data.Maybe (Maybe(..), fromJust) -import Network.Ethereum.Web3.Solidity.Size (class KnownSize, sizeVal) +import Data.Reflectable (class Reflectable, reflectType) +import Network.Ethereum.Core.HexString as Hex import Network.Ethereum.Types (mkHexString) import Partial.Unsafe (unsafePartial) import Type.Proxy (Proxy(..)) @@ -23,24 +27,29 @@ import Type.Proxy (Proxy(..)) newtype BytesN (n :: Int) = BytesN ByteString derive newtype instance eqBytesN :: Eq (BytesN n) -instance showBytesN :: KnownSize n => Show (BytesN n) where +instance showBytesN :: Show (BytesN n) where show (BytesN bs) = show <<< unsafePartial fromJust <<< mkHexString $ BS.toString bs Hex +generator :: forall n m. Reflectable n Int => MonadGen m => Proxy n -> m (BytesN n) +generator p = do + bs <- Hex.generator (reflectType p) + pure $ BytesN $ Hex.toByteString bs + -- | Access the underlying raw bytestring -unBytesN :: forall n. KnownSize n => BytesN n -> ByteString +unBytesN :: forall n. BytesN n -> ByteString unBytesN (BytesN bs) = bs -proxyBytesN :: forall n. KnownSize n => BytesN n +proxyBytesN :: forall n. BytesN n proxyBytesN = BytesN empty -update :: forall n. KnownSize n => BytesN n -> ByteString -> BytesN n +update :: forall n. BytesN n -> ByteString -> BytesN n update _ = BytesN -- | Attempt to coerce a bytestring into one of the appropriate size. -- | See module [Network.Ethereum.Web3.Solidity.Sizes](/Network.Ethereum.Web3.Solidity.Sizes) for some predefined sizes. -fromByteString :: forall proxy n. KnownSize n => proxy n -> ByteString -> Maybe (BytesN n) +fromByteString :: forall proxy n. Reflectable n Int => proxy n -> ByteString -> Maybe (BytesN n) fromByteString _ bs = - if not $ BS.length bs <= sizeVal (Proxy :: Proxy n) then + if not $ BS.length bs <= reflectType (Proxy :: Proxy n) then Nothing else Just $ BytesN bs diff --git a/src/Network/Ethereum/Web3/Solidity/EncodingType.purs b/src/Network/Ethereum/Web3/Solidity/EncodingType.purs index 37adb08..331f0da 100644 --- a/src/Network/Ethereum/Web3/Solidity/EncodingType.purs +++ b/src/Network/Ethereum/Web3/Solidity/EncodingType.purs @@ -5,14 +5,15 @@ module Network.Ethereum.Web3.Solidity.EncodingType ) where import Prelude + import Data.ByteString (ByteString) import Data.Functor.Tagged (Tagged) +import Data.Reflectable (class Reflectable, reflectType) +import Network.Ethereum.Types (Address, BigNumber) import Network.Ethereum.Web3.Solidity.Bytes (BytesN) import Network.Ethereum.Web3.Solidity.Int (IntN) -import Network.Ethereum.Web3.Solidity.Size (class IntSize, class KnownSize, sizeVal) import Network.Ethereum.Web3.Solidity.UInt (UIntN) import Network.Ethereum.Web3.Solidity.Vector (Vector) -import Network.Ethereum.Types (Address, BigNumber) import Type.Proxy (Proxy(..)) class EncodingType :: forall k. k -> Constraint @@ -32,12 +33,12 @@ instance encodingTypeBigNumber :: EncodingType BigNumber where typeName = const "int" isDynamic = const false -instance encodingTypeUIntN :: IntSize n => EncodingType (UIntN n) where - typeName = const $ "uint" <> (show $ sizeVal (Proxy :: Proxy n)) +instance encodingTypeUIntN :: Reflectable n Int => EncodingType (UIntN n) where + typeName = const $ "uint" <> (show $ reflectType (Proxy :: Proxy n)) isDynamic = const false -instance encodingTypeIntN :: IntSize n => EncodingType (IntN n) where - typeName = const $ "int" <> (show $ sizeVal (Proxy :: Proxy n)) +instance encodingTypeIntN :: Reflectable n Int => EncodingType (IntN n) where + typeName = const $ "int" <> (show $ reflectType (Proxy :: Proxy n)) isDynamic = const false instance encodingTypeString :: EncodingType String where @@ -52,18 +53,18 @@ instance encodingTypeArray :: EncodingType a => EncodingType (Array a) where typeName = const "[]" isDynamic = const true -instance encodingTypeBytes :: KnownSize n => EncodingType (BytesN n) where +instance encodingTypeBytes :: Reflectable n Int => EncodingType (BytesN n) where typeName = let - n = show (sizeVal (Proxy :: Proxy n)) + n = show (reflectType (Proxy :: Proxy n)) in const $ "bytes[" <> n <> "]" isDynamic = const false -instance encodingTypeVector :: (KnownSize n, EncodingType a) => EncodingType (Vector n a) where +instance encodingTypeVector :: (Reflectable n Int, EncodingType a) => EncodingType (Vector n a) where typeName = let - n = show (sizeVal (Proxy :: Proxy n)) + n = show (reflectType (Proxy :: Proxy n)) baseTypeName = typeName (Proxy :: Proxy a) in diff --git a/src/Network/Ethereum/Web3/Solidity/Generic.purs b/src/Network/Ethereum/Web3/Solidity/Generic.purs index 1f38119..e128faf 100644 --- a/src/Network/Ethereum/Web3/Solidity/Generic.purs +++ b/src/Network/Ethereum/Web3/Solidity/Generic.purs @@ -6,7 +6,7 @@ import Data.Either (Either) import Data.Functor.Tagged (Tagged, untagged, tagged) import Data.Generic.Rep (class Generic, Argument(..), Constructor(..), NoArguments(..), Product(..), from, to) import Data.Symbol (class IsSymbol) -import Network.Ethereum.Core.BigNumber (embed, unsafeToInt) +import Network.Ethereum.Core.BigNumber (fromInt, unsafeToInt) import Network.Ethereum.Core.HexString (HexString, numberOfBytes) import Parsing (ParseError, ParseState(..), Parser, Position(..), getParserT, runParser) import Parsing.Combinators (lookAhead) @@ -47,7 +47,7 @@ combineEncodedValues = foldl ( \{ accumulator, lengthOfPreviousDynamicValues } encodedValue -> if encodedValue.isDynamic then - { accumulator: accumulator <> uInt256HexBuilder (embed $ headsOffsetInBytes + lengthOfPreviousDynamicValues) + { accumulator: accumulator <> uInt256HexBuilder (fromInt $ headsOffsetInBytes + lengthOfPreviousDynamicValues) , lengthOfPreviousDynamicValues: lengthOfPreviousDynamicValues + encodedValue.encodingLengthInBytes } else diff --git a/src/Network/Ethereum/Web3/Solidity/Int.purs b/src/Network/Ethereum/Web3/Solidity/Int.purs index bc9d6a5..ae565cc 100644 --- a/src/Network/Ethereum/Web3/Solidity/Int.purs +++ b/src/Network/Ethereum/Web3/Solidity/Int.purs @@ -2,12 +2,18 @@ module Network.Ethereum.Web3.Solidity.Int ( IntN , unIntN , intNFromBigNumber + , generator ) where import Prelude -import Data.Maybe (Maybe(..)) -import Network.Ethereum.Core.BigNumber (BigNumber, embed, pow) -import Network.Ethereum.Web3.Solidity.Size (class KnownSize, sizeVal) + +import Control.Monad.Gen (class MonadGen) +import Data.Maybe (Maybe(..), fromJust) +import Data.Reflectable (class Reflectable, reflectType) +import Network.Ethereum.Core.BigNumber (BigNumber, fromInt, fromString, fromTwosComplement, pow) +import Network.Ethereum.Core.HexString as Hex +import Partial.Unsafe (unsafePartial) +import Type.Proxy (Proxy(..)) -------------------------------------------------------------------------------- -- * Statically sized signed integers @@ -20,21 +26,30 @@ derive newtype instance showIntN :: Show (IntN n) derive newtype instance eqIntN :: Eq (IntN n) derive newtype instance ordIntN :: Ord (IntN n) +generator :: forall n m. Reflectable n Int => MonadGen m => Proxy n -> m (IntN n) +generator p = do + bs <- Hex.generator (reflectType p `div` 8) + let + a = + if bs == mempty then zero + else unsafePartial $ fromJust $ fromString $ Hex.unHex $ bs + pure $ IntN $ fromTwosComplement (reflectType (Proxy @n)) a + -- | Access the raw underlying integer unIntN :: forall n. IntN n -> BigNumber unIntN (IntN a) = a -- | Attempt to coerce an signed `BigNumber` into a statically sized one. -- | See module [Network.Ethereum.Web3.Solidity.Sizes](/Network.Ethereum.Web3.Solidity.Sizes) for some predefined sizes. -intNFromBigNumber :: forall n proxy. KnownSize n => proxy n -> BigNumber -> Maybe (IntN n) -intNFromBigNumber proxy a +intNFromBigNumber :: forall n proxy. Reflectable n Int => proxy n -> BigNumber -> Maybe (IntN n) +intNFromBigNumber _ a | a < zero = let - minVal = negate $ (embed 2) `pow` (sizeVal proxy - one) + minVal = negate $ (fromInt 2) `pow` (reflectType (Proxy @n) - one) in if a < minVal then Nothing else Just <<< IntN $ a | otherwise = let - maxVal = (embed 2) `pow` (sizeVal proxy - one) - one + maxVal = (fromInt 2) `pow` (reflectType (Proxy @n) - one) - one in if a > maxVal then Nothing else Just <<< IntN $ a diff --git a/src/Network/Ethereum/Web3/Solidity/Size.purs b/src/Network/Ethereum/Web3/Solidity/Size.purs deleted file mode 100644 index c414e8d..0000000 --- a/src/Network/Ethereum/Web3/Solidity/Size.purs +++ /dev/null @@ -1,89 +0,0 @@ -module Network.Ethereum.Web3.Solidity.Size - ( class KnownSize - , sizeVal - , class IntSize - , class ByteSize - ) where - -import Data.Reflectable (class Reflectable, reflectType) -import Type.Proxy (Proxy(..)) - -class Reflectable n Int <= KnownSize (n :: Int) where - sizeVal :: forall proxy. proxy n -> Int - -instance (Reflectable n Int) => KnownSize n where - sizeVal _ = reflectType (Proxy :: Proxy n) - --- | `IntSize` is empty class, if there is instance of `IntSize` for some number it means there --- | is solidity type `int` of that size specific number in like `int16`, `int24` ... `int256` -class KnownSize n <= IntSize (n :: Int) - -instance intSize8 :: IntSize 8 -instance intSize16 :: IntSize 16 -instance intSize24 :: IntSize 24 -instance intSize32 :: IntSize 32 -instance intSize40 :: IntSize 40 -instance intSize48 :: IntSize 48 -instance intSize56 :: IntSize 56 -instance intSize64 :: IntSize 64 -instance intSize72 :: IntSize 72 -instance intSize80 :: IntSize 80 -instance intSize88 :: IntSize 88 -instance intSize96 :: IntSize 96 -instance intSize104 :: IntSize 104 -instance intSize112 :: IntSize 112 -instance intSize120 :: IntSize 120 -instance intSize128 :: IntSize 128 -instance intSize136 :: IntSize 136 -instance intSize144 :: IntSize 144 -instance intSize152 :: IntSize 152 -instance intSize160 :: IntSize 160 -instance intSize168 :: IntSize 168 -instance intSize176 :: IntSize 176 -instance intSize184 :: IntSize 184 -instance intSize192 :: IntSize 192 -instance intSize200 :: IntSize 200 -instance intSize208 :: IntSize 208 -instance intSize216 :: IntSize 216 -instance intSize224 :: IntSize 224 -instance intSize232 :: IntSize 232 -instance intSize240 :: IntSize 240 -instance intSize248 :: IntSize 248 -instance intSize256 :: IntSize 256 - --- | `ByteSize` is empty class, if there is instance of `ByteSize` for some number it means there --- | is solidity type `bytes` of that size specific number in like `bytes1`, `bytes2` ... `bytes32` -class KnownSize n <= ByteSize (n :: Int) - -instance byteSize1 :: ByteSize 1 -instance byteSize2 :: ByteSize 2 -instance byteSize3 :: ByteSize 3 -instance byteSize4 :: ByteSize 4 -instance byteSize5 :: ByteSize 5 -instance byteSize6 :: ByteSize 6 -instance byteSize7 :: ByteSize 7 -instance byteSize8 :: ByteSize 8 -instance byteSize9 :: ByteSize 9 -instance byteSize10 :: ByteSize 10 -instance byteSize11 :: ByteSize 11 -instance byteSize12 :: ByteSize 12 -instance byteSize13 :: ByteSize 13 -instance byteSize14 :: ByteSize 14 -instance byteSize15 :: ByteSize 15 -instance byteSize16 :: ByteSize 16 -instance byteSize17 :: ByteSize 17 -instance byteSize18 :: ByteSize 18 -instance byteSize19 :: ByteSize 19 -instance byteSize20 :: ByteSize 20 -instance byteSize21 :: ByteSize 21 -instance byteSize22 :: ByteSize 22 -instance byteSize23 :: ByteSize 23 -instance byteSize24 :: ByteSize 24 -instance byteSize25 :: ByteSize 25 -instance byteSize26 :: ByteSize 26 -instance byteSize27 :: ByteSize 27 -instance byteSize28 :: ByteSize 28 -instance byteSize29 :: ByteSize 29 -instance byteSize30 :: ByteSize 30 -instance byteSize31 :: ByteSize 31 -instance byteSize32 :: ByteSize 32 diff --git a/src/Network/Ethereum/Web3/Solidity/Tuple.purs b/src/Network/Ethereum/Web3/Solidity/Tuple.purs index 04cd0db..ad1dc78 100644 --- a/src/Network/Ethereum/Web3/Solidity/Tuple.purs +++ b/src/Network/Ethereum/Web3/Solidity/Tuple.purs @@ -1,33 +1,85 @@ -module Network.Ethereum.Web3.Solidity.Tuple where +module Network.Ethereum.Web3.Solidity.Tuple + ( Tuple0(..) + , Tuple10(..) + , Tuple11(..) + , Tuple12(..) + , Tuple13(..) + , Tuple14(..) + , Tuple15(..) + , Tuple16(..) + , Tuple1(..) + , Tuple2(..) + , Tuple3(..) + , Tuple4(..) + , Tuple5(..) + , Tuple6(..) + , Tuple7(..) + , Tuple8(..) + , Tuple9(..) + , curry1 + , curry10 + , curry11 + , curry12 + , curry13 + , curry14 + , curry15 + , curry16 + , curry2 + , curry3 + , curry4 + , curry5 + , curry6 + , curry7 + , curry8 + , curry9 + , unTuple1 + , uncurry1 + , uncurry10 + , uncurry11 + , uncurry12 + , uncurry13 + , uncurry14 + , uncurry15 + , uncurry16 + , uncurry2 + , uncurry3 + , uncurry4 + , uncurry5 + , uncurry6 + , uncurry7 + , uncurry8 + , uncurry9 + ) where import Prelude -import Data.Generic.Rep (class Generic) + import Data.Eq.Generic (genericEq) +import Data.Generic.Rep (class Generic) import Data.Show.Generic (genericShow) -- * Tuple0 data Tuple0 = Tuple0 -derive instance genericTuple0 :: Generic Tuple0 _ +derive instance Generic Tuple0 _ -instance showTupleO :: Show Tuple0 where +instance Show Tuple0 where show _ = "Tuple0" -instance eqTuple0 :: Eq Tuple0 where +instance Eq Tuple0 where eq _ _ = true -- * Tuple 1 newtype Tuple1 a = Tuple1 a -derive instance genericTuple1 :: Generic (Tuple1 a) _ +derive instance Generic (Tuple1 a) _ unTuple1 :: forall a. Tuple1 a -> a unTuple1 (Tuple1 a) = a -instance showTuple1 :: Show a => Show (Tuple1 a) where +instance Show a => Show (Tuple1 a) where show = genericShow -instance eqTuple1 :: Eq a => Eq (Tuple1 a) where +instance Eq a => Eq (Tuple1 a) where eq = genericEq uncurry1 :: forall a b. (a -> b) -> Tuple1 a -> b @@ -39,12 +91,12 @@ curry1 fun a = fun (Tuple1 a) -- * Tuple2 data Tuple2 a b = Tuple2 a b -derive instance genericTuple2 :: Generic (Tuple2 a b) _ +derive instance Generic (Tuple2 a b) _ -instance showTuple2 :: (Show a, Show b) => Show (Tuple2 a b) where +instance (Show a, Show b) => Show (Tuple2 a b) where show = genericShow -instance eqTuple2 :: (Eq a, Eq b) => Eq (Tuple2 a b) where +instance (Eq a, Eq b) => Eq (Tuple2 a b) where eq = genericEq uncurry2 :: forall a b c. (a -> b -> c) -> Tuple2 a b -> c @@ -56,12 +108,12 @@ curry2 fun a b = fun (Tuple2 a b) -- * Tuple3 data Tuple3 a b c = Tuple3 a b c -derive instance genericTuple3 :: Generic (Tuple3 a b c) _ +derive instance Generic (Tuple3 a b c) _ -instance showTuple3 :: (Show a, Show b, Show c) => Show (Tuple3 a b c) where +instance (Show a, Show b, Show c) => Show (Tuple3 a b c) where show = genericShow -instance eqTuple3 :: (Eq a, Eq b, Eq c) => Eq (Tuple3 a b c) where +instance (Eq a, Eq b, Eq c) => Eq (Tuple3 a b c) where eq = genericEq uncurry3 :: forall a b c d. (a -> b -> c -> d) -> Tuple3 a b c -> d @@ -73,12 +125,12 @@ curry3 fun a b c = fun (Tuple3 a b c) -- * Tuple4 data Tuple4 a b c d = Tuple4 a b c d -derive instance genericTuple4 :: Generic (Tuple4 a b c d) _ +derive instance Generic (Tuple4 a b c d) _ -instance showTuple4 :: (Show a, Show b, Show c, Show d) => Show (Tuple4 a b c d) where +instance (Show a, Show b, Show c, Show d) => Show (Tuple4 a b c d) where show = genericShow -instance eqTuple4 :: (Eq a, Eq b, Eq c, Eq d) => Eq (Tuple4 a b c d) where +instance (Eq a, Eq b, Eq c, Eq d) => Eq (Tuple4 a b c d) where eq = genericEq uncurry4 :: forall a b c d e. (a -> b -> c -> d -> e) -> Tuple4 a b c d -> e @@ -90,12 +142,12 @@ curry4 fun a b c d = fun (Tuple4 a b c d) -- * Tuple5 data Tuple5 a b c d e = Tuple5 a b c d e -derive instance genericTuple5 :: Generic (Tuple5 a b c d e) _ +derive instance Generic (Tuple5 a b c d e) _ -instance showTuple5 :: (Show a, Show b, Show c, Show d, Show e) => Show (Tuple5 a b c d e) where +instance (Show a, Show b, Show c, Show d, Show e) => Show (Tuple5 a b c d e) where show = genericShow -instance eqTuple5 :: (Eq a, Eq b, Eq c, Eq d, Eq e) => Eq (Tuple5 a b c d e) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e) => Eq (Tuple5 a b c d e) where eq = genericEq uncurry5 :: forall a b c d e f. (a -> b -> c -> d -> e -> f) -> Tuple5 a b c d e -> f @@ -107,12 +159,12 @@ curry5 fun a b c d e = fun (Tuple5 a b c d e) -- * Tuple6 data Tuple6 a b c d e f = Tuple6 a b c d e f -derive instance genericTuple6 :: Generic (Tuple6 a b c d e f) _ +derive instance Generic (Tuple6 a b c d e f) _ -instance showTuple6 :: (Show a, Show b, Show c, Show d, Show e, Show f) => Show (Tuple6 a b c d e f) where +instance (Show a, Show b, Show c, Show d, Show e, Show f) => Show (Tuple6 a b c d e f) where show = genericShow -instance eqTuple6 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f) => Eq (Tuple6 a b c d e f) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f) => Eq (Tuple6 a b c d e f) where eq = genericEq uncurry6 :: forall a b c d e f g. (a -> b -> c -> d -> e -> f -> g) -> Tuple6 a b c d e f -> g @@ -124,12 +176,12 @@ curry6 fun a b c d e f = fun (Tuple6 a b c d e f) -- * Tuple7 data Tuple7 a b c d e f g = Tuple7 a b c d e f g -derive instance genericTuple7 :: Generic (Tuple7 a b c d e f g) _ +derive instance Generic (Tuple7 a b c d e f g) _ -instance showTuple7 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g) => Show (Tuple7 a b c d e f g) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g) => Show (Tuple7 a b c d e f g) where show = genericShow -instance eqTuple7 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g) => Eq (Tuple7 a b c d e f g) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g) => Eq (Tuple7 a b c d e f g) where eq = genericEq uncurry7 :: forall a b c d e f g h. (a -> b -> c -> d -> e -> f -> g -> h) -> Tuple7 a b c d e f g -> h @@ -141,14 +193,11 @@ curry7 fun a b c d e f g = fun (Tuple7 a b c d e f g) -- * Tuple8 data Tuple8 a b c d e f g h = Tuple8 a b c d e f g h -derive instance genericTuple8 :: Generic (Tuple8 a b c d e f g h) _ +derive instance Generic (Tuple8 a b c d e f g h) _ -instance showTuple8 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h) => Show (Tuple8 a b c d e f g h) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h) => Show (Tuple8 a b c d e f g h) where show = genericShow -instance eqTuple8 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h) => Eq (Tuple8 a b c d e f g h) where - eq = genericEq - uncurry8 :: forall a b c d e f g h i. (a -> b -> c -> d -> e -> f -> g -> h -> i) -> Tuple8 a b c d e f g h -> i uncurry8 fun (Tuple8 a b c d e f g h) = fun a b c d e f g h @@ -158,12 +207,12 @@ curry8 fun a b c d e f g h = fun (Tuple8 a b c d e f g h) -- * Tuple9 data Tuple9 a b c d e f g h i = Tuple9 a b c d e f g h i -derive instance genericTuple9 :: Generic (Tuple9 a b c d e f g h i) _ +derive instance Generic (Tuple9 a b c d e f g h i) _ -instance showTuple9 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i) => Show (Tuple9 a b c d e f g h i) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i) => Show (Tuple9 a b c d e f g h i) where show = genericShow -instance eqTuple9 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i) => Eq (Tuple9 a b c d e f g h i) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i) => Eq (Tuple9 a b c d e f g h i) where eq = genericEq uncurry9 :: forall a b c d e f g h i j. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j) -> Tuple9 a b c d e f g h i -> j @@ -175,12 +224,12 @@ curry9 fun a b c d e f g h i = fun (Tuple9 a b c d e f g h i) -- * Tuple10 data Tuple10 a b c d e f g h i j = Tuple10 a b c d e f g h i j -derive instance genericTuple10 :: Generic (Tuple10 a b c d e f g h i j) _ +derive instance Generic (Tuple10 a b c d e f g h i j) _ -instance showTuple10 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j) => Show (Tuple10 a b c d e f g h i j) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j) => Show (Tuple10 a b c d e f g h i j) where show = genericShow -instance eqTuple10 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j) => Eq (Tuple10 a b c d e f g h i j) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j) => Eq (Tuple10 a b c d e f g h i j) where eq = genericEq uncurry10 :: forall a b c d e f g h i j k. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k) -> Tuple10 a b c d e f g h i j -> k @@ -192,14 +241,11 @@ curry10 fun a b c d e f g h i j = fun (Tuple10 a b c d e f g h i j) -- * Tuple11 data Tuple11 a b c d e f g h i j k = Tuple11 a b c d e f g h i j k -derive instance genericTuple11 :: Generic (Tuple11 a b c d e f g h i j k) _ +derive instance Generic (Tuple11 a b c d e f g h i j k) _ -instance showTuple11 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k) => Show (Tuple11 a b c d e f g h i j k) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k) => Show (Tuple11 a b c d e f g h i j k) where show = genericShow -instance eqTuple11 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k) => Eq (Tuple11 a b c d e f g h i j k) where - eq = genericEq - uncurry11 :: forall a b c d e f g h i j k l. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l) -> Tuple11 a b c d e f g h i j k -> l uncurry11 fun (Tuple11 a b c d e f g h i j k) = fun a b c d e f g h i j k @@ -209,12 +255,12 @@ curry11 fun a b c d e f g h i j k = fun (Tuple11 a b c d e f g h i j k) -- * Tuple12 data Tuple12 a b c d e f g h i j k l = Tuple12 a b c d e f g h i j k l -derive instance genericTuple12 :: Generic (Tuple12 a b c d e f g h i j k l) _ +derive instance Generic (Tuple12 a b c d e f g h i j k l) _ -instance showTuple12 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l) => Show (Tuple12 a b c d e f g h i j k l) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l) => Show (Tuple12 a b c d e f g h i j k l) where show = genericShow -instance eqTuple12 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l) => Eq (Tuple12 a b c d e f g h i j k l) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l) => Eq (Tuple12 a b c d e f g h i j k l) where eq = genericEq uncurry12 :: forall a b c d e f g h i j k l m. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l -> m) -> Tuple12 a b c d e f g h i j k l -> m @@ -228,10 +274,10 @@ data Tuple13 a b c d e f g h i j k l m = Tuple13 a b c d e f g h i j k l m derive instance genericTuple13 :: Generic (Tuple13 a b c d e f g h i j k l m) _ -instance showTuple13 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l, Show m) => Show (Tuple13 a b c d e f g h i j k l m) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l, Show m) => Show (Tuple13 a b c d e f g h i j k l m) where show = genericShow -instance eqTuple13 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l, Eq m) => Eq (Tuple13 a b c d e f g h i j k l m) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l, Eq m) => Eq (Tuple13 a b c d e f g h i j k l m) where eq = genericEq uncurry13 :: forall a b c d e f g h i j k l m n. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l -> m -> n) -> Tuple13 a b c d e f g h i j k l m -> n @@ -243,12 +289,12 @@ curry13 fun a b c d e f g h i j k l m = fun (Tuple13 a b c d e f g h i j k l m) -- * Tuple14 data Tuple14 a b c d e f g h i j k l m n = Tuple14 a b c d e f g h i j k l m n -derive instance genericTuple14 :: Generic (Tuple14 a b c d e f g h i j k l m n) _ +derive instance Generic (Tuple14 a b c d e f g h i j k l m n) _ -instance showTuple14 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l, Show m, Show n) => Show (Tuple14 a b c d e f g h i j k l m n) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l, Show m, Show n) => Show (Tuple14 a b c d e f g h i j k l m n) where show = genericShow -instance eqTuple14 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l, Eq m, Eq n) => Eq (Tuple14 a b c d e f g h i j k l m n) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l, Eq m, Eq n) => Eq (Tuple14 a b c d e f g h i j k l m n) where eq = genericEq uncurry14 :: forall a b c d e f g h i j k l m n o. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l -> m -> n -> o) -> Tuple14 a b c d e f g h i j k l m n -> o @@ -262,10 +308,10 @@ data Tuple15 a b c d e f g h i j k l m n o = Tuple15 a b c d e f g h i j k l m n derive instance genericTuple15 :: Generic (Tuple15 a b c d e f g h i j k l m n o) _ -instance showTuple15 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l, Show m, Show n, Show o) => Show (Tuple15 a b c d e f g h i j k l m n o) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l, Show m, Show n, Show o) => Show (Tuple15 a b c d e f g h i j k l m n o) where show = genericShow -instance eqTuple15 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l, Eq m, Eq n, Eq o) => Eq (Tuple15 a b c d e f g h i j k l m n o) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l, Eq m, Eq n, Eq o) => Eq (Tuple15 a b c d e f g h i j k l m n o) where eq = genericEq uncurry15 :: forall a b c d e f g h i j k l m n o p. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l -> m -> n -> o -> p) -> Tuple15 a b c d e f g h i j k l m n o -> p @@ -277,12 +323,12 @@ curry15 fun a b c d e f g h i j k l m n o = fun (Tuple15 a b c d e f g h i j k l -- * Tuple16 data Tuple16 a b c d e f g h i j k l m n o p = Tuple16 a b c d e f g h i j k l m n o p -derive instance genericTuple16 :: Generic (Tuple16 a b c d e f g h i j k l m n o p) _ +derive instance Generic (Tuple16 a b c d e f g h i j k l m n o p) _ -instance showTuple16 :: (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l, Show m, Show n, Show o, Show p) => Show (Tuple16 a b c d e f g h i j k l m n o p) where +instance (Show a, Show b, Show c, Show d, Show e, Show f, Show g, Show h, Show i, Show j, Show k, Show l, Show m, Show n, Show o, Show p) => Show (Tuple16 a b c d e f g h i j k l m n o p) where show = genericShow -instance eqTuple16 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l, Eq m, Eq n, Eq o, Eq p) => Eq (Tuple16 a b c d e f g h i j k l m n o p) where +instance (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l, Eq m, Eq n, Eq o, Eq p) => Eq (Tuple16 a b c d e f g h i j k l m n o p) where eq = genericEq uncurry16 :: forall a b c d e f g h i j k l m n o p q. (a -> b -> c -> d -> e -> f -> g -> h -> i -> j -> k -> l -> m -> n -> o -> p -> q) -> Tuple16 a b c d e f g h i j k l m n o p -> q diff --git a/src/Network/Ethereum/Web3/Solidity/UInt.purs b/src/Network/Ethereum/Web3/Solidity/UInt.purs index 04cf3da..e38d028 100644 --- a/src/Network/Ethereum/Web3/Solidity/UInt.purs +++ b/src/Network/Ethereum/Web3/Solidity/UInt.purs @@ -2,12 +2,17 @@ module Network.Ethereum.Web3.Solidity.UInt ( UIntN , unUIntN , uIntNFromBigNumber + , generator ) where import Prelude -import Data.Maybe (Maybe(..)) -import Network.Ethereum.Core.BigNumber (BigNumber, embed, pow) -import Network.Ethereum.Web3.Solidity.Size (class KnownSize, sizeVal) + +import Control.Monad.Gen (class MonadGen, chooseInt) +import Data.Maybe (Maybe(..), fromJust) +import Data.Reflectable (class Reflectable, reflectType) +import Network.Ethereum.Core.BigNumber (BigNumber, fromInt, fromString, pow) +import Network.Ethereum.Core.HexString as Hex +import Partial.Unsafe (unsafePartial) import Type.Proxy (Proxy(..)) -------------------------------------------------------------------------------- @@ -21,17 +26,32 @@ derive newtype instance showUIntN :: Show (UIntN n) derive newtype instance eqUIntN :: Eq (UIntN n) derive newtype instance ordUIntN :: Ord (UIntN n) +generator + :: forall n m + . Reflectable n Int + => MonadGen m + => Proxy n + -> m (UIntN n) +generator p = do + nBytes <- (flip div 8) <$> chooseInt 1 (reflectType p) + bs <- Hex.generator nBytes + let + a = + if bs == mempty then zero + else unsafePartial $ fromJust $ fromString $ Hex.unHex bs + pure $ UIntN $ if a < zero then -a else a + -- | Access the raw underlying unsigned integer -unUIntN :: forall n. KnownSize n => UIntN n -> BigNumber +unUIntN :: forall n. UIntN n -> BigNumber unUIntN (UIntN a) = a -- | Attempt to coerce an unsigned integer into a statically sized one. -- | See module [Network.Ethereum.Web3.Solidity.Sizes](/Network.Ethereum.Web3.Solidity.Sizes) for some predefined sizes. -uIntNFromBigNumber :: forall n. KnownSize n => Proxy n -> BigNumber -> Maybe (UIntN n) +uIntNFromBigNumber :: forall n. Reflectable n Int => Proxy n -> BigNumber -> Maybe (UIntN n) uIntNFromBigNumber _ a | a < zero = Nothing | otherwise = let - maxVal = (embed 2) `pow` (sizeVal (Proxy :: Proxy n)) - one + maxVal = (fromInt 2) `pow` (reflectType (Proxy :: Proxy n)) - one in if a > maxVal then Nothing else Just <<< UIntN $ a diff --git a/src/Network/Ethereum/Web3/Solidity/Vector.purs b/src/Network/Ethereum/Web3/Solidity/Vector.purs index a5658d9..f29c8e1 100644 --- a/src/Network/Ethereum/Web3/Solidity/Vector.purs +++ b/src/Network/Ethereum/Web3/Solidity/Vector.purs @@ -6,17 +6,21 @@ module Network.Ethereum.Web3.Solidity.Vector , (:<) , vectorLength , toVector + , generator ) where import Prelude + +import Control.Monad.Gen (class MonadGen) import Data.Array ((:)) import Data.Array as A import Data.Foldable (class Foldable) import Data.Maybe (Maybe(..)) +import Data.Reflectable (class Reflectable, reflectType) import Data.Traversable (class Traversable) -import Data.Unfoldable (class Unfoldable, class Unfoldable1) -import Network.Ethereum.Web3.Solidity.Size (class KnownSize, sizeVal) +import Data.Unfoldable (class Unfoldable, class Unfoldable1, replicateA) import Prim.Int (class Add) +import Type.Proxy (Proxy(..)) -- | Represents a statically sized vector of length `n`. -- | See module [Network.Ethereum.Web3.Solidity.Sizes](/Network.Ethereum.Web3.Solidity.Sizes) for some predefined sizes. @@ -30,6 +34,15 @@ derive newtype instance unfoldableVector :: Unfoldable (Vector n) derive newtype instance foldableVector :: Foldable (Vector n) derive newtype instance traversableVector :: Traversable (Vector n) +generator + :: forall n m proxy a + . Reflectable n Int + => MonadGen m + => proxy n + -> m a + -> m (Vector n a) +generator _ gen = Vector <$> replicateA (reflectType (Proxy @n)) gen + -- | Access the underlying array unVector :: forall a n. Vector n a -> Array a unVector (Vector as) = as @@ -50,9 +63,9 @@ vectorLength (Vector as) = A.length as -- | Attempt to coerce an array into a statically sized array. -- | See module [Network.Ethereum.Web3.Solidity.Sizes](/Network.Ethereum.Web3.Solidity.Sizes) for some predefined sizes. -toVector :: forall a (n :: Int) proxy. KnownSize n => proxy n -> Array a -> Maybe (Vector n a) -toVector proxy as = - if sizeVal proxy /= A.length as then +toVector :: forall a (n :: Int) proxy. Reflectable n Int => proxy n -> Array a -> Maybe (Vector n a) +toVector _ as = + if reflectType (Proxy @n) /= A.length as then Nothing else Just (Vector as) diff --git a/src/Network/Ethereum/Web3/Types.purs b/src/Network/Ethereum/Web3/Types.purs index ab8051a..78469b0 100644 --- a/src/Network/Ethereum/Web3/Types.purs +++ b/src/Network/Ethereum/Web3/Types.purs @@ -5,7 +5,7 @@ module Network.Ethereum.Web3.Types , module Network.Ethereum.Types ) where -import Network.Ethereum.Types (Address, BigNumber, HexString, embed, mkAddress, mkHexString, unAddress, unHex) +import Network.Ethereum.Types (Address, BigNumber, HexString, fromInt, mkAddress, mkHexString, unAddress, unHex) import Network.Ethereum.Web3.Types.EtherUnit (Wei, Babbage, Ether, Finney, KEther, Lovelace, Shannon, Szabo, ETHER) import Network.Ethereum.Web3.Types.TokenUnit (class TokenUnit, Value, convert, formatValue, fromMinorUnit, mkValue, toMinorUnit, NoPay) import Network.Ethereum.Web3.Types.Types (forkWeb3, forkWeb3', runWeb3, Block(..), BlockNumber(..), CallError(..), ChainCursor(..), Change(..), EventAction(..), FalseOrObject(..), Filter, FilterId, MethodName, Request, Response(..), RpcError(..), SyncStatus(..), Transaction(..), TransactionOptions(..), TransactionReceipt(..), TransactionStatus(..), Web3, Web3Par, Web3Error(..), _address, _data, _from, _fromBlock, _gas, _gasPrice, _nonce, _to, _toBlock, _topics, _value, defaultFilter, defaultTransactionOptions, mkRequest, throwWeb3) diff --git a/src/Network/Ethereum/Web3/Types/TokenUnit.purs b/src/Network/Ethereum/Web3/Types/TokenUnit.purs index c9f38ab..30989e5 100644 --- a/src/Network/Ethereum/Web3/Types/TokenUnit.purs +++ b/src/Network/Ethereum/Web3/Types/TokenUnit.purs @@ -10,6 +10,7 @@ module Network.Ethereum.Web3.Types.TokenUnit , convert , formatValue , mkValue + , generator , NoPay , MinorUnit , MinorUnitE3 @@ -22,11 +23,14 @@ module Network.Ethereum.Web3.Types.TokenUnit ) where import Prelude + +import Control.Monad.Gen (class MonadGen) import Data.Maybe (fromJust) -import Data.Ring.Module (class LeftModule, (^*)) +import Data.Ring.Module (class LeftModule) import Data.String (joinWith) import Data.Unfoldable (replicate) -import Network.Ethereum.Core.BigNumber (BigNumber, decimal, parseBigNumber) +import Network.Ethereum.Core.BigNumber (BigNumber, decimal, fromInt, fromStringAs) +import Network.Ethereum.Core.BigNumber as BigNumber import Partial.Unsafe (unsafePartial) import Simple.JSON (class ReadForeign, class WriteForeign, writeImpl) import Type.Proxy (Proxy(..)) @@ -38,28 +42,32 @@ data TokenUnitK -- | A value of some token in specific denomination newtype Value (a :: TokenUnitK) = Value BigNumber -derive newtype instance eqValue :: Eq (Value a) -derive newtype instance showValue :: Show (Value a) -derive newtype instance readFValue :: ReadForeign (Value a) +derive newtype instance Eq (Value a) +derive newtype instance Ord (Value a) +derive newtype instance Show (Value a) +derive newtype instance ReadForeign (Value a) + +generator :: forall m a proxy. MonadGen m => proxy a -> m (Value a) +generator _ = Value <$> BigNumber.generator -instance writeFNoPay :: WriteForeign (Value (NoPay t)) where +instance WriteForeign (Value (NoPay t)) where writeImpl _ = writeImpl (zero :: BigNumber) -else instance writeFValue :: WriteForeign (Value a) where +else instance WriteForeign (Value a) where writeImpl (Value x) = writeImpl x -instance semigroupTokenUnitSpec :: TokenUnitSpec a => Semigroup (Value a) where +instance TokenUnitSpec a => Semigroup (Value a) where append a b = Value (unValue a `add` unValue b) -instance monoidTokenUnitSpec :: TokenUnitSpec a => Monoid (Value a) where +instance TokenUnitSpec a => Monoid (Value a) where mempty = mkValue zero -instance modukeTokenUnitSpec :: TokenUnitSpec a => LeftModule (Value a) Int where +instance TokenUnitSpec a => LeftModule (Value a) Int where mzeroL = mkValue zero maddL (Value a) (Value b) = Value $ a + b msubL (Value a) (Value b) = Value $ a - b - mmulL a (Value b) = Value $ a ^* b + mmulL a (Value b) = Value $ fromInt a * b -instance unitTokenUnitSpec :: TokenUnitSpec a => TokenUnit (Value a) where +instance TokenUnitSpec a => TokenUnit (Value a) where fromMinorUnit = Value toMinorUnit = unValue @@ -87,51 +95,51 @@ mkValue = Value <<< (mul (divider (Proxy :: Proxy a))) foreign import data NoPay :: TokenK -> TokenUnitK -instance unitSpecNoPay :: TokenUnitSpec (NoPay t) where +instance TokenUnitSpec (NoPay t) where divider = const zero foreign import data MinorUnit :: TokenK -> TokenUnitK -instance unitSpecMinorUnit :: TokenUnitSpec (MinorUnit t) where +instance TokenUnitSpec (MinorUnit t) where divider = createDivider 0 foreign import data MinorUnitE3 :: TokenK -> TokenUnitK -instance unitSpecMinorUnitE3 :: TokenUnitSpec (MinorUnitE3 t) where +instance TokenUnitSpec (MinorUnitE3 t) where divider = createDivider 3 foreign import data MinorUnitE6 :: TokenK -> TokenUnitK -instance unitSpecMinorUnitE6 :: TokenUnitSpec (MinorUnitE6 t) where +instance TokenUnitSpec (MinorUnitE6 t) where divider = createDivider 6 foreign import data MinorUnitE9 :: TokenK -> TokenUnitK -instance unitSpecMinorUnitE9 :: TokenUnitSpec (MinorUnitE9 t) where +instance TokenUnitSpec (MinorUnitE9 t) where divider = createDivider 9 foreign import data MinorUnitE12 :: TokenK -> TokenUnitK -instance unitSpecMinorUnitE12 :: TokenUnitSpec (MinorUnitE12 t) where +instance TokenUnitSpec (MinorUnitE12 t) where divider = createDivider 12 foreign import data MinorUnitE15 :: TokenK -> TokenUnitK -instance unitSpecMinorUnitE15 :: TokenUnitSpec (MinorUnitE15 t) where +instance TokenUnitSpec (MinorUnitE15 t) where divider = createDivider 15 foreign import data MinorUnitE18 :: TokenK -> TokenUnitK -instance unitSpecMinorUnitE18 :: TokenUnitSpec (MinorUnitE18 t) where +instance TokenUnitSpec (MinorUnitE18 t) where divider = createDivider 18 foreign import data MinorUnitE21 :: TokenK -> TokenUnitK -instance unitSpecMinorUnitE21 :: TokenUnitSpec (MinorUnitE21 t) where +instance TokenUnitSpec (MinorUnitE21 t) where divider = createDivider 21 createDivider :: forall a. Int -> a -> BigNumber createDivider denomination _ = unsafeConvert $ "1" <> joinWith "" (replicate denomination "0") where unsafeConvert :: String -> BigNumber - unsafeConvert a = unsafePartial fromJust <<< parseBigNumber decimal $ a + unsafeConvert a = unsafePartial fromJust <<< fromStringAs decimal $ a diff --git a/test.dhall b/test.dhall index 848b39a..2138927 100644 --- a/test.dhall +++ b/test.dhall @@ -4,13 +4,17 @@ in conf ⫽ { sources = conf.sources # [ "test/web3/**/*.purs" ] , dependencies = conf.dependencies - # [ "spec" - , "node-buffer" + # [ "console" + , "enums" + , "identity" + , "integers" , "lists" + , "node-buffer" + , "nonempty" + , "quickcheck" + , "quickcheck-laws" , "quotient" + , "spec" , "unsafe-coerce" - , "avar" - , "console" - , "identity" ] } diff --git a/test/web3/Main.purs b/test/web3/Main.purs index db4fd3d..c18b3d6 100644 --- a/test/web3/Main.purs +++ b/test/web3/Main.purs @@ -1,6 +1,7 @@ module Test.Main where import Prelude + import Data.Identity (Identity(..)) import Data.Maybe (Maybe(..)) import Data.Newtype (un) @@ -19,22 +20,24 @@ import Web3Spec.Live.RPCSpec as RPCSpec import Web3Spec.Types.EtherUnitSpec as EtherUnitSpec import Web3Spec.Types.VectorSpec as VectorSpec +-- import Web3Spec.Types.EtherUnitSpec as EtherUnitSpec + main :: Effect Unit main = - launchAff_ do - let - cfg = defaultConfig { timeout = Just (Milliseconds $ 120.0 * 1000.0) } - p <- liftEffect $ httpProvider "http://localhost:8545" - void $ join - $ runSpecT cfg [ consoleReporter ] do - hoist do - EncodingDataSpec.spec - VectorSpec.spec - EncodingContainersSpec.spec - EncodingSimpleSpec.spec - EncodingGenericSpec.spec - EtherUnitSpec.spec - RPCSpec.spec p + launchAff_ + do + let + cfg = defaultConfig { timeout = Just (Milliseconds $ 120.0 * 1000.0) } + p <- liftEffect $ httpProvider "http://localhost:8545" + void $ join $ runSpecT cfg [ consoleReporter ] do + hoist do + EncodingDataSpec.spec + EncodingContainersSpec.spec + EncodingSimpleSpec.spec + EncodingGenericSpec.spec + EtherUnitSpec.spec + VectorSpec.spec + RPCSpec.spec p where hoist :: Spec ~> SpecT Aff Unit Aff hoist = mapSpecTree (pure <<< un Identity) identity diff --git a/test/web3/Web3Spec/Encoding/ContainersSpec.purs b/test/web3/Web3Spec/Encoding/ContainersSpec.purs index 60c9b6b..212f73d 100644 --- a/test/web3/Web3Spec/Encoding/ContainersSpec.purs +++ b/test/web3/Web3Spec/Encoding/ContainersSpec.purs @@ -1,34 +1,363 @@ -module Web3Spec.Encoding.ContainersSpec (spec) where +module Web3Spec.Encoding.ContainersSpec (spec, BMPString(..)) where import Prelude -import Effect.Aff (Aff) -import Data.ByteString as BS + +import Control.Monad.Gen (chooseInt, frequency, oneOf, suchThat) +import Data.Array (filter, foldMap, (..)) +import Data.Array.NonEmpty (NonEmptyArray, fromArray) +import Data.Array.NonEmpty as NEA import Data.Either (Either(..)) +import Data.Enum (toEnumWithDefaults) +import Data.Foldable (for_) import Data.Generic.Rep (class Generic) +import Data.Int (toNumber) import Data.Maybe (fromJust) -import Network.Ethereum.Web3.Solidity (BytesN, IntN, Tuple1(..), Tuple2(..), Tuple4(..), Tuple9(..), UIntN, fromByteString, intNFromBigNumber, nilVector, uIntNFromBigNumber, (:<)) +import Data.NonEmpty (NonEmpty(..)) +import Data.Reflectable (reifyType) +import Data.String (CodePoint, fromCodePointArray) +import Data.Tuple (Tuple(..)) +import Effect.Class (liftEffect) +import Network.Ethereum.Core.HexString as Hex +import Network.Ethereum.Core.Signatures as Address +import Network.Ethereum.Web3.Solidity (class GenericABIDecode, class GenericABIEncode, Tuple4(..), Tuple5(..), genericABIEncode, genericFromData) import Network.Ethereum.Web3.Solidity.AbiEncoding (class ABIEncode, class ABIDecode, toDataBuilder, fromData) -import Network.Ethereum.Web3.Solidity.Generic (genericFromData, genericABIEncode, class GenericABIDecode, class GenericABIEncode) -import Network.Ethereum.Web3.Solidity.Sizes (s1, s16, s2, s224, s256, s4) -import Network.Ethereum.Web3.Solidity.Vector (Vector, toVector) -import Network.Ethereum.Web3.Types (Address, HexString, embed, mkAddress, mkHexString) +import Network.Ethereum.Web3.Solidity.Bytes as BytesN +import Network.Ethereum.Web3.Solidity.EncodingType (class EncodingType) +import Network.Ethereum.Web3.Solidity.Int as IntN +import Network.Ethereum.Web3.Solidity.UInt as UIntN +import Network.Ethereum.Web3.Solidity.Vector as Vector +import Parsing (ParseError) import Partial.Unsafe (unsafePartial) +import Test.QuickCheck (class Arbitrary, arbitrary, quickCheck, quickCheckGen, (===)) +import Test.QuickCheck.Gen (Gen, arrayOf) import Test.Spec (Spec, describe, it) -import Test.Spec.Assertions (shouldEqual) spec :: Spec Unit spec = describe "encoding-spec for containers" do - staticArraysTests - dynamicArraysTests - tuplesTest - -roundTrip :: forall a. Show a => Eq a => ABIEncode a => ABIDecode a => a -> HexString -> Aff Unit -roundTrip decoded encoded = do - encoded `shouldEqual` toDataBuilder decoded - fromData encoded `shouldEqual` Right decoded - -roundTripGeneric + typePropertyTests + arrayTypePropertyTests + vecTypePropertyTests + nestedTypePropertyTests + tupleTests + +typePropertyTests :: Spec Unit +typePropertyTests = + describe "Type property tests" do + it "can encode/decode a string" $ liftEffect $ do + quickCheck \(x :: BMPString) -> (encodeDecode x) === Right x + + it "can encode/decode bytestring" $ liftEffect $ do + quickCheckGen $ do + n <- chooseInt 1 100 + x <- Hex.toByteString <$> Hex.generator n + pure $ encodeDecode x === Right x + + it "can encode/decode bool" $ liftEffect $ do + quickCheck \(x :: Boolean) -> encodeDecode x === Right x + + it "can encode/decode address" $ liftEffect $ do + quickCheckGen $ do + x <- Address.generator + pure $ encodeDecode x === Right x + + it "can encode/decode intN" $ liftEffect $ do + for_ intSizes $ \n -> quickCheckGen $ do + reifyType n \p -> do + x <- IntN.generator p + pure $ encodeDecode x === Right x + + it "can encode/decode uintN" $ liftEffect $ do + for_ intSizes $ \n -> quickCheckGen $ do + reifyType n \p -> do + x <- UIntN.generator p + pure $ encodeDecode x === Right x + + it "can encode/decode bytesN" $ liftEffect $ do + for_ bytesSizes $ \n -> quickCheckGen $ do + reifyType n \p -> do + x <- BytesN.generator p + pure $ encodeDecode x === Right x + + it "can encode/decode string" $ liftEffect $ do + quickCheck \(x :: BMPString) -> encodeDecode x === Right x + +arrayTypePropertyTests :: Spec Unit +arrayTypePropertyTests = do + + describe "Array type property tests" do + + it "Can encode/decode intN[]" $ liftEffect do + for_ intSizes $ \n -> quickCheckGen $ do + reifyType n \p -> do + x <- arrayOf (IntN.generator p) + pure $ encodeDecode x === Right x + + it "Can encode/decode uintN[]" $ liftEffect do + for_ intSizes $ \n -> quickCheckGen $ do + reifyType n \p -> do + x <- arrayOf (UIntN.generator p) + pure $ encodeDecode x === Right x + + it "Can encode/decode bytesN[]" $ liftEffect do + for_ bytesSizes $ \n -> quickCheckGen $ do + reifyType n \p -> do + x <- arrayOf (BytesN.generator p) + pure $ encodeDecode x === Right x + + it "Can encode/decode address[]" $ liftEffect do + quickCheckGen $ do + x <- Address.generator + pure $ encodeDecode x === Right x + + it "Can encode/decode string[]" $ liftEffect do + quickCheck $ \(x :: Array BMPString) -> + encodeDecode x === Right x + +vecTypePropertyTests :: Spec Unit +vecTypePropertyTests = do + + describe "Vector type property tests" do + + it "Can encode/decode intN[k]" $ liftEffect do + for_ intSizes $ \n -> + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> + reifyType n \pn -> do + x <- Vector.generator pk (IntN.generator pn) + pure $ encodeDecode x === Right x + + it "Can encode/decode uintN[k]" $ liftEffect do + for_ intSizes $ \n -> + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> + reifyType n \pn -> do + x <- Vector.generator pk (UIntN.generator pn) + pure $ encodeDecode x === Right x + + it "Can encode/decode bytesN[k]" $ liftEffect do + for_ bytesSizes $ \n -> + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> + reifyType n \pn -> do + x <- Vector.generator pk (BytesN.generator pn) + pure $ encodeDecode x === Right x + + it "Can encode/decode address[k]" $ liftEffect do + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> do + x <- Vector.generator pk Address.generator + pure $ encodeDecode x === Right x + + it "Can encode/decode string[k]" $ liftEffect do + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> do + x <- Vector.generator pk (arbitrary :: Gen BMPString) + pure $ encodeDecode x === Right x + +nestedTypePropertyTests :: Spec Unit +nestedTypePropertyTests = do + describe "Nested type property tests for vector, vector" do + + it "Can encode/decode bytesN[k1][k2]" $ liftEffect do + for_ bytesSizes $ \n -> do + quickCheckGen $ do + k1 <- chooseInt 1 10 + k2 <- chooseInt 1 10 + reifyType k1 \pk1 -> + reifyType k2 \pk2 -> + reifyType n \pn -> do + x <- Vector.generator pk2 (Vector.generator pk1 (BytesN.generator pn)) + pure $ encodeDecode x === Right x + + it "Can encode/decode string[k1][k2]" $ liftEffect do + quickCheckGen $ do + k1 <- chooseInt 1 10 + k2 <- chooseInt 1 10 + reifyType k1 \pk1 -> + reifyType k2 \pk2 -> do + x <- Vector.generator pk2 (Vector.generator pk1 (arbitrary :: Gen BMPString)) + pure $ encodeDecode x === Right x + + describe "Nested type property tests for array, vector" do + + it "Can encode/decode bytesN[k][]" $ liftEffect do + for_ bytesSizes $ \n -> do + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> + reifyType n \pn -> do + x <- arrayOf (Vector.generator pk (BytesN.generator pn)) + pure $ encodeDecode x === Right x + + it "Can encode/decode string[k][]" $ liftEffect do + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> do + x <- arrayOf (Vector.generator pk (arbitrary :: Gen BMPString)) + pure $ encodeDecode x === Right x + + describe "Nested type property tests for vector, array" do + + it "Can encode/decode uintN[][k]" $ liftEffect do + for_ intSizes $ \n -> do + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> + reifyType n \pn -> do + x <- (Vector.generator pk (arrayOf $ UIntN.generator pn)) + pure $ encodeDecode x === Right x + + it "Can encode/decode string[][k]" $ liftEffect do + quickCheckGen $ do + k <- chooseInt 1 10 + reifyType k \pk -> do + x <- (Vector.generator pk (arrayOf (arbitrary :: Gen BMPString))) + pure $ encodeDecode x === Right x + + describe "Nested type property tests for array, array" do + + it "Can encode/decode intN[][]" $ liftEffect do + for_ intSizes $ \n -> do + quickCheckGen $ + reifyType n \pn -> do + x <- (arrayOf (arrayOf $ IntN.generator pn)) + pure $ encodeDecode x === Right x + + it "Can encode/decode string[][]" $ liftEffect do + quickCheck \(x :: Array (Array BMPString)) -> + encodeDecode x === Right x + +tupleTests :: Spec Unit +tupleTests = do + describe "Basic static sized Tuple Tests" $ do + + it "Can encode/decode (intN, address, bool, uintN, bytesN)" $ liftEffect do + quickCheckGen $ do + n <- oneOf (pure <$> intSizes) + m <- oneOf (pure <$> intSizes) + k <- oneOf (pure <$> bytesSizes) + reifyType n \pn -> + reifyType m \pm -> + reifyType k \pk -> do + int <- IntN.generator pn + addr <- Address.generator + bool <- arbitrary :: Gen Boolean + uint <- UIntN.generator pm + bytes <- BytesN.generator pk + let x = Tuple5 int addr bool uint bytes + pure $ genericEncodeDecode x === Right x + + it "Can encode/decode (address[k], bool, intN[k], uint)" $ liftEffect do + quickCheckGen $ do + k1 <- chooseInt 1 10 + k2 <- chooseInt 1 10 + n <- oneOf (pure <$> intSizes) + m <- oneOf (pure <$> intSizes) + reifyType k1 \pk1 -> + reifyType k2 \pk2 -> + reifyType n \pn -> do + reifyType m \pm -> do + addrs <- arrayOf (Vector.generator pk1 Address.generator) + bool <- arbitrary @Boolean + ints <- Vector.generator pk2 (IntN.generator pn) + uint <- (UIntN.generator pm) + let x = Tuple4 addrs bool ints uint + pure $ genericEncodeDecode x === Right x + + describe "Basic dynamic sized Tuple Tests" $ do + + it "Can encode/decode (intN[], bytes, address[][k], string[k][], bool)" $ liftEffect do + quickCheckGen $ do + n <- oneOf (pure <$> intSizes) + m <- chooseInt 1 10 + k <- chooseInt 1 10 + reifyType n \pn -> + reifyType m \pm -> + reifyType k \pk -> do + ints <- arrayOf (IntN.generator pn) + bytes <- Hex.toByteString <$> (chooseInt 1 100 >>= Hex.generator) + addrs <- Vector.generator pm (arrayOf Address.generator) + strings <- arrayOf (Vector.generator pk (arbitrary @BMPString)) + bool <- arbitrary :: Gen Boolean + let x = Tuple5 ints bytes addrs strings bool + pure $ genericEncodeDecode x === Right x + + it "Can encode/decode (address[k], bool, intN[k], uint)" $ liftEffect do + quickCheckGen $ do + k1 <- chooseInt 1 10 + k2 <- chooseInt 1 10 + n <- oneOf (pure <$> intSizes) + m <- oneOf (pure <$> intSizes) + reifyType k1 \pk1 -> + reifyType k2 \pk2 -> + reifyType n \pn -> do + reifyType m \pm -> do + addrs <- arrayOf (Vector.generator pk1 Address.generator) + bool <- arbitrary @Boolean + ints <- Vector.generator pk2 (IntN.generator pn) + uint <- (UIntN.generator pm) + let x = Tuple4 addrs bool ints uint + pure $ genericEncodeDecode x === Right x + +-------------------------------------------------------------------------------- +newtype BMPString = BMPString String + +derive newtype instance Eq BMPString +derive newtype instance Show BMPString +derive newtype instance ABIDecode BMPString +derive newtype instance ABIEncode BMPString +derive newtype instance EncodingType BMPString + +data UnicodeChar = Normal CodePoint | Surrogates CodePoint CodePoint + +instance Arbitrary BMPString where + arbitrary = BMPString <$> do + ucs <- arrayOf arbitrary + pure $ fromCodePointArray $ foldMap f ucs + where + f uc = case uc of + Normal a -> [ a ] + Surrogates a b -> [ a, b ] + +instance Arbitrary UnicodeChar where + arbitrary = frequency $ NonEmpty (Tuple (1.0 - p) normalGen) [ Tuple p surrogatesGen ] + + where + hiLB = 0xD800 + hiUB = 0xDBFF + loLB = 0xDC00 + loUB = 0xDFFF + maxCP = 65535 + toCP = toEnumWithDefaults bottom top + -- must have a high surrogate followed by a low surrogate + surrogatesGen = Surrogates <$> (toCP <$> chooseInt hiLB hiUB) <*> (toCP <$> chooseInt loLB loUB) + normalGen = Normal <<< toCP <$> do + chooseInt 0 maxCP `suchThat` \n -> + (n < hiLB || n > hiUB) && (n < loLB || n > loUB) + -- probability that you pick a surrogate from all possible codepoints + p = toNumber ((hiUB - hiLB + 1) + (loUB - loLB + 1)) / toNumber (maxCP + 1) + +encodeDecode + :: forall a + . Show a + => Eq a + => ABIEncode a + => ABIDecode a + => a + -> Either ParseError a +encodeDecode x = + let + a = toDataBuilder x + in + (fromData a) + +genericEncodeDecode :: forall a rep . Show a => Eq a @@ -36,177 +365,14 @@ roundTripGeneric => GenericABIEncode rep => GenericABIDecode rep => a - -> HexString - -> Aff Unit -roundTripGeneric decoded encoded = do - encoded `shouldEqual` genericABIEncode decoded - genericFromData encoded `shouldEqual` Right decoded - -staticArraysTests :: Spec Unit -staticArraysTests = - describe "statically sized array tests" do - it "can encode statically sized vectors of addresses" do - let - mgivenElement = toVector s1 $ [ false ] - - givenElement = (unsafePartial fromJust $ mgivenElement) - - given = (unsafePartial fromJust $ toVector s2 [ givenElement, givenElement ]) - - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000000" - <> "0000000000000000000000000000000000000000000000000000000000000000" - roundTrip given expected - it "can encode statically sized vectors of statically sized vectors of type bool" do - let - mgiven = - toVector s2 - $ map (\a -> unsafePartial fromJust $ mkAddress =<< mkHexString a) - [ "407d73d8a49eeb85d32cf465507dd71d507100c1" - , "407d73d8a49eeb85d32cf465507dd71d507100c3" - ] - - given = (unsafePartial $ fromJust $ mgiven) :: Vector 2 Address - - expected = - unsafePartial (fromJust <<< mkHexString) $ "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1" - <> "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c3" - roundTrip given expected - it "can encode statically sized vectors of statically sized bytes" do - let - elem1 = unsafePartial fromJust (fromByteString s1 =<< flip BS.fromString BS.Hex "cf") - - elem2 = unsafePartial fromJust (fromByteString s1 =<< flip BS.fromString BS.Hex "68") - - elem3 = unsafePartial fromJust (fromByteString s1 =<< flip BS.fromString BS.Hex "4d") - - elem4 = unsafePartial fromJust (fromByteString s1 =<< flip BS.fromString BS.Hex "fb") - - given = unsafePartial fromJust (toVector s4 $ [ elem1, elem2, elem3, elem4 ]) :: Vector 4 (BytesN 1) - - expected = - unsafePartial (fromJust <<< mkHexString) - $ "cf00000000000000000000000000000000000000000000000000000000000000" - <> "6800000000000000000000000000000000000000000000000000000000000000" - <> "4d00000000000000000000000000000000000000000000000000000000000000" - <> "fb00000000000000000000000000000000000000000000000000000000000000" - roundTrip given expected - -dynamicArraysTests :: Spec Unit -dynamicArraysTests = - describe "dynamically sized array tests" do - it "can encode dynamically sized lists of bools" do - let - given = [ true, true, false ] - - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000003" - <> "0000000000000000000000000000000000000000000000000000000000000001" - <> "0000000000000000000000000000000000000000000000000000000000000001" - <> "0000000000000000000000000000000000000000000000000000000000000000" - roundTrip given expected - -tuplesTest :: Spec Unit -tuplesTest = - describe "tuples test" do - it "can encode 2-tuples with both static args" do - let - given = Tuple2 true false - - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000001" - <> "0000000000000000000000000000000000000000000000000000000000000000" - roundTripGeneric given expected - it "can encode 1-tuples with dynamic arg" do - let - given = Tuple1 [ true, false ] - - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000020" - <> "0000000000000000000000000000000000000000000000000000000000000002" - <> "0000000000000000000000000000000000000000000000000000000000000001" - <> "0000000000000000000000000000000000000000000000000000000000000000" - roundTripGeneric given expected - it "can encode 4-tuples with a mix of args -- (UInt, String, Boolean, Array Int)" do - let - given = Tuple4 1 "dave" true [ 1, 2, 3 ] - - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000001" - <> "0000000000000000000000000000000000000000000000000000000000000080" - <> "0000000000000000000000000000000000000000000000000000000000000001" - <> "00000000000000000000000000000000000000000000000000000000000000c0" - <> "0000000000000000000000000000000000000000000000000000000000000004" - <> "6461766500000000000000000000000000000000000000000000000000000000" - <> "0000000000000000000000000000000000000000000000000000000000000003" - <> "0000000000000000000000000000000000000000000000000000000000000001" - <> "0000000000000000000000000000000000000000000000000000000000000002" - <> "0000000000000000000000000000000000000000000000000000000000000003" - roundTripGeneric given expected - it "can do something really complicated" do - let - uint = unsafePartial $ fromJust $ uIntNFromBigNumber s256 $ embed 1 - - int = unsafePartial $ fromJust $ intNFromBigNumber s256 $ embed $ negate 1 - - bool = true - - int224 = unsafePartial $ fromJust $ intNFromBigNumber s224 $ embed 221 - - bools = true :< false :< nilVector - - ints = - [ unsafePartial $ fromJust $ intNFromBigNumber s256 $ embed 1 - , unsafePartial $ fromJust $ intNFromBigNumber s256 $ embed $ negate 1 - , unsafePartial $ fromJust $ intNFromBigNumber s256 $ embed 3 - ] - - string = "hello" - - bytes16 = unsafePartial fromJust $ fromByteString s16 =<< flip BS.fromString BS.Hex "12345678123456781234567812345678" - - elem = unsafePartial fromJust $ fromByteString s2 =<< flip BS.fromString BS.Hex "1234" - - vector4 = elem :< elem :< elem :< elem :< nilVector - - bytes2s = [ vector4, vector4 ] - - given = - Tuple9 uint int bool int224 bools ints string bytes16 bytes2s - :: Tuple9 (UIntN 256) - (IntN 256) - Boolean - (IntN 224) - (Vector 2 Boolean) - (Array (IntN 256)) - String - (BytesN 16) - (Array (Vector 4 (BytesN 2))) - - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000001" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "0000000000000000000000000000000000000000000000000000000000000001" - <> "00000000000000000000000000000000000000000000000000000000000000dd" - <> "0000000000000000000000000000000000000000000000000000000000000001" - <> "0000000000000000000000000000000000000000000000000000000000000000" - <> "0000000000000000000000000000000000000000000000000000000000000140" - <> "00000000000000000000000000000000000000000000000000000000000001c0" - <> "1234567812345678123456781234567800000000000000000000000000000000" - <> "0000000000000000000000000000000000000000000000000000000000000200" - <> "0000000000000000000000000000000000000000000000000000000000000003" - <> "0000000000000000000000000000000000000000000000000000000000000001" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "0000000000000000000000000000000000000000000000000000000000000003" - <> "0000000000000000000000000000000000000000000000000000000000000005" - <> "68656c6c6f000000000000000000000000000000000000000000000000000000" - <> "0000000000000000000000000000000000000000000000000000000000000002" - <> "1234000000000000000000000000000000000000000000000000000000000000" - <> "1234000000000000000000000000000000000000000000000000000000000000" - <> "1234000000000000000000000000000000000000000000000000000000000000" - <> "1234000000000000000000000000000000000000000000000000000000000000" - <> "1234000000000000000000000000000000000000000000000000000000000000" - <> "1234000000000000000000000000000000000000000000000000000000000000" - <> "1234000000000000000000000000000000000000000000000000000000000000" - <> "1234000000000000000000000000000000000000000000000000000000000000" - roundTripGeneric given expected + -> Either ParseError a +genericEncodeDecode a = + genericFromData $ genericABIEncode a + +intSizes :: NonEmptyArray Int +intSizes = unsafePartial fromJust + $ fromArray + $ filter (\x -> x `mod` 8 == 0) (8 .. 256) + +bytesSizes :: NonEmptyArray Int +bytesSizes = 1 NEA... 32 diff --git a/test/web3/Web3Spec/Encoding/DataSpec.purs b/test/web3/Web3Spec/Encoding/DataSpec.purs index 8ec5491..80cd4b9 100644 --- a/test/web3/Web3Spec/Encoding/DataSpec.purs +++ b/test/web3/Web3Spec/Encoding/DataSpec.purs @@ -1,35 +1,34 @@ module Web3Spec.Encoding.DataSpec (spec, approve) where import Prelude -import Data.Maybe (fromJust) -import Network.Ethereum.Web3.Solidity (UIntN, Tuple2, uIntNFromBigNumber) -import Network.Ethereum.Web3.Solidity.Sizes (s256) -import Network.Ethereum.Web3.Types (Address, HexString, TransactionOptions, NoPay, Web3, mkHexString, mkAddress) + import Data.Functor.Tagged (Tagged, tagged) -import Network.Ethereum.Web3.Contract (sendTx, mkDataField) +import Effect.Class (liftEffect) import Network.Ethereum.Core.Keccak256 (toSelector) -import Network.Ethereum.Web3.Solidity.Generic (genericFromRecordFields) -import Type.Proxy (Proxy(..)) -import Partial.Unsafe (unsafePartial) +import Network.Ethereum.Core.Signatures as Address +import Network.Ethereum.Web3.Contract (sendTx, mkDataField) +import Network.Ethereum.Web3.Solidity (Tuple2, UIntN) import Network.Ethereum.Web3.Solidity.AbiEncoding (toDataBuilder) +import Network.Ethereum.Web3.Solidity.Generic (genericFromRecordFields) +import Network.Ethereum.Web3.Solidity.UInt as UIntN +import Network.Ethereum.Web3.Types (Address, HexString, NoPay, TransactionOptions, Web3) +import Test.QuickCheck (quickCheckGen, (===)) import Test.Spec (Spec, describe, it) -import Test.Spec.Assertions (shouldEqual) +import Type.Proxy (Proxy(..)) spec :: Spec Unit spec = describe "data maker" do - it "can make the approval data" do - let - addr = unsafePartial fromJust $ (mkAddress =<< mkHexString "78534a937a855e15be172de35f2211626f92f8ec") - - val = unsafePartial fromJust $ uIntNFromBigNumber s256 one - - approvalD = mkDataField (Proxy :: Proxy ApproveFn) { _spender: addr, _value: val } + it "can make the approval data" $ liftEffect do + quickCheckGen do + args <- { _spender: _, _value: _ } <$> Address.generator <*> UIntN.generator (Proxy @256) + let + approvalD = mkDataField (Proxy :: Proxy ApproveFn) args - sel = toSelector "approve(address,uint256)" + sel = toSelector "approve(address,uint256)" - fullDat = sel <> toDataBuilder addr <> toDataBuilder val - approvalD `shouldEqual` fullDat + fullDat = sel <> toDataBuilder args._spender <> toDataBuilder args._value + pure $ approvalD === fullDat type ApproveFn = Tagged "approve(address,uint256)" (Tuple2 (Tagged "_spender" Address) (Tagged "_value" (UIntN 256))) diff --git a/test/web3/Web3Spec/Encoding/GenericSpec.purs b/test/web3/Web3Spec/Encoding/GenericSpec.purs index 5c41478..b064418 100644 --- a/test/web3/Web3Spec/Encoding/GenericSpec.purs +++ b/test/web3/Web3Spec/Encoding/GenericSpec.purs @@ -1,23 +1,15 @@ module Web3Spec.Encoding.GenericSpec (spec) where import Prelude -import Control.Error.Util (hush) -import Data.Array (unsafeIndex, uncons) + import Data.Functor.Tagged (Tagged, tagged) import Data.Generic.Rep (class Generic) -import Data.Eq.Generic (genericEq) -import Data.Show.Generic (genericShow) -import Data.Maybe (Maybe(..), fromJust) -import Data.Newtype (class Newtype, wrap) -import Record.Builder (build, merge) -import Type.Proxy (Proxy) -import Network.Ethereum.Web3.Solidity (Address, Tuple1, Tuple2(..), Tuple3(..), UIntN, fromData) -import Network.Ethereum.Web3.Solidity.Event (class IndexedEvent, decodeEvent, genericArrayParser) +import Effect.Class (liftEffect) +import Network.Ethereum.Web3.Solidity (Tuple2(..), Tuple3(..)) import Network.Ethereum.Web3.Solidity.Generic (genericToRecordFields) -import Network.Ethereum.Web3.Types (Change(..), HexString, embed, mkAddress, mkHexString) -import Partial.Unsafe (unsafePartial) +import Record.Builder (build, merge) +import Test.QuickCheck (quickCheck, (===)) import Test.Spec (Spec, describe, it) -import Test.Spec.Assertions (shouldEqual) spec :: Spec Unit spec = @@ -27,125 +19,60 @@ spec = toRecordFieldsSpec :: Spec Unit toRecordFieldsSpec = describe "test ToRecordFields class" do - it "pass toRecordFields basic test" do - let - as = Tuple3 (tagged 1) (tagged "hello") (tagged 'c') :: Tuple3 (Tagged "a" Int) (Tagged "d" String) (Tagged "e" Char) - WeirdTuple (genericToRecordFields as) - `shouldEqual` - WeirdTuple - { a: 1 - , d: "hello" - , e: 'c' - } - it "passes the merging test" do - let - as = Tuple3 (tagged 1) (tagged "hello") (tagged 'c') :: Tuple3 (Tagged "a" Int) (Tagged "d" String) (Tagged "e" Char) - - as' = Tuple2 (tagged 2) (tagged "bye") :: Tuple2 (Tagged "b" Int) (Tagged "c" String) + it "pass toRecordFields basic test" $ liftEffect do + quickCheck $ \(x :: { a :: Int, b :: Int, c :: String, d :: String }) -> + let + as = Tuple2 (tagged x.a) (tagged x.b) :: Tuple2 (Tagged "a" Int) (Tagged "b" Int) + bs = Tuple2 (tagged x.c) (tagged x.d) :: Tuple2 (Tagged "c" String) (Tagged "d" String) + in + (build (merge (genericToRecordFields as)) (genericToRecordFields bs)) + === + { a: x.a + , b: x.b + , c: x.c + , d: x.d + } + + it "pass toRecordFields basic test" $ liftEffect do + quickCheck $ \(x :: { a :: Int, b :: Int, c :: String, d :: String, e :: Char }) -> + let + as = Tuple3 (tagged x.a) (tagged x.d) (tagged x.e) :: Tuple3 (Tagged "a" Int) (Tagged "d" String) (Tagged "e" Char) + in + WeirdTuple (genericToRecordFields as) + === + WeirdTuple + { a: x.a + , d: x.d + , e: x.e + } + + it "passes the merging test" $ liftEffect do + quickCheck $ \(x :: { a :: Int, b :: Int, c :: String, d :: String, e :: Char }) -> + let + as = Tuple3 (tagged x.a) (tagged x.d) (tagged x.e) :: Tuple3 (Tagged "a" Int) (Tagged "d" String) (Tagged "e" Char) + + as' = Tuple2 (tagged x.b) (tagged x.c) :: Tuple2 (Tagged "b" Int) (Tagged "c" String) + + c = CombinedTuple $ build (merge (genericToRecordFields as)) (genericToRecordFields as') + in + c === CombinedTuple x - c = CombinedTuple $ build (merge (genericToRecordFields as)) (genericToRecordFields as') - c `shouldEqual` CombinedTuple { a: 1, b: 2, c: "bye", d: "hello", e: 'c' } - it "can parse a change an address array" do - let - (Transfer t) = transfer - - expected = Tuple2 (tagged t.to) (tagged t.from) :: Tuple2 (Tagged "to" Address) (Tagged "from" Address) - hush (fromData (unsafePartial $ unsafeIndex addressArray 1)) `shouldEqual` Just t.to - genericArrayParser (unsafePartial fromJust $ _.tail <$> uncons addressArray) `shouldEqual` Just expected - it "can combine events" do - decodeEvent change `shouldEqual` Just transfer +-------------------------------------------------------------------------------- newtype WeirdTuple = WeirdTuple { a :: Int, d :: String, e :: Char } -derive instance genericWeirdTuple :: Generic WeirdTuple _ - -instance showWeirdTuple :: Show WeirdTuple where - show = genericShow - -instance eqWeirdTuple :: Eq WeirdTuple where - eq = genericEq +derive instance Generic WeirdTuple _ +derive newtype instance Show WeirdTuple +derive newtype instance Eq WeirdTuple newtype OtherTuple = OtherTuple { b :: Int, c :: String } -derive instance genericOtherTuple :: Generic OtherTuple _ - -instance showOtherTuple :: Show OtherTuple where - show = genericShow - -instance eqOtherTuple :: Eq OtherTuple where - eq = genericEq - -data CombinedTuple = CombinedTuple { a :: Int, b :: Int, c :: String, d :: String, e :: Char } - -derive instance genericCombinedTuple :: Generic CombinedTuple _ - -instance showCombinedTuple :: Show CombinedTuple where - show = genericShow - -instance eqCombinedTuple :: Eq CombinedTuple where - eq = genericEq - --------------------------------------------------------------------------------- -newtype Transfer = Transfer { to :: Address, from :: Address, amount :: UIntN 256 } - -derive instance newtypeTransfer :: Newtype Transfer _ - -derive instance genericTransfer :: Generic Transfer _ - -instance indexedTransfer :: IndexedEvent (Tuple2 (Tagged "to" Address) (Tagged "from" Address)) (Tuple1 (Tagged "amount" (UIntN 256))) Transfer where - isAnonymous _ = false - -instance showTransfer :: Show Transfer where - show = genericShow - -instance eqTransfer :: Eq Transfer where - eq = genericEq - -transfer :: Transfer -transfer = - let - t = unsafePartial fromJust $ mkAddress =<< mkHexString "0x407d73d8a49eeb85d32cf465507dd71d507100c1" - - f = unsafePartial fromJust $ mkAddress =<< mkHexString "0x0000000000000000000000000000000000000001" - - a = unsafePartial fromJust $ map hush fromData =<< mkHexString "0x0000000000000000000000000000000000000000000000000000000000000001" - in - Transfer - { to: t - , from: f - , amount: a - } - -addressArray :: Array HexString -addressArray = - let - to = unsafePartial fromJust $ mkHexString "0x000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1" - - from = unsafePartial fromJust $ mkHexString "0x0000000000000000000000000000000000000000000000000000000000000001" - - topic = unsafePartial fromJust $ mkHexString "0x" - in - [ topic, to, from ] - -amount :: HexString -amount = unsafePartial fromJust $ mkHexString "0x0000000000000000000000000000000000000000000000000000000000000001" - -change :: Change -change = - Change - { data: amount - , topics: addressArray - , logIndex: zero - , transactionHash: tx - , transactionIndex: zero - , blockNumber: wrap $ embed 0 - , blockHash: bh - , address: a - , removed: false - } - where - bh = unsafePartial fromJust $ mkHexString "00" +derive instance Generic OtherTuple _ +derive newtype instance Show OtherTuple +derive newtype instance Eq OtherTuple - tx = unsafePartial fromJust $ mkHexString "00" +newtype CombinedTuple = CombinedTuple { a :: Int, b :: Int, c :: String, d :: String, e :: Char } - a = unsafePartial fromJust $ mkAddress =<< mkHexString "0x0000000000000000000000000000000000000000" +derive instance Generic CombinedTuple _ +derive newtype instance Show CombinedTuple +derive newtype instance Eq CombinedTuple \ No newline at end of file diff --git a/test/web3/Web3Spec/Encoding/SimpleSpec.purs b/test/web3/Web3Spec/Encoding/SimpleSpec.purs index d5c553b..d0969ca 100644 --- a/test/web3/Web3Spec/Encoding/SimpleSpec.purs +++ b/test/web3/Web3Spec/Encoding/SimpleSpec.purs @@ -1,254 +1,25 @@ module Web3Spec.Encoding.SimpleSpec (spec) where import Prelude -import Effect.Aff (Aff, error, throwError) +import Effect.Aff (error, throwError) import Control.Monad.Except (runExcept) -import Data.Array (replicate) -import Data.ByteString as BS import Data.Either (Either(Right), either) -import Data.Foldable (intercalate) import Foreign (ForeignError) import Data.List.Types (NonEmptyList) import Data.Maybe (Maybe(..), fromJust) import Data.Newtype (unwrap) -import Data.String (toLower) -import Data.Traversable (sequence) -import Network.Ethereum.Core.BigNumber (pow) -import Network.Ethereum.Web3.Solidity.AbiEncoding (class ABIEncode, class ABIDecode, toDataBuilder, fromData) -import Network.Ethereum.Web3.Solidity.Bytes (fromByteString) -import Network.Ethereum.Web3.Solidity.Int (intNFromBigNumber) -import Network.Ethereum.Web3.Solidity.Sizes (s1, s12, s16, s248, s256, s3, s8) -import Network.Ethereum.Web3.Solidity.UInt (uIntNFromBigNumber) -import Network.Ethereum.Web3.Types (Block, FalseOrObject(..), HexString, BigNumber, SyncStatus(..), embed, mkAddress, mkHexString, unHex) +import Network.Ethereum.Web3.Types (BigNumber, Block, FalseOrObject(..), HexString, SyncStatus(..), fromInt, mkHexString) import Partial.Unsafe (unsafePartial) import Simple.JSON (readJSON') import Test.Spec (Spec, describe, it) -import Test.Spec.Assertions (shouldEqual, shouldNotEqual) +import Test.Spec.Assertions (shouldEqual) spec :: Spec Unit spec = describe "encoding-spec" do - stringTests - bytesDTests - bytesNTests - intTests - uintNTests - intNTests - addressTests falseOrObjectTests blockTests -roundTrip :: forall a. Show a => Eq a => ABIEncode a => ABIDecode a => a -> HexString -> Aff Unit -roundTrip decoded encoded = do - encoded `shouldEqual` toDataBuilder decoded - fromData encoded `shouldEqual` Right decoded - -stringTests :: Spec Unit -stringTests = - describe "string tests" do - it "can encode simple strings" do - let - given = "gavofyork" - let - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000009" - <> "6761766f66796f726b0000000000000000000000000000000000000000000000" - roundTrip given expected - it "can encode complicated strings" do - let - given = "welcome to ethereum. welcome to ethereum. welcome to ethereum." - let - expected = - unsafePartial fromJust <<< mkHexString $ "000000000000000000000000000000000000000000000000000000000000003e" - <> "77656c636f6d6520746f20657468657265756d2e2077656c636f6d6520746f20" - <> "657468657265756d2e2077656c636f6d6520746f20657468657265756d2e0000" - roundTrip given expected - it "can encode unicode strings" do - let - given = "ää" - let - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000008" - <> "c383c2a4c383c2a4000000000000000000000000000000000000000000000000" - roundTrip given expected - it "can handle VERY long HexStrings" do - let - given = intercalate "" $ replicate 128 "0000000000000000000000000000000000000000000000000000000000000000" - let - expected = unsafePartial fromJust <<< mkHexString $ given - given `shouldEqual` unHex expected - it "can handle mixed case HexStrings" do - let - given = "fF" - let - expected = unsafePartial fromJust <<< mkHexString $ given - -- note; for easy equality we should canonicalize HexStrings as lowercase - toLower given `shouldEqual` unHex expected - it "fails on odd length HexStrings" do - let - givens = [ "f", "0", "000", "0f0", "fffff", "0000000000000000000000000000000000000000f" ] - _ <- sequence $ map (\g -> mkHexString g `shouldEqual` Nothing) givens - pure unit - it "should hold equality across cases" do - mkHexString "ff" `shouldEqual` mkHexString "Ff" - mkHexString "0000aa" `shouldEqual` mkHexString "0000AA" - mkHexString "0000aa" `shouldEqual` mkHexString "0000aa" - mkHexString "abcdef" `shouldEqual` mkHexString "AbCdEf" - mkHexString "" `shouldNotEqual` mkHexString "ff" - mkHexString "ff" `shouldNotEqual` mkHexString "aa" - -bytesDTests :: Spec Unit -bytesDTests = - describe "bytesD tests" do - it "can encode short bytesD" do - let - given = unsafePartial $ fromJust $ flip BS.fromString BS.Hex $ "c3a40000c3a4" - let - expected = - unsafePartial fromJust <<< mkHexString - $ "0000000000000000000000000000000000000000000000000000000000000006" - <> "c3a40000c3a40000000000000000000000000000000000000000000000000000" - roundTrip given expected - it "can encode long bytesD" do - let - given = - unsafePartial $ fromJust $ flip BS.fromString BS.Hex - $ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1" - let - expected = - unsafePartial fromJust <<< mkHexString - $ "000000000000000000000000000000000000000000000000000000000000009f" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - <> "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff100" - roundTrip given expected - it "can encode dave" do - let - given = "dave" - let - expected = - unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000004" - <> "6461766500000000000000000000000000000000000000000000000000000000" - roundTrip given expected - -bytesNTests :: Spec Unit -bytesNTests = - describe "byteN tests" do - it "can encode Bytes1" do - let - mgiven = fromByteString s1 <<< unsafePartial fromJust $ flip BS.fromString BS.Hex $ "cf" - - given = unsafePartial $ fromJust mgiven - - expected = unsafePartial fromJust <<< mkHexString $ "cf00000000000000000000000000000000000000000000000000000000000000" - roundTrip given expected - it "can encode Bytes3" do - let - mgiven = fromByteString s3 $ unsafePartial $ fromJust $ flip BS.fromString BS.Hex $ "cf0011" - - given = unsafePartial $ fromJust mgiven - - expected = unsafePartial fromJust <<< mkHexString $ "cf00110000000000000000000000000000000000000000000000000000000000" - roundTrip given expected - it "can encode Bytes12" do - let - mgiven = fromByteString s12 $ unsafePartial $ fromJust $ flip BS.fromString BS.Hex $ "6761766f66796f726b000000" - - given = unsafePartial $ fromJust mgiven - - expected = unsafePartial fromJust <<< mkHexString $ "6761766f66796f726b0000000000000000000000000000000000000000000000" - roundTrip given expected - -intTests :: Spec Unit -intTests = - describe "int/uint tests" do - it "can encode int" do - let - given = 21 - let - expected = unsafePartial fromJust <<< mkHexString $ "0000000000000000000000000000000000000000000000000000000000000015" - roundTrip given expected - it "can encode negative numbers" do - let - given = negate 1 - let - expected = unsafePartial fromJust <<< mkHexString $ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - roundTrip given expected - it "can encode some big number" do - let - given = 987654321 - let - expected = unsafePartial fromJust <<< mkHexString $ "000000000000000000000000000000000000000000000000000000003ade68b1" - roundTrip given expected - -addressTests :: Spec Unit -addressTests = - describe "addresses tests" do - it "can encode address" do - let - given = unsafePartial fromJust $ mkAddress =<< mkHexString "407d73d8a49eeb85d32cf465507dd71d507100c1" - let - expected = unsafePartial fromJust <<< mkHexString $ "000000000000000000000000407d73d8a49eeb85d32cf465507dd71d507100c1" - roundTrip given expected - -uintNTests :: Spec Unit -uintNTests = - describe "uint tests" do - it "can encode uint8" do - let - mgiven = uIntNFromBigNumber s8 $ (embed 2) `pow` 8 - one - - given = unsafePartial $ fromJust mgiven - - expected = unsafePartial fromJust <<< mkHexString $ "00000000000000000000000000000000000000000000000000000000000000ff" - roundTrip given expected - it "can encode larger uint256" do - let - mgiven = uIntNFromBigNumber s256 $ ((embed $ 2) `pow` 256) - one - - given = unsafePartial $ fromJust mgiven - - expected = unsafePartial fromJust <<< mkHexString $ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - roundTrip given expected - it "can fail to encode larger uin248" do - let - mgiven = (uIntNFromBigNumber s248 $ (embed $ 2) `pow` 256 - one) - mgiven `shouldEqual` Nothing - -intNTests :: Spec Unit -intNTests = - describe "uint tests" do - it "can encode int16" do - let - mgiven = intNFromBigNumber s16 $ embed $ negate 1 - - given = unsafePartial $ fromJust mgiven - - expected = unsafePartial fromJust <<< mkHexString $ "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - roundTrip given expected - it "can encode larger uint256" do - let - mgiven = intNFromBigNumber s256 $ ((embed $ 2) `pow` 255) - one - - given = unsafePartial $ fromJust mgiven - - expected = unsafePartial fromJust <<< mkHexString $ "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - roundTrip given expected - it "can fail to encode larger int248" do - let - mgiven = uIntNFromBigNumber s248 $ (embed $ 2) `pow` 255 - one - mgiven `shouldEqual` Nothing - it "can fail to encode larger negative int248" do - let - mgiven = uIntNFromBigNumber s248 $ negate $ (embed $ 2) `pow` 255 + one - mgiven `shouldEqual` Nothing - falseOrObjectTests :: Spec Unit falseOrObjectTests = describe "FalseOrObject tests" do @@ -259,7 +30,7 @@ falseOrObjectTests = it "can decode FalseOrObject instances that are objects" do let decodedObj = runExcept $ readJSON' "{ \"startingBlock\": \"0x0\", \"currentBlock\": \"0x1\", \"highestBlock\": \"0x2\" }" - decodedObj `shouldEqual` (Right $ FalseOrObject $ Just $ SyncStatus { startingBlock: embed 0, currentBlock: embed 1, highestBlock: embed 2 }) + decodedObj `shouldEqual` (Right $ FalseOrObject $ Just $ SyncStatus { startingBlock: fromInt 0, currentBlock: fromInt 1, highestBlock: fromInt 2 }) blockTests :: Spec Unit blockTests = @@ -270,7 +41,7 @@ blockTests = dBlock <- unwrap <$> either (throwError <<< error <<< show) pure decodedBlockE dBlock.nonce `shouldEqual` (Just $ upToHex "0x0000000000000000") dBlock.hash `shouldEqual` (Just $ upToHex "0x093ff26b85b5e3ac3e331f3d766a81990be76ec8ac79f62a81e30faa642dc26f") - dBlock.timestamp `shouldEqual` embed 1507570522 + dBlock.timestamp `shouldEqual` fromInt 1507570522 where -- this is block 1 on Eth mainnet blockPlaintext = "{\"difficulty\":\"0x1\",\"extraData\":\"0x0000000000000000000000000000000000000000000000000000000000000000759e3fae48d5abad53ab446f31ab3ae1531f2e4c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"gasLimit\":\"0x8000000\",\"gasUsed\":\"0x0\",\"hash\":\"0x093ff26b85b5e3ac3e331f3d766a81990be76ec8ac79f62a81e30faa642dc26f\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x0000000000000000\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x273\",\"stateRoot\":\"0xd3811ce828cfc6b07dbedfe073e1ef7e50bda2dac61a901e995c0f460a625cdd\",\"timestamp\":\"0x59dbb35a\",\"totalDifficulty\":\"0x1\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]}" diff --git a/test/web3/Web3Spec/Live/Utils.purs b/test/web3/Web3Spec/Live/Utils.purs index 4325c4c..7f31680 100644 --- a/test/web3/Web3Spec/Live/Utils.purs +++ b/test/web3/Web3Spec/Live/Utils.purs @@ -1,29 +1,31 @@ -module Web3Spec.Live.Utils where +module Web3Spec.Live.Utils + ( assertWeb3 + , bigGasLimit + , defaultTestTxOptions + , go + , joinWeb3Fork + , nullAddress + , pollTransactionReceipt + ) where import Prelude + import Control.Monad.Reader (ReaderT, runReaderT) -import Data.Array ((!!)) -import Data.ByteString as BS +import Data.Array.NonEmpty as NAE import Data.Either (Either(..)) import Data.Lens ((?~)) -import Data.Maybe (Maybe(..), fromJust) -import Data.Newtype (wrap, unwrap) +import Data.Maybe (fromJust) import Data.Traversable (intercalate) -import Data.Array.NonEmpty as NAE -import Data.Tuple (Tuple(..)) import Effect.Aff (Aff, Milliseconds(..), Fiber, joinFiber, delay) -import Effect.Aff.AVar as AVar import Effect.Aff.Class (class MonadAff, liftAff) import Effect.Class.Console as C -import Test.Spec (ComputationType(..), SpecT, hoistSpec) -import Network.Ethereum.Core.BigNumber (decimal, parseBigNumber) +import Network.Ethereum.Core.BigNumber (decimal, fromStringAs) import Network.Ethereum.Core.Signatures (mkAddress) -import Network.Ethereum.Web3 (class EventFilter, class KnownSize, Address, Web3Error, BigNumber, BlockNumber, BytesN, CallError, EventAction(..), HexString, Provider, TransactionOptions, TransactionReceipt(..), TransactionStatus(..), UIntN, Web3, _from, _gas, defaultTransactionOptions, event, embed, eventFilter, forkWeb3', fromByteString, intNFromBigNumber, mkHexString, runWeb3, uIntNFromBigNumber) +import Network.Ethereum.Web3 (Address, BigNumber, HexString, Provider, TransactionOptions, TransactionReceipt(..), TransactionStatus(..), Web3, Web3Error, _gas, defaultTransactionOptions, mkHexString, runWeb3) import Network.Ethereum.Web3.Api as Api -import Network.Ethereum.Web3.Solidity (class DecodeEvent, IntN) import Network.Ethereum.Web3.Types (NoPay) import Partial.Unsafe (unsafeCrashWith, unsafePartial) -import Type.Proxy (Proxy) +import Test.Spec (ComputationType(..), SpecT, hoistSpec) type Logger m = String -> m Unit @@ -37,29 +39,6 @@ go = in runReaderT m \logMsg -> C.log $ prefix <> "| " <> logMsg --- | Run a `Web3` action which will dispatch a single event, wait for the event, --- | then return the action's result and the event. -takeEvent - :: forall a ev i ni - . DecodeEvent i ni ev - => Show ev - => EventFilter ev - => Proxy ev - -> Address - -> Web3 a - -> Web3 (Tuple a ev) -takeEvent prx addrs web3Action = do - var <- liftAff AVar.empty - _ <- - forkWeb3' do - event (eventFilter prx addrs) - $ \e -> do - _ <- liftAff $ AVar.put e var - pure TerminateEvent - efRes <- web3Action - event <- liftAff $ AVar.take var - pure $ Tuple efRes event - -- | Assert the `Web3` action's result, crash the program if it doesn't succeed. assertWeb3 :: forall m a @@ -73,19 +52,6 @@ assertWeb3 provider a = Right x -> x Left err -> unsafeCrashWith $ "expected Right in `assertWeb3`, got error" <> show err -assertStorageCall - :: forall m a - . MonadAff m - => Provider - -> Web3 (Either CallError a) - -> m a -assertStorageCall p f = - liftAff do - eRes <- assertWeb3 p f - case eRes of - Right x -> pure x - Left err -> unsafeCrashWith $ "expected Right in `assertStorageCall`, got error" <> show err - pollTransactionReceipt :: forall m a . MonadAff m @@ -104,67 +70,6 @@ pollTransactionReceipt provider txHash k = Succeeded -> k receipt Failed -> unsafeCrashWith $ "Transaction failed : " <> show txHash -hangOutTillBlock - :: forall m - . MonadAff m - => Provider - -> Logger m - -> BlockNumber - -> m Unit -hangOutTillBlock provider logger bn = do - bn' <- assertWeb3 provider Api.eth_blockNumber - logger $ "Current block number : " <> show bn' - when (bn' < bn) do - liftAff $ delay (Milliseconds 1000.0) - hangOutTillBlock provider logger bn - -awaitNextBlock - :: forall m - . MonadAff m - => Provider - -> Logger m - -> m Unit -awaitNextBlock provider logger = do - n <- assertWeb3 provider Api.eth_blockNumber - let - next = wrap $ embed 1 + unwrap n - logger $ "Awaiting block number " <> show next - hangOutTillBlock provider logger next - -type ContractConfig = - { contractAddress :: Address - , userAddress :: Address - } - -deployContract - :: forall m - . MonadAff m - => Provider - -> Logger m - -> String - -> (TransactionOptions NoPay -> Web3 HexString) - -> m ContractConfig -deployContract p logger contractName deploymentTx = do - userAddress <- - assertWeb3 p - $ do - accounts <- Api.eth_getAccounts - pure $ unsafePartial fromJust $ accounts !! 0 - txHash <- - assertWeb3 p do - let - txOpts = defaultTestTxOptions # _from ?~ userAddress - txHash <- deploymentTx txOpts - pure txHash - logger $ "Submitted " <> contractName <> " deployment : " <> show txHash - let - k (TransactionReceipt rec) = case rec.contractAddress of - Nothing -> unsafeCrashWith "Contract deployment missing contractAddress in receipt" - Just addr -> pure addr - contractAddress <- pollTransactionReceipt p txHash k - logger $ contractName <> " successfully deployed to " <> show contractAddress - pure $ { contractAddress, userAddress } - joinWeb3Fork :: forall a m . MonadAff m @@ -177,35 +82,6 @@ joinWeb3Fork fiber = Left e -> unsafeCrashWith $ "Error in forked web3 process " <> show e Right a -> pure a -mkHexString' - :: String - -> HexString -mkHexString' hx = unsafePartial fromJust $ mkHexString hx - -mkUIntN - :: forall n - . KnownSize n - => Proxy n - -> Int - -> UIntN n -mkUIntN p n = unsafePartial fromJust $ uIntNFromBigNumber p $ embed n - -mkIntN - :: forall n - . KnownSize n - => Proxy n - -> Int - -> IntN n -mkIntN p n = unsafePartial fromJust $ intNFromBigNumber p $ embed n - -mkBytesN - :: forall n - . KnownSize n - => Proxy n - -> String - -> BytesN n -mkBytesN p s = unsafePartial fromJust $ fromByteString p =<< flip BS.fromString BS.Hex s - defaultTestTxOptions :: TransactionOptions NoPay defaultTestTxOptions = defaultTransactionOptions # _gas ?~ bigGasLimit @@ -213,4 +89,4 @@ nullAddress :: Address nullAddress = unsafePartial $ fromJust $ mkAddress =<< mkHexString "0000000000000000000000000000000000000000" bigGasLimit :: BigNumber -bigGasLimit = unsafePartial fromJust $ parseBigNumber decimal "4712388" +bigGasLimit = unsafePartial fromJust $ fromStringAs decimal "4712388" diff --git a/test/web3/Web3Spec/Types/EtherUnitSpec.purs b/test/web3/Web3Spec/Types/EtherUnitSpec.purs index 9871d9d..cbb862f 100644 --- a/test/web3/Web3Spec/Types/EtherUnitSpec.purs +++ b/test/web3/Web3Spec/Types/EtherUnitSpec.purs @@ -1,47 +1,61 @@ module Web3Spec.Types.EtherUnitSpec (spec) where import Prelude + +import Control.Apply (lift2, lift3) import Data.Lens ((.~), (^.)) import Data.Maybe (Maybe(..)) -import Data.Ring.Module (mzeroL, (^*), (^+), (^-)) +import Data.Ring.Module (class LeftModule, mzeroL, (^*), (^+), (^-)) +import Data.Tuple (Tuple(..)) +import Effect (Effect) +import Effect.Class (liftEffect) +import Effect.Class.Console (log) import Network.Ethereum.Core.BigNumber (pow) -import Network.Ethereum.Web3 (Ether, Shannon, Szabo, Value, Wei, _value, convert, defaultTransactionOptions, embed, fromMinorUnit, mkValue, formatValue) +import Network.Ethereum.Web3 (Shannon, Szabo, Value, Wei, Ether, _value, convert, defaultTransactionOptions, formatValue, fromInt, fromMinorUnit, mkValue) +import Network.Ethereum.Web3.Types.TokenUnit as Value +import Test.QuickCheck (arbitrary, quickCheck', (===)) +import Test.QuickCheck.Gen (Gen, chooseInt) +import Test.QuickCheck.Laws (checkLaws) +import Test.QuickCheck.Laws.Data as Data import Test.Spec (Spec, describe, it) import Test.Spec.Assertions (shouldEqual) +import Type.Proxy (Proxy(..)) spec :: Spec Unit -spec = +spec = describe "ether unit spec" do describe "conversion tests" do - it "can encode convert from a higher denomination to lower" - $ do - let - inEth = convert (mkValue one :: Value Ether) - - inWei = (mkValue $ (embed 10) `pow` 18) :: Value Wei - inEth `shouldEqual` inWei - let - shannon = mkValue (embed 10 `pow` 3) :: Value Shannon - - szabo = mkValue one :: Value Szabo - convert shannon `shouldEqual` szabo + it "can encode convert from a higher denomination to lower" do + let + inEth = convert (mkValue one :: Value Ether) + + inWei = (mkValue $ (fromInt 10) `pow` 18) :: Value Wei + inEth `shouldEqual` inWei + let + shannon = mkValue (fromInt 10 `pow` 3) :: Value Shannon + + szabo = mkValue one :: Value Szabo + convert shannon `shouldEqual` szabo + it "can perform arithmetic" do let - two = mkValue (embed 1 + embed 1) :: Value Shannon + two = mkValue (fromInt 1 + fromInt 1) :: Value Shannon two' = mkValue one ^+ mkValue one two `shouldEqual` two' (two ^- two') `shouldEqual` mzeroL - (2 ^* two') `shouldEqual` mkValue (embed 4) + (2 ^* two') `shouldEqual` mkValue (fromInt 4) + it "can use the lens properly" do let noPay = defaultTransactionOptions opts = defaultTransactionOptions # _value .~ Just (convert (mkValue one :: Value Ether)) (noPay ^. _value) `shouldEqual` Nothing - (opts ^. _value) `shouldEqual` (Just (fromMinorUnit (embed 10 `pow` 18) :: Value Wei)) + (opts ^. _value) `shouldEqual` (Just (fromMinorUnit (fromInt 10 `pow` 18) :: Value Wei)) + it "can format currencies correctly" do let - n = mkValue (embed 1) :: Value Ether + n = mkValue (fromInt 1) :: Value Ether m = convert n :: Value Wei @@ -50,3 +64,43 @@ spec = formatValue n `shouldEqual` "1" formatValue n' `shouldEqual` "1" formatValue m `shouldEqual` "1000000000000000000" + + describe "laws" do + it "satisfies basic laws" $ liftEffect $ checkLaws "Value Ether" $ do + Data.checkEqGen $ Value.generator (Proxy @Ether) + Data.checkOrdGen $ Value.generator (Proxy @Ether) + Data.checkSemigroupGen $ Value.generator (Proxy @Ether) + Data.checkMonoidGen $ Value.generator (Proxy @Ether) + checkLeftModuleGen arbitrary smallIntsGen (Value.generator (Proxy @Ether)) + +checkLeftModuleGen + :: forall r m + . LeftModule m r + => Eq m + => Show m + => Gen r + -- need this because of overflow (see smallIntsGen) + -> Gen (Tuple r r) + -> Gen m + -> Effect Unit +checkLeftModuleGen genR genRR genM = do + log "Checking 'Distributivity1' law for LeftModule" + quickCheck' 1000 $ lift3 distributivity1 genR genM genM + log "Checking 'Distributivity2' law for LeftModule" + quickCheck' 1000 $ lift3 distributivity2 genR genR genM + log "Checking 'Compatibility' law for LeftModule" + quickCheck' 1000 $ lift2 compatibility genRR genM + log "Checking 'identity' law for LeftModule" + quickCheck' 1000 $ (_identity <$> genM) + + where + distributivity1 r x y = r ^* (x ^+ y) === r ^* x ^+ r ^* y + distributivity2 r s x = (r + s) ^* x === r ^* x ^+ s ^* x + compatibility (Tuple r s) x = (r * s) ^* x === r ^* (s ^* x) + _identity x = one ^* x === x + +smallIntsGen :: Gen (Tuple Int Int) +smallIntsGen = do + n1 <- arbitrary + n2 <- chooseInt 0 (top `div` n1) + pure $ Tuple n1 n2 \ No newline at end of file diff --git a/test/web3/Web3Spec/Types/VectorSpec.purs b/test/web3/Web3Spec/Types/VectorSpec.purs index 8890d9d..8a56c94 100644 --- a/test/web3/Web3Spec/Types/VectorSpec.purs +++ b/test/web3/Web3Spec/Types/VectorSpec.purs @@ -67,27 +67,6 @@ test8 :: Vector 100000000000001 Int -- test8 :: Vector _ Int test8 = 1 :< 1 :< vec99999999999999 --- todo: delete this? --- test10 :: forall (n :: Int). Add n 1 10 => Vector n Int -> Vector 10 Int --- test10 l = 2 :< l - --- test10_ :: Vector _ Int --- test10_ :: Vector 10 Int --- test10_ = test10 vec9 - --- todo: delete this? --- test11 :: forall n. Add n 1 0 => Vector n Int -> Vector 0 Int --- test11 l = 2 :< l - --- As expected `test11` can be written, but can't be called --- test11_ = test11 nilVector --- we can write uncons like this, but when it's used see `test12` if you --- remove type annotation code will fail to compile. if inc and all --- classes which it's using had reverse functional dependencies --- then compiler could potentially infer type, but we don't have --- such implementation for `Inc` and even with such version [1] --- compiler still gives horrible error --- https://gist.github.com/safareli/e1d3805a48a0a772d72ed895945c3607#file-digitswithsupperclass-purs-L38-L103 vUncons :: forall a n nDec. Add nDec 1 n => Vector n a -> { head :: a, tail :: Vector nDec a } vUncons as = case uncons $ unsafeCoerce as of Nothing -> unsafeCrashWith "impossible case in vUncons from Network.Ethereum.Web3.Solidity.Vector"