From 54b25d51cffe097e46f53fa57656c4d50964ea68 Mon Sep 17 00:00:00 2001 From: Edgars Date: Wed, 10 Jun 2026 23:01:33 +0100 Subject: [PATCH 1/7] docs: fees & transaction-kit integration guide for app developers --- .../decentralized-applications/_meta.json | 3 +- .../fees-and-transaction-kit.mdx | 104 ++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 pages/developers/decentralized-applications/fees-and-transaction-kit.mdx diff --git a/pages/developers/decentralized-applications/_meta.json b/pages/developers/decentralized-applications/_meta.json index 4daf44892..dddae92df 100644 --- a/pages/developers/decentralized-applications/_meta.json +++ b/pages/developers/decentralized-applications/_meta.json @@ -5,6 +5,7 @@ "querying-a-transaction": "Querying a Transaction", "reading-data": "Reading Data from Intelligent Contracts", "writing-data": "Writing Data to Intelligent Contracts", + "fees-and-transaction-kit": "Fees & Transaction Kit", "testing": "Testing", "project-boilerplate": "Project Boilerplate" -} +} \ No newline at end of file diff --git a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx new file mode 100644 index 000000000..0034eb78c --- /dev/null +++ b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx @@ -0,0 +1,104 @@ +# Fees and the Transaction Kit + +This guide covers what an app developer needs to know about GenLayer's v0.6 fee model and how to integrate fee approval and signing into an app with `@genlayer/transaction-kit` — the drop-in fee receipt, hold-to-sign and tracking surface for React and Vue. + +## The fee model in five sentences + +1. Every transaction makes **one deposit up front** that covers everything it might need; whatever isn't used is **refunded automatically when the transaction finalizes**. +2. The deposit is the sum of four parts: **consensus work** (leader + validator time units priced in GEN per time unit), an **execution budget** per leader round (paying for receipt writes, storage writes and message writes — not GenVM compute, which is what the time units pay for), a **message-fees bucket** (funding child transactions the contract emits), and any **value** you send to the contract. +3. How much consensus work you buy is mostly a question of **appeal posture**: more appeal rounds means a bigger deposit and stronger guarantees — that's what the kit's `low` / `standard` / `high` presets control (0, 1, or 2 appeal rounds). +4. The deposit locks **price caps** (GEN per time unit, storage price, receipt gas price); if network prices rise above any cap before execution, the transaction is rejected and **nothing is charged** — caps are your price protection, never a hidden cost. +5. A transaction is **successful** only when its status is `ACCEPTED` or `FINALIZED` *and* the execution result is `FINISHED_WITH_RETURN`; an `UNDETERMINED` transaction may carry a leader result, but you should treat it as **not executed**. + +## What you never have to do + +- **Compute fees by hand.** Estimation derives the deposit from allocation parameters plus live price reads with headroom. +- **Simulate the call per estimate.** GenLayer transactions take minutes of consensus; simulating each one to size a quote would be far too expensive. Quotes are sized from a *developer fee profile* measured offline by your test suite (below), falling back to network defaults. +- **Explain a stuck transaction to your users.** The kit's tracking surface maps every status and error selector (`InsufficientFees`, `MaxPriceExceeded`, `FeeValueMustBeNonZero`, appeal bonds, user rejection…) to honest, human copy. + +## The toolchain + +| Tool | Role | +| --- | --- | +| [`genlayer-js`](https://github.com/genlayerlabs/genlayer-js) v2 | SDK: `estimateTransactionFees`, `writeContract`/`deployContract` with `fees`, `waitForTransactionReceipt({ waitUntil })`, `isSuccessful` | +| [`@genlayer/transaction-kit`](https://github.com/genlayerlabs/genlayer-transaction-kit) | Core: `estimate` → policy quote, submit via any EIP-1193 wallet, track, verification fingerprint | +| `@genlayer/transaction-kit-react` / `-vue` | ``: fee receipt, presets, price-protection card, hold-to-sign, status timeline, outcome surfaces | +| [`gltest`](https://github.com/genlayerlabs/genlayer-testing-suite) `--fee-profile` | Measures real per-method fee consumption from your test suite and writes the suggestions file the kit consumes | +| GenLayer Explorer | Per-transaction fee panel: net cost, allocation vs consumed, locked prices vs caps, refunds, validator distribution | + +## Integration in four steps + +### 1. Install + +```jsonc +// package.json — until the npm release, install from the packed branches: +{ + "dependencies": { + "@genlayer/transaction-kit": "github:genlayerlabs/genlayer-transaction-kit#pkg/core", + "@genlayer/transaction-kit-react": "github:genlayerlabs/genlayer-transaction-kit#pkg/react" + } +} +``` + +(Vue apps use `#pkg/vue`; the adapters never import the core at runtime, so the packages version independently.) + +### 2. Create the kit with your wallet's provider + +The kit depends only on an injected [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) provider — MetaMask, Privy (embedded or external), WalletConnect, anything that exposes `request()`. It never touches `window.ethereum` directly. + +```typescript +import { createTransactionKit } from '@genlayer/transaction-kit'; +import { testnetAsimov } from 'genlayer-js/chains'; +import feeProfile from './fee-profile.json'; + +const kit = createTransactionKit({ + chain: testnetAsimov, + provider: window.ethereum, // or await privyWallet.getEthereumProvider() + account: userAddress, + suggestions: feeProfile, // measured allocations — see step 4 +}); +``` + +### 3. Drop in the panel + +```tsx +import { GenLayerTransactionPanel } from '@genlayer/transaction-kit-react'; +import '@genlayer/transaction-kit-react/styles.css'; + + refetchAppState()} +/> +``` + +The panel owns the whole flow: estimate → itemized fee receipt with preset selection → price-protection card → hold-to-sign → status timeline → outcome. Deploys work the same way with `tx={{ kind: 'deploy', code }}`. + +If you want your own UI instead, the same flow is available headless: `useTransactionFlow` (React hook / Vue composable) drives `estimating → review → signing → tracking → done | error`, and the core's `estimate` / `submit` / `track` / `verification` can be called directly. + +### 4. Measure fees in your tests, ship the profile + +The panel never simulates your call to size a quote. Instead, your contract test suite measures what executions actually consume, and the kit uses those measurements as **suggestions** — prices stay live, so a stale profile can never break the solvency caps: + +```bash +gltest tests/ --fee-profile frontend/fee-profile.json # headroom defaults to 1.25 +``` + +This produces a JSON artifact keyed by method (plus a `deploy` entry) with measured allocation values as decimal strings. Commit it next to your frontend and pass it as `suggestions`. The receipt then shows *"Sized from the developer's measured fee profile"* instead of *"Sized from network defaults"* — provenance your users can see, and `PolicyQuote.source` reports it programmatically. Methods without a profile entry fall back to network defaults automatically; regenerate the profile when contract logic changes materially. + +## Outcomes: what to tell your users + +| Status + result | Meaning | Surface it as | +| --- | --- | --- | +| `ACCEPTED` / `FINALIZED` + `FINISHED_WITH_RETURN` | Success | Done; unused deposit refunds at finalization | +| `UNDETERMINED` + any result | Validators could not reach a majority | **Treat as not executed**, even if a leader result exists | +| Rejected at submission (`MaxPriceExceeded`, …) | A price cap was exceeded before execution | Nothing charged; re-estimate and retry | +| `FINISHED_WITH_ERROR` | The contract reverted/errored | Execution failed; consensus fees are still consumed | + +`isSuccessful(tx)` in genlayer-js encodes the success rule; the kit's outcome surfaces apply it for you. + +## Debugging fees + +Open the transaction in the Explorer: the **Fees** section shows net cost (what the sender actually paid after refunds), the full breakdown — allocation vs consumed, locked prices vs caps, refunds by category, per-validator distribution, appeal bonds — and a settlement-failed flag if fee settlement itself went wrong. If a quote looks oversized, check whether the method has a profile entry (`source: 'network-default'` means it doesn't) and whether your appeal preset is higher than the action warrants. From 39a27893409e422273149b984059ff57be3bada0 Mon Sep 17 00:00:00 2001 From: Edgars Date: Wed, 10 Jun 2026 23:02:34 +0100 Subject: [PATCH 2/7] docs: it's the GenLayer fee model, not a versioned one --- package-lock.json | 10 ++++++++++ .../fees-and-transaction-kit.mdx | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 082d76c99..870c2ca54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -913,6 +913,7 @@ "integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.55.0" }, @@ -1103,6 +1104,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1124,6 +1126,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1470,6 +1473,7 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.32.0.tgz", "integrity": "sha512-5JHBC9n75kz5851jeklCPmZWcg3hUe6sjqJvyk3+hVqFaKcHwHgxsjeN1yLmggoUc6STbtm9/NQyabQehfjvWQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10" } @@ -1852,6 +1856,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -5247,6 +5252,7 @@ "resolved": "https://registry.npmjs.org/next/-/next-15.0.7.tgz", "integrity": "sha512-Vl6fLEuOP1MgtEmDrY51BQr6Bl8oC8vDSHdA10xZWPPZa6e+dOwYNDLWHjvTktNLZkKYySpsW3Yzy4Lo+JORkw==", "license": "MIT", + "peer": true, "dependencies": { "@next/env": "15.0.7", "@swc/counter": "0.1.3", @@ -6336,6 +6342,7 @@ "resolved": "https://registry.npmjs.org/nextra/-/nextra-2.13.4.tgz", "integrity": "sha512-7of2rSBxuUa3+lbMmZwG9cqgftcoNOVQLTT6Rxf3EhBR9t1EI7b43dted8YoqSNaigdE3j1CoyNkX8N/ZzlEpw==", "license": "MIT", + "peer": true, "dependencies": { "@headlessui/react": "^1.7.17", "@mdx-js/mdx": "^2.3.0", @@ -6637,6 +6644,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6649,6 +6657,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -8074,6 +8083,7 @@ "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "license": "MIT", + "peer": true, "dependencies": { "ansi-sequence-parser": "^1.1.0", "jsonc-parser": "^3.2.0", diff --git a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx index 0034eb78c..1e037690b 100644 --- a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx +++ b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx @@ -1,6 +1,6 @@ # Fees and the Transaction Kit -This guide covers what an app developer needs to know about GenLayer's v0.6 fee model and how to integrate fee approval and signing into an app with `@genlayer/transaction-kit` — the drop-in fee receipt, hold-to-sign and tracking surface for React and Vue. +This guide covers what an app developer needs to know about GenLayer's fee model and how to integrate fee approval and signing into an app with `@genlayer/transaction-kit` — the drop-in fee receipt, hold-to-sign and tracking surface for React and Vue. ## The fee model in five sentences From ee04eb5af00b0e18264e072ea85dc8349ca9b372 Mon Sep 17 00:00:00 2001 From: Edgars Date: Wed, 10 Jun 2026 23:19:59 +0100 Subject: [PATCH 3/7] =?UTF-8?q?docs:=20designer=20corrections=20=E2=80=94?= =?UTF-8?q?=20presets=201/3/5=20+=20custom,=20rotations=20are=20app-empiri?= =?UTF-8?q?cal,=20can-take-minutes=20nuance,=20leave-and-track=20best=20pr?= =?UTF-8?q?actice,=20queue=20position?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fees-and-transaction-kit.mdx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx index 1e037690b..3c8994e09 100644 --- a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx +++ b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx @@ -6,14 +6,14 @@ This guide covers what an app developer needs to know about GenLayer's fee model 1. Every transaction makes **one deposit up front** that covers everything it might need; whatever isn't used is **refunded automatically when the transaction finalizes**. 2. The deposit is the sum of four parts: **consensus work** (leader + validator time units priced in GEN per time unit), an **execution budget** per leader round (paying for receipt writes, storage writes and message writes — not GenVM compute, which is what the time units pay for), a **message-fees bucket** (funding child transactions the contract emits), and any **value** you send to the contract. -3. How much consensus work you buy is mostly a question of **appeal posture**: more appeal rounds means a bigger deposit and stronger guarantees — that's what the kit's `low` / `standard` / `high` presets control (0, 1, or 2 appeal rounds). +3. How much consensus work you buy is mostly a question of **appeal posture**: more appeal rounds means a bigger deposit and stronger guarantees — that's what the kit's `low` / `standard` / `high` presets control (1, 3, or 5 appeal rounds; the Advanced expander accepts any higher number). Rotations also consume rounds, but they're not a user choice — they're empirical per application and come from the developer fee profile. 4. The deposit locks **price caps** (GEN per time unit, storage price, receipt gas price); if network prices rise above any cap before execution, the transaction is rejected and **nothing is charged** — caps are your price protection, never a hidden cost. 5. A transaction is **successful** only when its status is `ACCEPTED` or `FINALIZED` *and* the execution result is `FINISHED_WITH_RETURN`; an `UNDETERMINED` transaction may carry a leader result, but you should treat it as **not executed**. ## What you never have to do - **Compute fees by hand.** Estimation derives the deposit from allocation parameters plus live price reads with headroom. -- **Simulate the call per estimate.** GenLayer transactions take minutes of consensus; simulating each one to size a quote would be far too expensive. Quotes are sized from a *developer fee profile* measured offline by your test suite (below), falling back to network defaults. +- **Simulate the call per estimate.** GenLayer transactions can take minutes of consensus when they make several LLM calls or web requests; simulating each one to size a quote would be far too expensive. Quotes are sized from a *developer fee profile* measured offline by your test suite (below), falling back to network defaults. - **Explain a stuck transaction to your users.** The kit's tracking surface maps every status and error selector (`InsufficientFees`, `MaxPriceExceeded`, `FeeValueMustBeNonZero`, appeal bonds, user rejection…) to honest, human copy. ## The toolchain @@ -74,7 +74,9 @@ import '@genlayer/transaction-kit-react/styles.css'; /> ``` -The panel owns the whole flow: estimate → itemized fee receipt with preset selection → price-protection card → hold-to-sign → status timeline → outcome. Deploys work the same way with `tx={{ kind: 'deploy', code }}`. +The panel owns the whole flow: estimate → itemized fee receipt with preset selection → price-protection card → sign (click on desktop, press-and-hold on touch) → status timeline → outcome. While the transaction is queued, the timeline shows its position in the pending queue. Deploys work the same way with `tx={{ kind: 'deploy', code }}`. + +The timeline is good to have, but don't trap users in it: transactions that make LLM calls or web requests can take minutes, so best practice is to let the user navigate away and track the transaction in your backend (poll `getTransaction` / `waitForTransactionReceipt` server-side and notify on completion). Use `onDone` and `trackUntil="decided"` for the in-app happy path, not as your source of truth. If you want your own UI instead, the same flow is available headless: `useTransactionFlow` (React hook / Vue composable) drives `estimating → review → signing → tracking → done | error`, and the core's `estimate` / `submit` / `track` / `verification` can be called directly. From a8343154b0d3ba848062684b1aa0286122f639a8 Mon Sep 17 00:00:00 2001 From: Edgars Date: Wed, 10 Jun 2026 23:35:32 +0100 Subject: [PATCH 4/7] docs: pre-sign queue depth via consensus passthrough --- .../decentralized-applications/fees-and-transaction-kit.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx index 3c8994e09..1ca20bfad 100644 --- a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx +++ b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx @@ -74,7 +74,7 @@ import '@genlayer/transaction-kit-react/styles.css'; /> ``` -The panel owns the whole flow: estimate → itemized fee receipt with preset selection → price-protection card → sign (click on desktop, press-and-hold on touch) → status timeline → outcome. While the transaction is queued, the timeline shows its position in the pending queue. Deploys work the same way with `tx={{ kind: 'deploy', code }}`. +The panel owns the whole flow: estimate → itemized fee receipt with preset selection → price-protection card → sign (click on desktop, press-and-hold on touch) → status timeline → outcome. Before signing, the review step shows how many transactions are queued ahead for the target contract (read from the consensus contracts through the RPC passthrough — no extra infrastructure); while queued, the timeline shows your position. Deploys work the same way with `tx={{ kind: 'deploy', code }}`. The timeline is good to have, but don't trap users in it: transactions that make LLM calls or web requests can take minutes, so best practice is to let the user navigate away and track the transaction in your backend (poll `getTransaction` / `waitForTransactionReceipt` server-side and notify on completion). Use `onDone` and `trackUntil="decided"` for the in-app happy path, not as your source of truth. From 30c64572240354e1ac6c74f092ef4e7c5450656c Mon Sep 17 00:00:00 2001 From: Edgars Date: Thu, 11 Jun 2026 16:26:25 +0100 Subject: [PATCH 5/7] docs: gasless network behavior --- .../decentralized-applications/fees-and-transaction-kit.mdx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx index 1ca20bfad..7221393bd 100644 --- a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx +++ b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx @@ -90,6 +90,10 @@ gltest tests/ --fee-profile frontend/fee-profile.json # headroom defaults to 1 This produces a JSON artifact keyed by method (plus a `deploy` entry) with measured allocation values as decimal strings. Commit it next to your frontend and pass it as `suggestions`. The receipt then shows *"Sized from the developer's measured fee profile"* instead of *"Sized from network defaults"* — provenance your users can see, and `PolicyQuote.source` reports it programmatically. Methods without a profile entry fall back to network defaults automatically; regenerate the profile when contract logic changes materially. +## Gasless networks + +Some Studio deployments run **gasless** (fee accounting disabled — all prices zero). The kit detects this from the estimate (`policy.enabled: false`): the quote is marked `gasless`, the panel replaces the fee receipt with a "No fees on this network" notice, and submission omits fee params entirely. The same app code works on gasless and fee-charging networks without branches. + ## Outcomes: what to tell your users | Status + result | Meaning | Surface it as | From 84ffd1b2894c5b18d595848255f6c97430457cce Mon Sep 17 00:00:00 2001 From: Edgars Date: Tue, 16 Jun 2026 09:56:21 +0100 Subject: [PATCH 6/7] docs: split fee policy guide --- .../decentralized-applications/_meta.json | 7 +- .../fee-outcomes-and-debugging.mdx | 63 ++++ .../fee-profiling-and-estimation.mdx | 313 ++++++++++++++++++ .../fees-and-transaction-kit.mdx | 131 ++------ .../transaction-kit-integration.mdx | 122 +++++++ 5 files changed, 537 insertions(+), 99 deletions(-) create mode 100644 pages/developers/decentralized-applications/fee-outcomes-and-debugging.mdx create mode 100644 pages/developers/decentralized-applications/fee-profiling-and-estimation.mdx create mode 100644 pages/developers/decentralized-applications/transaction-kit-integration.mdx diff --git a/pages/developers/decentralized-applications/_meta.json b/pages/developers/decentralized-applications/_meta.json index dddae92df..035063049 100644 --- a/pages/developers/decentralized-applications/_meta.json +++ b/pages/developers/decentralized-applications/_meta.json @@ -5,7 +5,10 @@ "querying-a-transaction": "Querying a Transaction", "reading-data": "Reading Data from Intelligent Contracts", "writing-data": "Writing Data to Intelligent Contracts", - "fees-and-transaction-kit": "Fees & Transaction Kit", + "fees-and-transaction-kit": "Fees and Transaction Policy", + "transaction-kit-integration": "Transaction Kit Integration", + "fee-profiling-and-estimation": "Fee Profiling and Estimation", + "fee-outcomes-and-debugging": "Fee Outcomes and Debugging", "testing": "Testing", "project-boilerplate": "Project Boilerplate" -} \ No newline at end of file +} diff --git a/pages/developers/decentralized-applications/fee-outcomes-and-debugging.mdx b/pages/developers/decentralized-applications/fee-outcomes-and-debugging.mdx new file mode 100644 index 000000000..a7a023492 --- /dev/null +++ b/pages/developers/decentralized-applications/fee-outcomes-and-debugging.mdx @@ -0,0 +1,63 @@ +# Fee Outcomes and Debugging + +Applications should explain transaction outcomes in terms users or operators can act on. A submitted GenLayer transaction can be accepted by consensus, finalized later, rejected before execution or finish with a contract error. + +## Success rule + +A transaction is successful only when both are true: + +- the transaction status is `ACCEPTED` or `FINALIZED` +- the execution result is `FINISHED_WITH_RETURN` + +Use `isSuccessful(tx)` from `genlayer-js` or the transaction-kit outcome surfaces instead of checking status alone. A transaction can be accepted by validators and still finish with a contract error. + +## User-facing outcomes + +| Status and result | Meaning | Surface it as | +| --- | --- | --- | +| `ACCEPTED` / `FINALIZED` + `FINISHED_WITH_RETURN` | Execution succeeded. | Done. Unused fee budget refunds at finalization. | +| `UNDETERMINED` + any result | Validators could not reach a majority. | Treat as not executed, even if a leader result exists. | +| Rejected at submission, such as `MaxPriceExceeded` | A price cap or funding rule failed before execution. | Nothing charged. Re-estimate and retry. | +| `FINISHED_WITH_ERROR` | The contract reverted or errored. | Execution failed. Consensus fees may still be consumed. | + +## Gasless networks + +Some Studio deployments run gasless, with fee accounting disabled and all prices zero. The transaction kit detects this from the estimate: + +```typescript +if (quote.gasless) { + // Show "No fees on this network" and submit without fee params. +} +``` + +The same app code can work on gasless Studio and fee-charging networks. Do not hard-code a network name to decide whether fees are enabled; use the estimate result. + +## Refunds + +The initial deposit is a maximum budget, not the final cost. Unused budget is refunded when the transaction finalizes. Users should understand three numbers: + +- total deposit: what must be available when signing +- spent fees: what the transaction actually consumed +- refund: the unused part returned after finalization + +If an app tracks only until `decided`, it may not yet know the final refund. Use `finalized` tracking for screens or tools that need final fee settlement. + +## Explorer debugging + +Open the transaction in GenLayer Explorer and inspect the Fees section. It should show: + +- allocation versus consumed values +- locked price caps versus current prices +- message fee budget and consumption +- refunds by category +- settlement status + +If a quote looks too large, check whether the transaction used a developer profile or network defaults. `PolicyQuote.source` is `developer` when a matching `fee-profile.json` entry was used and `network-default` otherwise. + +If a transaction failed even though the user accepted the quote, check: + +- whether a price cap was exceeded before execution +- whether the method hit an unmeasured expensive branch +- whether `totalMessageFees` was too low for emitted child transactions +- whether the contract itself returned `FINISHED_WITH_ERROR` +- whether the app tracked only status and ignored execution result diff --git a/pages/developers/decentralized-applications/fee-profiling-and-estimation.mdx b/pages/developers/decentralized-applications/fee-profiling-and-estimation.mdx new file mode 100644 index 000000000..18d84bf44 --- /dev/null +++ b/pages/developers/decentralized-applications/fee-profiling-and-estimation.mdx @@ -0,0 +1,313 @@ +# Fee Profiling and Estimation + +Fee profiling is how a GenLayer app turns contract tests into reusable fee suggestions. The output is a JSON file, usually `frontend/fee-profile.json` or `fee-profile.json`, that any submission path can use when it estimates a deploy or write transaction. + +The profile is not a live simulation result. It is a checked-in artifact generated from representative tests. Prices and caps are still read live at transaction time. + +`@genlayer/transaction-kit` can consume the profile directly for frontend flows. If you are building a CLI, backend worker, Python script or custom JS app without transaction-kit, use the same profile values as inputs to `estimateTransactionFees` / `estimate_transaction_fees`, then submit the returned `distribution` and `feeValue`. + +## Why profiles exist + +GenLayer transactions can take different paths: + +- a method may or may not emit messages +- a method may make different LLM or web calls depending on state +- a child transaction may terminate early or run through an expensive branch +- appeals and rotations change the amount of consensus work that must be funded +- storage, receipt and rollup writes vary with output size + +Simulating all of that before every user action would be too slow. Instead, the developer measures the important branches during tests, commits the maximum observed requirements and lets the app quote from that profile. + +## How methods are aggregated + +Profiles are keyed by contract method name, not by pytest test name and not by arguments. + +If five tests call `register_and_claim`, all five observations are merged into one `methods.register_and_claim` entry. For each field, the profiler keeps the maximum value observed across the whole run, then applies headroom to fee and time-unit values. + +Example: + +| Test path | Execution budget | Message fees | Rotations | +| --- | ---: | ---: | ---: | +| user already registered, no referral message | 600 | 0 | 0 | +| new user, emits referral message | 200 | 800 | 1 | + +The generated profile uses: + +```json +{ + "methods": { + "register_and_claim": { + "executionBudgetPerRound": "600", + "totalMessageFees": "800", + "rotationsPerRound": "1" + } + } +} +``` + +That combined entry may not describe a single historical transaction. It is intentionally conservative: it is the union of the worst observed components for that method. This is the right default for app safety because a user can usually trigger any branch that the method allows. + +If your app can prove a specific user flow cannot hit the expensive branch, use an explicit override for that flow. Keep the committed profile conservative. + +## What to measure + +Measure scenarios, not just methods. A good profile suite covers: + +- deploy, if the app lets users deploy contracts +- the cheapest successful path +- the most expensive normal path +- every branch that emits a different message set +- every branch that changes storage or receipt size materially +- every branch where child transactions can run different paths +- expected failure/revert paths, if users can trigger them and they consume fees + +For a Rally-style contract, that means tests for paths such as: + +- user already registered, no referral message +- user not registered, referral message emitted +- main transaction passes and emits the shard message +- main transaction fails before the shard message +- shard transaction terminates early +- shard transaction reaches the expensive branch + +The parent method profile should include the worst observed `totalMessageFees` across these paths. If the parent emits child transactions, the message fee budget must be large enough for the worst child path that the parent can create. + +## Generate the profile + +Create a script in the app repository so developers do not have to remember the exact command: + +```jsonc +{ + "scripts": { + "test:fees": "python3 -m pytest tests/integration/test_football_bets.py::test_fee_profile_create_bet --fee-profile frontend/fee-profile.json -v -s" + } +} +``` + +Run it against a fee-reporting Studio or network: + +```bash +npm run test:fees -- --rpc-url http://127.0.0.1:4000/api +``` + +The underlying mechanism is a pytest option provided by `gltest`: + +```bash +python3 -m pytest tests/integration --fee-profile frontend/fee-profile.json +``` + +Use `--fee-profile-headroom` to adjust the multiplier: + +```bash +python3 -m pytest tests/integration \ + --fee-profile frontend/fee-profile.json \ + --fee-profile-headroom 1.5 +``` + +The default headroom is `1.25`. + +## Wait for finalized receipts + +Profile tests should wait for finalized receipts: + +```python +receipt = contract.create_bet(args=["2024-06-20", "Spain", "Italy", "1"]).transact( + fees=transaction_fee_preset(), + wait_until="finalized", +) +``` + +Earlier statuses can prove that validators accepted a transaction before all fee accounting and refunds are settled. Finalized receipts are the reliable source for the profile. + +## Submit the profiling transaction + +The measured transaction itself still needs a fee preset. In local Studio runs, use a trusted developer preset sized from the active fee policy: + +```python +from gltest.clients import get_gl_client + +FEE_ESTIMATE_OPTIONS = { + "leaderTimeunitsAllocation": 100, + "validatorTimeunitsAllocation": 200, + "totalMessageFees": 0, + "rotations": [1], +} + +def transaction_fee_preset(): + estimate = get_gl_client().estimate_transaction_fees(FEE_ESTIMATE_OPTIONS) + return { + "distribution": estimate["distribution"], + "feeValue": estimate["feeValue"], + } +``` + +Use that preset on deploys and writes: + +```python +contract = factory.deploy(fees=transaction_fee_preset(), wait_until="finalized") + +receipt = contract.create_bet(args=["2024-06-20", "Spain", "Italy", "1"]).transact( + fees=transaction_fee_preset(), + wait_until="finalized", +) +``` + +For message-producing scenarios, increase `totalMessageFees` in the profiling preset so the emitted child transactions have enough budget. The generated profile records the observed message budget from the finalized receipt. If no message-producing path is measured, that method will recommend a zero message budget. + +## Profile shape + +A generated profile looks like this: + +```json +{ + "version": 1, + "network": "localnet", + "measuredAt": "2026-06-15T17:26:36Z", + "deploy": { + "leaderTimeunitsAllocation": "125", + "validatorTimeunitsAllocation": "250", + "executionBudgetPerRound": "786434", + "totalMessageFees": "0", + "rotationsPerRound": "1" + }, + "methods": { + "create_bet": { + "leaderTimeunitsAllocation": "125", + "validatorTimeunitsAllocation": "250", + "executionBudgetPerRound": "786297", + "totalMessageFees": "0", + "rotationsPerRound": "1" + } + } +} +``` + +| Field | Meaning | +| --- | --- | +| `leaderTimeunitsAllocation` | Suggested leader time units for the method or deploy. | +| `validatorTimeunitsAllocation` | Suggested validator time units for the method or deploy. | +| `executionBudgetPerRound` | Budget for receipt, storage and rollup writes per leader round. | +| `totalMessageFees` | Budget for child transactions emitted by the contract. | +| `rotationsPerRound` | Suggested rotations per leader round. This is recorded exactly, not multiplied by headroom. | + +The profile intentionally does not contain `feeValue` or live price caps. Those are quoted from the current network fee policy when the user is about to sign. + +## Use the profile in transaction-kit + +For frontend flows, commit the profile next to the frontend and pass it to the kit: + +```typescript +import feeProfile from './fee-profile.json'; + +const kit = createTransactionKit({ + chain, + provider, + account, + suggestions: feeProfile, +}); +``` + +When a transaction matches a profile entry, the quote source is `developer`. Methods without a matching entry fall back to network defaults. + +## Use the profile with genlayer-js + +Without transaction-kit, load the profile yourself, select the deploy or method entry, convert `rotationsPerRound` into the `rotations` array for the appeal posture you want, then estimate from live prices: + +```typescript +import feeProfile from './fee-profile.json'; + +const methodProfile = feeProfile.methods.create_bet; +const appealRounds = 1n; +const rotationsPerRound = BigInt(methodProfile.rotationsPerRound ?? '0'); + +const estimate = await client.estimateTransactionFees({ + leaderTimeunitsAllocation: BigInt(methodProfile.leaderTimeunitsAllocation), + validatorTimeunitsAllocation: BigInt(methodProfile.validatorTimeunitsAllocation), + executionBudgetPerRound: BigInt(methodProfile.executionBudgetPerRound), + totalMessageFees: BigInt(methodProfile.totalMessageFees ?? '0'), + appealRounds, + rotations: Array.from( + { length: Number(appealRounds) + 1 }, + () => rotationsPerRound, + ), +}); + +await client.writeContract({ + address: contract, + functionName: 'create_bet', + args: ['2024-06-20', 'Spain', 'Italy', '1'], + fees: { + distribution: estimate.distribution, + feeValue: estimate.feeValue, + }, +}); +``` + +## Use the profile with genlayer-py + +Python follows the same shape: + +```python +import json + +with open("fee-profile.json", encoding="utf-8") as f: + fee_profile = json.load(f) + +method_profile = fee_profile["methods"]["create_bet"] +appeal_rounds = 1 +rotations_per_round = int(method_profile.get("rotationsPerRound", "0")) + +estimate = client.estimate_transaction_fees( + { + "leaderTimeunitsAllocation": int(method_profile["leaderTimeunitsAllocation"]), + "validatorTimeunitsAllocation": int(method_profile["validatorTimeunitsAllocation"]), + "executionBudgetPerRound": int(method_profile["executionBudgetPerRound"]), + "totalMessageFees": int(method_profile.get("totalMessageFees", "0")), + "appealRounds": appeal_rounds, + "rotations": [rotations_per_round] * (appeal_rounds + 1), + } +) + +tx_hash = client.write_contract( + account=account, + address=contract_address, + function_name="create_bet", + args=["2024-06-20", "Spain", "Italy", "1"], + fees={ + "distribution": estimate["distribution"], + "feeValue": estimate["feeValue"], + }, +) +``` + +## Use the profile with the CLI + +The CLI accepts fee estimate options as JSON. Extract the method profile and pass the same fields to `estimate-fees`: + +```bash +genlayer estimate-fees \ + --json \ + --fees '{"leaderTimeunitsAllocation":"125","validatorTimeunitsAllocation":"250","executionBudgetPerRound":"786297","totalMessageFees":"0","appealRounds":"1","rotations":["1","1"]}' +``` + +Then pass the printed transaction fee object to `genlayer write` or `genlayer deploy`: + +```bash +genlayer write 0x123456789abcdef create_bet \ + --fees '{"distribution":{"leaderTimeunitsAllocation":"125","validatorTimeunitsAllocation":"250","executionBudgetPerRound":"786297","totalMessageFees":"0","appealRounds":"1","rotations":["1","1"]},"feeValue":"..."}' \ + --args "2024-06-20" "Spain" "Italy" "1" +``` + +This is intentionally the same policy flow as transaction-kit: the profile provides allocation suggestions, while the SDK/CLI estimate reads current network prices and produces the final deposit. + +## Regenerate policy + +Regenerate and review `fee-profile.json` when you change: + +- contract code that affects LLM calls, web requests, messages, storage or receipt data +- application arguments that select different branches +- child transaction behavior +- intended appeal or rotation posture +- the GenVM or Studio version used for fee accounting + +Treat the file like a performance budget. It should change in the same pull request as the behavior that changed the fee shape. diff --git a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx index 7221393bd..8f3ba7f3d 100644 --- a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx +++ b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx @@ -1,110 +1,47 @@ -# Fees and the Transaction Kit +# Fees and Transaction Policy -This guide covers what an app developer needs to know about GenLayer's fee model and how to integrate fee approval and signing into an app with `@genlayer/transaction-kit` — the drop-in fee receipt, hold-to-sign and tracking surface for React and Vue. +GenLayer apps should treat fees as a measured application policy, not as ad hoc math at submission time. Whether you are building a web frontend, a backend service, a CLI or an automation script, the transaction must include fee parameters that are large enough for the path it can execute. -## The fee model in five sentences +The suggested fee parameters come from a developer fee profile generated by the contract test suite. `@genlayer/transaction-kit` is the recommended frontend integration, but the profile itself is not tied to transaction-kit. -1. Every transaction makes **one deposit up front** that covers everything it might need; whatever isn't used is **refunded automatically when the transaction finalizes**. -2. The deposit is the sum of four parts: **consensus work** (leader + validator time units priced in GEN per time unit), an **execution budget** per leader round (paying for receipt writes, storage writes and message writes — not GenVM compute, which is what the time units pay for), a **message-fees bucket** (funding child transactions the contract emits), and any **value** you send to the contract. -3. How much consensus work you buy is mostly a question of **appeal posture**: more appeal rounds means a bigger deposit and stronger guarantees — that's what the kit's `low` / `standard` / `high` presets control (1, 3, or 5 appeal rounds; the Advanced expander accepts any higher number). Rotations also consume rounds, but they're not a user choice — they're empirical per application and come from the developer fee profile. -4. The deposit locks **price caps** (GEN per time unit, storage price, receipt gas price); if network prices rise above any cap before execution, the transaction is rejected and **nothing is charged** — caps are your price protection, never a hidden cost. -5. A transaction is **successful** only when its status is `ACCEPTED` or `FINALIZED` *and* the execution result is `FINISHED_WITH_RETURN`; an `UNDETERMINED` transaction may carry a leader result, but you should treat it as **not executed**. +## The model -## What you never have to do +Every transaction makes one deposit up front. The deposit covers: -- **Compute fees by hand.** Estimation derives the deposit from allocation parameters plus live price reads with headroom. -- **Simulate the call per estimate.** GenLayer transactions can take minutes of consensus when they make several LLM calls or web requests; simulating each one to size a quote would be far too expensive. Quotes are sized from a *developer fee profile* measured offline by your test suite (below), falling back to network defaults. -- **Explain a stuck transaction to your users.** The kit's tracking surface maps every status and error selector (`InsufficientFees`, `MaxPriceExceeded`, `FeeValueMustBeNonZero`, appeal bonds, user rejection…) to honest, human copy. +- consensus work: leader and validator time units, priced in GEN per time unit +- execution budget: receipt, storage and rollup writes +- message budget: child transactions emitted by the contract +- user value: any value intentionally sent to the contract -## The toolchain +Unused fee budget is refunded automatically when the transaction finalizes. Price caps are locked at submission time; if network prices rise above a cap before execution, the transaction is rejected and nothing is charged. -| Tool | Role | -| --- | --- | -| [`genlayer-js`](https://github.com/genlayerlabs/genlayer-js) v2 | SDK: `estimateTransactionFees`, `writeContract`/`deployContract` with `fees`, `waitForTransactionReceipt({ waitUntil })`, `isSuccessful` | -| [`@genlayer/transaction-kit`](https://github.com/genlayerlabs/genlayer-transaction-kit) | Core: `estimate` → policy quote, submit via any EIP-1193 wallet, track, verification fingerprint | -| `@genlayer/transaction-kit-react` / `-vue` | ``: fee receipt, presets, price-protection card, hold-to-sign, status timeline, outcome surfaces | -| [`gltest`](https://github.com/genlayerlabs/genlayer-testing-suite) `--fee-profile` | Measures real per-method fee consumption from your test suite and writes the suggestions file the kit consumes | -| GenLayer Explorer | Per-transaction fee panel: net cost, allocation vs consumed, locked prices vs caps, refunds, validator distribution | - -## Integration in four steps - -### 1. Install - -```jsonc -// package.json — until the npm release, install from the packed branches: -{ - "dependencies": { - "@genlayer/transaction-kit": "github:genlayerlabs/genlayer-transaction-kit#pkg/core", - "@genlayer/transaction-kit-react": "github:genlayerlabs/genlayer-transaction-kit#pkg/react" - } -} -``` - -(Vue apps use `#pkg/vue`; the adapters never import the core at runtime, so the packages version independently.) - -### 2. Create the kit with your wallet's provider - -The kit depends only on an injected [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) provider — MetaMask, Privy (embedded or external), WalletConnect, anything that exposes `request()`. It never touches `window.ethereum` directly. - -```typescript -import { createTransactionKit } from '@genlayer/transaction-kit'; -import { testnetAsimov } from 'genlayer-js/chains'; -import feeProfile from './fee-profile.json'; - -const kit = createTransactionKit({ - chain: testnetAsimov, - provider: window.ethereum, // or await privyWallet.getEthereumProvider() - account: userAddress, - suggestions: feeProfile, // measured allocations — see step 4 -}); -``` - -### 3. Drop in the panel - -```tsx -import { GenLayerTransactionPanel } from '@genlayer/transaction-kit-react'; -import '@genlayer/transaction-kit-react/styles.css'; +## The pieces - refetchAppState()} -/> -``` - -The panel owns the whole flow: estimate → itemized fee receipt with preset selection → price-protection card → sign (click on desktop, press-and-hold on touch) → status timeline → outcome. Before signing, the review step shows how many transactions are queued ahead for the target contract (read from the consensus contracts through the RPC passthrough — no extra infrastructure); while queued, the timeline shows your position. Deploys work the same way with `tx={{ kind: 'deploy', code }}`. - -The timeline is good to have, but don't trap users in it: transactions that make LLM calls or web requests can take minutes, so best practice is to let the user navigate away and track the transaction in your backend (poll `getTransaction` / `waitForTransactionReceipt` server-side and notify on completion). Use `onDone` and `trackUntil="decided"` for the in-app happy path, not as your source of truth. - -If you want your own UI instead, the same flow is available headless: `useTransactionFlow` (React hook / Vue composable) drives `estimating → review → signing → tracking → done | error`, and the core's `estimate` / `submit` / `track` / `verification` can be called directly. - -### 4. Measure fees in your tests, ship the profile - -The panel never simulates your call to size a quote. Instead, your contract test suite measures what executions actually consume, and the kit uses those measurements as **suggestions** — prices stay live, so a stale profile can never break the solvency caps: - -```bash -gltest tests/ --fee-profile frontend/fee-profile.json # headroom defaults to 1.25 -``` - -This produces a JSON artifact keyed by method (plus a `deploy` entry) with measured allocation values as decimal strings. Commit it next to your frontend and pass it as `suggestions`. The receipt then shows *"Sized from the developer's measured fee profile"* instead of *"Sized from network defaults"* — provenance your users can see, and `PolicyQuote.source` reports it programmatically. Methods without a profile entry fall back to network defaults automatically; regenerate the profile when contract logic changes materially. - -## Gasless networks - -Some Studio deployments run **gasless** (fee accounting disabled — all prices zero). The kit detects this from the estimate (`policy.enabled: false`): the quote is marked `gasless`, the panel replaces the fee receipt with a "No fees on this network" notice, and submission omits fee params entirely. The same app code works on gasless and fee-charging networks without branches. +| Page | Use it for | +| --- | --- | +| [Transaction Kit Integration](./transaction-kit-integration) | Add the fee approval, signing and tracking flow to a React, Vue or headless frontend. | +| [Fee Profiling and Estimation](./fee-profiling-and-estimation) | Measure deploy and method fee requirements from representative contract tests and use `fee-profile.json` from JS, Python, CLI or transaction-kit. | +| [Fee Outcomes and Debugging](./fee-outcomes-and-debugging) | Interpret transaction outcomes, gasless networks, refunds and Explorer fee panels. | -## Outcomes: what to tell your users +## Recommended workflow -| Status + result | Meaning | Surface it as | -| --- | --- | --- | -| `ACCEPTED` / `FINALIZED` + `FINISHED_WITH_RETURN` | Success | Done; unused deposit refunds at finalization | -| `UNDETERMINED` + any result | Validators could not reach a majority | **Treat as not executed**, even if a leader result exists | -| Rejected at submission (`MaxPriceExceeded`, …) | A price cap was exceeded before execution | Nothing charged; re-estimate and retry | -| `FINISHED_WITH_ERROR` | The contract reverted/errored | Execution failed; consensus fees are still consumed | +1. Write normal contract tests for the app paths users can trigger. +2. Add fee-profile coverage for each economically meaningful branch. +3. Run the profile tests against a fee-reporting Studio or network. +4. Commit the generated `fee-profile.json` where your app or tool can load it. +5. Use the profile entry for the deploy or method you are submitting. +6. Estimate with the current network fee policy and submit the returned `distribution` and `feeValue`. -`isSuccessful(tx)` in genlayer-js encodes the success rule; the kit's outcome surfaces apply it for you. +This avoids live simulation before every user transaction. GenLayer transactions can include LLM calls, web requests, appeals and emitted messages, so per-click simulation would make the app slow and expensive. The profile gives the app a reproducible, test-backed estimate; the SDK still reads current prices and caps from the network when the user is about to sign. -## Debugging fees +## Tooling -Open the transaction in the Explorer: the **Fees** section shows net cost (what the sender actually paid after refunds), the full breakdown — allocation vs consumed, locked prices vs caps, refunds by category, per-validator distribution, appeal bonds — and a settlement-failed flag if fee settlement itself went wrong. If a quote looks oversized, check whether the method has a profile entry (`source: 'network-default'` means it doesn't) and whether your appeal preset is higher than the action warrants. +| Tool | Role | +| --- | --- | +| `genlayer-js` v2 | SDK methods for fee estimation, deploy/write submission, receipt waiting and success checks. | +| `genlayer-py` | Python SDK methods for the same estimate and submit flow in scripts, services and CLIs. | +| `@genlayer/transaction-kit` | Core estimate, submit, track and verification flow. | +| `@genlayer/transaction-kit-react` / `-vue` | Drop-in UI for fee receipt, preset selection, price protection, signing and status tracking. | +| GenLayer CLI | `estimate-fees`, `deploy` and `write` commands that accept fee JSON for terminal workflows. | +| `gltest --fee-profile` | Measures fee usage from contract tests and writes the profile consumed by apps and tools. | +| GenLayer Explorer | Shows transaction fee allocation, consumption, refunds and settlement details. | diff --git a/pages/developers/decentralized-applications/transaction-kit-integration.mdx b/pages/developers/decentralized-applications/transaction-kit-integration.mdx new file mode 100644 index 000000000..61be5ed05 --- /dev/null +++ b/pages/developers/decentralized-applications/transaction-kit-integration.mdx @@ -0,0 +1,122 @@ +# Transaction Kit Integration + +`@genlayer/transaction-kit` is the frontend layer for GenLayer fee approval and transaction tracking. It turns a transaction plus optional developer fee profile into a quote, shows the user what they are funding, submits through an EIP-1193 wallet provider and tracks the result. + +## Install + +```jsonc +{ + "dependencies": { + "@genlayer/transaction-kit": "github:genlayerlabs/genlayer-transaction-kit#pkg/core", + "@genlayer/transaction-kit-react": "github:genlayerlabs/genlayer-transaction-kit#pkg/react" + } +} +``` + +Vue apps use `#pkg/vue`. The adapters are thin UI packages around the same core flow. + +## Create the kit + +The kit depends on an injected [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) provider. MetaMask, Privy embedded wallets, external Privy wallets, WalletConnect and similar wallets work as long as they expose `request()`. + +```typescript +import { createTransactionKit } from '@genlayer/transaction-kit'; +import { testnetAsimov } from 'genlayer-js/chains'; +import feeProfile from './fee-profile.json'; + +const kit = createTransactionKit({ + chain: testnetAsimov, + provider: window.ethereum, + account: userAddress, + suggestions: feeProfile, +}); +``` + +`suggestions` is optional. Without it, the kit falls back to network defaults. With it, matching methods are quoted from your measured developer fee profile while prices and caps still come from live network reads. + +## Use the React panel + +```tsx +import { GenLayerTransactionPanel } from '@genlayer/transaction-kit-react'; +import '@genlayer/transaction-kit-react/styles.css'; + + refetchAppState()} +/> +``` + +The panel owns the normal flow: + +1. estimate the fee policy +2. show an itemized fee receipt +3. let the user choose `low`, `standard` or `high` appeal posture +4. show price-protection caps +5. ask the wallet to sign +6. track the transaction +7. surface success, failure or an unresolved outcome + +Deploys use the same panel shape: + +```tsx + +``` + +## Use the headless flow + +Use the headless API when you need a custom UI: + +```typescript +const quote = await kit.estimate( + { preset: 'standard' }, + { kind: 'write', address: contract, method: 'place_bet', args: [42] }, +); + +const { genlayerTxId } = await kit.submit(quote, { + kind: 'write', + address: contract, + method: 'place_bet', + args: [42], +}); + +const final = await kit.track(genlayerTxId, (status) => { + console.log(status.phase, status.statusName, status.executionResultName); +}); +``` + +The same flow is also exposed through framework hooks/composables where available. + +## Presets, profiles and overrides + +The kit builds fee options in this order: + +1. user preset: `low`, `standard` or `high` +2. developer profile: measured method or deploy suggestions +3. explicit caller overrides + +Use the preset for the user's appeal posture. Use the profile for method-specific allocations, message budget and rotations. Use overrides only when the app deliberately knows more than the default profile for a specific flow. + +```typescript +await kit.estimate( + { + preset: 'high', + overrides: { + totalMessageFees: 10_000_000_000_000_000n, + }, + }, + { kind: 'write', address: contract, method: 'register_and_claim', args }, +); +``` + +Overrides are useful for advanced apps that can distinguish modes before submission. For example, if a method has a rare expensive branch and the app can prove the current user cannot hit it, the app may override a lower message budget. The default committed profile should still be conservative. + +## Long-running transactions + +Do not force users to stare at a modal for minutes. Transactions that call LLMs, fetch web data or wait through appeals can take time. Use the panel timeline for immediate feedback, but track the transaction in your backend or application state as well. A user should be able to navigate away and come back to the outcome. From 5938da87836adf1dcf1f0d44e377a9814a8cc1e3 Mon Sep 17 00:00:00 2001 From: Edgars Date: Tue, 16 Jun 2026 10:22:16 +0100 Subject: [PATCH 7/7] docs: document CLI fee profile flow --- .../fee-profiling-and-estimation.mdx | 27 +++++++++++++++---- .../fees-and-transaction-kit.mdx | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/pages/developers/decentralized-applications/fee-profiling-and-estimation.mdx b/pages/developers/decentralized-applications/fee-profiling-and-estimation.mdx index 18d84bf44..1fd69ccab 100644 --- a/pages/developers/decentralized-applications/fee-profiling-and-estimation.mdx +++ b/pages/developers/decentralized-applications/fee-profiling-and-estimation.mdx @@ -282,22 +282,39 @@ tx_hash = client.write_contract( ## Use the profile with the CLI -The CLI accepts fee estimate options as JSON. Extract the method profile and pass the same fields to `estimate-fees`: +The CLI can consume the generated profile directly: ```bash -genlayer estimate-fees \ +genlayer estimate-fees 0x123456789abcdef create_bet \ + --fee-profile frontend/fee-profile.json \ + --fee-preset standard \ --json \ - --fees '{"leaderTimeunitsAllocation":"125","validatorTimeunitsAllocation":"250","executionBudgetPerRound":"786297","totalMessageFees":"0","appealRounds":"1","rotations":["1","1"]}' + --args "2024-06-20" "Spain" "Italy" "1" ``` -Then pass the printed transaction fee object to `genlayer write` or `genlayer deploy`: +For writes, pass the same profile path when submitting: ```bash genlayer write 0x123456789abcdef create_bet \ - --fees '{"distribution":{"leaderTimeunitsAllocation":"125","validatorTimeunitsAllocation":"250","executionBudgetPerRound":"786297","totalMessageFees":"0","appealRounds":"1","rotations":["1","1"]},"feeValue":"..."}' \ + --fee-profile frontend/fee-profile.json \ + --fee-preset standard \ --args "2024-06-20" "Spain" "Italy" "1" ``` +For deploys, the CLI uses the profile's `deploy` entry: + +```bash +genlayer deploy \ + --contract contracts/football_bets.py \ + --fee-profile frontend/fee-profile.json +``` + +`write` and targeted `estimate-fees` use `methods[method]`. The CLI converts the measured profile entry into SDK estimate options, reads current prices from the backend, then submits the returned `distribution` and `feeValue`. + +`--fee-preset` controls the appeal posture. The standard preset maps to one appeal round; `low` maps to zero and `high` maps to two. Use `--appeal-rounds` for an explicit override. + +Use `--fees` alongside `--fee-profile` only when a flow needs to override part of the generated profile, for example a custom `messageAllocations` set. Use `--fee-value` only when you need to force the transaction deposit instead of using the SDK estimate. + This is intentionally the same policy flow as transaction-kit: the profile provides allocation suggestions, while the SDK/CLI estimate reads current network prices and produces the final deposit. ## Regenerate policy diff --git a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx index 8f3ba7f3d..dd8de0c30 100644 --- a/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx +++ b/pages/developers/decentralized-applications/fees-and-transaction-kit.mdx @@ -42,6 +42,6 @@ This avoids live simulation before every user transaction. GenLayer transactions | `genlayer-py` | Python SDK methods for the same estimate and submit flow in scripts, services and CLIs. | | `@genlayer/transaction-kit` | Core estimate, submit, track and verification flow. | | `@genlayer/transaction-kit-react` / `-vue` | Drop-in UI for fee receipt, preset selection, price protection, signing and status tracking. | -| GenLayer CLI | `estimate-fees`, `deploy` and `write` commands that accept fee JSON for terminal workflows. | +| GenLayer CLI | `estimate-fees`, `deploy` and `write` commands that accept `fee-profile.json` for terminal workflows. | | `gltest --fee-profile` | Measures fee usage from contract tests and writes the profile consumed by apps and tools. | | GenLayer Explorer | Shows transaction fee allocation, consumption, refunds and settlement details. |