# Introducing the Fungible Token Contract

**URL:** <https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915>\
**Category:** Community\
**Created:** [July 22, 2025, 11:47am UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915 "2025-07-22T11:47:55Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![ymekuria](https://avatars.discourse-cdn.com/v4/letter/y/13edae/32.png) [@ymekuria](https://forums.minaprotocol.com/u/ymekuria)\
**Post date:** [July 22, 2025, 11:47am UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/1 "2025-07-22T11:47:55Z")

</div>

## Intro

We’re excited to introduce a new [Fungible Token Contract](https://github.com/o1-labs-XT/fungible-token-contract), currently released in beta. This new configurable contract simplifies token deployments, reduces the number of account updates needed for common actions, and lays the groundwork for powerful customizations while preserving properties like a static verification key, which is required for cross-application compatibility.

## Motivation

The original token standard relied on both a FungibleToken and a FungibleTokenAdmin contract. This added complexity, complicated deployments, increased account updates, and introduced trust assumptions around the admin.

The new design addresses these issues by:

- Using a single contract with built-in configurability

- Preserving a static verification key for auditability and composability

- Supporting flexible logic via sideloaded proofs

This makes it easier to reason about token behavior while enabling more advanced use cases like gated minting and ZK-powered airdrops.

## Key Features

#### Single Contract Design

Combines token and admin functionality, eliminating the need for an external admin contract. This reduces one account update per method (e.g. mint) and simplifies deployment.

#### Sideloaded Proof Support

Advanced conditions ([e.g. “only mint if the user owns 1,000 MINA”](https://github.com/o1-labs-XT/fungible-token-contract/blob/main/src/examples/side-loaded/run.ts)) can be enforced using proofs that live outside the token contract. These do not alter the token’s verification key and support privacy-preserving logic.

#### Static Verification Key

All logic lives in a single, auditable contract.

#### Design of the current Fungible Token Standard (mint)

### ![](https://canada1.discourse-cdn.com/flex035/uploads/minaprotocol/original/2X/c/c0b5f4a5a4a9903908a194b12f6e4016c0b1701e.jpeg)

Account Updates for mint (7 total):

1. feePayor
2. fundNewAccount - only if a tokenID account does not exist
3. FungibleToken.mint() zkApp method
4. FungibleTokenAdmin.canMint() zkApp method
5. Admin Signature
6. Token ID balance change
7. Token ID balance change

#### Design of new Fungible Token Contract (mint)

 ![image](https://canada1.discourse-cdn.com/flex035/uploads/minaprotocol/original/2X/6/6fd1e108ffc95b686a6a7bf2600a8e2165b0460a.png)

Account Updates for mint (6 total):

1. feePayer
2. fundNewAccount - only if a tokenID account does not exist
3. FungibleToken.mint() zkApp method
4. Admin Signature
5. Token ID balance change (mint)
6. Token ID balance change (mint)

## Conclusion

The new Mina Fungible Token Contract is a cleaner, more composable foundation for token-based apps. It preserves auditability with a static verification key, reduces deployment overhead, and enables advanced features like privacy-preserving mint conditions with a single contract.

We invite developers to contribute and collaborate with us as we build on this foundation and evolve the contract together in the future. If you’re building zkApps that involve tokens, we encourage you to try it and let us know what you need next.

---

<div class="post-metadata">

**Author:** ![ied](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.minaprotocol.com/ied/32/2172_2.png) [@ied](https://forums.minaprotocol.com/u/ied)\
**Post date:** [July 27, 2025, 9:28pm UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/2 "2025-07-27T21:28:19Z")

</div>

> Here we go again.

Good to see development on token side.

---

<div class="post-metadata">

**Author:** ![zk\_kirol](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.minaprotocol.com/zk_kirol/32/2046_2.png) [@zk\_kirol](https://forums.minaprotocol.com/u/zk_kirol)\
**Post date:** [July 28, 2025, 4:01pm UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/3 "2025-07-28T16:01:07Z")

</div>

Let me start by saying how much I appreciate the work on the new token contract standard! I believe it’s one of the most important improvements to the current devX on Mina.  
The ability to provide conditional logic in each of the core methods unlocks a lot of functionality.

I wanted to ask about the permission model for side‑loaded verification keys. I believe functionality similar to regular smart‑contract verification‑key upgradability would be very useful. Let me give an example:

Let’s assume there is conditional logic requiring a side‑loaded proof to trigger `mintWithProof`. As part of contract initialization, `updateSideLoadedVKeyHash` was called with `vkMap` and `OperationKeys.Mint`, so to mint, a proof with vk.A is required.

Going forward, if the required side‑loaded VK is upgraded (vk.A → vk.B), the admin currently has full unrestricted control to change that key.

- How could one guard an upgrade of the `SideLoadedVKey`—for example, via multisig, or another config, or logic like `Permissions.VerificationKey.proofDuringCurrentVersion()`—to give a more “permissionless” flavor?
- Would it be possible to make the `SideLoadedVKey` immutable?

On a separate note, I’m not sure I follow the API decision to throw on `transfer` and use `transferCustom`. `transferWithProof` would fit nicely with `mintWithProof` and `burnWithProof`—why not `transfer` and `transferWithProof`?

---

<div class="post-metadata">

**Author:** ![MartinOndejka](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.minaprotocol.com/martinondejka/32/2461_2.png) [@MartinOndejka](https://forums.minaprotocol.com/u/MartinOndejka)\
**Post date:** [July 28, 2025, 5:46pm UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/4 "2025-07-28T17:46:50Z")

</div>

I like the new design choices, however I think there are few technical details that should be figured out.

1. Why use multiple ZkPrograms with one method, instead of one ZkProgram with multiple methods? The `PublicOutputs` struct could hold the enum for each operation, and the token contract methods would constrain the correct operation. I find this API design simpler and easier to use, but it’s a personal preference.
2. Are [these account updates](https://github.com/o1-labs-XT/fungible-token-contract/blob/9d33cc35821ffa279baad3b693f6ec80f974c964/src/examples/side-loaded/program.eg.ts#L31-L57) actually constrained? I don’t know about the o1js magic behind the scenes, but from a quick glance it looks like the `minaAccountUpdate.publicKey == publicInput.address` is not actually enforced. Not only that, but I don’t even see them being added as children anywhere. I guess audit should find that, but before counting account updates, it should be known that there might be some missing.
3. I don’t like the `PublicOutputs` struct design.
  - Why are there balances and nonces, when there are already the whole account updates which contain preconditions?
  - AFAIK the public inputs/outputs are fairly expensive and the account update has ~160 fields. This doesn’t show up now, because they are probably not even constrained, however if all the fields are part of the public output (I might be wrong on this, maybe o1js hashes the account update already), it should be changed. Ideally it should include a field of forest hash, which allows you to do **any** account updates, not just the mina and token account update. The auxiliary output is there for this exact usecase, where you output only the hash and to be able to use it outside, you also return the whole structure as aux.
  - so my expectation would be something like

```auto
publicOutput: { forestHash: Field, method: OperationEnum }
auxiliaryOutput: CallForest

```

---

<div class="post-metadata">

**Author:** ![Jays](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.minaprotocol.com/jays/32/2445_2.png) [@Jays](https://forums.minaprotocol.com/u/Jays)\
**Post date:** [July 31, 2025, 8:17am UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/5 "2025-07-31T08:17:31Z")

</div>

**Great proposal!**

I really like the direction of this new Fungible Token Contract.  
✅ The single-contract design feels cleaner and reduces overhead.  
✅ Keeping a static verification key while adding sideloaded proof support strikes a strong balance between auditability and flexibility.  
✅ This clearly lays the groundwork for more advanced zkApp use cases (like gated minting or private airdrops) without adding trust assumptions.

That said, I do have a few questions and points of curiosity:

1. **Migration path:**

2. **Developer accessibility:**

3. **User experience:**

4. **Adoption roadmap:**

Overall, this feels like an important step toward making token interactions on Mina more composable and privacy-ready.  
Excited to see how this evolves and would love to hear thoughts on the questions above!

---

<div class="post-metadata">

**Author:** ![0x471](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.minaprotocol.com/0x471/32/2097_2.png) [@0x471](https://forums.minaprotocol.com/u/0x471)\
**Post date:** [August 11, 2025, 11:42am UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/6 "2025-08-11T11:42:20Z")

</div>

Thanks for the feedback Karol! I agree that the ability to upgrade side-loaded verification keys in a more controlled way would be very useful. With the current standard, updating the side-loaded VK hash requires a transaction authorized by a permissioned signer (typically the owner/admin). Because of this, it’s not currently possible to use solutions that require authorization with proofs.  
If we had native support for threshold signature schemes like FROST or similar primitives, we could move toward a more permissionless approach without requiring any change but in the meantime, we could consider introducing a permission model for `updateSideLoadedVKeyHash`, similar to what’s already done for side-loaded proof configs.

Here’s an idea: we can have a state variable like `vKeyUpdatePermission`, with three possible modes:

1. **Admin (0)** – requires an admin signature (default)
2. **Proof (1)** – requires a valid zkApp proof (e.g., for multisig logic)
3. **Impossible (2)** – disallows updates, making the VK immutable

Below is an **example** of such logic:

```auto
/**
 * Determines if the side-loaded VKey can be updated based on permission configuration.
 * 0: Admin signature required
 * 1: Proof required (for multisig or other zk-based authorization)
 * 2: Impossible (immutable)
 */
private async canChangeSideLoadedVKey(
  _vKey: VerificationKey,
  _operationKey: Field
): Promise<Bool> {
  const permission = this.vKeyUpdatePermission.getAndRequireEquals();

  const isAdminPermission = permission.equals(UInt8.from(0));
  const adminAuthorized = await this.ensureAdminSignature(isAdminPermission);

  const isProofPermission = permission.equals(UInt8.from(1));
  const currentAccountUpdate = AccountUpdate.create(this.address);
  const hasProofAuthorization = currentAccountUpdate.authorization.isProved;

  const isImpossible = permission.equals(UInt8.from(2));

  return Provable.switch(
    [isAdminPermission, isProofPermission, isImpossible],
    Bool,
    [Bool(true), hasProofAuthorization, Bool(false)]
  );
}

```

Regarding API consistency, the fungible token contract extends the native token contract, which defines core methods (e.g., `approve`, `transfer)` that cannot be modified to accept additional parameters like those required for side-loaded proofs. To work around this limitation, we use the `custom` keyword to define alternative methods (e.g., `transferCustom`) that support the required functionality. The original `transfer` and `approve` methods are intentionally overridden to throw errors, explicitly guiding developers to use their corresponding `custom` versions instead.

---

<div class="post-metadata">

**Author:** ![0x471](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.minaprotocol.com/0x471/32/2097_2.png) [@0x471](https://forums.minaprotocol.com/u/0x471)\
**Post date:** [August 11, 2025, 11:46am UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/7 "2025-08-11T11:46:26Z")

</div>

1. The `ZkProgram` you mention is in the examples folder and serves as a template for developers to follow. It doesn’t matter whether it includes one or multiple methods as long as the structure of the public outputs remains the same. That’s the important part because it’s what allows a side-loaded `ZkProgram` to be correctly bound to the fungible token contract.  
That said, in my personal opinion using a separate `ZkProgram` for each operation provides a few practical advantages for token contracts. It helps isolate custom logic per operation which makes maintenance and upgrades easier. It can also help avoid potential issues related to WASM memory usage when combining complex custom logic under a single circuit. I also think including multiple methods means a proof could correspond to any one of those methods, which may make it more difficult for the admin to review and approve. It introduces ambiguity about what exactly is being proven.

2. Could you please point out where you saw that public key constraint? I don’t think we have any code like that in the codebase.The account updates in the `ZkProgram` follow a two-stage verification approach. Let me explain with an example:  
`"If account has balance X, then allow mint"`

3. We include balances and nonces in `PublicOutputs` to support broader use cases where this data might be needed in downstream zkApps. if it’s not needed, these fields can hold dummy values. this doesn’t affect dynamic proof compatibility or the FTS verification key, so custom assertions in the ZkProgram won’t break verification as long as the public output format stays consistent. The `PublicOutputs` struct should be inclusive by design in order to bind the ZkProgram to the FTC. If it were to be minimized, it would be a considerable compromise on what can be proved with side-loaded proofs.

Thanks for the feedback Martin!

---

<div class="post-metadata">

**Author:** ![0x471](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.minaprotocol.com/0x471/32/2097_2.png) [@0x471](https://forums.minaprotocol.com/u/0x471)\
**Post date:** [August 11, 2025, 11:57am UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/8 "2025-08-11T11:57:34Z")

</div>

1. We’re already in close contact with several teams and actively discussing these topics to ensure smooth adoption and integration.

2. We have examples of side-loaded operations in the repo here:  
[fungible-token-contract/src/examples/side-loaded at main · o1-labs-XT/fungible-token-contract · GitHub](https://github.com/o1-labs-XT/fungible-token-contract/tree/main/src/examples/side-loaded)  
There’s also an example available on the new docs page:  
[Sideloaded Verification Keys | o1Labs Documentation](https://docs.o1labs.org/o1js/advanced-concepts/sideloaded-vks)

3. Measurable impacts like those you mentioned are expected with the protocol updates planned for the Fall 2025 hard fork.

4. For a broader set of examples, you can check out the repo here:  
[fungible-token-contract/src/examples at main · o1-labs-XT/fungible-token-contract · GitHub](https://github.com/o1-labs-XT/fungible-token-contract/tree/main/src/examples)

Thanks for the feedback Jays!

---

<div class="post-metadata">

**Author:** ![MartinOndejka](https://yyz1.discourse-cdn.com/flex035/user_avatar/forums.minaprotocol.com/martinondejka/32/2461_2.png) [@MartinOndejka](https://forums.minaprotocol.com/u/MartinOndejka)\
**Post date:** [August 11, 2025, 10:22pm UTC](https://forums.minaprotocol.com/t/introducing-the-fungible-token-contract/6915/9 "2025-08-11T22:22:48Z")

</div>

1. I think this would need the input from more people since it’s more about the personal preference and what people find easier. That said, isolation is I think pretty much the same, maintenance and upgrades I think are harder, hence the whole vkey map merkle tree here [fungible-token-contract/src/FungibleTokenContract.ts at 9d33cc35821ffa279baad3b693f6ec80f974c964 · o1-labs-XT/fungible-token-contract · GitHub](https://github.com/o1-labs-XT/fungible-token-contract/blob/9d33cc35821ffa279baad3b693f6ec80f974c964/src/FungibleTokenContract.ts#L206)

2. 

> Could you please point out where you saw that public key constraint?

I didn’t and I think it’s an issue. ~~The proof’s statement is public input with some address, and public output are account updates that should correspond to the same address. I don’t think that’s enforced anywhere, but I might be wrong.~~  
EDIT: I see now what you are doing there, those account updates are there just to fetch the account data using o1js. This isn’t the ideal design, you don’t have to add the whole account updates structure, just to witness two fields. My suggestion in third point would solve that.

> It’s also important to keep in mind that preconditions only apply to attached account updates.

Yes, they are not attached. Your test is just not sufficient to actually exploit the contract, you have to engineer the o1js a little bit. Try applying following diff and run the test again, it doesn’t fail where it should [gist:de32ba9787d3d38e36d2c91f7e9c6dd6 · GitHub](https://gist.github.com/MartinOndejka/de32ba9787d3d38e36d2c91f7e9c6dd6) . Notice that those changes didn’t change the vkey of the contract.

3. 

> We include balances and nonces in `PublicOutputs` to support broader use cases where this data might be needed in downstream zkApps.

Having just the balance and nonce limits the use cases heavily. Let’s say you want to prove something about the state of some third party contract. You can’t. Having a forest hash is as general as it can be. For this use case, you could create a forest with the account update of a third party contract, and later attach it in to the transaction in the FTC method.
