Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changes/20260718_cardano_wasm_demo_wallets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
project: cardano-wasm

pr: 1270

kind:
- feature

description: |
Demo: multi-wallet management (generate/restore, aliases, network switch with address re-derivation).
97 changes: 97 additions & 0 deletions cardano-wasm/demo/src/Format.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
module Format exposing (ada, adaToLovelace, amountError, lovelaceToAda, orDefault, shorten)

{-| String formatting and parsing: ada ↔ lovelace, abbreviation, small text helpers.
-}


{-| Parse a user-typed ADA amount ("1.5" → 1500000 lovelace).
-}
adaToLovelace : String -> Maybe Int
adaToLovelace s =
String.toFloat (String.trim s)
|> Maybe.map (\f -> round (f * 1.0e6))
Comment thread
palas marked this conversation as resolved.
Outdated


{-| True when a typed amount is non-empty but not a valid positive number (e.g. "1,5").
-}
amountError : String -> Bool
amountError s =
if String.trim s == "" then
False

else
case adaToLovelace s of
Just n ->
n <= 0

Nothing ->
True


lovelaceToAda : Int -> String
lovelaceToAda l =
let
sign =
if l < 0 then
"-"

else
""

a =
abs l

whole =
a // 1000000

frac =
String.padLeft 6 '0' (String.fromInt (modBy 1000000 a)) |> stripTrailingZeros
in
sign
++ String.fromInt whole
++ (if frac == "" then
""

else
"." ++ frac
)


ada : Int -> String
ada l =
lovelaceToAda l ++ " ₳"


stripTrailingZeros : String -> String
stripTrailingZeros s =
String.foldr
(\c ( acc, trimming ) ->
if trimming && c == '0' then
( acc, True )

else
( String.cons c acc, False )
)
( "", True )
s
|> Tuple.first


{-| Abbreviate long identifiers (addresses, keys, hashes) for display.
-}
shorten : String -> String
shorten s =
if String.length s > 22 then
String.left 12 s ++ "…" ++ String.right 6 s

else
s


orDefault : String -> String -> String
orDefault d s =
if String.trim s == "" then
d

else
s
52 changes: 35 additions & 17 deletions cardano-wasm/demo/src/Main.elm
Original file line number Diff line number Diff line change
@@ -1,24 +1,42 @@
module Main exposing (main)

{-| Placeholder page: proves the demo build pipeline and that the cardano-wasm
engine loads in the browser (web/ports.js only starts this application after
`initialise()` has succeeded). The wallet application itself follows.
{-| Entry point — wires The Elm Architecture together. The interesting code lives in:

- Types — every data type, the Model and the Msg
- State — initial state, derived queries, small updaters
- Update — the controller (one branch per Msg)
- View — the whole UI
- Wasm — the cardano-wasm boundary (port commands + decoders)
- Net / Format — static tables and pure utilities
- Ports — the raw port declarations (JS side: web/ports.js)

-}

import Html exposing (Html, div, h3, p, text)
import Html.Attributes exposing (class, style)
import Browser
import Ports
import State exposing (init)
import Types exposing (..)
import Update exposing (update)
import View exposing (view)
import Wasm


main : Html msg
main =
div [ class "grid" ]
[ div [ class "col" ] []
, div [ class "col" ]
[ div [ class "card", style "margin-top" "40px" ]
[ h3 [] [ text "cardano-wasm wallet demo" ]
, p [] [ text "✓ the cardano-wasm engine loaded successfully in your browser." ]
, p [ class "muted small" ] [ text "The wallet application will appear here as it is built up in subsequent changes." ]
]
]
, div [ class "col" ] []
{-| Each incoming port is decoded by Wasm and dispatched as a Msg.
-}
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.batch
[ Ports.wasmWalletGenerated (Wasm.decodeResult Wasm.genDecoder >> GotGeneratedWallet)
, Ports.wasmWalletRestored (Wasm.decodeResult Wasm.genDecoder >> GotRestoredWallet)
, Ports.wasmAddressesDerived (Wasm.decodeResult Wasm.addrsDecoder >> GotDerivedAddresses)
]


main : Program () Model Msg
main =
Browser.element
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
108 changes: 108 additions & 0 deletions cardano-wasm/demo/src/Net.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
module Net exposing
( blockfrostBase
, cliFlag
, explorerTx
, faucetUrl
, netMagic
, netName
, netTag
)

{-| Static tables for the three networks. Pure data — no logic lives here.
-}

import Types exposing (..)



-- NETWORKS


netName : Network -> String
netName n =
case n of
Mainnet ->
"Mainnet"

Preprod ->
"Preprod"

Preview ->
"Preview"


{-| Lowercase identifier used on the JS side of the ports.
-}
netTag : Network -> String
netTag n =
case n of
Mainnet ->
"mainnet"

Preprod ->
"preprod"

Preview ->
"preview"


netMagic : Network -> String
netMagic n =
case n of
Mainnet ->
"764824073"

Preprod ->
"1"

Preview ->
"2"


blockfrostBase : Network -> String
blockfrostBase n =
case n of
Mainnet ->
"https://cardano-mainnet.blockfrost.io/api/v0"

Preprod ->
"https://cardano-preprod.blockfrost.io/api/v0"

Preview ->
"https://cardano-preview.blockfrost.io/api/v0"


explorerTx : Network -> String
explorerTx n =
case n of
Mainnet ->
"https://cardanoscan.io/"

Preprod ->
"https://preprod.cardanoscan.io/"

Preview ->
"https://preview.cardanoscan.io/"


faucetUrl : Network -> Maybe String
faucetUrl n =
case n of
Mainnet ->
Nothing

_ ->
Just "https://docs.cardano.org/cardano-testnets/tools/faucet/"


cliFlag : Network -> String
cliFlag n =
case n of
Mainnet ->
"--mainnet"

Preprod ->
"--testnet-magic 1"

Preview ->
"--testnet-magic 2"
38 changes: 38 additions & 0 deletions cardano-wasm/demo/src/Ports.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
port module Ports exposing (..)

{-| The raw port declarations — the only holes in the wall between Elm and
JavaScript. The JS side lives in web/ports.js. Payloads are untyped JSON;
Wasm.elm encodes the requests and decodes the replies.
-}

import Json.Decode as D
import Json.Encode as E



-- PORTS (out → cardano-wasm / clipboard)


port wasmGenerateWallet : E.Value -> Cmd msg


port wasmRestoreWallet : E.Value -> Cmd msg


port wasmDeriveAddresses : E.Value -> Cmd msg


port clipboardWrite : String -> Cmd msg



-- PORTS (in ← cardano-wasm)


port wasmWalletGenerated : (D.Value -> msg) -> Sub msg


port wasmWalletRestored : (D.Value -> msg) -> Sub msg


port wasmAddressesDerived : (D.Value -> msg) -> Sub msg
Loading
Loading