intrepidshape/elm-web3/3.0.0

Web3.Error

The one error type every failure at the JS boundary decodes into.

Before this module existed the bridge collapsed every failure into a string: EIP-1193 code 4001 became a rejected tag and everything else became failed: <message>. err.code never crossed the boundary, so 4902 (the chain is not in the wallet) was indistinguishable from a dropped connection, and the standard switchChain -> addChain -> retry flow could not be written at all. -32002 ("already processing a request") was only recoverable inside connect, and a revert was a substring match away from a network blip.

Now the shim forwards the numeric code, the wallet message, and the raw revert payload, and this module turns those three fields into a value you can case on:

case walletError of
    Web3.Error.ChainNotAdded ->
        -- the retry flow the old string-typed channel made impossible
        ( model, web3Cmd (Wallet.encode (Wallet.addChain pulseChain)) )


    Web3.Error.RequestPending ->
        ( { model | toast = Just "Check your wallet" }, Cmd.none )


    Web3.Error.UserRejected ->
        ( model, Cmd.none )


    other ->
        ( { model | banner = Just (Web3.Error.toString other) }, Cmd.none )

The wire shape this decodes is the failure payload the shim emits:

{ tag: "failed", error: "execution reverted", code: -32000, revertData: "0x08c3.." }
type Error
= UserRejected
| RequestPending
| ChainNotAdded
| RpcError Int String
| Reverted
{ reason : Maybe String
, data : Maybe String
}
| Panic Int
| DecodeError String
| NetworkError String

Every way a request across the JS boundary can fail.

  • UserRejected -- EIP-1193 4001. The user dismissed the wallet prompt. Expected, not exceptional: do not show it as an error.
  • RequestPending -- EIP-1193 -32002. A request is already open in the wallet; the user has to go look at it. Retrying just queues another one.
  • ChainNotAdded -- EIP-1193 4902. The wallet does not know this chain, so wallet_switchEthereumChain cannot switch to it. Send addChain then switchChain again.
  • RpcError codeNumber message -- any other JSON-RPC or provider error, with the code preserved so a caller can match on codes this library has no opinion about.
  • Reverted { reason, data } -- the transaction (or simulation) reverted. reason is the decoded Error(string) payload when there was one; data is the raw revert bytes, which is the only thing a custom error can be resolved against.
  • Panic panicCode -- a Solidity Panic(uint256): 0x11 overflow, 0x12 divide by zero, 0x32 index out of bounds, and so on.
  • DecodeError message -- the boundary produced something Elm could not read (a malformed address, a response that failed its decoder).
  • NetworkError message -- transport-level failure with no code at all.
{ code : Maybe Int
, message : String
, revertData : Maybe String
}
-> Error

Turn the three fields the boundary carries into an Error.

classify { code = Just 4902, message = "Unrecognized chain ID", revertData = Nothing }
--> ChainNotAdded


classify { code = Nothing, message = "Failed to fetch", revertData = Nothing }
--> NetworkError "Failed to fetch"

Revert data wins over a generic provider code, because a wallet reports a revert as its own transport error (-32000, -32603) with the useful part buried in data. A recognised code (4001, -32002, 4902) still wins over revert data -- those three are decisions the user or the wallet made, not something the chain said.

decoder : Decoder Error

Decode a failure message straight off the port.

Tag-aware, because the shim reports the two outcomes the user caused with their own tags rather than as a code: rejected / connectRejected are UserRejected and connectPending is RequestPending. Everything else is read out of the error / code / revertData fields by classify.

D.decodeValue Web3.Error.decoder incoming

Lift a JSON decoding failure into the same channel as everything else, so an application has one error type rather than two.

case D.decodeValue (Call.responseDecoder totalSupply) value of
    Ok supply ->
        ...


    Err jsonErr ->
        handle (Web3.Error.fromDecodeError jsonErr)
toString : Error -> String

A one-line rendering, for a banner or a log. Not for control flow -- that is what the constructors are for.

code : Error -> Maybe Int

The EIP-1193 / JSON-RPC code behind an error, when there was one. Nothing for the failures that never had a code: a revert, a panic, a decode failure, a transport error.