Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIP-0113? | Programmable tokens #444

Open
wants to merge 48 commits into
base: master
Choose a base branch
from

Conversation

michele-nuzzi
Copy link

@michele-nuzzi michele-nuzzi commented Jan 20, 2023

This CIP proposes a solution to CPS-3? (Smart Tokens) implementable already with V2 Plutus contracts.

If adopted as a standard it would allow to emulate the behavior of account-based ledgers tokens on Cardano.


(rendered proposal in branch)

@michele-nuzzi
Copy link
Author

Please note that at this moment this is only a draft and may contain errors in the logic.

If you spot any or have suggestions on how to improve the logic contributions are more than welcome!

@matiwinnetou
Copy link
Contributor

matiwinnetou commented Jan 20, 2023

Question:
Would wallets have adhere to this standard, if no how would they know that they should show said ERC-20 alike asset in their wallets without it? If yes how could they do this?

@nielstron
Copy link
Contributor

@matiwinnetou How it works on EVM compatible chains is that the wallet stores locally a list of common tokens / users can manually add token addresses to track. Try installing Metamask and interacting with milkomeda.muesliswap.com

@michele-nuzzi
Copy link
Author

@matiwinnetou the solution proposed emulates the way tokens are used on EVM chains in many aspects

As @nielstron said the user of a wallet needs to manually add the contract to track

Wallets can find the user balance by the NFT associated with the payment credentials.

@matiwinnetou
Copy link
Contributor

I see, thanks, this helps a lot. So I guess it would be of course also possible to have some registry for those tokens so wallets could use it.

@rphair rphair changed the title meta-assets (ERC20-like assets) initial draft CIP-???? | ERC-20assets Jan 20, 2023
@rphair rphair changed the title CIP-???? | ERC-20assets CIP-???? | ERC20-like assets Jan 20, 2023
@rphair
Copy link
Collaborator

rphair commented Jan 20, 2023

thanks @michele-nuzzi - I just edited title to be specific & put the Draft status into the review stage itself; feel free to change back when you feel the drafting process is complete: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request

Also I'm adding a link to the readable proposal into the original comment & please keep updated if it changes path 🙏

@rphair rphair marked this pull request as draft January 20, 2023 13:34
Copy link
Contributor

@L-as L-as left a comment

Choose a reason for hiding this comment

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

As I've noted elsewhere, a system as described in this CIP is inherently centralised (but trustless) and thus can't support the throughput it would on account-model based ledgers.

A much simpler system which likely serves your needs is a system where the owner is noted in the token name of a token. Let's assume it's a PKH for simplicity, but in the future we want scripts + datums as owners too.
You then only own a token if you 1) actually hold it and 2) the token name includes your PKH. The token name can also contain extra information through hashing the PHK along with token-specific information. If necessary, a UTXO can be created to ensure the preimage is stored on Cardano itself. Then sending a token to someone would entail burning then minting it again. In this process, arbitrary checks can be done through the minting policy.
Implement royalties is trivial, and would be done by including the author name in the token name too.
Implementing freezing for USD stablecoins would include checking a centralised Merkle tree of frozen addresses and checking that yours is not included.

As for supporting scripts, I think just allowing a script + datum to be used as owner might be enough.

Also, unrelated to the idea itself, I would appreciate it if the CIP was better checked for grammatical/formatting errors/typos, etc.

@L-as
Copy link
Contributor

L-as commented Jan 21, 2023

@michaelpj might be interested

@michele-nuzzi
Copy link
Author

@L-as Thank you for helping with the CIP.

I like a lot the system you propose but from a first read I believe is missing an equivalent for the approve and transferFrom methods since the spending of a token always requires the owner to include them in a transaction.

Regarding throughput, it is only limited to the phase of the creation of a new account; while I understand it is a bottleneck it is an operation performed only once and it should not cause too many problems. Once an account is created it remembers its own state and multiple accounts can be used in parallel.

