Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

awrichar
Copy link
Contributor

This defines a basic confidential UTXO token interface that could potentially be implemented by both Noto and Zeto (and any other similar tokens in the future).

This defines a basic confidential UTXO token interface that could potentially be
implemented by both Noto and Zeto (and any other similar tokens in the future).

Signed-off-by: Andrew Richardson <[email protected]>
* Emits a {Transfer} event.
*/
function transfer(
bytes32 txId,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would like to better understand the need for the txId, which as I understand it is used here as a form of idempotency key, rather than replay attacks (replay attacks are protected against by checking the unspent status of the inputs and the uniqueness of the outputs).

There are some discussions in #676, which I'll summarize here. Essentially the idempotency key is to prevent a client like Paladin from re-submitting a new transaction to the contract in an attempt to fulfill a request on the private API ("please transfer 100 of my private UTXOs to Andrew"), in the event that the client lost track of the original submission. Without an idempotency key, duplicate transactions may end up getting submitted by a client if the client believes the original transaction has failed to reach the blockchain.

The argument against using an idempotency key based on onchain states is that:

  • it's costly because all txIds get saved onchain
  • idempotency keys are for preventing "honest mistakes" made by the client, rather than "malicious attacks". we need to ensure it's impossible to achieve on the client side, before resorting to onchain enforcement

bytes32 lockId,
bytes32[] calldata lockedInputs,
bytes32[] calldata lockedOutputs,
bytes32[] calldata outputs,
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems these 3 parameters can be consolidated under a LockStates object similar to the lock() function

* @param id the UTXO identifier
* @return unspent true or false depending on whether the identifier is in the unspent map
*/
function isUnspent(bytes32 id) external view returns (bool unspent);
Copy link
Contributor

Choose a reason for hiding this comment

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

When using nullifiers, the result should be "unknown" by design. there's another status "does not exist". so maybe instead of a simple bool, this should be an enum with 4 values: SPENT, UNSPENT, UNKNOWN, NONEXISTENT:

  • SPENT/UNSPENT for all existing UTXOs in non-nullifier tokens
  • UNKNOWN for all existing UTXOs in nullifier tokens
  • NONEXISTENT for all non-existing UTXOs in any token implementation

);

/**
* @dev Spend UTXOs and create new UTXOs.
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we're going a bit far leaning all the way to UTXO terminology here, rather than just "states".

When you consider FHE solutions use account states that mutated, it's a bit hard conceptually to think of this as a "UTXO model at the base EVM layer" ... when it's definitely not a UTXO model at the higher levels.

Copy link
Contributor

@jimthematrix jimthematrix Sep 26, 2025

Choose a reason for hiding this comment

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

I suspect we may have to accept that this proposal is limited to "UTXO based" token designs, rather than accommodating both UTXO and account based designs like those using FHE tends to be (e.g Zama confidential tokens, which are essentially equivalent to an ERC20 interface, that spell only the accounts and the delta, rather than inputs and outputs).

As an example, a typical FHE based implementation for a transfer():

function transfer(address to, bytes calldata encryptedAmount)

*/
function transfer(
bytes32 txId,
bytes32[] calldata inputs,
Copy link
Contributor

Choose a reason for hiding this comment

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

What about "read" and "info" states that are needed for other data not mutated.

Few examples:

  • Allow-lists for ZKPs ("read")
  • Account states for private EVM execution that are not modified ("read")
  • Transaction input payload reference ("info")
  • Transaction manifest for establishing subsets of states visible to each party ("info")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants