From f68883c9e24a19268ded5ccd2dfd2e2c5d7fb684 Mon Sep 17 00:00:00 2001 From: Interchouette <484423+Interchouette@users.noreply.github.com> Date: Tue, 11 Aug 2026 18:45:42 +0200 Subject: [PATCH] feat(args-builder): sync entrypoints and Args JSON via ceps schema Hydrate the Args builder from on-chain entrypoint metadata or the schema-only ceps wasm pack, prefill values from existing Args JSON, and emit session_args_json from the in-memory row model. Drop CEP-47. Co-authored-by: Cursor --- ceps-client-wasm-schema/pkg-nodejs/README.md | 320 +++++++++++++ .../pkg-nodejs/ceps_client_wasm.d.ts | 32 ++ .../pkg-nodejs/ceps_client_wasm.js | 241 ++++++++++ .../pkg-nodejs/ceps_client_wasm_bg.wasm | Bin 0 -> 87089 bytes .../pkg-nodejs/ceps_client_wasm_bg.wasm.d.ts | 17 + .../pkg-nodejs/package.json | 21 + ceps-client-wasm-schema/pkg/README.md | 320 +++++++++++++ .../pkg/ceps_client_wasm.d.ts | 74 +++ .../pkg/ceps_client_wasm.js | 333 +++++++++++++ .../pkg/ceps_client_wasm_bg.wasm | Bin 0 -> 87089 bytes .../pkg/ceps_client_wasm_bg.wasm.d.ts | 17 + ceps-client-wasm-schema/pkg/package.json | 25 + docker/Dockerfile | 1 + www/apps/frontend/project.json | 5 + www/libs/api-interfaces/src/index.ts | 1 + .../api-interfaces/src/lib/api-interfaces.ts | 4 + .../api-interfaces/src/lib/session-args.ts | 439 ++++++++++++++++++ .../arg-builder/arg-builder.component.html | 22 +- .../arg-builder/arg-builder.component.spec.ts | 81 +++- .../lib/arg-builder/arg-builder.component.ts | 293 +++++------- .../src/lib/arg-builder/cep-schema.service.ts | 100 ++++ .../src/lib/arg-builder/session-args.spec.ts | 75 +++ .../deployer/src/lib/arg-builder/tabs.ts | 318 +------------ .../lib/put-deploy/put-deploy.component.ts | 34 +- .../src/lib/argument/argument.component.html | 5 +- .../src/lib/argument/argument.component.ts | 97 +++- www/libs/ui/tabs/src/lib/tabs/tabs.ts | 6 +- www/package-lock.json | 20 + www/package.json | 2 + 29 files changed, 2378 insertions(+), 525 deletions(-) create mode 100644 ceps-client-wasm-schema/pkg-nodejs/README.md create mode 100644 ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm.d.ts create mode 100644 ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm.js create mode 100644 ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm_bg.wasm create mode 100644 ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm_bg.wasm.d.ts create mode 100644 ceps-client-wasm-schema/pkg-nodejs/package.json create mode 100644 ceps-client-wasm-schema/pkg/README.md create mode 100644 ceps-client-wasm-schema/pkg/ceps_client_wasm.d.ts create mode 100644 ceps-client-wasm-schema/pkg/ceps_client_wasm.js create mode 100644 ceps-client-wasm-schema/pkg/ceps_client_wasm_bg.wasm create mode 100644 ceps-client-wasm-schema/pkg/ceps_client_wasm_bg.wasm.d.ts create mode 100644 ceps-client-wasm-schema/pkg/package.json create mode 100644 www/libs/api-interfaces/src/lib/session-args.ts create mode 100644 www/libs/feature/deployer/src/lib/arg-builder/cep-schema.service.ts create mode 100644 www/libs/feature/deployer/src/lib/arg-builder/session-args.spec.ts diff --git a/ceps-client-wasm-schema/pkg-nodejs/README.md b/ceps-client-wasm-schema/pkg-nodejs/README.md new file mode 100644 index 0000000..f88ab12 --- /dev/null +++ b/ceps-client-wasm-schema/pkg-nodejs/README.md @@ -0,0 +1,320 @@ +# ceps-rust-ts-client + +One **Rust** client for Casper **CEP-18**, **CEP-78**, **CEP-85**, and **CEP-95**, with a **`ceps-client-cli`**, **`ceps-rust-ts-client-mcp`**, and **WASM packs** so the same client can run from shell, agents, Node, or the browser. + +It replaces the separate TypeScript **`client-js`** packages that lived next to each CEP contract. Instead of per-CEP JS clients, you use one library: `CEP18Client` / `CEP78Client` / `CEP85Client` / `CEP95Client`, on top of [`casper-rust-wasm-sdk`](https://github.com/casper-ecosystem/casper-rust-wasm-sdk). + +```text +CEP-18 client-js ─┐ +CEP-78 client-js ─┼─→ ceps-client (Rust) + ceps-client-cli + ceps-rust-ts-client-mcp + ceps-client-wasm +CEP-85 client-js ─┤ +CEP-95 JS client ─┘ +``` + +## What you get + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PieceNameRoleIn-tree path
Rust libraryceps‑clientCEP API for native Rust appsceps-client/
CLIceps‑client‑cliStatus and common queries from the shellceps-client-cli/
MCP serverceps-rust-ts-client-mcpFull CEP agent tools (stdio / HTTP :6790)mcp/
Client JS packsceps‑client‑wasmSame CEP classes for Node and browsers (replaces per-CEP client-js)ceps-client-wasm/pkg / pkg-nodejs
Demo contract WASMsceps‑contractsOn-chain bytecode for install (demo tips)tests/wasm/{cep18,cep78,cep85,cep95}/
+ +Release downloads use the same names: `ceps-client-cli-*-linux-x86_64`, `ceps-rust-ts-client-mcp-*-linux-x86_64`, `ceps-client-wasm-*.tgz`, and `ceps-contracts-*.tgz`. The library / CLI / MCP / JS packs are this client; `ceps-contracts` is sample contract bytecode, not the JS library. + +## What you can do + +| CEP | Standard | Typical flow | +| ------ | -------------- | -------------------------------------------------------------------------- | +| **18** | Fungible token | install → bind hash → `name` / `balance_of` → `transfer` / `mint` / `burn` | +| **78** | Enhanced NFT | install → bind hash → `mint` → `owner_of` / `balance_of` | +| **85** | Multi-token | install → bind hash → `mint` / `burn` → `balance_of(account, id)` | +| **95** | NFT (Odra tip) | install → `bind_odra_install` → `mint` → `owner_of` / `approve` | + +Defaults talk to local NCTL (`http://127.0.0.1:11101`, SSE `…:18101/events`, chain `casper-net-1`). + +## Usage + +Needs a running node, a secret-key PEM, and on-chain contract `.wasm` bytes (your own build, or the [demo tips](#contract-wasms-demos) via `make wasm-from-ceps`). + +### CEP-18 - fungible + +```text +install → named keys cep18_contract_hash_* / package_* + → set_contract_hash + → name / symbol / balance_of + → transfer | mint | burn +``` + +```rust +use ceps_client::cep18::InstallArgs; +use ceps_client::{CEP18Client, TransactionParams, EventsMode, Verbosity}; + +let mut client = CEP18Client::new( + "http://127.0.0.1:11101", + Some("http://127.0.0.1:18101/events".into()), + Some("casper-net-1".into()), + Some(Verbosity::Low), +)?; + +let put = client + .install( + &InstallArgs::new("MyToken", "MTK", 9, "1000000000") + .with_events_mode(EventsMode::CES) + .with_mint_and_burn(true), + &contract_wasm_bytes, + &TransactionParams::new(&secret_pem, "400000000000"), + ) + .await?; +client.set_contract_hash(&contract_hash, Some(&package_hash))?; +let bal = client.balance_of("account-hash-…").await?; +``` + +Details: [docs/cep18/](docs/cep18/) · example: `cargo run -p ceps-client --example cep18_install` + +### CEP-78 - NFT + +```text +install → named keys cep78_contract_hash_* / package_* + → set_contract_hash + → mint → owner_of / balance_of +``` + +```rust +use ceps_client::cep78::InstallArgs; +use ceps_client::{CEP78Client, TransactionParams, EventsMode78, Verbosity}; + +let mut client = CEP78Client::new(/* rpc, sse, chain, verbosity */)?; +client + .install( + &InstallArgs::new("MyNft", "NFT", 100).with_events_mode(EventsMode78::CES), + &contract_wasm_bytes, + &TransactionParams::new(&secret_pem, "600000000000"), + ) + .await?; +client.set_contract_hash(&contract_hash, Some(&package_hash))?; +client + .mint( + "account-hash-…", + r#"{"name":"token-1"}"#, + None, + &TransactionParams::new(&secret_pem, "5000000000"), + ) + .await?; +let owner = client.owner_of(&token_id).await?; +``` + +Details: [docs/cep78/](docs/cep78/) · example: `cargo run -p ceps-client --example cep78_install` + +### CEP-85 - multi-token + +```text +install → named keys cep85_contract_hash_* / package_* + → set_contract_hash + → mint / burn → balance_of(account, id) +``` + +```rust +use ceps_client::cep85::InstallArgs; +use ceps_client::{CEP85Client, TransactionParams, EventsMode, Verbosity}; + +let mut client = CEP85Client::new(/* rpc, sse, chain, verbosity */)?; +client + .install( + &InstallArgs::new("MyMulti", "https://example.com/{id}.json") + .with_events_mode(EventsMode::CES) + .with_enable_burn(true), + &contract_wasm_bytes, + &TransactionParams::new(&secret_pem, "550000000000"), + ) + .await?; +client.set_contract_hash(&contract_hash, Some(&package_hash))?; +client.mint(&owner, "1", "10", None, &TransactionParams::new(&secret_pem, "5000000000")).await?; +let bal = client.balance_of(&owner, "1").await?; +``` + +Details: [docs/cep85/](docs/cep85/) · example: `cargo run -p ceps-client --example cep85_install` + +### CEP-95 - NFT (Odra tip) + +```text +install (odra_cfg_package_hash_key_name) → bind_odra_install + → name / symbol / balance_of / owner_of + → mint | burn | transfer_from | approve* +``` + +```rust +use ceps_client::cep95::InstallArgs; +use ceps_client::{CEP95Client, TransactionParams, Verbosity}; + +let mut client = CEP95Client::new(/* rpc, sse, chain, verbosity */)?; +client + .install( + &InstallArgs::new("MyNft", "MNFT", "cep95_pkg_demo"), + &contract_wasm_bytes, + &TransactionParams::new(&secret_pem, "600000000000"), + ) + .await?; +client.bind_odra_install(&installer_public_key, "cep95_pkg_demo").await?; +client.mint(&owner, "1", None, &TransactionParams::new(&secret_pem, "5000000000")).await?; +let owner_of = client.owner_of("1").await?; +``` + +Details: [docs/cep95/](docs/cep95/) · example: `cargo run -p ceps-client --example cep95_install` + +### CLI + +```bash +cargo run -p ceps-client-cli -- status +cargo run -p ceps-client-cli -- cep18 info +cargo run -p ceps-client-cli -- cep78 balance --contract-hash --account +cargo run -p ceps-client-cli -- cep85 balance --contract-hash --account <…> --id 1 +cargo run -p ceps-client-cli -- cep95 owner-of --contract-hash --token-id 1 +``` + +Mutations are on the library / examples today. Flags: [docs/cli.md](docs/cli.md). + +### Try an install end-to-end + +```bash +make prepare && make build +make wasm-from-ceps # copy demo tip contract WASMs into tests/wasm/ +# NCTL running + SECRET_KEY_USER_1 set to a PEM: +cargo run -p ceps-client --example cep18_install +``` + +More setup: [docs/getting-started.md](docs/getting-started.md). + +## Client JS packs (`ceps-client-wasm`) + +`ceps-client-wasm` is the **Rust CEP client compiled for JavaScript** (the replacement for per-CEP `client-js`). Use it from Node or the browser for the same `CEP18Client` / `CEP78Client` / `CEP85Client` surface. + +It is **not** on-chain contract bytecode (that is `tests/wasm/` / `ceps-contracts-*.tgz`). + +| Target | In-tree (committed) | Typical use | +| ------ | ------------------------------ | -------------------------- | +| Node | `ceps-client-wasm/pkg-nodejs/` | Backend / scripts / Vitest | +| Web | `ceps-client-wasm/pkg/` | Bundled frontends | + +Rebuild locally with `make pack` (or `make nodejs` / `make web`). Details: [docs/wasm-ts.md](docs/wasm-ts.md) · [docs/releases.md](docs/releases.md). + +**Or** fetch a published pack (no local `wasm-pack`): + +```bash +TAG=v1.0.0 # or dev-preview +LABEL=${TAG#v} +curl -fsSL -o ceps-client-wasm-nodejs.tgz \ + "https://github.com/Interchouette-ITC/ceps-rust-ts-client/releases/download/${TAG}/ceps-client-wasm-nodejs-${LABEL}.tgz" +mkdir -p ceps-client-wasm && tar -xzf ceps-client-wasm-nodejs.tgz -C ceps-client-wasm +# -> ceps-client-wasm/pkg-nodejs/ +``` + +```js +import { CEP18Client } from "ceps-client-wasm"; // file:./ceps-client-wasm/pkg-nodejs after unpack + +const client = new CEP18Client( + "http://127.0.0.1:11101", + "http://127.0.0.1:18101/events", + "casper-net-1", + 0, +); +client.setContractHash(contractHash, packageHash); +const name = await client.name(); +const bal = await client.balanceOf("account-hash-…"); +``` + +Install from JS takes contract bytes as `Uint8Array` and returns JSON `{ transactionHash, hasExecutionResult }`. Bound methods today are a subset of the Rust API (see [docs/wasm-ts.md](docs/wasm-ts.md)); full parity is on `ceps-client`. + +## Contract WASMs (demos) + +This client is **not** a contract repo. You pass on-chain `.wasm` into `install`. + +**In-tree:** the last staged demo tip builds ship under `tests/wasm/{cep18,cep78,cep85}/` (same idea as committing `pkg` / `pkg-nodejs`). Use those bytes directly, or refresh with `make wasm-from-ceps`. + +**Or** download the release bundle (no tip checkout / no contract build): + +```bash +TAG=v1.0.0 +LABEL=${TAG#v} +curl -fsSL -o ceps-contracts.tgz \ + "https://github.com/Interchouette-ITC/ceps-rust-ts-client/releases/download/${TAG}/ceps-contracts-${LABEL}.tgz" +mkdir -p tests/wasm && tar -xzf ceps-contracts.tgz -C tests/wasm +``` + +Tips are short-lived entity-era builds for demos/CI, not a claim of "the" upstream CEP tip forever. Sources: + +| CEP | Demo tip repo | Branch | What you get | +| --- | ------------- | ------ | ------------ | +| 18 | [Interchouette-ITC/cep-18](https://github.com/Interchouette-ITC/cep-18) | `ceps-client-test` | Fungible contract WASM | +| 78 | [Interchouette-ITC/cep-78-enhanced-nft](https://github.com/Interchouette-ITC/cep-78-enhanced-nft) | `ceps-client-test` | NFT + session WASMs | +| 85 | [Interchouette-ITC/cep-85](https://github.com/Interchouette-ITC/cep-85) | `ceps-client-test` | Multi-token WASM | + +```bash +# after checking out those tips and building contracts there: +make wasm-from-ceps # → tests/wasm/{cep18,cep78,cep85}/ +``` + +Override checkout roots with `CEP18_PRODUCT` / `CEP78_PRODUCT` / `CEP85_PRODUCT`. Pins and SHAs: [docs/contributing.md](docs/contributing.md). All release downloads: [docs/releases.md](docs/releases.md). + +## Documentation + +| Doc | Description | +| ------------------------------------------------------------------------------------------------------------ | ----------------------------------- | +| [Getting started](docs/getting-started.md) | Build, NCTL defaults, first run | +| [Architecture](docs/architecture.md) | How lib / CLI / WASM sit on the SDK | +| [docs/cep18/](docs/cep18/) · [cep78/](docs/cep78/) · [cep85/](docs/cep85/) | Per-CEP guides | +| [CLI](docs/cli.md) · [WASM / TS](docs/wasm-ts.md) | Surfaces | +| [Testing](docs/testing.md) · [CI / CD](docs/ci.md) · [Releases](docs/releases.md) · [Docker](docs/docker.md) | Verify and ship | +| [Contributing](docs/contributing.md) · [SDK](docs/sdk.md) | Tips, pins, upgrades | +| [SECURITY.md](docs/SECURITY.md) | Keys and reporting | +| `make doc` | rustdoc → `docs/api-rust/` | + +## Docker + +```bash +make release-cli-bin && make docker-build IMAGE_TAG=local +docker run --rm ceps-rust-ts-client:local --help +docker pull interchouette/ceps-rust-ts-client:dev +``` + +See [docs/docker.md](docs/docker.md). + +## License / security + +GPL-3.0. See [LICENSE](LICENSE) and [docs/SECURITY.md](docs/SECURITY.md). diff --git a/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm.d.ts b/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm.d.ts new file mode 100644 index 0000000..9a99391 --- /dev/null +++ b/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm.d.ts @@ -0,0 +1,32 @@ +/* tslint:disable */ +/* eslint-disable */ + +/** + * CEP-18 schema JSON. + */ +export function cep18SchemaJson(): string; + +/** + * CEP-78 schema JSON. + */ +export function cep78SchemaJson(): string; + +/** + * CEP-85 schema JSON. + */ +export function cep85SchemaJson(): string; + +/** + * CEP-95 schema JSON. + */ +export function cep95SchemaJson(): string; + +/** + * JSON schema for a CEP id (`cep18`, `cep78`, `cep85`, `cep95`). + */ +export function schemaJson(cep: string): string; + +/** + * Supported CEP id strings. + */ +export function supportedCeps(): any; diff --git a/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm.js b/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm.js new file mode 100644 index 0000000..40852aa --- /dev/null +++ b/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm.js @@ -0,0 +1,241 @@ +/* @ts-self-types="./ceps_client_wasm.d.ts" */ + +/** + * CEP-18 schema JSON. + * @returns {string} + */ +function cep18SchemaJson() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.cep18SchemaJson(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} +exports.cep18SchemaJson = cep18SchemaJson; + +/** + * CEP-78 schema JSON. + * @returns {string} + */ +function cep78SchemaJson() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.cep78SchemaJson(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} +exports.cep78SchemaJson = cep78SchemaJson; + +/** + * CEP-85 schema JSON. + * @returns {string} + */ +function cep85SchemaJson() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.cep85SchemaJson(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} +exports.cep85SchemaJson = cep85SchemaJson; + +/** + * CEP-95 schema JSON. + * @returns {string} + */ +function cep95SchemaJson() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.cep95SchemaJson(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} +exports.cep95SchemaJson = cep95SchemaJson; + +/** + * JSON schema for a CEP id (`cep18`, `cep78`, `cep85`, `cep95`). + * @param {string} cep + * @returns {string} + */ +function schemaJson(cep) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(cep, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.schemaJson(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} +exports.schemaJson = schemaJson; + +/** + * Supported CEP id strings. + * @returns {any} + */ +function supportedCeps() { + const ret = wasm.supportedCeps(); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} +exports.supportedCeps = supportedCeps; +function __wbg_get_imports() { + const import0 = { + __proto__: null, + __wbg___wbindgen_throw_bb96b2010945f0bc: function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }, + __wbg_parse_1cc93481b0865939: function() { return handleError(function (arg0, arg1) { + const ret = JSON.parse(getStringFromWasm0(arg0, arg1)); + return ret; + }, arguments); }, + __wbindgen_cast_0000000000000001: function(arg0, arg1) { + // Cast intrinsic for `Ref(String) -> Externref`. + const ret = getStringFromWasm0(arg0, arg1); + return ret; + }, + __wbindgen_init_externref_table: function() { + const table = wasm.__wbindgen_externrefs; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + }, + }; + return { + __proto__: null, + "./ceps_client_wasm_bg.js": import0, + }; +} + +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_externrefs.set(idx, obj); + return idx; +} + +function getStringFromWasm0(ptr, len) { + return decodeText(ptr >>> 0, len); +} + +let cachedUint8ArrayMemory0 = null; +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } +} + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = cachedTextEncoder.encodeInto(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function takeFromExternrefTable0(idx) { + const value = wasm.__wbindgen_externrefs.get(idx); + wasm.__externref_table_dealloc(idx); + return value; +} + +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); +cachedTextDecoder.decode(); +function decodeText(ptr, len) { + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +const cachedTextEncoder = new TextEncoder(); + +if (!('encodeInto' in cachedTextEncoder)) { + cachedTextEncoder.encodeInto = function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + }; +} + +let WASM_VECTOR_LEN = 0; + +const wasmPath = `${__dirname}/ceps_client_wasm_bg.wasm`; +const wasmBytes = require('fs').readFileSync(wasmPath); +const wasmModule = new WebAssembly.Module(wasmBytes); +let wasmInstance = new WebAssembly.Instance(wasmModule, __wbg_get_imports()); +let wasm = wasmInstance.exports; +wasm.__wbindgen_start(); diff --git a/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm_bg.wasm b/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm_bg.wasm new file mode 100644 index 0000000000000000000000000000000000000000..54c5e081832f5f55b0efbed6a484bca8749d3c68 GIT binary patch literal 87089 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 bRUn{Q9UVZP53AgcqI+cUsDQl<@}K?>>EGU3 literal 0 HcmV?d00001 diff --git a/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm_bg.wasm.d.ts b/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm_bg.wasm.d.ts new file mode 100644 index 0000000..4c995f3 --- /dev/null +++ b/ceps-client-wasm-schema/pkg-nodejs/ceps_client_wasm_bg.wasm.d.ts @@ -0,0 +1,17 @@ +/* tslint:disable */ +/* eslint-disable */ +export const memory: WebAssembly.Memory; +export const cep18SchemaJson: () => [number, number]; +export const cep78SchemaJson: () => [number, number]; +export const cep85SchemaJson: () => [number, number]; +export const cep95SchemaJson: () => [number, number]; +export const schemaJson: (a: number, b: number) => [number, number, number, number]; +export const supportedCeps: () => [number, number, number]; +export const __wbindgen_exn_store: (a: number) => void; +export const __externref_table_alloc: () => number; +export const __wbindgen_externrefs: WebAssembly.Table; +export const __wbindgen_free: (a: number, b: number, c: number) => void; +export const __wbindgen_malloc: (a: number, b: number) => number; +export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; +export const __externref_table_dealloc: (a: number) => void; +export const __wbindgen_start: () => void; diff --git a/ceps-client-wasm-schema/pkg-nodejs/package.json b/ceps-client-wasm-schema/pkg-nodejs/package.json new file mode 100644 index 0000000..6f3d74c --- /dev/null +++ b/ceps-client-wasm-schema/pkg-nodejs/package.json @@ -0,0 +1,21 @@ +{ + "name": "ceps-client-wasm-schema-nodejs", + "collaborators": [ + "3099551+gRoussac@users.noreply.github.com" + ], + "description": "wasm-bindgen CEP client for Node and browsers (same API as ceps-client; replaces per-CEP client-js).", + "version": "1.0.0", + "license": "SEE LICENSE IN ../LICENSE", + "repository": { + "type": "git", + "url": "https://github.com/Interchouette-ITC/ceps-rust-ts-client" + }, + "files": [ + "ceps_client_wasm_bg.wasm", + "ceps_client_wasm.js", + "ceps_client_wasm.d.ts" + ], + "main": "ceps_client_wasm.js", + "homepage": "https://github.com/Interchouette-ITC/ceps-rust-ts-client", + "types": "ceps_client_wasm.d.ts" +} diff --git a/ceps-client-wasm-schema/pkg/README.md b/ceps-client-wasm-schema/pkg/README.md new file mode 100644 index 0000000..f88ab12 --- /dev/null +++ b/ceps-client-wasm-schema/pkg/README.md @@ -0,0 +1,320 @@ +# ceps-rust-ts-client + +One **Rust** client for Casper **CEP-18**, **CEP-78**, **CEP-85**, and **CEP-95**, with a **`ceps-client-cli`**, **`ceps-rust-ts-client-mcp`**, and **WASM packs** so the same client can run from shell, agents, Node, or the browser. + +It replaces the separate TypeScript **`client-js`** packages that lived next to each CEP contract. Instead of per-CEP JS clients, you use one library: `CEP18Client` / `CEP78Client` / `CEP85Client` / `CEP95Client`, on top of [`casper-rust-wasm-sdk`](https://github.com/casper-ecosystem/casper-rust-wasm-sdk). + +```text +CEP-18 client-js ─┐ +CEP-78 client-js ─┼─→ ceps-client (Rust) + ceps-client-cli + ceps-rust-ts-client-mcp + ceps-client-wasm +CEP-85 client-js ─┤ +CEP-95 JS client ─┘ +``` + +## What you get + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PieceNameRoleIn-tree path
Rust libraryceps‑clientCEP API for native Rust appsceps-client/
CLIceps‑client‑cliStatus and common queries from the shellceps-client-cli/
MCP serverceps-rust-ts-client-mcpFull CEP agent tools (stdio / HTTP :6790)mcp/
Client JS packsceps‑client‑wasmSame CEP classes for Node and browsers (replaces per-CEP client-js)ceps-client-wasm/pkg / pkg-nodejs
Demo contract WASMsceps‑contractsOn-chain bytecode for install (demo tips)tests/wasm/{cep18,cep78,cep85,cep95}/
+ +Release downloads use the same names: `ceps-client-cli-*-linux-x86_64`, `ceps-rust-ts-client-mcp-*-linux-x86_64`, `ceps-client-wasm-*.tgz`, and `ceps-contracts-*.tgz`. The library / CLI / MCP / JS packs are this client; `ceps-contracts` is sample contract bytecode, not the JS library. + +## What you can do + +| CEP | Standard | Typical flow | +| ------ | -------------- | -------------------------------------------------------------------------- | +| **18** | Fungible token | install → bind hash → `name` / `balance_of` → `transfer` / `mint` / `burn` | +| **78** | Enhanced NFT | install → bind hash → `mint` → `owner_of` / `balance_of` | +| **85** | Multi-token | install → bind hash → `mint` / `burn` → `balance_of(account, id)` | +| **95** | NFT (Odra tip) | install → `bind_odra_install` → `mint` → `owner_of` / `approve` | + +Defaults talk to local NCTL (`http://127.0.0.1:11101`, SSE `…:18101/events`, chain `casper-net-1`). + +## Usage + +Needs a running node, a secret-key PEM, and on-chain contract `.wasm` bytes (your own build, or the [demo tips](#contract-wasms-demos) via `make wasm-from-ceps`). + +### CEP-18 - fungible + +```text +install → named keys cep18_contract_hash_* / package_* + → set_contract_hash + → name / symbol / balance_of + → transfer | mint | burn +``` + +```rust +use ceps_client::cep18::InstallArgs; +use ceps_client::{CEP18Client, TransactionParams, EventsMode, Verbosity}; + +let mut client = CEP18Client::new( + "http://127.0.0.1:11101", + Some("http://127.0.0.1:18101/events".into()), + Some("casper-net-1".into()), + Some(Verbosity::Low), +)?; + +let put = client + .install( + &InstallArgs::new("MyToken", "MTK", 9, "1000000000") + .with_events_mode(EventsMode::CES) + .with_mint_and_burn(true), + &contract_wasm_bytes, + &TransactionParams::new(&secret_pem, "400000000000"), + ) + .await?; +client.set_contract_hash(&contract_hash, Some(&package_hash))?; +let bal = client.balance_of("account-hash-…").await?; +``` + +Details: [docs/cep18/](docs/cep18/) · example: `cargo run -p ceps-client --example cep18_install` + +### CEP-78 - NFT + +```text +install → named keys cep78_contract_hash_* / package_* + → set_contract_hash + → mint → owner_of / balance_of +``` + +```rust +use ceps_client::cep78::InstallArgs; +use ceps_client::{CEP78Client, TransactionParams, EventsMode78, Verbosity}; + +let mut client = CEP78Client::new(/* rpc, sse, chain, verbosity */)?; +client + .install( + &InstallArgs::new("MyNft", "NFT", 100).with_events_mode(EventsMode78::CES), + &contract_wasm_bytes, + &TransactionParams::new(&secret_pem, "600000000000"), + ) + .await?; +client.set_contract_hash(&contract_hash, Some(&package_hash))?; +client + .mint( + "account-hash-…", + r#"{"name":"token-1"}"#, + None, + &TransactionParams::new(&secret_pem, "5000000000"), + ) + .await?; +let owner = client.owner_of(&token_id).await?; +``` + +Details: [docs/cep78/](docs/cep78/) · example: `cargo run -p ceps-client --example cep78_install` + +### CEP-85 - multi-token + +```text +install → named keys cep85_contract_hash_* / package_* + → set_contract_hash + → mint / burn → balance_of(account, id) +``` + +```rust +use ceps_client::cep85::InstallArgs; +use ceps_client::{CEP85Client, TransactionParams, EventsMode, Verbosity}; + +let mut client = CEP85Client::new(/* rpc, sse, chain, verbosity */)?; +client + .install( + &InstallArgs::new("MyMulti", "https://example.com/{id}.json") + .with_events_mode(EventsMode::CES) + .with_enable_burn(true), + &contract_wasm_bytes, + &TransactionParams::new(&secret_pem, "550000000000"), + ) + .await?; +client.set_contract_hash(&contract_hash, Some(&package_hash))?; +client.mint(&owner, "1", "10", None, &TransactionParams::new(&secret_pem, "5000000000")).await?; +let bal = client.balance_of(&owner, "1").await?; +``` + +Details: [docs/cep85/](docs/cep85/) · example: `cargo run -p ceps-client --example cep85_install` + +### CEP-95 - NFT (Odra tip) + +```text +install (odra_cfg_package_hash_key_name) → bind_odra_install + → name / symbol / balance_of / owner_of + → mint | burn | transfer_from | approve* +``` + +```rust +use ceps_client::cep95::InstallArgs; +use ceps_client::{CEP95Client, TransactionParams, Verbosity}; + +let mut client = CEP95Client::new(/* rpc, sse, chain, verbosity */)?; +client + .install( + &InstallArgs::new("MyNft", "MNFT", "cep95_pkg_demo"), + &contract_wasm_bytes, + &TransactionParams::new(&secret_pem, "600000000000"), + ) + .await?; +client.bind_odra_install(&installer_public_key, "cep95_pkg_demo").await?; +client.mint(&owner, "1", None, &TransactionParams::new(&secret_pem, "5000000000")).await?; +let owner_of = client.owner_of("1").await?; +``` + +Details: [docs/cep95/](docs/cep95/) · example: `cargo run -p ceps-client --example cep95_install` + +### CLI + +```bash +cargo run -p ceps-client-cli -- status +cargo run -p ceps-client-cli -- cep18 info +cargo run -p ceps-client-cli -- cep78 balance --contract-hash --account +cargo run -p ceps-client-cli -- cep85 balance --contract-hash --account <…> --id 1 +cargo run -p ceps-client-cli -- cep95 owner-of --contract-hash --token-id 1 +``` + +Mutations are on the library / examples today. Flags: [docs/cli.md](docs/cli.md). + +### Try an install end-to-end + +```bash +make prepare && make build +make wasm-from-ceps # copy demo tip contract WASMs into tests/wasm/ +# NCTL running + SECRET_KEY_USER_1 set to a PEM: +cargo run -p ceps-client --example cep18_install +``` + +More setup: [docs/getting-started.md](docs/getting-started.md). + +## Client JS packs (`ceps-client-wasm`) + +`ceps-client-wasm` is the **Rust CEP client compiled for JavaScript** (the replacement for per-CEP `client-js`). Use it from Node or the browser for the same `CEP18Client` / `CEP78Client` / `CEP85Client` surface. + +It is **not** on-chain contract bytecode (that is `tests/wasm/` / `ceps-contracts-*.tgz`). + +| Target | In-tree (committed) | Typical use | +| ------ | ------------------------------ | -------------------------- | +| Node | `ceps-client-wasm/pkg-nodejs/` | Backend / scripts / Vitest | +| Web | `ceps-client-wasm/pkg/` | Bundled frontends | + +Rebuild locally with `make pack` (or `make nodejs` / `make web`). Details: [docs/wasm-ts.md](docs/wasm-ts.md) · [docs/releases.md](docs/releases.md). + +**Or** fetch a published pack (no local `wasm-pack`): + +```bash +TAG=v1.0.0 # or dev-preview +LABEL=${TAG#v} +curl -fsSL -o ceps-client-wasm-nodejs.tgz \ + "https://github.com/Interchouette-ITC/ceps-rust-ts-client/releases/download/${TAG}/ceps-client-wasm-nodejs-${LABEL}.tgz" +mkdir -p ceps-client-wasm && tar -xzf ceps-client-wasm-nodejs.tgz -C ceps-client-wasm +# -> ceps-client-wasm/pkg-nodejs/ +``` + +```js +import { CEP18Client } from "ceps-client-wasm"; // file:./ceps-client-wasm/pkg-nodejs after unpack + +const client = new CEP18Client( + "http://127.0.0.1:11101", + "http://127.0.0.1:18101/events", + "casper-net-1", + 0, +); +client.setContractHash(contractHash, packageHash); +const name = await client.name(); +const bal = await client.balanceOf("account-hash-…"); +``` + +Install from JS takes contract bytes as `Uint8Array` and returns JSON `{ transactionHash, hasExecutionResult }`. Bound methods today are a subset of the Rust API (see [docs/wasm-ts.md](docs/wasm-ts.md)); full parity is on `ceps-client`. + +## Contract WASMs (demos) + +This client is **not** a contract repo. You pass on-chain `.wasm` into `install`. + +**In-tree:** the last staged demo tip builds ship under `tests/wasm/{cep18,cep78,cep85}/` (same idea as committing `pkg` / `pkg-nodejs`). Use those bytes directly, or refresh with `make wasm-from-ceps`. + +**Or** download the release bundle (no tip checkout / no contract build): + +```bash +TAG=v1.0.0 +LABEL=${TAG#v} +curl -fsSL -o ceps-contracts.tgz \ + "https://github.com/Interchouette-ITC/ceps-rust-ts-client/releases/download/${TAG}/ceps-contracts-${LABEL}.tgz" +mkdir -p tests/wasm && tar -xzf ceps-contracts.tgz -C tests/wasm +``` + +Tips are short-lived entity-era builds for demos/CI, not a claim of "the" upstream CEP tip forever. Sources: + +| CEP | Demo tip repo | Branch | What you get | +| --- | ------------- | ------ | ------------ | +| 18 | [Interchouette-ITC/cep-18](https://github.com/Interchouette-ITC/cep-18) | `ceps-client-test` | Fungible contract WASM | +| 78 | [Interchouette-ITC/cep-78-enhanced-nft](https://github.com/Interchouette-ITC/cep-78-enhanced-nft) | `ceps-client-test` | NFT + session WASMs | +| 85 | [Interchouette-ITC/cep-85](https://github.com/Interchouette-ITC/cep-85) | `ceps-client-test` | Multi-token WASM | + +```bash +# after checking out those tips and building contracts there: +make wasm-from-ceps # → tests/wasm/{cep18,cep78,cep85}/ +``` + +Override checkout roots with `CEP18_PRODUCT` / `CEP78_PRODUCT` / `CEP85_PRODUCT`. Pins and SHAs: [docs/contributing.md](docs/contributing.md). All release downloads: [docs/releases.md](docs/releases.md). + +## Documentation + +| Doc | Description | +| ------------------------------------------------------------------------------------------------------------ | ----------------------------------- | +| [Getting started](docs/getting-started.md) | Build, NCTL defaults, first run | +| [Architecture](docs/architecture.md) | How lib / CLI / WASM sit on the SDK | +| [docs/cep18/](docs/cep18/) · [cep78/](docs/cep78/) · [cep85/](docs/cep85/) | Per-CEP guides | +| [CLI](docs/cli.md) · [WASM / TS](docs/wasm-ts.md) | Surfaces | +| [Testing](docs/testing.md) · [CI / CD](docs/ci.md) · [Releases](docs/releases.md) · [Docker](docs/docker.md) | Verify and ship | +| [Contributing](docs/contributing.md) · [SDK](docs/sdk.md) | Tips, pins, upgrades | +| [SECURITY.md](docs/SECURITY.md) | Keys and reporting | +| `make doc` | rustdoc → `docs/api-rust/` | + +## Docker + +```bash +make release-cli-bin && make docker-build IMAGE_TAG=local +docker run --rm ceps-rust-ts-client:local --help +docker pull interchouette/ceps-rust-ts-client:dev +``` + +See [docs/docker.md](docs/docker.md). + +## License / security + +GPL-3.0. See [LICENSE](LICENSE) and [docs/SECURITY.md](docs/SECURITY.md). diff --git a/ceps-client-wasm-schema/pkg/ceps_client_wasm.d.ts b/ceps-client-wasm-schema/pkg/ceps_client_wasm.d.ts new file mode 100644 index 0000000..4d3a342 --- /dev/null +++ b/ceps-client-wasm-schema/pkg/ceps_client_wasm.d.ts @@ -0,0 +1,74 @@ +/* tslint:disable */ +/* eslint-disable */ + +/** + * CEP-18 schema JSON. + */ +export function cep18SchemaJson(): string; + +/** + * CEP-78 schema JSON. + */ +export function cep78SchemaJson(): string; + +/** + * CEP-85 schema JSON. + */ +export function cep85SchemaJson(): string; + +/** + * CEP-95 schema JSON. + */ +export function cep95SchemaJson(): string; + +/** + * JSON schema for a CEP id (`cep18`, `cep78`, `cep85`, `cep95`). + */ +export function schemaJson(cep: string): string; + +/** + * Supported CEP id strings. + */ +export function supportedCeps(): any; + +export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; + +export interface InitOutput { + readonly memory: WebAssembly.Memory; + readonly cep18SchemaJson: () => [number, number]; + readonly cep78SchemaJson: () => [number, number]; + readonly cep85SchemaJson: () => [number, number]; + readonly cep95SchemaJson: () => [number, number]; + readonly schemaJson: (a: number, b: number) => [number, number, number, number]; + readonly supportedCeps: () => [number, number, number]; + readonly __wbindgen_exn_store: (a: number) => void; + readonly __externref_table_alloc: () => number; + readonly __wbindgen_externrefs: WebAssembly.Table; + readonly __wbindgen_free: (a: number, b: number, c: number) => void; + readonly __wbindgen_malloc: (a: number, b: number) => number; + readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; + readonly __externref_table_dealloc: (a: number) => void; + readonly __wbindgen_start: () => void; +} + +export type SyncInitInput = BufferSource | WebAssembly.Module; + +/** + * Instantiates the given `module`, which can either be bytes or + * a precompiled `WebAssembly.Module`. + * + * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated. + * + * @returns {InitOutput} + */ +export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput; + +/** + * If `module_or_path` is {RequestInfo} or {URL}, makes a request and + * for everything else, calls `WebAssembly.instantiate` directly. + * + * @param {{ module_or_path: InitInput | Promise }} module_or_path - Passing `InitInput` directly is deprecated. + * + * @returns {Promise} + */ +export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise } | InitInput | Promise): Promise; diff --git a/ceps-client-wasm-schema/pkg/ceps_client_wasm.js b/ceps-client-wasm-schema/pkg/ceps_client_wasm.js new file mode 100644 index 0000000..3b01b37 --- /dev/null +++ b/ceps-client-wasm-schema/pkg/ceps_client_wasm.js @@ -0,0 +1,333 @@ +/* @ts-self-types="./ceps_client_wasm.d.ts" */ + +/** + * CEP-18 schema JSON. + * @returns {string} + */ +export function cep18SchemaJson() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.cep18SchemaJson(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * CEP-78 schema JSON. + * @returns {string} + */ +export function cep78SchemaJson() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.cep78SchemaJson(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * CEP-85 schema JSON. + * @returns {string} + */ +export function cep85SchemaJson() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.cep85SchemaJson(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * CEP-95 schema JSON. + * @returns {string} + */ +export function cep95SchemaJson() { + let deferred1_0; + let deferred1_1; + try { + const ret = wasm.cep95SchemaJson(); + deferred1_0 = ret[0]; + deferred1_1 = ret[1]; + return getStringFromWasm0(ret[0], ret[1]); + } finally { + wasm.__wbindgen_free(deferred1_0, deferred1_1, 1); + } +} + +/** + * JSON schema for a CEP id (`cep18`, `cep78`, `cep85`, `cep95`). + * @param {string} cep + * @returns {string} + */ +export function schemaJson(cep) { + let deferred3_0; + let deferred3_1; + try { + const ptr0 = passStringToWasm0(cep, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + const ret = wasm.schemaJson(ptr0, len0); + var ptr2 = ret[0]; + var len2 = ret[1]; + if (ret[3]) { + ptr2 = 0; len2 = 0; + throw takeFromExternrefTable0(ret[2]); + } + deferred3_0 = ptr2; + deferred3_1 = len2; + return getStringFromWasm0(ptr2, len2); + } finally { + wasm.__wbindgen_free(deferred3_0, deferred3_1, 1); + } +} + +/** + * Supported CEP id strings. + * @returns {any} + */ +export function supportedCeps() { + const ret = wasm.supportedCeps(); + if (ret[2]) { + throw takeFromExternrefTable0(ret[1]); + } + return takeFromExternrefTable0(ret[0]); +} +function __wbg_get_imports() { + const import0 = { + __proto__: null, + __wbg___wbindgen_throw_bb96b2010945f0bc: function(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }, + __wbg_parse_1cc93481b0865939: function() { return handleError(function (arg0, arg1) { + const ret = JSON.parse(getStringFromWasm0(arg0, arg1)); + return ret; + }, arguments); }, + __wbindgen_cast_0000000000000001: function(arg0, arg1) { + // Cast intrinsic for `Ref(String) -> Externref`. + const ret = getStringFromWasm0(arg0, arg1); + return ret; + }, + __wbindgen_init_externref_table: function() { + const table = wasm.__wbindgen_externrefs; + const offset = table.grow(4); + table.set(0, undefined); + table.set(offset + 0, undefined); + table.set(offset + 1, null); + table.set(offset + 2, true); + table.set(offset + 3, false); + }, + }; + return { + __proto__: null, + "./ceps_client_wasm_bg.js": import0, + }; +} + +function addToExternrefTable0(obj) { + const idx = wasm.__externref_table_alloc(); + wasm.__wbindgen_externrefs.set(idx, obj); + return idx; +} + +function getStringFromWasm0(ptr, len) { + return decodeText(ptr >>> 0, len); +} + +let cachedUint8ArrayMemory0 = null; +function getUint8ArrayMemory0() { + if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) { + cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8ArrayMemory0; +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + const idx = addToExternrefTable0(e); + wasm.__wbindgen_exn_store(idx); + } +} + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8ArrayMemory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len); + const ret = cachedTextEncoder.encodeInto(arg, view); + + offset += ret.written; + ptr = realloc(ptr, len, offset, 1) >>> 0; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function takeFromExternrefTable0(idx) { + const value = wasm.__wbindgen_externrefs.get(idx); + wasm.__externref_table_dealloc(idx); + return value; +} + +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); +cachedTextDecoder.decode(); +const MAX_SAFARI_DECODE_BYTES = 2146435072; +let numBytesDecoded = 0; +function decodeText(ptr, len) { + numBytesDecoded += len; + if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) { + cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); + cachedTextDecoder.decode(); + numBytesDecoded = len; + } + return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len)); +} + +const cachedTextEncoder = new TextEncoder(); + +if (!('encodeInto' in cachedTextEncoder)) { + cachedTextEncoder.encodeInto = function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; + }; +} + +let WASM_VECTOR_LEN = 0; + +let wasmModule, wasmInstance, wasm; +function __wbg_finalize_init(instance, module) { + wasmInstance = instance; + wasm = instance.exports; + wasmModule = module; + cachedUint8ArrayMemory0 = null; + wasm.__wbindgen_start(); + return wasm; +} + +async function __wbg_load(module, imports) { + if (typeof Response === 'function' && module instanceof Response) { + if (!module.ok) { + throw new Error(`failed to fetch Wasm: ${module.status} ${module.statusText} fetching '${module.url}'`); + } + + if (typeof WebAssembly.instantiateStreaming === 'function') { + try { + return await WebAssembly.instantiateStreaming(module, imports); + } catch (e) { + const validResponse = expectedResponseType(module.type); + + if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') { + console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e); + + } else { throw e; } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + } else { + return instance; + } + } + + function expectedResponseType(type) { + switch (type) { + case 'basic': case 'cors': case 'default': return true; + } + return false; + } +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + + if (module !== undefined) { + if (Object.getPrototypeOf(module) === Object.prototype) { + ({module} = module) + } else { + console.warn('using deprecated parameters for `initSync()`; pass a single object instead') + } + } + + const imports = __wbg_get_imports(); + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + const instance = new WebAssembly.Instance(module, imports); + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(module_or_path) { + if (wasm !== undefined) return wasm; + + + if (module_or_path !== undefined) { + if (Object.getPrototypeOf(module_or_path) === Object.prototype) { + ({module_or_path} = module_or_path) + } else { + console.warn('using deprecated parameters for the initialization function; pass a single object instead') + } + } + + if (module_or_path === undefined) { + module_or_path = new URL('ceps_client_wasm_bg.wasm', import.meta.url); + } + const imports = __wbg_get_imports(); + + if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { + module_or_path = fetch(module_or_path); + } + + const { instance, module } = await __wbg_load(await module_or_path, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync, __wbg_init as default }; diff --git a/ceps-client-wasm-schema/pkg/ceps_client_wasm_bg.wasm b/ceps-client-wasm-schema/pkg/ceps_client_wasm_bg.wasm new file mode 100644 index 0000000000000000000000000000000000000000..54c5e081832f5f55b0efbed6a484bca8749d3c68 GIT binary patch literal 87089 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 bRUn{Q9UVZP53AgcqI+cUsDQl<@}K?>>EGU3 literal 0 HcmV?d00001 diff --git a/ceps-client-wasm-schema/pkg/ceps_client_wasm_bg.wasm.d.ts b/ceps-client-wasm-schema/pkg/ceps_client_wasm_bg.wasm.d.ts new file mode 100644 index 0000000..4c995f3 --- /dev/null +++ b/ceps-client-wasm-schema/pkg/ceps_client_wasm_bg.wasm.d.ts @@ -0,0 +1,17 @@ +/* tslint:disable */ +/* eslint-disable */ +export const memory: WebAssembly.Memory; +export const cep18SchemaJson: () => [number, number]; +export const cep78SchemaJson: () => [number, number]; +export const cep85SchemaJson: () => [number, number]; +export const cep95SchemaJson: () => [number, number]; +export const schemaJson: (a: number, b: number) => [number, number, number, number]; +export const supportedCeps: () => [number, number, number]; +export const __wbindgen_exn_store: (a: number) => void; +export const __externref_table_alloc: () => number; +export const __wbindgen_externrefs: WebAssembly.Table; +export const __wbindgen_free: (a: number, b: number, c: number) => void; +export const __wbindgen_malloc: (a: number, b: number) => number; +export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; +export const __externref_table_dealloc: (a: number) => void; +export const __wbindgen_start: () => void; diff --git a/ceps-client-wasm-schema/pkg/package.json b/ceps-client-wasm-schema/pkg/package.json new file mode 100644 index 0000000..2ae9d72 --- /dev/null +++ b/ceps-client-wasm-schema/pkg/package.json @@ -0,0 +1,25 @@ +{ + "name": "ceps-client-wasm-schema", + "type": "module", + "collaborators": [ + "3099551+gRoussac@users.noreply.github.com" + ], + "description": "wasm-bindgen CEP client for Node and browsers (same API as ceps-client; replaces per-CEP client-js).", + "version": "1.0.0", + "license": "SEE LICENSE IN ../LICENSE", + "repository": { + "type": "git", + "url": "https://github.com/Interchouette-ITC/ceps-rust-ts-client" + }, + "files": [ + "ceps_client_wasm_bg.wasm", + "ceps_client_wasm.js", + "ceps_client_wasm.d.ts" + ], + "main": "ceps_client_wasm.js", + "homepage": "https://github.com/Interchouette-ITC/ceps-rust-ts-client", + "types": "ceps_client_wasm.d.ts", + "sideEffects": [ + "./snippets/*" + ] +} diff --git a/docker/Dockerfile b/docker/Dockerfile index fc33ecd..a2449f5 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,6 +4,7 @@ FROM --platform=linux/amd64 node:24-alpine AS build WORKDIR /app COPY casper-rust-wasm-sdk ./casper-rust-wasm-sdk +COPY ceps-client-wasm-schema ./ceps-client-wasm-schema COPY wasm ./wasm COPY www/package.json www/package-lock.json www/decorate-angular-cli.js ./www/ WORKDIR /app/www diff --git a/www/apps/frontend/project.json b/www/apps/frontend/project.json index 4e08b40..fe877d4 100644 --- a/www/apps/frontend/project.json +++ b/www/apps/frontend/project.json @@ -31,6 +31,11 @@ "input": "node_modules/casper-rust-wasm-sdk", "glob": "casper_rust_wasm_sdk_bg.wasm", "output": "assets" + }, + { + "input": "node_modules/ceps-client-wasm-schema", + "glob": "ceps_client_wasm_bg.wasm", + "output": "assets" } ], "styles": [ diff --git a/www/libs/api-interfaces/src/index.ts b/www/libs/api-interfaces/src/index.ts index db9b100..2ebd767 100644 --- a/www/libs/api-interfaces/src/index.ts +++ b/www/libs/api-interfaces/src/index.ts @@ -2,3 +2,4 @@ export * from './lib/api-interfaces'; export * from './lib/api-enums'; export * from './lib/cl-type'; export * from './lib/stored-value'; +export * from './lib/session-args'; diff --git a/www/libs/api-interfaces/src/lib/api-interfaces.ts b/www/libs/api-interfaces/src/lib/api-interfaces.ts index 64915d5..dcae1a0 100644 --- a/www/libs/api-interfaces/src/lib/api-interfaces.ts +++ b/www/libs/api-interfaces/src/lib/api-interfaces.ts @@ -3,6 +3,10 @@ import { CLType } from './cl-type'; export type NamedCLTypeArg = { name: string; cl_type: CLType; + /** Prefer this when emitting session_args_json (from chain or ceps schema). */ + session_type?: string | Record; + /** Prefill value from existing Args JSON. */ + value?: unknown; entry_points?: string[]; install?: boolean; entry_point?: boolean; diff --git a/www/libs/api-interfaces/src/lib/session-args.ts b/www/libs/api-interfaces/src/lib/session-args.ts new file mode 100644 index 0000000..28c987f --- /dev/null +++ b/www/libs/api-interfaces/src/lib/session-args.ts @@ -0,0 +1,439 @@ +/** Session args JSON helpers for the Args builder (casper-client session_args_json shape). */ + +import { CLType } from './cl-type'; +import { NamedCLTypeArg } from './api-interfaces'; + +/** One entry in session_args_json / ceps ArgField type field. */ +export type SessionArgType = string | Record; + +/** Parsed runtime arg row. */ +export type SessionArgRow = { + name: string; + type: SessionArgType; + value?: unknown; +}; + +/** ceps-client schema ArgField. */ +export type CepArgField = { + name: string; + type: SessionArgType; + optional?: boolean; +}; + +/** Full CEP schema from schemaJson(). */ +export type CepSchema = { + cep: string; + install: CepArgField[]; + entrypoints: Record; +}; + +/** Parse session_args_json; empty / invalid → []. */ +export function parseSessionArgsJson(json: string | null | undefined): SessionArgRow[] { + if (!json || !String(json).trim()) { + return []; + } + try { + const parsed = JSON.parse(String(json).trim()) as unknown; + if (!Array.isArray(parsed)) { + return []; + } + const rows: SessionArgRow[] = []; + for (const item of parsed) { + if (!item || typeof item !== 'object') { + continue; + } + const name = (item as { name?: unknown }).name; + const type = (item as { type?: unknown }).type; + if (typeof name !== 'string' || name.length === 0 || type === undefined) { + continue; + } + rows.push({ + name, + type: type as SessionArgType, + value: (item as { value?: unknown }).value, + }); + } + return rows; + } catch { + return []; + } +} + +/** Serialize rows to session_args_json. Omits rows with empty name or undefined value. */ +export function serializeSessionArgsJson(rows: SessionArgRow[]): string { + const out = rows + .filter((r) => r.name && r.value !== undefined && r.value !== '') + .map((r) => ({ + name: r.name, + type: r.type, + value: r.value, + })); + return JSON.stringify(out); +} + +/** RPC / Parameter cl_type → session_args_json type field. */ +export function clTypeToSessionType(clType: unknown): SessionArgType { + if (clType === null || clType === undefined) { + return 'Any'; + } + if (typeof clType === 'string') { + return clType; + } + if (typeof clType === 'object') { + const obj = clType as Record; + if ('Option' in obj) { + return { Option: clTypeToSessionType(obj['Option']) }; + } + if ('List' in obj) { + return { List: clTypeToSessionType(obj['List']) }; + } + if ('ByteArray' in obj) { + return { ByteArray: obj['ByteArray'] as number }; + } + if ('Result' in obj) { + const result = obj['Result'] as { ok?: unknown; err?: unknown } | unknown[]; + if (Array.isArray(result) && result.length >= 2) { + return { + Result: { + ok: clTypeToSessionType(result[0]), + err: clTypeToSessionType(result[1]), + }, + }; + } + const r = result as { ok?: unknown; err?: unknown }; + return { + Result: { + ok: clTypeToSessionType(r.ok), + err: clTypeToSessionType(r.err), + }, + }; + } + if ('Map' in obj) { + const map = obj['Map'] as { key?: unknown; value?: unknown } | unknown[]; + if (Array.isArray(map) && map.length >= 2) { + return { + Map: { + key: clTypeToSessionType(map[0]), + value: clTypeToSessionType(map[1]), + }, + }; + } + const m = map as { key?: unknown; value?: unknown }; + return { + Map: { + key: clTypeToSessionType(m.key), + value: clTypeToSessionType(m.value), + }, + }; + } + if ('Tuple1' in obj) { + const t = obj['Tuple1']; + const inner = Array.isArray(t) ? t[0] : t; + return { Tuple1: [clTypeToSessionType(inner)] }; + } + if ('Tuple2' in obj) { + const t = obj['Tuple2'] as unknown[]; + return { + Tuple2: [clTypeToSessionType(t?.[0]), clTypeToSessionType(t?.[1])], + }; + } + if ('Tuple3' in obj) { + const t = obj['Tuple3'] as unknown[]; + return { + Tuple3: [ + clTypeToSessionType(t?.[0]), + clTypeToSessionType(t?.[1]), + clTypeToSessionType(t?.[2]), + ], + }; + } + } + if (clType instanceof CLType || (clType as { toString?: () => string }).toString) { + return sessionTypeFromClTypeLabel(String(clType)); + } + return 'Any'; +} + +/** Best-effort parse of CLType.toString() labels into session type. */ +function sessionTypeFromClTypeLabel(label: string): SessionArgType { + const trimmed = label.trim(); + const option = /^Option\((.*)\)$/.exec(trimmed); + if (option) { + return { Option: sessionTypeFromClTypeLabel(option[1]) }; + } + const list = /^List\((.*)\)$/.exec(trimmed); + if (list) { + return { List: sessionTypeFromClTypeLabel(list[1]) }; + } + const map = /^Map\((.*), (.*)\)$/.exec(trimmed); + if (map) { + return { + Map: { + key: sessionTypeFromClTypeLabel(map[1]), + value: sessionTypeFromClTypeLabel(map[2]), + }, + }; + } + const tuple1 = /^Tuple1\((.*)\)$/.exec(trimmed); + if (tuple1) { + return { Tuple1: [sessionTypeFromClTypeLabel(tuple1[1])] }; + } + const tuple2 = /^Tuple2\((.*), (.*)\)$/.exec(trimmed); + if (tuple2) { + return { + Tuple2: [ + sessionTypeFromClTypeLabel(tuple2[1]), + sessionTypeFromClTypeLabel(tuple2[2]), + ], + }; + } + const tuple3 = /^Tuple3\((.*), (.*), (.*)\)$/.exec(trimmed); + if (tuple3) { + return { + Tuple3: [ + sessionTypeFromClTypeLabel(tuple3[1]), + sessionTypeFromClTypeLabel(tuple3[2]), + sessionTypeFromClTypeLabel(tuple3[3]), + ], + }; + } + const result = /^Result\((.*), (.*)\)$/.exec(trimmed); + if (result) { + return { + Result: { + ok: sessionTypeFromClTypeLabel(result[1]), + err: sessionTypeFromClTypeLabel(result[2]), + }, + }; + } + return trimmed || 'Any'; +} + +/** Build a CLType for the argument select from a session type. */ +export function sessionTypeToClType(type: SessionArgType): CLType { + if (typeof type === 'string') { + switch (type) { + case 'Bool': + return CLType.Bool(); + case 'I32': + return CLType.I32(); + case 'I64': + return CLType.I64(); + case 'U8': + return CLType.U8(); + case 'U32': + return CLType.U32(); + case 'U64': + return CLType.U64(); + case 'U128': + return CLType.U128(); + case 'U256': + return CLType.U256(); + case 'U512': + return CLType.U512(); + case 'Unit': + return CLType.Unit(); + case 'String': + return CLType.String(); + case 'Key': + return CLType.Key(); + case 'URef': + return CLType.URef(); + case 'PublicKey': + return CLType.PublicKey(); + case 'ByteArray': + return CLType.ByteArray(); + case 'Any': + return CLType.Any(); + default: + return CLType.Any(); + } + } + if (type && typeof type === 'object') { + if ('Option' in type) { + return CLType.Option(sessionTypeToClType(type['Option'] as SessionArgType)); + } + if ('List' in type) { + return CLType.List(sessionTypeToClType(type['List'] as SessionArgType)); + } + if ('ByteArray' in type) { + return CLType.ByteArray(); + } + if ('Map' in type) { + const map = type['Map'] as { key: SessionArgType; value: SessionArgType }; + return CLType.Map( + sessionTypeToClType(map.key), + sessionTypeToClType(map.value), + ); + } + if ('Result' in type) { + const result = type['Result'] as { + ok: SessionArgType; + err: SessionArgType; + }; + return CLType.Result( + sessionTypeToClType(result.ok), + sessionTypeToClType(result.err), + ); + } + if ('Tuple1' in type) { + const t = type['Tuple1'] as SessionArgType[]; + return CLType.Tuple1(sessionTypeToClType(t[0])); + } + if ('Tuple2' in type) { + const t = type['Tuple2'] as SessionArgType[]; + return CLType.Tuple2( + sessionTypeToClType(t[0]), + sessionTypeToClType(t[1]), + ); + } + if ('Tuple3' in type) { + const t = type['Tuple3'] as SessionArgType[]; + return CLType.Tuple3( + sessionTypeToClType(t[0]), + sessionTypeToClType(t[1]), + sessionTypeToClType(t[2]), + ); + } + } + return CLType.Any(); +} + +/** On-chain Parameter-like → NamedCLTypeArg for storage / Custom tab. */ +export function parameterToNamedArg(param: { + name?: string; + cl_type?: unknown; +}): NamedCLTypeArg | null { + if (!param?.name || typeof param.name !== 'string') { + return null; + } + const sessionType = clTypeToSessionType(param.cl_type); + return { + name: param.name, + cl_type: sessionTypeToClType(sessionType), + session_type: sessionType, + }; +} + +/** ceps ArgField → NamedCLTypeArg. */ +export function cepFieldToNamedArg(field: CepArgField): NamedCLTypeArg { + return { + name: field.name, + cl_type: sessionTypeToClType(field.type), + session_type: field.type, + optional: !!field.optional, + }; +} + +/** Normalize any schema-like row into NamedCLTypeArg. */ +export function toNamedArg(raw: unknown): NamedCLTypeArg | null { + if (!raw || typeof raw !== 'object') { + return null; + } + const obj = raw as Record; + if (typeof obj['name'] !== 'string' || !obj['name']) { + return null; + } + if ('type' in obj && !('cl_type' in obj)) { + return cepFieldToNamedArg(obj as unknown as CepArgField); + } + if ('cl_type' in obj) { + const cl = obj['cl_type']; + if (cl instanceof CLType) { + return { + name: obj['name'] as string, + cl_type: cl, + session_type: + (obj['session_type'] as SessionArgType | undefined) ?? + clTypeToSessionType(String(cl)), + optional: !!obj['optional'], + value: obj['value'], + }; + } + const sessionType = + (obj['session_type'] as SessionArgType | undefined) ?? + clTypeToSessionType(cl); + return { + name: obj['name'] as string, + cl_type: sessionTypeToClType(sessionType), + session_type: sessionType, + optional: !!obj['optional'], + value: obj['value'], + }; + } + return null; +} + +/** + * Merge schema (names/types) with existing JSON values by name. + * Schema types win; values come from the JSON rows when present. + */ +export function mergeSchemaWithValues( + schema: unknown[], + values: SessionArgRow[], +): NamedCLTypeArg[] { + const byName = new Map(values.map((v) => [v.name, v])); + const rows: NamedCLTypeArg[] = []; + for (const raw of schema) { + const row = toNamedArg(raw); + if (!row) { + continue; + } + const existing = byName.get(row.name); + if (existing && existing.value !== undefined) { + rows.push({ ...row, value: existing.value }); + } else { + rows.push(row); + } + } + return rows; +} + +/** Coerce a form value for session_args_json based on session type. */ +export function coerceSessionValue( + type: SessionArgType, + raw: string, +): unknown { + const trimmed = raw.trim(); + if (trimmed === '') { + return undefined; + } + if (typeof type === 'string') { + switch (type) { + case 'Bool': + return trimmed.toLowerCase() === 'true'; + case 'I32': + case 'I64': + case 'U8': + case 'U32': + case 'U64': { + const n = Number(trimmed); + return Number.isFinite(n) ? n : trimmed; + } + case 'U128': + case 'U256': + case 'U512': + return trimmed; + case 'Unit': + return null; + default: + return trimmed; + } + } + try { + return JSON.parse(trimmed); + } catch { + return trimmed; + } +} + +/** NamedCLTypeArg list → SessionArgRow list for serialize. */ +export function namedArgsToSessionRows(args: NamedCLTypeArg[]): SessionArgRow[] { + return args + .filter((a) => a.name && a.value !== undefined && a.value !== '') + .map((a) => ({ + name: a.name, + type: a.session_type ?? clTypeToSessionType(a.cl_type), + value: a.value, + })); +} diff --git a/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.html b/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.html index 7f984b3..710df43 100644 --- a/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.html +++ b/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.html @@ -42,22 +42,12 @@