I recognize my writing is not the best and I'll try to put more effort into it.

@L-as
Copy link
Contributor

L-as commented Jan 21, 2023

@michele-nuzzi That is somewhat true, however, you could implement it by freezing the old tokens (ref inputs) and minting new ones, but this might not be any better than what you describe. How common is transferFrom and approve? The issue is a token that supports freezing would have transfer transactions that are more complex than the ones in this CIP AFAICT.

There is also the issue of complexity, the scheme described in this CIP is not simple, and also requires more effort from wallet developers to show the information.
A complex scheme also has a higher chance of bugs.

I'm not sure which scheme is better when taking that into account.

@michele-nuzzi
Copy link
Author

michele-nuzzi commented Jan 21, 2023

From what I can tell transferFrom is the preferred way for a smart contract to move those tokens.

It is true that for this first draft, it might be tricky to implement something like freezing; I was thinking to add a second field to the Account datum so that it can have additional arbitrary data:

const AccountManagerDatum = pstruct({
    Account: {
        amount: int,
        state: data
    },
    /* ... */
});

and then make sure that the state field is unchanged for all the standard redeemers (easily done with equalsData)

the account manager can have other redeemers after the standard one so that maybe a Freeze redeemer can be added and this will be able to freeze a given account by modifying the state field.

Regarding wallet implementation, it shouldn't be any harder than the current resolution of ADAHandles.

@MicroProofs
Copy link
Contributor

@L-as Thank you for helping with the CIP.

I like a lot the system you propose but from a first read I believe is missing an equivalent for the approve and transferFrom methods since the spending of a token always requires the owner to include them in a transaction.

Regarding throughput, it is only limited to the phase of the creation of a new account; while I understand it is a bottleneck it is an operation performed only once and it should not cause too many problems. Once an account is created it remembers its own state and multiple accounts can be used in parallel.

I recognize my writing is not the best and I'll try to put more effort into it.

I would say to avoid the ERC-20 approve function at all costs. It has plagued the Ethereum eco-system and even they are standardizing a new key signature method called permit to try to correct this terrible mistake. I'd recommend instead signatories be checked for transferFrom methods instead of the far outdated approve. I have not had the time to fully go through this. But I just want to warn against the mistake of implementing approve.

@michele-nuzzi
Copy link
Author

@MicroProofs If that's the case then there is no difference from the Transfer redeemer. Should we just drop Approve and TransferFrom and contracts will instead look only for Transfer redeemers to be used?

@MicroProofs
Copy link
Contributor

MicroProofs commented Jan 21, 2023

@michele-nuzzi
Yes since the whole idea of approve is you had no idea who is signing the transaction for a contract calling to an ERC-20 token contract. In Cardano we have easy access to that information. We should simplify it to just transfer. And I’ll review your draft tonight to give more feedback.

@L-as
Copy link
Contributor

L-as commented Jan 22, 2023

With those two removed is there any reason not to go for the token approach?

@michele-nuzzi
Copy link
Author

@L-as There would still be the problem of additional (trusted) data for script execution.

Implementing freezing for USD stablecoins would include checking a centralised Merkle tree of frozen addresses and checking that yours is not included

As you said implementing logic like freezing requires some external contract; this external contract would not be a standard, so someone else that wants to move around ERC20-like tokens would (and should) not know of this additional requirement

@L-as
Copy link
Contributor

L-as commented Jan 22, 2023

@michele-nuzzi It would always be the case that transferring tokens can fail. It should not be part of the interface in the first place. For wallets to support it seemlessly, you could have a helper untrusted script associated with the MP that tells you what you need to add to fix it. That would have to be the case even with your system to support arbitrary behaviour and keep the interface simple.

@michaelpj
Copy link
Contributor

I don't particularly have an opinion here and I'm not currently planning to review this. If this is a thing that people want then they can create it. I think it may not perform well (as @L-as says), and it sacrifices essentially all of the benefits of native tokens on Cardano.

