This commit is contained in:
waalge 2025-03-01 16:23:30 +00:00
commit 9851b6ceda
19 changed files with 2896 additions and 0 deletions

12
.envrc Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
# ^ added for shellcheck and file-type detection
# Watch & reload direnv on change
watch_file devshell.toml
if [[ $(type -t use_flake) != function ]]; then
echo "ERROR: use_flake function missing."
echo "Please update direnv to v2.30.0 or later."
exit 1
fi
use flake

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.direnv/
out/
elm-stuff/
node_modules/

1
.pre-commit-config.yaml Symbolic link
View File

@ -0,0 +1 @@
/nix/store/fvhmybn6prayixnc92dwnsmvzf1lywm4-pre-commit-config.json

29
README.md Normal file
View File

@ -0,0 +1,29 @@
# Tx elm
> Subbit.xyz tx builder using elm-cardano
## Setup
This repo is using nix flakes.
The "right" way to package elm with nix is not known to the author.
- Maybe [elm2nix](https://github.com/cachix/elm2nix)
## CLi
The way elm works seems to be rather sneaky. It adds `Elm` to the global js
scope. In typical use this is then picked up by some script embedded in the page
:
```js
var app = Elm.Main.init({ node: document.getElementById("elm"), flags : ... });
```
Instead we want to call it with node. Appending the output file with
```js
this.Elm.Cli.init({ flags: process.argv });
```
Does the job. But there must be a better way ...

13
elm-cardano.json Normal file
View File

@ -0,0 +1,13 @@
{
"MartinSStewart/elm-uint64": "2.0.0 <= v < 3.0.0",
"dwayne/elm-integer": "1.0.0 <= v < 2.0.0",
"dwayne/elm-natural": "1.1.1 <= v < 2.0.0",
"elm/bytes": "1.0.8 <= v < 2.0.0",
"elm/core": "1.0.5 <= v < 2.0.0",
"elm/json": "1.1.3 <= v < 2.0.0",
"elm-cardano/bech32": "1.0.0 <= v < 2.0.0",
"elm-toulouse/cbor": "4.0.1 <= v < 5.0.0",
"elmcraft/core-extra": "2.1.0 <= v < 3.0.0",
"jxxcarlson/hex": "4.0.1 <= v < 5.0.0",
"turboMaCk/any-dict": "2.6.0 <= v < 3.0.0"
}

8
elm-git.json Normal file
View File

@ -0,0 +1,8 @@
{
"git-dependencies": {
"direct": {
"git@github.com:elm-cardano/elm-cardano.git": "v0.2.0"
},
"indirect": {}
}
}

36
elm.json Normal file
View File

@ -0,0 +1,36 @@
{
"type": "application",
"source-directories": [
"elm-stuff/gitdeps/github.com/elm-cardano/elm-cardano.git/src",
"src"
],
"elm-version": "0.19.1",
"dependencies": {
"direct": {
"MartinSStewart/elm-uint64": "2.0.0",
"dwayne/elm-integer": "1.0.0",
"dwayne/elm-natural": "1.1.1",
"elm/browser": "1.0.2",
"elm/bytes": "1.0.8",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"elm/json": "1.1.3",
"elm-cardano/bech32": "1.0.0",
"elm-toulouse/cbor": "4.0.1",
"elmcraft/core-extra": "2.1.0",
"jxxcarlson/hex": "4.0.1",
"turboMaCk/any-dict": "3.0.0"
},
"indirect": {
"elm/regex": "1.0.0",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"elm/virtual-dom": "1.0.3",
"elm-toulouse/float16": "1.0.1"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

244
flake.lock Normal file
View File

@ -0,0 +1,244 @@
{
"nodes": {
"aiken": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1739388398,
"narHash": "sha256-VwGpkkp4isiiy8GUgJEz6HZklqiKOqc3zluKMVb/UxU=",
"owner": "waalge",
"repo": "aiken",
"rev": "68d7f9ff07a6ab0e8230bb85d475acfaaa0dbca7",
"type": "github"
},
"original": {
"owner": "waalge",
"ref": "waalge/fix-nix-build",
"repo": "aiken",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1738453229,
"narHash": "sha256-7H9XgNiGLKN1G1CgRh0vUL4AheZSYzPm+zmZ7vxbJdo=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "32ea77a06711b758da0ad9bd6a844c5740a87abd",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"git-hooks-nix": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1737465171,
"narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks-nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1739214665,
"narHash": "sha256-26L8VAu3/1YRxS8MHgBOyOM8xALdo6N0I04PgorE7UM=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "64e75cd44acf21c7933d61d7721e812eac1b5a0a",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1738452942,
"narHash": "sha256-vJzFZGaCpnmo7I6i416HaBLpC+hvcURh/BQwROcGIp8=",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
},
"original": {
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/072a6db25e947df2f31aab9eccd0ab75d5b2da11.tar.gz"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1736320768,
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4bc9c909d9ac828a039f288cf872d16d38185db8",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1740560979,
"narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5135c59491985879812717f4c9fea69604e7f26f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"aiken": "aiken",
"flake-parts": "flake-parts",
"git-hooks-nix": "git-hooks-nix",
"nixpkgs": "nixpkgs_3",
"treefmt-nix": "treefmt-nix"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1739327257,
"narHash": "sha256-rlGK8wxz/e50Z+PQRzuP+m03IrGkhcPGmgkBnkEZ9C8=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "e01f2c035b7b8a428c119b183f4cbc55f2eef07c",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1739829690,
"narHash": "sha256-mL1szCeIsjh6Khn3nH2cYtwO5YXG6gBiTw1A30iGeDU=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "3d0579f5cc93436052d94b73925b48973a104204",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

89
flake.nix Normal file
View File

@ -0,0 +1,89 @@
{
description = "Subbit.xyz : Cardano's featherweight L2";
inputs = {
flake-parts.url = "github:hercules-ci/flake-parts";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
git-hooks-nix.url = "github:cachix/git-hooks.nix";
git-hooks-nix.inputs.nixpkgs.follows = "nixpkgs";
treefmt-nix.url = "github:numtide/treefmt-nix";
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
aiken.url = "github:waalge/aiken/waalge/fix-nix-build";
};
outputs = inputs @ {flake-parts, ...}:
flake-parts.lib.mkFlake {inherit inputs;}
{
imports = [
inputs.git-hooks-nix.flakeModule
inputs.treefmt-nix.flakeModule
];
systems = ["x86_64-linux" "aarch64-darwin"];
perSystem = {
config,
self',
inputs',
pkgs,
system,
...
}: {
treefmt = {
projectRootFile = "flake.nix";
flakeFormatter = true;
programs = {
prettier = {
enable = true;
settings = {
printWidth = 80;
proseWrap = "always";
};
};
alejandra.enable = true;
elm-format.enable = true;
};
};
pre-commit.settings.hooks = {
treefmt.enable = true;
aiken = {
enable = true;
name = "aiken";
description = "Run aiken's formatter on ./aik";
files = "\\.ak";
entry = "${inputs'.aiken.packages.aiken}/bin/aiken fmt ./aik";
};
};
devShells.default = let
subbit =
pkgs.writeShellScriptBin "subbit"
''
#!/usr/bin/env bash
node out/index.js $@
'';
in
pkgs.mkShell {
nativeBuildInputs = [
config.treefmt.build.wrapper
];
shellHook = ''
${config.pre-commit.installationScript}
echo 1>&2 "Welcome to the development shell!"
export alias subbit="node out/index.js "
'';
packages = [
pkgs.just
inputs'.aiken.packages.aiken
pkgs.elmPackages.elm
pkgs.elmPackages.nodejs
pkgs.elmPackages.elm-language-server
pkgs.elmPackages.elm-git-install
pkgs.uglify-js
pkgs.caddy
pkgs.pnpm
pkgs.typescript-language-server
subbit
];
};
};
flake = {};
};
}

26
justfile Normal file
View File

@ -0,0 +1,26 @@
default:
just -l
out_dir := "./out"
filename := "elm.js"
out_path := out_dir + "/" + filename
min_out_path := out_dir + "/" + filename
static_dir := "./static"
setup:
mkdir -p {{out_dir}}
cp -r {{static_dir}}/* {{out_dir}}
dev args:
just setup
elm make --optimize src/Cli.elm --output {{out_path}}
cp src/index.js {{out_dir}}
node out/index.js {{args}}
serve:
caddy file-server --listen :8888 --root {{out_dir}}
# Make main and optimize
make:
elm make src/Main.elm --output elm.js --optimize

14
package.json Normal file
View File

@ -0,0 +1,14 @@
{
"name": "tx-elm",
"version": "1.0.0",
"description": "",
"main": "out/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.4.1",
"dependencies": {}
}

8
pnpm-lock.yaml Normal file
View File

@ -0,0 +1,8 @@
lockfileVersion: "9.0"
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.: {}

91
src/Cli.elm Normal file
View File

@ -0,0 +1,91 @@
port module Cli exposing (..)
import Cbor.Tag exposing (Tag(..))
import CliTools exposing (doOnCmd, mkCLi)
import Platform exposing (worker)
version : String
version =
"0.0.1"
versionMessage : String
versionMessage =
version
helpMessage : String
helpMessage =
"""
cli - a cli tool in elm
How to build a cli tool in elm
"""
type alias Flags =
()
type alias Model =
()
type Msg
= Args (List String)
main : Program Flags Model Msg
main =
worker
{ init = init
, update = update
, subscriptions = subscriptions
}
init : Flags -> ( Model, Cmd Msg )
init _ =
( (), Cmd.none )
subscriptions : Model -> Sub Msg
subscriptions _ =
do Args
port do : (List String -> msg) -> Sub msg
port stdout : String -> Cmd msg
s : String -> ( Model, Cmd msg )
s x =
( (), stdout x )
update : Msg -> Model -> ( Model, Cmd Msg )
update (Args l) model =
cli l model
cli : List String -> Model -> ( Model, Cmd Msg )
cli =
mkCLi stdout version helpMessage dos
dos : List (List String -> Model -> Maybe ( Model, Cmd Msg ))
dos =
[ doOnCmd "tx" doTx, doOnCmd "view" doView ]
doTx : List String -> Model -> ( Model, Cmd Msg )
doTx _ _ =
s "doTx is not yet implemented"
doView : List String -> Model -> ( Model, Cmd Msg )
doView _ _ =
s "doView is not yet implemented"

90
src/CliTools.elm Normal file
View File

@ -0,0 +1,90 @@
module CliTools exposing (..)
import Basics.Extra exposing (uncurry)
import MaybeExtra exposing (..)
{-| -}
doWrapString : (String -> Cmd msg) -> (List String -> Maybe String) -> List String -> model -> Maybe ( model, Cmd msg )
doWrapString pt fn args model =
Maybe.map (\x -> ( model, pt x )) (fn args)
isHelp : List String -> Bool
isHelp l =
case l of
[] ->
True
fst :: _ ->
List.member fst [ "help", "-h", "--help" ]
justHelp : String -> List String -> Maybe String
justHelp =
justIf isHelp
doHelpWith : (String -> Cmd msg) -> String -> List String -> model -> Maybe ( model, Cmd msg )
doHelpWith pt msg args =
doWrapString pt (justHelp msg) args
isVersion : List String -> Bool
isVersion l =
case l of
[] ->
False
fst :: _ ->
List.member fst [ "version", "-v", "--version" ]
justVersionWith : String -> List String -> Maybe String
justVersionWith =
justIf isVersion
doVersionWith : (String -> Cmd msg) -> String -> List String -> model -> Maybe ( model, Cmd msg )
doVersionWith pt version args =
doWrapString pt (justVersionWith version) args
mkDefDos : (String -> Cmd msg) -> String -> String -> List (List String -> model -> Maybe ( model, Cmd msg ))
mkDefDos stdout version helpMessage =
[ doHelpWith stdout helpMessage
, doVersionWith stdout version
]
doElse : (String -> Cmd msg) -> List String -> model -> ( model, Cmd msg )
doElse pt l model =
case l of
[] ->
( model, pt "Try `help`" )
fst :: _ ->
( model, pt ("Unknown cmd : `" ++ fst ++ "`") )
doOnCmd : String -> (List String -> model -> ( model, Cmd msg )) -> List String -> model -> Maybe ( model, Cmd msg )
doOnCmd cmd doThing args model =
case args of
[] ->
Nothing
fst :: rest ->
if cmd == fst then
Just (doThing rest model)
else
Nothing
mkCLi : (String -> Cmd msg) -> String -> String -> List (List String -> model -> Maybe ( model, Cmd msg )) -> List String -> model -> ( model, Cmd msg )
mkCLi stdout version helpMessage dos =
let
defDos =
mkDefDos stdout version helpMessage
in
\args -> \model -> justFirstElse (List.map uncurry (defDos ++ dos)) (uncurry (doElse stdout)) ( args, model )

100
src/Main.elm Normal file
View File

@ -0,0 +1,100 @@
module Main exposing (main)
import Browser
import Cardano.Data exposing (Data(..), toCbor)
import Cbor.Encode as CborE
import Hex.Convert as Hex
import Html exposing (Html, button, div, h1, text)
import Html.Attributes exposing (class)
import Html.Events exposing (onClick)
import Integer exposing (Integer)
import Natural
-- MAIN
main : Program () Model Msg
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model =
Int
init : Model
init =
0
modelToData model =
case Integer.fromInt model of
Just i ->
Int i
_ ->
Int Integer.one
-- type Data
-- = Constr Natural (List Data)
-- | Map (List ( Data, Data ))
-- | List (List Data)
-- | Int Integer
-- | Bytes (Bytes Any)
asString : Data -> String
asString d =
case d of
Constr n _ ->
"Constr " ++ Natural.toString n
Map l ->
"Map " ++ String.fromInt (List.length l)
_ ->
"OTHER "
-- UPDATE
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model * model + 1
Decrement ->
model - 1
-- VIEW
view : Model -> Html Msg
view model =
div [ class "container" ]
[ div [ class "row" ]
[ div [ class "col-sm-8" ] [ h1 [] [ text "Title" ] ] ]
, div [ class "row" ]
[ div [ class "col-sm-2" ] [ button [ onClick Increment ] [ text "+" ] ]
, div [ class "col-sm-2" ] [ text (Hex.toString (CborE.encode (toCbor (modelToData model)))) ]
, div [ class "col-sm-2" ] [ button [ onClick Decrement ] [ text "-" ] ]
]
]

35
src/MaybeExtra.elm Normal file
View File

@ -0,0 +1,35 @@
module MaybeExtra exposing (justFirst, justFirstElse, justIf)
justFirst : List (a -> Maybe b) -> a -> Maybe b
justFirst l x =
case l of
[] ->
Nothing
first :: rest ->
case first x of
Just y ->
Just y
_ ->
justFirst rest x
justFirstElse : List (a -> Maybe b) -> (a -> b) -> a -> b
justFirstElse l def x =
case justFirst l x of
Just y ->
y
Nothing ->
def x
justIf : (a -> Bool) -> b -> a -> Maybe b
justIf pred y x =
if pred x then
Just y
else
Nothing

10
src/index.js Normal file
View File

@ -0,0 +1,10 @@
var { stdout } = require("node:process");
var Elm = require("./elm").Elm;
var app = Elm.Cli.init({});
app.ports.stdout.subscribe(function (message) {
stdout.write(message + "\n");
});
app.ports.do.send(process.argv.slice(2));

16
static/index.html Normal file
View File

@ -0,0 +1,16 @@
<!doctype html>
<html>
<head>
<title>My Elm Project</title>
<meta charset="UTF-8" />
<title>Main</title>
<link rel="stylesheet" href="./nord-minicss.css" />
<script src="elm.js"></script>
</head>
<body>
<div id="elm"></div>
<script>
var app = Elm.Main.init({ node: document.getElementById("elm") });
</script>
</body>
</html>

2070
static/nord-minicss.css Normal file

File diff suppressed because it is too large Load Diff