Args builder

[active]="active" >
-
- @for (tab of defaultTabs; track tab) { - @if (tab['name'] === active) { - @for (type of tab.types; track trackByFn(i, type); let i = $index) { - @if ( - (!hasWasm && - (!(type.install || type.upgrade) || - type.entry_point)) || - (hasWasm && (type.install || type.upgrade)) - ) { - - } - } - } +
+ @for (row of rows; track trackByFn(i, row); let i = $index) { + }
diff --git a/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.spec.ts b/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.spec.ts index 458e209..ae0018f 100644 --- a/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.spec.ts +++ b/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.spec.ts @@ -3,22 +3,66 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArgBuilderComponent } from './arg-builder.component'; import { DeployerService } from '@casper-data/data-access-deployer'; import { StorageService } from '@casper-util/storage'; +import { CepSchemaService, seedCepSchemaCache } from './cep-schema.service'; +import { of } from 'rxjs'; +import { Tabs } from '@casper-ui/tabs'; describe('ArgBuilderComponent', () => { let component: ArgBuilderComponent; let fixture: ComponentFixture; - - const getState = jest.fn().mockReturnValue({ subscribe: jest.fn() }); + let storage: { get: jest.Mock; setState: jest.Mock }; beforeEach(async () => { + storage = { + get: jest.fn((key: string) => { + if (key === 'deploy_args') { + return JSON.stringify([ + { name: 'name', type: 'String', value: 'TOKEN' }, + { name: 'symbol', type: 'String', value: 'TKN' }, + ]); + } + if (key === 'args') { + return []; + } + if (key === 'entry_point') { + return ''; + } + return undefined; + }), + setState: jest.fn(), + }; + + const cepSchema = new CepSchemaService(); + seedCepSchemaCache(cepSchema, [ + { + cep: 'cep18', + install: [ + { name: 'name', type: 'String', optional: false }, + { name: 'symbol', type: 'String', optional: false }, + { name: 'decimals', type: 'U8', optional: false }, + { name: 'total_supply', type: 'U256', optional: false }, + ], + entrypoints: { + transfer: [ + { name: 'recipient', type: 'Key', optional: false }, + { name: 'amount', type: 'U256', optional: false }, + ], + }, + }, + ]); + await TestBed.configureTestingModule({ imports: [ArgBuilderComponent], providers: [ - { provide: DeployerService, useValue: { getState } }, { - provide: StorageService, - useValue: { get: jest.fn(), set: jest.fn(), setState: jest.fn() }, + provide: DeployerService, + useValue: { + getState: () => of({ has_wasm: true }), + setState: jest.fn(), + }, }, + { provide: StorageService, useValue: storage }, + { provide: CepSchemaService, useValue: cepSchema }, ], }).compileComponents(); @@ -30,4 +74,31 @@ describe('ArgBuilderComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('prefills install rows from deploy_args when wasm and CEP tab', async () => { + component.hasWasm = true; + component.active = Tabs['CEP-18']; + component.isOpen = true; + await Promise.resolve(); + await Promise.resolve(); + expect(component.rows.length).toBeGreaterThanOrEqual(2); + const nameRow = component.rows.find((r) => r.name === 'name'); + expect(nameRow?.value).toBe('TOKEN'); + }); + + it('build emits session_args_json and stores deploy_args', async () => { + component.hasWasm = true; + component.active = Tabs['CEP-18']; + component.isOpen = true; + await Promise.resolve(); + await Promise.resolve(); + const emitted: string[] = []; + component.argumentChanged.subscribe((v) => emitted.push(v)); + component.rows = component.rows.map((r) => + r.name === 'decimals' ? { ...r, value: '9' } : r, + ); + component.build(); + expect(emitted[0]).toContain('"name":"name"'); + expect(storage.setState).toHaveBeenCalled(); + }); }); diff --git a/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.ts b/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.ts index 8fce674..4f3d973 100644 --- a/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.ts +++ b/www/libs/feature/deployer/src/lib/arg-builder/arg-builder.component.ts @@ -1,50 +1,30 @@ import { - AfterViewInit, ChangeDetectionStrategy, + ChangeDetectorRef, Component, - ElementRef, EventEmitter, Input, OnDestroy, Output, - ViewChild, } from '@angular/core'; import { Tabs, TabsComponent } from '@casper-ui/tabs'; import { ArgumentComponent } from '@casper-ui/argument'; -import { customArg, defaultTabs } from './tabs'; +import { defaultTabs } from './tabs'; import { StorageService } from '@casper-util/storage'; -import { NamedCLTypeArg, State, CLType } from '@casper-api/api-interfaces'; +import { + NamedCLTypeArg, + State, + CLType, + parseSessionArgsJson, + mergeSchemaWithValues, + serializeSessionArgsJson, + coerceSessionValue, + SessionArgType, +} from '@casper-api/api-interfaces'; import { DeployerService } from '@casper-data/data-access-deployer'; import { Subscription } from 'rxjs'; - -interface ArgumentEntry { - name: string; - type: string | TypeObject; - value: T; -} - -interface TypeObject { - Option?: string; - List?: string | TypeObject; - Tuple1?: string[]; - Tuple2?: string[]; - Tuple3?: string[]; - Map?: { key: string; value: string }; - ByteArray?: number; - Result?: { ok: string; err: string }; -} - -const sortByName = (a: NamedCLTypeArg, b: NamedCLTypeArg) => { - const typeA = a['name'].toString().toUpperCase(); - const typeB = b['name'].toString().toUpperCase(); - if (typeA < typeB) { - return -1; - } else if (typeA > typeB) { - return 1; - } - return 0; -}; +import { CepSchemaService, tabToCepId } from './cep-schema.service'; @Component({ selector: 'casper-deployer-arg-builder', @@ -54,10 +34,12 @@ const sortByName = (a: NamedCLTypeArg, b: NamedCLTypeArg) => { styleUrls: ['./arg-builder.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class ArgBuilderComponent implements AfterViewInit, OnDestroy { +export class ArgBuilderComponent implements OnDestroy { @Input() set isOpen(value: boolean) { this._isOpen = value; - this.addArgs(); + if (value) { + void this.hydrate(); + } } get isOpen(): boolean { @@ -66,185 +48,148 @@ export class ArgBuilderComponent implements AfterViewInit, OnDestroy { @Output() closeModal: EventEmitter = new EventEmitter(); @Output() argumentChanged: EventEmitter = new EventEmitter(); - @ViewChild('form') formElt!: ElementRef; + Tabs = Tabs; default = Tabs.Custom; active: Tabs = this.default; - defaultTabs = defaultTabs.map((tab) => ({ - name: tab.name, - types: tab.types.slice().sort(sortByName), - })); - argument = ''; - hasWasm!: boolean; + tabDefs = defaultTabs; + /** Rows shown for the active tab. */ + rows: NamedCLTypeArg[] = []; + hasWasm = false; + entryPoint = ''; private _isOpen = false; - private getStateSubscription!: Subscription; + private getStateSubscription: Subscription; constructor( private readonly storageService: StorageService, private readonly deployerService: DeployerService, - ) {} - - ngAfterViewInit(): void { + private readonly cepSchemaService: CepSchemaService, + private readonly changeDetectorRef: ChangeDetectorRef, + ) { this.getStateSubscription = this.deployerService .getState() .subscribe((state: State) => { if (undefined !== state.has_wasm) { this.hasWasm = !!state.has_wasm; } + if (state.entry_point !== undefined) { + this.entryPoint = state.entry_point || ''; + } + if (this._isOpen) { + void this.hydrate(); + } }); } ngOnDestroy(): void { - this.getStateSubscription && this.getStateSubscription.unsubscribe(); + this.getStateSubscription?.unsubscribe(); } activateContent(tabIndex: Tabs) { this.active = tabIndex; + void this.hydrate(); } add() { - this.defaultTabs[this.active].types.push(customArg); + this.rows = [ + ...this.rows, + { name: '', cl_type: CLType.U8(), session_type: 'U8', value: '' }, + ]; + this.changeDetectorRef.markForCheck(); } trackByFn = (index: number, item: NamedCLTypeArg): string => - item['name'].toString(); + `${item.name}-${index}`; - build() { - this.argument = '['; - const collection = this.formElt.nativeElement.children as HTMLCollection; - - Array.from(collection).forEach((collection: Element) => { - const children = Array.from(collection.children); - - if (!(children[2] as HTMLInputElement).value) { - return; - } - const entry: ArgumentEntry = { - name: this.parseName( - children[2] as HTMLInputElement, - (children[1] as HTMLInputElement).value, - (children[0] as HTMLInputElement).value, - ), - type: this.parseType( - children[2] as HTMLInputElement, - (children[1] as HTMLInputElement).value, - ), - value: this.parseValue( - children[2] as HTMLInputElement, - (children[1] as HTMLInputElement).value, - ), - }; - - this.argument += JSON.stringify(entry) + ','; - }); - - this.argument = this.argument.slice(0, -1); // Remove trailing comma - this.argument += ']'; - - this.argument && this.argumentChanged.emit(this.argument); + onRowChange(index: number, row: NamedCLTypeArg) { + const next = [...this.rows]; + next[index] = row; + this.rows = next; } - private parseName( - input: HTMLInputElement, - type: string, - name: string, - ): string { - const inputValue = input.value.trim(); - switch (type) { - case CLType.Option(CLType.Any()).toString(): - case CLType.List(CLType.Any()).toString(): - case CLType.ByteArray().toString(): - case CLType.Result(CLType.Any(), CLType.Any()).toString(): - case CLType.Map(CLType.Any(), CLType.Any()).toString(): - case CLType.Tuple1(CLType.Any()).toString(): - case CLType.Tuple2(CLType.Any(), CLType.Any()).toString(): - case CLType.Tuple3(CLType.Any(), CLType.Any(), CLType.Any()).toString(): { - const parsedInput = JSON.parse(inputValue); - return parsedInput.name || name; - } - default: - return name; + build() { + const sessionRows = this.rows + .map((row) => { + const type: SessionArgType = + row.session_type ?? String(row.cl_type ?? 'Any'); + const raw = + row.value === undefined || row.value === null + ? '' + : typeof row.value === 'string' + ? row.value + : JSON.stringify(row.value); + const value = coerceSessionValue(type, raw); + return { + name: row.name?.replace(/\s*\*$/, '').trim() || '', + type, + value, + }; + }) + .filter((r) => r.name && r.value !== undefined); + + const json = serializeSessionArgsJson(sessionRows); + if (json && json !== '[]') { + this.storageService.setState({ deploy_args: json }); + this.argumentChanged.emit(json); } } - private parseType(input: HTMLInputElement, type: string) { - const inputValue = input.value.trim(); - switch (type) { - case CLType.Option(CLType.Any()).toString(): { - const parsedInput = JSON.parse(inputValue); - return { Option: parsedInput.type.Option }; - } - case CLType.List(CLType.Any()).toString(): { - const parsedInput = JSON.parse(inputValue); - return { List: parsedInput.type.List }; - } - case CLType.ByteArray().toString(): { - const parsedInput = JSON.parse(inputValue); - return { ByteArray: parsedInput.type.ByteArray }; - } - case CLType.Result(CLType.Any(), CLType.Any()).toString(): { - const parsedInput = JSON.parse(inputValue); - return { Result: parsedInput.type.Result }; - } - case CLType.Map(CLType.Any(), CLType.Any()).toString(): { - const parsedInput = JSON.parse(inputValue); - return { Result: parsedInput.type.Map }; - } - case CLType.Tuple1(CLType.Any()).toString(): { - const parsedInput = JSON.parse(inputValue); - return { Tuple1: parsedInput.type.Tuple1 }; - } - case CLType.Tuple2(CLType.Any(), CLType.Any()).toString(): { - const parsedInput = JSON.parse(inputValue); - return { Tuple2: parsedInput.type.Tuple2 }; - } - case CLType.Tuple3(CLType.Any(), CLType.Any(), CLType.Any()).toString(): { - const parsedInput = JSON.parse(inputValue); - return { Tuple3: parsedInput.type.Tuple3 }; + private async hydrate(): Promise { + await this.cepSchemaService.ensureReady().catch(() => undefined); + + const deployArgs = parseSessionArgsJson( + this.storageService.get('deploy_args') || '', + ); + const storedArgs = (this.storageService.get('args') || []) as unknown[]; + const entryPoint = + this.entryPoint || this.storageService.get('entry_point') || ''; + this.entryPoint = entryPoint; + this.hasWasm = !!( + this.hasWasm || this.storageService.get('has_wasm') + ); + + const tab = this.tabDefs.find((t) => t.name === this.active); + const cepId = tab?.cepId ?? tabToCepId(Tabs[this.active] || ''); + + let schemaRows: NamedCLTypeArg[] = []; + + if (Array.isArray(storedArgs) && storedArgs.length > 0) { + // On-chain entrypoint schema wins on Custom (and when present). + if (this.active === Tabs.Custom || !cepId) { + schemaRows = mergeSchemaWithValues(storedArgs, deployArgs); + if (this.active !== Tabs.Custom && schemaRows.length) { + this.active = Tabs.Custom; + } } - default: - return type; } - } - private parseValue(input: HTMLInputElement, type: string): T { - const inputValue = input.value.trim(); - - switch (type) { - case CLType.Bool().toString(): - return (inputValue.toLowerCase() === 'true') as unknown as T; - case CLType.I32().toString(): - case CLType.I64().toString(): - case CLType.U8().toString(): - case CLType.U32().toString(): - case CLType.U64().toString(): - case CLType.U128().toString(): - case CLType.U256().toString(): - case CLType.U512().toString(): - return Number(inputValue) as unknown as T; - case CLType.Unit().toString(): - return null as unknown as T; - case CLType.String().toString(): - return inputValue as unknown as T; - case CLType.Key().toString(): - case CLType.URef().toString(): - case CLType.PublicKey().toString(): - case CLType.Any().toString(): - return inputValue as unknown as T; - default: { - const test = JSON.parse(inputValue); - return test.value as unknown as T; + if (!schemaRows.length && cepId) { + if (this.hasWasm && !entryPoint) { + schemaRows = mergeSchemaWithValues( + this.cepSchemaService.installArgs(cepId), + deployArgs, + ); + } else if (entryPoint) { + schemaRows = mergeSchemaWithValues( + this.cepSchemaService.entrypointArgs(cepId, entryPoint), + deployArgs, + ); } } - } - private addArgs() { - const args: [] = this.storageService.get('args'); - this.defaultTabs[0].types = []; - args && - args.forEach((arg) => { - this.defaultTabs[0].types.push(arg); - }); + if (!schemaRows.length && deployArgs.length) { + schemaRows = mergeSchemaWithValues( + deployArgs.map((r) => ({ + name: r.name, + type: r.type, + optional: false, + })), + deployArgs, + ); + } + + this.rows = schemaRows; + this.changeDetectorRef.markForCheck(); } } diff --git a/www/libs/feature/deployer/src/lib/arg-builder/cep-schema.service.ts b/www/libs/feature/deployer/src/lib/arg-builder/cep-schema.service.ts new file mode 100644 index 0000000..f28380e --- /dev/null +++ b/www/libs/feature/deployer/src/lib/arg-builder/cep-schema.service.ts @@ -0,0 +1,100 @@ +import { Injectable } from '@angular/core'; +import { + CepArgField, + CepSchema, + cepFieldToNamedArg, + NamedCLTypeArg, +} from '@casper-api/api-interfaces'; +import init, { schemaJson, supportedCeps } from 'ceps-client-wasm-schema'; + +const CEP_IDS = ['cep18', 'cep78', 'cep85', 'cep95'] as const; +export type CepId = (typeof CEP_IDS)[number]; + +/** Loads ceps schema-only wasm and caches CEP-18/78/85/95 arg schemas. */ +@Injectable({ providedIn: 'root' }) +export class CepSchemaService { + private readonly cache = new Map(); + private initPromise: Promise | null = null; + private ready = false; + + /** Ensure wasm is initialized and schemas cached. */ + async ensureReady(): Promise { + if (this.ready) { + return; + } + if (!this.initPromise) { + this.initPromise = this.load(); + } + await this.initPromise; + } + + /** Synchronous get after ensureReady (or empty if not loaded). */ + getSchema(cep: string): CepSchema | undefined { + return this.cache.get(normalizeCepId(cep)); + } + + installArgs(cep: string): NamedCLTypeArg[] { + const schema = this.getSchema(cep); + return (schema?.install ?? []).map((f) => cepFieldToNamedArg(f)); + } + + entrypointArgs(cep: string, entryPoint: string): NamedCLTypeArg[] { + const schema = this.getSchema(cep); + const fields: CepArgField[] = schema?.entrypoints?.[entryPoint] ?? []; + return fields.map((f) => cepFieldToNamedArg(f)); + } + + supported(): string[] { + return [...this.cache.keys()]; + } + + private async load(): Promise { + await init({ + module_or_path: 'assets/ceps_client_wasm_bg.wasm', + }); + const ids = (supportedCeps() as string[]) || [...CEP_IDS]; + for (const id of ids) { + const raw = schemaJson(id); + const parsed = JSON.parse(raw) as CepSchema; + this.cache.set(normalizeCepId(id), parsed); + } + this.ready = true; + } +} + +/** Map UI tab label to ceps id. */ +export function tabToCepId(tabLabel: string): CepId | null { + const key = tabLabel.replace('-', '').toLowerCase(); + if (key === 'cep18' || key === '18') { + return 'cep18'; + } + if (key === 'cep78' || key === '78') { + return 'cep78'; + } + if (key === 'cep85' || key === '85') { + return 'cep85'; + } + if (key === 'cep95' || key === '95') { + return 'cep95'; + } + return null; +} + +function normalizeCepId(cep: string): string { + return cep.trim().toLowerCase(); +} + +/** Seed cache from pre-parsed schemas (unit tests). */ +export function seedCepSchemaCache( + service: CepSchemaService, + schemas: CepSchema[], +): void { + const anyService = service as unknown as { + cache: Map; + ready: boolean; + }; + for (const schema of schemas) { + anyService.cache.set(normalizeCepId(schema.cep), schema); + } + anyService.ready = true; +} diff --git a/www/libs/feature/deployer/src/lib/arg-builder/session-args.spec.ts b/www/libs/feature/deployer/src/lib/arg-builder/session-args.spec.ts new file mode 100644 index 0000000..80fe097 --- /dev/null +++ b/www/libs/feature/deployer/src/lib/arg-builder/session-args.spec.ts @@ -0,0 +1,75 @@ +import { + parseSessionArgsJson, + serializeSessionArgsJson, + clTypeToSessionType, + mergeSchemaWithValues, + sessionTypeToClType, + parameterToNamedArg, + coerceSessionValue, +} from '@casper-api/api-interfaces'; +import { CLType } from '@casper-api/api-interfaces'; + +describe('session-args helpers', () => { + it('parses and serializes session_args_json', () => { + const json = + '[{"name":"amount","type":"U256","value":"1000"},{"name":"owner","type":"Key","value":"account-hash-ab"}]'; + const rows = parseSessionArgsJson(json); + expect(rows).toHaveLength(2); + expect(rows[0].name).toBe('amount'); + expect(rows[0].type).toBe('U256'); + expect(serializeSessionArgsJson(rows)).toContain('U256'); + }); + + it('returns [] for invalid json', () => { + expect(parseSessionArgsJson('')).toEqual([]); + expect(parseSessionArgsJson('not-json')).toEqual([]); + expect(parseSessionArgsJson('{}')).toEqual([]); + }); + + it('maps nested List CLTypes', () => { + expect(clTypeToSessionType({ List: 'Key' })).toEqual({ List: 'Key' }); + expect(clTypeToSessionType({ List: { Tuple2: ['String', 'String'] } })).toEqual({ + List: { Tuple2: ['String', 'String'] }, + }); + const cl = sessionTypeToClType({ List: 'U256' }); + expect(cl.toString()).toContain('List'); + }); + + it('merges schema with values by name', () => { + const merged = mergeSchemaWithValues( + [ + { name: 'name', type: 'String', optional: false }, + { name: 'decimals', type: 'U8', optional: false }, + ], + [{ name: 'name', type: 'String', value: 'TOKEN' }], + ); + expect(merged[0].value).toBe('TOKEN'); + expect(merged[0].session_type).toBe('String'); + expect(merged[1].value).toBeUndefined(); + expect(merged[1].cl_type.toString()).toBe(CLType.U8().toString()); + }); + + it('normalizes on-chain parameters', () => { + const arg = parameterToNamedArg({ + name: 'recipient', + cl_type: 'Key', + }); + expect(arg?.name).toBe('recipient'); + expect(arg?.session_type).toBe('Key'); + }); + + it('keeps U256 as string', () => { + expect(coerceSessionValue('U256', '1000000000000000000')).toBe( + '1000000000000000000', + ); + expect(coerceSessionValue('U8', '9')).toBe(9); + expect(coerceSessionValue('Bool', 'true')).toBe(true); + }); + + it('parses Map nested type objects', () => { + const type = clTypeToSessionType({ + Map: { key: 'String', value: 'String' }, + }); + expect(type).toEqual({ Map: { key: 'String', value: 'String' } }); + }); +}); diff --git a/www/libs/feature/deployer/src/lib/arg-builder/tabs.ts b/www/libs/feature/deployer/src/lib/arg-builder/tabs.ts index 1eeda7e..d5737ed 100644 --- a/www/libs/feature/deployer/src/lib/arg-builder/tabs.ts +++ b/www/libs/feature/deployer/src/lib/arg-builder/tabs.ts @@ -1,311 +1,11 @@ -import { NamedCLTypeArg, CLType } from '@casper-api/api-interfaces'; import { Tabs } from '@casper-ui/tabs'; - -const customArg: NamedCLTypeArg = { name: '', cl_type: CLType.U8() }; - -const nameArg: NamedCLTypeArg = { name: 'name', cl_type: CLType?.String() }; - -const symbolArg: NamedCLTypeArg = { name: 'symbol', cl_type: CLType?.String() }; - -const decimalsArg: NamedCLTypeArg = { name: 'decimals', cl_type: CLType.U8() }; - -const ownerArg: NamedCLTypeArg = { name: 'owner', cl_type: CLType?.Key() }; - -const spenderArg: NamedCLTypeArg = { name: 'spender', cl_type: CLType?.Key() }; - -const recipientArg: NamedCLTypeArg = { - name: 'recipient', - cl_type: CLType?.Key(), -}; - -export const defaultTabs: { name: Tabs; types: NamedCLTypeArg[] }[] = [ - { name: Tabs.Custom, types: [] }, - { - name: Tabs['CEP-18'], - types: [ - { ...nameArg, install: true }, - { ...symbolArg, install: true }, - { ...decimalsArg, install: true }, - { name: 'total_supply', cl_type: CLType?.U256(), install: true }, - { - name: 'enable_mint_burn', - cl_type: CLType.U8(), - install: true, - optional: true, - }, - { - name: 'events_mode', - cl_type: CLType.U8(), - install: true, - optional: true, - }, - { - name: 'admin_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - }, - { - name: 'minter_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - }, - { - name: 'none_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - }, - { name: 'address', cl_type: CLType?.Key() }, - { name: 'amount', cl_type: CLType?.U256() }, - ownerArg, - spenderArg, - recipientArg, - ], - }, - { - name: Tabs['CEP-47'], - types: [ - { ...nameArg, install: true }, - { ...symbolArg, install: true }, - { name: 'meta', cl_type: CLType?.String(), install: true }, - ownerArg, - { name: 'token_id', cl_type: CLType?.U256() }, - { - name: 'token_meta', - cl_type: CLType?.Map(CLType?.String(), CLType?.String()), - }, - { name: 'token_ids', cl_type: CLType?.List(CLType?.U256()) }, - { - name: 'token_metas', - cl_type: CLType?.List(CLType?.Map(CLType?.String(), CLType?.String())), - }, - recipientArg, - { name: 'count', cl_type: CLType?.U32() }, - spenderArg, - { name: 'index', cl_type: CLType?.U256() }, - ], - }, - { - name: Tabs['CEP-78'], - types: [ - { - name: 'collection_name', - cl_type: CLType?.String(), - install: true, - upgrade: true, - }, - { name: 'collection_symbol', cl_type: CLType?.String(), install: true }, - { name: 'total_token_supply', cl_type: CLType?.U64(), install: true }, - { - name: 'allow_minting', - cl_type: CLType?.Bool(), - install: true, - optional: true, - }, - { - name: 'minting_mode', - cl_type: CLType.U8(), - install: true, - optional: true, - }, - { name: 'ownership_mode', cl_type: CLType.U8(), install: true }, - { name: 'nft_kind', cl_type: CLType.U8(), install: true }, - { name: 'nft_metadata_kind', cl_type: CLType.U8(), install: true }, - { name: 'metadata_mutability', cl_type: CLType.U8(), install: true }, - { - name: 'holder_mode', - cl_type: CLType.U8(), - install: true, - optional: true, - }, - { - name: 'owner_reverse_lookup_mode', - cl_type: CLType.U8(), - install: true, - optional: true, - }, - { name: 'token_id', cl_type: CLType?.U256() }, - { name: 'token_hash', cl_type: CLType?.Key() }, - { name: 'named_key_convention', cl_type: CLType.U8(), upgrade: true }, - { - name: 'whitelist_mode', - cl_type: CLType.U8(), - optional: true, - install: true, - }, - { - name: 'identifier_mode', - cl_type: CLType.U8(), - install: true, - entry_point: true, - }, - { - name: 'burn_mode', - cl_type: CLType.U8(), - install: true, - optional: true, - }, - { - name: 'operator_burn_mode', - cl_type: CLType.U8(), - install: true, - optional: true, - upgrade: true, - }, - { - name: 'json_schema', - cl_type: CLType?.String(), - install: true, - optional: true, - }, - { name: 'receipt_name', cl_type: CLType?.String() }, - { name: 'token_owner', cl_type: CLType?.Key() }, - { name: 'token_meta_data', cl_type: CLType?.String() }, - { name: 'source_key', cl_type: CLType?.Key() }, - { name: 'target_key', cl_type: CLType?.Key() }, - { name: 'operator', cl_type: CLType?.Key() }, - { name: 'approve_all', cl_type: CLType?.Bool() }, - { name: 'access_key_name', cl_type: CLType?.String(), upgrade: true }, - { name: 'hash_key_name', cl_type: CLType?.String(), upgrade: true }, - { - name: 'acl_white_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - }, - { - name: 'acl_package_mode', - cl_type: CLType?.Bool(), - install: true, - optional: true, - upgrade: true, - }, - { - name: 'package_operator_mode', - cl_type: CLType?.Bool(), - install: true, - optional: true, - upgrade: true, - }, - { - name: 'additional_required_metadata', - cl_type: CLType?.List(CLType?.U8()), - install: true, - optional: true, - }, - { - name: 'optional_metadata', - cl_type: CLType?.List(CLType?.U8()), - install: true, - optional: true, - }, - { - name: 'events_mode', - cl_type: CLType.U8(), - install: true, - optional: true, - upgrade: true, - }, - { - name: 'transfer_filter_contract', - cl_type: CLType?.Key(), - install: true, - optional: true, - }, - ], - }, - { - name: Tabs['CEP-85'], - types: [ - { ...nameArg, install: true, entry_point: true }, - { - name: 'uri', - cl_type: CLType?.String(), - install: true, - entry_point: true, - }, - { - name: 'events_mode', - cl_type: CLType.U8(), - install: true, - optional: true, - upgrade: true, - entry_point: true, - }, - { - name: 'enable_burn', - cl_type: CLType?.Bool(), - install: true, - optional: true, - entry_point: true, - }, - { - name: 'transfer_filter_contract', - cl_type: CLType?.Key(), - install: true, - optional: true, - }, - { - name: 'transfer_filter_method', - cl_type: CLType?.String(), - install: true, - optional: true, - }, - { - name: 'admin_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - entry_point: true, - }, - { - name: 'minter_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - entry_point: true, - }, - { - name: 'burner_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - entry_point: true, - }, - { - name: 'meta_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - entry_point: true, - }, - { - name: 'none_list', - cl_type: CLType?.List(CLType?.Key()), - install: true, - optional: true, - entry_point: true, - }, - { name: 'package_hash', cl_type: CLType?.Key(), upgrade: true }, - recipientArg, - ownerArg, - { name: 'id', cl_type: CLType?.U256() }, - { name: 'amount', cl_type: CLType?.U256() }, - { name: 'ids', cl_type: CLType?.List(CLType?.U256()) }, - { name: 'amounts', cl_type: CLType?.List(CLType?.U256()) }, - { name: 'account', cl_type: CLType?.Key() }, - { name: 'accounts', cl_type: CLType?.List(CLType?.Key()) }, - { name: 'operator', cl_type: CLType?.Key() }, - { name: 'approved', cl_type: CLType?.Bool() }, - { name: 'from', cl_type: CLType?.Key() }, - { name: 'to', cl_type: CLType?.Key() }, - { name: 'data', cl_type: CLType?.ByteArray() }, - { name: 'total_supply', cl_type: CLType?.U256() }, - { name: 'total_supplies', cl_type: CLType?.List(CLType?.U256()) }, - ], - }, +import { CepId } from './cep-schema.service'; + +/** Tab list for the Args builder (CEP-47 removed; CEP-95 added). */ +export const defaultTabs: { name: Tabs; cepId: CepId | null }[] = [ + { name: Tabs.Custom, cepId: null }, + { name: Tabs['CEP-18'], cepId: 'cep18' }, + { name: Tabs['CEP-78'], cepId: 'cep78' }, + { name: Tabs['CEP-85'], cepId: 'cep85' }, + { name: Tabs['CEP-95'], cepId: 'cep95' }, ]; - -export { customArg }; diff --git a/www/libs/feature/deployer/src/lib/put-deploy/put-deploy.component.ts b/www/libs/feature/deployer/src/lib/put-deploy/put-deploy.component.ts index 14e7a54..911252b 100644 --- a/www/libs/feature/deployer/src/lib/put-deploy/put-deploy.component.ts +++ b/www/libs/feature/deployer/src/lib/put-deploy/put-deploy.component.ts @@ -17,6 +17,7 @@ import { NamedCLTypeArg, State, extractEntryPoints, + parameterToNamedArg, } from '@casper-api/api-interfaces'; import { ResultService } from '../result/result.service'; import { Subscription } from 'rxjs'; @@ -546,34 +547,35 @@ export class PutDeployComponent implements AfterViewInit, OnDestroy { (entry_point: EntrypointsType) => entry_point['name'] === entry_point_value, ); - const args = entry_point?.['args']; - args && - this.storageService.setState({ - args: args as unknown as NamedCLTypeArg[], - entry_point: entry_point_value, - }); + const rawArgs = entry_point?.['args']; + const args = Array.isArray(rawArgs) + ? (rawArgs + .map((param: unknown) => parameterToNamedArg(param as { name?: string; cl_type?: unknown })) + .filter(Boolean) as NamedCLTypeArg[]) + : []; + this.storageService.setState({ + args, + entry_point: entry_point_value, + }); + this.deployerService.setState({ + args, + entry_point: entry_point_value, + }); } else { this.storageService.setState({ args: [], entry_point: '' }); + this.deployerService.setState({ args: [], entry_point: '' }); } } inputEntryPointChange($event: Event) { const entry_point_value = ($event.target as HTMLSelectElement).value; - if (!entry_point_value) { - this.storageService.setState({ args: [], entry_point: '' }); - } else { - this.storageService.setState({ entry_point: entry_point_value }); - } + this.updateArgs(entry_point_value || undefined); this.entryPoint = entry_point_value; } selectEntryPointChange($event: Event) { const entry_point_value = ($event.target as HTMLSelectElement).value; - if (!entry_point_value) { - this.storageService.setState({ args: [], entry_point: '' }); - } else { - this.updateArgs(entry_point_value); - } + this.updateArgs(entry_point_value || undefined); this.entryPoint = entry_point_value; } diff --git a/www/libs/ui/argument/src/lib/argument/argument.component.html b/www/libs/ui/argument/src/lib/argument/argument.component.html index 90cc9b7..3e273f4 100644 --- a/www/libs/ui/argument/src/lib/argument/argument.component.html +++ b/www/libs/ui/argument/src/lib/argument/argument.component.html @@ -7,11 +7,12 @@ defaultType['name'] + (defaultType['optional'] || defaultType['upgrade'] ? ' *' : '') " + (input)="onNameInput($event)" />