A few thoughts:

  • No Rationale :(
  • Is this actually a CIP? It seems like a design for something that someone could build on Cardano. I'll leave it to the Editors.
  • I would strongly encourage some prototyping. Does it actually work? Is an implementation too expensive? Can you build other systems that actually interact with this?

@rphair
Copy link
Collaborator

rphair commented Jan 23, 2023

@michaelpj: Is this actually a CIP? It seems like a design for something that someone could build on Cardano. I'll leave it to the Editors.

After putting it in Draft stage I've been watching the feedback expecting that it will produce a Rationale and other CIP prerequisites to be added before @michele-nuzzi brings it into the "Ready for review" stage. I'm not recommending anything specifically yet in case a broader approach suggests presentation as a CPS instead. The discussion has been productive so far even without a mature proposal yet.

@michele-nuzzi
Copy link
Author

@michaelpj me and @L-as have had a very productive discussion

regarding performances, the only bottle neck is the Merkle tree which is there to guarantee the uniqueness of an account. This constraint is not really useful, there is no harm in having multiple accounts with the same credentials so it will be removed before the final CIP.

The implementation will only require the account manager.

@KtorZ KtorZ added the Category: Tokens Proposals belonging to the 'Tokens' category. label Mar 18, 2023
@rphair rphair added the State: Waiting for Author Proposal showing lack of documented progress by authors. label Apr 4, 2023
@rphair
Copy link
Collaborator

rphair commented Apr 4, 2023

Added Waiting for Author tag by consensus in today's CIP meeting while waiting for last feedback to be accommodated in the draft.

@michele-nuzzi
Copy link
Author

@rphair just preparing the ground; will try to have the spec ready for review at most in 3 weeks worse case scenario

@rphair
Copy link
Collaborator

rphair commented Aug 29, 2024

@michele-nuzzi I'm happy to see Mermaid come in here & I'll keep an eye on Mermaid support on derived web sites that carry CIPs:

p.s. if your last commits make your PR ready for review before Tuesday please post here so we can include it in our CIP meeting agenda again (p.p.s. that would also take it out of Waiting for Author)

@michele-nuzzi
Copy link
Author

michele-nuzzi commented Aug 29, 2024

no @rphair, not yet ready, will tag you when is good

@michele-nuzzi
Copy link
Author

@rphair this should give a good description of the CIP. I will wait for some feedback before proceeding.

Copy link
Collaborator

@rphair rphair left a comment

Choose a reason for hiding this comment

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

thanks @michele-nuzzi, looks like the narrative behind your spec changes is in 50e6215 - but will you also be able to come to the next CIP meeting (Tuesday) to discuss your progress & publicly review it again after your updates? We can also try to agree then upon what it will take to merge this.

If you can be here, let us now, or we'll reschedule the community review for a future meeting: https://hackmd.io/@cip-editors/97

@rphair rphair added State: Confirmed Candiate with CIP number (new PR) or update under review. and removed State: Waiting for Author Proposal showing lack of documented progress by authors. labels Sep 15, 2024
@michele-nuzzi
Copy link
Author

@rphair I would love to, does it overlap with the plutus working group call?

@rphair
Copy link
Collaborator

rphair commented Sep 15, 2024

@michele-nuzzi I don't know; I doubt it since Plutus experts like @MicroProofs have attended the CIP meeting often. The CIP meeting date & time are as posted above and the meeting lasts 1 hour.

Co-authored-by: Ryan <44342099+Ryun1@users.noreply.github.com>
@michele-nuzzi michele-nuzzi changed the title CIP-0113? | Programmable token-like assets CIP-0113? | Programmable tokens Sep 17, 2024
@michele-nuzzi
Copy link
Author

I have changed the title because since the last changes the standard uses actual CNTs and not only numbers in datums.

The previous title was confusing other community members.

Copy link
Collaborator

@rphair rphair left a comment

Choose a reason for hiding this comment

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

My impression from community review in the CIP meeting today is this continues to show feasibility for community adoption, with generality as well as a broad range of specific applications.

Comment on lines +529 to +532
<!-- The rationale fleshes out the specification by describing what motivated the design and what led to particular design decisions. It SHOULD describe alternate designs considered and related work. The rationale SHOULD provide evidence of consensus within the community and discuss significant objections or concerns raised during the discussion.

It MUST also explain how the proposal affects the backward compatibility of existing solutions when applicable. If the proposal responds to a CPS, the 'Rationale' section SHOULD explain how it addresses the CPS, and answer any questions that the CPS poses for potential solutions.
-->
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
<!-- The rationale fleshes out the specification by describing what motivated the design and what led to particular design decisions. It SHOULD describe alternate designs considered and related work. The rationale SHOULD provide evidence of consensus within the community and discuss significant objections or concerns raised during the discussion.
It MUST also explain how the proposal affects the backward compatibility of existing solutions when applicable. If the proposal responds to a CPS, the 'Rationale' section SHOULD explain how it addresses the CPS, and answer any questions that the CPS poses for potential solutions.
-->

(artefact from template comment scaffold)

## Path to Active

### Acceptance Criteria
<!-- Describes what are the acceptance criteria whereby a proposal becomes 'Active' -->
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
<!-- Describes what are the acceptance criteria whereby a proposal becomes 'Active' -->

(artefact from template comment scaffold)

- independent transaction creation with `Transfer` redeemers

### Implementation Plan
<!-- A plan to meet those criteria. Or `N/A` if NOT applicable. -->
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
<!-- A plan to meet those criteria. Or `N/A` if NOT applicable. -->

Copy link

@fallen-icarus fallen-icarus left a comment

Choose a reason for hiding this comment

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

I appreciate you clarifying some things for me. Here are some thoughts based on my (hopefully) correct understanding of this CIP.

This CIP uses the term "state" a lot in both variable names and the prose, but I do not think this is the proper use of the term "state". I believe this contributed to my initial confusion. According to wikipedia:

In information technology and computer science, a system is described as stateful if it is designed to remember preceding events or user interactions; the remembered information is called the state of the system.
-- emphasis mine

AFAIU this CIP is not remembering preceding events, and therefore, there is no state.

Comment on lines +166 to +168
### High level idea

The core idea of the implementation is to emulate the ERC20 standard; where tokens are entries in a map with addresses (or credentials in our case) as key and integers (the balances) as value. ([see the OpenZeppelin implementation for reference](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/9b3710465583284b8c4c5d2245749246bb2e0094/contracts/token/ERC20/ERC20.sol#L16));

Choose a reason for hiding this comment

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

If this section is no longer accurate, I would appreciate it if you could update it. It was a huge contributor to my confusion over this CIP.

>
> this would allow for a more powerful model than the account based equivalent but implies higher execution costs
>
> with the goal of keeping the standard simple we allow only a single sender

Choose a reason for hiding this comment

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

Is this restriction still present or is this also outdated?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, only a single sender allowed so far, but open to discussions

Comment on lines +191 to +193
1) a `stateManager` contract
- with minting policy used to proof the validity of account's state utxos
- spending validator defining the rules to update the states

Choose a reason for hiding this comment

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

What about changing the name of the stateManager to controlManager. IMO, the state manager isn't managing "state" (ie, when assets are transferred it isn't updating anything). AFAIU it is just controlling whether the account has the proper permissions to spend the UTxOs. Calling it stateManager also contributed to my confusion.

I would also argue these aren't "state utxos", but rather "control utxos" because they control what the account can do with its utxos. The spending validator then defines the rules to update the controls.

2) a `transferManager` parametrized with the `stateManager` hash
- having minting policy for the tokens to be locked in the spending validator
- spending validator for the validation of single utxos (often just forwarding to the withdraw 0)
- certificate validator to allow registering the stake credentials (always succeed MAY be used) and specify the rules for de-registration (always fail MAY be used, but some logic based on the user state is RECOMMENDED)

