[{"name":"Web3","comment":" Top-level module that composes wallet and transaction state.\n\nThis module bundles `Web3.Wallet.State` and a list of named `Web3.Transaction.Status`\nvalues into a single `Model`. Wire it into your app's model/update/subscriptions\nto get wallet connection, chain validation, and transaction tracking with a single\nport pair.\n\n    port web3Cmd : Json.Encode.Value -> Cmd msg\n    port web3Sub : (Json.Decode.Value -> msg) -> Sub msg\n\n    type alias Model =\n        { web3 : Web3.Model\n        , ...\n        }\n\n    type Msg\n        = Web3Msg Web3.Msg\n        | ...\n\n    init : ( Model, Cmd Msg )\n    init =\n        ( { web3 = Web3.init (Web3.Types.chainId 369), ... }, Cmd.none )\n\n    update msg model =\n        case msg of\n            Web3Msg subMsg ->\n                ( { model | web3 = Web3.update subMsg model.web3 }, Cmd.none )\n\n    subscriptions model =\n        web3Sub (Json.Decode.decodeValue Web3.Wallet.decoder\n                    >> Result.map Web3.WalletMsg\n                    >> Result.withDefault (Web3Msg (Web3.WalletMsg Web3.Wallet.WalletDisconnected))\n                )\n\nFor individual modules without the combined wrapper, import `Web3.Wallet`,\n`Web3.Transaction`, `Web3.Balance`, `Web3.Block`, etc. directly.\n\n@docs Model, Msg\n@docs init, update, subscriptions, encodeTxCmd\n\n","unions":[{"name":"Msg","comment":" Messages for wallet and transaction updates.\n","args":[],"cases":[["WalletMsg",["Web3.Wallet.Msg"]],["TxMsg",["String.String","Web3.Transaction.Msg"]],["TxCmdMsg",["String.String","Web3.Transaction.TxCmd"]]]}],"aliases":[{"name":"Model","comment":" Combined wallet + transaction state.\n","args":[],"type":"{ wallet : Web3.Wallet.State, expectedChain : Web3.Types.ChainId, transactions : List.List ( String.String, Web3.Transaction.Status ) }"}],"values":[{"name":"encodeTxCmd","comment":" Encode a `TxCmd` (e.g. `RequestReceipt`) for sending via your port.\n\n    web3Cmd (Web3.encodeTxCmd (Tx.RequestReceipt hash \"my-id\"))\n\n","type":"Web3.Transaction.TxCmd -> Json.Encode.Value"},{"name":"init","comment":" Initialize with the expected chain ID.\n","type":"Web3.Types.ChainId -> Web3.Model"},{"name":"subscriptions","comment":" Placeholder for port subscriptions. Wire your port decoder here.\n","type":"Web3.Model -> Platform.Sub.Sub Web3.Msg"},{"name":"update","comment":" Update wallet and transaction state from a port message.\n","type":"Web3.Msg -> Web3.Model -> Web3.Model"}],"binops":[]},{"name":"Web3.Abi.Decode","comment":" Helpers to decode contract return values from JSON\nreceived through ports from the JS Web3 layer.\n\n@docs address, uint256, int256, bool, string, bytes32\n@docs uint8, uint16, uint32, uint64, uint128\n@docs hexSlot, uint256Slot, addressSlot, boolSlot, stringSlot, listSlot\n@docs tuple2Hex, tuple3Hex\n@docs decodeRevertReason\n\n","unions":[],"aliases":[],"values":[{"name":"address","comment":" Decode an Address return value.\n","type":"Json.Decode.Decoder Web3.Types.Address"},{"name":"addressSlot","comment":" Decode an Address from slot `n` of a raw 0x-hex ABI response.\nAddresses are right-aligned in the 32-byte slot.\n","type":"Basics.Int -> String.String -> Maybe.Maybe Web3.Types.Address"},{"name":"bool","comment":" Decode a bool return value.\n","type":"Json.Decode.Decoder Basics.Bool"},{"name":"boolSlot","comment":" Decode a bool from slot `n` of a raw 0x-hex ABI response.\n","type":"Basics.Int -> String.String -> Maybe.Maybe Basics.Bool"},{"name":"bytes32","comment":" Decode a bytes32 hex string (e.g. an event topic). Expects a 0x-prefixed\n66-character hex string and returns it as-is.\n","type":"Json.Decode.Decoder String.String"},{"name":"decodeRevertReason","comment":" Attempt to decode an EVM revert reason from raw revert data.\n\nChecks for the Error(string) selector `0x08c379a2` and decodes the\nABI-encoded string that follows. Returns Nothing if the data is not\na standard Error(string) revert.\n\n    decodeRevertReason \"0x08c379a200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000011496e73756666696369656e742066756e647300000000000000000000000000\"\n    --> Just \"Insufficient funds\"\n\n","type":"String.String -> Maybe.Maybe String.String"},{"name":"hexSlot","comment":" Extract the 64-character hex of the 32-byte word at slot index `n`\n(0-indexed) from a 0x-prefixed ABI-encoded hex string.\n\n    hexSlot 0 \"0x0000...0064\" == \"0000...0064\"\n\n","type":"Basics.Int -> String.String -> String.String"},{"name":"int256","comment":" Decode an int256 return value as a BigInt.\n","type":"Json.Decode.Decoder Web3.BigInt.BigInt"},{"name":"listSlot","comment":" Decode a dynamic array from slot `n`. Each element is decoded by calling\n`elemDecoder slotIndex fullHex`. Works for static-element arrays (uint256[],\naddress[], etc.) where elements are stored consecutively.\n","type":"Basics.Int -> (Basics.Int -> String.String -> Maybe.Maybe a) -> String.String -> Maybe.Maybe (List.List a)"},{"name":"string","comment":" Decode a string return value.\n","type":"Json.Decode.Decoder String.String"},{"name":"stringSlot","comment":" Decode a dynamic UTF-8 string from slot `n` of a raw 0x-hex ABI response.\nSlot `n` contains an offset pointer; the string length and data follow at\nthat offset.\n","type":"Basics.Int -> String.String -> Maybe.Maybe String.String"},{"name":"tuple2Hex","comment":" Decode a static 2-tuple from the head of a 0x-hex ABI response.\nEach decoder is called with its slot index (0, 1).\n","type":"(Basics.Int -> String.String -> Maybe.Maybe a) -> (Basics.Int -> String.String -> Maybe.Maybe b) -> String.String -> Maybe.Maybe ( a, b )"},{"name":"tuple3Hex","comment":" Decode a static 3-tuple from the head of a 0x-hex ABI response.\n","type":"(Basics.Int -> String.String -> Maybe.Maybe a) -> (Basics.Int -> String.String -> Maybe.Maybe b) -> (Basics.Int -> String.String -> Maybe.Maybe c) -> String.String -> Maybe.Maybe ( a, b, c )"},{"name":"uint128","comment":" Decode a uint128 return value as a BigInt.\n","type":"Json.Decode.Decoder Web3.BigInt.BigInt"},{"name":"uint16","comment":" Decode a uint16 return value as an Elm Int (validated 0–65535).\n","type":"Json.Decode.Decoder Basics.Int"},{"name":"uint256","comment":" Decode a uint256 return value as a BigInt.\nAccepts both decimal strings (\"100\") and 0x-prefixed hex strings (\"0x64\").\n","type":"Json.Decode.Decoder Web3.BigInt.BigInt"},{"name":"uint256Slot","comment":" Decode a uint256 from slot `n` of a raw 0x-hex ABI response.\n","type":"Basics.Int -> String.String -> Maybe.Maybe Web3.BigInt.BigInt"},{"name":"uint32","comment":" Decode a uint32 return value as an Elm Int (validated 0–4294967295).\n","type":"Json.Decode.Decoder Basics.Int"},{"name":"uint64","comment":" Decode a uint64 return value as a BigInt (exceeds JS/Elm safe Int range).\n","type":"Json.Decode.Decoder Web3.BigInt.BigInt"},{"name":"uint8","comment":" Decode a uint8 return value as an Elm Int (validated 0–255).\n","type":"Json.Decode.Decoder Basics.Int"}],"binops":[]},{"name":"Web3.Abi.Encode","comment":" Helpers to encode contract call parameters to JSON values\nfor passing through ports to the JS Web3 layer.\n\n@docs address, uint256, int256, bool, string, bytes, bytes32, bytesN\n@docs list, tuple2, tuple3\n\n","unions":[],"aliases":[],"values":[{"name":"address","comment":" Encode an Address argument.\n","type":"Web3.Types.Address -> Json.Encode.Value"},{"name":"bool","comment":" Encode a bool argument.\n","type":"Basics.Bool -> Json.Encode.Value"},{"name":"bytes","comment":" Encode raw bytes as a hex string.\n","type":"String.String -> Json.Encode.Value"},{"name":"bytes32","comment":" Encode a bytes32 value.\n","type":"String.String -> Json.Encode.Value"},{"name":"bytesN","comment":" Encode a bytesN value (bytes1 through bytes31).\n","type":"String.String -> Json.Encode.Value"},{"name":"int256","comment":" Encode an int256 argument from a BigInt.\n","type":"Web3.BigInt.BigInt -> Json.Encode.Value"},{"name":"list","comment":" Encode a dynamic array argument.\n\n    -- Transfer(address[],uint256[])\n    Encode.list Encode.address recipients\n    Encode.list Encode.uint256 amounts\n\n","type":"(a -> Json.Encode.Value) -> List.List a -> Json.Encode.Value"},{"name":"string","comment":" Encode a string argument.\n","type":"String.String -> Json.Encode.Value"},{"name":"tuple2","comment":" Encode a 2-tuple (struct with two fields).\n\n    -- swap(address token, uint256 amount)\n    Encode.tuple2 Encode.address Encode.uint256 ( tokenAddr, amount )\n\n","type":"(a -> Json.Encode.Value) -> (b -> Json.Encode.Value) -> ( a, b ) -> Json.Encode.Value"},{"name":"tuple3","comment":" Encode a 3-tuple (struct with three fields).\n","type":"(a -> Json.Encode.Value) -> (b -> Json.Encode.Value) -> (c -> Json.Encode.Value) -> ( a, b, c ) -> Json.Encode.Value"},{"name":"uint256","comment":" Encode a uint256 argument from a BigInt.\n","type":"Web3.BigInt.BigInt -> Json.Encode.Value"}],"binops":[]},{"name":"Web3.Balance","comment":" Typed balance query with correlation IDs.\n\nThis module provides a dedicated, typed interface for querying native ETH/PLS\nbalances. Multiple queries can be in flight simultaneously — each carries a\ncorrelation `id` that is echoed back so responses can be matched.\n\n    -- Send via your port:\n    web3Cmd (Web3.Balance.encode (Web3.Balance.getBalance address \"my-id\"))\n\n    -- Receive via your port:\n    case D.decodeValue Web3.Balance.decoder incoming of\n        Ok (Web3.Balance.GotBalance id wei) ->\n            -- id matches the one you sent; wei is the amount\n        Err _ ->\n            -- not a balance response\n\n@docs Cmd, Msg\n@docs getBalance, encode, decoder\n\n","unions":[{"name":"Cmd","comment":" A pending balance request. Carry the address and a correlation id.\n","args":[],"cases":[]},{"name":"Msg","comment":" A resolved balance response. Contains the correlation id and the amount in wei.\n","args":[],"cases":[["GotBalance",["String.String","Web3.Types.Wei"]]]}],"aliases":[],"values":[{"name":"decoder","comment":" Decode a `{tag:'balance', id, wei}` response from the JS port.\n","type":"Json.Decode.Decoder Web3.Balance.Msg"},{"name":"encode","comment":" Encode a `Cmd` for the JS port.\n\nProduces `{tag:'getBalance', address, id}`.\n\n","type":"Web3.Balance.Cmd -> Json.Encode.Value"},{"name":"getBalance","comment":" Build a balance query for `address`, tagged with `id`.\n","type":"Web3.Types.Address -> String.String -> Web3.Balance.Cmd"}],"binops":[]},{"name":"Web3.BigInt","comment":" Arbitrary-precision integers for EVM applications.\n\nSupports the full uint256 range (0 to 2^256-1) and int256 range.\nImplemented as a base-10^7 digit list with no external dependencies.\n\nAll values crossing the port boundary are decimal strings — this module\nhandles the conversion safely.\n\n@docs BigInt\n@docs fromInt, fromString, fromIntString, fromHexString, toString\n@docs add, sub, mul, div, mod\n@docs compare, eq, lt, lte, gt, gte\n@docs zero, isZero\n@docs decoder\n\n","unions":[{"name":"BigInt","comment":" An arbitrary-precision integer.\n","args":[],"cases":[]}],"aliases":[],"values":[{"name":"add","comment":" Add two BigInts.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Web3.BigInt.BigInt"},{"name":"compare","comment":" Compare two BigInts.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Basics.Order"},{"name":"decoder","comment":" Decode a BigInt from a JSON decimal string.\n\n    D.decodeString BigInt.decoder \"\\\"12345\\\"\" == Ok (BigInt.fromInt 12345)\n\n","type":"Json.Decode.Decoder Web3.BigInt.BigInt"},{"name":"div","comment":" Divide. Returns Nothing if divisor is zero.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Maybe.Maybe Web3.BigInt.BigInt"},{"name":"eq","comment":" Equality.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Basics.Bool"},{"name":"fromHexString","comment":" Parse a 0x-prefixed hexadecimal string as a non-negative BigInt.\nReturns Nothing for invalid input or empty hex body.\n\n    fromHexString \"0x64\"  == Just (fromInt 100)\n    fromHexString \"0xFF\"  == Just (fromInt 255)\n    fromHexString \"0x\"    == Nothing\n    fromHexString \"0xgg\"  == Nothing\n\n","type":"String.String -> Maybe.Maybe Web3.BigInt.BigInt"},{"name":"fromInt","comment":" Create a BigInt from an Elm Int.\n","type":"Basics.Int -> Web3.BigInt.BigInt"},{"name":"fromIntString","comment":" Alias for fromString (decimal). Kept for API symmetry with fromHexString.\n","type":"String.String -> Maybe.Maybe Web3.BigInt.BigInt"},{"name":"fromString","comment":" Parse a decimal integer string. Returns Nothing for invalid input.\n\nAccepts optional leading '-' or '+' sign. Rejects empty strings,\nnon-digit characters, and floating-point notation.\n\n    fromString \"0\"                    == Just zero\n    fromString \"-42\"                  == Just (fromInt -42)\n    fromString \"115792...9935\"        -- uint256 max: Just <big>\n    fromString \"abc\"                  == Nothing\n    fromString \"\"                     == Nothing\n\n","type":"String.String -> Maybe.Maybe Web3.BigInt.BigInt"},{"name":"gt","comment":" Greater than.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Basics.Bool"},{"name":"gte","comment":" Greater than or equal.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Basics.Bool"},{"name":"isZero","comment":" Is this BigInt zero?\n","type":"Web3.BigInt.BigInt -> Basics.Bool"},{"name":"lt","comment":" Less than.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Basics.Bool"},{"name":"lte","comment":" Less than or equal.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Basics.Bool"},{"name":"mod","comment":" Remainder: a `mod` b. Returns Nothing if b is zero.\n\n    mod (fromInt 10) (fromInt 3) == Just (fromInt 1)\n    mod (fromInt 10) zero        == Nothing\n\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Maybe.Maybe Web3.BigInt.BigInt"},{"name":"mul","comment":" Multiply.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Web3.BigInt.BigInt"},{"name":"sub","comment":" Subtract.\n","type":"Web3.BigInt.BigInt -> Web3.BigInt.BigInt -> Web3.BigInt.BigInt"},{"name":"toString","comment":" Convert to decimal string.\n","type":"Web3.BigInt.BigInt -> String.String"},{"name":"zero","comment":" Zero.\n","type":"Web3.BigInt.BigInt"}],"binops":[]},{"name":"Web3.Block","comment":" Block number and block data queries.\n\n    -- One-shot block number query:\n    port web3Cmd : Json.Encode.Value -> Cmd msg\n    port web3Sub : (Json.Decode.Value -> msg) -> Sub msg\n\n    type Msg = GotBlock Block.Msg | ...\n\n    subscriptions model =\n        web3Sub (Json.Decode.decodeValue Block.decoder >> Result.toMaybe >> Maybe.map GotBlock)\n\n    fetchBlock : Platform.Cmd msg\n    fetchBlock =\n        web3Cmd (Block.encode (Block.getBlockNumber \"my-id\"))\n\n@docs Cmd, Msg, Block\n@docs getBlockNumber, getBlock, watchBlockNumber, getBlockTransactionCount\n@docs encode, decoder\n\n","unions":[{"name":"Cmd","comment":" Commands to query block data via port.\n","args":[],"cases":[["RequestBlockNumber",["String.String"]],["RequestBlock",["Web3.Types.BlockNumber","String.String"]],["WatchBlockNumber",["String.String"]],["RequestBlockTxCount",["Web3.Types.BlockNumber","String.String"]]]},{"name":"Msg","comment":" Messages from the JS block port.\n","args":[],"cases":[["GotBlockNumber",["String.String","Basics.Int"]],["GotBlock",["String.String","Web3.Block.Block"]],["GotBlockTxCount",["String.String","Basics.Int"]]]}],"aliases":[{"name":"Block","comment":" A block header with key fields.\n","args":[],"type":"{ number : Basics.Int, hash : String.String, timestamp : Basics.Int, gasLimit : Web3.BigInt.BigInt, gasUsed : Web3.BigInt.BigInt, baseFeePerGas : Maybe.Maybe Web3.BigInt.BigInt, parentHash : String.String }"}],"values":[{"name":"decoder","comment":" Decode Msg responses from the JS port.\n","type":"Json.Decode.Decoder Web3.Block.Msg"},{"name":"encode","comment":" Encode a Cmd for the JS port.\n","type":"Web3.Block.Cmd -> Json.Encode.Value"},{"name":"getBlock","comment":" Request a specific block by block number. The `id` is echoed back.\n","type":"Web3.Types.BlockNumber -> String.String -> Web3.Block.Cmd"},{"name":"getBlockNumber","comment":" Request the current block number. The `id` is echoed back in the response.\n","type":"String.String -> Web3.Block.Cmd"},{"name":"getBlockTransactionCount","comment":" Request the number of transactions in a block. The second argument is a request ID.\n","type":"Web3.Types.BlockNumber -> String.String -> Web3.Block.Cmd"},{"name":"watchBlockNumber","comment":" Start polling the block number every 4 seconds. The `id` tags each\nGotBlockNumber response so you can distinguish multiple watchers.\n","type":"String.String -> Web3.Block.Cmd"}],"binops":[]},{"name":"Web3.Chain","comment":" Chain definitions for EVM networks.\n\nEach `Chain` bundles the chain ID, name, default RPC URL, block explorer URL,\nand native currency symbol. Pass `Chain.chainId someChain` wherever a\n`Web3.Types.ChainId` is needed (e.g. `Wallet.update` and `Wallet.switchChain`).\n\n    import Web3.Chain as Chain\n    import Web3.Wallet as Wallet\n\n    expectedChain : Web3.Types.ChainId\n    expectedChain =\n        Chain.chainId Chain.pulsechain\n\n    -- Add a network to the wallet (EIP-3085):\n    addPulseChain : Web3.Wallet.WalletCmd\n    addPulseChain =\n        Wallet.addChain\n            { chainId = 369\n            , chainName = Chain.name Chain.pulsechain\n            , rpcUrls = [ Chain.rpcUrl Chain.pulsechain ]\n            , nativeCurrency = { name = \"Pulse\", symbol = \"PLS\", decimals = 18 }\n            , blockExplorerUrls = [ Chain.blockExplorer Chain.pulsechain ]\n            }\n\n@docs Chain\n@docs pulsechain, pulsechainTestnet, ethereum, sepolia\n@docs bsc, polygon, arbitrum, optimism, base, avalanche, zksync, fantom, gnosis, linea, scroll\n@docs custom\n@docs chainId, name, rpcUrl, blockExplorer\n\n","unions":[],"aliases":[{"name":"Chain","comment":" An EVM chain definition.\n","args":[],"type":"{ chainId : Web3.Types.ChainId, name : String.String, rpcUrl : String.String, blockExplorer : String.String, nativeCurrency : String.String }"}],"values":[{"name":"arbitrum","comment":" Arbitrum One (chain ID 42161).\n","type":"Web3.Chain.Chain"},{"name":"avalanche","comment":" Avalanche C-Chain (chain ID 43114).\n","type":"Web3.Chain.Chain"},{"name":"base","comment":" Base mainnet (chain ID 8453).\n","type":"Web3.Chain.Chain"},{"name":"blockExplorer","comment":" Get the block explorer URL.\n","type":"Web3.Chain.Chain -> String.String"},{"name":"bsc","comment":" BNB Smart Chain mainnet (chain ID 56).\n","type":"Web3.Chain.Chain"},{"name":"chainId","comment":" Get the chain ID.\n","type":"Web3.Chain.Chain -> Web3.Types.ChainId"},{"name":"custom","comment":" Define a custom EVM chain.\n","type":"{ chainId : Basics.Int, name : String.String, rpcUrl : String.String, blockExplorer : String.String, nativeCurrency : String.String } -> Web3.Chain.Chain"},{"name":"ethereum","comment":" Ethereum mainnet (chain ID 1).\n","type":"Web3.Chain.Chain"},{"name":"fantom","comment":" Fantom Opera (chain ID 250).\n","type":"Web3.Chain.Chain"},{"name":"gnosis","comment":" Gnosis Chain (chain ID 100).\n","type":"Web3.Chain.Chain"},{"name":"linea","comment":" Linea mainnet (chain ID 59144).\n","type":"Web3.Chain.Chain"},{"name":"name","comment":" Get the chain name.\n","type":"Web3.Chain.Chain -> String.String"},{"name":"optimism","comment":" Optimism (chain ID 10).\n","type":"Web3.Chain.Chain"},{"name":"polygon","comment":" Polygon PoS mainnet (chain ID 137).\n","type":"Web3.Chain.Chain"},{"name":"pulsechain","comment":" PulseChain mainnet (chain ID 369).\n\nThe block explorer URL points to scan.pulsechain.com served via Pinata's IPFS\ngateway. This is the proper decentralised access method — the same explorer,\nbut fetched from IPFS rather than a centralised host.\n\n","type":"Web3.Chain.Chain"},{"name":"pulsechainTestnet","comment":" PulseChain testnet v4 (chain ID 943).\n","type":"Web3.Chain.Chain"},{"name":"rpcUrl","comment":" Get the RPC URL.\n","type":"Web3.Chain.Chain -> String.String"},{"name":"scroll","comment":" Scroll mainnet (chain ID 534352).\n","type":"Web3.Chain.Chain"},{"name":"sepolia","comment":" Sepolia testnet (chain ID 11155111).\n","type":"Web3.Chain.Chain"},{"name":"zksync","comment":" zkSync Era mainnet (chain ID 324).\n","type":"Web3.Chain.Chain"}],"binops":[]},{"name":"Web3.Contract.Call","comment":" Type-safe contract read calls (eth\\_call).\n\nBuild a read call, encode it for the JS port, decode the response.\nThe return type is parameterized so your decoder is checked at compile time.\n\n    import Web3.Contract.Call as Call\n    import Web3.Abi.Decode as Decode\n    import Web3.Abi.Encode as Encode\n\n    -- Read a uint256 return value\n    totalSupply : T.Address -> Call.ReadCall BigInt\n    totalSupply token =\n        Call.readCall\n            { contract = token\n            , method = \"totalSupply()\"\n            , args = []\n            , decoder = Decode.uint256\n            , id = \"total-supply\"\n            }\n\n    -- Simulate a write to catch reverts before broadcasting\n    simulate : T.Address -> BigInt -> T.Address -> Call.ReadCall Bool\n    simulate router amount caller =\n        Call.readCall\n            { contract = router\n            , method = \"buy(uint256)\"\n            , args = [ Encode.uint256 amount ]\n            , decoder = Decode.bool\n            , id = \"sim-buy\"\n            }\n            |> Call.withFrom caller\n\n    -- Send via port\n    web3Cmd (Call.encode (totalSupply tokenAddress))\n\n    -- Decode response\n    result = D.decodeValue (Call.responseDecoder myCall) incoming\n\nRelated modules: `Web3.Multicall` for batching multiple reads into one RPC call;\n`Web3.Contract.Send` for write calls.\n\n@docs ReadCall\n@docs readCall, withBlock, withFrom, encode, responseDecoder\n\n","unions":[{"name":"ReadCall","comment":" A read-only contract call with a typed return value.\n","args":["a"],"cases":[]}],"aliases":[],"values":[{"name":"encode","comment":" Encode a read call for the JS port.\n","type":"Web3.Contract.Call.ReadCall a -> Json.Encode.Value"},{"name":"readCall","comment":" Create a read call. The `id` is echoed back in the response so you can\nmatch responses to requests when multiple calls are in flight.\n","type":"{ contract : Web3.Types.Address, method : String.String, args : List.List Json.Encode.Value, decoder : Json.Decode.Decoder a, id : String.String } -> Web3.Contract.Call.ReadCall a"},{"name":"responseDecoder","comment":" Get the response decoder for a read call.\n","type":"Web3.Contract.Call.ReadCall a -> Json.Decode.Decoder a"},{"name":"withBlock","comment":" Set the block number for the call.\n","type":"Web3.Types.BlockNumber -> Web3.Contract.Call.ReadCall a -> Web3.Contract.Call.ReadCall a"},{"name":"withFrom","comment":" Add a `from` address — turns eth\\_call into a simulation of a write.\nCatches reverts without broadcasting.\n\n    simulateBuy tokenAddress amount userAddress\n        |> withFrom userAddress\n        |> encode\n        |> web3Cmd\n\n","type":"Web3.Types.Address -> Web3.Contract.Call.ReadCall a -> Web3.Contract.Call.ReadCall a"}],"binops":[]},{"name":"Web3.Contract.Event","comment":" Contract event watching via port subscriptions.\n\n    -- Subscribe to Transfer events\n    subscriptions model =\n        Event.watchEvent\n            { contract = tokenAddress\n            , event = \"Transfer\"\n            , filter = []\n            }\n            |> Event.decoder transferDecoder\n            |> onContractEvent TransferReceived\n\n@docs EventFilter, EventLog, GetLogsQuery\n@docs watchEvent, encode, decoder\n@docs getLogs, logsDecoder\n\n","unions":[],"aliases":[{"name":"EventFilter","comment":" An event subscription filter.\n","args":[],"type":"{ contract : Web3.Types.Address, event : String.String, topics : List.List (Maybe.Maybe String.String) }"},{"name":"EventLog","comment":" A decoded event log.\n","args":["a"],"type":"{ data : a, contract : Web3.Types.Address, topics : List.List String.String, blockNumber : Basics.Int, txHash : Web3.Types.TxHash, logIndex : Basics.Int }"},{"name":"GetLogsQuery","comment":" A query for getLogs with fromBlock/toBlock range.\n","args":[],"type":"{ contract : Web3.Types.Address, fromBlock : Web3.Types.BlockNumber, toBlock : Web3.Types.BlockNumber, topics : List.List (Maybe.Maybe String.String) }"}],"values":[{"name":"decoder","comment":" Decode a `watchEvent` log from the JS port.\n\nRequires the message to have `tag: \"eventLog\"` — use `logsDecoder` for\nthe batch `getLogs` response which wraps logs in an array.\n\n","type":"Json.Decode.Decoder a -> Json.Decode.Decoder (Web3.Contract.Event.EventLog a)"},{"name":"encode","comment":" Encode an event filter for the JS port.\n","type":"Web3.Contract.Event.EventFilter -> Json.Encode.Value"},{"name":"getLogs","comment":" Encode a getLogs query for the JS port.\n","type":"Web3.Contract.Event.GetLogsQuery -> Json.Encode.Value"},{"name":"logsDecoder","comment":" Decode a list of event logs with a custom data decoder.\n\nDecodes the `{ tag: \"logs\", logs: [...] }` response from `getLogs`.\n\n","type":"Json.Decode.Decoder a -> Json.Decode.Decoder (List.List (Web3.Contract.Event.EventLog a))"},{"name":"watchEvent","comment":" Create an event watch command.\n","type":"Web3.Contract.Event.EventFilter -> Json.Encode.Value"}],"binops":[]},{"name":"Web3.Contract.Send","comment":" Type-safe contract write calls (eth\\_sendTransaction).\n\nBuild a write call, encode it for the JS port, optionally estimate gas first.\n\n    -- Non-payable: approve\n    approve : Address -> BigInt -> WriteCall\n    approve spender amount =\n        writeCall\n            { contract = tokenAddress\n            , method = \"approve\"\n            , args = [ Encode.address spender, Encode.uint256 amount ]\n            }\n\n    -- Payable: buy\n    buy : BigInt -> Wei -> WriteCall\n    buy minTokens value =\n        payableCall\n            { contract = routerAddress\n            , method = \"buy\"\n            , args = [ Encode.uint256 minTokens ]\n            , value = value\n            }\n\n@docs WriteCall\n@docs writeCall, payableCall, withGasLimit\n@docs encode, estimateGas, deployCall, encodeRawSend\n\n","unions":[{"name":"WriteCall","comment":" A write call to a contract.\n","args":[],"cases":[]}],"aliases":[],"values":[{"name":"deployCall","comment":" Encode a contract deployment for submission via the port. ","type":"{ bytecode : String.String, args : List.List Json.Encode.Value, gasLimit : Maybe.Maybe Basics.Int } -> Json.Encode.Value"},{"name":"encode","comment":" Encode a write call for the JS port.\n","type":"Web3.Contract.Send.WriteCall -> Json.Encode.Value"},{"name":"encodeRawSend","comment":" Encode a raw signed transaction for broadcast via the port. ","type":"String.String -> Json.Encode.Value"},{"name":"estimateGas","comment":" Encode a write call as an estimateGas command for the JS port.\n\n    estimateGas myWriteCall\n    -- sends { tag: \"estimateGas\", contract: ..., method: ..., args: ..., value: ... }\n    -- JS responds with { tag: \"gasEstimate\", gas: \"21000\" }\n\n","type":"Web3.Contract.Send.WriteCall -> Json.Encode.Value"},{"name":"payableCall","comment":" Create a payable write call with a value.\n","type":"{ contract : Web3.Types.Address, method : String.String, args : List.List Json.Encode.Value, value : Web3.BigInt.BigInt } -> Web3.Contract.Send.WriteCall"},{"name":"withGasLimit","comment":" Set a gas limit.\n","type":"Basics.Int -> Web3.Contract.Send.WriteCall -> Web3.Contract.Send.WriteCall"},{"name":"writeCall","comment":" Create a non-payable write call.\n","type":"{ contract : Web3.Types.Address, method : String.String, args : List.List Json.Encode.Value } -> Web3.Contract.Send.WriteCall"}],"binops":[]},{"name":"Web3.Crypto","comment":" Cryptographic utilities via the Web3 port.\n\n@docs Cmd, Msg, keccak256, encode, decoder\n","unions":[{"name":"Cmd","comment":" Commands for the Crypto port. ","args":[],"cases":[]},{"name":"Msg","comment":" Messages from the Crypto port. ","args":[],"cases":[["GotKeccak256",["String.String","String.String"]]]}],"aliases":[],"values":[{"name":"decoder","comment":" Decode a Crypto message received from the port. ","type":"Json.Decode.Decoder Web3.Crypto.Msg"},{"name":"encode","comment":" Encode a Crypto command to send through the port. ","type":"Web3.Crypto.Cmd -> Json.Encode.Value"},{"name":"keccak256","comment":" Request the keccak256 hash of a UTF-8 string. The second argument is a request ID. ","type":"String.String -> String.String -> Web3.Crypto.Cmd"}],"binops":[]},{"name":"Web3.Fee","comment":" Gas price and fee history queries.\n\n    fetchGasPrice : Platform.Cmd msg\n    fetchGasPrice =\n        web3Cmd (Fee.encode (Fee.getGasPrice \"gas-query\"))\n\n@docs Cmd, Msg, FeeHistory\n@docs getGasPrice, getFeeHistory\n@docs encode, decoder\n\n","unions":[{"name":"Cmd","comment":" Commands to query fee data via port.\n","args":[],"cases":[["RequestGasPrice",["String.String"]],["RequestFeeHistory",["String.String","Basics.Int"]]]},{"name":"Msg","comment":" Messages from the JS fee port.\n","args":[],"cases":[["GotGasPrice",["String.String","Web3.BigInt.BigInt"]],["GotFeeHistory",["String.String","Web3.Fee.FeeHistory"]]]}],"aliases":[{"name":"FeeHistory","comment":" Historical fee data from eth_feeHistory.\n","args":[],"type":"{ baseFeePerGas : List.List Web3.BigInt.BigInt, gasUsedRatio : List.List Basics.Float, oldestBlock : Basics.Int }"}],"values":[{"name":"decoder","comment":" Decode Msg responses from the JS port.\n","type":"Json.Decode.Decoder Web3.Fee.Msg"},{"name":"encode","comment":" Encode a Cmd for the JS port.\n","type":"Web3.Fee.Cmd -> Json.Encode.Value"},{"name":"getFeeHistory","comment":" Request fee history for the last `blockCount` blocks.\n","type":"String.String -> Basics.Int -> Web3.Fee.Cmd"},{"name":"getGasPrice","comment":" Request the current gas price (eth_gasPrice). The `id` is echoed back.\n","type":"String.String -> Web3.Fee.Cmd"}],"binops":[]},{"name":"Web3.Multicall","comment":" Batch multiple contract reads into a single eth\\_call via Multicall3.\n\nThe Multicall3 contract (0xcA11bde05977b3631167028862bE2a173976CA11) is\ndeployed on Ethereum, PulseChain, and most EVM networks. It lets you pack N\nread calls into one RPC round-trip.\n\n    import Web3.Multicall as Multicall\n    import Web3.Abi.Encode as Encode\n\n    let\n        req =\n            Multicall.batch \"balance-check\"\n                [ Multicall.callSpec tokenAddress \"balanceOf(address)\" [ Encode.address myAddr ]\n                , Multicall.callSpec tokenAddress \"totalSupply()\" []\n                ]\n    in\n    -- Outgoing port: web3Cmd (Multicall.encode req)\n    -- Incoming port: match on { tag = \"multicallResult\" } and apply responseDecoder\n\n@docs CallSpec, callSpec, MulticallRequest, CallResult\n@docs batch, encode, responseDecoder\n\n","unions":[{"name":"CallSpec","comment":" A single call specification for inclusion in a multicall batch.\n","args":[],"cases":[]},{"name":"MulticallRequest","comment":" A batched multicall request with a correlation id.\n","args":[],"cases":[]}],"aliases":[{"name":"CallResult","comment":" The result of a single call within a multicall batch.\n\n`data` is the raw ABI-encoded return value as a hex string (0x-prefixed).\nApply your specific decoder (e.g. from Web3.Abi.Decode) to parse it.\n\n","args":[],"type":"{ success : Basics.Bool, data : String.String }"}],"values":[{"name":"batch","comment":" Batch a list of call specs into a multicall request.\n\nThe `id` is echoed back in the `multicallResult` response so you can match\nresponses to requests when multiple batches are in flight.\n\n","type":"String.String -> List.List Web3.Multicall.CallSpec -> Web3.Multicall.MulticallRequest"},{"name":"callSpec","comment":" Build a CallSpec for a contract read.\n\n    Multicall.callSpec\n        pairAddress\n        \"getReserves()\"\n        []\n\n","type":"Web3.Types.Address -> String.String -> List.List Json.Encode.Value -> Web3.Multicall.CallSpec"},{"name":"encode","comment":" Encode a multicall request for the JS port.\n\nProduces:\n\n    { \"tag\": \"multicall\"\n    , \"id\": \"...\"\n    , \"calls\": [ { \"contract\": \"0x...\", \"method\": \"...\", \"args\": [...] }, ... ]\n    }\n\n","type":"Web3.Multicall.MulticallRequest -> Json.Encode.Value"},{"name":"responseDecoder","comment":" Decoder for the `multicallResult` port response.\n\nExpects:\n\n    { \"tag\": \"multicallResult\"\n    , \"id\": \"...\"\n    , \"results\": [ { \"success\": true, \"data\": \"0x...\" }, ... ]\n    }\n\nResults are in the same order as the original call specs.\n\n","type":"Json.Decode.Decoder (List.List Web3.Multicall.CallResult)"}],"binops":[]},{"name":"Web3.Query","comment":" Typed decoders for on-chain read queries: transaction count (nonce),\nstorage slot reads, contract bytecode, and transaction lookup by hash.\n\nEach query carries a correlation `id` that is echoed back so responses can\nbe matched when multiple queries are in flight.\n\n    -- Send via your port:\n    web3Cmd (Web3.Query.encode (Web3.Query.getTxCount address \"nonce-1\"))\n\n    -- Receive via your port:\n    case D.decodeValue Web3.Query.decoder incoming of\n        Ok (Web3.Query.GotTxCount id nonce) -> ...\n        Ok (Web3.Query.GotStorageAt id val) -> ...\n        Ok (Web3.Query.GotCode id bytecode) -> ...\n        Ok (Web3.Query.GotTransaction id info) -> ...\n        Ok (Web3.Query.TransactionNotFound id) -> ...\n        Err _ -> -- not a query response\n\n@docs Cmd, Msg, TransactionInfo\n@docs getTxCount, getStorageAt, getCode, getTransaction, encode, decoder\n\n","unions":[{"name":"Cmd","comment":" Commands to send to the JS query port.\n","args":[],"cases":[["RequestTxCount",["Web3.Types.Address","String.String"]],["RequestStorageAt",["Web3.Types.Address","Basics.Int","String.String"]],["RequestCode",["Web3.Types.Address","String.String"]],["RequestTransaction",["Web3.Types.TxHash","String.String"]]]},{"name":"Msg","comment":" Messages from the JS query port.\n","args":[],"cases":[["GotTxCount",["String.String","Basics.Int"]],["GotStorageAt",["String.String","String.String"]],["GotCode",["String.String","String.String"]],["GotTransaction",["String.String","Web3.Query.TransactionInfo"]],["TransactionNotFound",["String.String"]]]}],"aliases":[{"name":"TransactionInfo","comment":" Information about a transaction fetched by hash.\n","args":[],"type":"{ hash : Web3.Types.TxHash, from : Web3.Types.Address, to : Maybe.Maybe Web3.Types.Address, value : Web3.Types.Wei, nonce : Basics.Int, data : String.String, gas : Basics.Int, blockNumber : Maybe.Maybe Basics.Int, blockHash : Maybe.Maybe String.String }"}],"values":[{"name":"decoder","comment":" Decode query responses from the JS port.\n\nHandles `txCount`, `storageAt`, `code`, `transaction`, and `transactionNotFound` tags.\nReturns `Err` for unknown tags.\n\n","type":"Json.Decode.Decoder Web3.Query.Msg"},{"name":"encode","comment":" Encode a `Cmd` for the JS port.\n","type":"Web3.Query.Cmd -> Json.Encode.Value"},{"name":"getCode","comment":" Build a contract bytecode query for `contract`, tagged with `id`.\n","type":"Web3.Types.Address -> String.String -> Web3.Query.Cmd"},{"name":"getStorageAt","comment":" Build a storage slot query. `slot` is the integer slot index (converted to hex).\n","type":"Web3.Types.Address -> Basics.Int -> String.String -> Web3.Query.Cmd"},{"name":"getTransaction","comment":" Request a transaction by hash. The second argument is a request ID.\n","type":"Web3.Types.TxHash -> String.String -> Web3.Query.Cmd"},{"name":"getTxCount","comment":" Build a transaction count (nonce) query for `address`, tagged with `id`.\n","type":"Web3.Types.Address -> String.String -> Web3.Query.Cmd"}],"binops":[]},{"name":"Web3.Sign","comment":" EIP-712 typed data signing and EIP-191 personal signing.\n\nBuild a typed data request, encode it for the JS port, decode the signature response.\nUse the `SignState` machine to track the lifecycle of a single sign request.\n\n    import Dict\n    import Json.Encode as E\n    import Web3.Sign as Sign\n\n    permitRequest : T.Address -> T.Address -> BigInt -> Int -> Sign.TypedData\n    permitRequest owner spender value nonce =\n        Sign.typedData\n            { domain =\n                { name = Just \"MyToken\", version = Just \"1\"\n                , chainId = Just 369, verifyingContract = Just tokenAddress\n                , salt = Nothing\n                }\n            , types =\n                Dict.fromList\n                    [ ( \"Permit\"\n                      , [ { name = \"owner\",    typeName = \"address\" }\n                        , { name = \"spender\",  typeName = \"address\" }\n                        , { name = \"value\",    typeName = \"uint256\" }\n                        , { name = \"nonce\",    typeName = \"uint256\" }\n                        , { name = \"deadline\", typeName = \"uint256\" }\n                        ]\n                      )\n                    ]\n            , primaryType = \"Permit\"\n            , message = E.object [ ... ]\n            }\n\n    -- Send via port:\n    web3Cmd (Sign.encode \"permit-1\" signerAddress (permitRequest owner spender value nonce))\n\n    -- Receive via port (use signatureDecoder for the raw sig string):\n    case D.decodeValue Sign.signatureDecoder incoming of\n        Ok sig -> -- sig is the 0x-prefixed signature\n        Err _ -> -- not a sign response\n\n**Sign state machine** — tracks one in-flight request:\n\n    type SignState\n        = SignIdle\n        | SignPending String          -- id\n        | Signed String String        -- id, signature\n        | SignFailed String String    -- id, error\n        | SignRejected String         -- id\n\n    -- Usage:\n    ( { model | signState = Sign.startSign \"permit-1\" model.signState }\n    , web3Cmd (Sign.encode \"permit-1\" addr request)\n    )\n\n    -- On port response:\n    newSignState = Sign.signUpdate signMsg model.signState\n\n@docs TypedData, Domain, TypeField\n@docs typedData, encode, personalSign, signatureDecoder\n@docs SignState, SignMsg\n@docs startSign, signUpdate, isSignTerminal\n\n","unions":[{"name":"SignMsg","comment":" Messages from the JS sign port.\n","args":[],"cases":[["SignResponse",["String.String","String.String"]],["SignError",["String.String","String.String"]],["SignCancel",["String.String"]]]},{"name":"SignState","comment":" Signing state machine. Tracks the lifecycle of a single sign request.\n\n  - `SignIdle` — no sign request in progress\n  - `SignPending id` — awaiting user approval for request `id`\n  - `Signed id sig` — user approved; `sig` is the 0x-prefixed signature\n  - `SignFailed id err` — signing failed (wallet error, network error)\n  - `SignRejected id` — user explicitly cancelled in wallet UI\n\n","args":[],"cases":[["SignIdle",[]],["SignPending",["String.String"]],["Signed",["String.String","String.String"]],["SignFailed",["String.String","String.String"]],["SignRejected",["String.String"]]]},{"name":"TypedData","comment":" An EIP-712 typed data signing request.\n","args":[],"cases":[]}],"aliases":[{"name":"Domain","comment":" EIP-712 domain separator parameters. All fields are optional;\ninclude only those your contract's domain hash covers.\n","args":[],"type":"{ name : Maybe.Maybe String.String, version : Maybe.Maybe String.String, chainId : Maybe.Maybe Basics.Int, verifyingContract : Maybe.Maybe Web3.Types.Address, salt : Maybe.Maybe String.String }"},{"name":"TypeField","comment":" A single field in a struct type definition.\n","args":[],"type":"{ name : String.String, typeName : String.String }"}],"values":[{"name":"encode","comment":" Encode a TypedData request as a port command.\n\n    web3Cmd (Sign.encode \"my-id\" signerAddress request)\n\nThe `id` is echoed back in the `signed` response so you can match\nresponses to requests when multiple signing requests are in flight.\n\n","type":"String.String -> Web3.Types.Address -> Web3.Sign.TypedData -> Json.Encode.Value"},{"name":"isSignTerminal","comment":" True if the sign state is terminal (no more updates expected).\n","type":"Web3.Sign.SignState -> Basics.Bool"},{"name":"personalSign","comment":" Sign an arbitrary message with `personal_sign` (EIP-191).\n\nUsed for login flows and simple off-chain authentication. The wallet\nwill display the raw message to the user before signing.\n\n    web3Cmd (Sign.personalSign \"login-1\" signerAddress \"Sign in to MyDapp\")\n\nResponses arrive on the same `signed` tag as EIP-712 — use `signatureDecoder`.\n\n","type":"String.String -> Web3.Types.Address -> String.String -> Json.Encode.Value"},{"name":"signUpdate","comment":" Update sign state from a port message.\nTerminal states never transition out.\n","type":"Web3.Sign.SignMsg -> Web3.Sign.SignState -> Web3.Sign.SignState"},{"name":"signatureDecoder","comment":" Decode the `signed` response from the JS port.\n\n    case D.decodeValue Sign.signatureDecoder incoming of\n        Ok sig ->\n            -- sig : String — the 0x-prefixed signature\n        Err _ ->\n            -- handle decode error\n\n","type":"Json.Decode.Decoder String.String"},{"name":"startSign","comment":" Transition from `SignIdle` to `SignPending` for the given correlation id.\nAll other states are unchanged (a sign already in flight is not replaced).\n","type":"String.String -> Web3.Sign.SignState -> Web3.Sign.SignState"},{"name":"typedData","comment":" Construct a TypedData value.\n","type":"{ domain : Web3.Sign.Domain, types : Dict.Dict String.String (List.List Web3.Sign.TypeField), primaryType : String.String, message : Json.Encode.Value } -> Web3.Sign.TypedData"}],"binops":[]},{"name":"Web3.Transaction","comment":" Transaction lifecycle state machine.\n\nEvery transaction goes through explicit states. Pattern matching forces you to\nhandle every case — no \"transaction failed silently\" bugs. The state machine\nfollows the TLA+ spec in `proofs/tla/TransactionSpec.tla`:\nterminal states never transition out (except via `TxReset`),\nand confirmation counts only increase.\n\n    case tx.status of\n        Idle ->\n            viewBuyButton\n\n        AwaitingSignature ->\n            viewSigningSpinner\n\n        Submitted hash ->\n            viewPendingWithHash hash\n\n        Confirming hash n ->\n            viewConfirming n\n\n        Confirmed receipt ->\n            viewSuccess receipt\n\n        Failed err ->\n            viewError err\n\n        Rejected ->\n            viewRejected\n\nTo poll for a receipt after submission, encode a `RequestReceipt` command:\n\n    case tx.status of\n        Submitted hash ->\n            ( model\n            , web3Cmd (Tx.encodeCmd (Tx.RequestReceipt hash \"my-tx\"))\n            )\n        _ ->\n            ( model, Cmd.none )\n\nTo reset a terminal transaction back to `Idle`:\n\n    Tx.update Tx.TxReset tx.status\n\nRelated modules: `Web3.Contract.Send` to build and encode write calls;\n`Web3.Contract.Call` to simulate writes before broadcasting.\n\n@docs Status, Msg, TxCmd, Receipt, EventLog\n@docs update, isTerminal, isPending, transactionConfirmations\n@docs encodeCmd, decoder, parseReceiptEvents\n\n","unions":[{"name":"Msg","comment":" Messages from the JS transaction port.\n","args":[],"cases":[["TxSubmitted",["String.String"]],["TxConfirmation",["String.String","Basics.Int"]],["TxConfirmed",["Web3.Transaction.ReceiptJson"]],["TxFailed",["String.String"]],["TxRejected",[]],["TxReset",[]],["TxReceiptNotFound",["String.String"]]]},{"name":"Status","comment":" Transaction lifecycle status. Cannot be in an invalid state.\n","args":[],"cases":[["Idle",[]],["AwaitingSignature",[]],["Submitted",["Web3.Types.TxHash"]],["Confirming",["Web3.Types.TxHash","Basics.Int"]],["Confirmed",["Web3.Transaction.Receipt"]],["Failed",["String.String"]],["Rejected",[]]]},{"name":"TxCmd","comment":" Commands to send to the JS transaction port.\n","args":[],"cases":[["RequestReceipt",["Web3.Types.TxHash","String.String"]]]}],"aliases":[{"name":"EventLog","comment":" A single log entry emitted during a transaction.\n","args":[],"type":"{ address : Web3.Types.Address, topics : List.List String.String, data : String.String, blockNumber : Basics.Int, logIndex : Basics.Int }"},{"name":"Receipt","comment":" A confirmed transaction receipt, including emitted logs.\n","args":[],"type":"{ txHash : Web3.Types.TxHash, blockNumber : Basics.Int, gasUsed : String.String, status : Basics.Bool, logs : List.List Web3.Transaction.EventLog }"}],"values":[{"name":"decoder","comment":" Decode transaction messages from JS port.\n","type":"Json.Decode.Decoder Web3.Transaction.Msg"},{"name":"encodeCmd","comment":" Encode a TxCmd for the JS port.\n","type":"Web3.Transaction.TxCmd -> Json.Encode.Value"},{"name":"isPending","comment":" Is this status still pending?\n","type":"Web3.Transaction.Status -> Basics.Bool"},{"name":"isTerminal","comment":" Is this status terminal (no more updates expected)?\n","type":"Web3.Transaction.Status -> Basics.Bool"},{"name":"parseReceiptEvents","comment":" Apply a list of event decoders to the logs in a receipt, collecting\nall successfully decoded events.\n\n    type MyEvent\n        = Transfer { from : Address, to : Address, amount : BigInt }\n        | Approval { owner : Address, spender : Address, amount : BigInt }\n\n    decodeTransfer : EventLog -> Maybe MyEvent\n    decodeTransfer log =\n        -- match topic[0] to Transfer signature, then decode\n        ...\n\n    events : List MyEvent\n    events =\n        parseReceiptEvents [ decodeTransfer, decodeApproval ] receipt\n\n","type":"List.List (Web3.Transaction.EventLog -> Maybe.Maybe a) -> Web3.Transaction.Receipt -> List.List a"},{"name":"transactionConfirmations","comment":" Number of confirmations for a confirmed transaction given the current block number.\n","type":"Basics.Int -> Web3.Transaction.Receipt -> Basics.Int"},{"name":"update","comment":" Update transaction status from a port message.\n\nTransitions are guarded to match the TLA+ GuardedNext specification:\n- Terminal states (Confirmed, Failed, Rejected) never transition out, except via TxReset.\n- TxSubmitted is only accepted from AwaitingSignature.\n- TxConfirmation is only accepted from Submitted or Confirming.\n- TxFailed is accepted from any non-terminal state.\n- TxRejected from AwaitingSignature → Rejected; from Submitted/Confirming → Failed.\n- TxReset from any terminal state → Idle; from non-terminal states → no-op.\n- TxReceiptNotFound is a no-op; the app can schedule another RequestReceipt.\n","type":"Web3.Transaction.Msg -> Web3.Transaction.Status -> Web3.Transaction.Status"}],"binops":[]},{"name":"Web3.Types","comment":" Core opaque types for EVM interaction.\n\nAll types are opaque — you can't accidentally pass a `TxHash` where an `Address`\nis expected. Construction validates format and returns `Maybe` so invalid input\nis a compile-time-visible code path, not a runtime crash.\n\n    import Web3.Types as T\n\n    -- Validate on input boundary (e.g. from a URL param or user field):\n    case T.address rawString of\n        Just addr ->\n            -- addr : T.Address — safe to use everywhere\n        Nothing ->\n            -- show validation error\n\n    -- BlockNumber is used by Contract.Call, Block, Fee, and Query:\n    T.encodeBlockNumber T.Latest    == Json.Encode.string \"latest\"\n    T.encodeBlockNumber (T.BlockNum 1000) == Json.Encode.int 1000\n\n`Wei` is an alias for `Web3.BigInt.BigInt` — use `Web3.Units.formatEther` to\nconvert to a human-readable string.\n\n@docs Address, TxHash, BlockNumber, ChainId, Wei, HexString\n@docs address, addressToString\n@docs txHash, txHashToString\n@docs chainId, chainIdToInt\n@docs hexString, hexStringToString\n@docs encodeBlockNumber\n\n","unions":[{"name":"Address","comment":" A validated Ethereum address (0x + 40 hex chars).\n","args":[],"cases":[]},{"name":"BlockNumber","comment":" A block number — specific or a tag.\n","args":[],"cases":[["BlockNum",["Basics.Int"]],["Latest",[]],["Pending",[]],["Earliest",[]]]},{"name":"ChainId","comment":" An EVM chain ID.\n","args":[],"cases":[]},{"name":"HexString","comment":" A raw hex string.\n","args":[],"cases":[]},{"name":"TxHash","comment":" A transaction hash (0x + 64 hex chars).\n","args":[],"cases":[]}],"aliases":[{"name":"Wei","comment":" Wei amount as a string (uint256 — too large for Int).\n","args":[],"type":"Web3.BigInt.BigInt"}],"values":[{"name":"address","comment":" Create an Address from a hex string. Returns Nothing if invalid.\n","type":"String.String -> Maybe.Maybe Web3.Types.Address"},{"name":"addressToString","comment":" Extract the string value of an Address.\n","type":"Web3.Types.Address -> String.String"},{"name":"chainId","comment":" Create a ChainId.\n","type":"Basics.Int -> Web3.Types.ChainId"},{"name":"chainIdToInt","comment":" Extract the integer value of a ChainId.\n","type":"Web3.Types.ChainId -> Basics.Int"},{"name":"encodeBlockNumber","comment":" Encode a BlockNumber for JSON-RPC (e.g. for eth_call block parameter).\n","type":"Web3.Types.BlockNumber -> Json.Encode.Value"},{"name":"hexString","comment":" Create a HexString.\n","type":"String.String -> Maybe.Maybe Web3.Types.HexString"},{"name":"hexStringToString","comment":" Extract the string value of a HexString.\n","type":"Web3.Types.HexString -> String.String"},{"name":"txHash","comment":" Create a TxHash from a hex string.\n","type":"String.String -> Maybe.Maybe Web3.Types.TxHash"},{"name":"txHashToString","comment":" Extract the string value of a TxHash.\n","type":"Web3.Types.TxHash -> String.String"}],"binops":[]},{"name":"Web3.Units","comment":" ETH / ERC-20 unit conversion — pure Elm, no JS required.\n\n    formatEther (BigInt.fromInt 1500000000000000000) == \"1.5\"\n    parseEther \"1.5\" == Just <1500000000000000000>\n\n@docs formatEther, parseEther\n@docs formatUnits, parseUnits\n\n","unions":[],"aliases":[],"values":[{"name":"formatEther","comment":" Convert Wei to a human-readable ETH string with up to 18 significant\ndecimal places. Trailing zeros are trimmed.\n\n    formatEther zero         == \"0\"\n    formatEther (10^18 wei)  == \"1\"\n    formatEther (1.5×10^18)  == \"1.5\"\n    formatEther (10^15 wei)  == \"0.001\"\n\n","type":"Web3.BigInt.BigInt -> String.String"},{"name":"formatUnits","comment":" Convert a token amount (in its smallest unit) to a human-readable string\nfor a token with the given number of decimal places. Trailing zeros are trimmed.\n\n    formatUnits 6 (BigInt.fromInt 1500000)  == \"1.5\"   -- USDC\n    formatUnits 8 (BigInt.fromInt 100000000) == \"1\"    -- WBTC\n\n","type":"Basics.Int -> Web3.BigInt.BigInt -> String.String"},{"name":"parseEther","comment":" Parse a human-readable ETH string to Wei. Returns Nothing for invalid\ninput, non-numeric strings, or negative values.\n\n    parseEther \"1\"                       == Just <10^18>\n    parseEther \"1.5\"                     == Just <1.5×10^18>\n    parseEther \"0.000000000000000001\"    == Just (BigInt.fromInt 1)\n    parseEther \"not-a-number\"            == Nothing\n    parseEther \"-1\"                      == Nothing\n\n","type":"String.String -> Maybe.Maybe Web3.BigInt.BigInt"},{"name":"parseUnits","comment":" Parse a human-readable token amount to its smallest unit, given the\ntoken's decimal count. Returns Nothing for invalid or negative input.\n\n    parseUnits 6 \"1.5\"  == Just (BigInt.fromInt 1500000)   -- USDC\n    parseUnits 6 \"0\"    == Just BigInt.zero\n    parseUnits 6 \"-1\"   == Nothing\n\n","type":"Basics.Int -> String.String -> Maybe.Maybe Web3.BigInt.BigInt"}],"binops":[]},{"name":"Web3.Wallet","comment":" Wallet connection state machine.\n\nThe wallet is modeled as an explicit state — you can't accidentally\ncall a contract without a connected wallet because the compiler\nwon't give you an `Address` from a `Disconnected` state.\n\n    case model.wallet of\n        Connected info ->\n            -- info.address : T.Address — only available here\n            buy info.address amount\n\n        WrongChain _ _ ->\n            -- wallet is connected but on the wrong chain\n            button [ onClick SwitchChain ] [ text \"Switch chain\" ]\n\n        ReadOnly ->\n            -- rpcUrl configured but no wallet; reads work, writes will fail\n            viewReadOnlyBanner\n\n        _ ->\n            showConnectButton\n\n**EIP-6963 multi-wallet discovery** — listen for `WalletsDiscovered` on the\nport and present the list to the user; call `selectWallet rdns` when they pick.\n\n**Typical connection flow:**\n\n1. User clicks \"Connect\" → call `startConnect` on state, send `connect` via port.\n2. `WalletsDiscovered providers` arrives → show picker if `providers` is non-empty.\n3. User picks a wallet → send `selectWallet rdns` via port.\n4. `WalletConnected addr chainId` arrives → `update` transitions to `Connected` or `WrongChain`.\n5. If `WrongChain` → send `switchChain expectedChain` via port.\n\nFor native balance queries, use `Web3.Balance`. For adding chains, use `addChain` with\na `ChainConfig` record and follow up with `switchChain`.\n\n@docs State, Msg, WalletCmd, WalletProvider, ChainConfig\n@docs update, startConnect\n@docs connect, disconnect, switchChain, selectWallet, addChain\n@docs watchAsset, requestPermissions, getPermissions\n@docs isConnected, isReadOnly, getAddress, getChainId\n@docs encode, decoder\n\n","unions":[{"name":"Msg","comment":" Messages from the JS wallet port.\n","args":[],"cases":[["WalletConnected",["String.String","Basics.Int"]],["WalletDisconnected",[]],["ChainChanged",["Basics.Int"]],["AccountChanged",["String.String"]],["WalletError",["String.String"]],["WalletsDiscovered",["List.List Web3.Wallet.WalletProvider"]],["ReadOnlyMode",[]],["ChainAdded",[]],["SwitchChainOk",["Basics.Int"]],["AssetWatched",[]],["GotPermissions",["List.List String.String"]]]},{"name":"State","comment":" Wallet connection state. Every possible state is explicit.\n\n  - `Disconnected` — no wallet detected, no rpcUrl configured\n  - `ReadOnly` — rpcUrl is present but no wallet; reads work, writes will fail\n  - `Connecting` — wallet connection in progress\n  - `Connected` — wallet connected on the expected chain\n  - `WrongChain` — wallet connected but on the wrong chain\n  - `Error` — unrecoverable error\n\n","args":[],"cases":[["Disconnected",[]],["ReadOnly",[]],["Connecting",[]],["Connected",["Web3.Wallet.ConnectedInfo"]],["WrongChain",["Web3.Wallet.ConnectedInfo","Web3.Types.ChainId"]],["Error",["String.String"]]]},{"name":"WalletCmd","comment":" Commands to send to JS via port.\n","args":[],"cases":[["RequestConnect",[]],["RequestDisconnect",[]],["RequestSwitchChain",["Basics.Int"]],["RequestSelectWallet",["String.String"]],["RequestAddChain",["Web3.Wallet.ChainConfig"]],["RequestWatchAsset",["{ address : Web3.Types.Address, symbol : String.String, decimals : Basics.Int, image : String.String }"]],["RequestPermissions",[]],["GetPermissions",[]]]}],"aliases":[{"name":"ChainConfig","comment":" Configuration for adding a new chain to the wallet (EIP-3085).\n","args":[],"type":"{ chainId : Basics.Int, chainName : String.String, rpcUrls : List.List String.String, nativeCurrency : { name : String.String, symbol : String.String, decimals : Basics.Int }, blockExplorerUrls : List.List String.String }"},{"name":"WalletProvider","comment":" A wallet provider discovered via EIP-6963.\n","args":[],"type":"{ name : String.String, icon : String.String, rdns : String.String }"}],"values":[{"name":"addChain","comment":" Request wallet_addEthereumChain (EIP-3085) to add a new network.\n\n    addChain\n        { chainId = 369\n        , chainName = \"PulseChain\"\n        , rpcUrls = [ \"https://rpc.pulsechain.com\" ]\n        , nativeCurrency = { name = \"Pulse\", symbol = \"PLS\", decimals = 18 }\n        , blockExplorerUrls = [ \"https://scan.pulsechain.com\" ]\n        }\n\n","type":"Web3.Wallet.ChainConfig -> Web3.Wallet.WalletCmd"},{"name":"connect","comment":" Command to request wallet connection.\n","type":"Web3.Wallet.WalletCmd"},{"name":"decoder","comment":" Decode a Msg from the JS wallet port.\n","type":"Json.Decode.Decoder Web3.Wallet.Msg"},{"name":"disconnect","comment":" Command to request wallet disconnection.\n","type":"Web3.Wallet.WalletCmd"},{"name":"encode","comment":" Encode a WalletCmd for the JS port.\n","type":"Web3.Wallet.WalletCmd -> Json.Encode.Value"},{"name":"getAddress","comment":" Extract the connected address, if any.\n","type":"Web3.Wallet.State -> Maybe.Maybe Web3.Types.Address"},{"name":"getChainId","comment":" Extract the connected chain ID, if any.\n","type":"Web3.Wallet.State -> Maybe.Maybe Web3.Types.ChainId"},{"name":"getPermissions","comment":" Command to get current wallet permissions (EIP-2255).\n","type":"Web3.Wallet.WalletCmd"},{"name":"isConnected","comment":" True if the wallet is in the Connected state.\n","type":"Web3.Wallet.State -> Basics.Bool"},{"name":"isReadOnly","comment":" True if in ReadOnly mode (rpcUrl present, no wallet). Reads work; writes will fail.\n","type":"Web3.Wallet.State -> Basics.Bool"},{"name":"requestPermissions","comment":" Command to request wallet permissions (EIP-2255).\n","type":"Web3.Wallet.WalletCmd"},{"name":"selectWallet","comment":" Command to select a specific wallet by its RDNS identifier (EIP-6963).\n\n    selectWallet \"io.metamask\"\n\n","type":"String.String -> Web3.Wallet.WalletCmd"},{"name":"startConnect","comment":" Transition to `Connecting` state before sending the `connect` port command.\n\nCall this when the user clicks the connect button, then send `connect` via the port:\n\n    ( { model | wallet = Wallet.startConnect model.wallet }\n    , web3Cmd (Wallet.encode Wallet.connect)\n    )\n\nValid transitions: `Disconnected → Connecting`, `Error _ → Connecting`.\nAll other states are unchanged (connecting while already connected is a no-op).\n\n","type":"Web3.Wallet.State -> Web3.Wallet.State"},{"name":"switchChain","comment":" Command to switch chain.\n","type":"Web3.Types.ChainId -> Web3.Wallet.WalletCmd"},{"name":"update","comment":" Update wallet state from a port message.\n","type":"Web3.Types.ChainId -> Web3.Wallet.Msg -> Web3.Wallet.State -> Web3.Wallet.State"},{"name":"watchAsset","comment":" Command to add a token to the wallet UI (EIP-747).\n","type":"{ address : Web3.Types.Address, symbol : String.String, decimals : Basics.Int, image : String.String } -> Web3.Wallet.WalletCmd"}],"binops":[]}]