# Overview

Frankencoin (ZCHF), Frankencoin Share Token (FCS) and the structure of the system.

## Structure and Purpose

Frankencoin is a collateral-backed Swiss franc stablecoin system. This documentation explains minting, savings, equity and governance. The [application](https://app.frankencoin.com) provides transaction interfaces; the [research publication](https://app.frankencoin.com/thesis-frankencoin.pdf) examines the economic model. Documentation feedback belongs in the [Frankencoin Telegram group](https://t.me/frankencoinzchf); the source is the [GitBook repository](https://github.com/Frankencoin-ZCHF/gitbook).

## Frankencoin (ZCHF) and Frankencoin Share Token (FCS)

| Token                                                                                         | Role                                                                                    |
| --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| [Frankencoin (ZCHF)](https://etherscan.io/address/0xB58E61C3098d85632Df34EecfB899A1Ed80921cB) | Stablecoin intended to track the Swiss franc                                            |
| [Frankencoin Share Token (FCS)](/pool-shares)                                                 | Canonical governance and share token, representing participation in the system's equity |

FCS holders participate in [governance](/governance) and share the economic gains and losses of the reserve's equity capital. Start with [investing and pool shares](/pool-shares) for acquisition, voting and exits, then use the [FCS mechanics reference](/pool-shares/fcs) for contract rules. Existing FPS and WFPS holders can use the [migration guide](/pool-shares/fcs-migration).

FPS continues as the underlying equity token: each FCS wraps one FPS. The Equity contract holds capital and prices that underlying token. Its [Ethereum address](https://etherscan.io/address/0x1bA26788dfDe592fec8bcB0Eaff472a42BE341B2), supply and prices remain FPS identities, not FCS data. The mechanics reference identifies the audited `FPS2` version behind the FCS design.

ZCHF has no fixed redemption promise for Swiss francs. Collateral, reserves, borrowing costs and market activity support its exchange rate. Auctions test the value of collateral without an external price oracle. They can take days, so their response differs from oracle-triggered liquidations. [Positions](/positions) describe the mechanism; [Risks](/risks) describes its failure modes.

Net fees and liquidation results change equity capital; losses reduce it. FCS governance uses vetoes, with time-weighted votes rather than token balances alone. A qualified action needs more than 1% of internal FCS voting power and the FCS contract meeting the underlying FPS quorum.

## Use Cases

### Payments

ZCHF can be transferred between addresses or used through payment services. The [Frankencoin website](https://frankencoin.com) lists services. On another chain, the token address, network and transfer route identify the asset; see [cross-chain transfers](/bridge-to-other-chains).

### Store of Wealth

ZCHF provides Swiss franc-denominated exposure on-chain. Its market price can diverge from one Swiss franc. [Savings](/savings) pays a governance-set rate to deposited ZCHF, with terms that depend on the savings contract version.

### Borrowing / Seignorage

Users can mint ZCHF against collateral in a position. The position records collateral and an amount to repay. A mint deducts the applicable up-front fee and retains a minter reserve, so the wallet receives less than the gross minted amount. The [opening](/positions/open), [cloning](/positions/clone) and [adjustment](/positions/adjust) guides explain the terms.

## Technical Architecture

| Component                               | Function                                                                                 |
| --------------------------------------- | ---------------------------------------------------------------------------------------- |
| ZCHF token and approved minting modules | Create, move and burn ZCHF under each module's rules                                     |
| MintingHub and positions                | Manage collateral-backed minting, challenges and settlement                              |
| FCS and governance modules              | Provide the share-token interface, time-weighted voting and ZCHF entry and exit paths    |
| Underlying Equity / FPS                 | Hold equity capital, price the FPS backing and maintain the underlying votes used by FCS |
| Savings                                 | Pay interest from the system to deposited ZCHF                                           |
| Stablecoin-conversion bridges           | Exchange ZCHF against a specified external stablecoin under a limit and expiry           |
| Cross-chain bridges                     | Transfer ZCHF between supported chains; distinct modules send voting snapshots           |

The [governance application](https://app.frankencoin.com/governance) lists proposed modules and their state. The early architecture used an XCHF bootstrap bridge; that historical example is not a statement about today's module count or bridge availability.


# Bridge to other Chains

Cross-chain ZCHF transfers and the distinction from voting synchronisation.

A cross-chain route transfers ZCHF between supported networks. A stablecoin-conversion route exchanges ZCHF for another stablecoin; it is not the same operation. The [Frankencoin website](https://frankencoin.com) lists network and service references, and the [application](https://app.frankencoin.com) provides the available transfer interfaces.

## Network and token identity

Ethereum mainnet has chain ID **1**. Its ZCHF token is [0xB58E61C3098d85632Df34EecfB899A1Ed80921cB](https://etherscan.io/address/0xB58E61C3098d85632Df34EecfB899A1Ed80921cB). Destination instances have their own chain IDs and token addresses. The maintained [token information API](/api-docs/ecosystem) supplies network-specific references; a matching token symbol alone does not identify a destination token or an available route.

The following destination references were returned by the public token information API on **18 September 2026**. They identify indexed ZCHF instances, not a guarantee of an active route between every pair.

| Chain             | Chain ID | Indexed ZCHF address                         |
| ----------------- | -------: | -------------------------------------------- |
| Optimism          |       10 | `0xd4dd9e2f021bb459d5a5f6c24c12fe09c5d45553` |
| Gnosis            |      100 | `0xd4dd9e2f021bb459d5a5f6c24c12fe09c5d45553` |
| Polygon           |      137 | `0xd4dd9e2f021bb459d5a5f6c24c12fe09c5d45553` |
| Sonic             |      146 | `0xd4dd9e2f021bb459d5a5f6c24c12fe09c5d45553` |
| Base              |     8453 | `0xd4dd9e2f021bb459d5a5f6c24c12fe09c5d45553` |
| Arbitrum One      |    42161 | `0xd4dd9e2f021bb459d5a5f6c24c12fe09c5d45553` |
| Avalanche C-Chain |    43114 | `0xd4dd9e2f021bb459d5a5f6c24c12fe09c5d45553` |

Route availability also depends on bridge configuration, limits and current state, not just the existence of tokens on both chains.

## Transfer sequence

1. Select the source and destination networks in the transfer interface. Match the wallet's source chain, the source token address and the destination token reference.
2. Set the recipient on the destination chain and the ZCHF amount. Contract-wallet addresses can differ between chains.
3. Read the route quote: source transaction gas, bridge or message fee, destination amount and any execution limits. Destination gas may be needed for later use of the received tokens; receiving ZCHF does not itself supply that gas.
4. If required, approve the route's spender, then submit the transfer. Approval alone does not send the tokens across chains.
5. Record the source transaction and message identifier. A confirmed source transaction is not destination completion.
6. Track delivery in the route's status view or, for CCIP routes, [CCIP Explorer](https://ccip.chain.link/). Destination completion means the destination transaction succeeded and the expected token balance arrived.

Rate limits, message execution errors and chain conditions can delay delivery. An unresolved source transfer should be traced by its identifier rather than assumed absent and sent again. The [bridge dependencies](/risks#ccip-and-blockchain-hacks) describe how a compromised chain or message path affects the system.

## FCS votes are not bridged tokens

[FCS](/pool-shares) is the governance and share token; the audited design keeps those shares on mainnet. To take part in governance on another chain, synchronise the FCS contract's underlying FPS votes and the individual holders' FCS votes. [Cross-chain governance](/governance#cross-chain-governance) guides both steps. A voting snapshot is not a spendable FCS balance on the destination chain, and the ZCHF transfer sequence above does not migrate FCS.


# Savings

Version-specific savings, interest collection and referral terms.

The [savings application](https://app.frankencoin.com/savings) lets users deposit ZCHF and earn a rate set through [FCS governance](/governance). Interest comes from the system's equity, reducing the net income available to [FCS holders](/pool-shares#economics); borrowing and savings rates are separate parameters. Depositing ZCHF in savings does not acquire FCS or voting power. Deposited ZCHF remains attributed to the savings account rather than being lent out to borrowers.

## Contract versions

Etherscan's published source and ABI for these Ethereum addresses were checked on 18 September 2026. They confirm the withdrawal-lock distinction and public methods below. Repository references are pinned to `8b4c4ab67bb361b91d58c474b87f4608fc4c0566`; the application may select a different module. The first explorer labels its contract `Savings`, despite the repository's later `SavingsV2.sol` naming.

| Version                  | Existing address reference                                                                                                 | Source behaviour                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Legacy SavingsV2         | [0x3bf301b0e2003e75a3e86ab82bd1eff6a9dfb2ae](https://etherscan.io/address/0x3bf301b0e2003e75a3e86ab82bd1eff6a9dfb2ae#code) | [SavingsV2.sol](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/minting/v2/SavingsV2.sol) applies an interest delay and a `FundsLocked` withdrawal check                                                                                                                                                                                   |
| Referral-enabled Savings | [0x27d9AD987BdE08a0d083ef7e0e4043C857A17B38](https://etherscan.io/address/0x27d9AD987BdE08a0d083ef7e0e4043C857A17B38#code) | [Savings.sol](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/savings/Savings.sol) inherits [AbstractSavings.sol](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/savings/AbstractSavings.sol): an interest delay, no equivalent ticks-based withdrawal lock, and referral support |

### Saving

A savings deposit transfers ZCHF to the selected module and credits the account. An old deposit remains in its original contract. Moving to a different module requires withdrawing under the old module's rules and depositing into the new one; it is not an automatic upgrade. The new deposit uses the new module's delay and referral terms.

To start saving:

1. **Select the account and module.** Connect the wallet holding your ZCHF and choose the intended network in the savings application. Identify the module address and its version; the table above covers the two checked Ethereum contracts. If you already have savings, locate the module holding that balance rather than assuming the application's selected module is the same one.
2. **Read the terms.** Check the annual rate, interest delay, withdrawal conditions and any referrer and referral fee. The quoted rate is an annual simple rate, not an automatically compounded APY. A referral fee reduces the interest you receive, not your deposited principal.
3. **Choose the amount.** Decide how much ZCHF to add and keep native currency for gas. A `save(amount)` transaction adds that amount; `adjust(targetAmount)` sets a target saved balance after collecting interest. They are not interchangeable inputs.
4. **Approve and deposit.** If needed, approve the chosen savings module to transfer the ZCHF, then submit the deposit transaction. Approval alone leaves the funds in your wallet; it does not start earning interest.
5. **Confirm the account credit.** After confirmation, read the saved balance, delay and referral terms on that same module. Check the wallet debit as well. A read-only API response or an unconfirmed transaction is not a completed deposit.

### Delay

Both source versions use a three-day delay before a fresh deposit earns interest. Adding funds to an existing account produces a weighted delay rather than restarting the full delay on the entire balance.

In **SavingsV2**, `withdraw` reverts with `FundsLocked` while the account's stored ticks exceed current ticks. The delay therefore also restricts withdrawals. In the **referral-enabled source**, `withdraw` has no equivalent ticks-based lock, so the interest delay does not itself prevent withdrawing principal. The contract version, not the page title, determines which behaviour applies.

### Interest

The rate can change through [governance](/governance#proposal-submission). Interest accrues on the stored principal. Uncollected interest does not compound; collection adds the user's interest to principal, after which it can earn interest too. Deposits and withdrawals refresh accrued interest as part of their operation.

### Collecting interest

Read `accruedInterest(account)` on your savings module to see gross pending interest. If a referrer is set, subtract its fee to find the net amount added to your savings. The API's historical collected-interest field is not this pending amount.

You can wait until your next deposit or withdrawal to collect, or submit `refreshMyBalance()` as a separate transaction. Collection adds net interest to the saved balance, not to the spending balance in your wallet. Afterwards, check the increased savings balance and any referral payment. Merely reading or simulating the method does not collect interest.

### Withdrawing savings

Return to the module that holds your savings and choose the ZCHF amount and recipient. The public `withdraw(target, amount)` operation collects interest and transfers funds to that recipient. Check the refreshed available balance and the version's withdrawal conditions before submitting. In SavingsV2, a remaining ticks-based delay can block the withdrawal; in the referral-enabled version, the interest delay alone does not lock principal.

After confirmation, check both the reduced saved balance and the recipient's ZCHF balance. To move to a different savings module, confirm this receipt before approving and depositing into the new one. That new deposit starts under the destination module's interest-delay and referral rules.

## Referral Module

A referral fee allocates a share of **earned interest**, not a share of deposited principal, to a referrer. The maximum is 250,000 parts per million (ppm), or 25%. At 200,000 ppm, 100 ZCHF of gross interest becomes 80 ZCHF for the user and 20 ZCHF for the referrer.

### How It Works

The referral-enabled source exposes these signatures:

```
save(uint192 amount, address referrer, uint24 referralFeePPM)
adjust(uint192 targetAmount, address referrer, uint24 referralFeePPM)
```

Amounts use ZCHF's 18-decimal base units. The fee uses a denominator of 1,000,000; for example, `200_000` means 20%, not 20 basis points. The transaction interface should show the referrer, fee percentage and resulting net interest before the user signs.

### Using the Frankencoin App to Refer Users

A referral link is an application feature, not a contract method. Its query parameters must match the selected application's implementation. The signed transaction's referrer and `referralFeePPM` determine the on-chain setting; an abbreviated address is not a valid transaction argument.

### Claiming Accrued Referral Fees

Collection pays the referrer when the user's interest is refreshed. A separate collection transaction is optional because deposits and withdrawals also refresh interest. The public methods in both pinned source versions are:

```
refreshMyBalance()
refreshBalance(address owner)
```

The account holder can use `refreshMyBalance()`. A referrer or another caller can use `refreshBalance(userAddress)`. The internal `refresh(address)` function is not an externally callable method. Refreshing another account does not change its owner or redirect the user's principal.

### Wallet Implementation Guide

An integration supplies the amount, referrer and fee to a supported method of the selected contract. It displays the gross rate, referral share and net rate separately. The maximum fee is a protocol limit, not a default fee.

The account holder can remove the referral by calling **`dropReferrer()`** on the referral-enabled contract through an interface exposing its verified ABI. This first collects accrued interest and settles the accrued referral fee, then clears the referrer and sets the fee to zero. Future interest has no referral deduction unless a later transaction sets a referrer again. Removal does not reverse fees already earned.

To remove it, select the module holding your account, read the current referrer and fee, then submit `dropReferrer()` from the account-owning wallet. After confirmation, read the account again: the referrer must be the zero address and the fee zero. Check later deposit or adjustment transactions as well, since a referral-enabled interface can supply referral terms again. These are contract operations, not a claim that every wallet exposes a removal button.

### Integration Details

[The savings API reference](/api-docs/savings) describes indexed balances and rates. API reads do not create deposits, collect interest or remove a referrer; those are on-chain transactions.


# Collateralized Minting

Minting Frankencoins against collateral, with version-specific borrowing terms.

A **position** holds a user's collateral and records the Frankencoins they have minted against it. It has one owner, initially its creator; ownership can be transferred. The owner can add collateral and mint within the position's price, collateral, time and capacity constraints.

Anyone can challenge a position when they believe the **market value of the collateral is below the stored liquidation price**, not the other way around. A challenge starts a [two-phase auction](/positions/auctions). Challengers must supply collateral of their own, which can be bought during the first phase. A challenge is not a risk-free price report. This mechanism replaces an external price oracle; it depends on participants being able and willing to challenge.

There are two entry paths:

* [Open a new position](/positions/open): an advanced-user proposal with configurable parameters and a veto period. The application provides a [creation route](https://app.frankencoin.com/mint/create).
* [Clone an existing position](/positions/clone): use an accepted position's terms, subject to available capacity and its expiry. This is usually the simpler path.

FCS holders oversee new proposals through [veto-based governance](/governance). Borrowing fees and liquidation results feed into the [reserve](/reserve), connecting minting to the equity in which FCS holders participate. Borrowers do not need FCS to open or clone a position.

## Contract versions

The implementation matters. This guide distinguishes the newer `contracts/minting/` source from historical screenshots and legacy terms. The source references are pinned to commit `8b4c4ab67bb361b91d58c474b87f4608fc4c0566`:

* [Position.sol](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/minting/Position.sol)
* [MintingHub.sol](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/minting/MintingHub.sol)

The chain, position address and originating hub identify a deployed position; its verified code and transaction quote determine its terms. The pinned source describes a version, not every existing position. Existing positions do not automatically gain a newer implementation's terms.


# Opening New Positions

Proposing a collateralised position and selecting its borrowing terms.

If the available positions do not offer your collateral or borrowing terms, you can propose a new position. You choose its collateral, capacity, price and other terms, deposit the initial collateral and pay a proposal fee. Qualified holders then have time to examine and veto it. If an accepted position already suits your needs, [cloning it](/positions/clone) avoids a new proposal and its waiting period.

Start at the [creation page](https://app.frankencoin.com/mint/create). The walkthrough below follows the decisions needed to create a position, rather than a particular screen layout. Contract rules refer to the [pinned minting sources](/positions#contract-versions); screenshots show a historical interface, not current quotes or button names.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-25af64ed7274561188bf974c39f79b9a46113df0%2Fkuva.png?alt=media" alt="Historical new-position button"><figcaption><p>Historical entry point for proposing a position.</p></figcaption></figure>

## Before you start

Connect the wallet that will own the position on the intended chain. You need the initial collateral, ZCHF for the proposal fee and the network's native asset for transaction gas. Identify the collateral by its token address, not just its symbol. The chain and selected minting hub determine which version's rules apply.

Plan both the gross amount you want to mint and the net ZCHF you need in your wallet. The proposal fee is separate from the fee and reserve deducted when you later mint. Opening the position transfers collateral but does not yet supply spending money.

## Proposal terms

In the pinned source, opening a position costs **1,000 ZCHF**. The fee goes to equity and is not returned after a veto. Choose an initialisation period of **at least three days**. During that period, a qualified holder can veto the proposal; a denied position cannot mint. The [FCS governance](/governance#veto-process) guide explains the holder and underlying-contract qualification checks.

Position creation is separate from the FCS minter-application rule of **1,200 ZCHF and 60 days**. You are proposing a collateral position, not applying to add a minting module.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-7c4689f1f71dc325fe43938e0130a018a6faf290%2Fkuva.png?alt=media" alt="Historical position proposal terms"><figcaption><p>Historical proposal form: fee and initialisation period.</p></figcaption></figure>

## Interest and minting capacity

Choose the term, risk premium and family minting limit together:

* **Term:** decide how long you need the position. In the pinned source, the duration runs from the end of initialisation to expiry. Later mints pay for the remaining term, not a new full term. At expiry, further minting stops and collateral can enter the [forced-sale process](/risks#missing-maturity-dates).
* **Risk premium:** choose the position-specific addition to the global borrowing rate. The newer `contracts/minting/Position.sol` calculates the annual rate as **global borrowing rate plus risk premium**. The proposer does not set the global rate. Contract inputs use parts per million (ppm): 10,000 ppm is 1%.
* **Family minting limit:** choose the maximum gross ZCHF debt for the original position and its clones together. This bounds the system's exposure through this position family; it is not the amount you must borrow at opening.

Minting deducts the interest fee up front, using the applicable annual rate at that mint and a 365-day year. The source caps the fee fraction at 100%. For an illustration, a global rate of 2% plus a 1% risk premium gives 3% a year. Minting 10,000 ZCHF with 365 days remaining incurs a 300 ZCHF fee; half that term incurs 150 ZCHF, before integer rounding. These are assumed rates, not current quotes. Repaying early does not return the fee.

Older interfaces described a user-set annual interest rate. For the pinned newer source, read both rate components and the resulting up-front fee rather than treating that older field as the complete formula.

The original position and its clones share a family minting limit. The original's collateral reserves some capacity for its own minting; family mints and repayments change what remains for clones. Capacity is not permanently split into independent clone allocations. A displayed available amount can change before execution.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-e0fca95d5656b3e2b644bf84b3b9b0a8e62c79de%2Fkuva.png?alt=media" alt="Historical position financial terms"><figcaption><p>Historical financial terms; field names differ between versions.</p></figcaption></figure>

## Collateral and liquidation price

### Choose the asset and amounts

Enter the collateral token address for the selected chain. Examine how other participants can obtain and value that asset: challengers must supply the same token to use the [auction mechanism](/positions/auctions). The [collateral discussion](https://github.com/Frankencoin-ZCHF/FrankenCoin/discussions/11) explains selection considerations.

Set the **minimum collateral** in units of that token, then choose an **initial collateral** amount at least as large. The minimum prevents small residual positions. In the pinned implementation, withdrawing below it closes the position and requires clearing its debt. The initial amount is what the hub transfers from your wallet into the position when the proposal transaction succeeds, not when you later mint.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-85da236cd4255e0701496b25746df48caaf73f0f%2Fkuva.png?alt=media" alt="Historical collateral fields"><figcaption><p>Historical collateral selection: token, minimum amount and initial deposit.</p></figcaption></figure>

### Set the liquidation price

The liquidation price is the stored ZCHF value per collateral token used for minting and challenges. It is not an oracle price or an automatically calculated debt-to-collateral ratio. A higher price supports more gross debt for the same collateral, but makes a challenge more likely to be justified when the market price falls below it. Minting less does not itself lower this stored price.

In the pinned `MintingHub.openPosition`, minimum collateral multiplied by liquidation price must represent at least **5,000 ZCHF**. This is a rule of that version, not a universal minimum for every deployment. For example, two WETH at 2,500 ZCHF per WETH have a liquidation value of 5,000 ZCHF; 20 WETH at 250 ZCHF have the same value. Neither example is a market quote or a recommendation for WETH's price.

Check the amounts together: initial collateral must meet the minimum, its value at the stored price must cover the debt you intend to mint, and the family limit must permit that debt. The source also requires the minimum collateral's liquidation value not to exceed the family limit. A contract-valid proposal can still be vetoed during initialisation.

## Reserve and auction duration

Choose the **reserve contribution**, the fraction of each gross mint retained in the reserve rather than paid to your wallet. It can absorb losses under the [reserve rules](/reserve). With an unimpaired reserve, that allocation helps repay the debt later; it is not a refundable interest fee or an unconditional claim to cash.

For example, a 10% reserve on a gross mint of 10,000 ZCHF retains 1,000 ZCHF. With the illustrative 300 ZCHF interest fee above, your wallet receives 8,700 ZCHF. The separate 1,000 ZCHF proposal fee is not part of this mint calculation.

Choose the **challenge period** with the collateral's trading conditions in mind. Bidders need time to obtain funds and assess the asset; a longer sale also leaves more time for its price to move. In the pinned implementation, `challengePeriod` sets the duration of each of the two auction phases: first fixed-price, then declining-price. Check how the selected interface expresses that duration rather than assuming a field labelled “Auction Duration” means the combined time.

Collateral volatility and the time needed to sell it inform the reserve choice. A larger reserve leaves less net ZCHF from a mint but provides more funds for settlement. It does not guarantee that a sale covers all debt and rewards. The [auction guide](/positions/auctions) explains what each phase sells.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-f4cc4b8b09b09f44082000d9397b436720740810%2Fkuva.png?alt=media" alt="Historical liquidation parameters"><figcaption><p>Historical liquidation-price, reserve and auction-duration fields.</p></figcaption></figure>

## Submission and confirmation

1. **Review the proposal.** Check the chain, hub, collateral address, minimum and initial collateral, initialisation period, expiry, rate components, reserve contribution, stored liquidation price and family limit. Compare your intended gross mint with its net wallet receipt.
2. **Authorise the collateral transfer.** If needed, approve the selected hub to transfer the initial collateral. Keep enough ZCHF available for the proposal fee. Approval grants an allowance; it does not open the position.
3. **Submit the proposal transaction.** After confirmation, record the new position address and check its owner, deposited collateral, terms and start time. The fee has been paid and the collateral deposited; opening a proposal is not itself a ZCHF mint.
4. **Follow the initialisation period.** Read the proposal state and whether it was vetoed. Passing the waiting period alone does not override an active challenge, closure or another minting restriction.
5. **Mint from the accepted position.** Once it is eligible, open its management view and choose the amount to mint. Review the reserve, up-front fee and net receipt, then submit and confirm the mint. The [adjustment guide](/positions/adjust) covers this first mint and later changes.

If the proposal is vetoed, it cannot mint and the fee is not returned. Remaining collateral can be withdrawn under the position's withdrawal rules, including any challenge or cooldown restriction.


# Cloning Existing Positions

Borrowing through an existing position's terms and available family capacity.

A clone reuses an accepted position's terms without a new collateral proposal. On the [mint page](https://app.frankencoin.com/mint), select a position for the intended collateral and inspect its available capacity, rate, reserve contribution, liquidation price and expiry. The [version reference](/positions#contract-versions) distinguishes the newer source from legacy interfaces.

## Choose a position to clone

Connect the wallet that will supply the collateral and select the intended chain. Match the collateral token address, then compare the available positions. A lower stored liquidation price supports less debt per token but is less likely to exceed the market price. The up-front fee and reserve determine how much of a gross mint reaches your wallet.

Check that the parent can be cloned, not merely that its displayed capacity is positive. An active challenge, cooldown, expiry or closure can prevent cloning. Unlike a new proposal, an eligible clone can deposit collateral and mint in the same transaction, without a new proposal fee or initialisation period.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-0763288abe717ff207560149cbd1a8cd6d7034c7%2Fkuva.png?alt=media" alt="Historical list of positions available for cloning"><figcaption><p>Historical availability display, not a current capacity quote.</p></figcaption></figure>

In the pinned newer implementation, the original and its clones share a family limit. The original's collateral can reserve capacity, and other family mints can change what remains available. The displayed limit is not a separate guaranteed allocation to the clone. A clone's expiry cannot exceed the original position's expiry. If the selected parent is itself a clone, follow its `original` reference to find that bound.

## Set collateral, mint amount and expiry

1. **Choose the gross mint amount.** This becomes debt, including the retained reserve and up-front fee. If you need a particular net amount, use the transaction quote to find the gross mint required rather than entering the desired wallet receipt as debt.
2. **Set the collateral deposit.** It must meet the inherited minimum and cover the gross debt at the parent's stored liquidation price. At 1,500 ZCHF per WETH, two WETH cover 3,000 ZCHF of gross debt, subject to family capacity and the other limits. Extra collateral does not automatically lower that price.
3. **Choose the expiry.** Select a date within the original's permitted term. A shorter remaining term reduces the up-front interest fee at the same rate, but brings repayment or expiry settlement closer.
4. **Review the result.** Read the parent and original addresses, effective annual rate, collateral deposit, gross debt, retained reserve, fee and net ZCHF. The rate and capacity can change between viewing the page and execution.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-0c35c5d89fb4318fcde82982feb3295e3f0c2a8b%2Fkuva.png?alt=media" alt="Historical clone transaction preview"><figcaption><p>Historical clone preview.</p></figcaption></figure>

For the historical example, a gross mint of 3,000 ZCHF with a 10% reserve and a 60 ZCHF up-front fee gives the wallet 2,640 ZCHF. The reserve receives 300 ZCHF. The fee depends on the applicable annual rate and remaining term; the newer source uses the global borrowing rate plus the position's risk premium.

## Submit and manage the clone

Approve the selected minting hub to transfer the collateral if an allowance is needed, then submit the clone transaction. Token approval authorises spending; it does not create the clone or mint ZCHF. Keep the network's native asset available for gas.

After the clone transaction confirms, record the new position address and check its owner, collateral, gross debt, stored price, expiry and received ZCHF. The clone is your position; it does not transfer ownership of the parent. Subsequent minting, repayment and collateral changes follow the [adjustment guide](/positions/adjust). Monitor its expiry even if you do not plan further changes.


# Adjusting a Position

Changing debt, collateral and the explicitly stored liquidation price.

The position owner can change its debt, collateral and liquidation price within the contract's limits. The [pinned source version](/positions#contract-versions) is the reference below. Historical screenshots illustrate the interface, not a current quote.

## Select the position

Connect the wallet that currently owns the position, which may differ from its creator if ownership was transferred. Open its management view from the application's positions list. Match the chain and position address, then read its collateral balance, gross debt, stored liquidation price, expiry and any cooldown or challenge. A newly proposed position can hold collateral without having minted any ZCHF.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-b03b5e5d763ebdb775bfda280b098ff9531c0825%2Fkuva.png?alt=media" alt="Historical owned-position list"><figcaption><p>Historical position list.</p></figcaption></figure>

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-21ca4f6bbb07133785efe398cc535ab0bb4f7e4d%2Fkuva.png?alt=media" alt="Historical position adjustment fields"><figcaption><p>Historical adjustment form.</p></figcaption></figure>

## Minting and repayment

Choose whether you want to increase debt and receive ZCHF, or reduce debt using ZCHF from your wallet. For the combined `adjust(newMinted, newCollateral, newPrice)` operation, all three inputs are **target totals**, not amounts to add or subtract. For example, moving from 3,000 ZCHF of gross debt to 2,000 ZCHF sets `newMinted` to 2,000 ZCHF. A direct `mint` instead specifies the additional gross amount, while `repay` specifies the wallet amount paid. The interface's transaction preview should make that distinction clear.

To mint, choose the intended debt and check both collateral cover and available family capacity. The position must also be outside cooldown, unexpired, open and free of active challenges. Read the up-front fee, reserve contribution and resulting net ZCHF before submitting; an available capacity figure alone does not establish eligibility.

Two WETH at a stored liquidation price of 1,500 ZCHF per WETH support a gross debt of 3,000 ZCHF, subject to the other position limits. In the historical example, a 300 ZCHF reserve contribution and a 90 ZCHF up-front fee leave 2,610 ZCHF for the wallet. The fee is not returned on repayment. Reserve recovery depends on the assigned reserve remaining available.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-6c0c7ad730c0fa42678c19ae4c94ff544d8bd5a5%2Fkuva.png?alt=media" alt="Historical minting outcome"><figcaption><p>Historical minting outcome with gross debt and net wallet receipt.</p></figcaption></figure>

With an unimpaired 10% reserve, repaying 900 ZCHF plus the position's 100 ZCHF assigned reserve extinguishes 1,000 ZCHF of gross debt. Shared reserve losses can increase the wallet contribution. The transaction quote uses the position's actual state.

For a repayment, choose the gross debt you want to clear and obtain the required wallet contribution from that state. Supply ZCHF, not collateral or Swiss francs. Approve any required spender for the chosen operation, then submit the repayment or combined adjustment. Afterwards, confirm the debt reduction and wallet debit. Clearing debt does not itself withdraw the collateral.

## Adding or withdrawing collateral

To add collateral through `adjust`, set the target collateral balance above the current balance and authorise the position to transfer the difference. Leave the debt and price targets unchanged if you only intend to add collateral. More collateral increases the amount of debt the position can support at its existing price, but does not change that price.

To withdraw collateral, select the amount to remove or the lower target balance, depending on the operation. The remaining collateral at the stored price must still cover outstanding gross debt. Active challenges and cooldowns restrict withdrawals. In the pinned source, withdrawing below the minimum closes the position and requires zero debt; to exit completely, clear the debt and withdraw the remaining collateral when those conditions permit.

## Lowering the liquidation price

**Adding collateral alone does not change the stored liquidation price.** Lowering the challenge price requires an explicit price adjustment, whether through `adjustPrice(newPrice)` or a combined `adjust` operation that supplies a different price.

The new collateral value at the lower price must still cover outstanding gross debt. For example, 3,000 ZCHF of debt needs three WETH at a price of 1,000 ZCHF per WETH. If only two WETH remain, debt must first fall to at most 2,000 ZCHF to use that price. Active challenges and the position's state can restrict adjustments.

Choose one of those ways to make the lower price possible: add enough collateral, repay enough debt, or combine the two. Set the lower price explicitly as part of the same adjustment or in a later price transaction. Depositing the extra WETH alone leaves the old challenge price in place.

If an interface combines a collateral deposit with a price change, the transaction must include both. The confirmed position's `price`, collateral balance and minted debt show whether the intended adjustment occurred.

## Increasing the liquidation price

In the pinned source, increasing the liquidation price triggers a three-day minting cooldown. It permits a higher collateral-based minting amount only after that period and within the family limit. Lowering the price does not trigger that same price-increase cooldown.

Submit the price increase separately from any mint that depends on the higher price. The combined `adjust` operation processes minting before changing the price, so it cannot use the new higher price to cover that mint. After confirmation, read the new price and cooldown; once the cooldown has passed, obtain a fresh mint quote.

## Confirm the adjustment

After the transaction confirms, compare the recorded debt, collateral balance and price with your target totals. Check the wallet's received or spent ZCHF and collateral as well. If the transaction reverted, the intended adjustment did not take effect; read the current challenge, cooldown, capacity and collateral state before changing the inputs.


# Challenges and Auctions

Collateral challenges, the two auction phases and settlement.

## Auction Design

Auctions in the Frankencoin system serve two purposes at once: price-determination and liquidation. Anyone can challenge any position at any time by posting some collateral that is then put up for sale. If a bidder is wiling to pay the position's liquidation price for the collateral, the system assumes that the position is fine. The position remains untouched and the challenger's posted collateral is sold. If, however, the winning bid is belove the liquidation price, some of the position's collateral is sold at that price, some of the loan repaid, and the challenger rewarded.

The auction has two phases:

1. **Fixed-price phase:** bidders buy the challenger's collateral at the position's liquidation price. Taking the challenged amount in this phase averts liquidation of that amount of the position's collateral.
2. **Declining-price phase:** if collateral remains challenged, the auction price falls towards zero. Bidders buy the position's collateral, and the challenger recovers the corresponding posted collateral under the settlement rules.

This separation makes defending an excessive liquidation price costly. If collateral worth 950 ZCHF is challenged at a liquidation value of 1,000 ZCHF, the owner cannot merely buy back their own collateral at 1,000 ZCHF: a first-phase defence buys the challenger's collateral. Repeated challenges therefore depend on challengers having access to that asset. A collateral asset wholly controlled by the position owner cannot support that mechanism.

After a successful challenge, the debt, sale proceeds, challenger reward and assigned reserve determine settlement. In the pinned implementation the challenger reward is 2% of the accepted bid, not an unconditional 2% return on posted collateral. The [reserve example](/reserve#challenge-settlement) shows the accounting for an unimpaired reserve. Partial liquidations and excess proceeds have their own allocation rules.

Adding collateral and lowering the stored liquidation price are separate operations. The [adjustment guide](/positions/adjust#lowering-the-liquidation-price) explains their debt and collateral constraints.

## How to Initiate an Auction

The [monitoring page](https://app.frankencoin.com/monitoring) lists positions. Start with the position address, chain, collateral token and stored liquidation price. Compare that price with the amount a market buyer would pay for the collateral; the protocol does not obtain an oracle price for you.

1. **Select the amount to challenge.** You need that amount of the same collateral token in your own wallet, as well as native gas. The challenged size is an amount of collateral, not a ZCHF bid. Check the position's minimum challenge size and existing challenges.
2. **Review the price and timing.** Read the current stored price and phase duration. The pinned `challenge` operation also takes `minimumPrice`: it reverts if the position's price has fallen below that value before execution. This protects the price condition you chose; it is not an oracle input.
3. **Approve and submit.** If needed, approve the hub to transfer your collateral, then submit the challenge transaction. Approval alone does not start an auction. Your posted collateral can be bought in the first phase, so a challenge is not a risk-free report or a guaranteed reward.
4. **Confirm and follow settlement.** Record the challenge number from the confirmed transaction and read its start time, size and phase. Follow bids to see how much was averted or settled. A first-phase purchase sells your posted collateral; later-phase settlement normally returns the corresponding posted collateral and pays the bid-based reward.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-69466daa3e13fc562c6e8295b4c4b0ea30094a93%2Fkuva.png?alt=media" alt="Historical challenge entry point"><figcaption><p>Historical monitoring interface.</p></figcaption></figure>

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-646e6282af9143cb2dc7d6e96fa2c88ca78f0914%2Fkuva.png?alt=media" alt="Historical challenge setup"><figcaption><p>Historical challenge setup, not a current quote.</p></figcaption></figure>

Some collateral returns can be postponed. In that case, read the hub's `pendingReturns` for your token and address, then use `returnPostponedCollateral` to collect it to the intended recipient. An indexed success label alone does not tell you whether collateral was returned or proceeds received; check the transaction and balances.

## How to Participate in Ongoing Auctions

The [auctions page](https://app.frankencoin.com/challenges) shows indexed challenges. The phase, remaining amount and transaction quote determine what collateral a bid buys and at what price. A confirmed bid can change the remaining amount before another transaction executes.

1. **Identify the challenge.** Match its chain, hub, position and challenge number. Read its remaining size and current phase. In the first phase you buy the challenger's collateral at the fixed liquidation price; in the second you buy the position's collateral at the declining price.
2. **Choose how much collateral to buy.** Obtain the corresponding ZCHF cost from the current quote and compare it with your intended purchase. The pinned `bid` method takes a collateral size, not a freely chosen auction price, and reduces that size if less remains available.
3. **Fund and submit the bid.** You need ZCHF and native gas. Approve the relevant spender if required, review the transaction's phase and expected amounts, then submit. A bid executes the purchase; it is not an offer held until all bids are collected at an auction deadline.
4. **Check what arrived.** After confirmation, check the ZCHF spent, collateral received and remaining challenge size. Partial bids can leave more collateral under challenge. If the phase changed or another bid executed first, the old page quote may no longer describe the result.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-0b1a208deeddee7d2cedb529d2695a13e12b6248%2Fkuva.png?alt=media" alt="Historical empty auction list"><figcaption><p>Historical empty state, not a statement about current auctions.</p></figcaption></figure>


# FCS: Investing and Pool Shares

FCS ownership, acquisition, governance, equity economics and exits.

## 📈 FCS: Investing and Pool Shares

### Reserve Pool Shares

Frankencoin Share Token (FCS) is Frankencoin's canonical governance and share token. Holding FCS gives economic exposure to the system's equity capital and builds time-weighted voting power. Net fees and liquidation results change that capital; losses reduce it.

FCS connects these two roles: holders bear the economic results of the system and can veto proposals through [governance](/governance). It is a share token, not a Swiss franc stablecoin or a savings deposit paying a set rate.

### Usage

#### Acquire FCS

The [equity application](https://app.frankencoin.com/equity) provides investment interfaces. The FCS design has three distinct acquisition routes:

* **Invest ZCHF:** `deposit` specifies the ZCHF input; `mint` specifies the FCS output. The contract buys underlying equity and issues FCS. The exchange rate is not one FCS per ZCHF.
* **Buy existing FCS:** a secondary-market trade transfers shares from another holder. Its quote and availability depend on the venue and liquidity.
* **Migrate existing shares:** FPS holders can wrap their balance one to one into FCS. WFPS holders first unwrap into FPS. The [migration guide](/pool-shares/fcs-migration) explains the different voting effects.

Use the preview for the intended operation and amount, rather than dividing by a displayed reference price. A preview estimates output; eligibility and transaction limits are separate checks. The [entry reference](/pool-shares/fcs#entry-paths) defines the contract paths.

For a new ZCHF investment:

1. **Identify the contract.** Connect the wallet holding the ZCHF and check the chain and FCS contract used by the interface. The audited design uses Ethereum mainnet. Keep native currency for gas. These are contract paths; an application may expose only a subset.
2. **Choose what to fix.** Use `deposit` if you have a ZCHF budget and want the resulting shares, or `mint` if you want a specified FCS amount and need to know its ZCHF cost. Direct FPS wrapping is a different operation, covered by [migration](/pool-shares/fcs-migration).
3. **Read the quote and limits.** Enter the chosen amount, read `previewDeposit` or `previewMint`, and check `maxDeposit` or `maxMint`. Review the expected input, shares, fees and recipient. A reference price multiplied by an amount omits curve and fee effects. If using the reviewed `depositExpected` variant, its minimum-share input can reject an output below your chosen threshold.
4. **Approve and invest.** If an allowance is needed, approve the specified spender for ZCHF, then submit the investment transaction. Approval alone does not create shares.
5. **Confirm ownership.** Check the received FCS balance and ZCHF spent. The new shares start without immediate votes; the governance section below explains what to check before acting.

For a secondary-market purchase, instead review the venue's FCS token address, input asset, expected output and trading costs. Confirm receipt of FCS after settlement. Buying underlying FPS through an older route leaves you with FPS until you wrap it; buying FCS does not transfer the seller's voting age.

#### Participate in governance

FCS votes depend on balance and average holding duration, not balance alone. Holders can delegate votes while retaining their own ability to act. Fresh ZCHF investment, WFPS migration and purchases from another holder do not carry immediate voting power; direct wrapping of aged FPS credits the legacy votes lost on transfer.

Qualified actions need more than 1% of internal FCS voting power, including valid delegation, and the FCS contract meeting the underlying FPS quorum. These checks are separate from the binding threshold used for ZCHF exits. See [FCS governance](/governance) for proposals and vetoes, and [voting mechanics](/pool-shares/fcs#voting) for accumulation, capping and vote destruction.

#### Exit an FCS holding

* **Redeem into ZCHF:** `redeem` fixes the shares burned; `withdraw` fixes the ZCHF output. Both need the FCS contract to be binding and eligible to redeem its underlying FPS, and both apply the redemption discount.
* **Sell existing FCS:** a market sale has its own quote and liquidity. It is not a redemption against the reserve.
* **Unwrap into FPS:** receive one underlying FPS per FCS. The caller's FCS holding duration must be at least the average across FCS holders, whether binding or unbound. This leaves the holder with FPS, not ZCHF.

There is no separate personal 90-day FCS redemption wait. Binding depends on the FCS contract's share of underlying FPS votes, and the 90-day condition applies to that contract as an FPS holder. The [exit reference](/pool-shares/fcs#exit-paths-and-eligibility) defines these gates and the different limits of `withdraw` and `redeem`.

For a ZCHF exit:

1. **Choose the result you need.** Use `redeem` to burn a specified FCS amount, or `withdraw` to request a specified ZCHF amount. A market sale and an unwrap do not use the same quote or eligibility rules.
2. **Check availability.** Read `maxRedeem` in shares or `maxWithdraw` in ZCHF. A zero limit can reflect the wrapper-level redemption gate. If redemption is disabled, a preview is not permission to execute it, and waiting as an individual holder does not alone open that gate.
3. **Review proceeds for that size.** Read `previewRedeem` or `previewWithdraw`, including the current discount. A single `withdraw` can burn at most 10% of total FCS supply; `redeem` has no such cap. For a large `redeem`, more shares can produce less ZCHF. The reviewed `redeemExpected` variant adds a minimum-proceeds condition; ordinary `redeem` does not.
4. **Submit and confirm.** Check the share amount, recipient and transaction conditions, then submit. After confirmation, verify both the FCS burned and ZCHF received. A changed discount or other intervening activity can change the result from an earlier preview.

To unwrap instead, compare your FCS holding duration with the holder average before choosing the share amount. If eligible, submit `unwrap` and confirm the matching FPS receipt. This delivers FPS, not ZCHF, and does not carry your FCS voting age back into the underlying FPS record.

### Economics

FCS participates in one equity pool, not a second reserve. Each FCS wraps one FPS; the underlying Equity contract holds the capital and prices FPS. ZCHF investment through FCS adds capital to Equity. Income benefits that shared capital, and losses reduce it. FCS does not promise a fixed yield or a separate cash distribution.

#### Proportional Capital Valuation

The underlying curve sets a marginal FPS reference valuation of three times equity capital. FCS uses the underlying FPS price for its `ask()` reference and applies a redemption discount to `bid()`. Neither value is a size-specific execution quote or an FCS market price.

The [underlying FPS reference](/pool-shares/fps-reference#proportional-capital-valuation) gives the formula and numerical example in FPS units. The [FCS pricing reference](/pool-shares/fcs#prices-and-redemption-discount) explains previews, fees and the size- and activity-dependent discount. An API field reporting FPS supply or price remains an underlying metric, even when displayed beside FCS.

#### Equilibrium

A simplified model considers 30,000,000 ZCHF of outstanding mints at 5% annual interest: gross annual borrowing income is 1,500,000 ZCHF. If investors require a 5% return, and if that income continues without expenses or losses, capitalising it gives a valuation of 30,000,000 ZCHF. The underlying FPS curve reaches that reference valuation at 10,000,000 ZCHF of equity. This is a model of the shared equity pool, not a valuation of FCS supply alone.

Savings changes the income available to equity holders:

```
net equity income = borrowing income + other net income - savings expense - losses
savings expense = interest-bearing savings balance × applicable savings rate
```

For example, an average interest-bearing savings balance of 10,000,000 ZCHF at 2% costs 200,000 ZCHF a year. With the borrowing income above, no other income and no losses, net equity income is 1,300,000 ZCHF. At the same assumed 5% required return, that would support a model valuation of 26,000,000 ZCHF, not 30,000,000 ZCHF. Actual accrual timing, changing rates, referrals and losses affect realised flows. Referral fees divide savings interest between the user and referrer; they are not an extra payment on top of that gross interest.

The one-third equity relationship is a model result under assumptions, not a reserve requirement enforced by the contracts. Borrowing to buy FCS exposes the holder to fees, losses, price changes and redemption conditions; a spread between quoted rates is not a risk-free arbitrage. Comparing the underlying FPS reference valuation with ZCHF supply does not uniquely reveal expected growth.

#### Limits to Capital Efficiency

Lower equity means a smaller buffer before losses reach shared minter reserves. More equity provides a larger buffer but changes the returns available per share. Neither the curve nor a proposed equilibrium ensures a particular reserve ratio or market price. [Reserve accounting](/reserve) explains the flows; [FCS mechanisms and dependencies](/risks#fcs-mechanisms-and-dependencies) describes how voting state and redemption activity affect holders.

### Underlying FPS

FPS remains the backing token and retains its own supply, address and legacy governance records. It is not a second name for FCS. The [underlying FPS reference](/pool-shares/fps-reference) contains the Equity curve, direct FPS redemption rules and historical interface examples. The [FCS mechanics reference](/pool-shares/fcs#version-and-terminology) maps the reader-facing FCS name to the audited `FPS2` identifiers.


# FCS Mechanics

Contract mechanics for FCS, Frankencoin's canonical governance and share token.

## FCS Mechanics

Frankencoin Share Token (FCS) is the canonical governance and share token. It combines participation in the system's equity with time-weighted voting. The [investing and pool shares guide](/pool-shares) explains the holder journey; this page defines the entry, voting and exit mechanics.

Each FCS wraps one underlying Frankencoin Pool Share (FPS). The Equity contract continues to hold the system's equity capital and to price FPS. FCS and FPS have separate supplies, voting records and contract interfaces.

### Version and terminology

This page describes the final V3 design in ChainSecurity's [14 July 2026 assessment](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf), at commit `c1f229e3b26050367aafcb55da294342b4cae382`. The report and reviewed code called the wrapper **FPS2** and the underlying FPS **FPS1**. The wrapper was renamed after the audit ([PR #120, "Rebrand FPS2 to FCS"](https://github.com/Frankencoin-ZCHF/FrankenCoin/pull/120)): the shipped contract is `contracts/equity/shares/FCS.sol`, and its `name()` and `symbol()` return `Frankencoin Shares` and `FCS`. This documentation uses **Frankencoin Share Token (FCS)** throughout; citations below to the audit and its pinned commit keep the report's original `FPS2`/`FPS1` labels, since they refer to that historical snapshot.

The contract mechanics below refer to that reviewed version. The [FCS API reference](/api-docs/fcs) documents the API-reported deployment and fields separately. The continuing Ethereum [FPS contract](https://etherscan.io/address/0x1bA26788dfDe592fec8bcB0Eaff472a42BE341B2) remains FPS, including in price and supply data.

### Entry paths

| Path                    | What happens                                                                                             | Initial FCS votes                                                             |
| ----------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| Invest ZCHF             | `deposit` fixes the ZCHF input; `mint` fixes the share output. The wrapper invests in FPS and issues FCS | No immediate votes from the new investment                                    |
| Buy from another holder | A secondary-market trade transfers existing shares                                                       | A market purchase does not carry the seller's accumulated votes               |
| Wrap existing FPS       | `wrap(amount)` transfers FPS into the wrapper and issues the same number of shares                       | Credits the FPS votes lost by the sender on that transfer                     |
| Migrate WFPS            | Unwrap WFPS into FPS, then wrap FPS into FCS                                                             | No carried votes from WFPS; the received FPS starts without accumulated votes |

The one-to-one ratio applies to **FPS wrapping**, not ZCHF investment. ZCHF is the ERC-4626 vault asset; FPS is the token backing each share. See the [migration guide](/pool-shares/fcs-migration) for the distinction between holder votes and wrapper votes.

### Voting

Votes grow with the holder's balance and average holding duration. Transfers and other balance changes affect that duration. The audited design adds:

* `cap(holder)`: anyone can apply the 365-day holding-duration cap to a particular holder. It reduces that holder's votes and total internal votes together. It is **not automatic expiry** or a continuously applied cap on every account.
* `delegateVoteTo()`: delegation is non-subtractive and transitive. A holder retains their own ability to act, and an address counts only once in a delegation chain.
* `attack()`: a holder sacrifices their votes to destroy an equal number of other holders' votes. This changes voting records, not token balances.

Qualified [governance actions](/governance#veto-process) require **more than 1% of voting power**, including valid delegation. The audit's reviewed design gated the wrapper behind a separate, higher underlying-FPS quorum (at least 2% of FPS voting power) in addition to the 1% FCS check. The shipped code replaced that two-tier gate with a single quorum, defined once in `Governance.sol` (`QUORUM = 100`, i.e. 1%) and applied identically to FCS-based actions and to legacy FPS1-based actions; the source notes the quorum "used to be 200 \[2%] in earlier versions, most notably in FPS1."

### Binding

The wrapper is **binding** when it controls **more than two thirds of underlying FPS votes**. This is not two thirds of FPS supply or internal FCS votes. The reviewed `isBinding()` checks `FPS1.relativeVotes(address(this)) * 3 > 2e18`.

Binding can reverse as voting balances change. It enables ZCHF redemption, subject to the wrapper's FPS holding duration, and enables `shoot(target)`. It does not itself prevent unwrapping in V3.

While binding, anyone can call `shoot(target)`. The wrapper spends an equal number of its own FPS votes to destroy the target's accumulated FPS votes through legacy `kamikaze`. The target keeps its FPS tokens, but loses those accumulated votes and must satisfy the legacy redemption-age condition again. Repeated calls can keep disrupting that recovery. This is not token confiscation. See [remaining outside the wrapper](/pool-shares/fcs-migration#remaining-outside-the-wrapper).

### Exit paths and eligibility

| Operation                         | Output                                                  | Conditions in the reviewed design                                                                                   |
| --------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| FCS `redeem` or `withdraw`        | ZCHF, with the redemption discount                      | The wrapper must be binding and its own FPS holding duration must satisfy `FPS1.canRedeem(address(this))`           |
| FCS `unwrap`                      | One FPS per share                                       | The caller's FCS holding duration must be at least the average across FCS holders; allowed while binding or unbound |
| Transfer or secondary-market sale | Shares to another address, or the market's quoted asset | Separate from protocol redemption; proceeds depend on the available route and liquidity                             |

There is no new personal 90-day FCS redemption wait. The relevant 90-day check belongs to the wrapper as an FPS holder. Receiving FPS after an unwrap changes the recipient's FPS holding duration; FCS age is not transferred back as legacy votes. [Direct FPS redemption](/pool-shares/fps-reference#direct-fps-redemption) then follows the underlying contract's rules.

The final V3 source resolves two inconsistent descriptions in the report: `unwrap` rejects durations **below** the holder average, so equality passes; `Equity.canRedeem` uses **at least** 90 days. See [FPS2.sol](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/c1f229e3b26050367aafcb55da294342b4cae382/contracts/equity/fps2/FPS2.sol#L68-L114) and [Equity.sol](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/c1f229e3b26050367aafcb55da294342b4cae382/contracts/equity/Equity.sol#L114-L119). The earlier p6 statement that unwrapping resumes only after unbinding is superseded by the V3 change on p8.

### Prices and redemption discount

The underlying FPS curve values the outstanding FPS at three times equity at the margin. This is a protocol reference valuation, not a secondary-market price or a fixed transaction quote.

| Value                               | Meaning                                                                             |
| ----------------------------------- | ----------------------------------------------------------------------------------- |
| `ask()`                             | Underlying `FPS1.price()`, before the FPS fee and transaction-size effects          |
| `bid()`                             | The same marginal price multiplied by the current discount, also before the FPS fee |
| `previewDeposit` / `previewMint`    | Entry estimates using the underlying curve                                          |
| `previewRedeem` / `previewWithdraw` | Exit estimates including the redemption discount                                    |
| Market quote                        | A route-specific price for trading existing tokens                                  |

For current share supply `S`, planned redemption `q` and weighted recent redemptions `R`, the discount **factor** is:

```
d = ((S - q / 2) / (S + R))^4
ZCHF proceeds = underlying FPS redemption proceeds × d
```

All three inputs use share units. A factor of 1 means no extra discount; a lower factor reduces proceeds. The underlying FPS curve and fee still apply. For example, with `S = 1,000`, `q = 100` and `R = 0`, the factor is `0.81450625`. This is an illustrative calculation, not a quote.

The withheld ZCHF returns to Equity. Weighted recent redemptions decay to zero over seven days **absent further redemptions**. Continuing redemptions reset the decay anchor and can prolong recovery. New investment can offset the tracked redemption volume. A planned redemption still incurs its own size-dependent discount even when recent volume has decayed to zero. [Audit pp6, 25, finding #010.](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf#page=25)

#### Withdraw and redeem are different

A single `withdraw`, which specifies ZCHF output, may burn at most **10% of total FCS supply**. This is not 10% of the holder's balance or a daily quota. The share-denominated `redeem` path has no such cap.

For sufficiently large redemptions, burning more shares can return **less ZCHF** because of the discount. Splitting a redemption changes fees, state and timing, so each transaction has its own quote. `redeemExpected` adds a minimum-proceeds condition; an ordinary `redeem` does not. Eligibility and available balance are separate from the preview calculation. [Audit pp11, 26.](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf#page=26)

### Accounting and ERC-4626 views

The final V3 definition is:

```
totalAssets = ZCHF.equity() × FCS.totalSupply() / FPS1.totalSupply()
```

It measures the equity fraction attributable to the issued wrapper shares, in ZCHF units. It is not the wrapper's full FPS balance valued for liquidation, three times equity or immediately redeemable cash. Extra FPS donated to the wrapper does not increase the numerator's share supply.

`convertToAssets(shares)` uses the underlying marginal FPS price and excludes the exit discount, fees and curve slippage. It is not `previewRedeem`. `maxRedeem(owner)` is denominated in shares; `maxWithdraw(owner)` is denominated in ZCHF. Both return zero when redemptions are disabled.

The audit records remaining ERC-4626 deviations and rounding behaviour: `previewWithdraw` retains the 10%-of-supply cap, `withdraw` can return slightly more ZCHF than requested, and `previewMint(0)` can be non-zero. Integrations must handle these behaviours rather than assume exact standard rounding. [Audit findings #005, #021 and #011](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf#page=20); [reviewed accounting code](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/c1f229e3b26050367aafcb55da294342b4cae382/contracts/equity/fps2/FPS2MintRedeem.sol).

### Related pages

* [Migration from FPS and WFPS](/pool-shares/fcs-migration)
* [Governance and cross-chain vote snapshots](/governance)
* [Investing and equity economics](/pool-shares)
* [Underlying FPS reference](/pool-shares/fps-reference)
* [Concrete FCS failure modes](/risks#fcs-mechanisms-and-dependencies)
* [FCS API reference](/api-docs/fcs)


# Migrating from FPS and WFPS

Entry and exit paths for FPS and WFPS holders in the audited FCS design.

Use this guide to move an existing FPS or WFPS holding into Frankencoin Share Token (FCS), the canonical governance and share token. FCS wraps FPS one to one. Migration changes the token you hold and how you exercise governance; it does not replace the underlying Equity contract. For a new investment with ZCHF, start with [acquiring FCS](/pool-shares#acquire-fcs). The [FCS reference](/pool-shares/fcs#version-and-terminology) identifies the audited version and terminology used here.

## Identify the starting token

| Starting balance | Route into FCS                                                                    | Voting effect                                                                  |
| ---------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| FPS on Ethereum  | Approve the selected wrapper for the amount, then call `wrap(amount)`             | FCS credits the FPS votes the sender loses on transfer                         |
| WFPS             | Obtain underlying FPS through the WFPS unwrap route, then wrap those FPS into FCS | Unwrapped FPS has no accumulated votes to carry over from WFPS                 |
| ZCHF             | Use ERC-4626 `deposit` or `mint`                                                  | The investment buys underlying FPS; the new FCS starts without immediate votes |
| Another asset    | Trade for the intended token through a quoted market route                        | This is a market trade, not a protocol migration                               |

WFPS on another chain first needs a supported route to the underlying Ethereum FPS. A matching ticker is not a chain or contract identifier. The audited FCS system does not bridge FCS tokens; it sends [voting snapshots](/governance#cross-chain-governance) to other chains.

## Transaction sequence

1. Identify the chain, starting token, wrapper address and underlying FPS address. The audited `FPS1` and `asset()` views identify the backing FPS and ZCHF asset respectively.
2. Choose the operation by input: `wrap` for FPS, `deposit` for a fixed ZCHF input, or `mint` for a fixed share output. For ZCHF entry, read the corresponding preview rather than dividing by a displayed marginal price.
3. If starting with WFPS, complete its unwrap route first and confirm receipt of FPS on Ethereum. Then approve the FCS contract for the intended FPS amount. For ZCHF entry, approve the required ZCHF amount instead. Approval alone does not migrate the holding.
4. Review the recipient, input, output and fees, then submit the chosen wrap or investment transaction. The reviewed `depositExpected` variant accepts a minimum share output.
5. After confirmation, read the received FCS balance and holder votes. Separately read the FCS contract's underlying FPS votes, binding state and redemption limits. A successful migration need not make a governance action or ZCHF exit immediately available.

These are contract operations; application interfaces expose their supported subset. The [API reference](/api-docs/fcs) distinguishes indexed data from transactions.

### If you already hold FPS

Choose how much FPS to move rather than treating migration as an all-or-nothing account change. For example, wrapping 10 FPS issues 10 FCS and reduces your FPS balance by 10. It does not spend 10 ZCHF. For aged FPS, read the internal FCS votes credited by the transfer as well as the token balance. A partial wrap moves only the selected shares and the legacy votes lost on that transfer.

### If you hold WFPS

Complete the supported WFPS unwrap and any required route to Ethereum first. Confirm the underlying FPS token and amount actually received before approving the FCS wrapper. Wrap that received FPS balance, then confirm the FCS receipt. WFPS holding time is not carried into FCS: the intermediate FPS arrives without accumulated votes. The two token conversions are not a promise of immediate voting eligibility or ZCHF redemption.

## Two voting records

**Holder votes inside FCS** and **the wrapper's votes in FPS** are separate records. Wrapping aged FPS credits the holder's internal FCS votes, but the wrapper receives FPS as an ordinary FPS holder. Those credited internal votes do not give the wrapper the same accumulated legacy age.

The audit's reviewed design let the wrapper fail a separate, higher underlying-FPS quorum even when an FCS holder individually cleared 1% of internal votes; the shipped code replaced that with a single shared 1% quorum (`Governance.sol`) used by both records, so the two votes now track the same threshold rather than different ones. Legacy `kamikaze` can still reduce the wrapper's FPS votes below that quorum. Binding has a separate threshold: more than two thirds of underlying FPS votes.

Gradual migration by large legacy voters can retain a legacy veto backstop while the wrapper accumulates FPS voting power, as described in ChainSecurity's SC4. [Audit p11.](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf#page=11)

## Returning to FPS or exiting into ZCHF

* **Unwrap:** burn FCS and receive the same amount of FPS. In V3 this works in either binding state if the holder's FCS holding duration is at least the holder average. It does not preserve the returned FPS's former voting age.
* **Redeem or withdraw:** burn FCS for ZCHF. This needs binding plus the wrapper's own 90-day FPS eligibility, and applies the [redemption discount](/pool-shares/fcs#prices-and-redemption-discount).
* **Sell:** transfer existing shares through a market route. Its liquidity and quote are separate from contract redemption.

Waiting 90 days as an individual FCS holder does not, by itself, enable redemption. `maxRedeem` and `maxWithdraw` reflect the wrapper-level gate; a preview can still return a value while the gate is closed.

For an unwrap, read your current FCS holding duration and the holder average, choose the shares to unwrap, then confirm the same number of FPS arrived after the transaction. For ZCHF proceeds, follow the [share guide's exit walkthrough](/pool-shares#exit-an-fcs-holding) instead. That route needs a size-specific quote and the wrapper-level gate, not the unwrap duration check.

## Remaining outside the wrapper

FPS remains transferable and retains its legacy identity. While FCS is binding, anyone can call `shoot(target)` against an external FPS holder. The wrapper sacrifices its own FPS votes to destroy the target's accumulated FPS votes. The tokens remain in the holder's account, but their governance weight and legacy redemption eligibility change.

The report's p5 description gives the resulting 90-day redemption-age consequence; p9 uses broader language about permanent redemption rights. The reviewed code calls legacy `kamikaze`, not a token burn. Destroyed votes do not return, but holding time can accumulate again; repeated shooting can keep resetting it. [Audit pp5 and 9](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf#page=5); [reviewed `shoot` implementation](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/c1f229e3b26050367aafcb55da294342b4cae382/contracts/equity/fps2/FPS2.sol#L89-L97).


# Underlying FPS Reference

Underlying FPS identities, the Equity curve and legacy holder mechanics.

[Frankencoin Share Token (FCS)](/pool-shares) is the canonical governance and share token. This page documents its underlying Frankencoin Pool Shares (FPS) and the legacy holder mechanics. It is a technical reference, not the starting point for acquiring FCS.

## Token identity and backing

The Equity contract holds the system's equity capital and issues FPS. Each FCS wraps one FPS, but the tokens have separate supplies, voting records and contract interfaces. The Ethereum [FPS address](https://etherscan.io/address/0x1bA26788dfDe592fec8bcB0Eaff472a42BE341B2) remains an FPS address. WFPS is an older wrapper, not another name for FCS.

The ChainSecurity report calls this underlying token `FPS1` and the FCS wrapper `FPS2`. Source identifiers and API routes retain these names; the [FCS version reference](/pool-shares/fcs#version-and-terminology) identifies the reviewed commit. `/ecosystem/fps/info`, `/prices/erc20/fps` and `/analytics/fps/*` report underlying FPS data, not FCS supply or market prices.

## Proportional Capital Valuation

The underlying curve is inspired by [The Continuous Capital Corporation](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4189472). For equity capital `K` in ZCHF and outstanding FPS supply `s`, its marginal reference valuation and price are:

```
V(K) = 3 × K
p = 3 × K / s
```

For positive capital and supply, the continuous model gives the following change after adding net capital `ΔK`, before implementation fees and integer rounding:

```
s_new = s × ((K + ΔK) / K)^(1/3)
p_new = p × ((K + ΔK) / K)^(2/3)
```

For example, equity of 1,000,000 ZCHF and supply of 10,000 FPS imply a marginal reference price of 300 ZCHF per FPS. This is not a guaranteed execution price. Finite transactions move along the curve; FCS ZCHF exits also apply the [redemption discount](/pool-shares/fcs#prices-and-redemption-discount). Secondary-market prices depend on trading liquidity and orders. The [equity income model](/pool-shares#equilibrium) relates fees, savings expense and losses to shared capital.

## Legacy votes and quorum

Legacy FPS votes grow with balance and average holding duration. For example, 10 FPS held for 730 days gives 7,300 FPS-days, compared with 7,000 FPS-days for 1,000 FPS held for seven days, before balance changes. A freshly borrowed balance carries no accumulated holding time. This legacy illustration does not apply the FCS `cap()` mechanism.

FPS retains its own delegation mechanism and quorum check; the applicable contract's quorum check determines eligibility, including fixed-point rounding at the boundary. That quorum is no longer 2%: `Equity` inherits the same shared `Governance` contract as FCS's governance modules, so a direct FPS holder's threshold is the same 1% (100 basis points) used throughout the system, not a separate higher gate. The 2% figure was the pre-rebrand value, used "most notably in FPS1" per the source before the shared quorum was unified.

Legacy `kamikaze` sacrifices the caller's votes to destroy the same number of votes at other addresses, affecting their redemption age too. While FCS is binding, permissionless `shoot(target)` invokes this mechanism against external FPS holders without burning their tokens. See [remaining outside the wrapper](/pool-shares/fcs-migration#remaining-outside-the-wrapper).

## Direct FPS redemption

Legacy FPS redemption burns FPS and returns ZCHF under the Equity curve. It requires the holder's **average holding duration of at least 90 days**, rather than an age attached to individual transferable tokens. Balance changes and vote destruction can affect that duration. FPS remains transferable; a secondary-market sale is separate from redemption.

The FCS contract is itself an FPS holder. Its underlying age therefore matters for FCS ZCHF exits, but an individual FCS holder has no separate personal 90-day redemption wait. Unwrapping FCS returns FPS without carrying FCS voting age back into the legacy record. See [FCS exit eligibility](/pool-shares/fcs#exit-paths-and-eligibility).

## Historical interface and metrics

The following screenshots are historical FPS interface examples. Their prices, balances, routes and controls are not current quotes or FCS instructions.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-3602bc1ba36502edf68c19bdc9ac058fff186bf2%2Fkuva.png?alt=media" alt="Historical FPS investment interface"><figcaption><p>Historical FPS entry and redemption view.</p></figcaption></figure>

A marginal FPS price does not determine the exact output of a finite purchase: fees and the curve also apply.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-4a2875e1a0e2c6c21622ab5f9cf53342deca06b5%2Fkuva.png?alt=media" alt="Historical FPS statistics"><figcaption><p>Historical FPS statistics, retaining the FPS unit and supply.</p></figcaption></figure>

| Metric                  | Meaning                                                              |
| ----------------------- | -------------------------------------------------------------------- |
| FPS reference valuation | FPS supply multiplied by the underlying marginal FPS price           |
| Total reserve           | ZCHF held in the reserve, including minter reserve and equity        |
| Equity capital          | Reserve capital after the minter-reserve allocation                  |
| Minter reserve          | Reserve attributed to outstanding positions, subject to loss sharing |
| Income and losses       | Historical flows over the displayed period, not a future return      |

An FPS statistic does not become an FCS statistic because an interface also offers FCS. See [reserve accounting](/reserve), the [ecosystem API](/api-docs/ecosystem) for underlying data and the [FCS API](/api-docs/fcs) for share-specific fields.


# Reserve

Reserve and equity accounting, with explicit example assumptions.

The reserve consists of ZCHF held for minter reserves and equity. [FCS holders](/pool-shares) participate in the equity through the token's underlying FPS backing. Protocol income increases that shared capital, while savings expense and losses reduce it. FCS does not create a separate reserve.

External stablecoins in conversion bridges are separate assets. Collateral remains in individual positions; the stylised balance sheet below records the associated repayment obligations rather than adding the collateral a second time.

## Balance Sheet Diagram

The accounting model is:

```
Assets                         Liabilities and equity
x: external bridge assets      z: total ZCHF supply
m: minter repayment claims      b: minter-reserve allocation
r: ZCHF reserve                e: equity

x + m + r = z + b + e
```

This gross presentation includes reserve-held ZCHF within total supply. It is an explanatory protocol balance sheet, not a company balance sheet or a valuation of every collateral token.

## Assets

* **Bridge assets (`x`):** external stablecoins held by stablecoin-conversion bridges. XCHF was used by the bootstrap bridge; that is a historical example, not a list of active bridges.
* **Minter repayment claims (`m`):** outstanding gross ZCHF debt recorded by positions.
* **Reserve (`r`):** ZCHF held for the system's minter-reserve allocation and equity.

## Liabilities and Equity

* **Total ZCHF supply (`z`):** issued ZCHF, including reserve holdings in this model.
* **Minter reserve (`b`):** the reserve allocation associated with minted debt. It can absorb losses and is not unconditionally recoverable by each minter.
* **Equity (`e`):** the residual reserve capital in which FCS holders participate. The underlying Equity contract accounts for this capital and issues the FPS backing each FCS.

## Example Scenarios

All amounts below are ZCHF-denominated illustrations, not observations of deployed positions.

### Historical stablecoin conversion

A 100 ZCHF redemption through the historical one-to-one XCHF bridge reduces bridge assets `x` and ZCHF supply `z` by 100. The example assumes available bridge assets and an eligible redemption; it says nothing about current route availability.

### Minting

Assume a gross mint of 500 ZCHF, a 20% reserve contribution and a 5% up-front fee. The wallet receives 375 ZCHF; the reserve retains 100 ZCHF for the minter and 25 ZCHF as equity income.

| Account                     | Change in ZCHF |
| --------------------------- | -------------: |
| Minter repayment claims `m` |           +500 |
| Reserve `r`                 |           +125 |
| Total supply `z`            |           +500 |
| Minter reserve `b`          |           +100 |
| Equity `e`                  |            +25 |

Both sides increase by 625 ZCHF under the gross presentation.

### Challenge settlement

Assume a fully liquidated debt of 5,000 ZCHF, an unimpaired 20% assigned reserve of 1,000 ZCHF, and a winning bid of 4,500 ZCHF. Use a challenger reward of **2% of the bid**, as in the [pinned MintingHub source](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/minting/MintingHub.sol). This example describes that source's accounting, not an unidentified historical deployment.

The challenger receives 90 ZCHF. The remaining 4,410 ZCHF of bid proceeds leaves a 590 ZCHF shortfall against the 5,000 ZCHF burn. The full 1,000 ZCHF minter-reserve liability is released; after covering the shortfall, equity gains 410 ZCHF.

| Account                     | Change in ZCHF |
| --------------------------- | -------------: |
| Minter repayment claims `m` |         -5,000 |
| Reserve `r`                 |           -590 |
| Total supply `z`            |         -5,000 |
| Minter reserve `b`          |         -1,000 |
| Equity `e`                  |           +410 |

Both sides fall by 5,590 ZCHF. The equity gain is not the same as the minter-reserve release. Different reward bases, partial liquidations or impaired reserves change the calculation. Expiry sales follow a [separate settlement path](/risks#missing-maturity-dates).

## Protection Mechanisms During Liquidation

Losses first use the affected position's minter reserve, then equity, then shared minter reserves. The last step can increase what other borrowers must return to settle their debt. This order describes loss allocation, not a guarantee that losses will fit within the available reserve.

## Equilibrium of Equity

The simplified model without savings expense, losses or different required returns gives:

```
3e = z - x
```

Here `e` is equity, `z` is total ZCHF supply and `x` is bridge-backed issuance, measured in ZCHF under the model's one-to-one bridge assumption. It is not an enforced reserve requirement. The [economic model](/pool-shares#equilibrium) explains how savings expense changes net equity income. FCS redemption prices and market quotes are separate from this relationship.


# FCS Governance

FCS veto-based governance, voting power and cross-chain voting snapshots.

## ⚖️ FCS Governance

### Overview

[Frankencoin Share Token (FCS)](/pool-shares) is Frankencoin's canonical governance token. Holders build time-weighted voting power and can delegate it to other participants. Frankencoin uses a veto process rather than a ballot on every proposal. For proposals with a waiting period:

1. A participant submits a proposal and pays any applicable fee.
2. Qualified holders can veto it during the relevant period.
3. If no veto succeeds, the change can be enacted under the module's rules.

The [Frankencoin thesis](https://www.zora.uzh.ch/id/eprint/259657/1/259657.pdf) describes the general model. FCS is the holder-facing governance interface. Its contract exercises underlying FPS votes, and both FCS-based and direct legacy FPS actions are qualified against the same shared 1% quorum.

### Taking part with FCS

1. **Build or delegate votes.** Acquire FCS through the [share guide](/pool-shares#usage), then read your voting power. New tokens do not necessarily bring immediate votes. Use `delegateVoteTo()` to delegate to another participant; you retain your own ability to act.
2. **Choose a proposal to examine.** The [governance application](https://app.frankencoin.com/governance) lists proposals and their state. Read the proposed module or parameter, chain, proposer and deadline. The [notification bot](/telegram-api-bot) can report indexed events, but does not cast vetoes.
3. **Check qualification for the action.** Read your internal FCS voting power, any valid delegation and the FCS contract's underlying quorum. On another chain, first complete the [two vote synchronisations](#cross-chain-governance). Token balance alone is not an eligibility check.
4. **Submit and confirm.** Use the relevant module's proposal or veto operation while its timing conditions permit. After the transaction confirms, read the proposal's on-chain state. A discussion message, API response or wallet approval is not a proposal or veto transaction.

The module table below identifies the available actions. You do not need FCS votes to submit a new minter application, but qualified actions such as position vetoes and rate proposals require them.

#### Choose between acting and delegating

To act yourself, identify the proposal by its chain, module and proposal or position address. Read the proposed change and the time in which that module allows a veto. Include valid delegation helpers where the selected operation requires them, then check qualification for that action. Holding FCS does not by itself satisfy either voting threshold.

If another participant will examine proposals for you, choose their address and submit `delegateVoteTo()` with that delegate. After confirmation, read the delegation record. Delegation does not transfer your FCS, cast a veto or prevent you from acting yourself. On a target chain, also check the local delegation state after vote synchronisation, which can overwrite it.

For a proposal you submit, check its recorded parameters and activation conditions after confirmation. A proposal with a grace period is not active merely because its submission succeeded. Follow its state until it is vetoed or can be enacted under the module's rules. Rate-limit changes are an exception to a general waiting-period assumption: the audited CCIP design applies them immediately.

### Immutable Modularity

The core contracts use separate modules for collateralised minting, savings, stablecoin conversion and cross-chain transfers. Parameters can change under their governance rules; an approved new module does not rewrite an old contract. Minting modules can mint, move and burn ZCHF. The [application's governance page](https://app.frankencoin.com/governance) lists proposals and module state; there is no fixed count of active modules in this guide.

### Community Consensus

Proposals can be discussed in the [Frankencoin forum](https://github.com/Frankencoin-ZCHF/FrankenCoin/discussions) and [Telegram group](https://t.me/frankencoinzchf). Discussion does not replace the on-chain proposal or veto.

### Proposal Submission

[New collateral positions](/positions/open) have their own proposal process. The following table describes the audited FCS governance modules, not every legacy deployment. Source: [audit pp7 and 10](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf#page=7).

| Module or action                 | Who can act                            | Timing and fee                                                                                                               |
| -------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `MinterGovernance.suggestMinter` | Anyone may submit a minter application | Minimum 60-day application period and 1,200 ZCHF fee; up to 200 ZCHF replenishes the reward pool                             |
| `denyMinter` / `denyPosition`    | Qualified FCS holders                  | Veto during the relevant proposal period                                                                                     |
| `denyUnannouncedMinter`          | Anyone                                 | Enforcement against a minter proposal bypassing the FCS announcement process; caller receives 10% of the reward pool         |
| `InterestGovernance`             | Qualified FCS holders on mainnet       | Borrowing and savings rate proposals use parts per million and a seven-day grace period                                      |
| `CCIPGovernance`                 | Qualified FCS holders                  | Configuration proposals follow CCIPAdmin rules; chain removal has a seven-day wait, but rate-limit changes apply immediately |

The 60 days and 1,200 ZCHF apply to **new minter applications**, not position creation or interest-rate changes. Sidechains receive rate updates from mainnet; they have no local interest-rate proposal path in this design.

`denyUnannouncedMinter` does not detect every bypass. A direct legacy FPS veto can leave a stale announcement, as described in audit finding #028. Rate-limit increases remain immediate in the audited version; finding #025 discusses a future delay, not an implemented one. These mechanisms still depend on participants monitoring proposals.

### Veto Process

#### FCS qualification

The caller, including valid delegation, needs **more than 1% of voting power**. Exactly 1% is not enough. This single quorum is defined once in `Governance.sol` (`QUORUM = 100` basis points) and applied identically whether the check runs against FCS's own vote totals or against the underlying FPS1 vote totals for a direct legacy action. The audit's reviewed design instead gated the wrapper behind a separate, higher underlying-FPS quorum (at least 2% of FPS voting power) on top of the 1% FCS check; the shipped code replaced that two-tier gate with the single unified threshold.

The quorum and the [binding threshold](/pool-shares/fcs#binding) are different. FCS governance can become usable before the wrapper is binding. A high internal voting share alone does not prove that the wrapper can exercise an underlying veto.

### Vote Accumulation

FCS votes grow with balance and average holding duration. An incoming balance does not carry the sender's holding time. Anyone can apply `cap()` to limit a particular holder's recorded duration to 365 days; this is not an automatic global cap. Direct FPS wrapping credits the holder's lost legacy votes, while ZCHF entry and WFPS migration provide no immediate carried votes.

`delegateVoteTo()` is transitive and non-subtractive. `attack()` lets a holder sacrifice votes to destroy an equal number of another holder's votes without changing token balances. The [FCS voting reference](/pool-shares/fcs#voting) defines these mechanisms.

### Cross-Chain Governance

FCS holders can exercise governance on other chains without moving their shares off mainnet. The audited system needs **two synchronisations** before a holder can act on a target chain:

1. Send the wrapper's underlying FPS votes through `GovernanceSender.pushVotes`.
2. Send the individual holders' FCS votes through `MainnetVotes.pushFPS2Votes` to `BridgedVotes` on the target chain.

Select the target chain and holders to update, obtain the CCIP fee and submit each required synchronisation. Anyone can trigger these calls and pay the fee in the native asset or LINK. Track both messages to delivery and read the destination voting state before submitting a governance action. FCS tokens remain on mainnet in this design: **vote snapshots, not tokens, cross the bridge**.

Snapshots are not live mainnet balances. A selective sync can update total votes without updating every holder. A mainnet sync also overwrites local sidechain delegation for each included address. Old failed messages can later execute and overwrite a newer corrective sync; restoring state may require processing outstanding messages before sending fresh snapshots. CCIP delivery, message ordering and complete updates therefore affect destination qualification. [Audit pp7–10 and finding #006, pp22–23](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf#page=22).

### Proposal monitoring

The veto design avoids a full ballot for uncontested proposals. Its operation still depends on holders examining proposals and acting within the applicable period. The audit's trust model assumes no malicious majority of internal FCS votes after binding. See [governance failure modes](/risks#fcs-mechanisms-and-dependencies).

#### Legacy FPS qualification

FPS still maintains its own underlying votes and quorum check; FCS does not globally replace that contract's rules. That quorum is the same shared 1% used by FCS-based actions, not a separate higher threshold. Direct FPS voting and the legacy holding-time example are documented in the [underlying FPS reference](/pool-shares/fps-reference#legacy-votes-and-quorum). Existing holders can follow the [migration guide](/pool-shares/fcs-migration) to take part through FCS.


# Notification Bot

Bot subscriptions, confirmation and the scope of position alerts.

The [Frankencoin API Telegram Bot](https://t.me/FrankencoinApiBot), **@FrankencoinApiBot**, reports proposals, position activity and other indexed events. FCS holders can use governance alerts to follow proposals; borrowers can follow their positions. Receiving an alert does not submit a transaction or establish voting eligibility. The [FCS governance guide](/governance#taking-part-with-fcs) explains how to act on a proposal.

## How to find it?

Open the linked bot in Telegram and send `/help`. The commands below are implemented in [telegram.service.ts at commit 4fdbd1007a2c75bddff2aaadf2d370e5dc296dd4](https://github.com/Frankencoin-ZCHF/frankencoin-api/blob/4fdbd1007a2c75bddff2aaadf2d370e5dc296dd4/src/integrations/telegram/telegram.service.ts). They were checked against public source on 18 September 2026, not by sending messages to the live bot.

## Subscription Handels

The heading above is retained for existing links. Subscription commands are:

| Command                  | Effect                                                                              |
| ------------------------ | ----------------------------------------------------------------------------------- |
| `/help`                  | Show available commands                                                             |
| `/status`                | Show active governance, all-position and owner subscriptions                        |
| `/start GOV`             | Subscribe to governance alerts                                                      |
| `/stop GOV`              | Remove governance alerts                                                            |
| `/start ALL`             | Subscribe to all-position alerts                                                    |
| `/stop ALL`              | Remove all-position alerts                                                          |
| `/start <owner address>` | Subscribe to positions belonging to a complete `0x` plus 40-hex-digit owner address |
| `/stop <owner address>`  | Remove that owner subscription                                                      |

Angle-bracket text is a placeholder, not a literal command argument. The first message registers the chat in the inspected implementation and enables governance and all-position alerts by default. `/status` shows the resulting state; unwanted categories can then be removed explicitly. The older `/MintingUpdates` toggle shown in historical material is not the command model above.

After subscribing, the bot returns a confirmation. Send `/status` and check the category or full owner address. To test the subscription controls without creating a protocol event, remove and re-add the category, checking `/status` after each change. This checks the subscription response, not future event delivery.

## Basic Messages

### Welcome and Environment Information

Help and status identify the chat and subscriptions. Event messages link to relevant transactions, addresses or application views.

### Minter Proposals

Governance alerts include minter proposals and vetoes, interest-rate changes and CCIP proposals or rate-limit changes.

### Minter Proposals Vetoed

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-6838ddc812c0a261adbaee1c345d42fff6fa9e4e%2FScreenshot%202024-09-26%20at%2012.13.41%E2%80%AFPM.png?alt=media" alt="Historical minter-veto notification"><figcaption><p>Historical message format.</p></figcaption></figure>

### Position Proposals

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-f5ff4359799213d2df0399a6cd3c30521e370118%2FScreenshot%202024-09-26%20at%2012.10.32%E2%80%AFPM.png?alt=media" alt="Historical position-proposal notification"><figcaption><p>Historical message format.</p></figcaption></figure>

### Challenges Started

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-df6932832085319e462e86dcfd4dacc0cf037975%2FScreenshot%202024-09-26%20at%2012.16.07%E2%80%AFPM.png?alt=media" alt="Historical challenge notification"><figcaption><p>Historical message format.</p></figcaption></figure>

### Minting Updates

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-dd3b9ff7a3bacb668a526b77fe7d65907ebff791%2FScreenshot%202024-09-26%20at%2012.15.21%E2%80%AFPM.png?alt=media" alt="Historical minting update"><figcaption><p>Historical message format.</p></figcaption></figure>

### Expiry and price alerts

The inspected source includes expiry notifications at the seven-day and one-day thresholds, and after expiry. Price alerts compare the service's collateral price feed with the position's liquidation price, using warning bands and cooldowns. Owner subscriptions follow the position's owner address; they are not subscriptions to a position address.

Notifications depend on service uptime, indexing, price coverage and Telegram delivery. They do not execute repayment or liquidation-price changes, and a missing alert does not change the contract's expiry or stored price.

### Historical subscription screenshots

These local assets preserve the older interface; the textual commands above describe the pinned implementation.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-4852b995d02f75c7a636335fd95da151ee6a2374%2FScreenshot%202024-09-26%20at%2012.25.01%E2%80%AFPM.png?alt=media" alt="Historical subscription settings"><figcaption><p>Historical subscription interface.</p></figcaption></figure>

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-dd13cdbfa638b9a02e1203bef602468c5fa5ed2f%2FScreenshot%202024-09-26%20at%2012.25.11%E2%80%AFPM.png?alt=media" alt="Historical subscription confirmation"><figcaption><p>Historical subscription interface.</p></figcaption></figure>


# Risks

Failure modes and dependencies affecting ZCHF, borrowers and equity holders.

## For Frankencoin Holders

### Fundamental Depeg

ZCHF depends on the value recoverable from collateral and reserves. A fall in collateral value can leave a shortfall after liquidation. Each position's collateral secures that position; it does not automatically cover another position's debt. **Aggregate collateral value above total ZCHF supply therefore does not rule out a shortfall.**

Auctions can last days. A temporary price fall may reverse during that time, but a permanent loss of collateral value does not. The affected minter reserve absorbs losses first, followed by equity and then shared minter reserves. Challenger rewards and settlement details also affect the amount available; a 20% reserve does not imply exactly 20% price protection after all costs. [Reserve accounting](/reserve) shows an explicit example.

### Collapse of Connected Stablecoins

A stablecoin-conversion bridge holds an external asset against ZCHF issuance. If the issuer fails or the asset loses value, the system can incur a loss. Minting limits and expiry constrain the route; they do not remove existing issuer exposure.

Earlier documentation described an XCHF bootstrap bridge and a VCHF bridge with an expiry of **15 April 2026**. These are historical references. As of this documentation revision on **18 September 2026**, those references do not establish which route is active, renewed or replaced. The [governance application](https://app.frankencoin.com/governance) and the selected bridge's contract state provide its identity, limit and expiry. A stablecoin-conversion bridge is distinct from a cross-chain ZCHF bridge.

### CCIP and Blockchain Hacks

Cross-chain transfers rely on [Chainlink CCIP](https://chain.link/cross-chain), the bridge contracts and the participating blockchains. A compromised chain or message path can affect issuance and collateral backing on other chains. Rate limits constrain flows; qualified governance can change those limits, including setting them to zero. The audited FCS governance design applies rate-limit changes immediately, while chain removal has a waiting period. [Cross-chain voting](/governance#cross-chain-governance) has separate snapshot dependencies.

### Temporary Depeg / Liquidity Risk

The ZCHF market price can differ from its intended Swiss franc value when available liquidity does not match order size. Selling 100,000 ZCHF at an average 0.98 CHF when the displayed price is 1.00 CHF gives 98,000 CHF before fees: a 2% difference. Splitting an order changes execution timing and can expose later trades to price movements or anticipation by other traders.

## For Minters (Borrowers)

### Missing Maturity Dates

At expiry, the applicable position version allows a forced sale. In the [pinned newer minting implementation](/positions#contract-versions), the sale starts from ten times the stored liquidation price and declines under the hub's pricing rules. It is a separate path from a collateral challenge.

The newer `Position.forceSale` uses assigned reserve in settlement. If sale proceeds plus available reserve cover the gross debt, the owner receives `proceeds + returnedReserve - minted`. Otherwise, proceeds reduce debt, and remaining collateral or the loss-settlement branch determines what follows. **Expiry does not unconditionally forfeit the full minter reserve.** Legacy deployments must be read under their own settlement rules.

The [notification bot](/telegram-api-bot) source includes expiry alerts, but delivery depends on indexing and the bot service. The position's on-chain expiry remains the operative deadline.

### Missing Market Movements

A position becomes economically challengeable when market value falls below its stored liquidation price. Adding collateral does not lower that price automatically; [an explicit adjustment](/positions/adjust#lowering-the-liquidation-price) is needed. The notification bot's price alerts depend on its off-chain price feed and subscription state. A successful challenge allocates the associated reserve under the settlement rules; it is not the same as an ordinary voluntary repayment.

### Third Line Bailout

If the affected position's reserve and equity do not cover a loss, shared minter reserves can absorb it. Another borrower's otherwise sound position may then have less assigned reserve available for repayment. Qualified FCS holders can veto new proposals through [governance](/governance#veto-process); qualification needs internal FCS voting power and the FCS contract meeting the underlying FPS quorum.

### Swiss Franc Appreciation

ZCHF debt is a Swiss franc-denominated liability. If the Swiss franc appreciates against a borrower's income or collateral currency, servicing the debt becomes more expensive in that currency. A lower nominal interest rate does not remove exchange-rate exposure. The [experience of foreign-currency mortgages](https://www.tandfonline.com/doi/full/10.1080/13604813.2023.2229695) illustrates this distinction; [interest-rate parity](https://en.wikipedia.org/wiki/Interest_rate_parity) is a model, not an exchange-rate forecast.

### Short Squeeze (Upwards Depeg)

Repayment requires ZCHF, not Swiss francs. Concentrated demand near maturity can push ZCHF above parity, making repayment more expensive. New minting may add supply, but it requires eligible collateral, available capacity and time. Rolling a position also depends on the available route and terms; it is not an unconditional escape from repayment demand.

### Liquidation Cascades

Leverage can amplify price falls when forced sellers enter a falling market. Even positions with moderate leverage can become exposed after a period of low volatility. The [model by Thurner and co-authors](http://dido.econ.yale.edu/~gean/art/p1371.pdf) illustrates this effect.

<figure><img src="https://72734689-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDL79g9F2voPSkPXydEp2%2Fuploads%2Fgit-blob-a5ad09dd1b7abfafa1cde08cf45e0b69f52079ea%2Fdyn_fund_zoom_prices_edited.jpeg?alt=media" alt="Model price paths with and without leverage"><figcaption><p>Illustrative model: leverage can smooth ordinary price movements while amplifying a liquidation cascade.</p></figcaption></figure>

## FCS mechanisms and dependencies

The [FCS reference](/pool-shares/fcs) and [migration guide](/pool-shares/fcs-migration) describe the final V3 mechanics assessed by ChainSecurity on 14 July 2026. The relevant dependencies are:

* **Equity exposure:** FCS participates in the reserve's gains and losses through underlying FPS. Savings expense, losses and new capital change the economics; an FCS market sale and protocol redemption can have different proceeds.
* **Two voting layers:** credited holder votes do not create the wrapper's underlying FPS age. A legacy `kamikaze` can reduce wrapper votes below the underlying quorum.
* **Binding and exits:** ZCHF redemption needs binding and the wrapper's FPS holding-duration eligibility. V3 unwrapping has a holder-average duration condition and remains possible while binding.
* **Discounted exits:** proceeds depend on size and recent activity. The 10% cap applies to `withdraw`, not `redeem`. Large `redeem` calls can burn more shares for less ZCHF; finding #012 remains accepted rather than code-corrected.
* **Legacy vote destruction:** while binding, permissionless `shoot` destroys accumulated FPS votes without burning the target's FPS balance. Repeated calls can delay recovery of legacy redemption eligibility.
* **Cross-chain snapshots:** stale or selectively updated votes can change effective qualification. Late execution of old failed messages can overwrite newer state; synchronisation can overwrite local delegations.
* **Proposal monitoring:** stale minter announcements can leave gaps in bypass enforcement. The audit assumes active monitoring and no malicious majority of FCS voting power after binding.
* **Underlying contracts and CCIP:** the wrapper uses the underlying contracts for pricing, vote accounting and redemption, and CCIP for cross-chain messages.

The report records code corrections, specification changes, acknowledgements and accepted risks; these are not all equivalent to code fixes. Sources: [trust model, SC1–SC5 and findings #005, #006, #010, #012, #025 and #028](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf#page=9).

## General Risks

### Technical Attacks

Contract defects can affect balances, issuance or transaction execution. Audits assess a defined scope and version; they do not remove every possible defect.

Publicly visible transactions can be reordered or preceded by other transactions, including auction bids. MEV protection depends on the **network, RPC and submission route**, not the wallet brand. For example, [MetaMask Smart Transactions](https://support.metamask.io/manage-crypto/transactions/smart-transactions) applies under its supported-network and transaction conditions; settings and custom RPCs affect the route. Private submission does not prevent every form of MEV.

### Frontend Hacks

An altered interface can show one recipient or operation while asking the wallet to sign another. The [Bybit incident analysis](https://www.sygnia.co/blog/sygnia-investigation-bybit-hack/) describes an example of interface compromise. Transaction data and simulation identify the operation being signed; a displayed page balance alone does not.

### Human Errors

A lost signing key can make a balance inaccessible. A transfer to the wrong address may be irreversible. Custodial services replace some key-management responsibilities with dependence on the custodian.

### Governance Failures

An unchallenged bad proposal can authorise unsound collateral or a minting module with broad powers. The veto process depends on holders examining proposals and acting in time. The same economic exposure that motivates participation does not guarantee it.

### Regulatory Risks

The [compliance page](https://www.frankencoin.com/compliance#compliance-summary) provides regulatory information. The [report tool](https://app.frankencoin.com/report) records holdings and income.

### Unknown Risks

This page describes known failure modes, not an exhaustive list. Documentation corrections can be raised in the [community channel](https://t.me/frankencoinzchf).


# Background FAQ

Answers about ZCHF, FCS governance and shares, and the main protocol operations.

### What is this all about?

Frankencoin lets users mint ZCHF against collateral. Positions record the collateral and repayment obligation. Frankencoin Share Token (FCS) is the canonical governance and share token: holders participate in equity gains and losses and can veto proposals when qualified. The [overview](/) maps the components.

### What can I use Frankencoin for?

Its main uses are payments, Swiss franc-denominated holdings and collateralised borrowing. Access and execution depend on the selected wallet, chain and application. You do not need FCS to use ZCHF or deposit it in savings.

### What is the main goal of the Frankencoin?

The system provides a collateral-backed token intended to track the Swiss franc, with on-chain minting and veto-based governance.

### How does Frankencoin differ from other stablecoins?

Collateral challenges and auctions replace an external price oracle. Challengers supply the same collateral asset, which makes its availability part of the mechanism. Auctions take time and depend on active participants; they do not prevent every pricing error. See [Challenges and Auctions](/positions/auctions).

### What does FCS represent?

FCS combines participation in the system's equity with time-weighted voting. Net income increases shared equity capital; losses reduce it. Each FCS wraps one underlying FPS, but FCS has its own supply and voting records. The [share guide](/pool-shares) explains ownership, acquisition and exits; the [mechanics reference](/pool-shares/fcs) defines the contract rules.

### How do FCS holders take part in governance?

Qualified FCS holders can veto proposals. An action needs more than 1% of internal FCS voting power, including valid delegation, and the FCS contract meeting the underlying FPS quorum. Voting power depends on holding time and delegation, not token balance alone. [FCS governance](/governance) explains proposals, qualification and cross-chain voting.

### What opportunities does Frankencoin offer for preserving purchasing power and investment?

ZCHF targets the Swiss franc's nominal value. It does not fix purchasing power or guarantee a market price. Savings pays a variable rate on ZCHF; FCS gives equity exposure, whose value can rise or fall. These are different economic positions.

### How can I acquire FCS?

Invest ZCHF through `deposit` or `mint`, buy existing FCS through a market route, or migrate an FPS or WFPS holding. ZCHF investment creates shares at the underlying curve's rate; it is not a one-to-one exchange. Only direct wrapping exchanges one FPS for one FCS. [Acquiring FCS](/pool-shares#acquire-fcs) explains the routes, and [migration](/pool-shares/fcs-migration) explains which votes carry over.

### When can I exit FCS?

FCS has no new personal 90-day redemption wait. ZCHF exits need the FCS contract to be binding and eligible to redeem its underlying FPS, then apply a discount. Unwrapping to FPS is available in either binding state when the holder meets the average-duration condition. A market sale is separate from either contract path. See [exit eligibility](/pool-shares/fcs#exit-paths-and-eligibility).

### Can FCS be bridged to another chain?

The audited system keeps FCS tokens on Ethereum mainnet and sends voting snapshots to other chains. A destination governance action needs both the FCS contract's underlying FPS votes and the individual holder's FCS votes synchronised. This is separate from [bridging ZCHF](/bridge-to-other-chains).

### Can savings always be withdrawn immediately?

It depends on the version. Legacy SavingsV2 has a ticks-based withdrawal lock. The referral-enabled source has an interest delay without that same withdrawal lock. [Savings](/savings#contract-versions) separates the versions and explains public interest-collection methods and referral removal.

### Is the Frankencoin a security?

The existing [LEXR memorandum](https://github.com/Frankencoin-ZCHF/www/blob/main/documents/ZCHF_FPS_Memo.pdf) analyses ZCHF and legacy FPS under Swiss law. It is not an FCS classification, and the FPS2 code audit supplies no legal classification.

### Is Frankencoin political?

The design offers an alternative way to create collateral-backed money. Protocol operation is defined by contracts and governance rules, rather than a required political position among users.

### When obtaining Frankencoin against a collateral, is that really *borrowing*?

The contract mints new ZCHF rather than lending an existing deposit. The interface term “borrowing” describes the resulting repayment obligation; “minting” describes the token operation. Both refer here to a collateralised position.

## Underlying FPS and existing holdings

### What are Frankencoin Pool Share (FPS) tokens?

FPS remains the underlying equity token. The Equity curve prices FPS, and its supply and contract address remain FPS data. FCS wraps FPS one to one; WFPS is a separate older wrapper. The [underlying FPS reference](/pool-shares/fps-reference) documents these identities and legacy rules.

### How can individuals invest in the FPS tokens of the Frankencoin ecosystem?

For the canonical share-token journey, use [FCS](/pool-shares). Direct FPS investment uses the underlying Equity contract and issues FPS, not FCS. Existing FPS or WFPS holdings can move into FCS through [migration](/pool-shares/fcs-migration). Historical bank-card, WFPS and Polygon instructions do not identify a current FCS route.

### How long is the locking period for FPS?

FPS is transferable, but direct redemption through Equity requires the holder's average holding duration to be at least 90 days. Transfers, new receipts and vote destruction can affect that duration. This is the underlying FPS rule, not a personal FCS lock. See [direct FPS redemption](/pool-shares/fps-reference#direct-fps-redemption).

### Are Reserve Pool Shares securities?

The [existing memorandum](https://github.com/Frankencoin-ZCHF/www/blob/main/documents/ZCHF_FPS_Memo.pdf) discusses legacy FPS. FCS has separate mechanics; the documentation does not transfer that conclusion to FCS.


# Developers

The Frankencoin API supplies indexed data for wallets, position explorers, payment-reference searches and financial dashboards. Query token information, account activity and historical observations over HTTP without building an indexer for each view.

Base URL: `https://api.frankencoin.com`. The [interactive specification](https://api.frankencoin.com/) lists routes and parameters. These pages describe read-only HTTP GET requests. They do not create positions, place bids, deposit savings or transfer tokens; those actions use the relevant contracts.

[FCS](/pool-shares) is Frankencoin's canonical governance and share token. Start with the [FCS controller](/api-docs/fcs) for its supply, reference prices and discount data. FPS routes remain the source for the underlying equity token's data; use those fields for FPS, not FCS.

## Controllers

* [FCS](/api-docs/fcs): canonical share-token information and redemption discount.
* [Ecosystem](/api-docs/ecosystem): ZCHF, underlying FPS, minter proposals and collateral catalogues.
* [Positions](/api-docs/positions): indexed lending positions and owner histories.
* [Challenges](/api-docs/challenges): indexed challenges, bids and current challenge prices.
* [Prices](/api-docs/prices): display prices, currencies, sources and indicative valuations.
* [Savings](/api-docs/savings): balances, rates, activity and referrals.
* [Transfers](/api-docs/transfers): reference-bearing transfers and candidate matching.
* [Analytics](/api-docs/analytics): underlying FPS metrics and shared equity financial logs.
* [Wallet integration](/api-docs/wallet-integration): module selection and contract read/write boundaries.

## Versions and data conventions

These pages describe API version `0.4.2`. API releases, position V1/V2, savings module versions and the FCS audit revision are separate version systems. A cached API result can lag chain state; `/status` reports service/indexer health, not finality for each record.

| Field family                                                 | Encoding and unit                                                                         |
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| ZCHF/FPS/FCS raw contract amounts                            | Decimal integer strings, 18 decimals                                                      |
| Position collateral balances                                 | Decimal integer strings, use `collateralDecimals`, which may be 0, 6, 8 or 18             |
| Savings module balances and collected interest               | Raw ZCHF strings; savings aggregate totals are already scaled JSON numbers                |
| Savings `rate`, leadrate `approvedRate`                      | Integer parts per million (PPM); divide by `1e6` for a fraction or `1e4` for a percentage |
| Ecosystem totals, FPS earnings/exposure, FCS info            | Already scaled JSON numbers for display; do not divide again                              |
| Transfer `created`, savings `updated`, analytics `timestamp` | Unix seconds; some endpoints encode these as decimal strings                              |
| Prices mapping `timestamp`                                   | Unix milliseconds; `0` can mean no usable price                                           |
| Status `lastChecked`                                         | ISO timestamp string                                                                      |
| `targetChain`                                                | Decimal CCIP selector string, not an EIP-155 chain ID                                     |

There is no global “all amounts are wei” or “all timestamps are seconds” rule. Keep raw quantities as integer strings/`BigInt`. JSON numbers may already have lost precision; do not use display totals to construct transaction amounts. Validate field types and units for each endpoint. Missing, null or malformed data is distinct from zero.

## Make a first request

Fetch the ZCHF overview for a supply and TVL dashboard:

```bash
curl --fail 'https://api.frankencoin.com/ecosystem/frankencoin/info'
```

Read `erc20` for token metadata, `chains` for chain-specific data, `token` for ZCHF metrics and `tvl` for CHF and USD totals. Use the [ecosystem field guide](/api-docs/ecosystem#frankencoin-and-legacy-fps) to interpret the response. Add `/fcs/info` for the canonical share token's supply and reference prices.

Most routes return JSON, but their wrappers differ: a list may use `{num, list}`, an address lookup may use `{num, owners, map}`, and an account response may be nested by chain and module. Read the endpoint's shape before iterating it. A successful HTTP status can still contain an error object; check both status and payload before displaying results.

## Executable examples

The JavaScript blocks are ES modules for Node.js 18+ or a compatible browser. Save each page's JavaScript block as its page name plus `.mjs`, keeping them in one directory (`README.mjs`, `transfers.mjs`, `savings.mjs`, `prices.mjs`). Imports below refer to those files. Functions only perform GET requests when called. The shared helpers below handle transport, type checks and exact amount formatting; the feature pages show how to interpret their results.

### Shared validation and exact display formatting

`getJson` rejects HTTP errors, error envelopes and malformed JSON. This helper accepts object/array endpoints; scalar endpoints need a separate validator. Endpoint functions must also check the expected schema. It propagates failures rather than treating them as empty results. `formatUnits` retains fractional base units without converting to `Number`.

```javascript
export function object(value, label = 'object') {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    throw new TypeError(`Expected ${label}`);
  }
  return value;
}

export function uint(value) {
  if (typeof value !== 'string' || !/^(0|[1-9][0-9]*)$/.test(value)) {
    throw new TypeError('Expected a decimal unsigned integer string');
  }
  return BigInt(value);
}

export function address(value) {
  if (typeof value !== 'string' || !/^0x[0-9a-fA-F]{40}$/.test(value)) {
    throw new TypeError('Expected a 20-byte address');
  }
  return value.toLowerCase();
}

export function formatUnits(raw, decimals) {
  const amount = uint(raw);
  if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) {
    throw new TypeError('Invalid decimals');
  }
  if (decimals === 0) return amount.toString();
  const digits = amount.toString().padStart(decimals + 1, '0');
  const fraction = digits.slice(-decimals).replace(/0+$/, '');
  return digits.slice(0, -decimals) + (fraction ? `.${fraction}` : '');
}

export async function getJson(path, fetchImpl = fetch) {
  if (typeof path !== 'string' || !path.startsWith('/') || path.startsWith('//')) {
    throw new TypeError('Expected an API-relative path');
  }
  const url = new URL(path, 'https://api.frankencoin.com');
  if (url.origin !== 'https://api.frankencoin.com') throw new TypeError('Invalid origin');
  const response = await fetchImpl(url, {
    method: 'GET', redirect: 'error', signal: AbortSignal.timeout(10000)
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  const data = await response.json();
  if (data === null || typeof data !== 'object' ||
      'error' in data || 'errors' in data || 'statusCode' in data) {
    throw new Error('Invalid API response or error envelope');
  }
  return data;
}
```

The address helper validates syntax, not EIP-55 checksum or contract identity. Use a chain-aware address registry and the selected contract's ABI when moving from indexed data to contract calls.

## Packages and source

* [Interactive API specification](https://api.frankencoin.com/): request parameters and response schemas.
* [API source](https://github.com/Frankencoin-ZCHF/frankencoin-api) and [API types package](https://www.npmjs.com/package/@frankencoin/api): service implementation and types for a pinned release.
* [Contract repository](https://github.com/Frankencoin-ZCHF/FrankenCoin) and [contract SDK](https://www.npmjs.com/package/@frankencoin/zchf): contract interfaces and deployment references.
* [Documentation validation](https://github.com/Frankencoin-ZCHF/gitbook/tree/main/tests/api-docs/README.md): example provenance, saved responses and offline test instructions.


# FCS API

Frankencoin Share Token (FCS) is the canonical governance and share token. Use this controller to build an FCS overview with supply, reference prices and the current marginal redemption discount. The two endpoints answer different questions: `/fcs/info` describes the share token, while `/fcs/discount` describes the effect of recent redemptions.

Start with the [share guide](/pool-shares) for the holder journey, [FCS mechanics](/pool-shares/fcs) for contract rules or [migration](/pool-shares/fcs-migration) for existing holdings. Underlying FPS supply, prices, reserve data and earnings remain under the FPS routes; they are not FCS metrics.

## GET /fcs/info

Request `GET /fcs/info` for token metadata, its reported chain/address and the `token` metrics. Example response (historical values, not a current quote):

```json
{
  "erc20": {"name": "Frankencoin Share", "symbol": "FCS", "decimals": 18},
  "chain": {"chainId": 1, "address": "0xDb861830D9Ae2d1fCF99fA0cfd3973de382B0B5b"},
  "token": {
    "ask": 1282.183703648763,
    "bid": 1282.183703648763,
    "totalAssets": 94965.24027041676,
    "totalSupply": 222.19571189409976,
    "isBinding": false
  }
}
```

| Field                    | Type and meaning                                                                                                                |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `erc20`                  | Reported name, symbol and decimals                                                                                              |
| `chain`                  | One chain/address object, not the FPS `chains` map                                                                              |
| `token.ask`, `token.bid` | Scaled numbers in ZCHF per FCS, from contract reference prices; not secondary-market quotes or size-specific execution previews |
| `token.totalAssets`      | Scaled ZCHF value; in audited V3, `ZCHF.equity() * totalSupply() / FPS1.totalSupply()`, the wrapper holders' fraction of equity |
| `token.totalSupply`      | Scaled FCS supply, not total legacy FPS supply                                                                                  |
| `token.isBinding`        | Boolean returned by the wrapper; binding depends on its share of underlying FPS voting power, not FCS token supply              |

Display the price fields as ZCHF per FCS, `totalSupply` as FCS and `totalAssets` as ZCHF. These values are already scaled; dividing them by `1e18` would scale them twice.

FCS wraps legacy FPS one for one, but ZCHF is the ERC-4626 vault asset. Wrapping FPS and depositing ZCHF are different entry paths. FCS supply measures issued wrapper shares; it is not the total supply of underlying FPS.

## GET /fcs/discount

Request `GET /fcs/discount` alongside the overview to show the marginal multiplier and recovery countdown. Example response (historical values):

```json
{
  "discount": 1,
  "recentlyRedeemed": "0",
  "weightedRecentRedemptions": "0",
  "redemptionAnchor": 0,
  "recoveryPeriodSeconds": 604800,
  "recoveryCountdownSeconds": 0
}
```

| Field                       | Type and meaning                                                                                |
| --------------------------- | ----------------------------------------------------------------------------------------------- |
| `discount`                  | Scaled marginal multiplier from `currentDiscount(0)`; `1` means no reduction, not a 1% discount |
| `recentlyRedeemed`          | Raw FCS quantity as a decimal integer string (18 decimals)                                      |
| `weightedRecentRedemptions` | Time-decayed redemption quantity as a decimal integer string (18 decimals)                      |
| `redemptionAnchor`          | Unix seconds, number                                                                            |
| `recoveryPeriodSeconds`     | Recovery period in seconds                                                                      |
| `recoveryCountdownSeconds`  | Number, `max(0, anchor + period - API server time)`                                             |

The seven-day recovery applies absent further redemptions; further activity can extend it. A multiplier of `1` means no reduction at the margin. A size-specific redemption can receive a different multiplier, so the countdown and `discount` belong in an overview, not in a calculation of guaranteed proceeds.

The API substitutes `0` if reading `bid()` fails and `1` if reading `currentDiscount(0)` fails. These values do not distinguish a successful read from a failed call. Use a direct contract read when the distinction affects a quote.

## Contract reads and transactions

To connect the overview to a deposit or redemption form:

1. Resolve the selected chain, FCS contract and ABI. Choose the operation: `deposit` and `withdraw` take ZCHF assets; `mint` and `redeem` take FCS shares.
2. Call the corresponding `previewDeposit`, `previewMint`, `previewWithdraw` or `previewRedeem` with the user's amount in base units for a size-specific quote.
3. Read `maxDeposit`, `maxMint`, `maxWithdraw` or `maxRedeem` for the account and check applicable holder/wrapper conditions. Previews calculate amounts; limits and eligibility determine whether the operation is available.
4. Refresh the quote and limits before asking the wallet to submit the transaction. Display the API's reference prices separately from that quote.

`convertToAssets` is a reference conversion, not discounted redemption proceeds. Use each operation's own preview and limit.

For a governance view, read holder votes and eligibility from the contracts rather than inferring them from supply or price. Direct FPS wrapping carries votes; fresh ZCHF entry and WFPS migration do not supply immediate votes. Internal wrapper quorum and the wrapper's legacy FPS quorum are separate.

## Source and version boundary

The contract mechanics above refer to the [ChainSecurity FPS2 report](https://reports.chainsecurity.com/Frankencoin/ChainSecurity_Frankencoin_FPS2_Audit.pdf), final V3 at `c1f229e3b26050367aafcb55da294342b4cae382` (14 July 2026). It calls the wrapper FPS2; it does not establish the identity of a deployed contract. The [FCS service source at `9013d8f`](https://github.com/Frankencoin-ZCHF/frankencoin-api/blob/9013d8fadf2bcc251d236c78328958ebcfbe1c26/src/modules/fcs/fcs.service.ts) defines the field conversions and fallbacks.


# Ecosystem API

Use the Ecosystem API to build a ZCHF overview, discover which collateral tokens appear in lending positions and follow minter proposals. It brings token metadata, chain-specific supply and reserve data into one place.

For Frankencoin's canonical governance and share token, use the [FCS controller](/api-docs/fcs). The FPS fields here describe its underlying equity token. See [API conventions](/api-docs) for units.

## Frankencoin and legacy FPS

| GET path                               | Response                                                                                                        |
| -------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `/ecosystem/frankencoin/info`          | `erc20`, `chains`, `token`, `fps`, `tvl` objects                                                                |
| `/ecosystem/frankencoin/keyvalues`     | Metric-ID map of `{id, value, amount}`; interpret `amount` by metric, since it can be a count or a raw quantity |
| `/ecosystem/frankencoin/totalsupply`   | ZCHF supply summary, not FCS supply                                                                             |
| `/ecosystem/coinmarketcap/totalsupply` | Supply route for the market-data integration                                                                    |
| `/ecosystem/fps/info`                  | Legacy FPS `erc20`, `chains`, `token`, `earnings`, `reserve` objects                                            |

Frankencoin info has `token.usd` (USD per ZCHF), `token.supply` (scaled ZCHF), chain-specific `supply` values, and `tvl.usd`/`tvl.chf`. The embedded `fps` object remains legacy FPS.

FPS info has `token.price` (ZCHF per FPS), `token.totalSupply` (FPS), `token.marketCap` (ZCHF), `earnings.profit`/`loss` and `reserve.balance`/`equity`/`minter` (ZCHF). These are already scaled JSON numbers. It does not supply `circulatingSupply`, `priceHistory` or `mintingCapacity`. Use the [prices history routes](/api-docs/prices) for available historical observations; minting eligibility requires the relevant contract state.

### Build a token overview

1. Request `GET /ecosystem/frankencoin/info` and display ZCHF supply, USD price and TVL with their stated units.
2. Iterate `chains` to show each reported chain's token address and supply. Retain chain ID with each address when linking to an explorer.
3. Request `GET /fcs/info` for FCS supply and reference prices. Use `GET /ecosystem/fps/info` only for the underlying FPS and reserve panel.
4. Add [analytics](/api-docs/analytics) for financial history rather than treating the current overview as a historical series.

## Minter proposals

A minter proposal requests authorisation for a contract to mint ZCHF. Use the proposal list to show who proposed a minter, its application period and any recorded denial. The chain-specific route narrows that view to one network; `chainId` is an EIP-155 chain ID, such as `1` for Ethereum.

* `GET /ecosystem/minter/list`
* `GET /ecosystem/minter/list/:chainId`

The response is `{num, list}`. A proposal contains `chainId`, `minter`, `suggestor`, `txHash`, `applicationPeriod`, `applicationFee`, `applyMessage` and `applyDate`, plus nullable denial fields `denyMessage`, `denyDate`, `denyTxHash` and `vetor` (the API's spelling).

`applyDate` and `denyDate` are Unix seconds; `applicationPeriod` is a duration in seconds and `applicationFee` is a raw ZCHF string with 18 decimals. Show `applyMessage` as the proposal description and, where present, `denyMessage` as the denial reason. The list includes denied proposals and has no `active/deprecated/proposed` enum or version field. A null denial field does not establish current authorisation: check the selected chain's contract state before offering a minting action.

## Collateral catalogue

| GET path                                  | Purpose                         |
| ----------------------------------------- | ------------------------------- |
| `/ecosystem/collateral/list`              | `{num, list}` of token metadata |
| `/ecosystem/collateral/mapping`           | Address-keyed metadata          |
| `/ecosystem/collateral/positions`         | Positions grouped by collateral |
| `/ecosystem/collateral/positions/details` | Grouped position details        |
| `/ecosystem/collateral/stats`             | Collateral statistics           |

Metadata includes `chainId`, `address`, `name`, `symbol` and `decimals`. Catalogue membership records use in indexed positions, not approval of a new position or a collateral whitelist. Original positions and clones have their own conditions.

For a collateral explorer, use `/list` to populate a token selector and `/positions` to find related positions. Choose `/positions/details` when the view also needs position balances and owners, or `/stats` for a collateral summary. Match tokens by chain and address, not symbol, and use each token's `decimals` to display its balances.

Position V1/V2 identifies a lending-contract version. It does not identify the FCS governance migration, API version or savings module version. Resolve chain, address and ABI from the [contract repository](https://github.com/Frankencoin-ZCHF/FrankenCoin) and [published SDK](https://www.npmjs.com/package/@frankencoin/zchf), pinned to the deployment used by the application.


# Positions API

A position is a borrowing contract that holds collateral against minted ZCHF. Use the Positions API to build a borrower's portfolio, find original positions to inspect for cloning and follow changes in debt, collateral and ownership.

## Endpoints

| GET path                                               | Purpose                                                                        |
| ------------------------------------------------------ | ------------------------------------------------------------------------------ |
| `/positions/list`                                      | Position list                                                                  |
| `/positions/mapping`                                   | Positions keyed by address                                                     |
| `/positions/open`                                      | Indexed open positions                                                         |
| `/positions/requests`                                  | Indexed position requests                                                      |
| `/positions/owners`                                    | `{num, owners, map}` with lowercase owner-address keys and arrays of positions |
| `/positions/mintingupdates/list`                       | Recent minting updates                                                         |
| `/positions/mintingupdates/mapping`                    | Updates grouped by position                                                    |
| `/positions/mintingupdates/position/:version/:address` | Updates for a position version/address                                         |
| `/positions/mintingupdates/owner/:address`             | Owner's minting updates                                                        |
| `/positions/owner/:address/fees`                       | Owner fee history                                                              |
| `/positions/owner/:address/debt`                       | Owner debt history                                                             |
| `/positions/owner/:address/history`                    | Position ownership history                                                     |
| `/positions/owner/:address/transfers`                  | Ownership transfers                                                            |

## Build an owner's portfolio

1. Fetch `GET /positions/owners`. The response contains `num`, `owners` and `map`.
2. Validate the owner's address and look it up in `map` using its lowercase form. The value is an array of positions. There is no bare `/positions/owner/:address` route.
3. For each position, show collateral balance, minted debt and state flags using the units below. Keep the position address and `version` for subsequent queries.
4. Add price observations for an indicative valuation using the [prices example](/api-docs/prices#indicative-position-valuation). It validates the map and separates an empty portfolio from malformed data.

```bash
curl --fail 'https://api.frankencoin.com/positions/owners'
```

## Fields and units

| Fields                                                | Meaning                                                                                                                          |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `position`, `owner`, `original`, `collateral`, `zchf` | Contract or owner addresses                                                                                                      |
| `version`, `isOriginal`, `isClone`                    | Lending-contract version and position type                                                                                       |
| `denied`, `closed`                                    | Indexed state flags, not a full transaction-eligibility check                                                                    |
| `minted`, ZCHF limits/capacities                      | Raw ZCHF integer strings, 18 decimals                                                                                            |
| `collateralBalance`, `minimumCollateral`              | Raw collateral integer strings; use `collateralDecimals`                                                                         |
| `price`                                               | Contract price scaling, not a fiat display quote: raw price times collateral base units divided by `1e18` yields ZCHF base units |
| `annualInterestPPM`, `reserveContribution`            | Integer PPM; the latter is a fraction, not an annual rate                                                                        |
| `created`, `start`, `expiration`, `challengePeriod`   | Seconds; `cooldown` may contain a large sentinel rather than a usable calendar date                                              |

For collateral with `d` decimals, the displayed contract price in ZCHF per whole collateral token is `rawPrice * 10^d / 10^36`. Do not divide all balances or all prices by `1e18`. See [prices](/api-docs/prices) for separate off-chain CHF/USD valuations.

## State and history

Original positions have a proposal period; clones derive parameters from an existing original position and remain subject to their own conditions. An indexed “open” position may still be unable to mint because of cooldown, expiry, a challenge or another contract constraint. Read the selected version's state before constructing a transaction.

Use `/positions/requests` to monitor proposals and `/positions/open` to narrow a position browser. To find potential originals for cloning, inspect `isOriginal`, the collateral and the original's conditions; the open list alone is not a list of eligible clones.

### Follow a position or owner

Request `/positions/mintingupdates/position/:version/:address` with the position's lending-contract version and address to inspect its recent updates. Use `/positions/mintingupdates/owner/:address` for the owner's recent minting activity. For example:

```bash
curl --fail 'https://api.frankencoin.com/positions/mintingupdates/position/2/0x826C54287c0C1E2A4D0fbF81E2e734c85C48d3f4'
```

The owner `/fees` and `/debt` routes support fee and debt views; `/history` and `/transfers` explain changes in position ownership. Ownership history matters when attributing earlier activity: a position's current owner need not have owned it when a historical event occurred.

Minting updates and owner history queries can return up to 1000 recent records, with no documented exhaustive pagination route. For complete accounting, reconcile a block-paginated contract-event index against chain state and ownership changes.

See [position mechanics](/positions) and [challenges](/api-docs/challenges). V1/V2 refers to lending contracts, not API releases or the FCS wrapper.


# Challenges API

A challenge tests whether a position's stated collateral price is too high. The Challenges API lets an application follow challenges, inspect bids and show available auction prices. Use it for an auction browser or a position's challenge history; the contract mechanics below explain what the records represent.

## Endpoints and identifiers

| GET path                      | Purpose                                                                                                    |
| ----------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `/challenges/list`            | `{num, list}` of challenges                                                                                |
| `/challenges/mapping`         | Challenges keyed by challenge ID                                                                           |
| `/challenges/challengers`     | Challenges grouped by challenger                                                                           |
| `/challenges/positions`       | Challenges grouped by position                                                                             |
| `/challenges/prices`          | `{num, ids, map}`; active challenge IDs mapped to current prices, not groups of challenges sharing a price |
| `/challenges/bids/list`       | Bid list                                                                                                   |
| `/challenges/bids/mapping`    | `{num, bidIds, map}` keyed by full bid ID                                                                  |
| `/challenges/bids/bidders`    | Bids grouped by bidder                                                                                     |
| `/challenges/bids/challenges` | Bids grouped by challenge                                                                                  |
| `/challenges/bids/positions`  | Bids grouped by position                                                                                   |

A challenge ID has the form `<position>-challenge-<number>`. A bid ID adds `-bid-<numberBid>`. These are distinct keys; `/bids/mapping` is not keyed by challenge ID. Consult the selected response wrapper before iterating a map.

### Build an auction view

1. Fetch `GET /challenges/list` to show challenge records, or use `/challenges/positions` for a position-centred view.
2. Fetch `GET /challenges/prices`. Iterate its `ids` and read the corresponding entries in `map` to show available prices for active challenge IDs. An empty map supplies no current auction prices.
3. Use `/challenges/bids/challenges` to attach bid history to each challenge. Use `/challenges/bids/mapping` only when looking up a full bid ID.
4. Load the position's collateral decimals and lending-contract version before displaying amounts. Before a bid, read current auction state from the matching contract; an indexed price can lag a changing auction.

```bash
curl --fail 'https://api.frankencoin.com/challenges/prices'
```

## Units and outcome fields

Challenge rows include `version`, `position`, `number`, `start`, `duration`, `size`, `liqPrice`, `filledSize`, `acquiredCollateral`, `status` and `txHash`. Bid rows add `numberBid`, `bidder`, `bidType`, `bid` and `price`. Amounts and timestamps are decimal strings; `start` is Unix seconds and `duration` is seconds.

`size`, `filledSize` and `acquiredCollateral` use the position's collateral base units. `bid` uses ZCHF base units. `liqPrice` and bid `price` use the contract price scale: multiplying by collateral base units and dividing by `1e18` yields ZCHF base units. Use collateral decimals to display ZCHF per whole token. A large raw price alone says nothing about undercollateralisation.

`status` is an indexer label, not a summary of the economic outcome. A `Success` record can have zero `acquiredCollateral`. Show `bidType`, `filledSize` and `acquiredCollateral` alongside the label, and use contract events/state to establish whether collateral changed hands or the position closed. Do not count every `Success` row as a liquidation.

## Challenge and bid mechanics

For MintingHubV2, the first phase allows aversion at the applicable challenge price. In the subsequent Dutch-auction phase, the price falls and a bid executes the corresponding collateral purchase immediately. Collateral is not held for a batch distribution based on bids collected until a fixed end time.

The challenge's `version`, position and minting hub determine the relevant ABI and rules. Do not apply the V2 explanation indiscriminately to V1 records. See [auction mechanics](/positions/auctions) and the [MintingHubV2 source](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/minting/v2/MintingHubV2.sol) for version-specific conditions.


# Prices API

Use the Prices API to display token prices, chart collateral price history and estimate the fiat value of a position. Each observation carries currency and source information so that an application can keep a valuation tied to its inputs.

These display prices are not a uniform protocol oracle and do not set a position's challenge price. For the canonical share token's reference prices, use [FCS](/api-docs/fcs). FPS price routes here report the underlying FPS token, not an FCS market price.

## Endpoints

| GET path                             | Response or purpose                                                                |
| ------------------------------------ | ---------------------------------------------------------------------------------- |
| `/prices/ticker/:ticker`             | `{chf, usd}` numbers for a ticker                                                  |
| `/prices/list`                       | Array of token metadata and price observations                                     |
| `/prices/mapping`                    | Lowercase address -> price observation                                             |
| `/prices/erc20/mint`                 | ZCHF metadata                                                                      |
| `/prices/erc20/fps`                  | Legacy FPS metadata                                                                |
| `/prices/erc20/collateral`           | Collateral metadata                                                                |
| `/prices/owner/:address/valueLocked` | Year-keyed values in raw ZCHF strings; use the Positions API for current positions |
| `/prices/marketChart`                | Market-data `prices`, `market_caps`, `total_volumes` series                        |
| `/prices/history/list`               | Available collateral histories keyed by address                                    |
| `/prices/history/:address`           | Available history for a token                                                      |
| `/prices/history/ratio`              | `collateralRatioByFreeFloat` and `collateralRatioBySupply` series                  |

## Currencies, units and freshness

A mapping observation contains `chainId`, `address`, `name`, `symbol`, `decimals`, `price.chf`, `price.usd`, `source` and `timestamp`. Prices are already scaled fiat values per whole token. `timestamp` is Unix **milliseconds**, unlike transfer and savings timestamps. Sources include `defillama`, `thegraph`, `custom` and `null`.

A record with a null source, timestamp zero or zero price may represent unavailable data. Reject missing/stale prices for a valuation rather than substitute zero or silently select another currency. Define a freshness threshold for the application. Preserve the quoted currency and source with each displayed value. A ticker alone is not a unique chain/token identity.

### Look up a price and build a chart

For a quick ticker display, request `/prices/ticker/:ticker`, for example:

```bash
curl --fail 'https://api.frankencoin.com/prices/ticker/WBTC'
```

Read `chf` or `usd` according to the display currency. For portfolio work, fetch `/prices/mapping` instead, select the lowercase collateral address and verify the observation's chain ID and decimals against the position.

To chart one collateral, request `/prices/history/:address`. Read its CHF prices, convert millisecond timestamp keys to dates and sort the observations chronologically. Use `/prices/history/list` when the view needs several collateral histories. Keep gaps visible; observations need not arrive at regular intervals.

The separate `/prices/marketChart` route provides `prices`, `market_caps` and `total_volumes` series. Its schema does not specify the quote currency, so confirm the feed's denomination before labelling the chart.

## Aggregate collateral ratios

Use `/prices/history/ratio` to plot the `collateralRatioBySupply` and `collateralRatioByFreeFloat` series separately. The names distinguish total supply from free float, but the schema does not specify the full numerator or free-float exclusions. Confirm those inputs before comparing the series to an external ratio. With the same numerator, a smaller free-float denominator yields a larger ratio; it is not inherently a more conservative measure against the same threshold.

These series do not define universal “healthy” bands or a protocol liquidation threshold. State the valuation assumptions and denominator whenever displaying a ratio. The protocol's challenge mechanism is separate from an off-chain portfolio estimate.

## Indicative position valuation

Fetch the owner's positions from `/positions/owners` and price observations from `/prices/mapping`. Match lowercase owner keys and collateral addresses, and use each token's decimals. Both sides of a ratio must use the same currency: collateral units times CHF per token divided by ZCHF debt times CHF per ZCHF. An assumption of one CHF per ZCHF is explicit parity valuation, not an observed exchange price.

The example below returns a rational pair of `BigInt` values for an indicative ratio. It keeps raw balances exact and uses the decimal values already returned by the price API; it cannot restore precision the server has discarded. It treats zero debt as a separate state and rejects missing or stale prices. The caller supplies chain ID, the ZCHF contract address, valuation time, freshness limit and an explicit CHF-per-ZCHF quote (or parity assumption).

```javascript
import {object, uint, address, formatUnits} from './README.mjs';

export function ownerPositions(response, owner) {
  const map = object(object(response).map, 'owner map');
  const rows = map[address(owner)];
  if (rows === undefined) return [];
  if (!Array.isArray(rows)) throw new TypeError('Expected position array');
  for (const row of rows) {
    if (address(object(row).owner) !== address(owner)) throw new TypeError('Owner mismatch');
  }
  return rows;
}

function decimal(value) {
  if (typeof value !== 'number' && typeof value !== 'string') throw new TypeError('Invalid decimal');
  if (typeof value === 'number' && (!Number.isFinite(value) || value <= 0)) throw new TypeError('Invalid quote');
  const match = /^(0|[1-9][0-9]*)(?:\.([0-9]+))?(?:e([+-]?[0-9]+))?$/i.exec(String(value));
  if (!match) throw new TypeError('Invalid decimal quote');
  const fraction = match[2] || '';
  const scale = fraction.length - Number(match[3] || 0);
  if (!Number.isSafeInteger(scale) || Math.abs(scale) > 255) throw new RangeError('Quote scale');
  let numerator = BigInt(match[1] + fraction), denominator = 1n;
  if (numerator === 0n) throw new RangeError('Quote must be positive');
  if (scale >= 0) denominator = 10n ** BigInt(scale);
  else numerator *= 10n ** BigInt(-scale);
  return {numerator, denominator};
}

export function indicativeRatio(position, observation, config) {
  object(position); object(config);
  if (!Number.isSafeInteger(config.chainId) || config.chainId <= 0 ||
      address(position.zchf) !== address(config.zchfAddress)) throw new TypeError('Asset mismatch');
  const collateral = uint(position.collateralBalance), debt = uint(position.minted);
  const decimals = position.collateralDecimals;
  if (!Number.isInteger(decimals) || decimals < 0 || decimals > 255) throw new TypeError('Invalid decimals');
  const zchf = decimal(config.zchfChf);
  if (debt === 0n) return {status: 'no-debt'};
  object(observation, 'price observation');
  if (observation.chainId !== config.chainId ||
      address(observation.address) !== address(position.collateral) ||
      observation.decimals !== decimals) throw new TypeError('Collateral identity mismatch');
  const {nowMs, maxAgeMs} = config;
  if (!Number.isSafeInteger(nowMs) || !Number.isSafeInteger(maxAgeMs) || maxAgeMs < 0 ||
      !Number.isSafeInteger(observation.timestamp) || observation.timestamp <= 0 ||
      observation.timestamp > nowMs || nowMs - observation.timestamp > maxAgeMs ||
      typeof observation.source !== 'string' || observation.source.length === 0) {
    throw new Error('Missing or stale price');
  }
  const chf = decimal(object(observation.price).chf);
  const numerator = collateral * chf.numerator * zchf.denominator * 10n ** 18n;
  const denominator = 10n ** BigInt(decimals) * chf.denominator * debt * zchf.numerator;
  return {status: 'indicative', currency: 'CHF', numerator: numerator.toString(),
    denominator: denominator.toString(), ratioDecimal: formatUnits((numerator * 1000000n / denominator).toString(), 6)};
}
```

`ratioDecimal` truncates to six decimal places for display. The numerator and denominator preserve the exact ratio of the supplied decimal inputs. The caller can fetch both maps with the [shared GET helper](/api-docs#executable-examples), then use `priceMap[position.collateral.toLowerCase()]`. An unsupported chain or missing observation is an error, not a zero valuation.


# Savings API

Savings modules let ZCHF holders earn interest on deposited balances. Use this API to show an account's savings, collected interest and recent activity, compare module rates or follow proposed rate changes. Each chain and module has its own records, so an account may have several separate savings balances.

## Endpoints

| GET path                               | Response                                                                             |
| -------------------------------------- | ------------------------------------------------------------------------------------ |
| `/savings/core/info`                   | `{status, totalBalance, ratioOfSupply, totalInterest}`                               |
| `/savings/core/ranked`                 | Account array, up to 1000 accounts ranked by balance                                 |
| `/savings/core/balance/:account`       | `chainId -> module address -> account record`                                        |
| `/savings/core/activity/:account`      | Up to 1000 recent activity records, not complete account history                     |
| `/savings/leadrate/info`               | `{rate, proposed, open}`, each keyed by chain and module                             |
| `/savings/leadrate/rates`              | Approved-rate records                                                                |
| `/savings/leadrate/proposals`          | Indexed rate proposals                                                               |
| `/savings/referrer/:referrer/mapping`  | `{num, accounts, map}`; `map` is chain -> module -> account -> details               |
| `/savings/referrer/:referrer/earnings` | `{earnings, chains, total}`; `earnings` is chain -> module -> account -> scaled ZCHF |

## Build a savings view

1. Fetch `GET /savings/core/info` for module rates and aggregate savings totals.
2. Fetch `/savings/core/balance/:account` with the wallet's address. Select the chain and exact module address in both responses, or show each pair as a separate account row.
3. Display the module's annual simple rate, the account's saved balance and its cumulative collected interest. The [display example](#validated-display-and-hypothetical-projection) keeps these units separate.
4. Fetch `/savings/core/activity/:account` for recent deposits, withdrawals and interest collections. Label this as recent activity: the endpoint returns at most 1000 records with no documented exhaustive pagination.
5. Use [wallet contract reads](/api-docs/wallet-integration#displaying-savings) to add pending interest, which is not part of the API's cumulative collected-interest field.

An absent module or an empty response is not a confirmed zero on-chain balance. For complete account history, reconcile an independent block-paginated event index.

## Schemas and units

`status` in `/core/info` is a map from chain ID to module address to module record. `totalBalance`, `ratioOfSupply` and `totalInterest` are **root-level fields**, not entries inside `status`.

| Field                                              | Type and unit                                      |
| -------------------------------------------------- | -------------------------------------------------- |
| Module `chainId`, `module`                         | Number and address                                 |
| Module `balance`, `save`, `withdraw`, `interest`   | Raw ZCHF decimal integer strings, 18 decimals      |
| Module `rate`                                      | Number, annual simple rate in PPM                  |
| Module `updated`                                   | Unix seconds, number                               |
| Module/account `counter`                           | Object of event counts                             |
| Root `totalBalance`, `totalInterest`               | Already scaled ZCHF numbers                        |
| Root `ratioOfSupply`                               | Fraction, not percentage                           |
| Account `account`, `module`, `chainId`             | Identity fields, not a top-level account wrapper   |
| Account `created`, `updated`                       | Unix seconds, numbers                              |
| Activity `kind`, `amount`, `txHash`, `blockheight` | Event kind, raw ZCHF amount, hash and block number |

Account `interest` is cumulative indexed **collected** interest, not a live pending-interest quote. The indexed gross interest total need not equal the net amount credited to the saver after referral fees. Referrer endpoints differ from core endpoints: referral balances and earnings are scaled numbers, and `referrerFee` is PPM. Do not apply the core raw-string conversion to them.

Example `/core/balance/:account` response (historical values):

```json
{
  "1": {
    "0x27d9ad987bde08a0d083ef7e0e4043c857a17b38": {
      "chainId": 1,
      "account": "0x963ec454423cd543db08bc38fc7b3036b425b301",
      "module": "0x27d9ad987bde08a0d083ef7e0e4043c857a17b38",
      "balance": "2000000000000000000000000",
      "created": 1751307767,
      "updated": 1785535811,
      "save": "8285068007024581430745819",
      "interest": "11897690286241121258229",
      "withdraw": "6296965697310822552004048",
      "counter": {"save": 14, "interest": 25, "withdraw": 22}
    }
  }
}
```

## Rates, accrual and module selection

The rate is set through governance proposals and changes, not automatically calculated from reserve metrics. For `rate=40000`, the annual simple fraction is `40000 / 1000000 = 0.04`, or 4%. Select the chain **and exact module address**; do not select the first result of `Object.values` or assume that every module has the same rate.

The contract accrues simple interest using rate ticks. Collecting interest through a state-changing refresh adds net interest to the saved balance, after which that balance can earn interest. APY therefore depends on refresh frequency, changing rates, entry delays, fees and integer rounding. The API's `rate` is not an automatically compounded APY.

For a hypothetical unchanged rate and an already interest-eligible balance, gross interest in base units is `floor(principal * ratePPM * seconds / 1000000 / 31536000)`. This is an estimate, not the live contract calculation across rate changes or delayed entry. Read `accruedInterest` for current gross pending interest on the chosen module. With a nonzero referrer, the inspected referral-capable contract deducts `floor(gross * referralFeePPM / 1000000)`; the remainder is net interest.

Module presence does not identify its ABI or version. Configure those explicitly. The [wallet guide](/api-docs/wallet-integration) distinguishes referral-capable modules from older interfaces and explains how to deposit, collect interest and withdraw.

### Follow rate changes

Use `/savings/leadrate/info` to show current approved rates alongside proposed changes. Its `rate`, `proposed` and `open` maps are each keyed by chain and module. Keep a proposal separate from the active rate until the corresponding change takes effect. `/savings/leadrate/rates` and `/savings/leadrate/proposals` provide the indexed records for rate-history and proposal views; `approvedRate` also uses PPM.

### Track referral earnings

Pass the referrer's address to `/savings/referrer/:referrer/mapping` to find referred accounts, grouped by chain, module and account. Join those identities to `/savings/referrer/:referrer/earnings` for an earnings breakdown, or use its `chains` and `total` fields for summaries. Referral balances and earnings are already scaled ZCHF numbers. `referrerFee` is PPM, not a ZCHF amount.

## Validated display and hypothetical projection

Fetch `/savings/core/info` and `/savings/core/balance/:account` with `getJson` from the [shared example](/api-docs#executable-examples), then pass the parsed responses to `savingsDisplay`. Missing data throws rather than displaying zero. `estimateSimple` uses already eligible seconds and a constant rate; it is not a substitute for the contract's tick calculation.

```javascript
import {object, uint, address, formatUnits} from './README.mjs';

function integer(value, maximum) {
  if (!Number.isSafeInteger(value) || value < 0 || value > maximum) {
    throw new TypeError('Invalid integer parameter');
  }
  return BigInt(value);
}

export function savingsDisplay(info, balances, chainId, moduleAddress, account) {
  object(info); object(balances);
  if (!Number.isSafeInteger(chainId) || chainId <= 0) throw new TypeError('Invalid chain');
  const module = address(moduleAddress), owner = address(account);
  for (const field of ['totalBalance', 'totalInterest', 'ratioOfSupply']) {
    if (typeof info[field] !== 'number' || !Number.isFinite(info[field]) || info[field] < 0) {
      throw new TypeError(`Invalid ${field}`);
    }
  }
  const status = object(object(object(info.status)[String(chainId)])[module]);
  const balance = object(object(balances[String(chainId)])[module]);
  for (const row of [status, balance]) {
    if (row.chainId !== chainId || address(row.module) !== module) throw new TypeError('Module mismatch');
    for (const field of ['balance', 'save', 'withdraw', 'interest']) uint(row[field]);
  }
  if (address(balance.account) !== owner) throw new TypeError('Account mismatch');
  integer(status.rate, 16777215); // uint24 PPM, not a base-unit ZCHF amount.
  return {
    annualSimplePercent: formatUnits(String(status.rate), 4),
    savedZCHF: formatUnits(balance.balance, 18),
    collectedInterestZCHF: formatUnits(balance.interest, 18)
  };
}

export function estimateSimple(principal, ratePPM, eligibleSeconds, referralFeePPM, hasReferrer) {
  const rate = integer(ratePPM, 16777215);
  const seconds = integer(eligibleSeconds, Number.MAX_SAFE_INTEGER);
  const feeRate = integer(referralFeePPM, 250000);
  if (typeof hasReferrer !== 'boolean') throw new TypeError('Expected referrer state');
  const gross = uint(principal) * rate * seconds / 1000000n / 31536000n;
  const fee = hasReferrer ? gross * feeRate / 1000000n : 0n;
  return {gross: gross.toString(), fee: fee.toString(), net: (gross - fee).toString()};
}
```


# Transfers API

A transfer reference is public text attached to a ZCHF transfer, such as an invoice number, order ID or payment purpose. The Transfers API indexes reference-bearing transfers so an application can find recent activity and identify payments to verify against an invoice.

Ordinary ERC-20 transfers without references are outside this dataset. An indexed match is a candidate, not proof of payment settlement; the [invoice workflow](#invoice-integration-boundary) explains the checks needed before crediting it.

## Endpoints and response shapes

| GET path                                    | Response               | Boundary                                                      |
| ------------------------------------------- | ---------------------- | ------------------------------------------------------------- |
| `/transfer/reference/list`                  | `{num, list}`          | Cached observations, not a complete ledger or reliable cursor |
| `/transfer/reference/counter`               | Number                 | Not a reliable high-water mark                                |
| `/transfer/reference/by/count/:count`       | One record or an error | A missing record is not proof of no transfer                  |
| `/transfer/reference/by/from/:from`         | Array                  | Cached outgoing matches                                       |
| `/transfer/reference/by/to/:to`             | Array                  | Cached incoming matches                                       |
| `/transfer/reference/history/by/from/:from` | Array                  | Indexed history, capped at 100 rows, no exposed cursor        |
| `/transfer/reference/history/by/to/:to`     | Array                  | Same cap and completeness limitation                          |

The opposite-party filter is `to` on sender routes and `from` on recipient routes. `reference` is an **exact, case-sensitive** match, not substring search. Encode query parameters with `URLSearchParams`. Do not interpolate reference text into a URL. References are public, untrusted text; render them as text, not HTML, and never include secrets.

### Find transfers for an account

For a recent-activity view, choose `/by/from/:from` for outgoing transfers or `/by/to/:to` for incoming transfers. For an invoice search, add the other party and the exact reference string. The history routes add a time window, but cap the result at 100 records without a cursor.

For example, this request searches indexed outgoing history for one exact reference:

```bash
curl --fail --get 'https://api.frankencoin.com/transfer/reference/history/by/from/0x963eC454423CD543dB08bc38fC7B3036B425b301' \
  --data-urlencode 'reference=12 months loan'
```

Read each row's recipient, source chain, amount and `targetChain` as well as its reference. The [candidate lookup](#candidate-lookup-example) adds an expected recipient and time window and checks the response against the invoice requirements.

### Time ranges and errors

Use ISO dates or explicit UTC ISO timestamps for history `start` and `end`. The range has an inclusive start and exclusive end after the API converts the dates to seconds. Whole-second UTC boundaries avoid rounding ambiguity.

```
GET /transfer/reference/history/by/from/0x963eC454423CD543dB08bc38fC7B3036B425b301?start=2024-01-01T00%3A00%3A00.000Z&end=2025-01-01T00%3A00%3A00.000Z
```

Numeric Unix-second query strings can produce an error payload even with HTTP 200; use the ISO form above and check the payload shape. An error, timeout, null row or malformed response means **data unavailable**, never “no payments”. An empty valid array means no indexed matches in that response, not a complete absence of payments.

### Counter and completeness limitations

The counter is not a reliable high-water mark. Do not use counter increases, ordering or `count > lastCount` as a notification/payment cursor. Refresh the relevant account query for a recent-activity display and retain its incomplete-history status. Latest caches and capped history can omit records; splitting time ranges alone cannot prove completeness when 100 events share a boundary.

For a complete ledger, use a separately validated indexer or the relevant contracts' logs via an RPC with explicit block-range pagination, persisted checkpoints, reorg handling and reconciliation against receipts. Verify each supported chain, contract deployment block and event ABI. Do not label an API-only export complete.

## Record fields

| Field                  | Type and meaning                                                                                                                          |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `amount`               | Decimal integer string in ZCHF base units (18 decimals)                                                                                   |
| `chainId`              | Number: source EIP-155 chain ID                                                                                                           |
| `count`, `created`     | Decimal integer strings; the specification also allows numbers. `created` is Unix seconds. Preserve count without floating-point coercion |
| `from`, `sender`, `to` | Addresses; `from` and original `sender` can differ. Validate before case-normalised comparison                                            |
| `reference`            | Exact public reference string, possibly empty                                                                                             |
| `targetChain`          | Decimal string: `0` is the same-chain sentinel; otherwise a CCIP chain selector, **not** an EIP-155 ID                                    |
| `txHash`               | Full source-chain transaction hash                                                                                                        |

Keep CCIP selectors as strings or `BigInt`; some exceed JavaScript's safe integer range. Resolve nonzero selectors against the maintained CCIP configuration for the relevant deployment. Never compare `chainId !== targetChain` to detect destination settlement. A source-chain reference event does not establish receipt on the destination chain.

## Invoice integration boundary

A reference/amount match is only a **candidate for verification**. This API record does not supply the token-emitting address, log index, receipt success, confirmations, or destination settlement proof needed to authorise delivery.

Before crediting a payment:

1. Require the intended recipient, payer policy, exact invoice identifier, chain, token contract and integer amount. Reject underpayment; handle partial/overpayments under an explicit business policy.
2. Fetch and validate the successful transaction receipt on the expected chain. Decode the correct contract's reference and token events and bind them to each other; matching a hash alone is insufficient.
3. Apply a chain-specific finality/reorg policy. For bridges, verify destination token, recipient and settlement independently.
4. Deduplicate by `(chainId, txHash, logIndex)`, persist allocation to the invoice atomically, and prevent reuse for another invoice. API `count` is not a substitute for a verified event identity.
5. Keep unavailable, no-indexed-match, candidate and verified-settlement states distinct. No API-only path in this guide returns `paid`.

## Candidate lookup example

This example accepts only same-chain candidates and never reports `paid`. It matches one transfer against the full invoice amount; it does not aggregate partial payments. Duplicate API rows are collapsed for display only. Receipt-level event deduplication and invoice allocation remain separate.

```javascript
import {object, uint, address, getJson} from './README.mjs';

function invoiceFields(invoice) {
  object(invoice);
  const from = address(invoice.from), to = address(invoice.to);
  if (!Number.isSafeInteger(invoice.chainId) || invoice.chainId <= 0 ||
      typeof invoice.reference !== 'string' || invoice.reference.length === 0 ||
      uint(invoice.amount) === 0n) throw new TypeError('Invalid invoice requirements');
  return {...invoice, from, to};
}

export function matchCandidates(rows, invoice) {
  const expected = invoiceFields(invoice);
  if (!Array.isArray(rows)) throw new TypeError('Expected transfer array');
  const candidates = new Map();
  for (const row of rows) {
    object(row, 'transfer');
    const from = address(row.from), to = address(row.to);
    address(row.sender);
    const amount = uint(row.amount);
    uint(row.targetChain);
    for (const field of ['count', 'created']) {
      if (typeof row[field] === 'number') {
        if (!Number.isSafeInteger(row[field]) || row[field] < 0) throw new TypeError(field);
      } else uint(row[field]);
    }
    if (!Number.isSafeInteger(row.chainId) || row.chainId <= 0 ||
        typeof row.reference !== 'string' || typeof row.txHash !== 'string' ||
        !/^0x[0-9a-fA-F]{64}$/.test(row.txHash)) throw new TypeError('Invalid transfer');
    if (from === expected.from && to === expected.to && row.chainId === expected.chainId &&
        row.targetChain === '0' && row.reference === expected.reference &&
        amount >= uint(expected.amount)) {
      // Candidate display key only: the API does not expose a receipt logIndex.
      const key = JSON.stringify([row.chainId, row.txHash.toLowerCase(), String(row.count),
        from, to, row.amount, row.reference]);
      candidates.set(key, row);
    }
  }
  return {status: candidates.size ? 'unverified-candidates' : 'no-indexed-match',
    complete: false, candidates: [...candidates.values()]};
}

export async function historyCandidates(invoice, start, end, fetchImpl = fetch) {
  const expected = invoiceFields(invoice);
  const dates = [start, end].map(value => {
    if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.000Z$/.test(value)) {
      throw new TypeError('Use whole-second UTC ISO timestamps');
    }
    const date = new Date(value);
    if (!Number.isFinite(date.getTime()) || date.toISOString() !== value) throw new TypeError('Invalid date');
    return date;
  });
  if (dates[0] >= dates[1]) throw new RangeError('start must precede end');
  const query = new URLSearchParams({to: expected.to, reference: expected.reference, start, end});
  const rows = await getJson(`/transfer/reference/history/by/from/${expected.from}?${query}`, fetchImpl);
  return matchCandidates(rows, expected);
}
```

Pass an invoice object containing `from`, `to`, `chainId`, `reference` and a decimal integer-string `amount` in ZCHF base units to `historyCandidates`, along with whole-second UTC ISO start and end timestamps. It returns `unverified-candidates` when rows meet those requirements, or `no-indexed-match` when none do. Both results have `complete: false`; malformed or unavailable responses throw. Send candidates to the receipt-verification stage, not directly to order fulfilment.

See [API conventions](/api-docs) for units and shared validation.


# Analytics API

Use the Analytics API to explain changes in the shared equity pool, chart its financial history and break down income, costs and collateral exposure. Transaction logs pair recorded financial events with point-in-time metrics; daily logs provide a smaller series for charts.

[FCS](/pool-shares) is the canonical governance and share token. These routes describe the shared equity pool and underlying FPS, so FPS prices, supply and per-token earnings retain their FPS units. Use the [FCS controller](/api-docs/fcs) for FCS supply, reference prices and redemption discount data.

## Endpoints and limits

| GET path                           | Response and use                                                       |
| ---------------------------------- | ---------------------------------------------------------------------- |
| `/analytics/profitLossLog`         | `{num, logs}`; up to 1000 recent profit/loss observations              |
| `/analytics/transactionLog/json`   | `{num, logs, pageInfo}`; paginated financial events and metrics        |
| `/analytics/transactionLog/csvE18` | Transaction-log page as CSV with scaled amounts                        |
| `/analytics/dailyLog/json`         | `{num, logs}` of available daily snapshots, capped at 1000             |
| `/analytics/dailyLog/csvE18`       | Daily snapshots as CSV with scaled amounts                             |
| `/analytics/fps/exposure`          | `{general, exposures}`; underlying FPS metrics and collateral exposure |
| `/analytics/fps/earnings`          | Scaled earnings and expense categories for underlying FPS              |

## Transaction logs

A transaction-log row describes an indexed financial event through its `kind` and `amount`, identifies its chain and transaction, and includes financial totals at that point. A row such as `Equity:Loss` records a loss; `Savings:Withdrawn` identifies a withdrawal. An equity dashboard can show the event alongside the pool's equity, savings balance and FPS price instead of joining it to today's values.

These are API records derived from indexed activity, not raw on-chain event logs. `txHash` links a row to its transaction, but a transaction can contain several events. The response does not establish that every on-chain event has a corresponding row or that the API's history is immutable.

### Query a page

```bash
curl --fail --get 'https://api.frankencoin.com/analytics/transactionLog/json' \
  --data-urlencode 'firstItem=false' \
  --data-urlencode 'limit=50'
```

Read rows from `logs` and pagination metadata from `pageInfo`; `num` counts rows in this response, not the entire history. The default page size is 50. Set `firstItem=false` for newest first, or `firstItem=true` to start with the oldest available records.

To request the next page, keep the same ordering and pass the previous response's full `pageInfo.endCursor` as `after`. Treat the cursor as opaque: URL-encode it, and do not shorten it or replace it with a row's `count`.

```bash
# Set END_CURSOR to the full pageInfo.endCursor from the previous response.
curl --fail --get 'https://api.frankencoin.com/analytics/transactionLog/json' \
  --data-urlencode 'firstItem=false' \
  --data-urlencode 'limit=50' \
  --data-urlencode "after=${END_CURSOR}"
```

Continue while `pageInfo.hasNextPage` is `true`. If a page lacks a next cursor, repeats a cursor or adds no new rows, stop and report an incomplete traversal rather than looping. Deduplicate overlapping rows without assuming that `txHash` alone identifies a row.

## Financial fields

| Transaction-log field         | Meaning and unit                                                                                      |
| ----------------------------- | ----------------------------------------------------------------------------------------------------- |
| `chainId`, `txHash`           | Chain and originating transaction                                                                     |
| `count`                       | Indexed record identifier, not an amount or a pagination cursor                                       |
| `timestamp`                   | Unix seconds                                                                                          |
| `kind`, `amount`              | Recorded financial event and its raw amount with 18 decimals; interpret the asset with the event kind |
| `totalInflow`, `totalOutflow` | Running financial inflow and outflow totals, raw ZCHF with 18 decimals                                |
| `totalEquity`, `totalSavings` | Point-in-time equity and savings totals, raw ZCHF with 18 decimals                                    |
| `fpsTotalSupply`              | Underlying FPS supply, raw FPS with 18 decimals                                                       |
| `fpsPrice`                    | ZCHF per FPS, scaled by `1e18`                                                                        |
| `realizedNetEarnings`         | Realised net earnings, raw ZCHF with 18 decimals                                                      |
| `earningsPerFPS`              | Earnings in ZCHF per FPS, scaled by `1e18`                                                            |

Financial quantities in transaction-log JSON are decimal strings. The [shared formatting helper](/api-docs#shared-validation-and-exact-display-formatting) displays non-negative raw quantities without losing fractional base units. Preserve the sign separately when formatting a signed net-earnings value.

The transaction schema does not include total ZCHF supply, V1/V2 minting totals or interest rates. Use [ecosystem info](/api-docs/ecosystem), [positions](/api-docs/positions) and [savings](/api-docs/savings) for those current data sources. Current values cannot fill gaps in a historical series.

## Daily snapshots

For a chart of equity, inflows, outflows or underlying FPS metrics, fetch `GET /analytics/dailyLog/json`. Each row supplies a `date`, a Unix-second `timestamp` and the available financial totals, not every transaction field. Financial fields use the same raw units as transaction-log JSON. Daily snapshots avoid processing each transaction when the chart needs only daily observations.

Daily routes expose no pagination parameters. The service loads at most 1000 snapshots in ascending timestamp order. Fetch the available series and select the required dates locally; do not rely on a `limit` query to trim it. Preserve missing dates as gaps, not zero balances, and check the last available date before using the series for a current chart.

## Earnings and exposure

Use `GET /analytics/profitLossLog` for recent recorded changes in profit and loss. Rows identify the chain, minter, `created` time in Unix seconds and `kind`, alongside `amount`, cumulative `profits` and `losses`, and `perFPS`. Amounts and totals are raw ZCHF strings with 18 decimals; `perFPS` is ZCHF per FPS scaled by `1e18`.

Use `GET /analytics/fps/earnings` to break down sources such as `minterProposalFees`, `investFees`, `redeemFees` and `positionProposalFees`, alongside costs such as `savingsInterestCosts` and `otherLossClaims`.

Earnings categories are already scaled ZCHF numbers. Do not divide them by `1e18`. The service derives some categories from other totals, so this breakdown is not an itemised receipt ledger. It describes the underlying pool's earnings, not payments to individual FCS holders.

Use `GET /analytics/fps/exposure` to pair general FPS metrics with per-collateral exposures. This supports a collateral-concentration view alongside the [collateral catalogue](/api-docs/ecosystem#collateral-catalogue). Monetary values use their named ZCHF units and are already scaled; `mint.interestAverage` and ratios are fractions, while `positions` fields are counts.

## Export completeness

The transaction CSV endpoint exports a page, not the full history: its default is also 50 rows. Apply `firstItem`, `limit` and `after` as for JSON. `pageInfo=true` appends pagination JSON to the CSV response, so a plain CSV parser must not consume that response unchanged. JSON is simpler for cursor-driven ingestion; use CSV for a selected page or assemble an export after collecting the pages.

Reaching `hasNextPage=false` completes the indexer's available traversal, not reconciliation with the chain. For accounting that needs complete history, reconcile records and totals against a separately validated indexer or block-range contract logs, including receipts and chain reorganisations. The profit/loss endpoint's 1000-record limit is separate from transaction-log pagination.


# Wallet Integration

This guide connects a wallet's ZCHF balance and savings view to contract actions. Use the API for indexed account activity and cross-module summaries, contract reads for current balances and pending interest, and wallet transactions for deposits, withdrawals and interest collection.

The [savings API](/api-docs/savings) defines HTTP schemas, units and limits; the [common helpers](/api-docs#executable-examples) preserve integer quantities.

## Chain, token and module selection

Configure each integration with an EIP-155 chain ID, ZCHF token address, savings module address and the ABI for that deployment. Addresses can coincide across chains without identifying the same contract state. The API's `status[chainId][moduleAddress]` lookup is explicit: mainnet has more than one reported module.

ZCHF uses 18 decimals and ERC-20 transfers, balances and allowances. Amounts passed to contracts are integer base units. Check the token address and spender for the selected chain rather than taking them from an arbitrary API record. Use the [contract repository](https://github.com/Frankencoin-ZCHF/FrankenCoin) and a pinned version of the [published SDK](https://www.npmjs.com/package/@frankencoin/zchf) to resolve deployment interfaces.

Read `balanceOf(account)` for the wallet's liquid ZCHF balance and `allowance(owner, spender)` before an operation that pulls tokens. Keep liquid ZCHF separate from funds credited to a savings account. Ordinary token transfers use the ERC-20 `transfer` or `transferFrom` interface; the [reference-transfer API](/api-docs/transfers) covers only transfers carrying reference messages.

## Savings contract interface

The methods below describe the referral-capable `AbstractSavings` interface at the [linked source revision](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/savings/AbstractSavings.sol). Older `SavingsV2` contracts do not have the same account/referral interface. Resolve the ABI and conditions for the selected module rather than assuming every indexed module implements these methods.

| Method or getter                                   | Classification                 | Purpose                                                               |
| -------------------------------------------------- | ------------------------------ | --------------------------------------------------------------------- |
| `savings(account)`                                 | Read-only                      | Saved balance, ticks and, where supported, referrer and referral fee  |
| `accruedInterest(account)`                         | Read-only                      | Gross pending interest at current state                               |
| `accruedInterest(account, timestamp)`              | Read-only                      | Gross interest at the supplied timestamp under contract tick rules    |
| `currentRatePPM`, `currentTicks`, `INTEREST_DELAY` | Read-only                      | Rate, tick counter and entry-delay parameter                          |
| `refreshBalance(owner)`, `refreshMyBalance()`      | **Transaction**                | Collect interest, pay any referral fee and update saved balance/ticks |
| `save(amount)`, `save(owner, amount)`              | **Transaction**                | Deposit ZCHF under the module's conditions                            |
| `withdraw(target, amount)`                         | **Transaction**                | Withdraw under the module's conditions                                |
| `adjust(targetAmount)`                             | **Transaction**                | Adjust savings balance                                                |
| Referral overloads and `dropReferrer()`            | **Transaction**, ABI-dependent | Set or remove referral terms                                          |

An `eth_call` simulation of a mutating method does not persist its result. It does not turn `refreshBalance` into a view method. Deposits that pull ZCHF require the appropriate allowance to the selected savings module.

### Deposit ZCHF

1. Select the wallet account, chain and savings module. Read its rate, entry-delay rules and any referral terms for display before confirmation.
2. Convert the entered ZCHF amount to 18-decimal integer base units. Read the wallet balance and allowance for that module.
3. If an allowance is needed, submit `approve(spender, amount)` to the ZCHF token with the selected module as spender. Wait for its successful receipt before the deposit.
4. Submit the supported `save(amount)` call, or `save(owner, amount)` to credit another account. Where referral overloads are supported, show the referrer and fee before requesting the signature.
5. After the deposit receipt succeeds, re-read the savings account. Indexed activity may appear later; approval alone does not deposit funds.

### Collect interest or withdraw

Use `refreshMyBalance()` or `refreshBalance(owner)` to collect interest into the saved balance, with any referral fee paid under the account's terms. This is a transaction, not a balance refresh performed by the API. After its receipt, re-read credited balance and pending interest to avoid displaying the same interest twice.

For a withdrawal, read the account and module conditions, then submit the supported `withdraw(target, amount)` with a validated recipient and base-unit amount. Use the receipt and resulting balances to report the amount transferred; the requested amount is not proof of the amount received. `adjust(targetAmount)` instead sets a target savings balance, depositing or withdrawing as needed, and may require allowance for a deposit.

## Displaying savings

Keep these values separate:

* **Saved balance**: the contract's current credited principal, including previously collected net interest.
* **Gross pending interest**: `accruedInterest(account)` before any referral deduction.
* **Net pending interest**: gross minus the applicable integer-rounded referral fee.
* **Cumulative collected interest**: indexed historical `interest`, not additional pending funds.

Read account terms and accrued interest at a consistent block. `saved + grossPending` is not the user's net balance when a referral fee applies. In this referral-capable contract the fee is at most 250,000 PPM (25% of gross interest), with no deduction for a zero referrer. Account referral terms and the selected version determine the actual deduction.

The entry delay, weighted tick adjustment and withdrawal conditions depend on the module version. Do not infer a universal lock period from the HTTP balance or a rate field. See [savings mechanics](/savings) and [source](https://github.com/Frankencoin-ZCHF/FrankenCoin/blob/8b4c4ab67bb361b91d58c474b87f4608fc4c0566/contracts/savings/AbstractSavings.sol).

## Indexed activity and other integrations

`/savings/core/activity/:account` returns at most 1000 recent records. Several events can share a transaction hash, so the hash alone is not a unique event key. A complete history requires an independent event traversal and reconciliation; a capped response cannot establish it.

For reference-bearing payments, use the [candidate-only transfer example](/api-docs/transfers#candidate-lookup-example). For the canonical share token FCS, distinguish [reference prices, previews and transaction limits](/api-docs/fcs#contract-reads-and-transactions). Use FCS-specific supply and contract references; the underlying FPS fields are not substitutes.

The [API package](https://www.npmjs.com/package/@frankencoin/api) can provide types for a pinned release. TypeScript types do not validate runtime JSON; retain HTTP, schema and unit checks.


