From 55a0ea2d18aae045c9f4398ab06adfe8e3fd1aef Mon Sep 17 00:00:00 2001 From: shum Date: Thu, 22 Oct 2026 01:35:41 +0000 Subject: [PATCH] store: read a revert with a reason as not dataless --- .../src/StoreService/Names/Chain.hs | 11 +++++++++-- tests/Bots/StoreService/NamesChainTests.hs | 5 ++++- tests/Bots/StoreService/NamesContractsTests.hs | 3 +++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/simplex-store-service/src/StoreService/Names/Chain.hs b/apps/simplex-store-service/src/StoreService/Names/Chain.hs index f01ee71a48..cac027ad36 100644 --- a/apps/simplex-store-service/src/StoreService/Names/Chain.hs +++ b/apps/simplex-store-service/src/StoreService/Names/Chain.hs @@ -115,6 +115,9 @@ maxRpcResponseBytes = 1024 * 1024 revertCode :: Int revertCode = 3 +revertMessage :: Text +revertMessage = "execution reverted" + -- | A node's error message is cut to this many characters. maxNodeMessageLength :: Int maxNodeMessageLength = 200 @@ -273,10 +276,14 @@ rpc ChainClient {ccManager, ccConfig = ChainConfig {cRpcUrl}, ccTimeout} method message <- e .: "message" revertData <- e .:? "data" pure $ - if code == revertCode || "execution reverted" `T.isPrefixOf` message - then ChainReverted $ maybe (Just RNWithoutData) (decodeRevert <=< JT.parseMaybe bytes) revertData + if code == revertCode || revertMessage `T.isPrefixOf` message + then ChainReverted $ maybe (dataless message) (decodeRevert <=< JT.parseMaybe bytes) revertData else unavailable $ "error " <> tshow code <> ": " <> T.take maxNodeMessageLength message unavailable reason = ChainUnavailable $ method <> ": " <> reason + -- a node or proxy that drops the data can keep a reason in the message, as in "execution reverted: ERC721: invalid token ID" + dataless message + | message == revertMessage = Just RNWithoutData + | otherwise = Nothing -- | Only the kind of failure, for callers: an exception's text can hold the URL or its host. httpFailure :: HTTP.HttpException -> Text diff --git a/tests/Bots/StoreService/NamesChainTests.hs b/tests/Bots/StoreService/NamesChainTests.hs index 3a3c1f9a54..874460ccbe 100644 --- a/tests/Bots/StoreService/NamesChainTests.hs +++ b/tests/Bots/StoreService/NamesChainTests.hs @@ -31,7 +31,7 @@ namesChainTests = describe "StoreService names: chain" $ do it "reads a dry run's revert with data by its name" $ testDryRunRevertNamed g it "reads an eth_call revert with data by its name" $ testCallRevertNamed g it "reads ownerOf of a name nobody holds as a revert" $ testOwnerOfRevert g - it "reads a revert without code 3 or data as a revert without data" $ testRevertWithoutData g + it "reads a bare revert message without data as a revert without data, and one with a reason as unknown" $ testRevertWithoutData g it "reads a revert of an unknown error as an unnamed revert" $ testUnknownRevert g describe "failures" $ do it "reads an RPC error as unavailable, without the URL" $ testRpcError g @@ -127,6 +127,9 @@ testRevertWithoutData :: TVar ChaChaDRG -> IO () testRevertWithoutData g = withChain g $ \node c -> do setFault node "eth_call" $ RpcError (-32000) "execution reverted" Nothing ethCall c fakeController availableCall `shouldReturn` Left (ChainReverted $ Just RNWithoutData) + -- a reason kept in the message names a revert that had data, so it is not read as one without + setFault node "eth_call" $ RpcError 3 "execution reverted: ERC721: invalid token ID" Nothing + ethCall c fakeController availableCall `shouldReturn` Left (ChainReverted Nothing) testUnknownRevert :: TVar ChaChaDRG -> IO () testUnknownRevert g = withChain g $ \node c -> do diff --git a/tests/Bots/StoreService/NamesContractsTests.hs b/tests/Bots/StoreService/NamesContractsTests.hs index e167eca8b6..7f5167ea58 100644 --- a/tests/Bots/StoreService/NamesContractsTests.hs +++ b/tests/Bots/StoreService/NamesContractsTests.hs @@ -184,6 +184,9 @@ testOwnerOfFailed g = withContracts g $ \node ct -> do -- viem's encodeErrorResult of ReentrantCall() answerCall node (registrarOf node) ownerOfCall $ Reverts $ unhex "37ed32e8" ownerOfToken ct alice `shouldReturn` Left (ChainReverted $ Just RNReentrantCall) + -- a node or proxy that keeps the reason in the message but drops the data + setFault node "eth_call" $ RpcError (-32000) "execution reverted: ERC721: invalid token ID" Nothing + ownerOfToken ct alice `shouldReturn` Left (ChainReverted Nothing) setFault node "eth_call" HttpFailure ownerOfToken ct alice `shouldReturn` Left (ChainUnavailable "eth_call: HTTP 502")