Skip to content

CHIP-0062: The Forge — Weighted N-Asset AMM - #217

Open
awizardxch wants to merge 2 commits into
Chia-Network:mainfrom
awizardxch:chip-awizard-weighted-n-asset-amm
Open

CHIP-0062: The Forge — Weighted N-Asset AMM#217
awizardxch wants to merge 2 commits into
Chia-Network:mainfrom
awizardxch:chip-awizard-weighted-n-asset-amm

Conversation

@awizardxch

@awizardxch awizardxch commented Sep 11, 2026

Copy link
Copy Markdown

Proposes a weighted constant-function market maker in which one singleton holds any number of assets, each in its own reserve coin, at weights fixed at pool creation. Built on CHIP-0050's action layer; authorizations are CHIP-0025 messages whose receiver coin id the pool derives rather than accepts.

Informational / Puzzle. Requires CHIP-0025 and CHIP-0050.

Supporting material — the full specification, architecture, a written CLVM pass over every leaf, security notes, and a comparison against publicly documented AMM failures — is referenced from the public forge-puzzles repository, alongside the implementation it describes.


Note

Low Risk
Documentation-only addition of a draft CHIP; no runtime, consensus, or dependency changes in this repository.

Overview
Adds CHIP-0062 (Draft, Informational / Puzzle) documenting The Forge: a weighted N-asset constant-function AMM built as a CHIP-0050 action-layer singleton with per-asset reserve coins and pool-authorized LP CAT.

The new spec defines pool configuration/state, the weighted product invariant, action leaves (swap, add, remove, collect, observe, DAO fee decrease), CHIP-0025 authorizations with derived receiver coin ids (not solution-supplied), a sorted on-chain registry, and security/testing expectations. It requires CHIP-0025 and CHIP-0050, states no consensus change, and points normative detail and the reference implementation to the external forge-puzzles repo (testnet11 deployment noted).

Note: Comments-URI references PR #217 while linking to pull/192 — likely a metadata typo for editors to fix.

Reviewed by Cursor Bugbot for commit 29203bd. Bugbot is set up for automated code reviews on this repo. Configure here.

Proposes a weighted constant-function market maker in which one singleton
holds any number of assets, each in its own reserve coin, at weights fixed
at pool creation. Built on CHIP-0050's action layer; authorizations are
CHIP-0025 messages whose receiver coin id the pool derives rather than
accepts.

Informational / Puzzle. Requires CHIP-0025 and CHIP-0050.

Supporting material — the full specification, architecture, a written CLVM
pass over every leaf, security notes, and a comparison against publicly
documented AMM failures — is referenced from the public forge-puzzles
repository, alongside the implementation it describes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 9713658. Configure here.

Comment thread CHIPs/chip-0062.md
| DAO fee | Lowers the DAO rate on a message from the configured recipient. The new rate MUST be strictly lower than the current one. |

One or more leaves may run in a single spend; the action layer threads state from
each to the next, and the finalizer commits the last.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty action spend not forbidden

High Severity

The design uses CHIP-0050's action layer and a finalizer that recreates all N reserves on every spend, but it never requires the inner puzzle to run at least one leaf. The action-layer rule is that a singleton must not be spendable with an empty action list, because a prior CHIP-0050 attempt to block no-action spends failed review.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by learned rule: Action layer and slots security

Reviewed by Cursor Bugbot for commit 9713658. Configure here.

@danieljperry danieljperry changed the title CHIP: The Forge — Weighted N-Asset AMM CHIP-0062: The Forge — Weighted N-Asset AMM Sep 11, 2026
@danieljperry

Copy link
Copy Markdown
Contributor

This CHIP is now a Draft. Please leave your reviews here, and feel free to discuss it in the #chips channel of our Discord.

@danieljperry

Copy link
Copy Markdown
Contributor

@awizardxch Thanks for creating this CHIP. I have updated the file name and added myself as a reviewer. Otherwise, I haven't made any changes. You may want to update the formatting to remove the mid-paragraph new line characters, as well as the non-standard U+2014 characters.

@greimela greimela left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Daybreak Blue security review

These findings come from a ChatGPT Daybreak Blue review of PR head 29203bd and Forge implementation f8fbcaa.

Severity Finding
P0 Genesis can authorize more LP tokens than pool state records
P1 Successive singleton generations can backfill oracle time
P1 The final LP position cannot redeem
P1 The protocol-fee units disagree
P1 Reserve receiver derivation trusts solution data

The first two issues were reproduced with spend bundles accepted by consensus validation. The terminal-LP and reserve-substitution cases were tested against the compiled puzzles.

Comment thread CHIPs/chip-0062.md
| A substituted coin satisfying an authorization | Derived receiver ids; CHIP-0025 mode binds sender puzzle and receiver coin at consensus |
| Value extracted by chaining actions in one spend | The invariant holds per action; suites value the actor's closing position at pre-sequence prices |
| A negative or reversed trade amount, reversing the fee | Sign assertions in the leaf, and an invariant unsatisfiable for such inputs |
| LP minted or melted without the pool's consent | The TAIL requires a message from the pool and a CAT parent; the melt inner's only behaviour is to destroy its whole amount |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0: Bind genesis issuance to a single LP eve

