diff --git a/examples/connect-button/package.json b/examples/connect-button/package.json
index 92c0042..670914d 100644
--- a/examples/connect-button/package.json
+++ b/examples/connect-button/package.json
@@ -10,7 +10,7 @@
"lint": "eslint"
},
"dependencies": {
- "@tari-project/ootle-wallet-daemon-signer": "workspace:*",
+ "@tari-project/ootle-wallet-daemon-signer": "link:../../packages/ootle-wallet-daemon-signer",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
@@ -18,7 +18,7 @@
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.0.0",
- "eslint": "catalog:",
+ "eslint": "^10.1.0",
"eslint-plugin-react-hooks": "^7.0.1",
"typescript": "~5.8.0",
"vite": "^6.0.0",
diff --git a/examples/indexer-explorer/package.json b/examples/indexer-explorer/package.json
index 6c692c6..fa1e0bb 100644
--- a/examples/indexer-explorer/package.json
+++ b/examples/indexer-explorer/package.json
@@ -10,8 +10,8 @@
"lint": "eslint src/"
},
"dependencies": {
- "@tari-project/ootle": "workspace:*",
- "@tari-project/ootle-indexer": "workspace:*",
+ "@tari-project/ootle": "link:../../packages/ootle",
+ "@tari-project/ootle-indexer": "link:../../packages/ootle-indexer",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
@@ -19,7 +19,7 @@
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.0.0",
- "eslint": "catalog:",
+ "eslint": "^10.1.0",
"eslint-plugin-react-hooks": "^7.0.1",
"typescript": "~5.8.0",
"vite": "^6.0.0",
diff --git a/examples/template-inspector/package.json b/examples/template-inspector/package.json
index a3ec4e9..017888f 100644
--- a/examples/template-inspector/package.json
+++ b/examples/template-inspector/package.json
@@ -10,8 +10,8 @@
"lint": "eslint src/"
},
"dependencies": {
- "@tari-project/ootle": "workspace:*",
- "@tari-project/ootle-indexer": "workspace:*",
+ "@tari-project/ootle": "link:../../packages/ootle",
+ "@tari-project/ootle-indexer": "link:../../packages/ootle-indexer",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
@@ -19,7 +19,7 @@
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.0.0",
- "eslint": "catalog:",
+ "eslint": "^10.1.0",
"eslint-plugin-react-hooks": "^7.0.1",
"typescript": "~5.8.0",
"vite": "^6.0.0",
diff --git a/examples/template-inspector/src/index.css b/examples/template-inspector/src/index.css
index 35ba97e..50454e7 100644
--- a/examples/template-inspector/src/index.css
+++ b/examples/template-inspector/src/index.css
@@ -18,7 +18,7 @@
--success: #4ade80;
--error: #f87171;
--warning: #fbbf24;
- --mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
+ --mono: ui-monospace, monospace;
}
html,
diff --git a/examples/transactions/eslint.config.js b/examples/transactions/eslint.config.js
new file mode 100644
index 0000000..f721894
--- /dev/null
+++ b/examples/transactions/eslint.config.js
@@ -0,0 +1,13 @@
+// @ts-check
+
+import { defineConfig } from "eslint/config";
+import reactHooks from "eslint-plugin-react-hooks";
+import sharedConfig from "../../eslint.config.mjs";
+
+export default defineConfig([
+ reactHooks.configs.flat.recommended,
+ sharedConfig,
+ {
+ files: ["**/*.{js,jsx,ts,tsx}"],
+ },
+]);
diff --git a/examples/transactions/index.html b/examples/transactions/index.html
new file mode 100644
index 0000000..e16b0dc
--- /dev/null
+++ b/examples/transactions/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Ootle Transactions
+
+
+
+
+
+
diff --git a/examples/transactions/package.json b/examples/transactions/package.json
new file mode 100644
index 0000000..1aa83d0
--- /dev/null
+++ b/examples/transactions/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "ootle-example-transactions",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "preview": "vite preview",
+ "lint": "eslint src/"
+ },
+ "dependencies": {
+ "@tari-project/ootle": "link:../../packages/ootle",
+ "@tari-project/ootle-indexer": "link:../../packages/ootle-indexer",
+ "@tari-project/ootle-secret-key-wallet": "link:../../packages/ootle-secret-key-wallet",
+ "react": "^19.2.4",
+ "react-dom": "^19.2.4"
+ },
+ "devDependencies": {
+ "@types/react": "^19.2.14",
+ "@types/react-dom": "^19.2.3",
+ "@vitejs/plugin-react": "^4.7.0",
+ "eslint": "^10.1.0",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "typescript": "~5.8.3",
+ "vite": "^6.4.1",
+ "vite-plugin-top-level-await": "^1.6.0",
+ "vite-plugin-wasm": "^3.6.0"
+ }
+}
diff --git a/examples/transactions/src/App.css b/examples/transactions/src/App.css
new file mode 100644
index 0000000..36ae3b5
--- /dev/null
+++ b/examples/transactions/src/App.css
@@ -0,0 +1,485 @@
+/* ── Root layout ─────────────────────────────────────────────────────────── */
+
+.layout {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.body {
+ flex: 1;
+ display: flex;
+ overflow: hidden;
+}
+
+/* ── Topbar ──────────────────────────────────────────────────────────────── */
+
+.topbar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ padding: 10px 20px;
+ border-bottom: 1px solid var(--border);
+ background: var(--surface);
+ flex-shrink: 0;
+}
+
+.topbar-left {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ min-width: 0;
+}
+
+.topbar-right {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex-shrink: 0;
+}
+
+.topbar-title {
+ font-size: 15px;
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ white-space: nowrap;
+}
+
+.url-input {
+ background: var(--surface-2);
+ border: 1px solid var(--border);
+ border-radius: 7px;
+ padding: 5px 10px;
+ color: var(--text-muted);
+ font-size: 12px;
+ font-family: var(--mono);
+ outline: none;
+ width: 280px;
+ transition:
+ border-color 0.15s,
+ color 0.15s;
+}
+
+.url-input:focus {
+ border-color: var(--accent);
+ color: var(--text);
+}
+
+/* ── Status badge ────────────────────────────────────────────────────────── */
+
+.status-badge {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ font-size: 12px;
+ font-weight: 500;
+ color: var(--text-muted);
+ white-space: nowrap;
+}
+
+.status-badge.connected {
+ color: var(--success);
+}
+
+.status-badge.errored {
+ color: var(--error);
+}
+
+.dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: currentColor;
+ box-shadow: 0 0 6px currentColor;
+ flex-shrink: 0;
+}
+
+/* ── Sidebar ─────────────────────────────────────────────────────────────── */
+
+.sidebar {
+ width: 140px;
+ flex-shrink: 0;
+ border-right: 1px solid var(--border);
+ background: var(--surface);
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.sidebar-header {
+ padding: 14px 16px 10px;
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.panel-title {
+ font-size: 13px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ color: var(--text-muted);
+}
+
+.arg-input-label {
+ display: flex;
+}
+.arg-input {
+ background: var(--surface-2);
+ border: 1px solid var(--border);
+ border-radius: 7px;
+ padding: 7px 10px;
+ color: var(--text);
+ font-size: 13px;
+ font-family: inherit;
+ display: flex;
+ transition: border-color 0.15s;
+}
+
+.arg-input:focus {
+ border-color: var(--accent);
+}
+
+.arg-input::placeholder {
+ color: var(--text-muted);
+ opacity: 0.5;
+}
+
+.template-list {
+ flex: 1;
+ overflow-y: auto;
+ padding: 6px;
+}
+
+.template-row {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 100%;
+ padding: 9px 10px;
+ background: none;
+ border: none;
+ border-radius: 8px;
+ cursor: pointer;
+ text-align: left;
+ gap: 2px;
+ transition: background 0.1s;
+}
+
+.template-row:hover {
+ background: var(--surface-2);
+}
+
+.template-row.selected {
+ background: color-mix(in srgb, var(--accent) 15%, var(--surface-2));
+}
+
+.template-name {
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--text);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 100%;
+}
+
+.template-addr {
+ font-size: 11px;
+ color: var(--text-muted);
+}
+
+/* ── Detail panel ────────────────────────────────────────────────────────── */
+
+.detail {
+ flex: 2;
+ overflow-y: auto;
+ padding: 20px 24px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.detail.smaller {
+ flex: 1;
+}
+
+.detail-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.address-chip {
+ font-size: 12px;
+ color: var(--text-muted);
+ background: var(--surface-2);
+ border: 1px solid var(--border);
+ border-radius: 6px;
+ padding: 3px 10px;
+ overflow: hidden;
+ overflow-x: auto;
+}
+
+.empty-detail {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 16px;
+ color: var(--text-muted);
+ font-size: 14px;
+}
+
+.tx-view {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+/* ── ABI view ────────────────────────────────────────────────────────────── */
+
+.abi-view {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.fn-card {
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ overflow: hidden;
+}
+
+.fn-card.constructor {
+ border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
+}
+
+.fn-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ width: 100%;
+ padding: 12px 16px;
+ background: none;
+ border: none;
+ cursor: pointer;
+ color: var(--text);
+ text-align: left;
+ transition: background 0.1s;
+}
+
+.fn-header:hover {
+ background: var(--surface-2);
+}
+
+.fn-sig {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+}
+
+.fn-badge {
+ font-size: 10px;
+ font-weight: 700;
+ padding: 2px 7px;
+ border-radius: 4px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ flex-shrink: 0;
+}
+
+.fn-badge.constructor {
+ background: color-mix(in srgb, var(--accent) 20%, transparent);
+ color: var(--accent);
+}
+
+.fn-name {
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--text);
+}
+
+.fn-args-preview {
+ font-size: 13px;
+ color: var(--text-muted);
+ font-family: var(--mono);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.fn-body {
+ border-top: 1px solid var(--border);
+ padding: 14px 16px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ background: var(--surface-2);
+}
+
+.fn-section {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.fn-section-label {
+ font-size: 11px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ color: var(--text-muted);
+}
+
+.arg-list {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.arg-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 5px 10px;
+ background: var(--surface);
+ border: 1px solid var(--border);
+ border-radius: 6px;
+}
+
+.arg-name {
+ font-size: 13px;
+ color: var(--text);
+ min-width: 100px;
+}
+
+.arg-type {
+ font-size: 12px;
+ color: var(--accent);
+}
+
+.fn-empty {
+ font-size: 13px;
+ color: var(--text-muted);
+}
+
+/* ── JSON fallback ───────────────────────────────────────────────────────── */
+
+.json-view {
+ background: var(--surface-2);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ padding: 16px;
+ font-family: var(--mono), monospace;
+ font-size: 12px;
+ line-height: 1.6;
+ overflow: auto;
+ color: var(--text);
+ white-space: pre;
+ max-height: 100%;
+}
+
+/* ── Shared ──────────────────────────────────────────────────────────────── */
+
+.mono {
+ font-family: var(--mono), monospace;
+}
+
+.loading-state {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 16px;
+ font-size: 13px;
+ color: var(--text-muted);
+}
+
+.empty-state {
+ padding: 16px;
+ font-size: 13px;
+ color: var(--text-muted);
+ text-align: center;
+}
+
+.error-banner {
+ margin: 8px 8px 0;
+ background: color-mix(in srgb, var(--error) 12%, transparent);
+ border: 1px solid color-mix(in srgb, var(--error) 30%, transparent);
+ border-radius: 8px;
+ padding: 10px 14px;
+ color: var(--error);
+ font-size: 13px;
+ line-height: 1.5;
+}
+
+/* ── Buttons ─────────────────────────────────────────────────────────────── */
+
+.btn-group {
+ display: flex;
+ gap: 6px;
+}
+
+.btn-ghost {
+ background: none;
+ border: 1px solid var(--border);
+ border-radius: 7px;
+ padding: 5px 12px;
+ color: var(--text-muted);
+ font-size: 13px;
+ font-family: inherit;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ white-space: nowrap;
+ transition:
+ border-color 0.15s,
+ color 0.15s;
+}
+
+.btn-ghost:hover:not(:disabled) {
+ border-color: var(--text-muted);
+ color: var(--text);
+}
+
+.btn-ghost:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+.btn-ghost.small {
+ font-size: 12px;
+ padding: 4px 10px;
+}
+
+/* ── Spinner ─────────────────────────────────────────────────────────────── */
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.spinner {
+ display: inline-block;
+ width: 14px;
+ height: 14px;
+ border: 2px solid rgba(255, 255, 255, 0.25);
+ border-top-color: #fff;
+ border-radius: 50%;
+ animation: spin 0.7s linear infinite;
+ flex-shrink: 0;
+}
+
+.spinner.dark {
+ border-color: rgba(100, 100, 120, 0.4);
+ border-top-color: var(--text-muted);
+}
diff --git a/examples/transactions/src/App.tsx b/examples/transactions/src/App.tsx
new file mode 100644
index 0000000..f17ccb1
--- /dev/null
+++ b/examples/transactions/src/App.tsx
@@ -0,0 +1,120 @@
+import "./App.css";
+import { DefinitionView, DotLogo, Spinner, TemplateRow, truncate } from "./components.tsx";
+import type { FunctionDef } from "@tari-project/ootle-indexer";
+import { useConnect } from "./hooks/useConnect.ts";
+import { useEffect, useRef, useState } from "react";
+import { useTemplates } from "./hooks/useTemplates.ts";
+import { Transact } from "./Transact.tsx";
+
+export function App() {
+ const connectionAttempted = useRef(false);
+ const [selectedFn, setSelectedFn] = useState(null);
+ const { status, provider, handleConnect } = useConnect();
+ const { templateList, selectTemplate, selectedAddress, definition, definitionLoading, definitionError } =
+ useTemplates({ provider });
+
+ useEffect(() => {
+ if (status !== "disconnected" || connectionAttempted.current) return;
+ handleConnect().finally(() => (connectionAttempted.current = true));
+ }, [handleConnect, status]);
+
+ function onTemplateSelect(address: string) {
+ setSelectedFn(null);
+ void selectTemplate(address);
+ }
+
+ const sidebarMarkup = (
+
+ );
+
+ const templateDetailsMarkup = (
+
+ {!selectedAddress && (
+
+
+
+ After getting the signer pubkey/address, select a template to inspect its ABI and interact with its
+ functions
+
+
+ )}
+
+ {selectedAddress && (
+ <>
+
+
+ Template: {definition?.V1.template_name}
+
+
+ {truncate(selectedAddress, 14, 10)}
+
+
+
+ {definitionLoading && (
+
+ Fetching definition…
+
+ )}
+
+ {definitionError && (
+
+ {definitionError}
+
+ )}
+
+ {definition && !definitionLoading && }
+ >
+ )}
+
+ );
+
+ const transactMarkup = (
+
+
+
Transact
+
+
+
+ );
+
+ return (
+
+
+
+ {sidebarMarkup}
+ {templateDetailsMarkup}
+ {transactMarkup}
+
+
+ );
+}
diff --git a/examples/transactions/src/Transact.tsx b/examples/transactions/src/Transact.tsx
new file mode 100644
index 0000000..7efd209
--- /dev/null
+++ b/examples/transactions/src/Transact.tsx
@@ -0,0 +1,88 @@
+import { TemplateDef, FunctionDef, IndexerProvider } from "@tari-project/ootle-indexer";
+
+import {
+ Network,
+ toHexStr,
+ TransactionBuilder,
+ type UnsignedTransactionV1,
+ type TransactionSignature,
+ sendTransaction,
+} from "@tari-project/ootle";
+
+import { getInputType, getTypeAsString } from "./components.tsx";
+import { useCallback, useMemo, useState } from "react";
+import { EphemeralKeySigner } from "@tari-project/ootle-secret-key-wallet";
+type PublishedTemplateAddress = string; // todo export in indexer
+interface TransactProps {
+ provider: IndexerProvider | null;
+ def?: TemplateDef | null;
+ templateAddress?: PublishedTemplateAddress | null;
+ selectedFunction?: FunctionDef | null;
+}
+
+export function Transact({ provider, def, selectedFunction, templateAddress }: TransactProps) {
+ const s = EphemeralKeySigner?.generate();
+ const b = new TransactionBuilder(Network.Esmeralda);
+
+ const [pubKey, setPubKey] = useState(null);
+ const [address, setAddress] = useState(null);
+ const [unsignedAccTx, setUnsignedAccTx] = useState(null);
+ const [signatures, setSignatures] = useState([]);
+
+ async function getSignerDetails() {
+ s.getPublicKey().then((pk) => setPubKey(pk));
+ s.getAddress().then((addr) => setAddress(addr));
+ }
+ function createAccountTX() {
+ if (pubKey) {
+ const accUnsignedTx = b.createAccount(pubKey).buildUnsignedTransaction();
+ setUnsignedAccTx(accUnsignedTx);
+ }
+ }
+
+ async function sendAccTx() {
+ if (provider && unsignedAccTx) {
+ const bla = await sendTransaction(provider, s, unsignedAccTx);
+ console.debug(bla);
+ }
+ }
+
+ return (
+
+
+ Transact with EphemeralKeySigner
+
+
+
+
+
+ {pubKey &&
{toHexStr(pubKey)}
}
+ {address &&
{address}
}
+
+
+
Build a transaction:
+
+ {pubKey && (
+
+ )}
+ {unsignedAccTx && (
+
+ )}
+
+ {unsignedAccTx && (
+
+
+ {JSON.stringify(unsignedAccTx, (key, value) => (key === "arg_type" ? getTypeAsString(value) : value), 2)}
+
+
+ )}
+
+
+ );
+}
diff --git a/examples/transactions/src/components.tsx b/examples/transactions/src/components.tsx
new file mode 100644
index 0000000..8f3b354
--- /dev/null
+++ b/examples/transactions/src/components.tsx
@@ -0,0 +1,225 @@
+import "./App.css";
+import type { TemplateMetadata, TemplateDef, FunctionDef } from "@tari-project/ootle-indexer";
+import { useEffect, useState } from "react";
+
+function DotLogo({ size = 40 }: { size?: number }) {
+ return (
+
+ );
+}
+
+// ── Sub-components ────────────────────────────────────────────────────────────
+
+function TemplateRow({
+ template,
+ selected,
+ onSelect,
+}: {
+ template: TemplateMetadata;
+ selected: boolean;
+ onSelect: () => void;
+}) {
+ return (
+
+ );
+}
+
+interface DefinitionViewProps {
+ definition: TemplateDef;
+ onSelected: (fnDef: FunctionDef) => void;
+}
+/**
+ * Renders the template definition. The exact shape of GetTemplateDefinitionResponse
+ * is determined by the on-chain data. We try to render known fields (functions list)
+ * nicely, and fall back to a formatted JSON dump.
+ */
+function DefinitionView({ definition, onSelected }: DefinitionViewProps) {
+ const [selectedIdx, setSelectedIdx] = useState(undefined);
+ function handleSelected(fnDef: FunctionDef, idx: number) {
+ setSelectedIdx(idx);
+ onSelected(fnDef);
+ }
+ // Try to extract a functions array from known response shapes
+ const functions = extractFunctions(definition);
+ if (functions.length > 0) {
+ return (
+
+ {functions.map((fn, i) => (
+ handleSelected(fnDef, i)}
+ />
+ ))}
+
+ );
+ }
+
+ // Fallback: raw JSON
+ return {JSON.stringify(definition, null, 2)};
+}
+
+type V1 = TemplateDef["V1"];
+type TemplateFns = V1["functions"];
+
+type FuncType = FunctionDef["output"];
+
+function FunctionCard({
+ fn,
+ fnIndex,
+ selectedIdx,
+ onSelected,
+}: {
+ fn: FunctionDef;
+ fnIndex: number;
+ onSelected: (fnDef: FunctionDef) => void;
+ selectedIdx?: number;
+}) {
+ const [expanded, setExpanded] = useState(false);
+
+ useEffect(() => {
+ setExpanded(fnIndex === selectedIdx);
+ }, [fnIndex, selectedIdx]);
+
+ function handleClick() {
+ onSelected(fn);
+ setExpanded(true);
+ }
+
+ return (
+
+
+
+ {expanded && (
+
+ {fn.arguments && fn.arguments.length > 0 && (
+
+
Arguments
+
+ {fn.arguments.map((arg, i) => (
+
+ {arg.name ?? `arg${i}`}
+ {getTypeAsString(arg.arg_type)}
+
+ ))}
+
+
+ )}
+
+ {fn.output !== undefined && fn.output !== null && (
+
+
Returns
+
{getTypeAsString(fn.output)}
+
+ )}
+
+ {(!fn.arguments || fn.arguments.length === 0) && (fn.output === undefined || fn.output === null) && (
+
No arguments · no return value
+ )}
+
+ )}
+
+ );
+}
+
+function Spinner({ dark = false }: { dark?: boolean }) {
+ return ;
+}
+
+function ChevronIcon({ expanded }: { expanded: boolean }) {
+ return (
+
+ );
+}
+
+// ── Helpers ───────────────────────────────────────────────────────────────────
+
+function truncate(str: string, head = 10, tail = 8): string {
+ if (str.length <= head + tail + 3) return str;
+ return `${str.slice(0, head)}…${str.slice(-tail)}`;
+}
+
+function extractFunctions(def: TemplateDef): TemplateFns {
+ if (typeof def !== "object" || def === null) return [];
+ const v1 = def.V1;
+ const fns = v1.functions;
+ if (Array.isArray(fns)) return fns as TemplateFns;
+ return [];
+}
+
+function getTypeAsString(funcType: FuncType): string {
+ if (typeof funcType === "string") {
+ return funcType;
+ }
+
+ const funcTypeKeys = Object.keys(funcType);
+ if (funcTypeKeys.length > 0) {
+ switch (funcTypeKeys[0]) {
+ case "Vec": {
+ return getTypeAsString(funcType["Vec" as keyof typeof funcType]);
+ }
+ case "Tuple": {
+ return JSON.stringify(funcType["Tuple" as keyof typeof funcType]);
+ }
+ case "Other": {
+ const other = funcType["Other" as keyof typeof funcType] as { name: string };
+ return other.name;
+ }
+ }
+ }
+
+ return "Unknown";
+}
+
+const numTypes = ["I8", "I16", "I32", "I64", "I128", "U8", "U16", "U32", "U64", "U128", "Amount"];
+
+function getInputType(strType: string): string {
+ if (numTypes.includes(strType)) {
+ return "number";
+ }
+ if (strType === "Bool") {
+ return "boolean";
+ }
+ return "text";
+}
+
+export {
+ DotLogo,
+ TemplateRow,
+ DefinitionView,
+ FunctionCard,
+ Spinner,
+ ChevronIcon,
+ truncate,
+ extractFunctions,
+ getTypeAsString,
+ getInputType,
+};
diff --git a/examples/transactions/src/hooks/useConnect.ts b/examples/transactions/src/hooks/useConnect.ts
new file mode 100644
index 0000000..b8d4671
--- /dev/null
+++ b/examples/transactions/src/hooks/useConnect.ts
@@ -0,0 +1,28 @@
+import { useCallback, useState } from "react";
+import { IndexerProvider, ProviderBuilder } from "@tari-project/ootle-indexer";
+import { defaultIndexerUrl, Network } from "@tari-project/ootle";
+
+type ConnectionStatus = "disconnected" | "connecting" | "connected";
+const indexerUrl = defaultIndexerUrl(Network.Esmeralda);
+
+export function useConnect() {
+ const [status, setStatus] = useState("disconnected");
+ const [provider, setProvider] = useState(null);
+
+ const handleConnect = useCallback(async () => {
+ setStatus("connecting");
+
+ try {
+ const p = await ProviderBuilder.new().withNetwork(Network.Esmeralda).withUrl(indexerUrl).connect();
+ if (p) {
+ setProvider(p);
+ setStatus("connected");
+ }
+ } catch (e) {
+ setStatus("disconnected");
+ console.error(e);
+ }
+ }, []);
+
+ return { status, handleConnect, provider };
+}
diff --git a/examples/transactions/src/hooks/useTemplates.ts b/examples/transactions/src/hooks/useTemplates.ts
new file mode 100644
index 0000000..8a9f9a2
--- /dev/null
+++ b/examples/transactions/src/hooks/useTemplates.ts
@@ -0,0 +1,68 @@
+import { IndexerProvider } from "@tari-project/ootle-indexer";
+import type { TemplateMetadata, TemplateDef } from "@tari-project/ootle-indexer";
+import { useCallback, useEffect, useRef, useState } from "react";
+
+export function useTemplates({ provider }: { provider: IndexerProvider | null }) {
+ const [selectedAddress, setSelectedAddress] = useState(null);
+ const [definition, setDefinition] = useState(null);
+ const [definitionLoading, setDefinitionLoading] = useState(false);
+ const [definitionError, setDefinitionError] = useState(null);
+ const [templateList, setTemplateList] = useState([]);
+
+ const hasFetched = useRef(false);
+
+ const fetchTemplates = useCallback(async () => {
+ const client = provider?.getClient();
+
+ if (client) {
+ try {
+ const res = await client
+ .getTransport()
+ .sendGet<{ templates?: TemplateMetadata[] }>("templates/cached", { limit: 50 });
+
+ if (res?.templates) {
+ setTemplateList(res.templates);
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ }, [provider]);
+
+ const selectTemplate = useCallback(
+ async (address: string) => {
+ setSelectedAddress(address);
+ setDefinition(null);
+ setDefinitionError(null);
+ setDefinitionLoading(true);
+ try {
+ const client = provider?.getClient();
+ const def = (await client?.templatesGet(address)) as unknown as { definition: TemplateDef };
+
+ setDefinition(def?.definition ?? null);
+ } catch (err) {
+ setDefinitionError(err instanceof Error ? err.message : "Failed to fetch template definition");
+ } finally {
+ setDefinitionLoading(false);
+ }
+ },
+ [provider],
+ );
+
+ useEffect(() => {
+ if (hasFetched.current && templateList?.length > 0) return;
+ fetchTemplates().then(() => {
+ hasFetched.current = true;
+ });
+ }, [fetchTemplates, templateList?.length]);
+
+ return {
+ templateList,
+ fetchTemplates,
+ selectTemplate,
+ selectedAddress,
+ definition,
+ definitionLoading,
+ definitionError,
+ };
+}
diff --git a/examples/transactions/src/hooks/useTransact.ts b/examples/transactions/src/hooks/useTransact.ts
new file mode 100644
index 0000000..816c997
--- /dev/null
+++ b/examples/transactions/src/hooks/useTransact.ts
@@ -0,0 +1,5 @@
+import { IndexerProvider } from "@tari-project/ootle-indexer";
+import type { TemplateMetadata } from "@tari-project/ootle-indexer";
+import { useCallback, useState } from "react";
+
+export function useTransact({ provider }: { provider: IndexerProvider | null }) {}
diff --git a/examples/transactions/src/index.css b/examples/transactions/src/index.css
new file mode 100644
index 0000000..35ba97e
--- /dev/null
+++ b/examples/transactions/src/index.css
@@ -0,0 +1,37 @@
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+:root {
+ --bg: #0a0a0b;
+ --surface: #141416;
+ --surface-2: #1e1e23;
+ --border: #2a2a32;
+ --accent: #9d4edd;
+ --accent-hover: #7b2fbe;
+ --text: #f0f0f0;
+ --text-muted: #888;
+ --success: #4ade80;
+ --error: #f87171;
+ --warning: #fbbf24;
+ --mono: "JetBrains Mono", "Fira Code", ui-monospace, monospace;
+}
+
+html,
+body {
+ height: 100%;
+ background: var(--bg);
+ color: var(--text);
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ font-size: 15px;
+ line-height: 1.6;
+ -webkit-font-smoothing: antialiased;
+}
+
+#root {
+ height: 100%;
+}
diff --git a/examples/transactions/src/main.tsx b/examples/transactions/src/main.tsx
new file mode 100644
index 0000000..8194c0e
--- /dev/null
+++ b/examples/transactions/src/main.tsx
@@ -0,0 +1,11 @@
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import { App } from "./App";
+import "./index.css";
+
+// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+createRoot(document.getElementById("root")!).render(
+
+
+ ,
+);
diff --git a/examples/transactions/tsconfig.json b/examples/transactions/tsconfig.json
new file mode 100644
index 0000000..e210009
--- /dev/null
+++ b/examples/transactions/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true
+ },
+ "include": ["src"]
+}
diff --git a/examples/transactions/vite.config.ts b/examples/transactions/vite.config.ts
new file mode 100644
index 0000000..2057d48
--- /dev/null
+++ b/examples/transactions/vite.config.ts
@@ -0,0 +1,8 @@
+import { defineConfig } from "vite";
+import wasm from "vite-plugin-wasm";
+import topLevelAwait from "vite-plugin-top-level-await";
+import react from "@vitejs/plugin-react";
+
+export default defineConfig({
+ plugins: [wasm(), topLevelAwait(), react()],
+});
diff --git a/packages/ootle-indexer/package.json b/packages/ootle-indexer/package.json
index abd64b0..b9d79b7 100644
--- a/packages/ootle-indexer/package.json
+++ b/packages/ootle-indexer/package.json
@@ -16,7 +16,8 @@
},
"scripts": {
"build": "tsc -b && vite build",
- "test": "vitest run"
+ "test": "vitest run",
+ "lint": "eslint"
},
"dependencies": {
"@tari-project/indexer-client": "catalog:",
@@ -24,13 +25,14 @@
"@tari-project/ootle-ts-bindings": "catalog:"
},
"devDependencies": {
- "@types/node": "catalog:",
"@microsoft/api-extractor": "catalog:",
+ "@types/node": "catalog:",
+ "eslint": "catalog:",
"typescript": "catalog:",
"unplugin-dts": "catalog:",
- "vitest": "catalog:",
"vite": "catalog:",
"vite-plugin-top-level-await": "catalog:",
- "vite-plugin-wasm": "catalog:"
+ "vite-plugin-wasm": "catalog:",
+ "vitest": "catalog:"
}
}
diff --git a/packages/ootle-indexer/src/index.ts b/packages/ootle-indexer/src/index.ts
index 07eacf6..f8e6aa2 100644
--- a/packages/ootle-indexer/src/index.ts
+++ b/packages/ootle-indexer/src/index.ts
@@ -7,7 +7,13 @@ export type { IndexerProviderOptions } from "./indexer-provider";
// Re-export from @tari-project/indexer-client so consumers don't need a direct dependency
export { IndexerClient, transports } from "@tari-project/indexer-client";
// Re-export from @tari-project/ootle-ts-binds so consumers don't need a direct dependency
-export type { TransactionEntry, TemplateMetadata } from "@tari-project/ootle-ts-bindings";
+export type {
+ TransactionEntry,
+ TemplateMetadata,
+ GetTemplateDefinitionResponse,
+ TemplateDef,
+ FunctionDef,
+} from "@tari-project/ootle-ts-bindings";
export { ProviderBuilder } from "./provider-builder";
export { resolveWantInputs } from "./want-input";
diff --git a/packages/ootle-secret-key-wallet/package.json b/packages/ootle-secret-key-wallet/package.json
index 785e560..96d7c46 100644
--- a/packages/ootle-secret-key-wallet/package.json
+++ b/packages/ootle-secret-key-wallet/package.json
@@ -16,7 +16,8 @@
},
"scripts": {
"build": "tsc -b && vite build",
- "test": "vitest run"
+ "test": "vitest run",
+ "lint": "eslint"
},
"dependencies": {
"@tari-project/ootle": "workspace:",
@@ -24,13 +25,14 @@
"@tari-project/ootle-wasm": "catalog:"
},
"devDependencies": {
- "@types/node": "catalog:",
"@microsoft/api-extractor": "catalog:",
+ "@types/node": "catalog:",
+ "eslint": "catalog:",
"typescript": "catalog:",
"unplugin-dts": "catalog:",
- "vitest": "catalog:",
"vite": "catalog:",
"vite-plugin-top-level-await": "catalog:",
- "vite-plugin-wasm": "catalog:"
+ "vite-plugin-wasm": "catalog:",
+ "vitest": "catalog:"
}
}
diff --git a/packages/ootle-wallet-daemon-signer/package.json b/packages/ootle-wallet-daemon-signer/package.json
index 3c20ecb..f9a9111 100644
--- a/packages/ootle-wallet-daemon-signer/package.json
+++ b/packages/ootle-wallet-daemon-signer/package.json
@@ -16,7 +16,8 @@
},
"scripts": {
"build": "tsc -b && vite build",
- "test": "vitest run"
+ "test": "vitest run",
+ "lint": "eslint"
},
"dependencies": {
"@tari-project/ootle": "workspace:",
@@ -27,6 +28,7 @@
"devDependencies": {
"@microsoft/api-extractor": "catalog:",
"@types/node": "catalog:",
+ "eslint": "catalog:",
"typescript": "catalog:",
"unplugin-dts": "catalog:",
"vite": "catalog:",
diff --git a/packages/ootle/package.json b/packages/ootle/package.json
index 82e1855..4f5bee0 100644
--- a/packages/ootle/package.json
+++ b/packages/ootle/package.json
@@ -16,20 +16,22 @@
},
"scripts": {
"build": "tsc -b && vite build",
- "test": "vitest run"
+ "test": "vitest run",
+ "lint": "eslint"
},
"dependencies": {
"@tari-project/ootle-ts-bindings": "catalog:",
"@tari-project/ootle-wasm": "catalog:"
},
"devDependencies": {
- "@types/node": "catalog:",
"@microsoft/api-extractor": "catalog:",
+ "@types/node": "catalog:",
+ "eslint": "catalog:",
"typescript": "catalog:",
"unplugin-dts": "catalog:",
- "vitest": "catalog:",
"vite": "catalog:",
"vite-plugin-top-level-await": "catalog:",
- "vite-plugin-wasm": "catalog:"
+ "vite-plugin-wasm": "catalog:",
+ "vitest": "catalog:"
}
}
diff --git a/packages/ootle/src/builder.ts b/packages/ootle/src/builder.ts
deleted file mode 100644
index 4d4adf8..0000000
--- a/packages/ootle/src/builder.ts
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2024 The Tari Project
-// SPDX-License-Identifier: BSD-3-Clause
-
-import type {
- Amount,
- ComponentAddress,
- MinotariBurnClaimProof,
- ClaimBurnOutputData,
- ConfidentialWithdrawProof,
- Instruction,
- InstructionArg,
- ResourceAddress,
- SubstateRequirement,
- UnsignedTransactionV1,
- PublishedTemplateAddress,
- WorkspaceOffsetId,
- AllocatableAddressType,
-} from "@tari-project/ootle-ts-bindings";
-import { Network } from "./network";
-import { parseWorkspaceStringKey } from "./helpers/workspace";
-
-/** A function that can be called on a published template. */
-export interface TariFunctionDefinition {
- templateAddress: PublishedTemplateAddress;
- functionName: string;
- args?: NamedArg[];
-}
-
-/** A method that can be called on a component. */
-export interface TariMethodDefinition {
- methodName: string;
- args?: NamedArg[];
- /** Call by component address. Mutually exclusive with `fromWorkspace`. */
- componentAddress?: ComponentAddress;
- /** Call the component stored under this workspace key. Mutually exclusive with `componentAddress`. */
- fromWorkspace?: string;
-}
-
-/**
- * A NamedArg is either:
- * - `{ Workspace: string }` — a named workspace reference resolved by the builder to a numeric ID
- * - `InstructionArg` — a fully-formed `{ Workspace: WorkspaceOffsetId }` or `{ Literal: string }`
- */
-export type NamedArg = { Workspace: string } | InstructionArg;
-
-/**
- * Wraps a literal value as an InstructionArg. The string representation is the
- * canonical format accepted by the Tari runtime; for complex types use the WASM
- * encoder to produce a properly BOR-encoded literal.
- */
-export function literalArg(value: Amount | string): InstructionArg {
- return { Literal: String(value) };
-}
-
-/**
- * Fluent builder for constructing UnsignedTransactionV1 objects.
- *
- * Only `buildUnsignedTransaction()` is exposed — signing is handled separately
- * via the `Signer` interface and `signTransaction` flow function.
- */
-export class TransactionBuilder {
- private unsignedTransaction: UnsignedTransactionV1;
- private allocatedIds: Map;
- private currentId: number;
-
- constructor(network: Network | number) {
- this.unsignedTransaction = {
- network: network,
- fee_instructions: [],
- instructions: [],
- inputs: [],
- min_epoch: null,
- max_epoch: null,
- dry_run: false,
- is_seal_signer_authorized: false,
- };
- this.allocatedIds = new Map();
- this.currentId = 0;
- }
-
- public static new(network: Network | number): TransactionBuilder {
- return new TransactionBuilder(network);
- }
-
- public callFunction(func: T, args: Exclude): this {
- const resolvedArgs = this.resolveArgs(args);
- return this.addInstruction({
- CallFunction: {
- address: func.templateAddress,
- function: func.functionName,
- args: resolvedArgs,
- },
- });
- }
-
- public callMethod(method: T, args: Exclude): this {
- if (!method.componentAddress && !method.fromWorkspace) {
- throw new Error("callMethod requires either componentAddress or fromWorkspace");
- }
- const call = method.componentAddress
- ? { Address: method.componentAddress }
- : { Workspace: this.requireNamedId(method.fromWorkspace!) };
- const resolvedArgs = this.resolveArgs(args);
- return this.addInstruction({
- CallMethod: {
- call,
- method: method.methodName,
- args: resolvedArgs,
- },
- });
- }
-
- public createAccount(ownerPublicKey: string, workspaceBucket?: string): this {
- const bucket_workspace_id = workspaceBucket ? this.getOffsetIdFromWorkspaceName(workspaceBucket) : null;
- return this.addInstruction({
- CreateAccount: {
- owner_public_key: ownerPublicKey,
- owner_rule: null,
- access_rules: null,
- bucket_workspace_id,
- },
- });
- }
-
- public createProof(account: ComponentAddress, resourceAddress: ResourceAddress): this {
- return this.addInstruction({
- CallMethod: {
- call: { Address: account },
- method: "create_proof_for_resource",
- args: [{ Literal: resourceAddress }],
- },
- });
- }
-
- public claimBurn(claim: MinotariBurnClaimProof, output_data: ClaimBurnOutputData): this {
- return this.addInstruction({
- ClaimBurn: { claim, output_data },
- });
- }
-
- public allocateAddress(allocatableType: AllocatableAddressType, workspaceIdName: string): this {
- const workspace_id = this.addNamedId(workspaceIdName);
- return this.addInstruction({
- AllocateAddress: { allocatable_type: allocatableType, workspace_id },
- });
- }
-
- /**
- * Saves the last instruction output to a named workspace variable.
- * The variable can be referenced by subsequent instructions using `{ Workspace: "name" }`.
- */
- public saveVar(name: string): this {
- const key = this.addNamedId(name);
- return this.addInstruction({
- PutLastInstructionOutputOnWorkspace: { key },
- });
- }
-
- /**
- * Adds a fee instruction that calls `pay_fee` on the given component.
- * The component must call `vault.pay_fee` and reveal enough confidential XTR.
- */
- public feeTransactionPayFromComponent(componentAddress: ComponentAddress, maxFee: Amount): this {
- return this.addFeeInstruction({
- CallMethod: {
- call: { Address: componentAddress },
- method: "pay_fee",
- args: [{ Literal: String(maxFee) }],
- },
- });
- }
-
- /**
- * Like `feeTransactionPayFromComponent` but uses a confidential withdraw proof.
- */
- public feeTransactionPayFromComponentConfidential(
- componentAddress: ComponentAddress,
- proof: ConfidentialWithdrawProof,
- ): this {
- return this.addFeeInstruction({
- CallMethod: {
- call: { Address: componentAddress },
- method: "pay_fee_confidential",
- args: [{ Literal: JSON.stringify(proof) }],
- },
- });
- }
-
- public dropAllProofsInWorkspace(): this {
- return this.addInstruction("DropAllProofsInWorkspace");
- }
-
- public addInstruction(instruction: Instruction): this {
- this.unsignedTransaction.instructions.push(instruction);
- return this;
- }
-
- public addFeeInstruction(instruction: Instruction): this {
- this.unsignedTransaction.fee_instructions.push(instruction);
- return this;
- }
-
- public withInstructions(instructions: Instruction[]): this {
- this.unsignedTransaction.instructions.push(...instructions);
- return this;
- }
-
- public withFeeInstructions(instructions: Instruction[]): this {
- this.unsignedTransaction.fee_instructions.push(...instructions);
- return this;
- }
-
- public withFeeInstructionsBuilder(builder: (b: TransactionBuilder) => TransactionBuilder): this {
- const inner = builder(new TransactionBuilder(this.unsignedTransaction.network));
- this.unsignedTransaction.fee_instructions = inner.unsignedTransaction.instructions;
- return this;
- }
-
- public addInput(input: SubstateRequirement): this {
- this.unsignedTransaction.inputs.push(input);
- return this;
- }
-
- public withInputs(inputs: SubstateRequirement[]): this {
- this.unsignedTransaction.inputs.push(...inputs);
- return this;
- }
-
- public withMinEpoch(minEpoch: number): this {
- this.unsignedTransaction.min_epoch = minEpoch;
- return this;
- }
-
- public withMaxEpoch(maxEpoch: number): this {
- this.unsignedTransaction.max_epoch = maxEpoch;
- return this;
- }
-
- public withUnsignedTransaction(unsignedTransaction: UnsignedTransactionV1): this {
- this.unsignedTransaction = unsignedTransaction;
- // Reset Workspace State
- this.allocatedIds = new Map();
- this.currentId = 0;
- return this;
- }
-
- public buildUnsignedTransaction(): UnsignedTransactionV1 {
- return {
- ...this.unsignedTransaction,
- instructions: [...this.unsignedTransaction.instructions],
- fee_instructions: [...this.unsignedTransaction.fee_instructions],
- inputs: [...this.unsignedTransaction.inputs],
- };
- }
-
- // Internal helpers
-
- private addNamedId(name: string): number {
- const id = this.currentId;
- this.allocatedIds.set(name, id);
- this.currentId += 1;
- return id;
- }
-
- private getNamedId(name: string): number | undefined {
- return this.allocatedIds.get(name);
- }
-
- private requireNamedId(name: string): number {
- const id = this.allocatedIds.get(name);
- if (id === undefined) {
- throw new Error(`No workspace variable named "${name}" has been defined`);
- }
- return id;
- }
-
- private getOffsetIdFromWorkspaceName(name: string): WorkspaceOffsetId {
- const parsed = parseWorkspaceStringKey(name);
- const id = this.getNamedId(parsed.name);
- if (id === undefined) {
- throw new Error(`No workspace variable named "${parsed.name}" has been defined`);
- }
- return { id, offset: parsed.offset };
- }
-
- private resolveArgs(args: NamedArg[]): InstructionArg[] {
- return args.map((arg): InstructionArg => {
- if (
- typeof arg === "object" &&
- arg !== null &&
- "Workspace" in arg &&
- typeof (arg as { Workspace: unknown }).Workspace === "string"
- ) {
- const workspaceId = this.getOffsetIdFromWorkspaceName((arg as { Workspace: string }).Workspace);
- return { Workspace: workspaceId };
- }
- return arg as InstructionArg;
- });
- }
-}
diff --git a/packages/ootle/src/builtin-templates.ts b/packages/ootle/src/builtin-templates.ts
index e498f99..ae7a472 100644
--- a/packages/ootle/src/builtin-templates.ts
+++ b/packages/ootle/src/builtin-templates.ts
@@ -8,7 +8,7 @@ import type {
PublishedTemplateAddress,
UnsignedTransactionV1,
} from "@tari-project/ootle-ts-bindings";
-import { TransactionBuilder } from "./builder";
+import { TransactionBuilder } from "./transaction/builder";
import type { Network } from "./network";
/**
diff --git a/packages/ootle/src/index.ts b/packages/ootle/src/index.ts
index 399da5f..9a14197 100644
--- a/packages/ootle/src/index.ts
+++ b/packages/ootle/src/index.ts
@@ -4,8 +4,8 @@
export { Network } from "./network";
export type { Signer } from "./signer";
export type { Provider } from "./provider";
-export { TransactionBuilder, literalArg } from "./builder";
-export type { TariFunctionDefinition, TariMethodDefinition, NamedArg } from "./builder";
+export { TransactionBuilder, literalArg } from "./transaction/builder";
+export type { TariFunctionDefinition, TariMethodDefinition, NamedArg } from "./transaction/builder";
export {
resolveTransaction,
signTransaction,
diff --git a/packages/ootle/src/stealth-transfer.ts b/packages/ootle/src/stealth-transfer.ts
index 1183225..12bddaa 100644
--- a/packages/ootle/src/stealth-transfer.ts
+++ b/packages/ootle/src/stealth-transfer.ts
@@ -3,7 +3,7 @@
import type { ComponentAddress, ResourceAddress, Amount, UnsignedTransactionV1 } from "@tari-project/ootle-ts-bindings";
import type { StealthOutputStatementFactory, StealthTransferStatement } from "./stealth";
-import { TransactionBuilder } from "./builder";
+import { TransactionBuilder } from "./transaction/builder";
import type { Network } from "./network";
/**
diff --git a/packages/ootle/src/transaction.ts b/packages/ootle/src/transaction.ts
index 7617741..371b1b8 100644
--- a/packages/ootle/src/transaction.ts
+++ b/packages/ootle/src/transaction.ts
@@ -14,7 +14,14 @@ import type {
import type { Provider } from "./provider";
import type { Signer } from "./signer";
import type { WatchOptions } from "./types";
-import { borEncodeTransaction, generateKeypair, hashUnsignedTransaction, schnorrSign } from "@tari-project/ootle-wasm";
+import {
+ borEncodeTransaction,
+ generateKeypair,
+ generateOotleSecretKey,
+ hashUnsignedTransaction,
+ publicKeyFromSecretKey,
+ schnorrSign,
+} from "@tari-project/ootle-wasm";
import { toHexStr } from "./helpers";
/**
@@ -33,23 +40,25 @@ export async function resolveTransaction(
* Collects signatures from all provided signers and assembles a signed Transaction.
*/
export async function signTransaction(signers: Signer[], unsignedTx: UnsignedTransactionV1): Promise {
- const { secret_key, public_key: seal_signer_public_key } = generateKeypair();
+ const { owner_key, view_key } = generateOotleSecretKey();
+
+ const seal_signer_public_key = (await signers[0]?.getPublicKey()) ?? publicKeyFromSecretKey(view_key);
+
const allSignatures: TransactionSignature[] = [];
for (const signer of signers) {
const sigs = await signer.signTransaction(unsignedTx);
allSignatures.push(...sigs);
}
+ console.debug("allSignatures= ", allSignatures);
const body: UnsealedTransactionV1 = {
transaction: unsignedTx,
signatures: allSignatures,
};
const hash = hashUnsignedTransaction(JSON.stringify(unsignedTx), seal_signer_public_key);
+ const s = schnorrSign(owner_key, hash);
- const s = schnorrSign(secret_key, hash);
-
- // TODO - come back to check check these toString()s after types align
const seal_signature = {
public_key: toHexStr(seal_signer_public_key),
signature: {
@@ -57,6 +66,7 @@ export async function signTransaction(signers: Signer[], unsignedTx: UnsignedTra
signature: toHexStr(s.signature),
},
};
+
return {
V1: {
body,
diff --git a/packages/ootle/src/transaction/builder.ts b/packages/ootle/src/transaction/builder.ts
new file mode 100644
index 0000000..b54c630
--- /dev/null
+++ b/packages/ootle/src/transaction/builder.ts
@@ -0,0 +1,510 @@
+// Copyright 2024 The Tari Project
+// SPDX-License-Identifier: BSD-3-Clause
+
+import type {
+ Amount,
+ ComponentAddress,
+ MinotariBurnClaimProof,
+ ClaimBurnOutputData,
+ ConfidentialWithdrawProof,
+ Instruction,
+ InstructionArg,
+ ResourceAddress,
+ SubstateRequirement,
+ UnsignedTransactionV1,
+ PublishedTemplateAddress,
+ WorkspaceOffsetId,
+ AllocatableAddressType,
+ Transaction,
+ UnsignedTransaction,
+ TransactionSignature,
+} from "@tari-project/ootle-ts-bindings";
+import { Network } from "../network";
+import { parseWorkspaceStringKey, toHexStr } from "../helpers";
+import { TransactionRequest } from "./request";
+
+/** A function that can be called on a published template. */
+export interface TariFunctionDefinition {
+ templateAddress: PublishedTemplateAddress;
+ functionName: string;
+ args?: NamedArg[];
+}
+
+/** A method that can be called on a component. */
+export interface TariMethodDefinition {
+ methodName: string;
+ args?: NamedArg[];
+ /** Call by component address. Mutually exclusive with `fromWorkspace`. */
+ componentAddress?: ComponentAddress;
+ /** Call the component stored under this workspace key. Mutually exclusive with `componentAddress`. */
+ fromWorkspace?: string;
+}
+
+/**
+ * A NamedArg is either:
+ * - `{ Workspace: string }` — a named workspace reference resolved by the builder to a numeric ID
+ * - `InstructionArg` — a fully-formed `{ Workspace: WorkspaceOffsetId }` or `{ Literal: string }`
+ */
+export type NamedArg = { Workspace: string } | InstructionArg;
+
+/**
+ * Wraps a literal value as an InstructionArg. The string representation is the
+ * canonical format accepted by the Tari runtime; for complex types use the WASM
+ * encoder to produce a properly BOR-encoded literal.
+ */
+export function literalArg(value: Amount | string): InstructionArg {
+ return { Literal: String(value) };
+}
+
+/**
+ * This interface defines the constructor for a Transaction object.
+ * It is used to create a new signed Transaction instance from an UnsignedTransaction and an array of TransactionSignatures.
+ * The constructor takes an UnsignedTransaction and an array of TransactionSignatures as parameters.
+ * @public
+ */
+
+export interface TransactionConstructor {
+ /**
+ * Creates a new {@link Transaction} instance.
+ *
+ * @param unsignedTransaction - The UnsignedTransaction to create the Transaction from.
+ * @param signatures - An array of {@link TransactionSignature} objects, each containing:
+ * - `public_key`: A string representing a valid 32-byte Ristretto255 public key.
+ * - `signature`: An object containing:
+ * - `public_nonce`: A string representing the public nonce part of the Schnorr signature.
+ * - **NOTE:** Must be a valid 32-byte Ristretto255 public key, serialized as a hex string.
+ * - `signature`: A string representing the actual Schnorr signature scalar.
+ * - **NOTE:** Must be a valid 32-byte Schnorr signature scalar, serialized as a hex string.
+ *
+ * All fields must be validly encoded, canonical Ristretto255 public keys or Schnorr signature components in the correct format and length.
+ * Any deviation (e.g., wrong length, invalid encoding) will result in errors or failed signature verification.
+ *
+ * @returns A new Transaction instance.
+ */
+ new (unsignedTransaction: UnsignedTransaction, signatures: TransactionSignature[]): Transaction;
+}
+
+export interface Builder {
+ /**
+ * Adds a function call to the transaction, allowing the developer to invoke a function on a published template. This implements {@link TariFunctionDefinition}
+ * @param func - The function definition to call, which includes the function name, arguments, and template address.
+ * @param args - The arguments to pass to the function. These should be provided as an array of {@link NamedArg} objects. Optional.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ */
+ callFunction(func: T, args: Exclude): this;
+
+ /**
+ * Adds a method call to the transaction, allowing the developer to invoke a method on a component. This implements {@link TariMethodDefinition}
+ * @param method - The method definition to call, which includes the method name, arguments, and component address.
+ * @param args - The arguments to pass to the method. These should be provided as an array of {@link NamedArg} objects. Optional.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ */
+ callMethod(method: T, args: Exclude): this;
+
+ /**
+ * Adds an instruction to create a new account in the Tari Network to the transaction.
+ * @param ownerPublicKey - The public key of the account owner, represented as a 64-character hexadecimal string.
+ * @param workspaceBucket - An optional workspace bucket name to associate with the account. If provided, it will be used to create a workspace for the account. Allows for referencing the account elsewhere in the transaction without requiring it's address.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ * @example
+ */
+ createAccount(ownerPublicKey: Uint8Array, workspaceBucket?: string): this;
+
+ /**
+ * Creates an internal proof that can be used to prove ownership of a resource in a component's account.
+ * @param account - The address of the component account that owns the resource. represented as a 64-character hexadecimal string, prepended with "component_".
+ * @param resourceAddress - The address of the resource to create a proof for, represented as a 64-character hexadecimal string, prepended with "resource_".
+ * @returns The current instance of the Builder, allowing for method chaining.
+ */
+ createProof(account: ComponentAddress, resourceAddress: ResourceAddress): this;
+
+ /**
+ * Creates a variable in the workspace to store the output of the last instruction, which can be used later in the transaction.
+ * @param key - The name of the variable to save the last instruction's output to.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ * @remarks
+ * Must be used after an instruction that produces an output, such as a function call or method call, and before any subsequent instructions that may use the saved variable.
+ */
+ saveVar(key: string): this;
+
+ /**
+ * Calls a method to remove all proofs in the current workspace.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ * @remarks
+ * Any proof references saved in the workspace via saveVar will be removed, invalidating any subsequent instructions that call on the variable.
+ */
+ dropAllProofsInWorkspace(): this;
+
+ /**
+ * Adds a `ClaimBurn` instruction to the transaction, allowing the user to claim a previously burned confidential output.
+ *
+ * @param claim - A {@link MinotariBurnClaimProof} object containing cryptographic proofs that authorize the claim. This includes the burn output address, ownership proof, range proof, and optional withdraw proof.
+ * @param output_data
+ * @returns The current instance of the Builder, enabling method chaining.
+ * @remarks
+ * - The `MinotariBurnClaimProof` must be constructed off-chain using valid cryptographic data.
+ * - If `withdraw_proof` is required by the burn process, it must be included.
+ * - This method should be used only when recovering burned confidential resources.
+ */
+ claimBurn(claim: MinotariBurnClaimProof, output_data: ClaimBurnOutputData): this;
+
+ addInput(inputObject: SubstateRequirement): this;
+ addSignatures(signatures: TransactionSignature[]): this;
+
+ /** Adds a raw instruction to the transaction.
+ *
+ * @param instruction - A fully-formed {@link Instruction} object, such as `CreateAccount`, `CallMethod`, `ClaimBurn`, etc.
+ *
+ * @returns The current instance of the Builder for method chaining.
+ *
+ * @remarks
+ * This method allows advanced or low-level access to the instruction set used in the Tari transaction engine.
+ * It should typically be used when:
+ * - A specific instruction is not exposed via a dedicated builder method (e.g. `EmitLog`, `ClaimValidatorFees`)
+ * - You need to construct instructions dynamically at runtime (e.g. from config files or user input)
+ * - You require more control over optional fields not exposed in convenience methods (e.g. custom `owner_rule`)
+ * - You are working with experimental or less-common instructions
+ *
+ * For common operations like creating accounts or calling methods, prefer high-level builder methods
+ * such as `createAccount()` or `callMethod()` for better readability and type safety.
+ */
+ addInstruction(instruction: Instruction): this;
+
+ addFeeInstruction(instruction: Instruction): this;
+
+ /**
+ * Allows for the addition of a condition to the transaction that requires the minimum epoch in which the transaction can be executed. Transaction fails if executed before this epoch.
+ * @param minEpoch - The minimum epoch in which the transaction can be executed. If not set, the transaction can be executed in any epoch.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ */
+ withMinEpoch(minEpoch: number): this;
+
+ /**
+ * Allows for the addition of a condition to the transaction that requires the maximum epoch in which the transaction can be executed. Transaction fails if executed after this epoch.
+ * @param maxEpoch - The maximum epoch in which the transaction can be executed. If not set, the transaction can be executed in any epoch.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ */
+ withMaxEpoch(maxEpoch: number): this;
+
+ /**
+ * Adds a substate requirement to the transaction, which is used to specify the inputs required for the transaction.
+ * @param inputs - An array of {@link SubstateRequirement} objects that define the inputs required for the transaction, consisting of substate IDs and optional versions. Typically, null version is used to indicate that any version of the substate is acceptable.
+ * @returns The current instance of the Builder, allowing for method
+ */
+
+ withInputs(inputs: SubstateRequirement[]): this;
+
+ /**
+ * Similar to {@link addInstruction}, but allows for adding multiple instructions at once.
+ * @param instructions - An array of {@link Instruction} objects to add to the transaction. These instructions will be executed in the order they are added.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ */
+
+ withInstructions(instructions: Instruction[]): this;
+
+ /**
+ * Similar to {@link addFeeInstruction}, but allows for adding multiple fee instructions at once.
+ * This is useful for complex transactions that require multiple fee instructions.
+ * @param instructions - An array of {@link Instruction} objects to add as fee instructions. These instructions will be executed in the order they are added.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ */
+ withFeeInstructions(instructions: Instruction[]): this;
+
+ withFeeInstructionsBuilder(builder: (builder: TransactionBuilder) => this): this;
+
+ /**
+ * Allows for setting an existing unsigned transaction to build upon. This is useful for modifying or extending an existing unsigned transaction.
+ * @param unsignedTransaction - An {@link UnsignedTransactionV1} object representing the base transaction to build upon.
+ * @returns The current instance of the Builder, allowing for method chaining.
+ * @remarks
+ * Using withUnsignedTransaction() overwrites the builder’s current unsigned transaction state with the provided one.
+ * Useful in cases where the unsigned transaction has been (partially) constructed already.
+ */
+ withUnsignedTransaction(unsignedTransaction: UnsignedTransactionV1): this;
+
+ /**
+ * Adds a method for specifying the component (typically an Account) that pays the transaction fee.
+ *
+ * @param componentAddress
+ * @param maxFee
+ * @remarks
+ * - The component must have a method `pay_fee` that calls `vault.pay_fee` with enough revealed confidential XTR.
+ * - Calls to vault.pay_fee lock up the `maxFee` amount for the duration of the transaction.
+ */
+ feeTransactionPayFromComponent(componentAddress: ComponentAddress, maxFee: Amount): this;
+
+ /**
+ * Similar to {@link feeTransactionPayFromComponent}, but allows for paying the transaction fee using a confidential withdraw proof.
+ *
+ * @param componentAddress - The address of the component from which to pay the fee, represented as a 64-character hexadecimal string, optionally prefixed by "component_".
+ * @param proof - A {@link ConfidentialWithdrawProof} object containing the necessary cryptographic proofs to authorize the fee payment.
+ */
+ feeTransactionPayFromComponentConfidential(
+ componentAddress: ComponentAddress,
+ proof: ConfidentialWithdrawProof,
+ ): this;
+
+ buildUnsignedTransaction(): UnsignedTransactionV1;
+
+ build(): Transaction;
+}
+
+/**
+ * Fluent builder for constructing UnsignedTransactionV1 objects.
+ *
+ * Only `buildUnsignedTransaction()` is exposed — signing is handled separately
+ * via the `Signer` interface and `signTransaction` flow function.
+ */
+export class TransactionBuilder implements Builder {
+ private unsignedTransaction: UnsignedTransactionV1;
+ private readonly signatures: TransactionSignature[];
+ private allocatedIds: Map;
+ private currentId: number;
+
+ constructor(network: Network | number) {
+ this.unsignedTransaction = {
+ network,
+ fee_instructions: [],
+ instructions: [],
+ inputs: [],
+ min_epoch: null,
+ max_epoch: null,
+ dry_run: false,
+ is_seal_signer_authorized: false,
+ };
+ this.signatures = [];
+ this.allocatedIds = new Map();
+ this.currentId = 0;
+ }
+
+ public static new(network: Network | number): TransactionBuilder {
+ return new TransactionBuilder(network);
+ }
+
+ public addSignatures(signatures: TransactionSignature[]): this {
+ this.signatures.push(...signatures);
+ return this;
+ }
+ public callFunction(func: T, args: Exclude): this {
+ const resolvedArgs = this.resolveArgs(args);
+ return this.addInstruction({
+ CallFunction: {
+ address: func.templateAddress,
+ function: func.functionName,
+ args: resolvedArgs,
+ },
+ });
+ }
+
+ public callMethod(method: T, args: Exclude): this {
+ if (!method.componentAddress && !method.fromWorkspace) {
+ throw new Error("callMethod requires either componentAddress or fromWorkspace");
+ }
+ const call = method.componentAddress
+ ? { Address: method.componentAddress }
+ : { Workspace: this.requireNamedId(method.fromWorkspace) };
+ const resolvedArgs = this.resolveArgs(args);
+ return this.addInstruction({
+ CallMethod: {
+ call,
+ method: method.methodName,
+ args: resolvedArgs,
+ },
+ });
+ }
+
+ public createAccount(ownerPublicKey: Uint8Array, workspaceBucket?: string): this {
+ const bucket_workspace_id = workspaceBucket ? this.getOffsetIdFromWorkspaceName(workspaceBucket) : null;
+ return this.addInstruction({
+ CreateAccount: {
+ owner_public_key: toHexStr(ownerPublicKey),
+ owner_rule: null,
+ access_rules: null,
+ bucket_workspace_id,
+ },
+ });
+ }
+
+ public createProof(account: ComponentAddress, resourceAddress: ResourceAddress): this {
+ return this.addInstruction({
+ CallMethod: {
+ call: { Address: account },
+ method: "create_proof_for_resource",
+ args: [{ Literal: resourceAddress }],
+ },
+ });
+ }
+
+ public claimBurn(claim: MinotariBurnClaimProof, output_data: ClaimBurnOutputData): this {
+ return this.addInstruction({
+ ClaimBurn: { claim, output_data },
+ });
+ }
+
+ public allocateAddress(allocatableType: AllocatableAddressType, workspaceIdName: string): this {
+ const workspace_id = this.addNamedId(workspaceIdName);
+ return this.addInstruction({
+ AllocateAddress: { allocatable_type: allocatableType, workspace_id },
+ });
+ }
+
+ /**
+ * Saves the last instruction output to a named workspace variable.
+ * The variable can be referenced by subsequent instructions using `{ Workspace: "name" }`.
+ */
+ public saveVar(name: string): this {
+ const key = this.addNamedId(name);
+ return this.addInstruction({
+ PutLastInstructionOutputOnWorkspace: { key },
+ });
+ }
+
+ /**
+ * Adds a fee instruction that calls `pay_fee` on the given component.
+ * The component must call `vault.pay_fee` and reveal enough confidential XTR.
+ */
+ public feeTransactionPayFromComponent(componentAddress: ComponentAddress, maxFee: Amount): this {
+ return this.addFeeInstruction({
+ CallMethod: {
+ call: { Address: componentAddress },
+ method: "pay_fee",
+ args: [{ Literal: String(maxFee) }],
+ },
+ });
+ }
+
+ /**
+ * Like `feeTransactionPayFromComponent` but uses a confidential withdraw proof.
+ */
+ public feeTransactionPayFromComponentConfidential(
+ componentAddress: ComponentAddress,
+ proof: ConfidentialWithdrawProof,
+ ): this {
+ return this.addFeeInstruction({
+ CallMethod: {
+ call: { Address: componentAddress },
+ method: "pay_fee_confidential",
+ args: [{ Literal: JSON.stringify(proof) }],
+ },
+ });
+ }
+
+ public dropAllProofsInWorkspace(): this {
+ return this.addInstruction("DropAllProofsInWorkspace");
+ }
+
+ public addInstruction(instruction: Instruction): this {
+ this.unsignedTransaction.instructions.push(instruction);
+ return this;
+ }
+
+ public addFeeInstruction(instruction: Instruction): this {
+ this.unsignedTransaction.fee_instructions.push(instruction);
+ return this;
+ }
+
+ public withInstructions(instructions: Instruction[]): this {
+ this.unsignedTransaction.instructions.push(...instructions);
+ return this;
+ }
+
+ public withFeeInstructions(instructions: Instruction[]): this {
+ this.unsignedTransaction.fee_instructions.push(...instructions);
+ return this;
+ }
+
+ public withFeeInstructionsBuilder(builder: (b: TransactionBuilder) => TransactionBuilder): this {
+ const inner = builder(new TransactionBuilder(this.unsignedTransaction.network));
+ this.unsignedTransaction.fee_instructions = inner.unsignedTransaction.instructions;
+ return this;
+ }
+
+ public addInput(input: SubstateRequirement): this {
+ this.unsignedTransaction.inputs.push(input);
+ return this;
+ }
+
+ public withInputs(inputs: SubstateRequirement[]): this {
+ this.unsignedTransaction.inputs.push(...inputs);
+ return this;
+ }
+
+ public withMinEpoch(minEpoch: number): this {
+ this.unsignedTransaction.min_epoch = minEpoch;
+ return this;
+ }
+
+ public withMaxEpoch(maxEpoch: number): this {
+ this.unsignedTransaction.max_epoch = maxEpoch;
+ return this;
+ }
+
+ public withUnsignedTransaction(unsignedTransaction: UnsignedTransactionV1): this {
+ this.unsignedTransaction = unsignedTransaction;
+ // Reset Workspace State
+ this.allocatedIds = new Map();
+ this.currentId = 0;
+ return this;
+ }
+
+ public buildUnsignedTransaction(): UnsignedTransactionV1 {
+ return {
+ ...this.unsignedTransaction,
+ instructions: [...this.unsignedTransaction.instructions],
+ fee_instructions: [...this.unsignedTransaction.fee_instructions],
+ inputs: [...this.unsignedTransaction.inputs],
+ };
+ }
+
+ public build(): Transaction {
+ return new TransactionRequest(this.buildUnsignedTransaction(), this.signatures);
+ }
+
+ // Internal helpers
+
+ private addNamedId(name: string): number {
+ const id = this.currentId;
+ this.allocatedIds.set(name, id);
+ this.currentId += 1;
+ return id;
+ }
+
+ private getNamedId(name: string): number | undefined {
+ return this.allocatedIds.get(name);
+ }
+
+ private requireNamedId(name?: string): number {
+ if (name === undefined) {
+ throw new Error(`Workspace name is required for this operation.`);
+ }
+ const id = this.allocatedIds.get(name);
+ if (id === undefined) {
+ throw new Error(`No workspace variable named "${name}" has been defined`);
+ }
+ return id;
+ }
+
+ private getOffsetIdFromWorkspaceName(name: string): WorkspaceOffsetId {
+ const parsed = parseWorkspaceStringKey(name);
+ const id = this.getNamedId(parsed.name);
+ if (id === undefined) {
+ throw new Error(`No workspace variable named "${parsed.name}" has been defined`);
+ }
+ return { id, offset: parsed.offset };
+ }
+
+ private resolveArgs(args: NamedArg[]): InstructionArg[] {
+ return args.map((arg): InstructionArg => {
+ if (
+ typeof arg === "object" &&
+ arg !== null &&
+ "Workspace" in arg &&
+ typeof (arg as { Workspace: unknown }).Workspace === "string"
+ ) {
+ const workspaceId = this.getOffsetIdFromWorkspaceName((arg as { Workspace: string }).Workspace);
+ return { Workspace: workspaceId };
+ }
+ return arg as InstructionArg;
+ });
+ }
+}
diff --git a/packages/ootle/src/transaction/index.ts b/packages/ootle/src/transaction/index.ts
new file mode 100644
index 0000000..7689833
--- /dev/null
+++ b/packages/ootle/src/transaction/index.ts
@@ -0,0 +1,10 @@
+export {
+ TariFunctionDefinition,
+ TariMethodDefinition,
+ NamedArg,
+ literalArg,
+ TransactionConstructor,
+ Builder,
+ TransactionBuilder,
+} from "./builder";
+export { TransactionRequest } from "./request";
diff --git a/packages/ootle/src/transaction/request.ts b/packages/ootle/src/transaction/request.ts
new file mode 100644
index 0000000..1b6f962
--- /dev/null
+++ b/packages/ootle/src/transaction/request.ts
@@ -0,0 +1,74 @@
+import {
+ Epoch,
+ Instruction,
+ SubstateRequirement,
+ Transaction,
+ UnsignedTransactionV1,
+ TransactionSignature,
+ VersionedSubstateId,
+ TransactionV1,
+} from "@tari-project/ootle-ts-bindings";
+
+///TODO this implementation is not fully done, see:
+/// https://github.com/tari-project/tari-ootle/blob/development/dan_layer/transaction/src/transaction.rs
+export class TransactionRequest implements Transaction {
+ id: string;
+ V1: TransactionV1;
+
+ constructor(unsignedTransaction: UnsignedTransactionV1, signatures: TransactionSignature[]) {
+ this.id = "";
+ this.V1 = {
+ body: {
+ transaction: unsignedTransaction,
+ signatures,
+ },
+ seal_signature: {
+ public_key: "",
+ signature: {
+ public_nonce: "",
+ signature: "",
+ },
+ },
+ };
+ }
+
+ withFilledInputs(filled_inputs: Array): this {
+ return { ...this, filled_inputs };
+ }
+
+ getUnsignedTransaction(): UnsignedTransactionV1 {
+ return this.V1.body.transaction;
+ }
+
+ getFeeInstructions(): Instruction[] {
+ return this.V1.body.transaction.fee_instructions;
+ }
+
+ getInstructions(): Instruction[] {
+ return this.V1.body.transaction.instructions;
+ }
+
+ getSignatures(): TransactionSignature[] {
+ return this.V1.body.signatures;
+ }
+
+ getInputs(): SubstateRequirement[] {
+ return this.V1.body.transaction.inputs;
+ }
+
+ getMinEpoch(): Epoch | null {
+ return this.V1.body.transaction.min_epoch;
+ }
+
+ getMaxEpoch(): Epoch | null {
+ return this.V1.body.transaction.max_epoch;
+ }
+
+ setId(id: string): void {
+ this.id = id;
+ }
+
+ getId(): string {
+ return this.id;
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9c36a73..4e0ef89 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,14 +13,14 @@ catalogs:
specifier: ^7.57.7
version: 7.57.7
'@tari-project/indexer-client':
- specifier: ^1.2.1
- version: 1.2.1
+ specifier: ^1.3.1
+ version: 1.3.1
'@tari-project/ootle-ts-bindings':
- specifier: ^1.30.0
- version: 1.30.0
+ specifier: ^1.30.1
+ version: 1.30.1
'@tari-project/ootle-wasm':
- specifier: ^0.3.0
- version: 0.3.0
+ specifier: ^0.3.1
+ version: 0.3.1
'@tari-project/wallet_jrpc_client':
specifier: ^1.14.0
version: 1.14.0
@@ -28,8 +28,8 @@ catalogs:
specifier: ^25.5.0
version: 25.5.0
eslint:
- specifier: ^10.0.3
- version: 10.0.3
+ specifier: ^10.1.0
+ version: 10.1.0
eslint-config-prettier:
specifier: ^10.1.8
version: 10.1.8
@@ -73,16 +73,16 @@ importers:
devDependencies:
'@eslint/js':
specifier: 'catalog:'
- version: 10.0.1(eslint@10.0.3(jiti@2.6.1))
+ version: 10.0.1(eslint@10.1.0(jiti@2.6.1))
'@types/node':
specifier: 'catalog:'
version: 25.5.0
eslint:
specifier: 'catalog:'
- version: 10.0.3(jiti@2.6.1)
+ version: 10.1.0(jiti@2.6.1)
eslint-config-prettier:
specifier: 'catalog:'
- version: 10.1.8(eslint@10.0.3(jiti@2.6.1))
+ version: 10.1.8(eslint@10.1.0(jiti@2.6.1))
knip:
specifier: 'catalog:'
version: 6.0.0
@@ -100,7 +100,7 @@ importers:
version: 5.9.3
typescript-eslint:
specifier: 'catalog:'
- version: 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
+ version: 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)
docs:
dependencies:
@@ -130,7 +130,7 @@ importers:
examples/connect-button:
dependencies:
'@tari-project/ootle-wallet-daemon-signer':
- specifier: workspace:*
+ specifier: link:../../packages/ootle-wallet-daemon-signer
version: link:../../packages/ootle-wallet-daemon-signer
react:
specifier: ^19.0.0
@@ -149,11 +149,11 @@ importers:
specifier: ^4.0.0
version: 4.7.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))
eslint:
- specifier: 'catalog:'
- version: 10.0.3(jiti@2.6.1)
+ specifier: ^10.1.0
+ version: 10.1.0(jiti@2.6.1)
eslint-plugin-react-hooks:
specifier: ^7.0.1
- version: 7.0.1(eslint@10.0.3(jiti@2.6.1))
+ version: 7.0.1(eslint@10.1.0(jiti@2.6.1))
typescript:
specifier: ~5.8.0
version: 5.8.3
@@ -170,10 +170,10 @@ importers:
examples/indexer-explorer:
dependencies:
'@tari-project/ootle':
- specifier: workspace:*
+ specifier: link:../../packages/ootle
version: link:../../packages/ootle
'@tari-project/ootle-indexer':
- specifier: workspace:*
+ specifier: link:../../packages/ootle-indexer
version: link:../../packages/ootle-indexer
react:
specifier: ^19.0.0
@@ -192,11 +192,11 @@ importers:
specifier: ^4.0.0
version: 4.7.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))
eslint:
- specifier: 'catalog:'
- version: 10.0.3(jiti@2.6.1)
+ specifier: ^10.1.0
+ version: 10.1.0(jiti@2.6.1)
eslint-plugin-react-hooks:
specifier: ^7.0.1
- version: 7.0.1(eslint@10.0.3(jiti@2.6.1))
+ version: 7.0.1(eslint@10.1.0(jiti@2.6.1))
typescript:
specifier: ~5.8.0
version: 5.8.3
@@ -213,10 +213,10 @@ importers:
examples/template-inspector:
dependencies:
'@tari-project/ootle':
- specifier: workspace:*
+ specifier: link:../../packages/ootle
version: link:../../packages/ootle
'@tari-project/ootle-indexer':
- specifier: workspace:*
+ specifier: link:../../packages/ootle-indexer
version: link:../../packages/ootle-indexer
react:
specifier: ^19.0.0
@@ -235,11 +235,11 @@ importers:
specifier: ^4.0.0
version: 4.7.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))
eslint:
- specifier: 'catalog:'
- version: 10.0.3(jiti@2.6.1)
+ specifier: ^10.1.0
+ version: 10.1.0(jiti@2.6.1)
eslint-plugin-react-hooks:
specifier: ^7.0.1
- version: 7.0.1(eslint@10.0.3(jiti@2.6.1))
+ version: 7.0.1(eslint@10.1.0(jiti@2.6.1))
typescript:
specifier: ~5.8.0
version: 5.8.3
@@ -253,14 +253,60 @@ importers:
specifier: ^3.6.0
version: 3.6.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))
+ examples/transactions:
+ dependencies:
+ '@tari-project/ootle':
+ specifier: link:../../packages/ootle
+ version: link:../../packages/ootle
+ '@tari-project/ootle-indexer':
+ specifier: link:../../packages/ootle-indexer
+ version: link:../../packages/ootle-indexer
+ '@tari-project/ootle-secret-key-wallet':
+ specifier: link:../../packages/ootle-secret-key-wallet
+ version: link:../../packages/ootle-secret-key-wallet
+ react:
+ specifier: ^19.2.4
+ version: 19.2.4
+ react-dom:
+ specifier: ^19.2.4
+ version: 19.2.4(react@19.2.4)
+ devDependencies:
+ '@types/react':
+ specifier: ^19.2.14
+ version: 19.2.14
+ '@types/react-dom':
+ specifier: ^19.2.3
+ version: 19.2.3(@types/react@19.2.14)
+ '@vitejs/plugin-react':
+ specifier: ^4.7.0
+ version: 4.7.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))
+ eslint:
+ specifier: ^10.1.0
+ version: 10.1.0(jiti@2.6.1)
+ eslint-plugin-react-hooks:
+ specifier: ^7.0.1
+ version: 7.0.1(eslint@10.1.0(jiti@2.6.1))
+ typescript:
+ specifier: ~5.8.3
+ version: 5.8.3
+ vite:
+ specifier: ^6.4.1
+ version: 6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2)
+ vite-plugin-top-level-await:
+ specifier: ^1.6.0
+ version: 1.6.0(rollup@4.59.0)(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))
+ vite-plugin-wasm:
+ specifier: ^3.6.0
+ version: 3.6.0(vite@6.4.1(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(yaml@2.8.2))
+
packages/ootle:
dependencies:
'@tari-project/ootle-ts-bindings':
specifier: 'catalog:'
- version: 1.30.0
+ version: 1.30.1
'@tari-project/ootle-wasm':
specifier: 'catalog:'
- version: 0.3.0
+ version: 0.3.1
devDependencies:
'@microsoft/api-extractor':
specifier: 'catalog:'
@@ -268,6 +314,9 @@ importers:
'@types/node':
specifier: 'catalog:'
version: 25.5.0
+ eslint:
+ specifier: 'catalog:'
+ version: 10.1.0(jiti@2.6.1)
typescript:
specifier: 'catalog:'
version: 5.9.3
@@ -291,13 +340,13 @@ importers:
dependencies:
'@tari-project/indexer-client':
specifier: 'catalog:'
- version: 1.2.1
+ version: 1.3.1
'@tari-project/ootle':
specifier: 'workspace:'
version: link:../ootle
'@tari-project/ootle-ts-bindings':
specifier: 'catalog:'
- version: 1.30.0
+ version: 1.30.1
devDependencies:
'@microsoft/api-extractor':
specifier: 'catalog:'
@@ -305,6 +354,9 @@ importers:
'@types/node':
specifier: 'catalog:'
version: 25.5.0
+ eslint:
+ specifier: 'catalog:'
+ version: 10.1.0(jiti@2.6.1)
typescript:
specifier: 'catalog:'
version: 5.9.3
@@ -331,10 +383,10 @@ importers:
version: link:../ootle
'@tari-project/ootle-ts-bindings':
specifier: 'catalog:'
- version: 1.30.0
+ version: 1.30.1
'@tari-project/ootle-wasm':
specifier: 'catalog:'
- version: 0.3.0
+ version: 0.3.1
devDependencies:
'@microsoft/api-extractor':
specifier: 'catalog:'
@@ -342,6 +394,9 @@ importers:
'@types/node':
specifier: 'catalog:'
version: 25.5.0
+ eslint:
+ specifier: 'catalog:'
+ version: 10.1.0(jiti@2.6.1)
typescript:
specifier: 'catalog:'
version: 5.9.3
@@ -368,7 +423,7 @@ importers:
version: link:../ootle
'@tari-project/ootle-ts-bindings':
specifier: 'catalog:'
- version: 1.30.0
+ version: 1.30.1
'@tari-project/wallet_jrpc_client':
specifier: 'catalog:'
version: 1.14.0
@@ -382,6 +437,9 @@ importers:
'@types/node':
specifier: 'catalog:'
version: 25.5.0
+ eslint:
+ specifier: 'catalog:'
+ version: 10.1.0(jiti@2.6.1)
typescript:
specifier: 'catalog:'
version: 5.9.3
@@ -1787,14 +1845,14 @@ packages:
'@swc/wasm@1.15.18':
resolution: {integrity: sha512-zeSORFArxqUwfVMTRHu8AN9k9LlfSn0CKDSzLhJDITpgLoS0xpnocxsgMjQjUcVYDgO47r9zLP49HEjH/iGsFg==}
- '@tari-project/indexer-client@1.2.1':
- resolution: {integrity: sha512-245jDEjYLK9JkIBb4EAkVek/zFpSswK8tCTQ0vh1oa9F1d4/F8d8SftBxe8U7+nrLEuE8fW4efyg+3Z6DXYKxg==}
+ '@tari-project/indexer-client@1.3.1':
+ resolution: {integrity: sha512-hZVSLUIqkhuSEjsUmbIZERY6poBlzwIZwiwCwrxm17OKNnM6Po31B364UIhtoEw1mXbAVXm0BmAUlEM9b3katA==}
- '@tari-project/ootle-ts-bindings@1.30.0':
- resolution: {integrity: sha512-l1EFiB0dLcbfN9+IjZCUM8+j1+wAhTaz1jWDRgb8P1rostGo4y1jSlNH1F33oDG+z3lIJsMfiW9D3G9uLeVckg==}
+ '@tari-project/ootle-ts-bindings@1.30.1':
+ resolution: {integrity: sha512-4SFLp/rDGPsPFvwJQV9jbmGO7SiSrRO7+gpnJsRYyG+gBQ6m670GvB6VVX6V0l5Oq4GlpnyZSBKn3W2kCZCsYA==}
- '@tari-project/ootle-wasm@0.3.0':
- resolution: {integrity: sha512-nQObQkIBweNTmteUTVG/5jcPSDJIQTlRpqTu5B+NRACTD1L4MkRR4IOjc6BX+iLzFM904msRZ3IdClnqLhvEzA==}
+ '@tari-project/ootle-wasm@0.3.1':
+ resolution: {integrity: sha512-l4gxLxTCT/0oDGhfOEdTHfU+mlhtyfUNvMd9IM0H/E1Om4f8AC/+rJshyRaICL9udwOw/BRCOgK9cptVFsd77Q==}
'@tari-project/wallet_jrpc_client@1.14.0':
resolution: {integrity: sha512-7X8bsY26Qa4HYzJN9/S/95APUHyl/K0NPpo1+1MFQcYNckJfyBA79Sz8a7cBvKQiAXvS4d+qv2StV+dAcJVKRQ==}
@@ -2387,8 +2445,8 @@ packages:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
- eslint@10.0.3:
- resolution: {integrity: sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==}
+ eslint@10.1.0:
+ resolution: {integrity: sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
hasBin: true
peerDependencies:
@@ -4468,9 +4526,9 @@ snapshots:
'@esbuild/win32-x64@0.27.4':
optional: true
- '@eslint-community/eslint-utils@4.9.1(eslint@10.0.3(jiti@2.6.1))':
+ '@eslint-community/eslint-utils@4.9.1(eslint@10.1.0(jiti@2.6.1))':
dependencies:
- eslint: 10.0.3(jiti@2.6.1)
+ eslint: 10.1.0(jiti@2.6.1)
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.12.2': {}
@@ -4491,9 +4549,9 @@ snapshots:
dependencies:
'@types/json-schema': 7.0.15
- '@eslint/js@10.0.1(eslint@10.0.3(jiti@2.6.1))':
+ '@eslint/js@10.0.1(eslint@10.1.0(jiti@2.6.1))':
optionalDependencies:
- eslint: 10.0.3(jiti@2.6.1)
+ eslint: 10.1.0(jiti@2.6.1)
'@eslint/object-schema@3.0.3': {}
@@ -5160,19 +5218,19 @@ snapshots:
'@swc/wasm@1.15.18': {}
- '@tari-project/indexer-client@1.2.1':
+ '@tari-project/indexer-client@1.3.1':
dependencies:
- '@tari-project/ootle-ts-bindings': 1.30.0
+ '@tari-project/ootle-ts-bindings': 1.30.1
- '@tari-project/ootle-ts-bindings@1.30.0':
+ '@tari-project/ootle-ts-bindings@1.30.1':
dependencies:
bech32: 2.0.0
- '@tari-project/ootle-wasm@0.3.0': {}
+ '@tari-project/ootle-wasm@0.3.1': {}
'@tari-project/wallet_jrpc_client@1.14.0':
dependencies:
- '@tari-project/ootle-ts-bindings': 1.30.0
+ '@tari-project/ootle-ts-bindings': 1.30.1
'@tybys/wasm-util@0.10.1':
dependencies:
@@ -5265,15 +5323,15 @@ snapshots:
'@types/unist@3.0.3': {}
- '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)':
+ '@typescript-eslint/eslint-plugin@8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
- '@typescript-eslint/parser': 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.57.1
- '@typescript-eslint/type-utils': 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/utils': 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/type-utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.57.1
- eslint: 10.0.3(jiti@2.6.1)
+ eslint: 10.1.0(jiti@2.6.1)
ignore: 7.0.5
natural-compare: 1.4.0
ts-api-utils: 2.5.0(typescript@5.9.3)
@@ -5281,14 +5339,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)':
+ '@typescript-eslint/parser@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/scope-manager': 8.57.1
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.57.1
debug: 4.4.3
- eslint: 10.0.3(jiti@2.6.1)
+ eslint: 10.1.0(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -5311,13 +5369,13 @@ snapshots:
dependencies:
typescript: 5.9.3
- '@typescript-eslint/type-utils@8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)':
+ '@typescript-eslint/type-utils@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
- '@typescript-eslint/utils': 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)
debug: 4.4.3
- eslint: 10.0.3(jiti@2.6.1)
+ eslint: 10.1.0(jiti@2.6.1)
ts-api-utils: 2.5.0(typescript@5.9.3)
typescript: 5.9.3
transitivePeerDependencies:
@@ -5340,13 +5398,13 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)':
+ '@typescript-eslint/utils@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1))
+ '@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1))
'@typescript-eslint/scope-manager': 8.57.1
'@typescript-eslint/types': 8.57.1
'@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
- eslint: 10.0.3(jiti@2.6.1)
+ eslint: 10.1.0(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -5885,15 +5943,15 @@ snapshots:
escape-string-regexp@5.0.0: {}
- eslint-config-prettier@10.1.8(eslint@10.0.3(jiti@2.6.1)):
+ eslint-config-prettier@10.1.8(eslint@10.1.0(jiti@2.6.1)):
dependencies:
- eslint: 10.0.3(jiti@2.6.1)
+ eslint: 10.1.0(jiti@2.6.1)
- eslint-plugin-react-hooks@7.0.1(eslint@10.0.3(jiti@2.6.1)):
+ eslint-plugin-react-hooks@7.0.1(eslint@10.1.0(jiti@2.6.1)):
dependencies:
'@babel/core': 7.29.0
'@babel/parser': 7.29.2
- eslint: 10.0.3(jiti@2.6.1)
+ eslint: 10.1.0(jiti@2.6.1)
hermes-parser: 0.25.1
zod: 4.3.6
zod-validation-error: 4.0.2(zod@4.3.6)
@@ -5911,9 +5969,9 @@ snapshots:
eslint-visitor-keys@5.0.1: {}
- eslint@10.0.3(jiti@2.6.1):
+ eslint@10.1.0(jiti@2.6.1):
dependencies:
- '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1))
+ '@eslint-community/eslint-utils': 4.9.1(eslint@10.1.0(jiti@2.6.1))
'@eslint-community/regexpp': 4.12.2
'@eslint/config-array': 0.23.3
'@eslint/config-helpers': 0.5.3
@@ -7710,13 +7768,13 @@ snapshots:
typescript: 5.9.3
yaml: 2.8.2
- typescript-eslint@8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3):
+ typescript-eslint@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3):
dependencies:
- '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
- '@typescript-eslint/parser': 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/eslint-plugin': 8.57.1(@typescript-eslint/parser@8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)
+ '@typescript-eslint/parser': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)
'@typescript-eslint/typescript-estree': 8.57.1(typescript@5.9.3)
- '@typescript-eslint/utils': 8.57.1(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)
- eslint: 10.0.3(jiti@2.6.1)
+ '@typescript-eslint/utils': 8.57.1(eslint@10.1.0(jiti@2.6.1))(typescript@5.9.3)
+ eslint: 10.1.0(jiti@2.6.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index f48b09e..75bde58 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -8,12 +8,12 @@ packages:
catalog:
"@eslint/js": ^10.0.1
"@microsoft/api-extractor": ^7.57.7
- "@tari-project/indexer-client": ^1.2.1
- "@tari-project/ootle-ts-bindings": ^1.30.0
- "@tari-project/ootle-wasm": ^0.3.0
+ "@tari-project/indexer-client": ^1.3.1
+ "@tari-project/ootle-ts-bindings": ^1.30.1
+ "@tari-project/ootle-wasm": ^0.3.1
"@tari-project/wallet_jrpc_client": ^1.14.0
"@types/node": ^25.5.0
- eslint: ^10.0.3
+ eslint: ^10.1.0
eslint-config-prettier: ^10.1.8
knip: ^6.0.0
prettier: ^3.8.1