[{"name":"Web3.Ui.AbiInput","comment":" Render a typed input for any Solidity argument shape -- `address`,\n`uint*`, `int*`, `bool`, `string`, `bytes`, `bytesN`, `T[]`, `T[N]`, and\n`tuple` (recursively).\n\nThe component is **stateless**, like every other primitive in this library.\nThe caller owns a `Value` per ABI argument and passes it back in on every\nrender. Validation feedback is wired the same way (`error : Maybe String`).\n\nA complete write flow looks like this:\n\n    -- in your Model\n    type alias Model =\n        { fooArg : Web3.Ui.AbiInput.Value\n        , fooError : Maybe String\n        , ...\n        }\n\n    -- in init\n    init =\n        { fooArg = Web3.Ui.AbiInput.initFor fooField, ... }\n\n    -- in your view\n    Web3.Ui.AbiInput.view []\n        { field = fooField\n        , value = model.fooArg\n        , onChange = FooArgChanged\n        , error = model.fooError\n        }\n\n    -- when the user clicks Send, parse to JSON\n    case Web3.Ui.AbiInput.parse fooField model.fooArg of\n        Ok jsonValue -> -- pass to Web3.Contract.Send\n        Err msg      -> ( { model | fooError = Just msg }, Cmd.none )\n\n@docs view, Config\n@docs Field, field, fieldName, fieldSolType, fieldChildren\n@docs Value, valueString, valueList, valueTuple\n@docs stringValue, listValues, tupleValues\n@docs initFor, parse, parseSlot\n\n","unions":[{"name":"Field","comment":" Description of one ABI argument: name, Solidity type string, and (for\ntuples) the recursive list of child fields.\n","args":[],"cases":[]},{"name":"Value","comment":" Opaque user-input value for an ABI argument of any shape.\n\nScalars carry their raw input buffer (a `String`); arrays carry a list of\ninner values; tuples carry an ordered list of field values matching their\n`Field.children`.\n","args":[],"cases":[]}],"aliases":[{"name":"Config","comment":" Configuration for `view`.\n","args":["msg"],"type":"{ field : Web3.Ui.AbiInput.Field, value : Web3.Ui.AbiInput.Value, onChange : Web3.Ui.AbiInput.Value -> msg, error : Maybe.Maybe String.String }"}],"values":[{"name":"field","comment":" Construct a `Field`. ","type":"{ name : String.String, solType : String.String, children : List.List Web3.Ui.AbiInput.Field } -> Web3.Ui.AbiInput.Field"},{"name":"fieldChildren","comment":" Extract a field's recursive children (tuple fields), empty otherwise. ","type":"Web3.Ui.AbiInput.Field -> List.List Web3.Ui.AbiInput.Field"},{"name":"fieldName","comment":" Extract the field's display name. ","type":"Web3.Ui.AbiInput.Field -> String.String"},{"name":"fieldSolType","comment":" Extract the field's Solidity type string (e.g. `\"uint256\"`, `\"address[]\"`). ","type":"Web3.Ui.AbiInput.Field -> String.String"},{"name":"initFor","comment":" Build the empty `Value` matching a `Field`'s shape.\n\nFor scalars, returns `valueString \"\"`. For dynamic arrays, an empty list.\nFor fixed arrays, a list of N empty inner values. For tuples, a tuple of\nempty values matching the field's children.\n","type":"Web3.Ui.AbiInput.Field -> Web3.Ui.AbiInput.Value"},{"name":"listValues","comment":" Extract array elements, if this is a list value. ","type":"Web3.Ui.AbiInput.Value -> Maybe.Maybe (List.List Web3.Ui.AbiInput.Value)"},{"name":"parse","comment":" Parse a `Value` against its `Field` into a `Json.Encode.Value` ready for\nthe JS port. Returns `Err message` on the first validation failure.\n","type":"Web3.Ui.AbiInput.Field -> Web3.Ui.AbiInput.Value -> Result.Result String.String Json.Encode.Value"},{"name":"parseSlot","comment":" Parse a `Value` against its `Field` into a [`Calldata.Slot`](Web3-Abi-Calldata#Slot).\n\nThis is the canonical parser for the pure-Elm calldata path -- codegen tools\nemit `parseSlot` calls and feed the resulting slots to\n[`Web3.Abi.Calldata.calldata`](Web3-Abi-Calldata#calldata) plus a baked\nselector, producing complete `\"0x...\"` calldata with zero JavaScript\ninvolvement.\n\nUse `parseSlot` when building `readCallRaw` / `writeCallRaw`; use the\nexisting [`parse`](#parse) when you need the legacy method+args port shape.\n","type":"Web3.Ui.AbiInput.Field -> Web3.Ui.AbiInput.Value -> Result.Result String.String Web3.Abi.Calldata.Slot"},{"name":"stringValue","comment":" Extract the raw input buffer from a scalar value, if it is one. ","type":"Web3.Ui.AbiInput.Value -> Maybe.Maybe String.String"},{"name":"tupleValues","comment":" Extract tuple field values, if this is a tuple value. ","type":"Web3.Ui.AbiInput.Value -> Maybe.Maybe (List.List Web3.Ui.AbiInput.Value)"},{"name":"valueList","comment":" Construct a list value (for `T[]` / `T[N]` arguments). ","type":"List.List Web3.Ui.AbiInput.Value -> Web3.Ui.AbiInput.Value"},{"name":"valueString","comment":" Construct a scalar value from a raw input buffer. ","type":"String.String -> Web3.Ui.AbiInput.Value"},{"name":"valueTuple","comment":" Construct a tuple value. ","type":"List.List Web3.Ui.AbiInput.Value -> Web3.Ui.AbiInput.Value"},{"name":"view","comment":" Render the input(s) for one ABI argument.\n\nComposes the existing `Web3.Ui.Input.*` primitives, recursing into arrays\nand tuples. Class names follow the BEM convention `web3-abi-input` /\n`web3-abi-input__label` / `web3-abi-input__error` / `web3-abi-input__list`\n/ `web3-abi-input__tuple`.\n","type":"List.List (Html.Attribute msg) -> Web3.Ui.AbiInput.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.AccountPill","comment":" The standard dapp header unit: one pill that is the whole wallet story.\n\nRenders per `Wallet.State`:\n\n  - `Disconnected` / `Error` -- a connect button\n  - `Connecting` -- the connect button, busy\n  - `ReadOnly` -- a \"read-only\" chip (no wallet, reads work)\n  - `Connected` -- identicon + truncated address + chain chip + optional\n    balance + disconnect\n  - `WrongChain` -- the same pill flagged, chain chip shows the mismatch\n\n<!---->\n\n    AccountPill.view\n        { onConnect = ConnectClicked\n        , onDisconnect = DisconnectClicked\n        , chainLabel = Chain.nameOf     -- T.ChainId -> String\n        , balance = model.plsBalance    -- Maybe String, pre-formatted\n        }\n        model.wallet\n\nCSS classes: `web3-pill`, `web3-pill--connect`, `--connecting`,\n`--readonly`, `--connected`, `--wrong-chain`; `web3-pill__identicon`,\n`web3-pill__address`, `web3-pill__chain`, `web3-pill__balance`,\n`web3-pill__disconnect`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" Handlers plus how to name a chain and what balance to show (already\nformatted -- pair with `Web3.Ui.Amount`).\n","args":["msg"],"type":"{ onConnect : msg, onDisconnect : msg, chainLabel : Web3.Types.ChainId -> String.String, balance : Maybe.Maybe String.String }"}],"values":[{"name":"view","comment":" Render the pill for any wallet state. ","type":"Web3.Ui.AccountPill.Config msg -> Web3.Wallet.State -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.ActivityRow","comment":" One row of an on-chain activity feed. The dapp pattern is universal:\n\"Alice bought 1.2M FOO\", \"Bob staked 50k BAR for 90 days\", \"0xABC graduated\".\nThis component renders one such row with a `Kind` enum that drives the\nicon and color modifier classes.\n\n    Web3.Ui.ActivityRow.view\n        { kind = Web3.Ui.ActivityRow.Buy\n        , primary = \"1.2M FOO\"\n        , secondary = Just \"by 0xABC...DEF\"\n        , atSec = activity.timestamp\n        , nowSec = model.nowSec\n        , onClick = Just (OpenTx activity.txHash)\n        }\n\nCSS classes: `web3-activityrow`, `web3-activityrow--buy/--sell/--stake/...`,\n`web3-activityrow__icon`, `web3-activityrow__primary`, `web3-activityrow__secondary`,\n`web3-activityrow__time`.\n\n@docs view, Kind, defaultLabel\n\n","unions":[{"name":"Kind","comment":" One of the standard on-chain activity kinds. Add more as the lib evolves. ","args":[],"cases":[["Buy",[]],["Sell",[]],["Stake",[]],["Unstake",[]],["Penalty",[]],["Create",[]],["Graduate",[]],["Claim",[]],["Other",["String.String"]]]}],"aliases":[],"values":[{"name":"defaultLabel","comment":" Default short label per kind, suitable as the icon's accessibility name. ","type":"Web3.Ui.ActivityRow.Kind -> String.String"},{"name":"view","comment":" Render an activity row. ","type":"{ kind : Web3.Ui.ActivityRow.Kind, primary : String.String, secondary : Maybe.Maybe String.String, atSec : Basics.Int, nowSec : Basics.Int, onClick : Maybe.Maybe msg } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Address","comment":" Address display and input components.\n\n    -- Display with optional explorer link:\n    Web3.Ui.Address.view []\n        { explorerUrl = Just \"https://etherscan.io/address/\" }\n        addr\n\n    -- Truncated address string (pure, no Html):\n    Web3.Ui.Address.short addr  --> \"0x1234...abcd\"\n\n    -- Truncated with custom lengths (e.g. token detail page):\n    Web3.Ui.Address.shortWith { prefixChars = 8, suffixChars = 6 } addr\n    --> \"0x123456...789abc\"\n\n    -- Address text input:\n    Web3.Ui.Address.input []\n        { value = model.addressInput\n        , onInput = AddressInputChanged\n        , valid = True\n        }\n\n@docs view, short, shortWith, input\n@docs copyable\n\n","unions":[],"aliases":[],"values":[{"name":"copyable","comment":" The short address display plus a copy affordance.\n\nThe button emits `onCopy <full 0x address>` -- the component states intent,\nthe APP performs the copy. This package ships zero JavaScript, so wire the\nmessage to a one-line port:\n\n    -- JS side of your app:\n    app.ports.copyToClipboard.subscribe(s => navigator.clipboard.writeText(s))\n\nCSS classes: `web3-address`, `web3-address__copy` (aria-label \"Copy address\").\n\n","type":"List.List (Html.Attribute msg) -> { onCopy : String.String -> msg, explorerUrl : Maybe.Maybe String.String } -> Web3.Types.Address -> Html.Html msg"},{"name":"input","comment":" A text input for address entry. Adds `web3-input-address--invalid` class\nwhen `valid` is `False`.\n\nThe host app is responsible for validating with `T.address`.\n\nCSS class: `web3-input-address` (plus `web3-input-address--invalid` when invalid)\n\n","type":"List.List (Html.Attribute msg) -> { value : String.String, onInput : String.String -> msg, valid : Basics.Bool } -> Html.Html msg"},{"name":"short","comment":" Truncate an address to \"0x\" + first 4 hex chars + \"...\" + last 4 hex chars.\n\n    short addr  --> \"0x1234...abcd\"\n\n","type":"Web3.Types.Address -> String.String"},{"name":"shortWith","comment":" Truncate an address with configurable prefix and suffix lengths.\n\n`prefixChars` is the total number of leading characters including `\"0x\"`.\n`suffixChars` is the number of trailing hex characters.\n\n    shortWith { prefixChars = 6, suffixChars = 4 } addr\n    --> \"0x1234...abcd\"   (same as short)\n\n    shortWith { prefixChars = 8, suffixChars = 6 } addr\n    --> \"0x123456...789abc\"\n\n","type":"{ prefixChars : Basics.Int, suffixChars : Basics.Int } -> Web3.Types.Address -> String.String"},{"name":"view","comment":" Display an address as \"0x1234...abcd\". Optionally wraps in an `<a>` tag\nwhen `explorerUrl` is `Just url`.\n\nCSS class: `web3-address`\n\n","type":"List.List (Html.Attribute msg) -> { explorerUrl : Maybe.Maybe String.String } -> Web3.Types.Address -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Amount","comment":" Token amount input and display with SI suffix formatting.\n\n    -- Amount input with inline symbol label:\n    Web3.Ui.Amount.amountInput []\n        { value = model.amountStr\n        , onInput = AmountChanged\n        , decimals = 18\n        , symbol = \"PLS\"\n        , valid = True\n        }\n\n    -- Format a Wei value as human-readable (caller appends symbol):\n    Web3.Ui.Amount.formatWei 18 weiAmount ++ \" PLS\"\n    --> \"1.23M PLS\"\n\n@docs amountInput, formatWei, presetRow, formatWeiDust\n\n","unions":[],"aliases":[],"values":[{"name":"amountInput","comment":" Numeric text input for a token amount. `value` is a plain decimal string\n(e.g. `\"1000.5\"`). No parsing is done here -- call `Web3.Units.parseUnits` in\nyour `update` to convert to Wei.\n\nAdds `web3-amount-input--invalid` when `valid` is `False`.\n\nCSS classes: `web3-amount-wrapper` (outer div), `web3-amount-input` (input),\n`web3-amount-symbol` (symbol label)\n\n","type":"List.List (Html.Attribute msg) -> { value : String.String, onInput : String.String -> msg, decimals : Basics.Int, symbol : String.String, valid : Basics.Bool } -> Html.Html msg"},{"name":"formatWei","comment":" Format a Wei BigInt as a human-readable string with SI suffix.\n\nThe symbol is not appended -- concatenate it yourself so the caller controls\nspacing and placement.\n\n    formatWei 18 onePls   --> \"1\"\n    formatWei 18 largePls --> \"1.23M\"\n    formatWei 6  oneUsdc  --> \"1\"\n\nSI suffixes: K (10^3), M (10^6), B (10^9), T (10^12).\nValues below 1000 are shown with up to 2 decimal places, trailing zeros trimmed.\nA nonzero value under 0.01 (which a flat 2dp would render as `\"0.00\"`) instead\nshows its first two significant fractional digits (e.g. `\"0.0000019\"`) so real\nsub-cent balances never vanish.\n\nAll formatting stays in integer/string space -- the amount is never routed\nthrough `Float`, so neither very small nor very large values lose precision.\n\n","type":"Basics.Int -> Web3.BigInt.BigInt -> String.String"},{"name":"formatWeiDust","comment":" Like [`formatWei`](#formatWei), with the dust convention: a nonzero\namount that would display as zero (below 0.0001 of the unit) renders as\n`\"<0.0001\"` instead -- \"0\" must mean zero, never \"too small to show\".\n","type":"Basics.Int -> Web3.BigInt.BigInt -> String.String"},{"name":"presetRow","comment":" A row of balance-percentage chips -- 25% / 50% / 75% / MAX -- the\ncompanion every [`amountInput`](#amountInput) deserves. Emits the chosen\npercentage (100 = MAX); the app computes the actual amount from the live\nbalance, so the chips never display a stale number.\n\n    Web3.Ui.Amount.presetRow [] { onPick = FillPercent }\n\n    -- FillPercent 100 -> set input to formatUnits decimals balance\n\nCSS classes: `web3-amount-presets`, `web3-amount-presets__chip`,\n`web3-amount-presets__chip--max`.\n\n","type":"List.List (Html.Attribute msg) -> { onPick : Basics.Int -> msg } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.ApprovalFlow","comment":" The approve-then-act state machine -- the most repeated flow in all of\nweb3. Every ERC-20 interaction (deposit, stake, swap, lock...) is really two\ntransactions gated by an allowance read, and every dapp hand-rolls the same\nfragile ladder. This is that ladder, once, with the guards proven by fuzz\ntests (`ApprovalFlowTest`) and modeled in `proofs/tla/ApprovalSpec.tla`.\n\n    +------------------+  insufficient   +----------------+\n    | CheckingAllowance+---------------->| ApprovalNeeded |\n    +-------+----------+                 +-------+--------+\n            | sufficient                 startApprove\n            v                                    v\n    +--------------+   approve confirmed +------------+\n    |  ReadyToAct  |<---(via re-check)---|  Approving |\n    +-------+------+                     +------------+\n       startAction\n            v\n    +--------------+    tx confirmed     +------------+\n    |    Acting    +-------------------->|  Completed |\n    +--------------+                     +------------+\n\nDesign choices, deliberately:\n\n  - **A confirmed approval re-checks the allowance** instead of optimistically\n    assuming it. Non-standard tokens (USDT's approve-to-zero-first rule,\n    fee-on-transfer tokens) make optimism a lie; the chain is the truth.\n  - **Wallet rejection is not failure.** Rejecting the approve returns to\n    `ApprovalNeeded`; rejecting the action returns to `ReadyToAct` -- the user\n    changed their mind, nothing is broken.\n  - **The two transactions cannot cross.** `ApproveTx` messages are ignored\n    while `Acting`, `ActionTx` messages while `Approving` -- a late\n    confirmation from one leg can never corrupt the other (the same\n    no-cross-confusion rule elm-web3's SignSpec proves).\n\nWire-up sketch:\n\n    -- fire the allowance read, then:\n    ApprovalFlow.update { required = amountWei } (AllowanceLoaded current) step\n\n    -- user clicks approve:\n    ( { model | step = ApprovalFlow.startApprove model.step }\n    , web3Cmd approveCall\n    )\n\n    -- port messages for the approve tx:\n    ApprovalFlow.update req (ApproveTx txMsg) step\n\n@docs Step, Msg\n@docs start, update, startApprove, startAction, reset\n@docs isTerminal, needsApproval\n@docs view, Config\n\n","unions":[{"name":"Msg","comment":" Inputs to the machine. `ApproveTx`/`ActionTx` wrap the port's\n`Tx.Msg` for whichever leg it belongs to -- route by your correlation id.\n","args":[],"cases":[["AllowanceLoaded",["Web3.BigInt.BigInt"]],["AllowanceFailed",["String.String"]],["ApproveTx",["Web3.Transaction.Msg"]],["ActionTx",["Web3.Transaction.Msg"]],["Retry",[]]]},{"name":"Step","comment":" Where the flow is. `Approving`/`Acting` carry the inner transaction\nlifecycle; `Blocked` carries the error that stopped the flow.\n","args":[],"cases":[["CheckingAllowance",[]],["ApprovalNeeded",[]],["Approving",["Web3.Transaction.Status"]],["ReadyToAct",[]],["Acting",["Web3.Transaction.Status"]],["Completed",["Web3.Transaction.Receipt"]],["Blocked",["String.String"]]]}],"aliases":[{"name":"Config","comment":" Labels and handlers for the two-step control. ","args":["msg"],"type":"{ approveLabel : String.String, actionLabel : String.String, onApprove : msg, onAction : msg, onRetry : msg }"}],"values":[{"name":"isTerminal","comment":" True in `Completed` -- the flow finished; only [`reset`](#reset) leaves. ","type":"Web3.Ui.ApprovalFlow.Step -> Basics.Bool"},{"name":"needsApproval","comment":" True when the user's next click should be Approve. ","type":"Web3.Ui.ApprovalFlow.Step -> Basics.Bool"},{"name":"reset","comment":" Leave a finished or blocked flow: `Completed`/`Blocked ->\nCheckingAllowance` (a fresh flow re-reads the chain). No-op elsewhere.\n","type":"Web3.Ui.ApprovalFlow.Step -> Web3.Ui.ApprovalFlow.Step"},{"name":"start","comment":" The flow begins by asking the chain what is already approved. ","type":"Web3.Ui.ApprovalFlow.Step"},{"name":"startAction","comment":" User clicked the action: `ReadyToAct -> Acting AwaitingSignature`.\nNo-op from every other step -- the only door into `Acting`, which is what\nmakes \"never act on an unverified allowance\" a structural guarantee rather\nthan a convention.\n","type":"Web3.Ui.ApprovalFlow.Step -> Web3.Ui.ApprovalFlow.Step"},{"name":"startApprove","comment":" User clicked Approve: `ApprovalNeeded -> Approving AwaitingSignature`.\nNo-op from every other step -- this is the only door into `Approving`.\n","type":"Web3.Ui.ApprovalFlow.Step -> Web3.Ui.ApprovalFlow.Step"},{"name":"update","comment":" Advance the machine. `required` is the amount the action needs approved.\n","type":"{ required : Web3.BigInt.BigInt } -> Web3.Ui.ApprovalFlow.Msg -> Web3.Ui.ApprovalFlow.Step -> Web3.Ui.ApprovalFlow.Step"},{"name":"view","comment":" A two-step control: numbered step indicator plus the one button that is\nvalid right now. The root carries a state modifier so every step is\nindependently styleable.\n\nCSS classes: `web3-approval`, `web3-approval--checking`, `--approve`,\n`--approving`, `--ready`, `--acting`, `--done`, `--blocked`;\n`web3-approval__steps`, `web3-approval__step`, `web3-approval__step--active`,\n`web3-approval__step--done`, `web3-approval__button`, `web3-approval__error`.\n\n","type":"Web3.Ui.ApprovalFlow.Config msg -> Web3.Ui.ApprovalFlow.Step -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Balance","comment":" Balance display components.\n\nUses `Web3.Units.formatUnits` / `Web3.Units.formatEther` internally.\n\n    -- ERC-20 token with 6 decimals:\n    Web3.Ui.Balance.view []\n        { decimals = 6, symbol = \"USDC\" }\n        amount\n\n    -- Native ETH/PLS balance:\n    Web3.Ui.Balance.viewEther []\n        { symbol = \"ETH\" }\n        weiAmount\n\n    -- Balance that may not have loaded yet:\n    Web3.Ui.Balance.viewMaybe []\n        { decimals = 6, symbol = \"USDC\", loading = \"...\" }\n        model.balance\n\n    -- Ether balance that may not have loaded yet:\n    Web3.Ui.Balance.viewEtherMaybe []\n        { symbol = \"ETH\", loading = \"...\" }\n        model.ethBalance\n\n@docs view, viewEther, viewMaybe, viewEtherMaybe\n\n","unions":[],"aliases":[],"values":[{"name":"view","comment":" Display a token balance formatted with the given decimal count and symbol.\n\n    view [] { decimals = 6, symbol = \"USDC\" } amount\n    --> <span class=\"web3-balance\">1.5 USDC</span>\n\nCSS class: `web3-balance`\n\n","type":"List.List (Html.Attribute msg) -> { decimals : Basics.Int, symbol : String.String } -> Web3.BigInt.BigInt -> Html.Html msg"},{"name":"viewEther","comment":" Display a Wei amount as ether (18 decimals) with the given symbol.\n\n    viewEther [] { symbol = \"ETH\" } weiAmount\n    --> <span class=\"web3-balance\">1.5 ETH</span>\n\nCSS class: `web3-balance`\n\n","type":"List.List (Html.Attribute msg) -> { symbol : String.String } -> Web3.BigInt.BigInt -> Html.Html msg"},{"name":"viewEtherMaybe","comment":" Display a Wei balance that may not have loaded yet (18 decimals).\n\nDelegates to `viewMaybe` with `decimals = 18`.\n\nCSS classes: `web3-balance`, `web3-balance--loading` (loading state only)\n\n","type":"List.List (Html.Attribute msg) -> { symbol : String.String, loading : String.String } -> Maybe.Maybe Web3.BigInt.BigInt -> Html.Html msg"},{"name":"viewMaybe","comment":" Display a token balance that may not have loaded yet.\n\nShows `opts.loading` with class `web3-balance--loading` while `Nothing`.\nRenders the same as `view` once the balance arrives.\n\nCSS classes: `web3-balance`, `web3-balance--loading` (loading state only)\n\n","type":"List.List (Html.Attribute msg) -> { decimals : Basics.Int, symbol : String.String, loading : String.String } -> Maybe.Maybe Web3.BigInt.BigInt -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.BlockRefresh","comment":" A refresh policy for chain-derived data -- the missing piece between\n`Block.watchBlockNumber` and your `RemoteCall`s.\n\nChain data goes stale exactly once per block, so refetch cadence should be\nexpressed in blocks, not milliseconds:\n\n    type alias Model =\n        { ticker : BlockRefresh.Ticker\n        , balance : RemoteCall String\n        }\n\n    -- init\n    { ticker = BlockRefresh.init (EveryNBlocks 5), ... }\n\n    -- on each { tag = \"blockNumber\" } port message:\n    let\n        ( ticker, refetch ) =\n            BlockRefresh.onBlock number model.ticker\n    in\n    if refetch then\n        ( { model | ticker = BlockRefresh.markRefreshed number ticker\n          , balance = RemoteCall.request \"bal-...\" model.balance }\n        , web3Cmd (Balance.encode ...)\n        )\n    else\n        ( { model | ticker = ticker }, Cmd.none )\n\nThat worked example IS the balance watcher: balances re-read on-cadence,\ndriven by chain time. Any `RemoteCall` slots in the same way.\n\n@docs Policy, Ticker\n@docs init, withPolicy, onBlock, markRefreshed\n\n","unions":[{"name":"Policy","comment":" `EveryBlock` -- refetch on every new block. `EveryNBlocks n` -- refetch\nwhen at least `n` blocks have passed since the last refresh. `Manual` --\nnever fire from block signals (the app refetches explicitly).\n","args":[],"cases":[["EveryBlock",[]],["EveryNBlocks",["Basics.Int"]],["Manual",[]]]},{"name":"Ticker","comment":" Tracks the policy and the block height of the last refresh. Opaque. ","args":[],"cases":[]}],"aliases":[],"values":[{"name":"init","comment":" ","type":"Web3.Ui.BlockRefresh.Policy -> Web3.Ui.BlockRefresh.Ticker"},{"name":"markRefreshed","comment":" Record that the refetch was actually fired at this height. Separate from\n[`onBlock`](#onBlock) so a refetch you decided to skip (e.g. one already in\nflight) doesn't advance the watermark.\n","type":"Basics.Int -> Web3.Ui.BlockRefresh.Ticker -> Web3.Ui.BlockRefresh.Ticker"},{"name":"onBlock","comment":" Feed a new block number in; the `Bool` says \"fire your refetch now\".\nThe first observed block always fires (except under `Manual`) -- data that\nhas never been fetched is infinitely stale.\n","type":"Basics.Int -> Web3.Ui.BlockRefresh.Ticker -> ( Web3.Ui.BlockRefresh.Ticker, Basics.Bool )"},{"name":"withPolicy","comment":" Change cadence, keeping the last-refreshed watermark. ","type":"Web3.Ui.BlockRefresh.Policy -> Web3.Ui.BlockRefresh.Ticker -> Web3.Ui.BlockRefresh.Ticker"}],"binops":[]},{"name":"Web3.Ui.BondCard","comment":" Card for a fixed-term bond receipt: principal locked for a maturity\nperiod, accruing some pro-rata yield from a shared pool, redeemable at\nmaturity (or rolled for another term). Generic for any term-deposit\nprimitive (OHM-style bonds, fixed-term deposits, treasury bills, ...).\n\n```elm\nWeb3.Ui.BondCard.view\n    { bondId = receipt.id\n    , principal = receipt.principal\n    , principalSymbol = \"PLS\"\n    , decimals = 18\n    , maturitySec = receipt.maturity\n    , nowSec = model.nowSec\n    , pendingYield = receipt.pendingYield\n    , yieldSymbol = \"PLS\"\n    , onClaimYield = Just (ClaimYield receipt.id)\n    , onRedeem = Just (Redeem receipt.id)\n    , onRoll = Just (Roll receipt.id)\n    }\n```\n\nCSS classes: `web3-bondcard`, `web3-bondcard__id`, `web3-bondcard__principal`,\n`web3-bondcard__maturity`, `web3-bondcard__yield`, `web3-bondcard__actions`,\n`web3-bondcard__action`. Maturity gets a `--matured` modifier once\n`nowSec >= maturitySec`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ bondId : Basics.Int, principal : Web3.BigInt.BigInt, principalSymbol : String.String, decimals : Basics.Int, maturitySec : Basics.Int, nowSec : Basics.Int, pendingYield : Web3.BigInt.BigInt, yieldSymbol : String.String, onClaimYield : Maybe.Maybe msg, onRedeem : Maybe.Maybe msg, onRoll : Maybe.Maybe msg }"}],"values":[{"name":"view","comment":" Render the bond-receipt card. ","type":"Web3.Ui.BondCard.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.BondingCurve","comment":" SVG sparkline of an `A * x^N` bonding-curve price function. Renders the\nprice-vs-supply curve from supply 0 to a chosen max, with optional markers\nfor the current spot price and floor price.\n\n    Web3.Ui.BondingCurve.sparkline\n        { coeffA = curve.coeffA\n        , exponent = 1.1\n        , supply = curve.supply\n        , maxSupply = maxSupply\n        , floorPrice = Just curve.floorPrice\n        , width = 320\n        , height = 80\n        }\n\nThe component is generic for any curve of shape `price = A * supply^N`,\nwhich covers `x^1.1`, classic `x^2`, and any sub-/super-linear\nissuance model. Math runs on `Float` because the chart is purely visual --\ndo not consume the rendered points for on-chain calculation.\n\nCSS classes: `web3-bondingcurve`, `web3-bondingcurve__path`,\n`web3-bondingcurve__spot`, `web3-bondingcurve__floor`. Stroke colors come\nfrom CSS.\n\n@docs sparkline\n\n","unions":[],"aliases":[],"values":[{"name":"sparkline","comment":" Render the sparkline. ","type":"{ coeffA : Web3.BigInt.BigInt, exponent : Basics.Float, supply : Web3.BigInt.BigInt, maxSupply : Web3.BigInt.BigInt, floorPrice : Maybe.Maybe Web3.BigInt.BigInt, width : Basics.Int, height : Basics.Int } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.ChainGate","comment":" Render content only when the wallet is on the expected chain.\n\nA common pattern in every dapp: show the actual UI when connected to the right\nnetwork, show a prompt to switch otherwise. This wraps that pattern so it does\nnot have to be repeated in every view.\n\n    Web3.Ui.ChainGate.chainGate model.wallet\n        (Chain.chainId Chain.pulsechain)\n        (Html.text \"Please switch to PulseChain\")\n        actualContent\n\n@docs chainGate\n\n","unions":[],"aliases":[],"values":[{"name":"chainGate","comment":" Render `contentView` when the wallet is `Connected` on `expectedChain`.\nRender `wrongChainView` for every other state (disconnected, wrong chain,\nread-only, connecting, error).\n\n","type":"Web3.Wallet.State -> Web3.Types.ChainId -> Html.Html msg -> Html.Html msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.ChainSelector","comment":" Pick a chain from the set your dapp supports, wired to the\nswitch-chain flow.\n\n    ChainSelector.view\n        { entries =\n            [ { chainId = 369, label = \"PulseChain\" }\n            , { chainId = 1, label = \"Ethereum\" }\n            ]\n        , current = Wallet.getChainId model.wallet |> Maybe.map T.chainIdToInt\n        , onSelect = SwitchChainTo   -- then: web3Cmd (Wallet.encode (Wallet.switchChain ...))\n        }\n\nSelecting an entry is a *request* -- render the truth from `Wallet.State`,\nnever optimistically: the pill only moves when `SwitchChainOk` /\n`ChainChanged` actually arrives. Chains the wallet doesn't know yet are the\n`Wallet.addChain` (EIP-3085) flow; fire it from your `onSelect` handler when\nthe switch errors.\n\nRendered as a `role=\"radiogroup\"` of buttons -- keyboard and screen-reader\nnative.\n\nCSS classes: `web3-chains`, `web3-chains__option`,\n`web3-chains__option--current`.\n\n@docs view, Config, Entry\n\n","unions":[],"aliases":[{"name":"Config","comment":" The supported set, the currently-connected chain id (if any), and the\nswitch request handler.\n","args":["msg"],"type":"{ entries : List.List Web3.Ui.ChainSelector.Entry, current : Maybe.Maybe Basics.Int, onSelect : Basics.Int -> msg }"},{"name":"Entry","comment":" One selectable chain. ","args":[],"type":"{ chainId : Basics.Int, label : String.String }"}],"values":[{"name":"view","comment":" Render the selector. ","type":"List.List (Html.Attribute msg) -> Web3.Ui.ChainSelector.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.ContractRead","comment":" Render a Solidity `view` / `pure` function call as a typed form: function\nname header, one [`AbiInput`](Web3-Ui-AbiInput) per argument, a \"Read\"\nbutton, and a result panel that pattern-matches on the current `Status`.\n\nLike every primitive in this lib, the component is **stateless** -- the\ncaller owns the `args` (one `AbiInput.Config` per argument) and the\n`status` and passes them in on every render. The lib renders, the caller\nwires the result back through `update`.\n\nPair with [`Web3.Contract.Call`](https://package.elm-lang.org/packages/intrepidshape/elm-web3/latest/Web3-Contract-Call)\nto actually fire the read:\n\n    -- in update\n    Read fn ->\n        ( { model | reads = Dict.insert fn Pending model.reads }\n        , web3Cmd\n            (Call.encode\n                (Call.readCall\n                    { contract = tokenAddress\n                    , method = \"balanceOf(address)\"\n                    , args = [ resolvedArg ]\n                    , decoder = Decode.uint256\n                    , id = fn\n                    }\n                )\n            )\n        )\n\n    -- in view\n    Web3.Ui.ContractRead.view []\n        { name = \"balanceOf\"\n        , solType = \"uint256\"\n        , args = [ holderArgConfig ]\n        , status = Maybe.withDefault Idle (Dict.get \"balanceOf\" model.reads)\n        , onRead = Read \"balanceOf\"\n        , readLabel = \"Read\"\n        }\n\nCSS classes: `web3-contract-read`, `web3-contract-read__header`,\n`web3-contract-read__name`, `web3-contract-read__return-type`,\n`web3-contract-read__args`, `web3-contract-read__button`,\n`web3-contract-read__pending`, `web3-contract-read__result`,\n`web3-contract-read__result--success`, `web3-contract-read__result--failed`.\n\n@docs view, Config\n@docs Status\n@docs isIdle, isPending, isTerminal\n\n","unions":[{"name":"Status","comment":" Lifecycle of a single read.\n\n`Success String` carries the rendered result -- the caller stringifies the\ntyped value (using whatever `Web3.Abi.Decode`-produced value they decoded).\nThis keeps `ContractRead` agnostic to the result type.\n","args":[],"cases":[["Idle",[]],["Pending",[]],["Success",["String.String"]],["Failed",["String.String"]]]}],"aliases":[{"name":"Config","comment":" Configuration for `view`.\n\n  - `name` -- the function name to display (e.g. `\"balanceOf\"`).\n  - `solType` -- the return type string for display (e.g. `\"uint256\"`).\n  - `args` -- one [`AbiInput`](Web3-Ui-AbiInput) config per ABI argument.\n  - `status` -- current lifecycle state.\n  - `onRead` -- message dispatched when the user clicks the read button.\n  - `readLabel` -- button label when idle / terminal (e.g. `\"Read\"` or `\"Refresh\"`).\n\n","args":["msg"],"type":"{ name : String.String, solType : String.String, args : List.List (Web3.Ui.AbiInput.Config msg), status : Web3.Ui.ContractRead.Status, onRead : msg, readLabel : String.String }"}],"values":[{"name":"isIdle","comment":" `True` for `Idle`. ","type":"Web3.Ui.ContractRead.Status -> Basics.Bool"},{"name":"isPending","comment":" `True` for `Pending`. ","type":"Web3.Ui.ContractRead.Status -> Basics.Bool"},{"name":"isTerminal","comment":" `True` for `Success _` and `Failed _`. ","type":"Web3.Ui.ContractRead.Status -> Basics.Bool"},{"name":"view","comment":" Render the read form. ","type":"List.List (Html.Attribute msg) -> Web3.Ui.ContractRead.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.ContractWrite","comment":" Render a Solidity write function (non-payable or payable) as a typed\nform: function name header, one [`AbiInput`](Web3-Ui-AbiInput) per\nargument, an optional `msg.value` input for `payable` functions, a \"Send\"\nbutton wired through the [`Tx.Status`](https://package.elm-lang.org/packages/intrepidshape/elm-web3/latest/Web3-Transaction#Status)\nstate machine, and a status badge plus tx-hash link below.\n\nLike every primitive in this lib, the component is **stateless** -- the\ncaller owns the `args`, the `msgValue` input buffer, and the `txStatus`\nand passes them in on every render.\n\nA complete write flow looks like this:\n\n    -- in update\n    Send fn ->\n        case resolveArgs model fn of\n            Ok args ->\n                ( { model | tx = Tx.update Tx.TxSent model.tx }\n                , web3Cmd\n                    (Send.encode\n                        (Send.writeCall\n                            { contract = tokenAddress\n                            , method = \"approve\"\n                            , args = args\n                            }\n                        )\n                    )\n                )\n\n            Err _ ->\n                ( model, Cmd.none )\n\n    -- in view\n    Web3.Ui.ContractWrite.view []\n        { name = \"approve\"\n        , args = [ spenderArg, amountArg ]\n        , payable = Nothing\n        , txStatus = model.tx\n        , onSend = Send \"approve\"\n        , sendLabel = \"Approve\"\n        , pendingLabel = \"Approving...\"\n        , explorerUrl = Just \"https://scan.pulsechain.com/tx/\"\n        }\n\nCSS classes: `web3-contract-write`, `web3-contract-write__header`,\n`web3-contract-write__name`, `web3-contract-write__mutability`,\n`web3-contract-write__args`, `web3-contract-write__value`,\n`web3-contract-write__value-label`, `web3-contract-write__footer`,\n`web3-contract-write__error`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" Configuration for `view`.\n\n  - `name` -- the function name to display (e.g. `\"approve\"`).\n  - `args` -- one [`AbiInput`](Web3-Ui-AbiInput) config per ABI argument.\n  - `payable` -- for `payable` functions, the `msg.value` input state and\n    callback; `Nothing` for non-payable.\n  - `txStatus` -- current transaction lifecycle state.\n  - `onSend` -- message dispatched when the user clicks the send button.\n  - `sendLabel` -- button label when the tx is idle (e.g. `\"Approve\"`).\n  - `pendingLabel` -- button label while the tx is in-flight (e.g. `\"Approving...\"`).\n  - `explorerUrl` -- optional explorer URL prefix for the tx-hash link;\n    `Nothing` renders the hash as plain text (useful in local dev).\n\n","args":["msg"],"type":"{ name : String.String, args : List.List (Web3.Ui.AbiInput.Config msg), payable : Maybe.Maybe { value : String.String, onValueChange : String.String -> msg, valid : Basics.Bool }, txStatus : Web3.Transaction.Status, onSend : msg, sendLabel : String.String, pendingLabel : String.String, explorerUrl : Maybe.Maybe String.String }"}],"values":[{"name":"view","comment":" Render the write form. ","type":"List.List (Html.Attribute msg) -> Web3.Ui.ContractWrite.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Deadline","comment":" Transaction deadline picker -- preset chips plus a custom minutes input,\nthe sibling of `Web3.Ui.SlippageInput`.\n\n    Web3.Ui.Deadline.view\n        { valueMinutes = model.deadlineMinutes\n        , onChange = DeadlineChanged\n        , presetsMinutes = [ 10, 20, 30 ]\n        }\n\n    -- At send time, turn minutes into the unix deadline the contract wants:\n    deadline =\n        Deadline.toUnixDeadline model.deadlineMinutes nowMillis\n\nCSS classes: `web3-deadline`, `web3-deadline__chip`,\n`web3-deadline__chip--active`, `web3-deadline__custom`.\n\n@docs view, Config, toUnixDeadline\n\n","unions":[],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ valueMinutes : Basics.Int, onChange : Basics.Int -> msg, presetsMinutes : List.List Basics.Int }"}],"values":[{"name":"toUnixDeadline","comment":" Unix timestamp (seconds) `minutes` from a `Time.posixToMillis` now. ","type":"Basics.Int -> Basics.Int -> Basics.Int"},{"name":"view","comment":" Render the picker. ","type":"Web3.Ui.Deadline.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.EventFeed","comment":" A live on-chain event feed -- the binder between `Web3.Subscription` log\nstreams and rendered rows.\n\nThe port layer answers a subscription request with\n`{ tag = \"subscribed\", status = \"open\" | \"failed\" }` -- `failed` meaning the\nWebSocket was unavailable and the bridge fell back to polling, so events\nstill flow, just with more latency. The `Feed` tracks that honestly (a\n`Fallback` chip, not a lie of liveness), keeps items newest-first, and\nenforces a cap so a hot contract can't grow your model without bound.\n\n    -- MODEL\n    { feed : EventFeed.Feed TokenCreated }\n\n    -- port `subscribed` for our correlation id:\n    { model | feed = EventFeed.onSubscribed { open = ok } model.feed }\n\n    -- each decoded LogEvent:\n    { model | feed = EventFeed.push tokenCreated model.feed }\n\n    -- VIEW\n    EventFeed.view\n        { onLoadMore = Just LoadOlder }   -- app fires a getLogs range query\n        viewTokenCreated\n        model.feed\n\nGeneric over the item type -- the app decodes its own events; this module\nnever guesses at your ABI.\n\nCSS classes: `web3-eventfeed`, `web3-eventfeed__status`,\n`web3-eventfeed__status--connecting/--live/--fallback/--closed`,\n`web3-eventfeed__list`, `web3-eventfeed__more`.\n\n@docs Feed, Status\n@docs init, onSubscribed, push, items, status, closed\n@docs view, Config\n\n","unions":[{"name":"Feed","comment":" The feed. Opaque -- the cap and ordering are invariants, not suggestions. ","args":["item"],"cases":[]},{"name":"Status","comment":" Where the stream stands. `Fallback` = events arrive by polling. ","args":[],"cases":[["Connecting",[]],["Live",[]],["Fallback",[]],["Closed",[]]]}],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ onLoadMore : Maybe.Maybe msg }"}],"values":[{"name":"closed","comment":" Mark the stream closed (unwatch / teardown). ","type":"Web3.Ui.EventFeed.Feed item -> Web3.Ui.EventFeed.Feed item"},{"name":"init","comment":" A feed awaiting its subscription confirmation. Caps below 1 are lifted\nto 1 -- a feed that can hold nothing is not a feed.\n","type":"{ cap : Basics.Int } -> Web3.Ui.EventFeed.Feed item"},{"name":"items","comment":" Newest first. ","type":"Web3.Ui.EventFeed.Feed item -> List.List item"},{"name":"onSubscribed","comment":" Apply the port's `subscribed` answer: `open = True` -> `Live`,\n`open = False` -> `Fallback` (the bridge is polling; events still flow).\n","type":"{ open : Basics.Bool } -> Web3.Ui.EventFeed.Feed item -> Web3.Ui.EventFeed.Feed item"},{"name":"push","comment":" Prepend an event. The oldest entries fall off past the cap. ","type":"item -> Web3.Ui.EventFeed.Feed item -> Web3.Ui.EventFeed.Feed item"},{"name":"status","comment":" ","type":"Web3.Ui.EventFeed.Feed item -> Web3.Ui.EventFeed.Status"},{"name":"view","comment":" Render the feed: status chip, `aria-live` list, optional load-more.\n","type":"Web3.Ui.EventFeed.Config msg -> (item -> Html.Html msg) -> Web3.Ui.EventFeed.Feed item -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.FeeBreakdown","comment":" Render a fee-split table. Each row is one slice of the fee -- the caller\nprovides labels, basis points, and (optionally) the Wei amount that slice will\nreceive at the current trade size.\n\n    Web3.Ui.FeeBreakdown.view\n        { totalBps = 150\n        , symbol = \"PLS\"\n        , decimals = 18\n        , gross = Just tradeGrossWei\n        , slices =\n            [ { label = \"Creator\", bps = 50, recipient = Just creatorAddr }\n            , { label = \"Protocol\", bps = 10, recipient = Nothing }\n            , { label = \"Platform\", bps = 20, recipient = Nothing }\n            , { label = \"Token pool\", bps = 70, recipient = Nothing }\n            ]\n        }\n\nStyle classes: `web3-feebreakdown`, `web3-feebreakdown__row`, `web3-feebreakdown__label`,\n`web3-feebreakdown__bps`, `web3-feebreakdown__amount`, `web3-feebreakdown__total`.\n\n@docs view, Slice\n\n","unions":[],"aliases":[{"name":"Slice","comment":" One row of the fee table. ","args":[],"type":"{ label : String.String, bps : Basics.Int, recipient : Maybe.Maybe Web3.Types.Address }"}],"values":[{"name":"view","comment":" Render the breakdown table. ","type":"{ totalBps : Basics.Int, symbol : String.String, decimals : Basics.Int, gross : Maybe.Maybe Web3.BigInt.BigInt, slices : List.List Web3.Ui.FeeBreakdown.Slice } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.FeeFlowDiagram","comment":" Educational visualization of a fee split -- a horizontal stacked bar with\nproportional segments, plus a label and Wei amount under each. Pairs with\n`Web3.Ui.FeeBreakdown` (which is the equivalent table view); use this one\nwhen you want a hero-tier \"where does my fee go?\" graphic on a marketing\npage or trade modal.\n\nThe bar segments are sized by basis points; total `bps` does not need to\nsum to 10000 (the bar is normalized to its own total).\n\nEach `Slice` accepts an optional `kind` string -- emitted as a CSS class\nsuffix (`web3-feeflow__seg--<kind>`) so the consumer can color the\nsegments by recipient type without inline styles.\n\n```elm\nWeb3.Ui.FeeFlowDiagram.view\n    { gross = tradeFee\n    , symbol = \"PLS\"\n    , decimals = 18\n    , width = 480\n    , height = 24\n    , slices =\n        [ { label = \"veToken holders\", bps = 3000, kind = Just \"ve\" }\n        , { label = \"stakers\",        bps = 3000, kind = Just \"stakers\" }\n        , { label = \"floor pool\",     bps = 2000, kind = Just \"floor\" }\n        , { label = \"buy & burn\",     bps = 1000, kind = Just \"burn\" }\n        , { label = \"treasury\",       bps = 1000, kind = Just \"treasury\" }\n        ]\n    }\n```\n\nCSS classes: `web3-feeflow`, `web3-feeflow__bar`, `web3-feeflow__seg`,\n`web3-feeflow__seg--<kind>`, `web3-feeflow__legend`,\n`web3-feeflow__legend-item`, `web3-feeflow__legend-swatch`,\n`web3-feeflow__legend-label`, `web3-feeflow__legend-amount`.\n\n@docs view, Slice\n\n","unions":[],"aliases":[{"name":"Slice","comment":" One fee segment. `kind` becomes the CSS modifier suffix; pass\n`Nothing` for a default-colored segment. ","args":[],"type":"{ label : String.String, bps : Basics.Int, kind : Maybe.Maybe String.String }"}],"values":[{"name":"view","comment":" Render the diagram (bar + legend). ","type":"{ gross : Web3.BigInt.BigInt, symbol : String.String, decimals : Basics.Int, width : Basics.Int, height : Basics.Int, slices : List.List Web3.Ui.FeeFlowDiagram.Slice } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Form","comment":" Accumulating form validation -- combine per-field results into one\n`Validated args` where **every** problem is collected, not just the first.\nThe applicative sibling of the per-field validation the input atoms\n(`Web3.Ui.Input`, `Web3.Ui.Amount`) already do.\n\n    type alias SendForm =\n        { to : T.Address, amount : BigInt }\n\n    validated : Model -> Validated SendForm\n    validated model =\n        succeed SendForm\n            |> andMap (fromMaybe \"Recipient is not a valid address\" model.parsedTo)\n            |> andMap (fromMaybe \"Amount is not a valid number\" model.parsedAmount)\n\n    -- in view: disable the button and show ALL the problems\n    button [ disabled (not (Form.isValid v)) ] ...\n    Form.errorList [] v\n\n`Result` short-circuits on the first `Err`; forms shouldn't -- a user fixing\na three-field form one error at a time is the classic papercut this type\nremoves.\n\nCSS classes: `web3-form-errors`, `web3-form-errors__item`.\n\n@docs Validated, succeed, fail, fromMaybe\n@docs andMap, map, map2\n@docs errors, isValid, errorList\n\n","unions":[],"aliases":[{"name":"Validated","comment":" A value that is either fully valid or carries every accumulated\nproblem.\n","args":["a"],"type":"Result.Result (List.List String.String) a"}],"values":[{"name":"andMap","comment":" Apply the next field, **accumulating** errors from both sides. ","type":"Web3.Ui.Form.Validated a -> Web3.Ui.Form.Validated (a -> b) -> Web3.Ui.Form.Validated b"},{"name":"errorList","comment":" Render the problems as a list (`role=\"alert\"`), or nothing when valid. ","type":"List.List (Html.Attribute msg) -> Web3.Ui.Form.Validated a -> Html.Html msg"},{"name":"errors","comment":" All accumulated problems (empty when valid). ","type":"Web3.Ui.Form.Validated a -> List.List String.String"},{"name":"fail","comment":" A single failure. ","type":"String.String -> Web3.Ui.Form.Validated a"},{"name":"fromMaybe","comment":" Lift a parsed field: `Nothing` becomes the given error. The natural\nbridge from `T.address` / `BigInt.fromString` / `Units.parseUnits`.\n","type":"String.String -> Maybe.Maybe a -> Web3.Ui.Form.Validated a"},{"name":"isValid","comment":" ","type":"Web3.Ui.Form.Validated a -> Basics.Bool"},{"name":"map","comment":" ","type":"(a -> b) -> Web3.Ui.Form.Validated a -> Web3.Ui.Form.Validated b"},{"name":"map2","comment":" ","type":"(a -> b -> c) -> Web3.Ui.Form.Validated a -> Web3.Ui.Form.Validated b -> Web3.Ui.Form.Validated c"},{"name":"succeed","comment":" Start a pipeline with the record constructor. ","type":"a -> Web3.Ui.Form.Validated a"}],"binops":[]},{"name":"Web3.Ui.FundingPool","comment":" Render a **crowd-funded pool** panel -- generic shape for any\n\"escrowed funding with a target, deadline, and refund fallback\"\nmechanism.\n\nUse cases:\n\n  - Community-funded audits (pledgers crowdfund a security review ->\n    auditor delivers report -> escrow releases).\n  - Bug-bounty / vulnerability-fix pools.\n  - Open-source-feature crowdfunding (pledgers fund a feature ->\n    maintainer ships -> escrow releases).\n  - Refundable kickstarter-style commitments where the deliverable\n    is on-chain verifiable.\n\nThe structural pattern is universal:\n\n  1. Someone commits to deliver against a target.\n  2. Anyone pledges native value while the deadline is in the future.\n  3. Once the target is met, the proposer releases by publishing a\n     deliverable URI.\n  4. If the deadline passes without release, every pledger pulls back\n     their own deposit.\n\nThree lifecycle states swap the primary CTA:\n\n  - `Open` -- collecting pledges, deadline ahead.\n  - `Funded` -- target reached, awaiting release.\n  - `Closed` -- released (with deliverable URL) or expired (refunds).\n\nThe panel title, status pill copy, CTA verbs, and disclaimer are all\nconfigurable via [`Labels`](#Labels) so the panel can speak in the\nvoice of whichever mechanism the caller is exposing.\n\n    Web3.Ui.FundingPool.view []\n        { pool =\n            Web3.Ui.FundingPool.pool\n                { target = \"25.0\"\n                , balance = \"14.75\"\n                , unit = \"PLS\"\n                , deadline = \"6 days\"\n                , pledgerCount = 47\n                , topPledgers = [ { label = \"alice.eth\", amount = \"2.5\" } ]\n                , status = Open\n                , reportUrl = Nothing\n                }\n        , labels = Web3.Ui.FundingPool.defaultLabels\n        , onPledge = Just PledgeStandard\n        , onPledgeCustom = Just PledgeCustom\n        , onRefund = Nothing\n        , onViewReport = Nothing\n        }\n\nStateless -- caller owns the `Pool` and emits the appropriate `msg`.\n\nCSS classes follow BEM: `web3-funding-pool`,\n`web3-funding-pool__head`, `web3-funding-pool__title`,\n`web3-funding-pool__subtitle`, `web3-funding-pool__stats`,\n`web3-funding-pool__stat`, `web3-funding-pool__stat-label`,\n`web3-funding-pool__stat-value`, `web3-funding-pool__progress`,\n`web3-funding-pool__progress-fill`, `web3-funding-pool__pledgers`,\n`web3-funding-pool__pledger`, `web3-funding-pool__actions`,\n`web3-funding-pool__cta`,\n`web3-funding-pool__cta--primary / --secondary`,\n`web3-funding-pool__report`, `web3-funding-pool__disclaimer`.\n\n@docs view, Config, Pool, Pledger, Status\n@docs Labels, defaultLabels\n@docs pool\n\n","unions":[{"name":"Pool","comment":" The pool's current observable state. Values are pre-formatted\nstrings because amount/unit/precision is the caller's concern -- this\nprimitive is rendering, not arithmetic.\n","args":[],"cases":[]},{"name":"Status","comment":" A pool's lifecycle state. ","args":[],"cases":[["Open",[]],["Funded",[]],["Closed",[]]]}],"aliases":[{"name":"Config","comment":" Configuration for `view`. Each `onXxx` is optional -- pass `Nothing`\nto disable that affordance.\n","args":["msg"],"type":"{ pool : Web3.Ui.FundingPool.Pool, labels : Web3.Ui.FundingPool.Labels, onPledge : Maybe.Maybe msg, onPledgeCustom : Maybe.Maybe msg, onRefund : Maybe.Maybe msg, onViewReport : Maybe.Maybe msg }"},{"name":"Labels","comment":" Configurable strings rendered inside the panel. Override per pool\nmechanism: an audit pool reads \"Community-funded audit\" / \"Pledge\" /\n\"View audit report\"; a feature-funding pool reads \"Community-funded\nfeature\" / \"Back\" / \"View deliverable\".\n\n`statusLabels` maps each [`Status`](#Status) to its status-pill text.\n","args":[],"type":"{ title : String.String, pledgeCta : String.String, pledgeCustomCta : String.String, refundCta : String.String, viewReportCta : String.String, fundedMessage : String.String, disclaimer : String.String, targetLabel : String.String, poolLabel : String.String, deadlineLabel : String.String, pledgerCountLabel : String.String, topPledgersTitle : String.String, statusLabels : { open : String.String, funded : String.String, closed : String.String } }"},{"name":"Pledger","comment":" One pledger row in the \"top pledgers\" list. ","args":[],"type":"{ label : String.String, amount : String.String }"}],"values":[{"name":"defaultLabels","comment":" Generic defaults. Caller overrides any field with mechanism-specific\ncopy (e.g. forge.intrepiddev uses audit-pool framing on top of this). ","type":"Web3.Ui.FundingPool.Labels"},{"name":"pool","comment":" Construct a `Pool`. Progress is derived from target+balance via\nbest-effort string parsing; the result clamps to [0, 100].\n","type":"{ target : String.String, balance : String.String, unit : String.String, deadline : String.String, pledgerCount : Basics.Int, topPledgers : List.List Web3.Ui.FundingPool.Pledger, status : Web3.Ui.FundingPool.Status, reportUrl : Maybe.Maybe String.String } -> Web3.Ui.FundingPool.Pool"},{"name":"view","comment":" Render the panel. ","type":"List.List (Html.Attribute msg) -> Web3.Ui.FundingPool.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.GasEstimate","comment":" Gas cost estimate display.\n\nPair with `Web3.Contract.Send.estimateGas` and `Web3.Fee.getGasPrice` to show\nthe expected cost of a transaction before the user confirms.\n\n    Web3.Ui.GasEstimate.view []\n        { gasUnits = model.gasEstimate\n        , gasPrice = model.gasPrice\n        , decimals = 18\n        , symbol = \"PLS\"\n        }\n\n@docs view\n\n","unions":[],"aliases":[],"values":[{"name":"view","comment":" Display an estimated transaction cost in the native token.\n\n`gasUnits` -- gas limit returned by `estimateGas` (decoded from port)\n`gasPrice` -- current gas price in wei, from `Web3.Fee.getGasPrice`\n\nShows `\"Estimating...\"` while either value is `Nothing`.\n\nCost = gasUnits x gasPrice (in wei), displayed via `formatUnits`.\n\nCSS class: `web3-gas-estimate`\n\n","type":"List.List (Html.Attribute msg) -> { gasUnits : Maybe.Maybe Web3.BigInt.BigInt, gasPrice : Maybe.Maybe Web3.BigInt.BigInt, decimals : Basics.Int, symbol : String.String } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.GaugeRow","comment":" One row of a vote-escrow gauge list: gauge label, current epoch, total\nvotes on the gauge, total bribes pooled for voters, your vote share, and\noptional vote / bribe / claim actions. Generic for any Curve-style gauge\nvoting design (Curve, Aerodrome, Velodrome, bribe markets generally).\n\nThe available actions depend on epoch state:\n\n  - `epoch == currentEpoch`: vote and bribe both make sense; claim does not.\n  - `epoch < currentEpoch`: epoch is closed; voters can claim, bribers can\n    forfeit, no new votes/bribes possible.\n\nThe component does not enforce these -- caller passes `Nothing` for actions\nthat should be hidden in the current state.\n\n```elm\nWeb3.Ui.GaugeRow.view\n    { gaugeLabel = gauge.label\n    , epoch = gauge.epoch\n    , currentEpoch = market.currentEpoch\n    , totalVotes = gauge.totalVotes\n    , totalBribes = gauge.totalBribes\n    , bribeSymbol = \"PLS\"\n    , bribeDecimals = 18\n    , veSymbol = \"veToken\"\n    , veDecimals = 18\n    , yourVote = gauge.yourVote\n    , aprBps = Just gauge.aprBps\n    , onVote = Just (Vote gauge.id)\n    , onBribe = Just (Bribe gauge.id)\n    , onClaim = Nothing\n    }\n```\n\nCSS classes: `web3-gaugerow`, `web3-gaugerow__label`, `web3-gaugerow__epoch`,\n`web3-gaugerow__votes`, `web3-gaugerow__bribes`, `web3-gaugerow__share`,\n`web3-gaugerow__apr`, `web3-gaugerow__actions`, `web3-gaugerow__action`.\nEpoch gets a `--closed` modifier when `epoch < currentEpoch`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ gaugeLabel : String.String, epoch : Basics.Int, currentEpoch : Basics.Int, totalVotes : Web3.BigInt.BigInt, totalBribes : Web3.BigInt.BigInt, bribeSymbol : String.String, bribeDecimals : Basics.Int, veSymbol : String.String, veDecimals : Basics.Int, yourVote : Web3.BigInt.BigInt, aprBps : Maybe.Maybe Basics.Int, onVote : Maybe.Maybe msg, onBribe : Maybe.Maybe msg, onClaim : Maybe.Maybe msg }"}],"values":[{"name":"view","comment":" Render the row. ","type":"Web3.Ui.GaugeRow.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.HoldClock","comment":" Visual countdown of a graduated-fee tier. Common pattern: an early-exit fee\nthat decays linearly from a base rate to a floor over N days. Shows current\nfee % and a progress ring/line until the floor is reached.\n\n    Web3.Ui.HoldClock.view\n        { holdSeconds = nowSec - weightedBuyTime\n        , decayDays = 40\n        , baseFeeBps = 500   -- 5%\n        , minFeeBps  = 100   -- 1%\n        }\n\nStyle classes: `web3-holdclock`, `web3-holdclock__current`, `web3-holdclock__bar`,\n`web3-holdclock__fill`, `web3-holdclock__remaining`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" ","args":[],"type":"{ holdSeconds : Basics.Int, decayDays : Basics.Int, baseFeeBps : Basics.Int, minFeeBps : Basics.Int }"}],"values":[{"name":"view","comment":" Render the hold-clock widget. ","type":"Web3.Ui.HoldClock.Config -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Identicon","comment":" Deterministic address identicons -- the classic 8x8 \"blockies\" every\nwallet renders, in pure Elm.\n\nFaithful port of the canonical `ethereum/blockies` algorithm (xorshift PRNG\nseeded from the lowercased address, three HSL colors, 8-wide vertically\nmirrored grid), so the identicon for an address here matches the one users\nalready recognise from MetaMask et al. Recognition is the whole point of an\nidenticon -- a pretty-but-different avatar would be worse than none.\n\n    Identicon.view [] { size = 24 } holderAddress\n\nRendered as an inline SVG (`viewBox 0 0 8 8`, crisp edges) so it scales\nlosslessly to any `size`. The three colors are data -- derived from the\naddress -- so they are set as SVG `fill` attributes; everything else\n(border-radius for circular crops, borders, drop shadows) belongs to your\nCSS via the `web3-identicon` class.\n\n    .web3-identicon { border-radius: 50%; }   /* circular crop */\n\nCSS class: `web3-identicon`.\n\n@docs view, cells, Cells\n\n","unions":[],"aliases":[{"name":"Cells","comment":" The raw material of an identicon, for custom renderers (canvas, PNG\nexport): three CSS color strings and the 64-cell grid in row-major order.\nCell values: `0` background, `1` color, `2` spot color.\n","args":[],"type":"{ color : String.String, bgColor : String.String, spotColor : String.String, grid : List.List Basics.Int }"}],"values":[{"name":"cells","comment":" Compute the colors and grid for any seed string (normally the lowercase\n`0x...` address -- [`view`](#view) handles that for you). Deterministic: the\nsame seed always yields the same `Cells`.\n","type":"String.String -> Web3.Ui.Identicon.Cells"},{"name":"view","comment":" Render the identicon for an address at a given pixel size.\n\nThe `attrs` land on an `Html.span` wrapper (class `web3-identicon`), not on\nthe SVG itself -- `Html.Attributes.class` sets the `className` *property*,\nwhich is read-only on SVG nodes and would crash at runtime. The wrapper\nmakes any `Html.Attribute` safe to pass.\n\n","type":"List.List (Html.Attribute msg) -> { size : Basics.Int } -> Web3.Types.Address -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Input","comment":" Typed input primitives for EVM data entry.\n\nThin wrappers around `Html.input` with semantic class names and appropriate\n`type` / `inputmode` attributes. No validation -- the host app calls\n`T.address`, `BigInt.fromString`, etc. on the string value.\n\nThe `address`, `bigInt`, and `bytes` inputs accept a `valid : Bool` field.\nPass `False` to add the `--invalid` modifier class (e.g. after a failed parse).\n\n    Web3.Ui.Input.address []\n        { value = model.toAddress, onInput = ToAddressChanged, valid = True }\n\n    Web3.Ui.Input.bigInt []\n        { value = model.amount, onInput = AmountChanged, valid = True }\n\n    Web3.Ui.Input.bool []\n        { value = model.approve, onToggle = ApproveToggled }\n\n@docs address, bigInt, bool, text, bytes\n\n","unions":[],"aliases":[],"values":[{"name":"address","comment":" Text input for Ethereum address entry (`0x...`).\n\nAdds `web3-input-address--invalid` when `valid` is `False`.\n\nCSS class: `web3-input-address`\n\n","type":"List.List (Html.Attribute msg) -> { value : String.String, onInput : String.String -> msg, valid : Basics.Bool } -> Html.Html msg"},{"name":"bigInt","comment":" Text input for a BigInt / uint256 value.\n\nUses `inputmode=\"numeric\"` to show the numeric keyboard on mobile.\nAdds `web3-input-bigint--invalid` when `valid` is `False`.\n\nCSS class: `web3-input-bigint`\n\n","type":"List.List (Html.Attribute msg) -> { value : String.String, onInput : String.String -> msg, valid : Basics.Bool } -> Html.Html msg"},{"name":"bool","comment":" Checkbox input for a boolean value.\n\nCSS class: `web3-input-bool`\n\n","type":"List.List (Html.Attribute msg) -> { value : Basics.Bool, onToggle : Basics.Bool -> msg } -> Html.Html msg"},{"name":"bytes","comment":" Text input for hex bytes (`0x...`).\n\nAdds `web3-input-bytes--invalid` when `valid` is `False`.\n\nCSS class: `web3-input-bytes`\n\n","type":"List.List (Html.Attribute msg) -> { value : String.String, onInput : String.String -> msg, valid : Basics.Bool } -> Html.Html msg"},{"name":"text","comment":" Plain text input.\n\nCSS class: `web3-input-text`\n\n","type":"List.List (Html.Attribute msg) -> { value : String.String, onInput : String.String -> msg } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.LockPeriod","comment":" Slider for picking a lock-period (in days) with an optional penalty-curve\nhint underneath. Designed for stake-and-lock contracts where users choose\nhow long to commit (any min/max range; pass what your contract enforces).\n\n    Web3.Ui.LockPeriod.view\n        { value = model.lockDays\n        , onChange = LockDaysChanged\n        , min = 1\n        , max = 365\n        , penaltyAtMax = Just 35  -- 35% penalty at full early-exit\n        }\n\nThe slider renders a native `<input type=\"range\">` for accessibility, with the\npenalty hint as a side-by-side `<output>`. Style via `web3-lockperiod`,\n`web3-lockperiod__slider`, `web3-lockperiod__readout`, `web3-lockperiod__penalty`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" Slider configuration. `penaltyAtMax` is the maximum early-exit penalty in\npercent (e.g., `Just 35` for 35%). When `Nothing`, no penalty hint is shown.\n","args":["msg"],"type":"{ value : Basics.Int, onChange : Basics.Int -> msg, min : Basics.Int, max : Basics.Int, penaltyAtMax : Maybe.Maybe Basics.Int }"}],"values":[{"name":"view","comment":" Render the slider. ","type":"Web3.Ui.LockPeriod.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.NFTStakeCard","comment":" Card for an ERC-721 stake position. Like `Web3.Ui.StakeCard`, but the\nposition is a transferable NFT -- so the card surfaces the `tokenId`, an\noptional transfer action, and a separate \"redeem at floor\" action for\ndesigns where floor-redemption is a distinct primitive\nfrom full unstake.\n\nTwo countdowns are tracked independently:\n\n  - `unlockTimeSec`: when principal can be withdrawn without penalty.\n  - `floorEligibleAt`: when the position becomes eligible for floor-price\n    redemption. Anchored to mint time so transfers do not reset it\n    (the wash-trade-immunity property).\n\n```elm\nWeb3.Ui.NFTStakeCard.view\n    { tokenId = position.id\n    , amount = position.amount\n    , symbol = \"PULSE\"\n    , decimals = 18\n    , startTimeSec = position.startTime\n    , unlockTimeSec = position.unlockTime\n    , floorEligibleAt = position.floorEligibleAt\n    , pendingYield = position.pendingYield\n    , yieldSymbol = \"PLS\"\n    , nowSec = model.nowSec\n    , onClaimYield = Just (ClaimYield position.id)\n    , onUnstake = Just (Unstake position.id)\n    , onRedeemAtFloor = Just (RedeemFloor position.id)\n    , onTransfer = Just (OpenTransfer position.id)\n    }\n```\n\nCSS classes: `web3-nftstakecard`, `web3-nftstakecard__id`,\n`web3-nftstakecard__amount`, `web3-nftstakecard__lock`,\n`web3-nftstakecard__floor`, `web3-nftstakecard__yield`,\n`web3-nftstakecard__actions`, `web3-nftstakecard__action`. The floor\ncountdown gets a `--eligible` modifier when `nowSec >= floorEligibleAt`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ tokenId : Basics.Int, amount : Web3.BigInt.BigInt, symbol : String.String, decimals : Basics.Int, startTimeSec : Basics.Int, unlockTimeSec : Basics.Int, floorEligibleAt : Basics.Int, pendingYield : Web3.BigInt.BigInt, yieldSymbol : String.String, nowSec : Basics.Int, onClaimYield : Maybe.Maybe msg, onUnstake : Maybe.Maybe msg, onRedeemAtFloor : Maybe.Maybe msg, onTransfer : Maybe.Maybe msg }"}],"values":[{"name":"view","comment":" Render the NFT stake-position card. ","type":"Web3.Ui.NFTStakeCard.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.PaginatedLogs","comment":" Block-range windowed `eth_getLogs` paging -- newest window first, walking\nback toward genesis without overlaps or gaps.\n\n    -- start: latest window\n    ( pager, range ) =\n        PaginatedLogs.init { latest = 21688204, window = 5000 }\n    -- fire getLogs for range.fromBlock..range.toBlock, then:\n    pager2 = PaginatedLogs.onLoaded (List.length logs) pager\n\n    -- \"Load older\" click:\n    case PaginatedLogs.next pager2 of\n        Just ( pager3, olderRange ) -> fire the next query\n        Nothing -> at genesis; nothing older exists\n\nRanges tile exactly: `[latest-window+1 .. latest]`, then\n`[latest-2w+1 .. latest-w]`, ... clamped at 0.\n\nCSS classes: `web3-pagedlogs__more`.\n\n@docs Pager, Range\n@docs init, next, onLoaded, exhausted, loadedCount\n@docs loadMoreButton\n\n","unions":[{"name":"Pager","comment":" Opaque paging state. ","args":[],"cases":[]}],"aliases":[{"name":"Range","comment":" An inclusive block range for one `getLogs` query. ","args":[],"type":"{ fromBlock : Basics.Int, toBlock : Basics.Int }"}],"values":[{"name":"exhausted","comment":" True once the walk reached genesis. ","type":"Web3.Ui.PaginatedLogs.Pager -> Basics.Bool"},{"name":"init","comment":" The newest window. `window` is clamped to >= 1. ","type":"{ latest : Basics.Int, window : Basics.Int } -> ( Web3.Ui.PaginatedLogs.Pager, Web3.Ui.PaginatedLogs.Range )"},{"name":"loadMoreButton","comment":" A load-more button that disappears at genesis. ","type":"{ onLoadMore : msg, label : String.String } -> Web3.Ui.PaginatedLogs.Pager -> Html.Html msg"},{"name":"loadedCount","comment":" Total logs recorded via [`onLoaded`](#onLoaded). ","type":"Web3.Ui.PaginatedLogs.Pager -> Basics.Int"},{"name":"next","comment":" The next older window, or `Nothing` at genesis. ","type":"Web3.Ui.PaginatedLogs.Pager -> Maybe.Maybe ( Web3.Ui.PaginatedLogs.Pager, Web3.Ui.PaginatedLogs.Range )"},{"name":"onLoaded","comment":" Record how many logs a window returned (display/telemetry). ","type":"Basics.Int -> Web3.Ui.PaginatedLogs.Pager -> Web3.Ui.PaginatedLogs.Pager"}],"binops":[]},{"name":"Web3.Ui.PendingOverlay","comment":" Overlay shown while waiting for wallet signature.\n\nEvery dapp needs a \"check your wallet\" prompt when a transaction is\n`AwaitingSignature`. This module provides a consistent overlay so each\nproject does not hand-roll the same spinner and copy.\n\n    -- Always rendered (you control visibility via CSS or parent logic):\n    Web3.Ui.PendingOverlay.view []\n        { message = \"Check your wallet\" }\n\n    -- Only renders during AwaitingSignature, empty otherwise:\n    Web3.Ui.PendingOverlay.conditionalView []\n        { message = \"Check your wallet\" }\n        model.tx\n\n    -- Multi-step approve -> call sequence:\n    Web3.Ui.PendingOverlay.viewMultiStep []\n        { steps =\n            [ { label = \"Approve $TOKEN\", state = StepDone }\n            , { label = \"Stake $TOKEN\", state = StepActive }\n            , { label = \"Confirm on-chain\", state = StepPending }\n            ]\n        , currentStatus = model.txStatus\n        }\n\n\n# Single-step\n\n@docs view, conditionalView\n\n\n# Multi-step\n\n@docs viewMultiStep, Step, StepState\n\n","unions":[{"name":"StepState","comment":" Lifecycle of a single step in a multi-step sequence.\n\n`StepFailed` carries the revert / wallet reason so the user sees a\nspecific message -- useful for the post-approve, mid-call failure mode\n(approve confirms, then the call reverts and the approve toast already\nfaded). The multi-step overlay keeps the full sequence visible until\nthe user dismisses it or starts a new tx.\n\n","args":[],"cases":[["StepPending",[]],["StepActive",[]],["StepDone",[]],["StepFailed",["String.String"]]]}],"aliases":[{"name":"Step","comment":" One row in a multi-step pending overlay.\n\n  - `label` -- human title for the step (e.g. \"Approve $TOKEN\").\n  - `state` -- `StepPending` (not started), `StepActive` (in flight),\n    `StepDone` (confirmed), `StepFailed reason` (last revert reason).\n\n","args":[],"type":"{ label : String.String, state : Web3.Ui.PendingOverlay.StepState }"}],"values":[{"name":"conditionalView","comment":" Render the overlay only when `status` is `Tx.AwaitingSignature`.\n\nReturns `Html.text \"\"` for all other states -- safe to always include in your\nview tree.\n\n","type":"List.List (Html.Attribute msg) -> { message : String.String } -> Web3.Transaction.Status -> Html.Html msg"},{"name":"view","comment":" Render the overlay unconditionally.\n\nCSS classes: `web3-pending-overlay` (root), `web3-pending-overlay-inner`,\n`web3-pending-spinner`, `web3-pending-message`\n\n","type":"List.List (Html.Attribute msg) -> { message : String.String } -> Html.Html msg"},{"name":"viewMultiStep","comment":" Render a vertical stack of steps with the active one spinning.\nRenders an empty node if every step is `StepPending` AND the overall\n`currentStatus` is `Tx.Idle` (nothing is happening yet -- caller can\nkeep the overlay inert without a per-page conditional).\n\nCSS classes: `web3-pending-overlay`, `web3-pending-overlay-multi`,\n`web3-pending-step`, `web3-pending-step--pending` /\n`--active` / `--done` / `--failed`, `web3-pending-step-glyph`,\n`web3-pending-step-label`, `web3-pending-step-reason`.\n\n","type":"List.List (Html.Attribute msg) -> { steps : List.List Web3.Ui.PendingOverlay.Step, currentStatus : Web3.Transaction.Status } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.PriceDisplay","comment":" Token price display with automatic notation selection.\n\nBonding curve prices span many orders of magnitude. This module picks the most\nreadable notation automatically so each project does not reimplement the same\nrange-checking logic.\n\n    Web3.Ui.PriceDisplay.view []\n        { decimals = 18, symbol = \"PLS\" }\n        priceWei\n\n    Web3.Ui.PriceDisplay.format 18 priceWei\n    --> \"0.0042\"    (fixed for normal range)\n    --> \"1.23M\"     (SI suffix for large)\n    --> \"4.56e-10\"  (scientific for tiny)\n\n@docs view, format\n\n","unions":[],"aliases":[],"values":[{"name":"format","comment":" Format a price BigInt as a human-readable string. No symbol appended.\n\nSee `view` for notation selection rules.\n\n","type":"Basics.Int -> Web3.BigInt.BigInt -> String.String"},{"name":"view","comment":" Display a token price, automatically selecting notation.\n\nNotation rules:\n\n- >= 1 000: SI suffix (`1.23M`, `45.6K`)\n- 0.001 - 999.99: fixed decimal (`1.23`, `0.456`, `0.00123`)\n- < 0.001: scientific (`4.56e-10`)\n\nCSS class: `web3-price`\n\n","type":"List.List (Html.Attribute msg) -> { decimals : Basics.Int, symbol : String.String } -> Web3.BigInt.BigInt -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.ProgressRing","comment":" Circular progress ring (SVG) for \"X% toward Y\" displays -- graduation\nprogress, vesting unlock, vault deposit cap. The circular variant of\n`Web3.Ui.SupplyBar`.\n\n    Web3.Ui.ProgressRing.view\n        { current = curve.curvePls\n        , target = graduationThreshold\n        , size = 64\n        , label = Just \"graduation\"\n        }\n\nCSS classes: `web3-progressring`, `web3-progressring__track`,\n`web3-progressring__fill`, `web3-progressring__label`. Stroke width and color\ncome from CSS -- set `stroke` and `stroke-width` on the `__track` and `__fill`\nelements; geometry is inline for SVG.\n\n@docs view\n\n","unions":[],"aliases":[],"values":[{"name":"view","comment":" Render the ring. ","type":"{ current : Web3.BigInt.BigInt, target : Web3.BigInt.BigInt, size : Basics.Int, label : Maybe.Maybe String.String } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.RelativeTime","comment":" Compact relative-time string for transaction ages, position open dates,\ngraduation timestamps -- anywhere a dapp shows \"how long ago\".\n\n    Web3.Ui.RelativeTime.view { nowSec = model.nowSec, atSec = tx.timestamp }\n    --> \"2m ago\" / \"3h ago\" / \"1d ago\" / \"Apr 12\"\n\nFor non-Html callers (e.g., row labels):\n\n    Web3.Ui.RelativeTime.format { nowSec = model.nowSec, atSec = tx.timestamp }\n    --> \"2m ago\"\n\nCSS class on the rendered span: `web3-relativetime`. The full RFC-style\ntimestamp (ISO-like seconds) is set as a `title` attribute for hover.\n\n@docs view, format\n\n","unions":[],"aliases":[],"values":[{"name":"format","comment":" Pure helper: convert a Unix-seconds delta into a human string.\n\nBuckets:\n\n  - < 60s  -> \"Ns ago\"\n  - < 60m  -> \"Nm ago\"\n  - < 24h  -> \"Nh ago\"\n  - < 30d  -> \"Nd ago\"\n  - else   -> \"Nw ago\" (weeks; coarse beyond a month)\n\nNegative deltas (future) render as \"in Ns\" / \"in Nm\" / etc.\n","type":"{ nowSec : Basics.Int, atSec : Basics.Int } -> String.String"},{"name":"view","comment":" Render the relative time string inside a span with a `title` showing the\nabsolute Unix seconds for accessibility / hover. ","type":"{ nowSec : Basics.Int, atSec : Basics.Int } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.RemoteCall","comment":" The remote-data type for correlation-id port round-trips -- the missing\nfoundation under every read in a dapp.\n\nEvery elm-web3 read (balance, contract call, fee, block...) is fire-and-forget\nthrough a port, matched back by a correlation id. Modelling each one by hand\nmeans every dapp reinvents `Maybe (Result String a)` plus an in-flight flag\nplus id bookkeeping. `RemoteCall` is that shape, once, with the id\nbookkeeping built in:\n\n    type alias Model =\n        { reserves : RemoteCall Reserves }\n\n    -- fire\n    ( { model | reserves = RemoteCall.request \"reserves-1\" model.reserves }\n    , web3Cmd (Call.encode reservesCall)\n    )\n\n    -- resolve (ONLY applies if the id matches the one in flight --\n    -- a stale response for an earlier request cannot clobber a newer one)\n    { model | reserves = RemoteCall.resolve incomingId result model.reserves }\n\n    -- render\n    RemoteCall.view\n        { skeleton = Skeleton.line []\n        , failed = \\\\err -> Html.text err\n        }\n        viewReserves\n        model.reserves\n\nThe id guard follows the same rule the `Web3.Sign` machine proves in\nelm-web3's `SignSpec.tla`: a response for a different correlation id never\ncompletes the pending call.\n\n@docs RemoteCall\n@docs notAsked, request, resolve, reset\n@docs map, withDefault, toMaybe, isLoading, correlationId\n@docs view, Slots\n\n","unions":[{"name":"RemoteCall","comment":" The four honest states of a port round-trip. `Loading` carries the\ncorrelation id of the request in flight.\n","args":["a"],"cases":[["NotAsked",[]],["Loading",["String.String"]],["Ready",["a"]],["Failed",["String.String"]]]}],"aliases":[{"name":"Slots","comment":" What to render for the non-`Ready` states. `NotAsked` renders the\nskeleton too -- from the user's point of view \"not fetched yet\" and\n\"fetching\" look the same.\n","args":["msg"],"type":"{ skeleton : Html.Html msg, failed : String.String -> Html.Html msg }"}],"values":[{"name":"correlationId","comment":" The correlation id in flight, if any. ","type":"Web3.Ui.RemoteCall.RemoteCall a -> Maybe.Maybe String.String"},{"name":"isLoading","comment":" True while a request is in flight. ","type":"Web3.Ui.RemoteCall.RemoteCall a -> Basics.Bool"},{"name":"map","comment":" Transform the value inside `Ready`. ","type":"(a -> b) -> Web3.Ui.RemoteCall.RemoteCall a -> Web3.Ui.RemoteCall.RemoteCall b"},{"name":"notAsked","comment":" The initial state. ","type":"Web3.Ui.RemoteCall.RemoteCall a"},{"name":"request","comment":" Mark a request in flight under the given correlation id. Always\ntransitions -- firing a new request supersedes whatever came before, and its\nid becomes the only one `resolve` will accept.\n","type":"String.String -> Web3.Ui.RemoteCall.RemoteCall a -> Web3.Ui.RemoteCall.RemoteCall a"},{"name":"reset","comment":" Back to `NotAsked` (e.g. on wallet disconnect or chain switch, when the\ndata no longer describes anything).\n","type":"Web3.Ui.RemoteCall.RemoteCall a -> Web3.Ui.RemoteCall.RemoteCall a"},{"name":"resolve","comment":" Apply a response -- but only if its correlation id matches the request in\nflight. A stale or misrouted response (an earlier request answered late,\nanother component's id) is dropped on the floor, never shown as fresh data.\n","type":"String.String -> Result.Result String.String a -> Web3.Ui.RemoteCall.RemoteCall a -> Web3.Ui.RemoteCall.RemoteCall a"},{"name":"toMaybe","comment":" `Just` the value when `Ready`. ","type":"Web3.Ui.RemoteCall.RemoteCall a -> Maybe.Maybe a"},{"name":"view","comment":" Render a `RemoteCall`, wrapped in a `<div class=\"web3-remote\">` whose\nmodifier tracks the state -- `--loading` carries `aria-busy=\"true\"` so\nassistive tech knows the region is settling.\n\nCSS classes: `web3-remote`, `web3-remote--loading`, `web3-remote--ready`,\n`web3-remote--failed`.\n\n","type":"Web3.Ui.RemoteCall.Slots msg -> (a -> Html.Html msg) -> Web3.Ui.RemoteCall.RemoteCall a -> Html.Html msg"},{"name":"withDefault","comment":" The value, or a fallback. ","type":"a -> Web3.Ui.RemoteCall.RemoteCall a -> a"}],"binops":[]},{"name":"Web3.Ui.Revert","comment":" Human-readable revert reasons. elm-web3 can decode `Error(string)`\nrevert data (`Web3.Abi.Decode.decodeRevertReason`) -- this module is the\nmissing last mile that actually shows it to the user instead of a raw\n`0x08c379a0...` blob.\n\n    case model.txError of\n        Just raw ->\n            Revert.banner [] { onDismiss = Just DismissError } raw\n\n        Nothing ->\n            Html.text \"\"\n\nDecoding is best-effort and honest about it: a decodable `Error(string)`\npayload renders its message; anything else (custom solc errors, empty\nreverts, out-of-gas) renders a labelled fallback with the truncated selector\nso the information is preserved, never invented.\n\nCSS classes: `web3-revert`, `web3-revert--decoded`, `web3-revert--raw`,\n`web3-revert__reason`, `web3-revert__dismiss`, `web3-revert-toast`.\n\n@docs banner, toast, reason\n@docs bannerWith, toastWith\n\n","unions":[],"aliases":[],"values":[{"name":"banner","comment":" An inline error banner (`role=\"alert\"` -- screen readers announce it).\nPass `onDismiss = Just msg` to get a dismiss button.\n","type":"List.List (Html.Attribute msg) -> { onDismiss : Maybe.Maybe msg } -> String.String -> Html.Html msg"},{"name":"bannerWith","comment":" Like [`banner`](#banner), but tries a typed custom-error decoder first --\npass elm-web3's `Abi.Decode.decodeCustomError yourFragments` (>= 1.4.0). A\ndecoded custom error renders its name and args\n(`web3-revert--custom`, name in `web3-revert__name`); anything else falls\nthrough to the standard `Error(string)` path.\n","type":"List.List (Html.Attribute msg) -> { onDismiss : Maybe.Maybe msg, decode : String.String -> Maybe.Maybe { name : String.String, args : List.List String.String } } -> String.String -> Html.Html msg"},{"name":"reason","comment":" Extract the best human-readable string from raw revert data.\n`Ok message` when it decoded as `Error(string)`; `Err shortForm` (a\ntruncated hex selector, or the raw input for non-hex strings) when it\ndid not.\n","type":"String.String -> Result.Result String.String String.String"},{"name":"toast","comment":" The same content shaped for a toast/notification stack. ","type":"List.List (Html.Attribute msg) -> { onDismiss : Maybe.Maybe msg } -> String.String -> Html.Html msg"},{"name":"toastWith","comment":" Toast-shaped sibling of [`bannerWith`](#bannerWith). ","type":"List.List (Html.Attribute msg) -> { onDismiss : Maybe.Maybe msg, decode : String.String -> Maybe.Maybe { name : String.String, args : List.List String.String } } -> String.String -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.SecurityCard","comment":" Render a Trustpilot-style **security pre-flight card** for a smart\ncontract, summarising static-analysis findings (Slither, Aderyn, Mythril,\n...).\n\nDesign intent (informed by the dapp-gen UX research brief):\n\n  - **Never** a single Pass/Fail verdict. Always a *breakdown* of finding\n    counts by severity. Static analyzers have known false-positive\n    rates; presenting a single pass/fail invites misinterpretation.\n  - Top-3 most-severe findings surface inline so a user has *actionable*\n    information without clicking into a long report.\n  - The \"request human audit\" CTA is always available -- the card is a\n    funnel, not a substitute, and that framing protects everyone (the\n    auditor, the user, and the contract author).\n\nThe component is **stateless** like every primitive in this lib -- the\ncaller owns the [`Findings`](#Findings) record and the `Stubbed` flag\n(set to `Yes` when no analysis has run, e.g. an Etherscan-only fetch\nwhere no NatSpec or sources are available).\n\n    Web3.Ui.SecurityCard.view []\n        { findings =\n            Web3.Ui.SecurityCard.findings\n                { critical = 0\n                , high = 2\n                , medium = 5\n                , low = 11\n                , info = 8\n                }\n        , topFindings =\n            [ { severity = High\n              , title = \"Centralization risk -- onlyOwner can mint\"\n              , tool = \"slither\"\n              }\n            , { severity = High\n              , title = \"Reentrancy in withdraw()\"\n              , tool = \"slither\"\n              }\n            , { severity = Medium\n              , title = \"Missing zero-address check in setOracle\"\n              , tool = \"aderyn\"\n              }\n            ]\n        , toolVersions = [ ( \"slither\", \"0.10.4\" ), ( \"aderyn\", \"0.5.1\" ) ]\n        , stubbed = NotStubbed\n        , reportUrl = Just \"/dapp/1/0x.../report\"\n        , onRequestAudit = Just RequestAudit\n        }\n\nCSS classes follow BEM: `web3-security-card`, `web3-security-card__head`,\n`web3-security-card__counts`, `web3-security-card__count`,\n`web3-security-card__count--critical / --high / --medium / --low / --info`,\n`web3-security-card__findings`, `web3-security-card__finding`,\n`web3-security-card__finding-severity`, `web3-security-card__finding-title`,\n`web3-security-card__finding-tool`, `web3-security-card__tools`,\n`web3-security-card__actions`, `web3-security-card__disclaimer`.\n\n@docs view, Config\n@docs Findings, findings\n@docs Finding, Severity\n@docs Stubbed\n\n","unions":[{"name":"Findings","comment":" Per-severity counts. ","args":[],"cases":[]},{"name":"Severity","comment":" Severity stops. ","args":[],"cases":[["Critical",[]],["High",[]],["Medium",[]],["Low",[]],["Info",[]]]},{"name":"Stubbed","comment":" Whether real analysis ran or the card is rendering the \"not-available\"\nstate. Etherscan-only fetches don't carry the metadata needed for\nanalysis, and during early development the analyser may be stubbed.\n","args":[],"cases":[["NotStubbed",[]],["Yes",["String.String"]]]}],"aliases":[{"name":"Config","comment":" Configuration for `view`. ","args":["msg"],"type":"{ findings : Web3.Ui.SecurityCard.Findings, topFindings : List.List Web3.Ui.SecurityCard.Finding, toolVersions : List.List ( String.String, String.String ), stubbed : Web3.Ui.SecurityCard.Stubbed, reportUrl : Maybe.Maybe String.String, onRequestAudit : Maybe.Maybe msg }"},{"name":"Finding","comment":" One specific finding from a tool. ","args":[],"type":"{ severity : Web3.Ui.SecurityCard.Severity, title : String.String, tool : String.String }"}],"values":[{"name":"findings","comment":" Construct a `Findings` value. ","type":"{ critical : Basics.Int, high : Basics.Int, medium : Basics.Int, low : Basics.Int, info : Basics.Int } -> Web3.Ui.SecurityCard.Findings"},{"name":"view","comment":" Render the card. ","type":"List.List (Html.Attribute msg) -> Web3.Ui.SecurityCard.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Sign","comment":" Signing lifecycle UI components.\n\nPattern-match on `Sign.SignState` to render sign request status without missing\nany state.\n\n    -- Display current sign state:\n    Web3.Ui.Sign.stateView [] model.signState\n\n    -- Sign button disabled while pending:\n    Web3.Ui.Sign.signButton []\n        { label = \"Sign Permit\", onSign = RequestSign }\n        model.signState\n\n    -- Display the signature once signed:\n    Web3.Ui.Sign.signatureView [] model.signState\n\n@docs stateView, signButton, signatureView\n\n","unions":[],"aliases":[],"values":[{"name":"signButton","comment":" A sign button that is active in `SignIdle` and terminal states, and disabled\nwhile `SignPending`.\n\nCSS class: `web3-sign-btn`\n\n","type":"List.List (Html.Attribute msg) -> { label : String.String, onSign : msg } -> Web3.Sign.SignState -> Html.Html msg"},{"name":"signatureView","comment":" Displays the signature value when in the `Signed` state.\n\nReturns an empty text node for all other states -- safe to always render.\n\nCSS classes: `web3-signature` (wrapper div), `web3-signature-value` (code element)\n\n","type":"List.List (Html.Attribute msg) -> Web3.Sign.SignState -> Html.Html msg"},{"name":"stateView","comment":" Displays the sign lifecycle state as a labelled span.\n\nLabels: \"Idle\" / \"Waiting for signature...\" / \"Signed\" / \"Failed: <err>\" / \"Rejected\"\n\nCSS class: `web3-sign-state`\n\n","type":"List.List (Html.Attribute msg) -> Web3.Sign.SignState -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.SimulateFirst","comment":" Simulate-then-send: preview a write's outcome before asking for a\nsignature. The structural guarantee, in the family of\n[`ApprovalFlow`](Web3-Ui-ApprovalFlow): **a transaction cannot be sent\nwithout a completed simulation** -- `confirm` is the only door into\n`Sending`, and it only opens from `Previewing`.\n\nelm-web3 already has the capability: `Contract.Call.withFrom` runs the\nwrite's calldata as an `eth_call` from the user's address, returning what\nthe transaction *would* do (or its revert) for free.\n\n    -- user clicks the primary button:\n    ( { model | sim = SimulateFirst.start model.sim }\n    , web3Cmd (Call.encode simulatedCall)      -- readCall withFrom\n    )\n\n    -- simulation answer (callResult or failed for our id):\n    { model | sim = SimulateFirst.onSimResult (Ok decodedPreview) model.sim }\n\n    -- user confirms the previewed outcome:\n    ( { model | sim = SimulateFirst.confirm model.sim }\n    , web3Cmd (Send.encode theRealWrite)\n    )\n\n    -- tx lifecycle:\n    { model | sim = SimulateFirst.onTx txMsg model.sim }\n\nWallet rejection is not failure: rejecting the signature returns to\n`Previewing` -- the preview is still true; the user just declined.\n\nCSS classes: `web3-simulate`, `web3-simulate--idle/--simulating/--preview/\n--sending/--done/--refused`, `web3-simulate__preview`,\n`web3-simulate__button`, `web3-simulate__error`.\n\n@docs Step\n@docs start, onSimResult, confirm, onTx, reset\n@docs view, Config\n\n","unions":[{"name":"Step","comment":" `Previewing` carries the simulated outcome (already decoded/rendered to\na string by the app); `Refused` carries the reason (simulation revert, tx\nfailure).\n","args":[],"cases":[["Idle",[]],["Simulating",[]],["Previewing",["String.String"]],["Sending",["String.String","Web3.Transaction.Status"]],["Done",["Web3.Transaction.Receipt"]],["Refused",["String.String"]]]}],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ simulateLabel : String.String, confirmLabel : String.String, onStart : msg, onConfirm : msg, onReset : msg }"}],"values":[{"name":"confirm","comment":" `Previewing -> Sending AwaitingSignature` -- the only door into\n`Sending`; unreachable without a completed simulation.\n","type":"Web3.Ui.SimulateFirst.Step -> Web3.Ui.SimulateFirst.Step"},{"name":"onSimResult","comment":" Apply the simulation's answer. Only meaningful in `Simulating`. ","type":"Result.Result String.String String.String -> Web3.Ui.SimulateFirst.Step -> Web3.Ui.SimulateFirst.Step"},{"name":"onTx","comment":" Drive the inner transaction. Only meaningful in `Sending`:\nconfirmed -> `Done`, failed -> `Refused`, wallet-rejected -> back to\n`Previewing` (the preview is still valid).\n","type":"Web3.Transaction.Msg -> Web3.Ui.SimulateFirst.Step -> Web3.Ui.SimulateFirst.Step"},{"name":"reset","comment":" `Done`/`Refused` -> `Idle`. No-op elsewhere. ","type":"Web3.Ui.SimulateFirst.Step -> Web3.Ui.SimulateFirst.Step"},{"name":"start","comment":" `Idle -> Simulating` (fire the simulation). No-op elsewhere. ","type":"Web3.Ui.SimulateFirst.Step -> Web3.Ui.SimulateFirst.Step"},{"name":"view","comment":" The one valid control per step, plus the preview/receipt/error surface. ","type":"Web3.Ui.SimulateFirst.Config msg -> Web3.Ui.SimulateFirst.Step -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Skeleton","comment":" Loading placeholders shaped like the things they stand in for.\n\nEvery read-heavy dapp needs these; pairing each atom with a skeleton of the\nsame silhouette keeps layouts from jumping when data lands. All shimmer and\ncolor comes from your CSS -- the classes are the whole API:\n\n    .web3-skeleton {\n        background: linear-gradient(90deg, #222 25%, #333 50%, #222 75%);\n        background-size: 200% 100%;\n        animation: web3-shimmer 1.4s infinite;\n        border-radius: 4px;\n    }\n    @keyframes web3-shimmer {\n        to { background-position: -200% 0; }\n    }\n\nEach helper renders a `<span>` (or `<div>` for `block`) that is\n`aria-hidden` -- the loading announcement belongs on the container (see\n`Web3.Ui.RemoteCall.view`, which sets `aria-busy`), not on each bone.\n\nCSS classes: `web3-skeleton`, plus a shape modifier --\n`web3-skeleton--line`, `--block`, `--circle`, `--pill`, `--address`,\n`--amount`.\n\n@docs line, block, circle, pill, address, amount\n\n","unions":[],"aliases":[],"values":[{"name":"address","comment":" Sized like a truncated address (`0x1234...abcd`). ","type":"List.List (Html.Attribute msg) -> Html.Html msg"},{"name":"amount","comment":" Sized like a token amount. ","type":"List.List (Html.Attribute msg) -> Html.Html msg"},{"name":"block","comment":" A rectangular area placeholder (cards, charts). ","type":"List.List (Html.Attribute msg) -> Html.Html msg"},{"name":"circle","comment":" A circular placeholder (identicons, token logos). ","type":"List.List (Html.Attribute msg) -> Html.Html msg"},{"name":"line","comment":" A one-line text placeholder. ","type":"List.List (Html.Attribute msg) -> Html.Html msg"},{"name":"pill","comment":" A rounded chip placeholder (badges, buttons). ","type":"List.List (Html.Attribute msg) -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.SlippageInput","comment":" Slippage tolerance picker for trade UIs. Renders a row of preset chips\n(0.1% / 0.5% / 1%) plus a custom input. Returns the chosen value as basis\npoints so the caller can compute `minTokensOut` / `minPlsOut`.\n\n    Web3.Ui.SlippageInput.view\n        { valueBps = model.slippageBps\n        , onChange = SlippageChanged\n        , presetsBps = [ 10, 50, 100 ]   -- 0.1%, 0.5%, 1%\n        }\n\n    -- Helper: derive the minOut value given an expected amount and slippage.\n    let\n        minOut =\n            Web3.Ui.SlippageInput.minOutFromBps model.slippageBps expectedAmount\n    in\n    contractCall { ..., minOut = minOut }\n\nStyle classes: `web3-slippage`, `web3-slippage__chip`, `web3-slippage__chip--active`,\n`web3-slippage__custom`.\n\n@docs view, Config, minOutFromBps\n\n","unions":[],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ valueBps : Basics.Int, onChange : Basics.Int -> msg, presetsBps : List.List Basics.Int }"}],"values":[{"name":"minOutFromBps","comment":" Compute `expected * (10000 - slippageBps) / 10000` in BigInt arithmetic.\nReturns `BigInt.zero` if the slippage exceeds 100%.\n","type":"Basics.Int -> Web3.BigInt.BigInt -> Web3.BigInt.BigInt"},{"name":"view","comment":" Render the picker. ","type":"Web3.Ui.SlippageInput.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.StakeCard","comment":" Generic stake-position card: amount staked, lock-days remaining, accrued\nyield, eligibility badge (e.g., floor-protection eligibility), plus claim and\nunstake action buttons. Intended for any lock-and-yield staking contract.\n\n    Web3.Ui.StakeCard.view\n        { amount = position.amount\n        , symbol = \"TKN\"\n        , decimals = 18\n        , startTimeSec = position.startTime\n        , lockDays = position.lockDays\n        , nowSec = nowSec\n        , yieldAccrued = position.yieldAccrued\n        , yieldSymbol = \"PLS\"\n        , badges = [ { active = position.floorEligible, label = \"floor protected\" } ]\n        , onClaimYield = Just (ClaimYield idx)\n        , onUnstake = Just (Unstake idx)\n        , unstakeLabel = \"Unstake\"\n        }\n\nStyle: `web3-stakecard`, `web3-stakecard__amount`, `web3-stakecard__lock`,\n`web3-stakecard__yield`, `web3-stakecard__badge`, `web3-stakecard__actions`,\n`web3-stakecard__action`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ amount : Web3.BigInt.BigInt, symbol : String.String, decimals : Basics.Int, startTimeSec : Basics.Int, lockDays : Basics.Int, nowSec : Basics.Int, yieldAccrued : Web3.BigInt.BigInt, yieldSymbol : String.String, badges : List.List { active : Basics.Bool, label : String.String }, onClaimYield : Maybe.Maybe msg, onUnstake : Maybe.Maybe msg, unstakeLabel : String.String }"}],"values":[{"name":"view","comment":" Render a stake-position card. ","type":"Web3.Ui.StakeCard.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.StatCell","comment":" Single label/value cell with optional delta and sentiment color. The\nunit-of-display analytics row that every dapp reaches for: \"TVL\", \"24h\nvolume\", \"stakers\", \"floor\".\n\n    Web3.Ui.StatCell.view\n        { label = \"Floor price\"\n        , value = \"0.024 PLS\"\n        , delta = Just \"+1.4%\"\n        , sentiment = Web3.Ui.StatCell.Positive\n        }\n\nCSS classes: `web3-statcell`, `web3-statcell__label`, `web3-statcell__value`,\n`web3-statcell__delta`, `web3-statcell--positive` / `--negative` / `--neutral`.\n\n@docs view, Sentiment\n\n","unions":[{"name":"Sentiment","comment":" ","args":[],"cases":[["Positive",[]],["Negative",[]],["Neutral",[]]]}],"aliases":[],"values":[{"name":"view","comment":" Render the stat cell. ","type":"{ label : String.String, value : String.String, delta : Maybe.Maybe String.String, sentiment : Web3.Ui.StatCell.Sentiment } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.SupplyBar","comment":" Progress bar for any \"how full is X\" metric -- token supply against a cap,\ngraduation reserves toward a threshold, vault deposits toward an issuance limit.\n\n    -- Bare usage:\n    Web3.Ui.SupplyBar.view\n        { current = curve.supply\n        , max = maxSupply\n        , label = Just \"supply\"\n        }\n\n    -- With a milestone marker (e.g., the bonding-curve graduation threshold\n    -- sitting partway along a max-reserve bar):\n    Web3.Ui.SupplyBar.withMilestone\n        { current = curve.curvePls\n        , max = totalCapacity\n        , milestone = Just { at = graduationThreshold, label = \"graduation\" }\n        , label = Just \"reserves\"\n        }\n\nThe bar is rendered as a styled `div` with CSS classes `web3-supplybar`,\n`web3-supplybar__fill`, `web3-supplybar__milestone`, `web3-supplybar__label`.\nWidth is controlled by `width: <pct>%` inline; everything else is style-via-CSS.\n\n@docs view, withMilestone, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" Minimal config for a plain progress bar. ","args":[],"type":"{ current : Web3.BigInt.BigInt, max : Web3.BigInt.BigInt, label : Maybe.Maybe String.String }"}],"values":[{"name":"view","comment":" Render a progress bar without a milestone marker. ","type":"Web3.Ui.SupplyBar.Config -> Html.Html msg"},{"name":"withMilestone","comment":" Render a progress bar, optionally annotated with a milestone marker\npositioned at `milestone.at` along the `[0, max]` range. ","type":"{ current : Web3.BigInt.BigInt, max : Web3.BigInt.BigInt, milestone : Maybe.Maybe { at : Web3.BigInt.BigInt, label : String.String }, label : Maybe.Maybe String.String } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.TokenAmountPair","comment":" The swap/deposit workhorse: token identity, decimals-aware amount input,\nlive balance line, and percent presets -- one compound, one Config.\n\n    TokenAmountPair.view []\n        { symbol = \"FOO\"\n        , logoUrl = Just \"https://tokens.example/foo.png\"\n        , balance = Just \"420.69 FOO\"\n        , value = model.amountStr\n        , onInput = AmountChanged\n        , onPercent = FillPercent      -- 25/50/75/100; app computes from balance\n        , onOpenSelector = Just OpenTokenPicker\n        , valid = model.amountValid\n        }\n\nThe percent chips emit intent (`onPercent 100` = MAX); the app computes the\nactual amount from its live balance so the chips can never show a stale\nnumber. `onOpenSelector = Nothing` renders the token as a static label\n(fixed-token forms).\n\nCSS classes: `web3-tokenamount`, `web3-tokenamount__token`,\n`web3-tokenamount__symbol`, `web3-tokenamount__input`,\n`web3-tokenamount__input--invalid`, `web3-tokenamount__balance`,\n`web3-tokenamount__presets`, `web3-tokenamount__chip`,\n`web3-tokenamount__chip--max`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ symbol : String.String, logoUrl : Maybe.Maybe String.String, balance : Maybe.Maybe String.String, value : String.String, onInput : String.String -> msg, onPercent : Basics.Int -> msg, onOpenSelector : Maybe.Maybe msg, valid : Basics.Bool }"}],"values":[{"name":"view","comment":" Render the pair. ","type":"List.List (Html.Attribute msg) -> Web3.Ui.TokenAmountPair.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.TokenLogo","comment":" Token logo atom -- an `img` when a logo URL is known, a deterministic\nletter tile when it is not.\n\nEvery token list, swap panel, and portfolio row needs the same thing: show\nthe logo if we have one, and degrade to something recognisable (not a broken\nimage icon) when we don't. The fallback tile shows the first 1-2 characters\nof the symbol and picks one of eight hue classes from a character-code hash\nof the symbol -- so `\"USDC\"` is always the same colour everywhere it appears,\nwith zero inline styles. Your stylesheet owns the actual palette.\n\n    Web3.Ui.TokenLogo.view\n        { logoUrl = Just \"https://tokens.example/usdc.png\"\n        , symbol = \"USDC\"\n        , size = 24\n        }\n\n    -- No logo known -- renders the \"FO\" letter tile:\n    Web3.Ui.TokenLogo.view\n        { logoUrl = Nothing, symbol = \"FOO\", size = 24 }\n\nThe image is `loading=\"lazy\"` (token lists are long) with `alt` set to the\nsymbol. The tile carries `role=\"img\"` and `aria-label` with the symbol so\nscreen readers announce the same thing either way. Sizing uses width/height\nattributes (img) and SVG width/height (tile) -- the same convention as\n`Web3.Ui.Identicon` -- never inline styles.\n\nCSS classes: `web3-tokenlogo`, `web3-tokenlogo--img`, `web3-tokenlogo--tile`,\n`web3-tokenlogo--hue-0` ... `web3-tokenlogo--hue-7`.\n\n@docs view, Config\n\n","unions":[],"aliases":[{"name":"Config","comment":" What to render: the logo URL if known, the token symbol (alt text and\ntile letters), and the pixel size.\n","args":[],"type":"{ logoUrl : Maybe.Maybe String.String, symbol : String.String, size : Basics.Int }"}],"values":[{"name":"view","comment":" Render the logo image, or the letter-tile fallback when `logoUrl` is\n`Nothing`.\n","type":"Web3.Ui.TokenLogo.Config -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.TokenSearch","comment":" Search input for filtering a token list by name/symbol/address. Generic\nacross any dapp that lists tokens (launchpad, DEX, vault aggregator, NFT\ncollection picker).\n\n    Web3.Ui.TokenSearch.view\n        { value = model.query\n        , onInput = QueryChanged\n        , placeholder = \"Search tokens...\"\n        }\n\nThe component does not perform filtering -- it just renders the input and\nemits change events. Filter logic is the consumer's responsibility (an\n`on*Match` predicate that compares against `name`, `symbol`, `address`).\n\nCSS classes: `web3-tokensearch`, `web3-tokensearch__input`, `web3-tokensearch__icon`.\n\n@docs view\n\n","unions":[],"aliases":[],"values":[{"name":"view","comment":" Render the search input. ","type":"{ value : String.String, onInput : String.String -> msg, placeholder : String.String } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.TradeTabs","comment":" Generic tab switcher for trade UIs (Buy / Sell / Stake, or any other\nnamed tab set). Single-select, button-based, accessible.\n\n    type TradeTab = BuyTab | SellTab | StakeTab\n\n    Web3.Ui.TradeTabs.view\n        { current = model.tradeTab\n        , onSelect = ChangeTradeTab\n        , tabs =\n            [ { id = BuyTab,   label = \"Buy\" }\n            , { id = SellTab,  label = \"Sell\" }\n            , { id = StakeTab, label = \"Stake\" }\n            ]\n        }\n\nCSS classes: `web3-tradetabs`, `web3-tradetabs__tab`, `web3-tradetabs__tab--active`.\n\n@docs view, Tab\n\n","unions":[],"aliases":[{"name":"Tab","comment":" One tab definition. `id` is your app's tab type; `label` is what users see. ","args":["id"],"type":"{ id : id, label : String.String }"}],"values":[{"name":"view","comment":" Render the switcher. ","type":"{ current : id, onSelect : id -> msg, tabs : List.List (Web3.Ui.TradeTabs.Tab id) } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Transaction","comment":" Transaction lifecycle UI components.\n\nPattern-match on `Tx.Status` to render appropriate UI without missing any state.\n\n    -- Status badge with modifier class:\n    Web3.Ui.Transaction.statusBadge [] tx.status\n\n    -- Action button disabled while in-flight:\n    Web3.Ui.Transaction.actionButton []\n        { label = \"Buy\", pendingLabel = \"Buying...\", onPress = SubmitBuy }\n        tx.status\n\n    -- Clickable tx hash link:\n    Web3.Ui.Transaction.txHashLink []\n        { explorerUrl = \"https://etherscan.io/tx/\" }\n        hash\n\n    -- Hash link extracted from status (Nothing when no hash available):\n    -- Pass Nothing for explorerUrl in local dev -- renders a plain span instead of a dead link.\n    Web3.Ui.Transaction.statusHashLink []\n        { explorerUrl = Just \"https://etherscan.io/tx/\" }\n        tx.status\n\n    -- Confirmed receipt display:\n    Web3.Ui.Transaction.receiptView []\n        { explorerUrl = \"https://etherscan.io/tx/\" }\n        receipt\n\n@docs statusBadge, actionButton, txHashLink, statusHashLink, receiptView\n@docs confirmationProgress\n\n","unions":[],"aliases":[],"values":[{"name":"actionButton","comment":" An action button that is active in `Idle` and terminal states, and disabled\nwhile in-flight (`AwaitingSignature`, `Submitted`, `Confirming`).\n\nShows `pendingLabel` while the transaction is in-flight.\n\nCSS classes: `web3-action-btn`, `web3-action-btn--pending` (added when disabled)\n\n","type":"List.List (Html.Attribute msg) -> { label : String.String, pendingLabel : String.String, onPress : msg } -> Web3.Transaction.Status -> Html.Html msg"},{"name":"confirmationProgress","comment":" Confirmation progress as a row of dots -- one per required confirmation,\nfilled as they land. Renders only in `Confirming`; every other status is\n`Html.text \"\"` so it composes inline without layout cost.\n\nThe count comes straight from the `Confirming _ n` state, which the\n`Web3.Transaction` machine guarantees is strictly increasing (see\n`MonotonicConfirmations` in elm-web3's `proofs/tla/TransactionSpec.tla`) --\nthese dots can never un-fill.\n\nCSS classes: `web3-confirmations`, `web3-confirmations__dot`,\n`web3-confirmations__dot--filled`, `web3-confirmations__count`.\n\n","type":"List.List (Html.Attribute msg) -> { required : Basics.Int } -> Web3.Transaction.Status -> Html.Html msg"},{"name":"receiptView","comment":" Display a confirmed transaction receipt.\n\nRoot element: `<div class=\"web3-receipt web3-receipt--success|failed\">`\n\nEach field is wrapped in `<div class=\"web3-receipt-field\">`.\n\nCSS classes: `web3-receipt`, `web3-receipt--success`, `web3-receipt--failed`, `web3-receipt-field`\n\n","type":"List.List (Html.Attribute msg) -> { explorerUrl : Maybe.Maybe String.String } -> Web3.Transaction.Receipt -> Html.Html msg"},{"name":"statusBadge","comment":" A status badge for the current transaction state.\n\nLabels: \"Idle\" / \"Awaiting Signature\" / \"Pending\" / \"Confirming\" / \"Confirmed\" / \"Failed\" / \"Rejected\"\n\nCSS classes: `web3-tx-badge` plus one of:\n`web3-tx-badge--idle`, `web3-tx-badge--pending`, `web3-tx-badge--confirmed`,\n`web3-tx-badge--failed`, `web3-tx-badge--rejected`\n\n","type":"List.List (Html.Attribute msg) -> Web3.Transaction.Status -> Html.Html msg"},{"name":"statusHashLink","comment":" Extract a hash display from a `Tx.Status`. Returns `Nothing` when there is\nno hash (Idle, AwaitingSignature, Failed, Rejected).\n\nWhen `explorerUrl` is `Just url`, renders a clickable `<a>` link.\nWhen `explorerUrl` is `Nothing` (e.g. local Anvil dev), renders a plain\n`<span class=\"web3-tx-hash\">` with the truncated hash instead of a dead link.\n\n","type":"List.List (Html.Attribute msg) -> { explorerUrl : Maybe.Maybe String.String } -> Web3.Transaction.Status -> Maybe.Maybe (Html.Html msg)"},{"name":"txHashLink","comment":" A truncated tx-hash display, linked to a block explorer when one is\nconfigured.\n\nWhen `explorerUrl` is `Just url`, the url is prepended to the full hash to\nbuild the href. When `Nothing` (e.g. local Anvil dev), renders a plain\n`<span class=\"web3-tx-hash\">` instead of a dead link -- the same convention\nas [`statusHashLink`](#statusHashLink) and `Web3.Ui.Address`.\n\nExample: `{ explorerUrl = Just \"https://etherscan.io/tx/\" }`\n\nCSS classes: `web3-tx-link` (linked), `web3-tx-hash` (plain)\n\n","type":"List.List (Html.Attribute msg) -> { explorerUrl : Maybe.Maybe String.String } -> Web3.Types.TxHash -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.TrendIndicator","comment":" Compact Up/Neutral/Down arrow with paired volume pills. Useful for any\ntrend-aware contract -- trend-detector outputs, lending-rate sentiment,\nDAO vote pressure indicators, etc.\n\n    -- From explicit trend:\n    Web3.Ui.TrendIndicator.view\n        { trend = Web3.Ui.TrendIndicator.Down\n        , buyVolume = buyVol\n        , sellVolume = sellVol\n        }\n\n    -- Or derive trend from volumes given a threshold (in basis points,\n    -- e.g., 5800 = 58% one side):\n    let\n        trend =\n            Web3.Ui.TrendIndicator.fromVolumes\n                { buyVolume = buyVol\n                , sellVolume = sellVol\n                , thresholdBps = 5800\n                }\n    in\n    Web3.Ui.TrendIndicator.view { trend = trend, buyVolume = buyVol, sellVolume = sellVol }\n\nStyle classes: `web3-trend`, `web3-trend--up`, `web3-trend--neutral`,\n`web3-trend--down`, `web3-trend__arrow`, `web3-trend__pill`.\n\n@docs view, Trend, fromVolumes\n\n","unions":[{"name":"Trend","comment":" ","args":[],"cases":[["Up",[]],["Neutral",[]],["Down",[]]]}],"aliases":[],"values":[{"name":"fromVolumes","comment":" Derive a Trend from buy/sell volumes plus a threshold in basis points. The\nside that exceeds the threshold determines the direction; otherwise Neutral.\n","type":"{ buyVolume : Web3.BigInt.BigInt, sellVolume : Web3.BigInt.BigInt, thresholdBps : Basics.Int } -> Web3.Ui.TrendIndicator.Trend"},{"name":"view","comment":" Render the indicator. ","type":"{ trend : Web3.Ui.TrendIndicator.Trend, buyVolume : Web3.BigInt.BigInt, sellVolume : Web3.BigInt.BigInt } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.TxQueue","comment":" Many transactions in flight, without Model gymnastics.\n\nA `TxQueue` is a labelled collection of `Tx.Status` machines keyed by\ncorrelation id. `begin` when the user acts, route each port `Tx.Msg` to its\nid with `update`, render the whole thing as a toast stack:\n\n    type alias Model =\n        { txs : TxQueue }\n\n    -- user clicks buy:\n    { model | txs = TxQueue.begin \"buy-42\" \"Buy 1.2M FOO\" model.txs }\n\n    -- port message for a tx:\n    { model | txs = TxQueue.update incomingId txMsg model.txs }\n\n    -- in view:\n    TxQueue.toastStack\n        { onDismiss = DismissTx\n        , explorerUrl = Just \"https://scan.pulsechain.com/tx/\"\n        }\n        model.txs\n\nEvery entry is a full `Web3.Transaction` state machine, so all its\nguarantees hold per-transaction (terminal absorbing, monotonic\nconfirmations) -- and because messages are routed by id, one transaction's\nlate confirmation can never touch another's state.\n\nCSS classes: `web3-txq`, `web3-txq__toast`, `web3-txq__toast--pending`,\n`--confirmed`, `--failed`, `web3-txq__label`, `web3-txq__status`,\n`web3-txq__dismiss`.\n\n@docs TxQueue, empty\n@docs begin, update, dismiss\n@docs pendingCount, entries\n@docs toastStack, Config\n\n","unions":[{"name":"TxQueue","comment":" The queue. Opaque; read it through [`entries`](#entries) /\n[`pendingCount`](#pendingCount).\n","args":[],"cases":[]}],"aliases":[{"name":"Config","comment":" ","args":["msg"],"type":"{ onDismiss : String.String -> msg, explorerUrl : Maybe.Maybe String.String }"}],"values":[{"name":"begin","comment":" Start tracking a transaction under a correlation id, with a\nhuman-readable label (\"Approve FOO\", \"Stake 50k\"). Starts in\n`AwaitingSignature` -- call it when you fire the write.\n","type":"String.String -> String.String -> Web3.Ui.TxQueue.TxQueue -> Web3.Ui.TxQueue.TxQueue"},{"name":"dismiss","comment":" Remove an entry (the toast's x button). ","type":"String.String -> Web3.Ui.TxQueue.TxQueue -> Web3.Ui.TxQueue.TxQueue"},{"name":"empty","comment":" No transactions. ","type":"Web3.Ui.TxQueue.TxQueue"},{"name":"entries","comment":" Every entry, id-ordered -- for custom renderings. ","type":"Web3.Ui.TxQueue.TxQueue -> List.List ( String.String, { label : String.String, status : Web3.Transaction.Status } )"},{"name":"pendingCount","comment":" How many transactions are still in flight. ","type":"Web3.Ui.TxQueue.TxQueue -> Basics.Int"},{"name":"toastStack","comment":" Render the queue as a stack of toasts (`aria-live=\"polite\"` -- status\nchanges are announced without stealing focus). Empty queue renders an empty\ncontainer, so the stack can be styled `position: fixed` unconditionally.\n","type":"Web3.Ui.TxQueue.Config msg -> Web3.Ui.TxQueue.TxQueue -> Html.Html msg"},{"name":"update","comment":" Route a port `Tx.Msg` to the transaction it belongs to. Unknown ids are\nignored -- a message for a transaction you never began (or already dismissed)\ncannot create ghost entries.\n","type":"String.String -> Web3.Transaction.Msg -> Web3.Ui.TxQueue.TxQueue -> Web3.Ui.TxQueue.TxQueue"}],"binops":[]},{"name":"Web3.Ui.VeBalanceChart","comment":" SVG line chart of vote-escrow balance decaying linearly from `nowSec` to\n`unlockTime`. Educational primitive -- shows the user how their vote weight\nwill fade as the lock matures.\n\nLinear-decay model (Curve / Aerodrome standard):\n\n    veBalance(t) = amount * max(0, unlockTime - t) / maxLockSec\n\nAt `t = nowSec` the line starts at the current ve-balance; at\n`t = unlockTime` it hits zero and stays there. The chart samples\n`sampleCount` points across that span and connects them with a polyline.\nMath runs on `Float` because the chart is purely visual -- do not consume\nrendered points for on-chain calculation.\n\n    Web3.Ui.VeBalanceChart.view\n        { amount = lock.amount\n        , unlockTime = lock.unlockTime\n        , maxLockSec = fourYears\n        , nowSec = model.nowSec\n        , width = 320\n        , height = 80\n        }\n\nCSS classes: `web3-vebalancechart`, `web3-vebalancechart__path`,\n`web3-vebalancechart__current`, `web3-vebalancechart__axis`. Stroke and\nfill colors come from the consumer stylesheet.\n\n@docs view\n\n","unions":[],"aliases":[],"values":[{"name":"view","comment":" Render the decay chart. ","type":"{ amount : Web3.BigInt.BigInt, unlockTime : Basics.Int, maxLockSec : Basics.Int, nowSec : Basics.Int, width : Basics.Int, height : Basics.Int } -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.VeLock","comment":" Lock-duration picker for vote-escrow tokens (Curve veCRV, GMX esGMX,\nveToken, ...). Renders an amount input, a lock-period slider snapped\nto a configurable step (typically 1 week, the Curve standard), and a live\nprojection of the resulting ve-balance.\n\nLinear-decay vote-escrow standard:\n\n    veBalance = amount * lockSec / maxLockSec\n\nSo a maximum-length lock yields ve-balance equal to the principal; a quarter\nof max-length yields a quarter of the principal.\n\n    Web3.Ui.VeLock.view\n        { amount = model.lockAmount\n        , amountInput = model.lockAmountInput\n        , decimals = 18\n        , symbol = \"TKN\"\n        , veSymbol = \"veToken\"\n        , lockSec = model.lockSec\n        , minLockSec = oneWeek\n        , maxLockSec = fourYears\n        , stepSec = oneWeek\n        , onAmountInput = LockAmountInput\n        , onLockChange = LockSecChanged\n        }\n\nCSS classes: `web3-velock`, `web3-velock__amount`, `web3-velock__slider`,\n`web3-velock__readout`, `web3-velock__preview`, `web3-velock__preview-label`,\n`web3-velock__preview-value`. Caller provides colors via stylesheet.\n\n@docs view, Config, veBalance\n\n","unions":[],"aliases":[{"name":"Config","comment":" All configuration. `amount` is the principal as `BigInt` (used for the\nve-balance projection). `amountInput` is the live string in the input box\n(separate so the consumer owns parse/validation state). `lockSec` is the\nchosen lock duration in seconds. `stepSec` is the slider granularity --\npass `604800` (1 week) to match Curve's snap-to-week semantics.\n","args":["msg"],"type":"{ amount : Web3.BigInt.BigInt, amountInput : String.String, decimals : Basics.Int, symbol : String.String, veSymbol : String.String, lockSec : Basics.Int, minLockSec : Basics.Int, maxLockSec : Basics.Int, stepSec : Basics.Int, onAmountInput : String.String -> msg, onLockChange : Basics.Int -> msg }"}],"values":[{"name":"veBalance","comment":" Pure helper: project ve-balance from principal + chosen lock + max lock.\nMirrors the on-chain formula `amount * lockSec / maxLockSec`. Returns\n`BigInt.fromInt 0` if `maxLockSec` is non-positive.\n","type":"{ amount : Web3.BigInt.BigInt, lockSec : Basics.Int, maxLockSec : Basics.Int } -> Web3.BigInt.BigInt"},{"name":"view","comment":" Render the picker. ","type":"Web3.Ui.VeLock.Config msg -> Html.Html msg"}],"binops":[]},{"name":"Web3.Ui.Wallet","comment":" Wallet connection UI components.\n\nAll functions accept `List (Html.Attribute msg)` as the first argument -- merged\nonto the root element. Pass `[]` for no extra attributes.\n\nNo default styles -- every element has a semantic class name. Supply your own CSS.\n\n    -- Basic connect/disconnect button:\n    Web3.Ui.Wallet.connectButton []\n        { onConnect = ConnectWallet, onDisconnect = DisconnectWallet }\n        model.wallet\n\n    -- Full wallet UI handling all states:\n    Web3.Ui.Wallet.viewState []\n        { onConnect = ConnectWallet\n        , onSwitchChain = SwitchChain\n        , onDisconnect = DisconnectWallet\n        , knownChains = [ Chain.pulsechain, Chain.ethereum ]\n        }\n        model.wallet\n\n@docs connectButton, walletPicker, viewState, chainBadge, disconnectPopover\n\n","unions":[],"aliases":[],"values":[{"name":"chainBadge","comment":" Displays the connected chain name, looked up from the supplied chain list.\nFalls back to \"Unknown Chain\" if not found.\n\nShows \"Wrong Chain\" when in `WrongChain` state.\nShows \"Read-only\" when in `ReadOnly` state.\nShows \"--\" when connecting, disconnected, or in error state.\n\nCSS class: `web3-chain-badge`\n\n","type":"List.List (Html.Attribute msg) -> List.List Web3.Chain.Chain -> Web3.Wallet.State -> Html.Html msg"},{"name":"connectButton","comment":" A connect/disconnect button. Shows \"Connect\" in disconnected/error states\nand \"Disconnect\" in connected states.\n\nCSS classes: `web3-connect-btn`, `web3-disconnect-btn`\n\n","type":"List.List (Html.Attribute msg) -> { onConnect : msg, onDisconnect : msg } -> Web3.Wallet.State -> Html.Html msg"},{"name":"disconnectPopover","comment":" The connected-state address control: short address (with copy button and\noptional explorer link, via `Web3.Ui.Address.copyable`) plus a disconnect\nbutton. Intended to render inside a caller-controlled dropdown/popover --\nthis component doesn't manage its own open/closed state, since that's\ninherently the consumer's UI decision (a click-triggered popover, an\nalways-visible panel, etc.).\n\nCSS class: `web3-disconnect-popover`\n\n    Web3.Ui.Wallet.disconnectPopover []\n        { onCopy = CopyToClipboard\n        , onDisconnect = DisconnectWallet\n        , explorerUrl = Just (Chain.blockExplorer Chain.pulsechain ++ \"/address/\")\n        }\n        info\n\n","type":"List.List (Html.Attribute msg) -> { onCopy : String.String -> msg, onDisconnect : msg, explorerUrl : Maybe.Maybe String.String } -> Web3.Wallet.ConnectedInfo -> Html.Html msg"},{"name":"viewState","comment":" Full wallet UI -- handles all `Wallet.State` variants with appropriate labels\nand actions.\n\nPass `knownChains` so the `WrongChain` branch can display the target network name.\n\nCSS class: `web3-wallet-state`\n\n","type":"List.List (Html.Attribute msg) -> { onConnect : msg, onSwitchChain : msg, onDisconnect : msg, knownChains : List.List Web3.Chain.Chain } -> Web3.Wallet.State -> Html.Html msg"},{"name":"walletPicker","comment":" A list of EIP-6963 discovered wallets for the user to pick from.\n\nEach wallet renders as a button with the wallet icon and name. Pass the RDNS of\nthe currently active wallet as `selected` to highlight it.\n\nCSS classes: `web3-wallet-picker` (container), `web3-wallet-option` (each item),\n`web3-wallet-option--selected` (active wallet)\n\n","type":"List.List (Html.Attribute msg) -> { onSelect : String.String -> msg, selected : Maybe.Maybe String.String } -> List.List Web3.Wallet.WalletProvider -> Html.Html msg"}],"binops":[]}]