11module EVM.Transaction where
22
3- import EVM (initialContract , ceilDiv )
3+ import EVM (initialContract , ceilDiv , collision )
44import EVM.Expr qualified as Expr
55import EVM.FeeSchedule
66import EVM.Format (hexText )
@@ -12,6 +12,7 @@ import Optics.Core hiding (cons)
1212
1313import Data.Aeson (FromJSON (.. ))
1414import Data.Aeson qualified as JSON
15+ import Data.Function (applyWhen )
1516import Data.Aeson.Types qualified as JSON
1617import Data.ByteString (ByteString , cons )
1718import Data.ByteString qualified as BS
@@ -34,10 +35,14 @@ data TxType
3435 = LegacyTransaction
3536 | AccessListTransaction
3637 | EIP1559Transaction
38+ | EIP4844Transaction
39+ | EIP7702Transaction
3740 deriving (Show , Eq , Generic )
3841
3942instance JSON. ToJSON TxType where
4043 toJSON t = case t of
44+ EIP7702Transaction -> " 0x4" -- permanently sets the code for an EOA
45+ EIP4844Transaction -> " 0x3" -- Proto-Danksharding
4146 EIP1559Transaction -> " 0x2" -- EIP1559
4247 LegacyTransaction -> " 0x1" -- EIP2718
4348 AccessListTransaction -> " 0x1" -- EIP2930
@@ -124,6 +129,8 @@ signingData tx =
124129 else normalData
125130 AccessListTransaction -> eip2930Data
126131 EIP1559Transaction -> eip1559Data
132+ EIP4844Transaction -> eip4844Data
133+ EIP7702Transaction -> eip7702Data
127134 where v = tx. v
128135 to' = case tx. toAddr of
129136 Just a -> BS $ word160Bytes a
@@ -166,7 +173,6 @@ signingData tx =
166173 , BS tx. txdata
167174 , rlpAccessList
168175 ]
169-
170176 eip2930Data = cons 0x01 $ rlpList
171177 [ rlpWord256 tx. chainId
172178 , rlpWord256 tx. nonce
@@ -177,6 +183,31 @@ signingData tx =
177183 , BS tx. txdata
178184 , rlpAccessList
179185 ]
186+ eip4844Data = cons 0x03 $ rlpList
187+ [ rlpWord256 tx. chainId
188+ , rlpWord256 tx. nonce
189+ , rlpWord256 maxPrio
190+ , rlpWord256 maxFee
191+ , rlpWord256 (into tx. gasLimit)
192+ , to'
193+ , rlpWord256 tx. value
194+ , BS tx. txdata
195+ , rlpAccessList
196+ , rlpWord256 $ undefined -- TODO EIP4844 max_fee_per_blob_gas
197+ , undefined -- TODO EIP4844 blob_versioned_hashes
198+ ]
199+ eip7702Data = cons 0x04 $ rlpList
200+ [ rlpWord256 tx. chainId
201+ , rlpWord256 tx. nonce
202+ , rlpWord256 maxPrio
203+ , rlpWord256 maxFee
204+ , rlpWord256 (into tx. gasLimit)
205+ , to'
206+ , rlpWord256 tx. value
207+ , BS tx. txdata
208+ , rlpAccessList
209+ , undefined -- TODO EIP4844 authorization_list
210+ ]
180211
181212accessListPrice :: FeeSchedule Word64 -> [AccessListEntry ] -> Word64
182213accessListPrice fs al =
@@ -220,7 +251,7 @@ instance FromJSON Transaction where
220251 toAddr <- addrFieldMaybe val " to"
221252 v <- wordField val " v"
222253 value <- wordField val " value"
223- txType <- fmap (read :: String -> Int ) <$> ( val JSON. .:? " type" )
254+ txType <- fmap (read :: String -> Int ) <$> val JSON. .:? " type"
224255 case txType of
225256 Just 0x00 -> pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value LegacyTransaction [] Nothing Nothing 1
226257 Just 0x01 -> do
@@ -229,6 +260,14 @@ instance FromJSON Transaction where
229260 Just 0x02 -> do
230261 accessListEntries <- (val JSON. .: " accessList" ) >>= parseJSONList
231262 pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP1559Transaction accessListEntries maxPrio maxFee 1
263+ Just 0x03 -> do
264+ accessListEntries <- (val JSON. .: " accessList" ) >>= parseJSONList
265+ -- TODO: capture max_fee_per_blob_gas and blob_versioned_hashes EIP4844
266+ pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP4844Transaction accessListEntries maxPrio maxFee 1
267+ Just 0x04 -> do
268+ accessListEntries <- (val JSON. .: " accessList" ) >>= parseJSONList
269+ -- TODO: capture authorization_list EIP7702
270+ pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value EIP7702Transaction accessListEntries maxPrio maxFee 1
232271 Just _ -> fail " unrecognized custom transaction type"
233272 Nothing -> pure $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value LegacyTransaction [] Nothing Nothing 1
234273 parseJSON invalid =
@@ -268,13 +307,20 @@ initTx vm =
268307 preState = setupTx origin coinbase gasPrice gasLimit vm. env. contracts
269308 oldBalance = view (accountAt toAddr % # balance) preState
270309 creation = vm. tx. isCreate
271- initState =
272- ((Map. adjust (over # balance (`Expr.sub` value))) origin)
273- . (Map. adjust (over # balance (Expr. add value))) toAddr
274- . (if creation
275- then Map. insert toAddr (toContract & (set # balance oldBalance))
276- else touchAccount toAddr)
277- $ preState
310+ -- Check for collision at target address for CREATE transactions
311+ hasCollision = creation && collision (Map. lookup toAddr preState)
312+ -- For collision: don't transfer value, don't create contract
313+ initState = if hasCollision
314+ then touchAccount toAddr preState
315+ else ((Map. adjust (over # balance (`Expr.sub` value))) origin)
316+ . (Map. adjust (over # balance (Expr. add value))) toAddr
317+ . (if creation
318+ then Map. insert toAddr (toContract & (set # balance oldBalance))
319+ else touchAccount toAddr)
320+ $ preState
278321 in
279322 vm & # env % # contracts .~ initState
280323 & # tx % # txReversion .~ preState
324+ -- For collision: set code to empty so exec1 immediately stops and calls finalize
325+ -- (don't set #result directly, as that bypasses finalize which handles gas payment)
326+ & applyWhen hasCollision (# state % # code .~ RuntimeCode (ConcreteRuntimeCode " " ))
0 commit comments