Skip to content

Conversation

zie1ony
Copy link
Contributor

@zie1ony zie1ony commented May 14, 2025

rendered

Following update is the conclusion of all the conversations about token standards.
This is a more detailed and cleaned up version of the CEP-18 standard.

/// the tokens on behalf of the caller.
/// The `inc_by` is the amount by which the allowance should be increased.
fn increase_allowance(&mut self, spender: Key, inc_by: U256);
}

Choose a reason for hiding this comment

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

Hi,

I would add mint and burn even if they are behind a configuration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most of the times during the development having mint and burn just made things unnecessarily complicated, and is not useful at all, as burning and minting logic is different from project to project. I'm strongly against having them in the standard.

Having it in casper-ecosystem's implementation with modalities is still good idea as it serves as a standalone token with basic governance. But these doesn't have to be part of the standard.

/// Spender does not have enough allowance approved.
InsufficientAllowance = 60002,
/// The user cannot target themselves.
CannotTargetSelfUser = 60003,

Choose a reason for hiding this comment

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

Right now this first errors in current implmentation are

pub enum Cep18Error {
    InvalidContext = 60000,
    InsufficientBalance = 60001,
    InsufficientAllowance = 60002,
    Overflow = 60003,
    PackageHashMissing = 60004,
etc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The errors I propose are implementation-agnostic and comes from the logic of the token mechanics. Things like InvalidContext are framework-releated and shouldn't be here. In the casper-ecosystem/cep18 implementation, you have errors that doesn't happen in Odra's implementation and vice versa.


The CEP-18 token implementations are available under the following links:
- https://github.com/casper-ecosystem/cep18
- https://github.com/odradev/odra/blob/HEAD/modules/src/cep18_token.rs

Choose a reason for hiding this comment

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

It seems to me strange to establish an interface using Casper Key type and having an implementation that differs by implementing the wrapper Address like

pub fn transfer(&mut self, recipient: &Address, amount: &U256) {

It's a small difference but it kind of defeats the intention of this document somehow.

cc @darthsiroftardis

Copy link
Contributor Author

@zie1ony zie1ony Aug 12, 2025

Choose a reason for hiding this comment

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

Odra's Address serializes and deserializes to Key, but only supports Key's variants that makes sense. You can think of it as a implementation detail.

From Odra's codebase

impl CLTyped for Address {
    fn cl_type() -> CLType {
        CLType::Key
    }
}

impl ToBytes for Address {
    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
        Key::from(*self).to_bytes()
    }

    fn serialized_length(&self) -> usize {
        Key::from(*self).serialized_length()
    }
}

impl FromBytes for Address {
    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
        let (key, remainder) = Key::from_bytes(bytes)?;

        let address = match key {
            Key::Account(account_hash) => Address::Account(account_hash),
            Key::Hash(raw_contract_package_hash) => {
                Address::Contract(ContractPackageHash::new(raw_contract_package_hash))
            }
            _ => return Err(bytesrepr::Error::Formatting)
        };

        Ok((address, remainder))
    }
}

While casper-ecosystem implementation actually allows for all kinds of Key types, but it still uses only AccountHash and PackageHash (and EntityAddr, but this most likely will never be turned on, more here: casper-network/casper-node#4511). Other kinds of Keys simply doesn't make any sense to be recipient of tokens, as they would be unrecoverable.

Given all the above, I think Key as address is technically the right choice for the standard, from the lack of better choice.

Copy link
Contributor Author

@zie1ony zie1ony left a comment

Choose a reason for hiding this comment

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

@gRoussac thanks for reviewing

/// the tokens on behalf of the caller.
/// The `inc_by` is the amount by which the allowance should be increased.
fn increase_allowance(&mut self, spender: Key, inc_by: U256);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most of the times during the development having mint and burn just made things unnecessarily complicated, and is not useful at all, as burning and minting logic is different from project to project. I'm strongly against having them in the standard.

Having it in casper-ecosystem's implementation with modalities is still good idea as it serves as a standalone token with basic governance. But these doesn't have to be part of the standard.

/// Spender does not have enough allowance approved.
InsufficientAllowance = 60002,
/// The user cannot target themselves.
CannotTargetSelfUser = 60003,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The errors I propose are implementation-agnostic and comes from the logic of the token mechanics. Things like InvalidContext are framework-releated and shouldn't be here. In the casper-ecosystem/cep18 implementation, you have errors that doesn't happen in Odra's implementation and vice versa.


The CEP-18 token implementations are available under the following links:
- https://github.com/casper-ecosystem/cep18
- https://github.com/odradev/odra/blob/HEAD/modules/src/cep18_token.rs
Copy link
Contributor Author

@zie1ony zie1ony Aug 12, 2025

Choose a reason for hiding this comment

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

Odra's Address serializes and deserializes to Key, but only supports Key's variants that makes sense. You can think of it as a implementation detail.

From Odra's codebase

impl CLTyped for Address {
    fn cl_type() -> CLType {
        CLType::Key
    }
}

impl ToBytes for Address {
    fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
        Key::from(*self).to_bytes()
    }

    fn serialized_length(&self) -> usize {
        Key::from(*self).serialized_length()
    }
}

impl FromBytes for Address {
    fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
        let (key, remainder) = Key::from_bytes(bytes)?;

        let address = match key {
            Key::Account(account_hash) => Address::Account(account_hash),
            Key::Hash(raw_contract_package_hash) => {
                Address::Contract(ContractPackageHash::new(raw_contract_package_hash))
            }
            _ => return Err(bytesrepr::Error::Formatting)
        };

        Ok((address, remainder))
    }
}

While casper-ecosystem implementation actually allows for all kinds of Key types, but it still uses only AccountHash and PackageHash (and EntityAddr, but this most likely will never be turned on, more here: casper-network/casper-node#4511). Other kinds of Keys simply doesn't make any sense to be recipient of tokens, as they would be unrecoverable.

Given all the above, I think Key as address is technically the right choice for the standard, from the lack of better choice.

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.

2 participants