Impact: A pool creator or keyless router can mint LP tokens that the pool state does not record, wait for users to deposit, then redeem the hidden supply for reserve assets.

Attack path

  1. The TAIL genesis branch accepts the singleton launcher coin announcement instead of a pool message.
  2. That announcement does not commit to the LP eve coin making the assertion.
  3. Multiple independently funded eves can reuse the announcement in one bundle.

Reproduction: A complete registered-pool bundle passed consensus validation. Pool state recorded 5,000,000 LP, while two eves minted 10,000,000 LP in total.

Required change: Bind genesis authorization to exactly one derived LP eve coin ID. Add a regression test that rejects duplicate eves.

Relevant Forge code

Comment thread CHIPs/chip-0062.md
| LP minted or melted without the pool's consent | The TAIL requires a message from the pool and a CAT parent; the melt inner's only behaviour is to destroy its whole amount |
| An LP token used against a pool of a different revision | The TAIL asserts the protocol version |
| Two pools claiming the same market | The sorted registry |
| Oracle manipulation within one spend | The accumulator advances on pre-spend prices, with the height pinned across every action in a bundle |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Prevent cross-generation oracle backfilling

Impact: An attacker can make the oracle report elapsed blocks at a manipulated price even though no block elapsed. A downstream contract that releases assets based on this TWAP can be drained.

Attack path

  1. Spend the pool at declared height H - 31 and perform a swap.
  2. In the same bundle, spend the ephemeral successor at declared height H and observe the new price.
  3. The same-height guard only covers leaves within one action-layer spend, so the cross-generation pair passes.

Reproduction: The compiled bundle passed consensus validation and recorded 31 blocks at the manipulated price at one actual height. Spot moved from 1, while the claimed 32-block TWAP became 3.9004.

Required change: Bind elapsed time to actual coin birth heights across singleton generations. If the puzzle cannot enforce that relationship, remove the oracle from this proposal.

Comment thread CHIPs/chip-0062.md
|---|---|
| `swap` | One reserve rises, one falls. Requires a positive input and a positive claimed output, distinct in-range asset indices, and an exact invariant match. The protocol and DAO slices are taken from the output and accrued in state. |
| `add` | Deposits to one or more reserves, minting LP. Requires a positive LP delta, non-negative deposits, and at least one positive deposit. |
| `remove` | Burns LP and pays a proportional share of every reserve. Requires the burn to be positive and strictly less than total LP, so the pool outlives every withdrawal. |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Provide a redeemable terminal state

Impact: The final LP holder cannot withdraw their share of the reserves. Another holder can redeem all but the victim's final LP unit and leave that share permanently trapped.

Cause: The rule burn < total_lp forbids burning the remaining LP supply, while the registry accepts any positive supply without requiring a minimum relationship to the reserves.

Reproduction: A compiled pool registered successfully with reserves of 10,000,000 and 20,000,000 and total_lp = 1. Its only LP unit could not be burned.

Required change: Add a terminal close path. Alternatively, define a bounded minimum-liquidity amount, lock it deliberately at genesis, and enforce that invariant during registration.

Comment thread CHIPs/chip-0062.md

**Configuration** is curried and therefore immutable for the life of a pool:
asset ids in canonical order, the weight of each, the trade fee in basis points,
the protocol fee in parts per million, the LP TAIL hash, and the DAO recipient's

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Use one protocol-fee unit

Impact: A wallet following the CHIP can authorize a fee 100 times larger than intended, diverting the difference to the protocol recipient.

Source Stated unit Denominator
This CHIP Parts per million 1,000,000
Referenced Forge specification and code Basis points 10,000

For example, a value of 5 means 5 ppm under this text but 5 bps in the puzzle.

Required change: Choose one unit, denominator, and maximum. State them normatively and use the same names in the implementation.

Relevant Forge code

Comment thread CHIPs/chip-0062.md
### Authorization

Every reserve release and every LP mint or melt is a CHIP-0025 `SendMessage` with
mode `SENDER_PUZZLE | RECEIVER_COIN`. The receiver's coin id MUST be derived by

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Commit the canonical reserve parent

Impact: An attacker can replace the expected reserve successor and strand the canonical reserve, breaking the proposal's anti-substitution guarantee. This is not a free drain by itself because the attacker must provide replacement value.

Cause: The reference finalizer accepts reserve_parent_ids from its solution and uses those values in coinid(...). The pool does not independently commit to or authenticate them.

Reproduction: An attacker-created reserve with the same puzzle and amount satisfied the message and became the successor while the canonical reserve remained unspent.

Required change: Commit the reserve parent or full coin ID in pool state, or prove its lineage before deriving the receiver.

Relevant Forge code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants