Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hevm.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ library
EVM.Solvers,
EVM.Exec,
EVM.Format,
EVM.ConsoleLog,
EVM.Fetch,
EVM.FeeSchedule,
EVM.Op,
Expand Down
11 changes: 11 additions & 0 deletions src/EVM.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1858,6 +1858,11 @@ accessStorageForGas addr key = do
cheatCode :: Expr EAddr
cheatCode = LitAddr $ unsafeInto (keccak' "hevm cheat code")

-- | The address used by Foundry/Hardhat console.log
-- 0x000000000000000000636F6e736F6c652e6c6f67 (ASCII "console.log")
consoleAddr :: Expr EAddr
consoleAddr = LitAddr 0x000000000000000000636F6e736F6c652e6c6f67

cheat
:: forall t . (?conf :: Config, ?op :: Word8, VMOps t, Typeable t)
=> Gas t -> (Expr EWord, Expr EWord) -> (Expr EWord, Expr EWord) -> [Expr EWord]
Expand Down Expand Up @@ -2249,6 +2254,12 @@ delegateCall this gasGiven xTo xContext xValue xInOffset xInSize xOutOffset xOut
precompiledContract this gasGiven xTo' xContext' xValue xInOffset xInSize xOutOffset xOutSize xs
| xTo == cheatCode = do
cheat gasGiven (xInOffset, xInSize) (xOutOffset, xOutSize) xs
| xTo == consoleAddr = do
calldata <- readMemory xInOffset xInSize
pushTrace $ ConsoleLog calldata
assign' (#state % #stack) (Lit 1 : xs)
assign (#state % #returndata) mempty
next
| otherwise =
callChecks this gasGiven xContext xTo xValue xInOffset xInSize xOutOffset xOutSize xs $
\xGas -> do
Expand Down
189 changes: 189 additions & 0 deletions src/EVM/ConsoleLog.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
module EVM.ConsoleLog
( formatConsoleLog
) where

import Data.ByteString (ByteString)
import Data.ByteString qualified as BS
import Data.ByteString.Char8 qualified as Char8
import Data.ByteString.Builder (byteStringHex, toLazyByteString)
import Data.ByteString.Lazy (toStrict)
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Text (Text, pack, intercalate)
import Data.Text qualified as T
import Data.Text.Encoding qualified as T

import EVM.ABI (AbiType(..), AbiValue(..), decodeBuf, AbiVals(..))
import EVM.Types (Expr(..), EType(..), FunctionSelector(..), abiKeccak, word32)

-- | Try to decode and format a console.log calldata buffer.
-- Returns a human-readable representation of the log message.
formatConsoleLog :: Expr Buf -> Text
formatConsoleLog (ConcreteBuf bs) = formatConsoleLogBS bs
formatConsoleLog _ = "<symbolic console.log>"

formatConsoleLogBS :: ByteString -> Text
formatConsoleLogBS bs
| BS.length bs < 4 = "console::log()"
| otherwise =
let selector = FunctionSelector (word32 (BS.unpack (BS.take 4 bs)))
payload = BS.drop 4 bs
in case Map.lookup selector consoleLogSigs of
Just (_, types) ->
case decodeBuf types (ConcreteBuf payload) of
(CAbi vals, _) -> "console::log(" <> intercalate ", " (map showAbiVal vals) <> ")"
_ -> "console::log(" <> hexBS bs <> ")"
Nothing -> "console::log(" <> hexBS bs <> ")"
where
hexBS b = "0x" <> T.decodeUtf8 (toStrict (toLazyByteString (byteStringHex b)))

-- | Format an ABI value for console output
showAbiVal :: AbiValue -> Text
showAbiVal (AbiString s) = T.pack (show (Char8.unpack s))
showAbiVal (AbiAddress addr) = pack (show addr)
showAbiVal (AbiBool b) = if b then "true" else "false"
showAbiVal v = pack (show v)

-- | Map from 4-byte selector to (display name, parameter types) for all
-- console.log overloads.
consoleLogSigs :: Map FunctionSelector (Text, [AbiType])
consoleLogSigs = Map.fromList
[ sig "log()" []
-- single param
, sig "log(bool)" [AbiBoolType]
, sig "log(address)" [AbiAddressType]
, sig "log(uint256)" [AbiUIntType 256]
, sig "log(int256)" [AbiIntType 256]
, sig "log(string)" [AbiStringType]
, sig "log(bytes)" [AbiBytesDynamicType]
-- two params
, sig "log(bool,bool)" [AbiBoolType, AbiBoolType]
, sig "log(bool,address)" [AbiBoolType, AbiAddressType]
, sig "log(bool,uint256)" [AbiBoolType, AbiUIntType 256]
, sig "log(bool,string)" [AbiBoolType, AbiStringType]
, sig "log(address,bool)" [AbiAddressType, AbiBoolType]
, sig "log(address,address)" [AbiAddressType, AbiAddressType]
, sig "log(address,uint256)" [AbiAddressType, AbiUIntType 256]
, sig "log(address,string)" [AbiAddressType, AbiStringType]
, sig "log(uint256,bool)" [AbiUIntType 256, AbiBoolType]
, sig "log(uint256,address)" [AbiUIntType 256, AbiAddressType]
, sig "log(uint256,uint256)" [AbiUIntType 256, AbiUIntType 256]
, sig "log(uint256,string)" [AbiUIntType 256, AbiStringType]
, sig "log(string,bool)" [AbiStringType, AbiBoolType]
, sig "log(string,address)" [AbiStringType, AbiAddressType]
, sig "log(string,uint256)" [AbiStringType, AbiUIntType 256]
, sig "log(string,string)" [AbiStringType, AbiStringType]
, sig "log(string,int256)" [AbiStringType, AbiIntType 256]
-- three params
, sig "log(bool,bool,bool)" [AbiBoolType, AbiBoolType, AbiBoolType]
, sig "log(bool,bool,address)" [AbiBoolType, AbiBoolType, AbiAddressType]
, sig "log(bool,bool,uint256)" [AbiBoolType, AbiBoolType, AbiUIntType 256]
, sig "log(bool,bool,string)" [AbiBoolType, AbiBoolType, AbiStringType]
, sig "log(bool,address,bool)" [AbiBoolType, AbiAddressType, AbiBoolType]
, sig "log(bool,address,address)" [AbiBoolType, AbiAddressType, AbiAddressType]
, sig "log(bool,address,uint256)" [AbiBoolType, AbiAddressType, AbiUIntType 256]
, sig "log(bool,address,string)" [AbiBoolType, AbiAddressType, AbiStringType]
, sig "log(bool,uint256,bool)" [AbiBoolType, AbiUIntType 256, AbiBoolType]
, sig "log(bool,uint256,address)" [AbiBoolType, AbiUIntType 256, AbiAddressType]
, sig "log(bool,uint256,uint256)" [AbiBoolType, AbiUIntType 256, AbiUIntType 256]
, sig "log(bool,uint256,string)" [AbiBoolType, AbiUIntType 256, AbiStringType]
, sig "log(bool,string,bool)" [AbiBoolType, AbiStringType, AbiBoolType]
, sig "log(bool,string,address)" [AbiBoolType, AbiStringType, AbiAddressType]
, sig "log(bool,string,uint256)" [AbiBoolType, AbiStringType, AbiUIntType 256]
, sig "log(bool,string,string)" [AbiBoolType, AbiStringType, AbiStringType]
, sig "log(address,bool,bool)" [AbiAddressType, AbiBoolType, AbiBoolType]
, sig "log(address,bool,address)" [AbiAddressType, AbiBoolType, AbiAddressType]
, sig "log(address,bool,uint256)" [AbiAddressType, AbiBoolType, AbiUIntType 256]
, sig "log(address,bool,string)" [AbiAddressType, AbiBoolType, AbiStringType]
, sig "log(address,address,bool)" [AbiAddressType, AbiAddressType, AbiBoolType]
, sig "log(address,address,address)" [AbiAddressType, AbiAddressType, AbiAddressType]
, sig "log(address,address,uint256)" [AbiAddressType, AbiAddressType, AbiUIntType 256]
, sig "log(address,address,string)" [AbiAddressType, AbiAddressType, AbiStringType]
, sig "log(address,uint256,bool)" [AbiAddressType, AbiUIntType 256, AbiBoolType]
, sig "log(address,uint256,address)" [AbiAddressType, AbiUIntType 256, AbiAddressType]
, sig "log(address,uint256,uint256)" [AbiAddressType, AbiUIntType 256, AbiUIntType 256]
, sig "log(address,uint256,string)" [AbiAddressType, AbiUIntType 256, AbiStringType]
, sig "log(address,string,bool)" [AbiAddressType, AbiStringType, AbiBoolType]
, sig "log(address,string,address)" [AbiAddressType, AbiStringType, AbiAddressType]
, sig "log(address,string,uint256)" [AbiAddressType, AbiStringType, AbiUIntType 256]
, sig "log(address,string,string)" [AbiAddressType, AbiStringType, AbiStringType]
, sig "log(uint256,bool,bool)" [AbiUIntType 256, AbiBoolType, AbiBoolType]
, sig "log(uint256,bool,address)" [AbiUIntType 256, AbiBoolType, AbiAddressType]
, sig "log(uint256,bool,uint256)" [AbiUIntType 256, AbiBoolType, AbiUIntType 256]
, sig "log(uint256,bool,string)" [AbiUIntType 256, AbiBoolType, AbiStringType]
, sig "log(uint256,address,bool)" [AbiUIntType 256, AbiAddressType, AbiBoolType]
, sig "log(uint256,address,address)" [AbiUIntType 256, AbiAddressType, AbiAddressType]
, sig "log(uint256,address,uint256)" [AbiUIntType 256, AbiAddressType, AbiUIntType 256]
, sig "log(uint256,address,string)" [AbiUIntType 256, AbiAddressType, AbiStringType]
, sig "log(uint256,uint256,bool)" [AbiUIntType 256, AbiUIntType 256, AbiBoolType]
, sig "log(uint256,uint256,address)" [AbiUIntType 256, AbiUIntType 256, AbiAddressType]
, sig "log(uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256]
, sig "log(uint256,uint256,string)" [AbiUIntType 256, AbiUIntType 256, AbiStringType]
, sig "log(uint256,string,bool)" [AbiUIntType 256, AbiStringType, AbiBoolType]
, sig "log(uint256,string,address)" [AbiUIntType 256, AbiStringType, AbiAddressType]
, sig "log(uint256,string,uint256)" [AbiUIntType 256, AbiStringType, AbiUIntType 256]
, sig "log(uint256,string,string)" [AbiUIntType 256, AbiStringType, AbiStringType]
, sig "log(string,bool,bool)" [AbiStringType, AbiBoolType, AbiBoolType]
, sig "log(string,bool,address)" [AbiStringType, AbiBoolType, AbiAddressType]
, sig "log(string,bool,uint256)" [AbiStringType, AbiBoolType, AbiUIntType 256]
, sig "log(string,bool,string)" [AbiStringType, AbiBoolType, AbiStringType]
, sig "log(string,address,bool)" [AbiStringType, AbiAddressType, AbiBoolType]
, sig "log(string,address,address)" [AbiStringType, AbiAddressType, AbiAddressType]
, sig "log(string,address,uint256)" [AbiStringType, AbiAddressType, AbiUIntType 256]
, sig "log(string,address,string)" [AbiStringType, AbiAddressType, AbiStringType]
, sig "log(string,uint256,bool)" [AbiStringType, AbiUIntType 256, AbiBoolType]
, sig "log(string,uint256,address)" [AbiStringType, AbiUIntType 256, AbiAddressType]
, sig "log(string,uint256,uint256)" [AbiStringType, AbiUIntType 256, AbiUIntType 256]
, sig "log(string,uint256,string)" [AbiStringType, AbiUIntType 256, AbiStringType]
, sig "log(string,string,bool)" [AbiStringType, AbiStringType, AbiBoolType]
, sig "log(string,string,address)" [AbiStringType, AbiStringType, AbiAddressType]
, sig "log(string,string,uint256)" [AbiStringType, AbiStringType, AbiUIntType 256]
, sig "log(string,string,string)" [AbiStringType, AbiStringType, AbiStringType]
-- four params (most common combinations)
, sig "log(bool,bool,bool,bool)" [AbiBoolType, AbiBoolType, AbiBoolType, AbiBoolType]
, sig "log(bool,bool,bool,address)" [AbiBoolType, AbiBoolType, AbiBoolType, AbiAddressType]
, sig "log(bool,bool,bool,uint256)" [AbiBoolType, AbiBoolType, AbiBoolType, AbiUIntType 256]
, sig "log(bool,bool,bool,string)" [AbiBoolType, AbiBoolType, AbiBoolType, AbiStringType]
, sig "log(address,address,address,address)" [AbiAddressType, AbiAddressType, AbiAddressType, AbiAddressType]
, sig "log(address,address,address,uint256)" [AbiAddressType, AbiAddressType, AbiAddressType, AbiUIntType 256]
, sig "log(address,address,address,string)" [AbiAddressType, AbiAddressType, AbiAddressType, AbiStringType]
, sig "log(uint256,uint256,uint256,uint256)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256, AbiUIntType 256]
, sig "log(uint256,uint256,uint256,string)" [AbiUIntType 256, AbiUIntType 256, AbiUIntType 256, AbiStringType]
, sig "log(string,string,string,string)" [AbiStringType, AbiStringType, AbiStringType, AbiStringType]
, sig "log(string,string,string,uint256)" [AbiStringType, AbiStringType, AbiStringType, AbiUIntType 256]
, sig "log(string,string,string,address)" [AbiStringType, AbiStringType, AbiStringType, AbiAddressType]
, sig "log(string,string,string,bool)" [AbiStringType, AbiStringType, AbiStringType, AbiBoolType]
, sig "log(string,uint256,uint256,uint256)" [AbiStringType, AbiUIntType 256, AbiUIntType 256, AbiUIntType 256]
, sig "log(string,address,address,address)" [AbiStringType, AbiAddressType, AbiAddressType, AbiAddressType]
, sig "log(string,bool,bool,bool)" [AbiStringType, AbiBoolType, AbiBoolType, AbiBoolType]
, sig "log(string,string,uint256,uint256)" [AbiStringType, AbiStringType, AbiUIntType 256, AbiUIntType 256]
, sig "log(string,uint256,string,uint256)" [AbiStringType, AbiUIntType 256, AbiStringType, AbiUIntType 256]
, sig "log(string,address,uint256,uint256)" [AbiStringType, AbiAddressType, AbiUIntType 256, AbiUIntType 256]
, sig "log(string,uint256,address,uint256)" [AbiStringType, AbiUIntType 256, AbiAddressType, AbiUIntType 256]
, sig "log(string,bool,string,string)" [AbiStringType, AbiBoolType, AbiStringType, AbiStringType]
, sig "log(string,string,address,address)" [AbiStringType, AbiStringType, AbiAddressType, AbiAddressType]
, sig "log(string,address,string,address)" [AbiStringType, AbiAddressType, AbiStringType, AbiAddressType]
, sig "log(string,address,address,string)" [AbiStringType, AbiAddressType, AbiAddressType, AbiStringType]
, sig "log(string,uint256,uint256,string)" [AbiStringType, AbiUIntType 256, AbiUIntType 256, AbiStringType]
, sig "log(string,uint256,string,string)" [AbiStringType, AbiUIntType 256, AbiStringType, AbiStringType]
, sig "log(string,string,uint256,string)" [AbiStringType, AbiStringType, AbiUIntType 256, AbiStringType]
, sig "log(string,address,uint256,string)" [AbiStringType, AbiAddressType, AbiUIntType 256, AbiStringType]
, sig "log(string,uint256,address,string)" [AbiStringType, AbiUIntType 256, AbiAddressType, AbiStringType]
, sig "log(string,address,string,uint256)" [AbiStringType, AbiAddressType, AbiStringType, AbiUIntType 256]
, sig "log(string,string,address,uint256)" [AbiStringType, AbiStringType, AbiAddressType, AbiUIntType 256]
, sig "log(string,address,string,string)" [AbiStringType, AbiAddressType, AbiStringType, AbiStringType]
, sig "log(string,bool,address,address)" [AbiStringType, AbiBoolType, AbiAddressType, AbiAddressType]
, sig "log(string,bool,uint256,uint256)" [AbiStringType, AbiBoolType, AbiUIntType 256, AbiUIntType 256]
, sig "log(string,bool,string,uint256)" [AbiStringType, AbiBoolType, AbiStringType, AbiUIntType 256]
, sig "log(string,bool,uint256,string)" [AbiStringType, AbiBoolType, AbiUIntType 256, AbiStringType]
, sig "log(string,bool,address,string)" [AbiStringType, AbiBoolType, AbiAddressType, AbiStringType]
, sig "log(string,bool,string,address)" [AbiStringType, AbiBoolType, AbiStringType, AbiAddressType]
, sig "log(string,bool,uint256,address)" [AbiStringType, AbiBoolType, AbiUIntType 256, AbiAddressType]
, sig "log(string,bool,address,uint256)" [AbiStringType, AbiBoolType, AbiAddressType, AbiUIntType 256]
, sig "log(string,bool,bool,string)" [AbiStringType, AbiBoolType, AbiBoolType, AbiStringType]
, sig "log(string,bool,bool,address)" [AbiStringType, AbiBoolType, AbiBoolType, AbiAddressType]
, sig "log(string,bool,bool,uint256)" [AbiStringType, AbiBoolType, AbiBoolType, AbiUIntType 256]
]
where
sig :: ByteString -> [AbiType] -> (FunctionSelector, (Text, [AbiType]))
sig s types = (abiKeccak s, (pack (Char8.unpack s), types))
3 changes: 3 additions & 0 deletions src/EVM/Format.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import Prelude hiding (LT, GT)

import EVM (traceForest, traceForest', traceContext, cheatCode)
import EVM.ABI (getAbiSeq, parseTypeName, AbiValue(..), AbiType(..), SolError(..), Indexed(..), Event(..))
import EVM.ConsoleLog (formatConsoleLog)
import EVM.Dapp (DappContext(..), DappInfo(..), findSrc, showTraceLocation)
import EVM.Expr qualified as Expr
import EVM.Solidity (SolcContract(..), Method(..))
Expand Down Expand Up @@ -328,6 +329,8 @@ showTrace trace =
ReturnTrace out (CreationContext {}) ->
let l = Expr.bufLength out
in "← " <> formatExpr l <> " bytes of code"
ConsoleLog buf ->
"\x1b[36m" <> formatConsoleLog buf <> "\x1b[0m"
EntryTrace t ->
t
FrameTrace (CreationContext { address }) ->
Expand Down
1 change: 1 addition & 0 deletions src/EVM/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,7 @@ data TraceData
| ErrorTrace EvmError
| EntryTrace Text
| ReturnTrace (Expr Buf) FrameContext
| ConsoleLog (Expr Buf)
deriving (Eq, Ord, Show, Generic)

-- | Wrapper type containing vm traces and the context needed to pretty print them properly
Expand Down
Loading
Loading