Choose a reason for hiding this comment

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

De-registering the tansferManager seems like a good way to freeze all utxos. This feature may not always be desired, but perhaps it is worth mentioning. DApps can decide for themselves if they want this feature.

Also, why would the user state be part of the logic for de-registering? I thought de-registering impacts the whole DApp since it would disable the withdraw 0 observer.

Copy link
Author

Choose a reason for hiding this comment

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

Yes you are right, de-registering should not be based on the user state

const Account = pstruct({
Account: {
credentials: PCredential.type,
state: data
Copy link

@fallen-icarus fallen-icarus Sep 18, 2024

Choose a reason for hiding this comment

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

Again, I'd argue this isn't "state" but rather the controls/permissions for this account. There is no "remembering" occurring here.

Comment on lines +500 to +504
The `Transfer` transaction allows the transfer of programmable tokens from one account to another within the `transferManager` contract. This transaction involves the following components:

- **transferManagerObserver**: The contract that validates the inputs and ensures the correctness of the transfer.
- **stateManagerContract**: The contract that manages the state of the accounts involved in the transfer.
- **transferManagerContract**: The contract that handles the actual transfer of tokens.

Choose a reason for hiding this comment

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

I don't think the stateManagerContract should be included in this list because it doesn't seem like it is actually involved. Its utxos are only referenced which means the contract is not executed. It being included here is why I initially thought the state manager utxos were normal inputs instead of reference inputs.


The validation is performed in the minting policy.

The minting policy MUST succeed if only one asset is created under the policy respecting the naming convention explained below, and MAY succeed if other assets are created as long as their name is NOT of length 32.

Choose a reason for hiding this comment

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

Why does the length matter?

Copy link
Author

Choose a reason for hiding this comment

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

to avoid potential conflict with the resulting hash

very remote but better avoid

should not be a very useful feature anyway


Main differences were in the proposed:
- [use of sorted merkle trees to prove uniqueness](https://github.com/cardano-foundation/CIPs/pull/444/commits/525ce39a89bde1ddb62e126e347828e3bf0feb58#diff-370b6563a47be474523d4f4dbfdf120c567c3c0135752afb61dc16c9a2de8d74R72) of an account during creation;
- account credentials as asset name

Choose a reason for hiding this comment

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

Why was the account credential abandoned for the name? If the account credential was used, users could trivially check their account statuses since they know the token name to lookup. The current naming scheme makes it very difficult for users to lookup their account statuses.

Copy link
Author

Choose a reason for hiding this comment

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

using the credentials for the name could not guarantee the unique association of an account to a set of utxos

a user could create as many accounts with the same name, with different states (or control data, or config, whatever you like) which could represent a security vulnerability from the perspective of the spending logic

Copy link

@fallen-icarus fallen-icarus Sep 19, 2024

Choose a reason for hiding this comment

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

Currently, the transferManager is parameterized by the stateManager. I haven't fully thought this through, but what if it wasn't and instead the stateManager hash was in the transferManager datum? It could be something like:

  1. accountManager - a (new) minting policy where the token name is the user's credential. This enables the DApp entity to see all registered credentials. This accountManager does not control any state; it just enables using the list of associated tokens as a list of registered users.
  2. stateManager - the current stateManager except it is parameterized by the accountManager hash and the user's credential. So all users get their own stateManager minting policy for the programmable token. The DApp entity can see the credential in the registered list (tokens for accountManager) and can then lookup all associated stateManager tokens for that credential. Minting new state tokens requires referencing the account token for that credential.
  3. transferManager - the current transferManager except it is not parameterized by anything. Instead, the stateManager hash is also included in the transferManager datum. The accountManager hash could also go in the datum. This contract does not mint the programmable tokens. Instead, they programmable tokens will be deposited into it.

This approach could make the transferManager general purpose so each user only needs one address for all programmable tokens. This approach also makes it easy for users to check their statuses since all tokens minted with a given stateManager (parameterized with their credential) belongs to them. In other words, they can find all of their state UTxOs using just their credential. Thoughts?

EDIT: The accountManager may not be necessary since you likely already know the credential you want to freeze.

Copy link
Author

Choose a reason for hiding this comment

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

This approach could make the transferManager general purpose so each user only needs one address for all programmable tokens

I find it hard to believe that many tokens will adhere to the same spending rules

Choose a reason for hiding this comment

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

I find it hard to believe that many tokens will adhere to the same spending rules

I don't think they will, but the differences could possibly be encoded into the state UTxOs. Currently, the lack of generality is a deal breaker IMO. Do you expect users to have a separate address for every stablecoin, every NFT collection, every goverment issued DID, etc? Do you expect frontends to actually add support for possibly an infinite number of addresses? I don't. You might get a few addresses to be supported, but that means the frontends effectively act as gatekeepers for which tokens can be programmable since unsupported tokens effectively don't exist. Perhaps there can't be a universal transferManager, but perhaps 2 or 3 different transferManagers can cover most use cases.

- with minting policy used to proof the validity of account's state utxos
- spending validator defining the rules to update the states
2) a `transferManager` parametrized with the `stateManager` hash
- having minting policy for the tokens to be locked in the spending validator

Choose a reason for hiding this comment

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

Can this be made more general? Having the transferManager also mint the tokens means users will have a separate address for every programmable token. This can quickly become a nightmare for frontends to integrate. It would be better if there was a single transferManager that could be used with all programmable tokens.

Programmable tokens don't necessarily need to be minted by the transferManager. Instead, they can be deposited into the transferManager by the entity minting the token. For example, a user selling NFTs can ensure that all NFTs sold go to a transferManager address.

There may also be use cases where a token should be allowed to leave the transferManager. For example, lets say there is an art NFT that has been collecting royalties for a while and now the royalty owners would like to release the NFT from the royalty requirement (there may be legal/tax benefits to this in the future). This approach doesn't seem to have a "release" mechanism.

A "release" mechanism could also be useful for upgrading the transferManager. If a new version comes out, users can possibly transfer their assets from transferManager v1 to transferManager v2 (depending on the control utxos). The current design seems to prevent upgrades like this. If this CIP actually does support upgrades, it definitely needs to mention how upgrades are possible. Upgrades don't seem to be mentioned anywhere.

Comment on lines +446 to +448
Only **one (1)** utxo from the `transferManager` is allowed to be spent in a burn transaction.

If a user desires to burn a different amount than the one currently present they should break the operation in 2 different transactions:

Choose a reason for hiding this comment

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

Why only one utxo per transaction? This seems unnecessarily restrictive. Why can't multiple utxos be spent? The entire balance from each utxo spent can be burned. The observer can tell which utxos are being used for burning based on the redeemers used to spend them.

@michele-nuzzi
Copy link
Author

@fallen-icarus

AFAIU this CIP is not remembering preceding events, and therefore, there is no state

the standard does not impose real rules of what the "state" is used for

from the "Transfer" tx perspective it acts kinda like the "Read" monad, aka. a configuration.

This configuration could be changed (by the user or some other party according to the specific implementation)

so it "could" act as a state

The same way the datum attached to an utxo does not necessarily remember previous stuff, but it could and often does, that is why it is used as state.

I believe using state as name makes it easier to build an idea around the prupose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Category: Tokens Proposals belonging to the 'Tokens' category. State: Confirmed Candiate with CIP number (new PR) or update under review.
Projects
None yet
Development

Successfully merging this pull request may close these issues.