feat(apps/token): add token definition prototype

This commit is contained in:
Ricardo Guilherme Schmidt
2026-08-19 01:30:24 -03:00
parent 09f3d594a7
commit 3a5c12c377
18 changed files with 29836 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 3.21)
project(TokenUiPlugin LANGUAGES CXX)
find_package(Qt6 6.8 REQUIRED COMPONENTS Core Gui Network Qml Quick QuickControls2)
qt_standard_project_setup(REQUIRES 6.8)
include(CTest)
if(DEFINED ENV{LOGOS_MODULE_BUILDER_ROOT})
include($ENV{LOGOS_MODULE_BUILDER_ROOT}/cmake/LogosModule.cmake)
else()
message(FATAL_ERROR "LogosModule.cmake not found. Set LOGOS_MODULE_BUILDER_ROOT.")
endif()
set(LOGOS_WALLET_SOURCE_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/../shared/wallet"
CACHE PATH "Path to the shared Logos wallet module"
)
set(LOGOS_WALLET_GENERATED_DIR
"${CMAKE_CURRENT_SOURCE_DIR}/generated_code"
CACHE PATH "Path to generated Logos SDK sources"
)
add_subdirectory("${LOGOS_WALLET_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/shared-wallet")
# ui_qml module with a hand-written C++ backend (QtRO .rep view contract +
# generated *SimpleSource/*ViewPluginBase). Mirrors the LEZ wallet UI module.
# The token prototype owns no wallet implementation; it links the repository's
# shared wallet access library and consumes its compiled Logos.Wallet QML module.
logos_module(
NAME token_ui
REP_FILE src/TokenUiBackend.rep
SOURCES
src/TokenUiPluginInterface.h
src/TokenUiPlugin.h
src/TokenUiPlugin.cpp
src/TokenUiBackend.h
src/TokenUiBackend.cpp
FIND_PACKAGES
Qt6Gui
LINK_LIBRARIES
Qt6::Gui
LINK_TARGETS
logos_wallet_access
)
+89
View File
@@ -0,0 +1,89 @@
# Token UI
A QML UI application for the token program.
See the [Logos QML UI App Tutorial](https://github.com/logos-co/logos-tutorial/blob/master/tutorial-qml-ui-app.md) for more information.
> **Status:** interactive UI prototype. The **Create** view models the current
> Token Program's fungible and non-fungible definition settings, and **Inspect**
> renders the supplied July 12, 2026 testnet fixture snapshot. It never creates
> accounts, signs, reads a live chain, or submits a transaction.
## Token-definition prototype
The prototype is deliberately local-only. It lets an operator prepare every
currently supported creation shape and see the resulting stored state before a
production transaction path exists:
- Fungible definitions: raw `u128` supply; fixed (`None`), self, or external
mint authority; metadata omitted or linked.
- Non-fungible definitions: printable supply plus required metadata, with an
initial master holding that controls printing.
- Metadata: `Simple` or `Expanded` standard, URI, and creators string. The
program initializes `primary_sale_date` to `0`; it has no creation input for
decimals, symbol, description, image, royalties, collection, or mutable
metadata.
- Target accounts: definition, first holding/master, and metadata (when used)
are surfaced because each must be fresh and authorized at submission time.
The **Inspect** fixtures are a historical testnet reference, not a live read.
They include fixed, self-authorized, external-authority, revoked-authority, and
metadata-backed fungibles, plus the Glitchlings NFT collection. Display decimals
shown for fungibles are UI inference from the fixture policy; the Token Program
does not store a decimal field.
Metadata-backed definitions and NFT definitions need typed instruction
serialization today because the generic IDL cannot encode the structured
creation arguments. Any production submission implementation must use that
route rather than treating this prototype as a transaction client.
## Wallet / chain integration
This app is a `ui_qml` module with a hand-written C++ backend
(`src/TokenUiBackend.*`, plugin in `src/TokenUiPlugin.*`) that depends on the
core **`logos_execution_zone`** wallet module. The backend delegates wallet
lifecycle and account state to the repository's shared wallet access library
(`apps/shared/wallet`) and exposes an async QtRO surface
(`src/TokenUiBackend.rep`) plus its shared account list model to the QML view.
The navbar consumes the compiled `Logos.Wallet` QML module.
**Onboarding is non-invasive.** The app opens straight to the first screen; the
navbar shows **Connect** (opens a password-only modal) or **Connected** + the
account selector. There is no path picking — the wallet uses LEZ's canonical
home, `~/.lee/wallet/` (override with `LEE_WALLET_HOME_DIR`, the same var LEZ
honors), and its config (`wallet_config.json`) self-initializes.
Account/keystore sharing follows the runtime:
- **Standalone** (`nix run .`): own core-module instance, but the canonical
`~/.lee/wallet` keystore is shared with the LEZ wallet UI and any other LEZ
app on the machine. A previously-created wallet auto-opens on launch.
- **Inside Basecamp**: the core wallet module is a single shared instance, so on
startup the shared wallet provider reuses the already-open wallet, surfacing
**shared** accounts across apps.
## Setup
This project requires Nix with experimental features enabled. If you haven't already, enable them permanently:
```bash
mkdir -p ~/.config/nix && echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
```
## Running the UI
Start the UI with:
```bash
nix run .
```
This builds and runs the application in development mode.
## Updating Dependencies
To update the pinned versions of dependencies in `flake.lock`:
```bash
nix flake update
```
+26869
View File
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
{
description = "Logos Token QML UI create and manage tokens on the LEZ token program";
inputs = {
logos-module-builder.url = "github:logos-co/logos-module-builder";
# Shared C++ wallet access and Logos.Wallet QML sources.
shared_wallet = {
url = "path:../shared/wallet";
flake = false;
};
# Core wallet module (the LEZ wallet FFI Qt plugin). The input name must
# match the metadata.json `dependencies` entry so the builder can resolve
# it as a module dependency. Keep this aligned with the AMM UI module.
logos_execution_zone = {
url = "github:logos-blockchain/logos-execution-zone-module?rev=d70225ced646934d2294fd9e8f8b03615c104b80";
inputs.logos-execution-zone.url =
"github:logos-blockchain/logos-execution-zone?rev=a7e06a660940a00093b1760560d37ff84aff5a05";
};
};
outputs = inputs@{ logos-module-builder, shared_wallet, ... }:
logos-module-builder.lib.mkLogosQmlModule {
src = ./.;
configFile = ./metadata.json;
flakeInputs = inputs;
preConfigure = ''
cmakeFlagsArray+=("-DLOGOS_WALLET_SOURCE_DIR=${shared_wallet}")
'';
externalLibInputs = { };
postInstall = ''
test -f ${./qml}/Logos/Wallet/qmldir
walletQmlDir="shared-wallet/qml/Logos/Wallet"
if [ ! -d "$walletQmlDir" ]; then
echo "Built Logos.Wallet QML module not found"
exit 1
fi
walletQmlInstallDir="$out/lib/Logos/Wallet"
mkdir -p "$walletQmlInstallDir"
cp -r "$walletQmlDir/." "$walletQmlInstallDir/"
test -f "$walletQmlInstallDir/qmldir"
'';
};
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

+25
View File
@@ -0,0 +1,25 @@
{
"name": "token_ui",
"version": "0.1.0",
"type": "ui_qml",
"category": "token",
"description": "UI module for the token program",
"main": "token_ui_plugin",
"view": "qml/Main.qml",
"icon": "icons/token.png",
"dependencies": ["logos_execution_zone"],
"nix": {
"packages": {
"build": [],
"runtime": ["qt6.qtdeclarative", "zstd", "krb5", "abseil-cpp"]
},
"external_libraries": [],
"cmake": {
"find_packages": [],
"extra_sources": [],
"extra_include_dirs": [],
"extra_link_libraries": []
}
}
}
+5
View File
@@ -0,0 +1,5 @@
module Logos.Wallet
optional plugin logos_wallet_qmlplugin ../../../Logos/Wallet
classname Logos_WalletPlugin
prefer :/qt/qml/Logos/Wallet/
depends QtQuick
+103
View File
@@ -0,0 +1,103 @@
import QtQuick
import QtQuick.Layouts
import Logos.Theme
import "pages"
import "state"
Item {
id: root
// Backend replica + account model, bridged from the C++ backend.
readonly property var backend: logos.module("token_ui")
readonly property var accountModel: logos.model("token_ui", "accountModel")
property bool ready: false
// Local-only prototype data. It is seeded from the supplied testnet
// deployment record and never performs a chain read or submits a call.
TokenPrototypeStore {
id: tokenPrototypeStore
}
Connections {
target: logos
function onViewModuleReadyChanged(moduleName, isReady) {
if (moduleName === "token_ui")
root.ready = isReady && root.backend !== null;
}
}
Component.onCompleted: {
root.ready = root.backend !== null && logos.isViewModuleReady("token_ui");
}
// Connectivity banner: shown when a wallet is open but its configured
// sequencer doesn't answer reachability probes (so transactions will fail).
Rectangle {
id: connectionBanner
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
z: 101
readonly property bool show: root.ready && root.backend && root.backend.isWalletOpen && root.backend.sequencerAddr.length > 0 && !root.backend.sequencerReachable
height: show ? 32 : 0
visible: height > 0
clip: true
color: Theme.palette.warning
Behavior on height {
NumberAnimation {
duration: 150
easing.type: Easing.OutCubic
}
}
Text {
anchors.centerIn: parent
width: parent.width - 40
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideMiddle
font.pixelSize: 12
font.weight: Font.Medium
color: Theme.palette.background
text: qsTr("Unable to connect to network")
}
}
// The app is always usable; the wallet is opt-in via the navbar "Connect"
// control. Prototype views render immediately and stay local-only.
NavBar {
id: navbar
anchors.top: connectionBanner.bottom
anchors.left: parent.left
anchors.right: parent.right
z: 100
backend: root.ready ? root.backend : null
accountModel: root.accountModel
}
StackLayout {
anchors.top: navbar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
currentIndex: navbar.currentIndex
CreatePage {
Layout.fillWidth: true
Layout.fillHeight: true
store: tokenPrototypeStore
}
ManagePage {
Layout.fillWidth: true
Layout.fillHeight: true
store: tokenPrototypeStore
}
}
}
+82
View File
@@ -0,0 +1,82 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Logos.Controls
import Logos.Theme
import Logos.Wallet
// Self-contained navigation bar — styling is independent of any view's theme.
Item {
id: root
property alias currentIndex: navigationTabs.currentIndex
// Wallet wiring, passed down from Main.qml.
property var backend: null
property var accountModel: null
implicitHeight: 56
Rectangle {
anchors.fill: parent
color: Theme.palette.background
// Bottom separator
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 1
color: Theme.palette.borderSecondary
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 20
anchors.rightMargin: 20
spacing: 4
// App identity
Text {
text: qsTr("Logos Token")
color: Theme.palette.text
font.pixelSize: 17
font.weight: Font.Bold
}
Item {
Layout.fillWidth: true
}
LogosTabBar {
id: navigationTabs
Layout.preferredWidth: 180
LogosTabButton {
text: qsTr("Create")
Accessible.name: qsTr("Open Create")
}
LogosTabButton {
text: qsTr("Inspect")
Accessible.name: qsTr("Open Inspect")
}
}
// Wallet / account control on the far right.
WalletControl {
id: accountControl
Layout.leftMargin: 12
wallet: root.backend
accountModel: root.accountModel
viewportWidth: root.width
watchCall: function(result, success, failure) {
logos.watch(result, success, failure)
}
}
}
}
}
+990
View File
@@ -0,0 +1,990 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Logos.Controls
import Logos.Theme
Item {
id: root
property var store: null
property int tokenKind: 0
property int authorityMode: 0
property bool metadataEnabled: false
property int metadataStandard: 0
property bool prepared: false
property string preparedMessage: ""
readonly property bool isFungible: tokenKind === 0
readonly property bool requiresMetadata: !isFungible
readonly property bool hasMetadata: requiresMetadata || metadataEnabled
readonly property string maximumU128: "340282366920938463463374607431768211455"
readonly property string instructionName: isFungible && !hasMetadata ? "new_fungible_definition" : "new_definition_with_metadata"
readonly property string supplyLabel: isFungible ? qsTr("Initial raw supply") : qsTr("Printable supply")
readonly property string supplyValue: supplyField.text.length > 0 ? supplyField.text : qsTr("Not set")
readonly property string authorityValue: !isFungible ? qsTr("Not applicable to NFT definitions") : authorityMode === 0 ? qsTr("None — fixed supply") : authorityMode === 1 ? qsTr("Definition account — self authority") : externalAuthorityField.text.length > 0 ? externalAuthorityField.text : qsTr("External authority required")
readonly property bool validSupply: isUnsignedU128(supplyField.text)
readonly property bool validDefinitionTarget: isAccountId(definitionTargetField.text)
readonly property bool validHoldingTarget: isAccountId(holdingTargetField.text)
readonly property bool validMetadataTarget: !hasMetadata || isAccountId(metadataTargetField.text)
readonly property bool validExternalAuthority: !isFungible || authorityMode !== 2 || (isAccountId(externalAuthorityField.text) && externalAuthorityField.text !== "11111111111111111111111111111111")
readonly property bool canPrepare: validSupply && validDefinitionTarget && validHoldingTarget && validMetadataTarget && validExternalAuthority
readonly property var authorityOptions: [
{ text: qsTr("Fixed supply — no future minting"), accessibleName: qsTr("Fixed supply with no mint authority") },
{ text: qsTr("Self authority — definition account signs future mints"), accessibleName: qsTr("Use definition account as mint authority") },
{ text: qsTr("External authority — a separate account signs future mints"), accessibleName: qsTr("Use external mint authority") }
]
readonly property var metadataStandards: [
{ text: qsTr("Simple"), accessibleName: qsTr("Use Simple metadata standard") },
{ text: qsTr("Expanded"), accessibleName: qsTr("Use Expanded metadata standard") }
]
readonly property var examplePatterns: [
{ pattern: "fixed", text: qsTr("Fixed example"), accessibleName: qsTr("Load fixed supply creation example") },
{ pattern: "metadata", text: qsTr("Metadata example"), accessibleName: qsTr("Load metadata token creation example") },
{ pattern: "nft", text: qsTr("NFT example"), accessibleName: qsTr("Load non-fungible collection creation example") }
]
readonly property var readinessChecks: [
{ label: qsTr("Definition target"), detail: qsTr("init · signer · writable"), ok: validDefinitionTarget },
{ label: isFungible ? qsTr("Initial holding") : qsTr("NFT master holding"), detail: qsTr("init · signer · writable"), ok: validHoldingTarget },
{ label: qsTr("Metadata target"), detail: hasMetadata ? qsTr("init · signer · writable") : qsTr("not used by this instruction"), ok: hasMetadata ? validMetadataTarget : true }
].concat(isFungible && authorityMode === 2 ? [
{ label: qsTr("External mint authority"), detail: qsTr("Must be a non-zero account ID"), ok: validExternalAuthority }
] : [])
component FormTextField: LogosTextField {
Layout.fillWidth: true
Layout.preferredHeight: 42
textInput.selectByMouse: true
}
component CompactButton: LogosButton {
Layout.preferredWidth: 132
Layout.preferredHeight: 34
radius: Theme.spacing.radiusMedium
}
function isUnsignedU128(value) {
if (!/^[0-9]+$/.test(value))
return false;
var normalized = value.replace(/^0+(?=\d)/, "");
if (normalized.length < maximumU128.length)
return true;
if (normalized.length > maximumU128.length)
return false;
return normalized <= maximumU128;
}
function isAccountId(value) {
return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(value);
}
function setPattern(pattern) {
prepared = false;
preparedMessage = "";
if (pattern === "fixed") {
tokenKind = 0;
authorityMode = 0;
metadataEnabled = false;
nameField.text = qsTr("Example fixed supply");
supplyField.text = "7654321";
return;
}
if (pattern === "metadata") {
tokenKind = 0;
authorityMode = 2;
metadataEnabled = true;
metadataStandard = 1;
nameField.text = qsTr("Example metadata token");
supplyField.text = "10000000000000000000000000";
metadataUriField.text = "data:application/json;base64,...";
creatorsField.text = qsTr("Creator or authority label");
return;
}
tokenKind = 1;
metadataEnabled = true;
metadataStandard = 1;
nameField.text = qsTr("Example collection");
supplyField.text = "64";
metadataUriField.text = "data:application/json;base64,...";
creatorsField.text = qsTr("Master-holding creator");
}
function prepareDefinition() {
if (!canPrepare)
return;
var draft = {
id: "draft-" + Date.now(),
name: nameField.text.length > 0 ? nameField.text : qsTr("Untitled definition"),
type: isFungible ? "fungible" : "nonFungible",
definitionId: definitionTargetField.text,
holdingId: holdingTargetField.text,
metadataId: hasMetadata ? metadataTargetField.text : "",
rawSupply: supplyField.text,
displaySupply: supplyField.text,
inferredDecimals: "",
authorityMode: isFungible ? (authorityMode === 0 ? "fixed" : authorityMode === 1 ? "self" : "external") : "masterHolding",
authority: isFungible && authorityMode === 2 ? externalAuthorityField.text : authorityMode === 1 ? definitionTargetField.text : "",
metadataStandard: hasMetadata ? (metadataStandard === 0 ? "Simple" : "Expanded") : "",
metadataUri: hasMetadata ? metadataUriField.text : "",
creators: hasMetadata ? creatorsField.text : "",
description: qsTr("Prepared locally. No token account or transaction was created."),
source: "draft",
instruction: instructionName,
printableCopies: !isFungible ? supplyField.text : ""
};
if (store && store.addDraft)
store.addDraft(draft);
prepared = true;
preparedMessage = qsTr("Draft prepared locally. Switch to Inspect to review it alongside the testnet fixtures.");
}
Rectangle {
anchors.fill: parent
color: "#151515"
}
Flickable {
id: scroll
anchors.fill: parent
clip: true
contentWidth: width
contentHeight: content.implicitHeight + 48
flickableDirection: Flickable.VerticalFlick
ColumnLayout {
id: content
width: Math.max(280, Math.min(scroll.width - 40, 1440))
x: Math.max(20, (scroll.width - width) / 2)
y: 24
spacing: 18
RowLayout {
Layout.fillWidth: true
spacing: 18
ColumnLayout {
Layout.fillWidth: true
spacing: 5
Text {
color: "#E7E1D8"
font.pixelSize: 28
font.weight: Font.DemiBold
text: qsTr("Create token definition")
}
Text {
Layout.fillWidth: true
color: "#A9A098"
font.pixelSize: 14
wrapMode: Text.Wrap
text: qsTr("Prepare the exact definition shape first. This prototype does not create accounts, sign, or submit a transaction.")
}
}
LogosBadge {
Layout.alignment: Qt.AlignTop
color: Theme.palette.primary
text: qsTr("Prototype")
}
}
GridLayout {
id: workbench
readonly property int columnCount: content.width >= 1220 ? 3 : content.width >= 820 ? 2 : 1
Layout.fillWidth: true
columns: columnCount
columnSpacing: 14
rowSpacing: 14
Rectangle {
id: formPanel
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.preferredWidth: workbench.columnCount === 3 ? 520 : 0
Layout.columnSpan: workbench.columnCount === 1 ? 1 : 1
implicitHeight: formContent.implicitHeight + 32
radius: 16
color: "#1B1B1B"
border.color: "#303030"
border.width: 1
ColumnLayout {
id: formContent
anchors.fill: parent
anchors.margins: 16
spacing: 14
Text {
color: "#E7E1D8"
font.pixelSize: 18
font.weight: Font.DemiBold
text: qsTr("Definition settings")
}
LogosTabBar {
id: kindTabs
Layout.fillWidth: true
Layout.preferredHeight: 42
currentIndex: root.tokenKind
onCurrentIndexChanged: {
root.tokenKind = currentIndex;
if (root.requiresMetadata)
root.metadataEnabled = true;
root.prepared = false;
}
LogosTabButton {
text: qsTr("Fungible")
Accessible.name: qsTr("Create fungible token definition")
}
LogosTabButton {
text: qsTr("Non-fungible")
Accessible.name: qsTr("Create non-fungible token definition")
}
}
Text {
Layout.fillWidth: true
color: "#A9A098"
font.pixelSize: 13
wrapMode: Text.Wrap
text: root.isFungible ? qsTr("Fungible definitions create one initial holding with the full raw supply.") : qsTr("NFT definitions always include metadata and create a master holding that controls printing.")
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#303030"
}
ColumnLayout {
Layout.fillWidth: true
spacing: 7
Text {
color: "#E7E1D8"
font.pixelSize: 15
font.weight: Font.DemiBold
text: qsTr("Identity and supply")
}
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Name")
}
FormTextField {
id: nameField
Accessible.name: qsTr("Token definition name")
placeholderText: qsTr("Name stored on the definition")
text: qsTr("New token")
onTextChanged: root.prepared = false
}
Text {
color: "#A9A098"
font.pixelSize: 12
text: root.supplyLabel
}
FormTextField {
id: supplyField
Accessible.name: root.supplyLabel
placeholderText: qsTr("Unsigned 128-bit integer")
text: "1000000"
textInput.inputMethodHints: Qt.ImhDigitsOnly
textInput.validator: RegularExpressionValidator {
regularExpression: /^[0-9]*$/
}
onTextChanged: root.prepared = false
}
Text {
Layout.fillWidth: true
color: root.validSupply ? "#A9A098" : "#F08A76"
font.pixelSize: 12
wrapMode: Text.Wrap
text: root.validSupply ? qsTr("Raw u128 value. Decimal display precision is not stored by the Token Program.") : qsTr("Enter an unsigned 128-bit integer (0 through 340282366920938463463374607431768211455).")
}
}
Rectangle {
Layout.fillWidth: true
color: "#303030"
visible: root.isFungible
Layout.preferredHeight: visible ? 1 : 0
}
ColumnLayout {
Layout.fillWidth: true
visible: root.isFungible
Layout.preferredHeight: visible ? implicitHeight : 0
spacing: 7
Text {
color: "#E7E1D8"
font.pixelSize: 15
font.weight: Font.DemiBold
text: qsTr("Mint authority")
}
Text {
Layout.fillWidth: true
color: "#A9A098"
font.pixelSize: 12
wrapMode: Text.Wrap
text: qsTr("This is the full authority surface at creation. NFTs have no definition-level mint authority.")
}
Repeater {
model: root.authorityOptions
delegate: LogosRadioButton {
required property int index
required property var modelData
Layout.fillWidth: true
Accessible.name: modelData.accessibleName
checked: root.authorityMode === index
text: modelData.text
onClicked: {
root.authorityMode = index
root.prepared = false
}
}
}
FormTextField {
id: externalAuthorityField
Layout.preferredHeight: root.authorityMode === 2 ? 42 : 0
visible: root.authorityMode === 2
Accessible.name: qsTr("External mint authority account ID")
placeholderText: qsTr("External authority account ID")
onTextChanged: root.prepared = false
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#303030"
}
ColumnLayout {
Layout.fillWidth: true
spacing: 8
RowLayout {
Layout.fillWidth: true
spacing: 10
ColumnLayout {
Layout.fillWidth: true
spacing: 3
Text {
color: "#E7E1D8"
font.pixelSize: 15
font.weight: Font.DemiBold
text: qsTr("Metadata")
}
Text {
color: "#A9A098"
font.pixelSize: 12
text: root.requiresMetadata ? qsTr("Required for non-fungible definitions") : qsTr("Optional for fungible definitions")
}
}
LogosSwitch {
id: metadataSwitch
Layout.alignment: Qt.AlignVCenter
Accessible.name: qsTr("Include metadata account")
checked: root.hasMetadata
enabled: !root.requiresMetadata
onToggled: {
root.metadataEnabled = checked;
root.prepared = false;
}
}
}
Text {
Layout.fillWidth: true
color: "#A9A098"
font.pixelSize: 12
wrapMode: Text.Wrap
text: qsTr("The program stores only standard, URI, creators, definition link, and a primary-sale date initialized to 0. It does not validate URI content or metadata schema.")
}
ColumnLayout {
Layout.fillWidth: true
visible: root.hasMetadata
Layout.preferredHeight: visible ? implicitHeight : 0
spacing: 7
RowLayout {
Layout.fillWidth: true
spacing: 8
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Metadata standard")
}
Repeater {
model: root.metadataStandards
delegate: LogosRadioButton {
required property int index
required property var modelData
Accessible.name: modelData.accessibleName
checked: root.metadataStandard === index
text: modelData.text
onClicked: {
root.metadataStandard = index
root.prepared = false
}
}
}
Item {
Layout.fillWidth: true
}
}
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("URI")
}
FormTextField {
id: metadataUriField
Accessible.name: qsTr("Metadata URI")
placeholderText: qsTr("data:application/json;base64,... or external URI")
onTextChanged: root.prepared = false
}
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Creators")
}
FormTextField {
id: creatorsField
Accessible.name: qsTr("Metadata creators string")
placeholderText: qsTr("Creator account or attribution string")
onTextChanged: root.prepared = false
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#303030"
}
ColumnLayout {
Layout.fillWidth: true
spacing: 7
Text {
color: "#E7E1D8"
font.pixelSize: 15
font.weight: Font.DemiBold
text: qsTr("New target accounts")
}
Text {
Layout.fillWidth: true
color: "#A9A098"
font.pixelSize: 12
wrapMode: Text.Wrap
text: qsTr("Every target must be a new default-valued account and authorize this transaction. The prototype can validate shape, not on-chain account state.")
}
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Definition target account")
}
FormTextField {
id: definitionTargetField
Accessible.name: qsTr("Definition target account ID")
placeholderText: qsTr("New authorized account ID")
onTextChanged: root.prepared = false
}
Text {
color: "#A9A098"
font.pixelSize: 12
text: root.isFungible ? qsTr("Initial fungible holding target") : qsTr("NFT master holding target")
}
FormTextField {
id: holdingTargetField
Accessible.name: root.isFungible ? qsTr("Initial fungible holding target account ID") : qsTr("NFT master holding target account ID")
placeholderText: qsTr("New authorized account ID")
onTextChanged: root.prepared = false
}
ColumnLayout {
Layout.fillWidth: true
visible: root.hasMetadata
Layout.preferredHeight: visible ? implicitHeight : 0
spacing: 7
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Metadata target account")
}
FormTextField {
id: metadataTargetField
Accessible.name: qsTr("Metadata target account ID")
placeholderText: qsTr("New authorized account ID")
onTextChanged: root.prepared = false
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 8
Repeater {
model: root.examplePatterns
delegate: CompactButton {
required property var modelData
Accessible.name: modelData.accessibleName
text: modelData.text
onClicked: root.setPattern(modelData.pattern)
}
}
Item {
Layout.fillWidth: true
}
}
}
}
Rectangle {
id: previewPanel
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.preferredWidth: workbench.columnCount === 3 ? 410 : 0
Layout.columnSpan: workbench.columnCount === 1 ? 1 : 1
implicitHeight: previewContent.implicitHeight + 32
radius: 16
color: "#1B1B1B"
border.color: "#303030"
border.width: 1
ColumnLayout {
id: previewContent
anchors.fill: parent
anchors.margins: 16
spacing: 13
Text {
color: "#E7E1D8"
font.pixelSize: 18
font.weight: Font.DemiBold
text: qsTr("Definition preview")
}
Rectangle {
Layout.fillWidth: true
implicitHeight: previewHeadline.implicitHeight + 22
radius: 10
color: root.isFungible ? "#211914" : "#181D25"
border.color: root.isFungible ? "#49301F" : "#31435D"
border.width: 1
RowLayout {
id: previewHeadline
anchors.fill: parent
anchors.margins: 11
spacing: 10
Rectangle {
Layout.preferredHeight: 30
Layout.preferredWidth: kindBadge.implicitWidth + 16
radius: 15
color: root.isFungible ? "#2D211A" : "#182534"
border.color: root.isFungible ? "#6A4329" : "#40607A"
border.width: 1
Text {
id: kindBadge
anchors.centerIn: parent
color: root.isFungible ? "#F2D8C7" : "#BFD8F4"
font.pixelSize: 12
font.weight: Font.DemiBold
text: root.isFungible ? qsTr("Fungible") : qsTr("NFT collection")
}
}
Text {
Layout.fillWidth: true
color: "#E7E1D8"
elide: Text.ElideRight
font.pixelSize: 16
font.weight: Font.DemiBold
text: nameField.text.length > 0 ? nameField.text : qsTr("Untitled definition")
}
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 9
RowLayout {
Layout.fillWidth: true
Text {
color: "#A9A098"
font.pixelSize: 12
text: root.isFungible ? qsTr("Stored total supply") : qsTr("Stored printable supply")
}
Item {
Layout.fillWidth: true
}
Text {
color: root.validSupply ? "#E7E1D8" : "#F08A76"
font.pixelSize: 14
font.weight: Font.Medium
text: root.supplyValue
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#303030"
}
RowLayout {
Layout.fillWidth: true
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Creation instruction")
}
Item {
Layout.fillWidth: true
}
Text {
color: "#F2D8C7"
font.family: "monospace"
font.pixelSize: 12
text: root.instructionName
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#303030"
}
ColumnLayout {
Layout.fillWidth: true
visible: root.isFungible
Layout.preferredHeight: visible ? implicitHeight : 0
spacing: 5
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Mint authority")
}
Text {
Layout.fillWidth: true
color: "#E7E1D8"
font.pixelSize: 14
wrapMode: Text.Wrap
text: root.authorityValue
}
}
ColumnLayout {
Layout.fillWidth: true
visible: !root.isFungible
Layout.preferredHeight: visible ? implicitHeight : 0
spacing: 5
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Master holding behavior")
}
Text {
Layout.fillWidth: true
color: "#E7E1D8"
font.pixelSize: 14
wrapMode: Text.Wrap
text: qsTr("Starts with print balance %1. The master remains reserved, so at most %2 printed copies are possible.").arg(root.supplyValue).arg(root.validSupply && supplyField.text !== "0" ? qsTr("one fewer than the printable supply") : qsTr("none until a positive printable supply is set"))
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: root.hasMetadata ? 1 : 0
visible: root.hasMetadata
color: "#303030"
}
ColumnLayout {
Layout.fillWidth: true
visible: root.hasMetadata
Layout.preferredHeight: visible ? implicitHeight : 0
spacing: 6
Text {
color: "#A9A098"
font.pixelSize: 12
text: qsTr("Metadata account")
}
Text {
Layout.fillWidth: true
color: "#E7E1D8"
elide: Text.ElideMiddle
font.family: "monospace"
font.pixelSize: 12
text: metadataTargetField.text.length > 0 ? metadataTargetField.text : qsTr("New metadata target required")
}
Text {
Layout.fillWidth: true
color: "#A9A098"
font.pixelSize: 12
wrapMode: Text.Wrap
text: qsTr("%1 · URI and creators are stored as supplied.").arg(root.metadataStandard === 0 ? qsTr("Simple") : qsTr("Expanded"))
}
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: holdingOutcome.implicitHeight + 24
radius: 10
color: "#101010"
border.color: "#343434"
border.width: 1
ColumnLayout {
id: holdingOutcome
anchors.fill: parent
anchors.margins: 12
spacing: 6
Text {
color: "#A9A098"
font.pixelSize: 12
text: root.isFungible ? qsTr("Initial fungible holding") : qsTr("Initial NFT master holding")
}
Text {
Layout.fillWidth: true
color: "#E7E1D8"
elide: Text.ElideMiddle
font.family: "monospace"
font.pixelSize: 12
text: holdingTargetField.text.length > 0 ? holdingTargetField.text : qsTr("New holding target required")
}
Text {
Layout.fillWidth: true
color: "#E7E1D8"
font.pixelSize: 14
wrapMode: Text.Wrap
text: root.isFungible ? qsTr("Receives the full initial raw supply: %1.").arg(root.supplyValue) : qsTr("Receives the master state and print balance: %1.").arg(root.supplyValue)
}
}
}
LogosButton {
Layout.fillWidth: true
Layout.preferredHeight: 46
Accessible.name: qsTr("Prepare token definition draft")
enabled: root.canPrepare
text: root.canPrepare ? qsTr("Prepare definition") : qsTr("Complete required targets")
onClicked: root.prepareDefinition()
}
Text {
Layout.fillWidth: true
visible: root.prepared
Layout.preferredHeight: visible ? implicitHeight : 0
color: "#78C88D"
font.pixelSize: 12
wrapMode: Text.Wrap
text: root.preparedMessage
}
}
}
Rectangle {
id: readinessPanel
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
Layout.preferredWidth: workbench.columnCount === 3 ? 300 : 0
Layout.columnSpan: workbench.columnCount === 1 ? 1 : workbench.columnCount === 2 ? 2 : 1
implicitHeight: readinessContent.implicitHeight + 32
radius: 16
color: "#181818"
border.color: "#303030"
border.width: 1
ColumnLayout {
id: readinessContent
anchors.fill: parent
anchors.margins: 16
spacing: 12
Text {
color: "#E7E1D8"
font.pixelSize: 18
font.weight: Font.DemiBold
text: qsTr("Protocol readiness")
}
Text {
Layout.fillWidth: true
color: "#A9A098"
font.pixelSize: 13
wrapMode: Text.Wrap
text: qsTr("Creation requires fresh authorized target accounts. Green checks confirm form shape only; account state remains unverified until a real client reads the chain.")
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#303030"
}
Repeater {
model: root.readinessChecks
delegate: RowLayout {
id: readinessRow
required property var modelData
Layout.fillWidth: true
spacing: 9
LogosBadge {
color: readinessRow.modelData.ok ? Theme.palette.success : Theme.palette.error
text: readinessRow.modelData.ok ? qsTr("Ready") : qsTr("Needs input")
}
ColumnLayout {
Layout.fillWidth: true
spacing: 2
Text {
color: "#E7E1D8"
font.pixelSize: 14
font.weight: Font.Medium
text: readinessRow.modelData.label
}
Text {
color: "#A9A098"
font.pixelSize: 12
text: readinessRow.modelData.detail
}
}
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: unsupportedNotice.implicitHeight + 22
radius: 8
color: "#211914"
border.color: "#49301F"
border.width: 1
Text {
id: unsupportedNotice
anchors.fill: parent
anchors.margins: 11
color: "#F2D8C7"
font.pixelSize: 12
wrapMode: Text.Wrap
text: root.hasMetadata ? qsTr("Metadata-backed definitions require typed serialization today; the generic IDL route cannot serialize the structured creation arguments.") : qsTr("This simple fungible path maps to the generic creation instruction, but this prototype never submits it.")
}
}
Text {
Layout.fillWidth: true
color: "#8E8780"
font.pixelSize: 12
wrapMode: Text.Wrap
text: qsTr("No on-chain symbol, decimal field, image, royalty, collection, or mutable metadata setting exists in the current token definition schema.")
}
}
}
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,368 @@
import QtQuick 2.15
QtObject {
id: root
readonly property var fixtureDefinitions: [
{
"id": "9JDLE5Qr8dXKBstucN5sZi5tCCYy7SnfCEKax77JZTd7",
"name": "Pebble",
"type": "fungible",
"definitionId": "9JDLE5Qr8dXKBstucN5sZi5tCCYy7SnfCEKax77JZTd7",
"definitionHex": "7b464ff9dd0d3bc07f7e2e0b0667ccd066d85ad12be4c79fc55687a863910aa6",
"holdingId": "DhKocL4KzaRbL25Dw3V8rDvTa6aNefxyCAb8F22Tyazn",
"rawSupply": "7654321",
"displaySupply": "7,654,321",
"inferredDecimals": 0,
"authorityMode": "fixed",
"source": "testnet",
"instruction": "new_fungible_definition",
"holdings": [
{
"id": "DhKocL4KzaRbL25Dw3V8rDvTa6aNefxyCAb8F22Tyazn",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "6419754",
"displayBalance": "6,419,754"
},
{
"id": "78Ynj85rpDFfAH2Tk1F296k6uJrU2h8mFoAsJg8sM3DZ",
"wallet": "single-asset",
"role": "recipient",
"rawBalance": "1234567",
"displayBalance": "1,234,567"
}
]
},
{
"id": "5u7LShcFeBwFubWbD1N6jBYMGonEuj1sfi2A6j9D5UK3",
"name": "Aurora",
"type": "fungible",
"definitionId": "5u7LShcFeBwFubWbD1N6jBYMGonEuj1sfi2A6j9D5UK3",
"definitionHex": "48c81cf032e601ca367fc9816b957dbf5c0e4c11cf7008e8f4581ec1a67aab42",
"holdingId": "5rZuJSHTm2NggSBFbKuyZd6D3f7qWAhTB7YsgFZDYe8",
"rawSupply": "98765432100",
"displaySupply": "98,765.4321",
"inferredDecimals": 6,
"authorityMode": "fixed",
"source": "testnet",
"instruction": "new_fungible_definition",
"holdings": [
{
"id": "5rZuJSHTm2NggSBFbKuyZd6D3f7qWAhTB7YsgFZDYe8",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "90000000000",
"displayBalance": "90,000"
},
{
"id": "HrC5H28wnK64dsx4xDkUUPipxDvcNFmB9FizJ4vDEHLR",
"wallet": "mixed-assets",
"role": "recipient",
"rawBalance": "8765432100",
"displayBalance": "8,765.4321"
}
]
},
{
"id": "2TN8jTUxgxRZgATDGANqiNuALpAyUM3Lks6wiNYvSdDi",
"name": "Cobalt",
"type": "fungible",
"definitionId": "2TN8jTUxgxRZgATDGANqiNuALpAyUM3Lks6wiNYvSdDi",
"definitionHex": "159caef810ea545951b3bd913efe625ee45008c80865c330e72a72ed48b61649",
"holdingId": "7mKCcw4dtQW6LyxFazZpB7XUisfGLRNfxBcWEiJaBvgZ",
"rawSupply": "9876543210000",
"displaySupply": "9,876,543.21",
"inferredDecimals": 6,
"authorityMode": "fixed",
"source": "testnet",
"instruction": "new_fungible_definition",
"holdings": [
{
"id": "7mKCcw4dtQW6LyxFazZpB7XUisfGLRNfxBcWEiJaBvgZ",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "9876543210000",
"displayBalance": "9,876,543.21"
}
]
},
{
"id": "8wRnGribscuUhJJFca4nXwR8iAYtrpUU1JXLqZNXMSou",
"name": "Meridian",
"type": "fungible",
"definitionId": "8wRnGribscuUhJJFca4nXwR8iAYtrpUU1JXLqZNXMSou",
"definitionHex": "75f33110b185717209e3955f228d4a4448801d0ce8ba438a4a268050eeff3f44",
"holdingId": "DNckKy9rUwohZS51ZYGxpw6Ke2WYusaBwqDecMzqCiqF",
"rawSupply": "123456789012345678",
"displaySupply": "123,456,789.012345678",
"inferredDecimals": 9,
"authorityMode": "fixed",
"source": "testnet",
"instruction": "new_fungible_definition",
"holdings": [
{
"id": "DNckKy9rUwohZS51ZYGxpw6Ke2WYusaBwqDecMzqCiqF",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "100000000000000000",
"displayBalance": "100,000,000"
},
{
"id": "4TkHNLiC6WQeFkeTmh6bAcLzYYhqbeJyRci92bXAcF8D",
"wallet": "mixed-assets",
"role": "recipient",
"rawBalance": "23456789012345678",
"displayBalance": "23,456,789.012345678"
}
]
},
{
"id": "HwzCapv2fVYKhrdK68Q7QwdhfNkJDXrCGxqkW8e9UjDG",
"name": "Quartz",
"type": "fungible",
"definitionId": "HwzCapv2fVYKhrdK68Q7QwdhfNkJDXrCGxqkW8e9UjDG",
"definitionHex": "fbd107ca4bb66bc58f59ac2d32a759be3ee0fb453f8fecd1991c11837d9660c7",
"holdingId": "AtBFdo6vXRoDHwadHPHvMk84bW3fLtjy7YHF5UeS7Trx",
"rawSupply": "12345678901234567890123456",
"displaySupply": "12,345,678.901234567890123456",
"inferredDecimals": 18,
"authorityMode": "fixed",
"source": "testnet",
"instruction": "new_fungible_definition",
"holdings": [
{
"id": "AtBFdo6vXRoDHwadHPHvMk84bW3fLtjy7YHF5UeS7Trx",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "10000000000000000000000000",
"displayBalance": "10,000,000"
},
{
"id": "3JqMVkxAnSVuvetAppW5g3jKrFKv5otyhHNUkYw859p2",
"wallet": "mixed-assets",
"role": "recipient",
"rawBalance": "2345678901234567890123456",
"displayBalance": "2,345,678.901234567890123456"
}
]
},
{
"id": "6juKZuiBbxwymyet38MnsvidbsomcQEYLR8jhaFYp7f4",
"name": "Devium",
"type": "fungible",
"definitionId": "6juKZuiBbxwymyet38MnsvidbsomcQEYLR8jhaFYp7f4",
"definitionHex": "5547fcb72644d95a385d313b887a96be41ff263bce6150b49fd87276839822bf",
"holdingId": "63JPusYpdEEtri5ZHcLqfeM9XdnNsbc3pVdwhFnZB6ep",
"rawSupply": "1000000750000000000000000000000",
"displaySupply": "1,000,000,750,000",
"inferredDecimals": 18,
"authorityMode": "external",
"authority": "HLf2CQotnxpjsrG98xrtUb7qoQcXufU6ARtMAdcuP55c",
"authorityLabel": "Devium Authority",
"source": "testnet",
"instruction": "new_fungible_definition",
"holdings": [
{
"id": "63JPusYpdEEtri5ZHcLqfeM9XdnNsbc3pVdwhFnZB6ep",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "1000000750000000000000000000000",
"displayBalance": "1,000,000,750,000"
}
]
},
{
"id": "Hqvym6HLGdu3WXZendqNKLL2feRmwFJ46CrVYhVBw1ti",
"name": "Mint Condition",
"type": "fungible",
"definitionId": "Hqvym6HLGdu3WXZendqNKLL2feRmwFJ46CrVYhVBw1ti",
"definitionHex": "fa43e74a97d79c5f907ff3edabda5ad89bfbd3b0922572e675d4ad3c7b6029c7",
"holdingId": "HPYP64fjV6fRRk8rDMVFU4JXnh8g9SMpfFLMFVUBirVx",
"rawSupply": "164803398874989484820",
"displaySupply": "164,803,398,874.98948482",
"inferredDecimals": 9,
"authorityMode": "renounced",
"initialAuthority": "Hqvym6HLGdu3WXZendqNKLL2feRmwFJ46CrVYhVBw1ti",
"source": "testnet",
"instruction": "new_fungible_definition",
"holdings": [
{
"id": "HPYP64fjV6fRRk8rDMVFU4JXnh8g9SMpfFLMFVUBirVx",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "164803398874989484820",
"displayBalance": "164,803,398,874.98948482"
}
]
},
{
"id": "6L9bqMV8YiRpTNfqrZaZAyAFwqyNKHG2iQehRqANtVxk",
"name": "Oops! All Metadata",
"type": "fungible",
"definitionId": "6L9bqMV8YiRpTNfqrZaZAyAFwqyNKHG2iQehRqANtVxk",
"definitionHex": "4f3231d8a01e1d79f163bc27fce0c860a4a2f6890280e9d135eafbde0d68ed79",
"holdingId": "B8juLRsBJLSAaFtFhwx3K3PpcxZ4aKkxnkh6LeFzdjBy",
"metadataId": "AHUVjXRgedSGexq9ebwHZ2yfoXed2BKfMm2MQAcxE8WZ",
"rawSupply": "424218967453",
"displaySupply": "424,218.967453",
"inferredDecimals": 6,
"authorityMode": "fixed",
"metadataStandard": "Simple",
"metadataUri": "data:application/json;base64,eyJuYW1lIjoiT29wcyEgQWxsIE1ldGFkYXRhIiwiZGVzY3JpcHRpb24iOiJBIHRva2VuIHdpdGggbW9yZSBtZXRhZGF0YSB0aGFuIHNlbnNlLiIsIm1vZGUiOiJmaXhlZCIsInN0YW5kYXJkIjoiU2ltcGxlIn0=",
"creators": "Department of Redundant Metadata",
"description": "A token with more metadata than sense.",
"source": "testnet",
"instruction": "new_definition_with_metadata",
"holdings": [
{
"id": "B8juLRsBJLSAaFtFhwx3K3PpcxZ4aKkxnkh6LeFzdjBy",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "424118967453",
"displayBalance": "424,118.967453"
},
{
"id": "Dxd4WrxvZHmbCCVkFB7dzh6UYcKTc6GLegPS1XeAqowr",
"wallet": "all-assets",
"role": "recipient",
"rawBalance": "100000000",
"displayBalance": "100"
}
]
},
{
"id": "Hqfz9dfGeMgWbL9rhUnMPLgdQZUMczjNjUWCTRozkkk7",
"name": "Pocket Lint Deluxe",
"type": "fungible",
"definitionId": "Hqfz9dfGeMgWbL9rhUnMPLgdQZUMczjNjUWCTRozkkk7",
"definitionHex": "fa32f354408857006f8ea396b0419823bd04436eadb2d273d2618a46b4793ed8",
"holdingId": "7aDup3sUWkFF2RZnCXyo78BEvaKT3Wf4dWrPTqaMeRXj",
"metadataId": "GciAureLmwkCKuELgo2YzF1dYhh8Jcu7cMciquuY7no5",
"rawSupply": "10000000000000000000000000",
"displaySupply": "10,000,000",
"inferredDecimals": 18,
"authorityMode": "external",
"authority": "2VavnvNLSTNUWhaG4Tkdw86WrRJx5dyoyQYij6KQzz3Y",
"metadataStandard": "Expanded",
"metadataUri": "data:application/json;base64,eyJuYW1lIjoiUG9ja2V0IExpbnQgRGVsdXhlIiwiZGVzY3JpcHRpb24iOiJQcmVtaXVtIGxpbnQsIG5vdyB0b2tlbml6ZWQuIiwibW9kZSI6ImV4dGVybmFsLWF1dGhvcml0eSIsImF1dGhvcml0eSI6IjJWYXZudk5MU1ROVVdoYUc0VGtkdzg2V3JSSng1ZHlveVFZaWo2S1F6ejNZIn0=",
"creators": "2VavnvNLSTNUWhaG4Tkdw86WrRJx5dyoyQYij6KQzz3Y",
"description": "Premium lint, now tokenized.",
"source": "testnet",
"instruction": "new_definition_with_metadata",
"holdings": [
{
"id": "7aDup3sUWkFF2RZnCXyo78BEvaKT3Wf4dWrPTqaMeRXj",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "10000000000000000000000000",
"displayBalance": "10,000,000"
}
]
},
{
"id": "14tAtixMByFyJrcZVyWibitnijLgd59PfyrjdnYzo8La",
"name": "Sir Mints-a-Lot",
"type": "fungible",
"definitionId": "14tAtixMByFyJrcZVyWibitnijLgd59PfyrjdnYzo8La",
"definitionHex": "00fe99e4fbd4c71f92e47c384c6235244c8cce39b6d6367e1e338eca0ffe01cb",
"holdingId": "B1yzSPqaetRJx19aXUd7xpQje5iYNF2Qwr1SKbFLCf8F",
"metadataId": "21ByKA4ZCYWm8pfPyhR1Q1tqYT2RRA8JGsDBX1cp24c7",
"rawSupply": "1000000000000000000",
"displaySupply": "1,000,000,000",
"inferredDecimals": 9,
"authorityMode": "self",
"authority": "14tAtixMByFyJrcZVyWibitnijLgd59PfyrjdnYzo8La",
"metadataStandard": "Simple",
"metadataUri": "data:application/json;base64,eyJuYW1lIjoiU2lyIE1pbnRzLWEtTG90IiwiZGVzY3JpcHRpb24iOiJBIHNlbGYtYXV0aG9yaXplZCBtaW50aW5nIGVudGh1c2lhc3QuIiwibW9kZSI6InNlbGYtYXV0aG9yaXR5IiwiYXV0aG9yaXR5IjoiMTR0QXRpeE1CeUZ5SnJjWlZ5V2liaXRuaWpMZ2Q1OVBmeXJqZG5Zem84TGEifQ==",
"creators": "14tAtixMByFyJrcZVyWibitnijLgd59PfyrjdnYzo8La",
"description": "A self-authorized minting enthusiast.",
"source": "testnet",
"instruction": "new_definition_with_metadata",
"holdings": [
{
"id": "B1yzSPqaetRJx19aXUd7xpQje5iYNF2Qwr1SKbFLCf8F",
"wallet": "all-assets",
"role": "treasury",
"rawBalance": "1000000000000000000",
"displayBalance": "1,000,000,000"
}
]
},
{
"id": "3sG2zN3fAXBvs9mdgFHFiJFSFiwiYyaR5HUA3gZZTSXt",
"name": "Glitchlings",
"type": "nonFungible",
"definitionId": "3sG2zN3fAXBvs9mdgFHFiJFSFiwiYyaR5HUA3gZZTSXt",
"definitionHex": "2a9769e40f12e6d1567bea757974fd9cbb504be1bd8e5a262e6ee5bfaf533d53",
"holdingId": "5QTBFQpYDZDrj569WPBamVqdauNkRRo4DrYE4ZZZn2Pv",
"metadataId": "BxAbq1EK6Hg6y8g296719V9HQJSet4yio5VG9Ub3YHtU",
"authorityMode": "masterHolding",
"metadataStandard": "Expanded",
"metadataUri": "data:application/json;base64,eyJuYW1lIjoiR2xpdGNobGluZ3MiLCJkZXNjcmlwdGlvbiI6IkxFWiB0ZXN0bmV0IG5vbi1mdW5naWJsZSBjb2xsZWN0aW9uIiwibmV0d29yayI6InRlc3RuZXQiLCJhdXRob3JpdHkiOiI1UVRCRlFwWURaRHJqNTY5V1BCYW1WcWRhdU5rUlJvNERyWUU0WlpabjJQdiJ9",
"creators": "5QTBFQpYDZDrj569WPBamVqdauNkRRo4DrYE4ZZZn2Pv",
"description": "LEZ testnet non-fungible collection",
"source": "testnet",
"instruction": "new_definition_with_metadata",
"printableCopies": 64,
"holdings": [
{
"id": "5QTBFQpYDZDrj569WPBamVqdauNkRRo4DrYE4ZZZn2Pv",
"wallet": "all-assets",
"role": "nftMaster",
"printBalance": 63
},
{
"id": "96b6C9RCd5WCa42RgaRMT7ePEJh5GXLLhbBxd15qwA1i",
"role": "nftPrintedCopy",
"owned": true
}
]
}
]
property var draftDefinitions: []
readonly property var allDefinitions: root.fixtureDefinitions.concat(root.draftDefinitions)
function findDefinition(id) {
var wantedId = String(id || "")
for (var index = 0; index < root.allDefinitions.length; ++index) {
var definition = root.allDefinitions[index]
if (definition.id === wantedId || definition.definitionId === wantedId)
return definition
}
return null
}
function visibleDefinitions(query, typeFilter) {
var search = String(query || "").trim().toLowerCase()
var expectedType = String(typeFilter || "all")
return root.allDefinitions.filter(function(definition) {
if (expectedType !== "all" && definition.type !== expectedType)
return false
if (!search)
return true
var fields = [definition.name, definition.id, definition.definitionId, definition.type]
return fields.some(function(field) {
return String(field || "").toLowerCase().indexOf(search) !== -1
})
})
}
function shortAddress(address) {
var value = String(address || "")
return value.length > 13
? value.substring(0, 6) + "..." + value.substring(value.length - 4)
: value
}
function addDraft(draft) {
if (!draft)
return null
root.draftDefinitions = root.draftDefinitions.concat([draft])
return draft
}
}
+65
View File
@@ -0,0 +1,65 @@
#include "TokenUiBackend.h"
#include "LogosWalletProvider.h"
#include "WalletController.h"
#include "logos_api.h"
TokenUiBackend::TokenUiBackend(LogosAPI* logosAPI, QObject* parent)
: TokenUiBackendSimpleSource(parent),
m_logosAPI(logosAPI ? logosAPI : new LogosAPI("token_ui", this)),
m_wallet(std::make_unique<LogosWalletProvider>(m_logosAPI)),
m_walletController(std::make_unique<WalletController>(
*m_wallet, QStringLiteral("TokenUI")))
{
connect(m_walletController.get(), &WalletController::stateChanged,
this, &TokenUiBackend::syncWalletState);
syncWalletState();
m_walletController->start();
}
TokenUiBackend::~TokenUiBackend() = default;
WalletAccountModel* TokenUiBackend::accountModel() const
{
return m_walletController->accountModel();
}
QString TokenUiBackend::createAccountPublic()
{
return m_walletController->createAccount(true);
}
QString TokenUiBackend::createAccountPrivate()
{
return m_walletController->createAccount(false);
}
QString TokenUiBackend::createNewDefault(QString password)
{
const QString mnemonic = m_walletController->createDefaultWallet(password);
syncWalletState();
return mnemonic;
}
bool TokenUiBackend::openExisting()
{
const bool opened = m_walletController->open();
syncWalletState();
return opened;
}
void TokenUiBackend::disconnectWallet()
{
m_walletController->disconnect();
syncWalletState();
}
void TokenUiBackend::syncWalletState()
{
const WalletUiState& state = m_walletController->state();
setIsWalletOpen(state.isWalletOpen);
setWalletExists(state.walletExists);
setWalletHome(state.walletHome);
setSequencerAddr(state.sequencerAddress);
setSequencerReachable(state.sequencerReachable);
}
+45
View File
@@ -0,0 +1,45 @@
#ifndef TOKEN_UI_BACKEND_H
#define TOKEN_UI_BACKEND_H
#include <memory>
#include <QObject>
#include "rep_TokenUiBackend_source.h"
#include "WalletAccountModel.h"
class LogosAPI;
class LogosWalletProvider;
class WalletController;
// Token UI backend. Wallet lifecycle and account state are delegated to the
// repository's shared WalletController; this class only adapts that state to
// the TokenUiBackend QtRO contract.
class TokenUiBackend : public TokenUiBackendSimpleSource {
Q_OBJECT
Q_PROPERTY(WalletAccountModel* accountModel READ accountModel CONSTANT)
public:
explicit TokenUiBackend(LogosAPI* logosAPI = nullptr, QObject* parent = nullptr)
;
~TokenUiBackend() override;
WalletAccountModel* accountModel() const;
public slots:
QString createAccountPublic() override;
QString createAccountPrivate() override;
QString createNewDefault(QString password) override;
bool openExisting() override;
void disconnectWallet() override;
private:
void syncWalletState();
LogosAPI* m_logosAPI;
std::unique_ptr<LogosWalletProvider> m_wallet;
std::unique_ptr<WalletController> m_walletController;
};
#endif // TOKEN_UI_BACKEND_H
+29
View File
@@ -0,0 +1,29 @@
// QtRO view contract for the Token UI backend. PROPs auto-sync to every QML
// replica; SLOTs are the async surface QML calls via logos.watch(...).
// The account list is exposed separately as a Q_PROPERTY model on the backend
// (reached from QML via logos.model("token_ui", "accountModel")).
class TokenUiBackend
{
PROP(bool isWalletOpen READONLY)
PROP(bool walletExists READONLY)
PROP(QString walletHome READONLY)
PROP(QString sequencerAddr READONLY)
// Whether the configured sequencer answered the last reachability probe.
// Defaults true so the UI doesn't flash a warning before the first check.
PROP(bool sequencerReachable READONLY)
// Account management
SLOT(QString createAccountPublic())
SLOT(QString createAccountPrivate())
// Creates a fresh per-app wallet at walletHome and returns its BIP39
// mnemonic (empty on failure), which is the only chance to record it.
SLOT(QString createNewDefault(QString password))
// Re-open the existing on-disk wallet after a disconnect.
SLOT(bool openExisting())
// Close this app's wallet view (lock); does not delete the wallet and, in
// Basecamp, does not close the wallet other apps share.
SLOT(void disconnectWallet())
}
+19
View File
@@ -0,0 +1,19 @@
#include "TokenUiPlugin.h"
#include "TokenUiBackend.h"
#include <QDebug>
TokenUiPlugin::TokenUiPlugin(QObject* parent)
: QObject(parent)
{
}
TokenUiPlugin::~TokenUiPlugin() = default;
void TokenUiPlugin::initLogos(LogosAPI* api)
{
if (m_backend) return;
m_backend = new TokenUiBackend(api, this);
setBackend(m_backend);
qDebug() << "TokenUiPlugin: backend initialized";
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef TOKEN_UI_PLUGIN_H
#define TOKEN_UI_PLUGIN_H
#include <QObject>
#include <QString>
#include <QtPlugin> // for Q_PLUGIN_METADATA, Q_INTERFACES
#include "TokenUiPluginInterface.h"
#include "LogosViewPluginBase.h"
class LogosAPI;
class TokenUiBackend;
// Thin plugin entry point. Holds a TokenUiBackend and lets the generated
// view-plugin base expose it to ui-host.
class TokenUiPlugin : public QObject,
public TokenUiPluginInterface,
public TokenUiBackendViewPluginBase
{
Q_OBJECT
Q_PLUGIN_METADATA(IID TokenUiPluginInterface_iid FILE "../metadata.json")
Q_INTERFACES(TokenUiPluginInterface)
public:
explicit TokenUiPlugin(QObject* parent = nullptr);
~TokenUiPlugin() override;
QString name() const override { return "token_ui"; }
QString version() const override { return "0.1.0"; }
// Called by ui-host after plugin load. Creates the backend and wires it
// up with the provided LogosAPI.
Q_INVOKABLE void initLogos(LogosAPI* api);
private:
TokenUiBackend* m_backend = nullptr;
};
#endif // TOKEN_UI_PLUGIN_H
+19
View File
@@ -0,0 +1,19 @@
#ifndef TOKEN_UI_PLUGIN_INTERFACE_H
#define TOKEN_UI_PLUGIN_INTERFACE_H
#include <QtPlugin> // for Q_DECLARE_INTERFACE
#include "interface.h"
// Marker interface used by Qt's plugin loader to identify the Token UI plugin.
// The actual API surface (slots, properties, signals) lives in
// TokenUiBackend.rep — this header only carries the IID.
class TokenUiPluginInterface : public PluginInterface
{
public:
virtual ~TokenUiPluginInterface() = default;
};
#define TokenUiPluginInterface_iid "org.logos.TokenUiPluginInterface"
Q_DECLARE_INTERFACE(TokenUiPluginInterface, TokenUiPluginInterface_iid)
#endif // TOKEN_UI_PLUGIN_INTERFACE_H