# Core Improvement Proposals: Complete Corpus > Machine-readable copy of the Core Improvement Proposal registry. This file is generated from the repository sources. Do not edit it manually. --- # CIP Editors Source: https://cip.coreblockchain.net/docs/cip-editors/ Repository path: docs/cip-editors.md CIP editors are members of the [GitHub team — CIP](https://github.com/orgs/core-coin/teams/cip). ## CIP Editor Responsibilities *For each new CIP submission, an editor undertakes the following tasks:* - Review the CIP to ensure it is sound and comprehensive. The ideas presented must be technically coherent, even if they might not seem likely to reach final status. - Ensure the title accurately describes the content. - Review the CIP for language aspects (spelling, grammar, sentence structure, etc.), markup (using GitHub-flavored Markdown), and code style. Should the CIP fall short of being ready, the editor will return it to the author with specific revision instructions. *Once the CIP meets the standards for the repository, the CIP editor will:* - Assign a CIP number (typically the PR number or, if the author prefers, the Issue # if there had been a discussion in the Issues section of this repository about this CIP). - Merge the associated pull request. - Notify the CIP author about the next steps. Many CIPs are crafted and overseen by developers who have write access to the Core codebase. The CIP editors monitor CIP changes, making corrections to structure, grammar, spelling, or markup as necessary. The role of the editors is not to judge the merits of CIPs. They handle the administrative and editorial aspects only. --- # CIP Rationale Source: https://cip.coreblockchain.net/docs/cip-rationale/ Repository path: docs/cip-rationale.md CIPs are intended to serve as the primary mechanisms for proposing new features, gathering community technical feedback on an issue, and documenting the design decisions made within Core. Since CIPs are maintained as text files in a versioned repository, their revision history serves as the historical record of a feature's evolution. For those implementing Core, CIPs offer a streamlined way to monitor the progress of their work. Ideally, each implementation maintainer would provide a list of the CIPs they have implemented. This approach would give end users an easy reference to understand the current status of a specific implementation or library. --- # CIP Tags Source: https://cip.coreblockchain.net/docs/cip-tags/ Repository path: docs/cip-tags.md > We have six tags for CIPs. ## Core Improvements that require a consensus fork, as well as those that aren't necessarily consensus-critical but might be relevant to "core dev" discussions. ## Networking This includes improvements related to devp2p and Light Core Subprotocol, as well as proposed enhancements to the network protocol specifications of Whisper and Swarm. ## Interface This category involves improvements around client API/RPC specifications and standards. It also includes certain language-level standards, such as method names and contract ABIs. The label "interface" aligns with the [interfaces repo], and discussions should primarily take place in that repository before a CIP is submitted to the CIPs repository. ## CBC Application-level standards and conventions, including contract standards like token standards, name registries, URI schemes, library/package formats, and wallet formats. ## Informational This describes a Core design issue or provides general guidelines or information to the Core community. It doesn't propose a new feature. Informational CIPs don't necessarily represent the consensus of the Core community or a recommendation. Thus, users and implementers can either follow or ignore Informational CIPs. ## Meta This describes a process related to Core or suggests a change to (or an event in) a process. Process CIPs resemble Standards Track CIPs but pertain to areas other than the Core protocol. They might propose an implementation, but not to the Core's codebase. Often, they require community consensus. Unlike Informational CIPs, they are more directive, and users typically can't ignore them. Examples include procedures, guidelines, changes to the decision-making process, and alterations to the tools or environment used in Core development. All meta-CIPs are also considered Process CIPs. --- > It's strongly advised that a single CIP should encompass just one primary proposal or idea. The more concise a CIP is, the more likely it is to succeed. A modification to one client doesn't require a CIP. However, a change that impacts multiple clients or sets a standard for various apps does. > A CIP must meet certain essential criteria. It should clearly and comprehensively describe the proposed enhancement. This enhancement should represent a net benefit. If there's a suggested implementation, it needs to be robust and must not unduly complicate the protocol. --- --- # CIP Workflow Source: https://cip.coreblockchain.net/docs/cip-workflow/ Repository path: docs/cip-workflow.md **Key Stakeholders**: - **You**, the champion or *CIP author* - **CIP editors** [(details)](/docs/cip-editors) - **Core Developers** Before diving in, validate your idea with the Core community. It's efficient to determine the originality and broad applicability of your concept before investing time. Relying solely on online searches might not provide comprehensive insights. Ensure the proposal isn't limited in its benefits to just you, the author. An idea may seem brilliant individually but may not serve the wider Core community. As a champion, your tasks include: - Drafting the CIP in the prescribed format - Steering discussions in relevant forums - Building community consensus The typical journey of a successful CIP is: > [ WIP ] -> [ DRAFT ] -> [ LAST CALL ] -> [ ACCEPTED ] -> [ FINAL ] Every stage transition requires the CIP author's request and subsequent approval by the CIP editors. Use a pull request to modify the status and add a link guiding further discussions. ## Status Definitions - **Active**: Some Informational and Process CIPs remain active indefinitely. - **Work in progress (WIP)**: After gauging community support, the champion drafts a CIP via a pull request, which may also include an initial implementation to facilitate review. - ✔️ **Draft**: A CIP editor, upon agreement, assigns a CIP number (typically derived from related issue or PR) and merges your pull request. - ❌ **Draft**: Grounds for refusal include lack of focus, excessive scope, redundant effort, technical fallacies, insufficient motivation, compatibility issues, or deviation from Core's ethos. - **Draft**: After the initial merge, iterate on your draft via subsequent pull requests until it matures for the next stage. A draft, especially if it impacts consensus, should ideally have a working implementation. - ✔️ **Last Call**: CIP editors, if in agreement, mark the status and set a 14-day review window. - ❌ **Last Call**: A request might be turned down if substantial edits to the draft are anticipated. - **Last Call**: Prominent listing of the CIP on [Core CIP Website](http://cip.coreblockchain.net). - ❌: Significant alterations or unresolved technical concerns revert the CIP to draft. - ✔️ **Accepted**: Applicable only to Core CIPs. A smooth Last Call leads to acceptance. - ✔️ **Final**: For non-core CIPs, a smooth Last Call culminates in a Final status. - **Accepted**: Pertains solely to Core CIPs. The decision to incorporate it in client builds through a hard fork isn't under CIP purview. - ✔️ **Final**: A Core CIP has to be implemented across a minimum of three viable Core clients for "Final" consideration. Upon wide community adoption, it's designated "Final". - **Final**: Reflects the current pinnacle. A "Final" CIP gets updated strictly for error corrections. ## Other Unique Statuses - **Deferred**: For core CIPs postponed for an upcoming hard fork. - **Rejected**: Either a fundamentally flawed CIP or a Core CIP declined by Core Devs. - **Active**: Similar to Final, but can undergo updates without changing the CIP number. - **Superseded**: A once-final CIP no longer represents the zenith. Another "Final" CIP references the outdated one. ## Pillars of a Successful CIP Each CIP should include: - **Preamble**: RFC 822 headers detailing CIP metadata. - **Simple Summary**: An easy-to-understand synopsis. - **Abstract**: A concise (~200 words) technical summary. - **Motivation**: (Optional but crucial for protocol changes) Why the current specs fall short. - **Specification**: Detailed syntax and semantics for the feature ensuring interoperability. - **Rationale**: Delve into the design motivation, alternatives, related work, and community consensus. - **Backwards Compatibility**: Describe any backward incompatibilities and proposed resolutions. - **Test Cases**: Mandatory for consensus-impacting CIPs. - **Implementations**: Complete prior to achieving "Final" status. - **Copyright Waiver**: All CIPs must reside in the public domain. ## CIP Formats and Templates Draft CIPs using [markdown](https://guides.github.com/features/mastering-markdown/). Images should reside in the CIP-specific subdirectory, `/static/cip`. ## CIP Header Preamble Mandatory and optional headers should follow the RFC 822 style. ### Author Header Represents author details in various permissible formats. Supported formats: - `First Last `: URL, ENS, IPFS can be a personal website or blog. - `First Last `: Email address. - `First Last (@github)`: GitHub username. - `First Last (@nickname@pod.tld)`: Nickname and pod (e.g., @coretalk.space). - `First Last` `[cb00…@coreid]` or ``: CoreID from CorePass. ## Auxiliary Files Supporting files, diagrams, etc., should follow a structured naming convention. ## Transferring CIP Ownership Occasionally, ownership might need a transfer. Ideally, retain original authors as co-authors. Valid reasons include the original author's inaccessibility or disinterest. Disagreement isn't a valid reason. Consensus is pursued, but alternative CIPs are always an option. Express your interest in a takeover by addressing both the original author and the CIP editor. Absent timely feedback from the original author, the CIP editor decides. ## Copyright Rights relinquished via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # CIP-0 example Source: https://cip.coreblockchain.net/docs/cip-0/ Repository path: docs/cip-0.md **Frontmatter:** ```yaml --- cip: 0 title: CIP-0 example description: CIP-0 example template keywords: [cip, cip-0, example, template] author: Author (@author@server.pod) lang: en-US status: [draft] tags: [example] date: 2022-01-01 discussions-to: requires: # Only required when you reference a CIP in the `Specification` section. Otherwise, remove this field. --- ``` > This is the suggested template for new CIPs. Note that a CIP number will be assigned by an editor. When opening a pull request to submit your CIP, please use an abbreviated title in the file name, `cip-ID.md`, where "ID" is the CIP ID. The title should be 44 characters or fewer. > Provide a simplified and layperson-accessible explanation of the CIP. ## Abstract > A short (~200 words) description of the technical issue being addressed. ## Motivation > The motivation is critical for CIPs that aim to change the Core protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the CIP solves. CIP submissions without sufficient motivation may be rejected outright. ## Specification > The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow for competing, interoperable implementations on any of the current Core platforms. ## Rationale > The rationale elaborates on the specification by describing the motivations behind the design and the reasons for particular design decisions. It should discuss alternative designs that were considered and related work. The rationale may also provide evidence of consensus within the community and should address important objections or concerns raised during discussions. ## Backwards Compatibility > All CIPs introducing backward incompatibilities must include a section detailing these incompatibilities and their severity. The CIP must explain how the author proposes to address these incompatibilities. CIP submissions without a comprehensive backward compatibility analysis may be rejected outright. ## Test Cases > Test cases for implementation are mandatory for CIPs affecting consensus changes. Other CIPs can opt to include links to relevant test cases if applicable. ## Implementation > Implementations must be finalized before any CIP is given "Final" status, but they don't need to be completed before the CIP is accepted. While achieving consensus on the specification and rationale before initiating coding is beneficial, the principle of "rough consensus and running code" can be instrumental in resolving many API details discussions. ## Security Considerations > All CIPs must contain a section discussing the security implications and considerations relevant to the proposed change. This section should provide details important for security discussions, highlight potential risks, and be referenced throughout the lifecycle of the proposal. CIP submissions lacking the "Security Considerations" section will be rejected. A CIP cannot proceed to "Final" status without a Security Considerations discussion deemed satisfactory by the reviewers. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Core Improvement Proposals Source: https://cip.coreblockchain.net/docs/cip/ Repository path: docs/cip.md > The Core Improvement Proposal (CIP) sets standards for the Core platform, encompassing core protocol specifications, client APIs, and contract standards. ## Contributing You can begin by opening your proposal under the [issues section of this repository](https://github.com/core-coin/cip/issues). ### Review - [What is a CIP](/docs/what-is-cip) - [CIP rationale](/docs/cip-rationale) - [CIP tags](/docs/cip-tags) - [CIP workflow](/docs/cip-workflow) - [CIP editors](/docs/cip-editors) - [CIP example](/docs/cip-0) ### How to start Begin by contributing to the [issues section of this repository](https://github.com/core-coin/cip/issues/new/choose). Once done, you'll be able to draft a CIP using the associated issue number (if available). **Next steps:** 1. Review the template [cip-0](/docs/cip-0.md) 2. [Create a CIP in the Online Editor](https://github.com/core-coin/cip/new/master?filename=cip/cip-0.md&message=CIP%20Proposal&description=CIP%20Proposal%20Draft&value=---%0Acip%3A%20%0Atitle%3A%20%0Adescription%3A%20%0Akeywords%3A%20%5B%5D%0Aauthor%3A%20%0Alang%3A%20en-US%0Astatus%3A%20%5Bdraft%5D%0Atags%3A%20%5B%5D%0Adate%3A%202022-12-01%0Adiscussions-to%3A%20%0A---%0A%3C%21--Introduction--%3E%0A%0A%3C%21--truncate--%3E%0A%0A%23%23%20Abstract%0A%0A%0A%0A%23%23%20Motivation%0A%0A%0A%0A%23%23%20Specification%0A%0A%0A%0A%23%23%20Rationale%0A%0A%0A%0A%23%23%20Backwards%20Compatibility%0A%0A%0A%0A%23%23%20Test%20Cases%0A%0A%0A%0A%23%23%20Implementation%0A%0A%0A%0A%23%23%20Security%20Considerations%0A%0A%0A%0A%23%23%20Copyright%0A%0ACopyright%20and%20related%20rights%20waived%20via%20%5BCC0%5D%28https%3A%2F%2Fcreativecommons.org%2Fpublicdomain%2Fzero%2F1.0%2F%29.%0A) Or 1. [Fork](https://github.com/core-coin/cip/fork) this repository. 2. Modify the template [cip-0](https://github.com/core-coin/cip/blob/master/cip/docs/cip-0.md) and move it to the [`cip` folder](https://github.com/core-coin/cip/blob/master/cip). 3. [Submit a Pull Request](https://github.com/core-coin/cip/compare) to the Core CIP repository. For auxiliary content, place it in the [CIP image directory](https://github.com/core-coin/cip/blob/master/static/cip) `/static/cip/cip-x`, where 'x' is the CIP number. Link images using the path `/cip/cip-x/cip-x-1.png`. ## Tags - `Draft`: Consideration phase for a CIP. - `Accepted`: A CIP set for immediate adoption, often slated for the next hard fork (relevant for Core/Consensus layer CIPs). - `Final`: A CIP adopted in a prior hard fork (pertinent to Core/Consensus layer CIPs). - `Deferred`: A CIP not up for immediate adoption, but might be reconsidered for future hard forks. ## Categories CIPs are categorized into various types, each with its own list: - **Core**: Improvements involving a consensus fork or changes significant to core development discussions. - **Networking**: Enhancements concerning network protocol specs. - **Interface**: Focuses on client API/RPC specs, language standards like method names, and contract ABIs. Discussions should mainly take place in the interfaces repo before submitting a CIP here. - **CBC**: Application standards and conventions like token standards and name registries. - **Informational**: Addresses Core design issues or offers guidelines to the Core community without suggesting new features. - **Meta**: Outlines processes around Core or proposes process changes. These are more binding than informational CIPs and often require community consensus. ## Channels - [Discussions](https://github.com/core-coin/cip/discussions) - [GH Issues](https://github.com/core-coin/cip/issues) --- # What is a CIP Source: https://cip.coreblockchain.net/docs/what-is-cip/ Repository path: docs/what-is-cip.md > CIP stands for Core Improvement Proposal. A CIP is a design document that provides information to the Core community or introduces a new feature to Core, its processes, or its environment. The document should present a clear technical specification and a rationale for the proposed feature. It is the responsibility of the CIP author to build consensus within the community and to document any dissenting opinions. --- # Core Cryptography Scheme Source: https://cip.coreblockchain.net/cip/core/cip-98/ Repository path: cip/core/cip-98.md This standard details the cryptography scheme employed in the Core Blockchain. ## Motivation The goal of this standard is to circumvent the limitations of Ed448 and create an HD derivation scheme analogous to BIP32. ## Specification ### Conventions Our cryptographic approach is based on the Ed448 elliptic cryptography described in RFC 8032. Terms such as "private key", "public key", "secret scalar", "signature", etc., used in this text retain their original meanings and MUST be serialized following the guidelines of the referenced document. ### Dual Cryptography The signature schemes of Ed25519 and Ed448 differ significantly from Secp256k1. In the Ed448 scheme, rather than using a private key as a secret scalar, it applies the function SHAKE256(privateKey, 114) to achieve a 114-byte output. This output is then split into two 57-byte segments. The left segment is used as the secret scalar, and the right segment as the secret nonce. We have identified a method to calculate the public key or sign a message using only the secret scalar (left part of the output). We will capitalize on this opportunity and introduce a new scheme rooted in scalar cryptography, dubbed "Scheme1". The conventional scheme, outlined in RFC 8032, will be referenced as "Scheme0". ### Reasoning BIP32's classical HD derivation scheme hinges on the linear relationship between the private and public keys. In Ed448, this relationship is disrupted during the initial stage when the hash of the private key is computed to extract the secret scalar. To restore this dependency, we propose the implementation of a novel scheme that preserves it. ### Private Key Types The choice between signature schemes will depend on the type of private key in use. The final bit of the last byte of a private key (little-endian) will determine the key's classification. A value of 0 denotes the use of the conventional Scheme0. A value of 1 indicates the use of Scheme1. Thus, if the most significant bit of the last byte (in little-endian) is 0, the steps from RFC 8032 are carried out unchanged. ### Scheme1 If the most significant bit of the last byte (little-endian) of the private key is set to 1, Scheme1 is applied. This key is treated as a secret scalar. All the requirements defined in RFC 8032 pertaining to the secret scalar will be executed (e.g., the lowest 2 bits of the first byte are set to 0, the last byte to 0, and the penultimate byte to 1). With this scalar, one can compute the public key or sign the message, using the same string that the nonce uses. ### Overall Cryptography Scheme Given that the `privateKey` is represented by `byte[57]`, the overall scheme can be described as follows: ```go if (privateKey[56] & 0x80 == 0x00) { // Most significant bit of the last byte equals 0, process as usual: output = SHAKE256(privateKey, 114) left = output[0:57] right = output[57:114] // .... } else { // Most significant bit of the last byte equals 1: scalar = privateKey scalar[0] &= 0xfc scalar[56] = 0 scalar[55] |= 0x80 left = scalar right = scalar // then proceed as usual } ``` ## Rationale This design provides a choice between the classic Ed448 signature technique and the ability to generate HD-wallet keys similar to BIP32. Consequently, those desiring the conventional approach should ensure the last bit of their private key is set to 0. Conversely, users aiming for an HD-derivation scheme should set this bit to 1. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Core HD-Wallet Scheme Source: https://cip.coreblockchain.net/cip/core/cip-99/ Repository path: cip/core/cip-99.md This standard outlines the HD-wallet derivation scheme for Core Coin. ## Abstract This standard outlines the HD-wallet derivation scheme for Core Coin. ## Motivation Given that the classical BIP32 scheme is incompatible with Ed448, this standard presents an alternative implementation of HD-derivation. This alternative is based on the modified Core Coin cryptographic mechanism. ## Specification ### Definitions ```plaintext || : concatenation + : regular addition of two numbers +++ : addition of two points on Ed448 H(x, salt) : 57 bytes of the hash function HMAC-SHA512 with 2048 cycles of PBKDF2: PBKDF2(HMAC-SHA512, x, salt, 57, 2048) chain, privKey, publicKey : [57]byte ``` ### Extended Keys An extended key is represented by a [114]byte array. It is derived from `chaincode || privateKey` or `chaincode || publicKey`. As only Scheme1 supports HD-wallet derivation, all private keys must have the most significant bit of the last byte equal to 1. More details can be found in the "Core Coin Cryptography Scheme" CIP. ### Master Key The master key forms the root of the HD-derivation tree. It can be derived from mnemonics using a slightly modified version of the BIP39 scheme. You should follow this scheme until obtaining the seed (BIP39 Seed). The master key can then be derived as: ```go chain = H(seed, "mnemonicforthechain") key = H(seed, "mnemonicforthekey") masterKey = chain || key ``` For enhanced security, you should: ```txt set the most significant bit of the last byte to 1 (masterKey[113] |= 0x80) set the most significant bit of the next-to-last byte to 1 (masterKey[112] |= 0x80) clear the second significant bit of the next-to-last byte (masterKey[112] &= 0xbf) ``` This master key corresponds exactly to the `m` in the `m/44'/...` derivation path. ### Child Pair Generation: Chain Code You can always generate `pubKey` from `privKey`. This operation will be denoted as `private2public`. Child chaincode generation varies between usual and hardened keys. For a usual key: ```go pubKey = private2public(privKey) childChain = H(0x03 || pubKey || i , chain), where i < 2^31 ``` For a hardened key: ```go childChain = H(0x01 || privKey || i , chain), where i >= 2^31 ``` ### Child Pair Generation: Private Key to Private Key Child private key generation also differs between usual and hardened keys. For a usual key: ```go pubKey = private2public(privKey) template = H(0x02 || pubKey || i , chain), where i < 2^31 ``` For a hardened key: ```go template = H(0x00 || privKey || i , chain), where i >= 2^31 ``` Next, nullify the top 4 bytes and the last 2 bits of the template for security reasons, resulting in the `clampedTemplate = clamp(template)`. Then, the child `privKey` can be computed as: ```go childPrivKey = privKey + clampedTemplate ``` Note: There won't be an overflow since the upper bytes of the template are set to zero. EdDSA security standards require the 9th bit of `privateKey` to be 1 to defend against rho-attacks. We clamp the template because we want to add it to `privateKey` while ensuring that the 9th bit of the result remains secure. As 32 bits following the 9th bit are zero, we can perform at least \(2^{22}\) addition operations on the `privateKey`. This limits the tree's hierarchy level to approximately \(2^{20}\). ### Child Pair Generation: Private Key to Public Key Public keys are generated using the `privateKey2privateKey` method, and then deriving the public key from the private key as usual. ### Child Pair Generation: Public Key to Public Key It is impossible to generate a child public key from a hardened public key, which is the primary attribute of hardened keys: they are designed to prevent such derivation. However, usual child public keys can be derived from a usual parent public key. First, calculate the `clampedTemplate` as described above: ```go template = H(0x02 || publicKey || i , chain), where i < 2^31 template -> clampedTemplate ``` Next, compute a point on Ed448 corresponding to the `clampedTemplate` and add it to the `publicKey` using point addition for Ed448: ```go point = private2public(clampedTemplate) childPublicKey = publicKey +++ point ``` This public key aligns with the private key generated by the `private2private` method. ## Rationale This scheme offers an alternative between the classical Ed448 signature mechanism and the ability to generate HD-wallet keys in a BIP32-like manner. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Token Standard Source: https://cip.coreblockchain.net/cip/cbc/cip-20/ Repository path: cip/cbc/cip-20.md A standard interface for tokens. ## Abstract This standard allows for the implementation of a standard API for tokens within smart contracts. It provides basic functionality to transfer tokens and allows tokens to be approved so they can be spent by an on-chain third party. ## Motivation A standard interface allows any tokens on Core to be reused by other applications, from wallets to decentralized exchanges. ## Specification ### Token #### Methods **NOTES**: - The following specifications use syntax from Ylem `0.8.4` (or above). - Callers MUST handle `false` from `returns (bool success)`. They MUST NOT assume that `false` is never returned! ##### name Returns the name of the token, e.g., `"MyToken"`. OPTIONAL: This method can improve usability, but interfaces and other contracts MUST NOT expect these values to be present. ```solidity function name() external view returns (string memory); ``` ##### symbol Returns the symbol of the token, e.g., "CTN". OPTIONAL: This method can improve usability, but interfaces and other contracts MUST NOT expect these values to be present. ```solidity function symbol() external view returns (string memory); ``` ##### decimals Returns the number of decimals the token uses. For instance, `8` means dividing the token amount by `100000000` to get its user representation. OPTIONAL: This method can improve usability, but interfaces and other contracts MUST NOT expect these values to be present. ```solidity function decimals() external view returns (uint8); ``` ##### totalSupply Returns the total token supply. ```solidity function totalSupply() external view returns (uint256); ``` ##### balanceOf Returns the account balance of an account with address `_owner`. ```solidity function balanceOf(address _owner) external view returns (uint256 balance); ``` ##### transfer Transfers `_value` amount of tokens to address `_to` and MUST fire the `Transfer` event. The function SHOULD `revert` if the caller's account balance does not have enough tokens to spend. *Note:* Transfers of 0 values MUST be treated as normal transfers and fire the `Transfer` event. ```solidity function transfer(address _to, uint256 _value) external returns (bool success); ``` ##### transferFrom Transfers `_value` amount of tokens from address `_from` to address `_to`, and MUST fire the `Transfer` event. The `transferFrom` method is used for a withdrawal workflow, allowing contracts to transfer tokens on your behalf. This can, for example, allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULD `revert` unless the `_from` account has deliberately authorized the sender of the message via some mechanism. *Note:* Transfers of 0 values MUST be treated as normal transfers and fire the `Transfer` event. ```solidity function transferFrom(address _from, address _to, uint256 _value) external returns (bool success); ``` ##### approve Allows `_spender` to withdraw from your account multiple times, up to the `_value` amount. If this function is called again, it overwrites the current allowance with `_value`. **NOTE**: To prevent attack vectors like the one [described here](https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), clients SHOULD ensure they create user interfaces that set the allowance first to `0` before setting it to another value for the same spender. However, the contract itself shouldn't enforce this to maintain backward compatibility with contracts deployed earlier. ```solidity function approve(address _spender, uint256 _value) external returns (bool success); ``` ##### allowance Returns the amount that `_spender` is still allowed to withdraw from `_owner`. ```solidity function allowance(address _owner, address _spender) external view returns (uint256 remaining); ``` #### Events ##### Transfer MUST trigger when tokens are transferred, including zero-value transfers. A token contract that creates new tokens SHOULD trigger a Transfer event with the `_from` address set to `address(0)` when tokens are created. ```solidity event Transfer(address indexed _from, address indexed _to, uint256 _value); ``` ##### Approval MUST trigger on any successful call to `approve(address _spender, uint256 _value)`. ```solidity event Approval(address indexed _owner, address indexed _spender, uint256 _value); ``` ## Implementation There are already numerous CBC20-compliant tokens deployed on the Core network. Different implementations have been crafted by various teams, each with its unique trade-offs, ranging from energy efficiency to enhanced security. ## Copyright Copyright and related rights waived via CC0. --- # ICAN Source: https://cip.coreblockchain.net/cip/cbc/cip-100/ Repository path: cip/cbc/cip-100.md We are introducing a standard for regular Core account and contract addresses — ICAN — with HRP and checksum, similar to what we are all using for current financial transactions. ICAN stands for International Crypto Account Number. Only hexadecimal characters are used to minimize the size of the address in blockchain storage. ## Abstract The ICAN facilitates the communication and processing of transactions. It allows exchanging account identification details in a machine-readable form. ## Motivation Finding reliable and financially friendly blockchain addresses for the community. Easy to understand and easy to interpret, but still compatible with modern financial solutions. The checksum is applied in this structure to ensure minimum risk of mistyping the address. ICAN is based on the financial standards that people are using already to simplify the matter for them. ## Specification A regular ICAN address consists of: 1. Crypto Identifier - CID (HRP) 2. Checksum 3. BCAN - Account address (Hexadecimal) ### Crypto Identifier (CID) The Crypto Identifier (human-readable part) uniquely identifies the network. This part must contain 2 hexadecimal characters. In the 22-byte binary address, the two hexadecimal characters encode a one-byte network prefix. Currently unassigned and deleted [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Current_codes) are available. ### Checksum The checksum consists of 2 decimal digits calculated from both CID and BCAN using the ISO 7064 MOD-97-10 algorithm. In the 22-byte binary address, these digits are stored as one packed BCD byte. For example, checksum `54` is stored as `0x54`, not as the integer value 54 (`0x36`). Why did we choose modulo 97 (IBAN format)? 1. This gives us the possibility to detect ~99% of mistakes. 2. Easy to process/calculate. 3. Well known by financial institutions. Generation steps: 1. Remove spaces and convert CID and BCAN to uppercase. 2. Construct `BCAN || CID || "00"`. 3. Replace hexadecimal letters with their decimal values: `A = 10`, `B = 11`, ..., `F = 15`. 4. Interpret the result as a decimal integer and calculate its remainder modulo 97. 5. Subtract the remainder from 98. 6. If one digit is returned, prefix it with `0`. Validation steps: 1. Remove spaces and convert the complete ICAN to uppercase. 2. Move the first four characters (`CID || checksum`) to the end. 3. Replace hexadecimal letters with their decimal values. 4. Interpret the result as a decimal integer. The ICAN is valid if its remainder modulo 97 equals 1. ### BCAN BCAN (Basic Crypto Account Number) is the 20-byte raw account or contract identifier, represented by 40 hexadecimal characters. For an externally owned account, BCAN is derived by applying SHA3-256 to the 57-byte public key and taking the last 20 bytes of the hash: ```text BCAN = SHA3-256(publicKey)[12:] ``` Contract BCAN values are produced by the corresponding contract-address derivation scheme rather than from a public key. ### Network classification #### ISO 3166-1 alpha-2 codes | CID | Network | Network id | | --- | ------------------ | ---------- | | cb | mainnet | 1 | | ab | testnet (Devín) | 3 | | ce | private/enterprise | >3 | #### Tickers Tickers are composed of CID prefixed with the "X" character. They can be suffixed with assets running on the network. This is a technical recommendation. From the perspective of marketing and usability, the asset can be used without prefix and network. Example: > XCB > XABTKN > XCBCTN ### Address structure | Symbol | Explanation | | ------ | -------------------------------------------------------------------- | | n | Digits (numeric characters 0 to 9 only). | | a | Uppercase letters (alphabetic characters A-Z only). | | c | Uppercase and lowercase alphanumeric characters (A-Z, a-z, and 0-9). | | h | Hexadecimal characters (A-F, a-f, 0-9). | | e | Blank space. | | k | Check digits. | | nn! | Fixed length. | | nn | Maximum length. | #### Format ```txt hh!kk!hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh! ``` #### Binary representation A regular ICAN address occupies 22 bytes: | Byte range | Contents | | ---------- | --------------------------------------------- | | `0` | One-byte CID/network prefix | | `1` | Two decimal checksum digits encoded as BCD | | `2..21` | 20-byte BCAN | #### System address exceptions Core also uses raw 22-byte system values, including the real zero address `address(0)` and precompiled-contract addresses `address(1)` through `address(9)`. These values do not contain a valid CID and checksum and are not regular ICAN addresses. The real zero address `address(0)` is distinct from a checksummed ICAN whose BCAN is zero. The corresponding checksummed zero addresses are `CB540000000000000000000000000000000000000000` for mainnet, `AB720000000000000000000000000000000000000000` for Devín, and `CE450000000000000000000000000000000000000000` for private networks. #### Human and machine representation Human representation has all letters uppercase and each group of 4 characters is divided into spaces. The machine address is composed without any spaces. Human address example: > AB18 AB12 CD45 EF78 0369 0101 ABAB FFFF 0000 1234 5678 Machine address example: > AB18AB12CD45EF7803690101ABABFFFF000012345678 > ab18ab12cd45ef7803690101ababffff000012345678 Note: With QR codes, capital letters in Alphanumeric mode are a must. #### Conversion table | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | |----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----| | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ## Rationale We use hexadecimal characters to minimize the size of the addresses in blockchain. The first part — CID — reflects the type of the network to avoid misunderstanding of the type of address. We choose two-digit checksum because it is already in use and it is easier to interpret while delivering the address in several cases. The third part contains the 20-byte raw account or contract identifier. For an externally owned account, it is derived from the last 20 bytes of the SHA3-256 hash of the public key. ## Implementation Validators: - Golang — [go-ican](https://github.com/bchainhub/go-ican) - JavaScript — [ican.js](https://github.com/bchainhub/ican.js) - Bash — [ican-validate.sh](https://github.com/bchainhub/ican-validate.sh) - .Net — [IcanNet](https://github.com/bchainhub/IcanNet) - Elixir — [ican](https://github.com/ayrat555/ican) ## Security Considerations 40 characters (20 bytes) of blockchain address. Why 20 bytes? 1. Heuristic aimed to simplify the management of the key; that is, copying and pasting, checksums, or confirmations over the phone in large transfers. 1. Pre-empting security mechanisms. Hashing functions are broken every other decade or so, hence cutting the full public key is a great extra layer of defense. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Ownership Management Standard Source: https://cip.coreblockchain.net/cip/cbc/cip-102/ Repository path: cip/cbc/cip-102.md Standardized Ownership Management for Smart Contracts ## Abstract This proposal defines a standard interface for reading and managing ownership of smart contracts on Core Blockchain. It provides functions to transfer and renounce ownership and a standard event for observing ownership changes. ## Motivation Many smart contracts restrict administrative operations to a single owner. A standard interface allows wallets, registries, user interfaces, and other contracts to read the current owner and interact with ownership management without depending on a particular internal implementation. ## Specification ### CBC-102 Interface The following specification uses syntax from Ylem `0.8.4` (or above). ```solidity interface CBC102 { event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function owner() external view returns (address); function renounceOwnership() external; function transferOwnership(address newOwner) external; } ``` Implementations MAY declare these functions as `public` or `external`. The `owner()` function MAY be declared as `pure` or `view`. ### Ownership `owner()` MUST return the address of the current owner. If ownership has been renounced, it MUST return the real zero address, `address(0)`. This standard does not prescribe how ownership is stored or calculated. State variables such as `_owner`, constructors, initializers, and modifiers such as `onlyOwner` are implementation details and are not part of the CBC-102 interface. ### Transferring Ownership `transferOwnership(address newOwner)` MUST: - revert when called by an account other than the current owner; - revert when `newOwner` is either `address(0)` or the [CBC-100](./cip-100.md) checksummed zero address represented by `Checksum.zeroAddress()` in Core OpenZeppelin; - set `newOwner` as the current owner on success; and - emit `OwnershipTransferred(previousOwner, newOwner)` on success. The standard does not prescribe a revert reason or error type. It also does not restrict `newOwner` to an externally owned account; another smart contract MAY own a CBC-102 contract. ### Renouncing Ownership `renounceOwnership()` MUST: - revert when called by an account other than the current owner; - set the current owner to the real zero address, `address(0)`; and - emit `OwnershipTransferred(previousOwner, address(0))` on success. After renunciation, no caller can satisfy an ownership check based on `owner()`. CBC-102 itself exposes no function for restoring ownership, although an implementation MAY provide additional recovery or governance mechanisms outside this standard. ### Initial Ownership CBC-102 does not require a particular initialization mechanism or require the deployer to be the initial owner. When an initial non-zero owner is established, the implementation SHOULD emit `OwnershipTransferred(address(0), initialOwner)`. ### Interface Identification The CBC-102 interface identifier is `0x4ae1c531`, calculated as the XOR of the following Core function selectors: - `owner()` — `0xbe0e67a3` - `renounceOwnership()` — `0x5e0827e9` - `transferOwnership(address)` — `0xaae7857b` The `OwnershipTransferred` event is not included in the interface identifier. A CBC-102 implementation SHOULD implement [CBC-165](./cip-165.md) and return `true` for both `0x80ada41b` and `0x4ae1c531`. CBC-165 discovery is not a requirement for CBC-102 function and event conformance; existing contracts that implement the CBC-102 behavior without `supportsInterface` remain compatible with this standard. ## Rationale The interface follows the ownership model used by Core OpenZeppelin while keeping internal storage and access-control mechanisms outside the standard. A dedicated `renounceOwnership()` function distinguishes intentional renunciation from ordinary ownership transfer and allows `transferOwnership()` to reject both Core representations of the zero address. Keeping CBC-165 discovery recommended rather than mandatory preserves compatibility with existing Core ownership implementations while allowing new contracts to expose machine-readable interface support. ## Backward Compatibility The function and event signatures are compatible with the current Core OpenZeppelin `Ownable` implementation. Existing contracts with different ownership APIs are not automatically CBC-102 compliant and may require an adapter. This proposal introduces no Core Blockchain protocol changes. ## Security Considerations - Compromise or loss of the owner's private key can compromise or permanently disable owner-controlled functionality. - Ownership transfer is a one-step operation. Transferring to an incorrect address can permanently lose administrative control; implementations MAY provide a two-step transfer extension. - Renouncing ownership can permanently disable all functionality restricted to the owner. Applications SHOULD clearly warn users before submitting a renunciation transaction. - If the owner is another contract, the security and availability of that contract become part of the ownership trust model. - A positive CBC-165 response declares interface support but does not prove that an implementation is correct or trustworthy. ## Conclusion The CBC-102 standard provides a common interface for reading, transferring, and renouncing smart contract ownership on Core Blockchain while allowing different internal ownership implementations. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # WrapperToken - A CBC20 Token Standard Extension for Wrapping and Unwrapping Tokens Source: https://cip.coreblockchain.net/cip/cbc/cip-103/ Repository path: cip/cbc/cip-103.md WrapperToken - A CBC20 Token Standard Extension ## Abstract This proposal defines a CBC20 extension for wrapping a designated CBC20 token. Users can deposit the underlying token to mint wrapper tokens and burn wrapper tokens to withdraw the underlying token. ## Motivation The extension provides a standard way for applications to discover the underlying token and perform wrapping and unwrapping without depending on a particular storage layout or initialization mechanism. ## Specification ### CBC-103 Interface The following specification uses syntax from Ylem `0.8.4` (or above). ```solidity interface CBC103 /* is CBC20 */ { function wrappedToken() external view returns (address); function buy(uint256 amount) external payable; function sell(uint256 amount) external; } ``` ### Underlying Token `wrappedToken()` MUST return the designated underlying CBC20 token. The returned address MUST NOT be `address(0)` or the checksummed zero address, and it MUST NOT change during the lifetime of the wrapper token. This standard does not prescribe how the underlying token address is stored or initialized. ### Wrapping `buy(uint256 amount)` MUST atomically: 1. transfer exactly `amount` underlying tokens from the caller to the wrapper token contract; and 2. mint exactly `amount` wrapper tokens to the caller. The operation MUST revert without minting wrapper tokens if the underlying transfer reverts, returns `false`, or transfers less than `amount`. ### Unwrapping `sell(uint256 amount)` MUST atomically: 1. burn exactly `amount` wrapper tokens from the caller; and 2. transfer exactly `amount` underlying tokens to the caller. The operation MUST revert without burning wrapper tokens if the underlying transfer reverts, returns `false`, or transfers less than `amount`. Successful wrapping and unwrapping MUST follow the CBC20 minting and burning event requirements. ### Backing Invariant For an exact-transfer underlying token, the wrapper contract's underlying-token balance MUST be greater than or equal to the total supply of wrapper tokens. Direct transfers of underlying tokens to the wrapper contract can create excess backing but MUST NOT create additional wrapper tokens. CBC-103 does not define wrapping of native XCB. Callers MUST NOT attach native XCB to `buy()` or `sell()`. ## Rationale The one-to-one conversion model keeps accounting simple and makes the backing of the wrapper token independently verifiable. Keeping initialization and storage outside the standard allows constructor-based, initializer-based, and factory-based implementations. ## Backward Compatibility CBC-103 extends CBC20 without changing its existing functions. A CBC-103 implementation remains subject to all CBC20 requirements. ## Security Considerations - Implementations MUST handle both reverted transfers and transfers that return `false`. - Fee-on-transfer, rebasing, or otherwise non-exact underlying tokens are incompatible with the one-to-one model unless the implementation explicitly accounts for actual balance changes. - Implementations MUST preserve the backing invariant across external token calls and protect against reentrancy where the underlying token can execute callbacks or other arbitrary code. - Users should verify `wrappedToken()` before acquiring wrapper tokens. A wrapper is only as secure and liquid as its underlying token. - Native XCB sent to an implementation that does not explicitly reject or return it may become inaccessible. ## Conclusion CBC-103 provides a standard interface and one-to-one accounting model for CBC20 wrapper tokens. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # PriceFeed - Decentralized Oracle for Aggregating and Providing Asset Price Data Source: https://cip.coreblockchain.net/cip/cbc/cip-104/ Repository path: cip/cbc/cip-104.md PriceFeed, Decentralized Oracle for Asset Price Data ## Abstract This CIP introduces the "PriceFeed" contract, designed to act as a decentralized oracle for aggregating and providing asset price data. It allows authorized nodes to submit aggregated price data that is made available for other contracts to consume. This contract is crucial for applications that require reliable and up-to-date asset price information, such as stablecoins, synthetic assets, and various DeFi platforms. ## Motivation The motivation behind the PriceFeed contract is to provide a reliable, decentralized source of asset price data that smart contracts can use. In many DeFi applications, accurate and timely price information is crucial for collateralization, liquidation, and synthetic asset pricing. The PriceFeed contract aims to fulfill this need by aggregating price data from multiple authorized sources, thereby reducing reliance on any single data provider. ## Specification ### The PriceFeed Contract **Key Features:** - Price Data Submission: Authorized nodes can submit price data to the contract. - Data Aggregation: Submitted prices are aggregated over a specified time interval (default 30 minutes). - Price Retrieval: Functions to retrieve the latest individual price or the aggregated price over a period. - Access Control: Only authorized nodes can submit price data managed by the contract owner. ### Technical Details - Blockchain Compatibility: Core Blockchain (Ylem version >=0.8.0) - Core Functions: - `addPrice(uint _price)`: Allows authorized nodes to submit price data. - `getLatestPrice()`: Retrieves the latest submitted price. - `getAggregatedPrice()`: Retrieves aggregated price data. - Events: - `PriceUpdated(uint timestamp, uint price)`: Emitted when a new price is submitted. ## Rationale The decentralized nature of the PriceFeed contract ensures that price data is not reliant on a single source, enhancing its reliability and security. Aggregating data over time also helps smooth out price volatility and provides a more stable reference for other contracts. ## Backward Compatibility This contract does not directly interact with the CBC20 token standard or other existing standards; hence it maintains compatibility with existing Core Blockchain contracts. ## Security Considerations Security considerations include ensuring the integrity and authenticity of the price data. Measures should be taken to vet and authorize only trustworthy nodes, and regular audits are recommended to check for potential vulnerabilities or manipulations. ## Conclusion The PriceFeed contract provides a crucial service for the Core Blockchain ecosystem by supplying reliable asset price data in a decentralized manner. Its integration is vital for the smooth functioning of many DeFi applications that rely on accurate price information. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # EquivalentToken - CBC20 Token Extension for Value Equivalence Based on External Price Feeds Source: https://cip.coreblockchain.net/cip/cbc/cip-105/ Repository path: cip/cbc/cip-105.md EquivalentToken - CBC20 Token Extension ## Abstract This CIP introduces the "EquivalentToken" contract, an extension of the BaseToken contract, that integrates external price feeds to determine the equivalent value of token transactions. This functionality enables the token to adapt its transfer, approval, and spending mechanisms based on real-time price data, making it suitable for applications requiring value stability or reference to external assets. ## Motivation The motivation behind the EquivalentToken contract is to provide a flexible token standard that can reference external price data to maintain value equivalence. This is particularly useful for tokens pegged to external assets, stablecoins, or any application where the token value needs to be adjusted in accordance with real-world data. ## Specification ### The EquivalentToken Contract **Key Features:** - Price Feed Integration: Utilizes an external price feed contract to obtain real-time price data. - Value Equivalence Functions: Includes modified transfer, approve, and transferFrom functions that calculate the equivalent amount of tokens based on the current price data. - Oracle Management: Functionality for the contract owner to change the price feed source. ### Technical Details - Blockchain Compatibility: Core Blockchain (Ylem version >0.8.0) - Core Functions: - `transferEquivalent`, `approveEquivalent`, `transferFromEquivalent`: Modified CBC20 functions to handle value equivalence. - `changeOracle(address _priceFeed)`: The owner can change the price feed source. - `_equivalent(uint256 amount)`: Private function to calculate the equivalent amount of tokens based on the price feed. ## Rationale Integrating external price feeds into the EquivalentToken contract addresses the need for tokens whose value can be adjusted based on real-world data. This feature is essential in applications where tokens are pegged to external assets or value stability is critical. ## Backward Compatibility This contract is compatible with the CBC20 standard, with additional functionalities that do not hinder its integration with existing Core Blockchain-based systems. ## Security Considerations Security considerations include the reliability and security of the external price feed and the potential for manipulation. Regular audits and reviews of the EquivalentToken contract and the price feed are recommended to ensure data integrity and contract security. ## Conclusion The EquivalentToken contract extends the CBC20 standard by incorporating external price data to maintain value equivalence. This innovation could significantly enhance the utility of tokens in various Core Blockchain applications, especially those requiring value stability or asset pegging. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # ChequableToken - An Extension of CBC20 Token Standard Enabling Cheque-Based Transactions Source: https://cip.coreblockchain.net/cip/cbc/cip-106/ Repository path: cip/cbc/cip-106.md ChequableToken - CBC20 Token Extension ## Abstract This CIP introduces the "ChequableToken" contract, an innovative extension of the BaseToken contract that introduces a cheque system. This system allows token holders to issue cheques, authorizing third-party spenders to withdraw a specified amount of tokens within a certain deadline, enhancing flexibility and control in token management. ## Motivation The primary motivation behind the ChequableToken contract is to provide a more flexible and secure way of authorizing token transactions. In some scenarios, traditional token allowances in CBC20 tokens can be too rigid and insecure. The cheque system in ChequableToken addresses this by allowing token holders to issue time-bound, nonce-tracked cheques to specific spenders, thus offering a more controlled and secure method for token allowances. ## Specification ### The ChequableToken Contract **Key Features:** - Cheque Struct: A data structure for cheques, including the owner, spender, amount, nonce, deadline, and signature. - Cheque Issuance and Cashing: Functionality for issuing and cashing cheques, with nonce tracking, to prevent double-spending. - Nonce Management: A system for managing nonces to ensure each cheque is unique and used only once. - Signature Verification: A mechanism to verify that the token holder authorizes the cheque. ### Technical Details - Blockchain Compatibility: Core Blockchain (Ylem version >0.8.0) - Core Functions: - `cashCheque(Cheque calldata cheque)`: A spender can cash a cheque, approving the specified token amount. - `_useNonce(address account, uint256 nonce)`: Private function to mark a nonce as used. - `_computeChequeHash(...)`: Computes the hash of a cheque for signature verification. - Events: - `ChequeCash(...)`: Emitted when a cheque is successfully cashed. ## Rationale Introducing a cheque system in the ChequableToken contract provides a more secure and controlled token transaction solution. It allows token holders to issue limited, time-bound authorizations, particularly useful in scenarios requiring conditional or deferred token transfers. ## Backward Compatibility ChequableToken maintains compatibility with the CBC20 standard, ensuring it can seamlessly integrate into the existing Core Blockchain ecosystem. ## Security Considerations Security is a significant concern, especially regarding handling signatures and nonce management. The contract should undergo thorough security audits to ensure its robustness and to mitigate potential vulnerabilities, particularly in replay attacks and signature forgery. ## Conclusion The ChequableToken contract extends the standard CBC20 functionality by introducing a secure and flexible cheque system for token allowances. This innovation could significantly enhance the utility and security of token transactions in various Core Blockchain applications. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Caller Contract for Secure Execution of On-Chain Transactions in CBC20 Token Ecosystems Source: https://cip.coreblockchain.net/cip/cbc/cip-108/ Repository path: cip/cbc/cip-108.md Caller Contract - Secure Execution of On-Chain Transactions ## Abstract This CIP introduces the "Caller" contract, a specialized smart contract designed to securely execute transactions on behalf of other contracts within the CBC20 token ecosystem. Its primary function is to facilitate the safe and controlled execution of transactions, particularly in systems where external contract interaction is necessary, such as with the BountiableToken contract. ## Motivation The motivation behind this CIP is to provide a secure and controlled way to execute transactions, especially for contracts that involve complex interactions with various parts of the Core Blockchain network. The Caller contract serves as a middleman to ensure that only authorized contracts (like the BountiableToken contract) can initiate specific operations, thereby enhancing security and reliability. ## Specification ### The "Caller" Contract **Key Features:** - Secure Transaction Execution: Facilitates the execution of external transactions in a controlled environment. - Contract Authentication: Only authorized contracts (specified at deployment) can call their functions. - Energy Limit Specification: Allows specifying an energy limit for executing transactions to manage transaction costs effectively. ### Technical Details - Blockchain Compatibility: Core Blockchain (Ylem version >0.8.0) - Core Functionality: - `bountyCall(address target, bytes memory data, uint256 energyLimit)`: Executes a transaction to a target address with specified data and energy limit. - Security: Includes checks to ensure that the caller is the authorized contract. ## Rationale The Caller contract addresses the need for secure and controlled execution of transactions from other contracts. Limiting the ability to initiate transactions to a specified address significantly reduces the risk of unauthorized or malicious actions, which is particularly crucial in contracts handling financial transactions or bounties. ## Backward Compatibility The Caller contract is designed to be compatible with existing CBC20 tokens and Core Blockchain smart contracts, ensuring seamless integration within the Core Blockchain ecosystem. ## Security Considerations Security audits and reviews are recommended to ensure the Caller contract does not introduce vulnerabilities, especially since it deals with executing transactions. Special attention should be paid to the contract's ability to limit execution solely to authorized addresses. ## Conclusion The Caller contract is a crucial component in systems that require secure and controlled execution of external transactions. Its adoption could enhance the security and functionality of CBC20 tokens and other smart contracts that require external interactions within the Core Blockchain network. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # BountiableToken - On-Chain Bounties for Transaction Execution Source: https://cip.coreblockchain.net/cip/cbc/cip-109/ Repository path: cip/cbc/cip-109.md BountiableToken - On-Chain Bounties for Transaction Execution ## Abstract This CIP introduces the "BountiableToken" contract, an innovative extension of the BaseToken contract that introduces a bounty mechanism. This mechanism allows token holders to incentivize the execution of specific transactions by attaching a bounty reward. This feature enhances interaction possibilities within the Core Blockchain network by encouraging third-party execution of transactions. ## Motivation The primary motivation behind this CIP is to solve the challenge of incentivizing on-chain execution of specific transactions or tasks. By enabling token holders to assign a reward in tokens for executing a transaction, the BountiableToken contract can stimulate activity and enhance transaction efficiency within decentralized applications. ## Specification ### The "BountiableToken" Contract **Key Features:** - Bounty Struct: A data structure to hold bounty details, including the owner, target, data, reward, nonce, deadline, energy limit, and signature. - Bounty Execution Mechanism: Functionality to allow users to 'cash' bounties, attempt the specified transaction, and claim the reward. - Nonce Management: Each token holder has a sequential nonce that is consumed when a bounty is accepted for processing. - Signature Verification: Ensures that the token holder authorizes the bounty. ### Technical Details - Blockchain Compatibility: Core Blockchain (Ylem version >0.8.0) - Core Functions: - `cashBounty(Bounty[] calldata bounties)`: Processes valid bounties, transfers rewards, and attempts their target calls. - Security: Any account can call `cashBounty`; authorization is established by the token holder's signature. ## Rationale Introducing a bounty mechanism in the BountiableToken contract addresses the need to incentivize specific Core Blockchain network actions. This feature is particularly beneficial when token holders wish to encourage the execution of specific tasks but cannot execute them personally. ## Backward Compatibility This contract is compatible with the CBC20 standard, ensuring it can be integrated seamlessly into the Core Blockchain ecosystem. ## Security Considerations Security is paramount, especially in handling signatures and executing transactions. The contract should undergo thorough security audits to ensure its robustness and to identify and mitigate potential vulnerabilities. ## Conclusion The BountiableToken contract extends the functionality of the standard CBC20 token by introducing an on-chain bounty mechanism. This innovation has the potential to significantly enhance the utility and interactivity of tokens in the Core Blockchain ecosystem. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # BaseToken - Enhanced CBC20 Token Implementation Source: https://cip.coreblockchain.net/cip/cbc/cip-110/ Repository path: cip/cbc/cip-110.md BaseToken - Enhanced CBC20 Token Implementation ## Abstract This CIP introduces the "BaseToken" contract, an abstract smart contract that extends the standard CBC20 token functionality. It incorporates additional features like batch transfers and dynamic allowance adjustments while retaining the standard CBC20 transfer and allowance functions. ## Motivation The primary motivation behind this CIP is to enhance the CBC20 token standard by introducing additional functionalities commonly needed in many decentralized applications. These include batch transfers for efficiency and methods for increasing or decreasing allowances, which provide better control over token spending. ## Specification ### The "BaseToken" Contract **Key Features:** - Standard CBC20 Functions: Includes all standard CBC20 functions such as transfer, approve, and transferFrom. - Batch Transfers: Allows the execution of multiple transfers in a single transaction. - Dynamic Allowance Management: Functions to increase or decrease token allowances. - Minting and Burning: Internal functions (`_mint` and `_burn`) for creating and destroying tokens. - Events: Emitting events for minting (Mint), burning (Burn), transfers (Transfer), and approvals (Approval). ### Technical Details - Blockchain Compatibility: Core Blockchain (Ylem version >0.8.0) - Core Functions: - `batchTransfer(address[] memory recipients, uint256[] memory amounts)`: Allows batch token transfers. - `increaseAllowance(address spender, uint256 addedValue)`: Dynamically adjust allowances to increase spending limits. - `decreaseAllowance(address spender, uint256 subtractedValue)`: Dynamically adjust allowances to decrease spending limits. - `_mint(address account, uint256 amount)`: Internal function for token minting. - `_burn(address account, uint256 amount)`: Internal function for token burning. ## Rationale Including batch transfers and dynamic allowance management, the "BaseToken" contract addresses efficiency and flexibility concerns in token operations. Batch transfers reduce transaction costs and complexity, while dynamic allowance management gives users more control over their token allowances. ## Backward Compatibility This contract implements the standard CBC20 transfer and allowance functions. Its minting and burning operations emit custom `Mint` and `Burn` events instead of CBC20 `Transfer` events. ## Security Considerations Security measures, such as checks for transfer amounts and balances, are integral to the "BaseToken" contract. Additional audits and peer reviews are recommended to identify potential vulnerabilities and ensure robustness. ## Conclusion The "BaseToken" contract extends the CBC20 standard by adding practical features for real-world applications. Its adoption could streamline operations in numerous CBC20 DApps, enhancing the overall Core Blockchain ecosystem's efficiency and functionality. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # AssetToken - Fixed Supply CBC20 Token Source: https://cip.coreblockchain.net/cip/cbc/cip-111/ Repository path: cip/cbc/cip-111.md AssetToken - Fixed Supply CBC20 Token ## Abstract This CIP introduces "AssetToken," an extension of the BaseToken contract with a fixed total supply. It inherits all the functionalities of the BaseToken while initializing with a predefined number of tokens minted to the creator's address. This contract serves as a standardized model for fixed-supply CBC20 tokens. ## Motivation The rationale behind the AssetToken contract is to offer a straightforward, deploy-ready CBC20 token implementation with a fixed supply. Many projects require tokens with a fixed total supply for reasons like preserving scarcity or ensuring predictability in token economics. AssetToken fulfills this need while providing the extended functionalities of the BaseToken. ## Specification ### Inherited Features - Inherits BaseToken: Includes all functionalities of the BaseToken, such as standard CBC20 functions and extended features like batch transfers and dynamic allowance adjustments. ### Fixed Total Supply - The total supply is fixed at contract deployment, with the entire supply minted to the creator's address. ## Technical Details - Blockchain Compatibility: Core Blockchain (Ylem version >0.8.0) - Constructor: - Initializes the token with the specified name, symbol, and decimals. - Mints the total supply to the contract creator's address. ## Rationale The choice of a fixed supply and the immediate minting to the creator's address in the AssetToken contract are designed to simplify the deployment process for projects needing a standard, fixed-supply CBC20 token. This standardization can facilitate a wide range of applications, including but not limited to digital assets, project tokens, and various decentralized finance (DeFi) applications. ## Backward Compatibility AssetToken is fully compatible with the CBC20 standard, ensuring seamless integration with existing Core Blockchain wallets, exchanges, and other CBC20-compliant platforms. ## Security Considerations While inheriting the security features of the BaseToken, it is crucial to conduct thorough audits and reviews of the AssetToken contract, especially considering the irreversibility of the initial token minting process. ## Conclusion The AssetToken contract extends the BaseToken functionality to provide a simple, secure, standardized implementation for fixed-supply CBC20 tokens. Its adoption could streamline the creation of fixed-supply tokens across various Core Blockchain projects. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # RWA Metadata Storage Source: https://cip.coreblockchain.net/cip/cbc/cip-150/ Repository path: cip/cbc/cip-150.md On-Chain Key-Value Metadata Storage for Compact Descriptors. ## Abstract Smart contracts often rely on off-chain JSON documents (e.g. for NFTs) or fixed functions (e.g. `name()`, `symbol()`) for metadata. This standard introduces a flexible on-chain key-value structure for metadata that must be durable, auditable, and optionally updateable. Use cases include document fingerprints, compliance references, and structured legal proofs. The KV structure works in parallel with existing standards to enhance trust, transparency, and auditability in tokenized systems. ## Motivation This CIP defines a standardized smart contract interface for storing compact key-value (KV) metadata on-chain. It supports **sealed (immutable)** and **unsealed (updatable)** entries. It is particularly useful for **Real World Asset (RWA)** tokenization and complements existing practices like CBC-20 metadata and off-chain NFT metadata URLs. ## Specification ### Interface ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IOnChainMetadataKV { function getMetadataValue(string calldata key) external view returns (string memory value); function hasMetadataKey(string calldata key) external view returns (bool exists); function isMetadataSealed(string calldata key) external view returns (bool sealed); function listMetadataKeys() external view returns (string[] memory keys); function getMetadataByIndex(uint256 index) external view returns (string memory key, string memory value); function metadataCount() external view returns (uint256 total); function setMetadataValue(string calldata key, string calldata value) external; function sealMetadataKey(string calldata key) external; } ``` ## Reference Implementation ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract OnChainMetadataKV is IOnChainMetadataKV { struct Entry { string value; bool sealed; bool exists; } mapping(string => Entry) private _entries; string[] private _keys; function setMetadataValue(string calldata key, string calldata value) external override { Entry storage entry = _entries[key]; require(!entry.sealed, "Metadata key sealed"); if (!entry.exists) { _keys.push(key); entry.exists = true; } entry.value = value; } function sealMetadataKey(string calldata key) external override { require(_entries[key].exists, "Key not found"); _entries[key].sealed = true; } function getMetadataValue(string calldata key) external view override returns (string memory) { require(_entries[key].exists, "Key not found"); return _entries[key].value; } function hasMetadataKey(string calldata key) external view override returns (bool) { return _entries[key].exists; } function isMetadataSealed(string calldata key) external view override returns (bool) { require(_entries[key].exists, "Key not found"); return _entries[key].sealed; } function listMetadataKeys() external view override returns (string[] memory) { return _keys; } function getMetadataByIndex(uint256 index) external view override returns (string memory, string memory) { require(index < _keys.length, "Index out of bounds"); string memory key = _keys[index]; return (key, _entries[key].value); } function metadataCount() external view override returns (uint256) { return _keys.length; } } ``` ## Real World Asset Use Case RWA tokens frequently require referencing off-chain legal and regulatory artifacts, such as: * Investment prospectuses * Legal disclaimers * KYC provider certifications * Title deed hashes * Security identifiers (ISIN, CUSIP) By using on-chain metadata KV: * Developers can store compact fingerprints or signed digests. * Legal auditors can verify presence and integrity on-chain. * Updates can be allowed when needed—but sealed once finalized. ### RWA Ticker The RWA ticker should be chosen according to the commodities being tokenized: Examples: ``` RWAZCH5 ``` Where: * `RWA` = Real World Asset prefix * `ZC` = Commodity (Corn) * `H` = Month code (March) * `5` = Year (2025) Another example: ``` RWASWSG5 ``` Where: * `SWS` = Sugar type * `G` = Month code (February) * `5` = Year (2025) Keep tickers **short, descriptive, and standardized**. #### Month Code Table | Month | Code | | --------- | ---- | | January | F | | February | G | | March | H | | April | J | | May | K | | June | M | | July | N | | August | Q | | September | U | | October | V | | November | X | | December | Z | ## Comparison Summary | Method | Scope | Location | Mutable | Standard Use | | -------------------------- | ----------- | --------- | -------- | --------------------------------- | | `name()` / `symbol()` | Global | On-chain | No | Identification (CBC-20/4626) | | `tokenURI()` | Per-token | Off-chain | Yes | NFT metadata (CBC-721/1155) | | **Metadata KV (this CIP)** | Key-defined | On-chain | Optional | Contextual metadata, docs, hashes | ## Rationale The on-chain key-value metadata storage approach addresses the need for flexible, auditable, and optionally updateable metadata in smart contracts. This standard provides a middle ground between immutable on-chain data and mutable off-chain references. This is particularly valuable for **real-world asset tokenization**, where legal and regulatory compliance requires both transparency and the ability to update information when necessary. ## Backward Compatibility This standard is designed to work alongside existing metadata approaches like: * CBC-20 `name()` / `symbol()` * CBC-721 `tokenURI()` It adds additional metadata capability without breaking existing implementations. ## Security Considerations * Mutable entries should be carefully permissioned. * Metadata keys should be sealed once finalized. * Consider role-based access control for `setMetadataValue`. * Consider versioned metadata keys such as: ``` PROSPECTUS_V1 PROSPECTUS_V2 LEGAL_HASH_V1 ``` to preserve historical references. ## Conclusion The On-Chain Metadata KV standard provides a flexible and secure approach to managing metadata in smart contracts. Its adoption improves transparency, auditability, and compliance in tokenized systems, especially for **Real World Asset (RWA)** ecosystems. ## References **Deployment Script** DeployOnChainKV.s.sol Solidity deployment script using the Foxar framework. **Test Suite** OnChainKV.t.sol Unit tests validating metadata setting, sealing, and retrieval. **Contract ABI** IOnChainMetadataKV.abi.json Interface ABI for integration with external tooling. ## Copyright Copyright and related rights waived via CC0. --- # Token Lifecycle Metadata Standard Source: https://cip.coreblockchain.net/cip/cbc/cip-151/ Repository path: cip/cbc/cip-151.md Token Lifecycle Metadata Standard ## Abstract This CIP defines standard key-value metadata fields `tokenExpiration` and `tradingStop` for controlling token usability over time. These metadata keys help developers, exchanges, and auditors enforce lifecycle logic such as halting trading or blocking transfers after expiration. In tokenized systems, especially real-world assets (RWA), tokens may need expiration conditions based on legal or operational constraints. This CIP introduces two optional metadata fields: `tokenExpiration` (the date after which tokens become unusable) and `tradingStop` (the date after which exchanges should stop listing the asset). These values are stored as compact Unix timestamps in a key-value metadata store, such as defined in CIP-KV-Metadata. ## Motivation Real-world tokenized instruments often have lifecycles. For example: - A bond may mature and must not be tradable afterward. - A compliance-based token may be invalid beyond a certain date. - A fund share may end its subscription window. Today, most smart contracts do not provide built-in support for such expiration logic, requiring custom development. This standard proposes consistent metadata keys for enforcing and signaling such behavior. ## Specification ### Key Definitions #### tokenExpiration - Purpose: Specifies the Unix timestamp after which the token is considered expired. - Format: Stringified Unix timestamp, e.g. `"1719878400"` for 2024-07-02. - Behavior: On-chain enforcement recommended. After this timestamp, transfers and burns should be disabled. #### tradingStop - Purpose: Specifies the last date exchanges should allow token trading. - Format: Stringified Unix timestamp, e.g. `"1719705600"` for 2024-06-30. - Behavior: Informational. Intended for off-chain enforcement by exchanges or trading platforms. ### Example Values ```json "tokenExpiration": "1719878400" "tradingStop": "1719705600" ``` These values should be stored in the KV metadata registry of the token smart contract, e.g.: ```solidity setValue("tokenExpiration", "1719878400"); setValue("tradingStop", "1719705600"); sealKey("tokenExpiration"); sealKey("tradingStop"); ``` ## Recommended On-Chain Enforcement Token contracts should add a modifier such as: ```solidity modifier notExpired() { string memory expStr = kv.getValue("tokenExpiration"); require(bytes(expStr).length > 0, "No expiration set"); uint256 expiry = parseUint(expStr); require(block.timestamp < expiry, "Token expired"); _; } ``` Apply to: - transfer() - transferFrom() - burn() (if applicable) If `tokenExpiration` is not set, transfers should proceed as normal. ## Exchange and DEX Guidance - `tradingStop` is intended as a signal to **centralized exchanges** to halt listings or order books. - `tokenExpiration` may be read off-chain or enforced via token logic to prevent post-maturity movement. ## Timestamp Format - Use standard Unix epoch time (seconds since 1970). - To reduce size, truncate right digits (e.g. to nearest day or hour) if high precision is unnecessary. - Example: `"1719878400"` = 2024-07-02 00:00:00 UTC ## Backward Compatibility This metadata format is compatible with: - CIP-KV-Metadata standard (on-chain key-value) - CBC-20 and CBC-721 smart contracts - EVM and non-EVM chains using Unix timestamp logic ## Security Considerations - If `tokenExpiration` is used, it should be **sealed** via `sealKey()` to prevent tampering. - Frontends should handle expired tokens gracefully and avoid exposing transfer UI. - Incorrect timestamps may cause false lockouts or trading issues. ## Conclusion The Token Lifecycle Metadata Standard provides a standardized approach to managing token expiration and trading stop dates in Core Blockchain applications. Its adoption can enhance security and compliance in tokenized asset systems. ## References - **Test Suite:** [TokenWithExpiration.t.sol](/cip/cip-151/TokenWithExpiration.t.sol) Solidity test file containing unit tests for the TokenWithExpiration contract, verifying correct handling of token expiration logic and related behaviors. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Standard for Publishing Lab Certificates as On-Chain Metadata Source: https://cip.coreblockchain.net/cip/cbc/cip-152/ Repository path: cip/cbc/cip-152.md Standard for Publishing Lab Certificates as On-Chain Metadata ## Abstract In physical goods verification and tokenized commodity markets, lab reports are critical to establish product quality, purity, or authenticity. This standard defines: - The key `"lab"` to be used in on-chain KV metadata. - The required file name `lab.json`. - Expected format for the contents of the certificate, supporting structured fields with units. The value associated with the `"lab"` key MUST point to the off-chain file (e.g. IPFS or other decentralized storage). The JSON file MUST be immutable, versioned, and cryptographically verifiable. ## Motivation A growing number of asset-backed tokens (e.g. coffee beans, precious metals, food, chemicals, pharmaceuticals) need to attach lab-verified attributes such as: - Moisture content - pH - Color - Presence of contaminants - Protein or ash content These properties are typically stored off-chain in PDF or JSON files, without on-chain standardization or discoverability. This CIP enables reliable on-chain discovery of lab reports using a well-defined pattern. ## Specification ### Key: `"lab"` - This key must be stored using a CIP-compliant key-value metadata contract. - The key must be: `lab` - The value must be a URL or content-addressed reference to a JSON file (e.g. IPFS CID). Example: ```txt key: "lab" value: "ipfs://QmXabc123.../lab.json" ``` ### File Name: `lab.json` - The filename MUST be `lab.json` to signal standardization. - It should be referenced by the `lab` key. ### Expected JSON Structure A `lab.json` file SHOULD contain a flat structure of property objects, each including: - `value`: Numeric or string value - `unit`: Optional unit field Example 1: ```json { "moisture": { "value": 9.7, "unit": "percent" }, "SO2": { "value": 4.83, "unit": "ppm" }, "pH": { "value": 6.51 }, "protein": { "value": 0.31, "unit": "percent" }, "ASH": { "value": 0, "unit": "percent" }, "color": { "value": "white" } } ``` Example 2 (identical structure with different values): ```json { "moisture": { "value": 10.1, "unit": "percent" }, "SO2": { "value": 3.15, "unit": "ppm" }, "pH": { "value": 6.45 }, "protein": { "value": 0.25, "unit": "percent" }, "ASH": { "value": 0.02, "unit": "percent" }, "color": { "value": "beige" } } ``` ## Enforcement and Sealing - The `"lab"` entry SHOULD be sealed using `sealKey("lab")` after it is published. - If multiple lab reports exist over time, consider storing versioned keys: - `"lab_v1"`, `"lab_v2"`, with `"lab"` always pointing to the latest. ## Storage and Format - The JSON file SHOULD be stored on IPFS, Arweave, or similar immutable content-addressed storage. - The `value` in KV is recommended to be the IPFS CID (e.g. `"ipfs://..."`) or full URL. ## Use Cases - Tokenized food, beverages, commodities - Pharmaceutical tracking (e.g. generic ingredients) - Energy, water, or industrial emissions - Precious metal or gemstone verification ## Rationale The lab certificate standard addresses the critical need for verifiable quality and authenticity data in tokenized physical assets. By providing a consistent structure and on-chain discoverability, this standard enables trust and transparency in commodity markets while maintaining the flexibility needed for diverse analytical requirements. ## Backward Compatibility This standard is fully compatible with CIP-KV-Metadata and may be embedded as part of broader metadata systems (e.g. tokenURI JSON can also reference lab.json). ## Security Considerations - Make sure the referenced `lab.json` is immutable and hosted in trusted decentralized storage. - Consider signing the file with a lab's cryptographic signature (optional extension). - Do not store unverified lab reports without a sealing mechanism. ## Conclusion The Standard for Publishing Lab Certificates as On-Chain Metadata provides a reliable and standardized approach to linking verifiable lab reports with tokenized assets. Its adoption can enhance trust, transparency, and compliance in physical asset tokenization. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Standard Interface Detection Source: https://cip.coreblockchain.net/cip/cbc/cip-165/ Repository path: cip/cbc/cip-165.md Standard Interface Detection ## Abstract Herein, we standardize the following: 1. How interfaces are identified 2. How a contract will publish the interfaces it implements 3. How to detect if a contract implements CBC-165 4. How to detect if a contract implements any given interface ## Motivation For some "standard interfaces" like the CBC-20 token interface, it is sometimes useful to query whether a contract supports the interface and if yes, which version of the interface, in order to adapt the way in which the contract is to be interacted with. This proposal standardizes the concept of interfaces and standardizes the identification (naming) of interfaces. ## Specification ### How Interfaces are Identified For this standard, an *interface* is a set of function selectors as defined by the Core Blockchain ABI. This is a subset of Ylem's concept of interfaces and the `interface` keyword definition, which also defines return types, mutability, and events. The following code examples use syntax from Ylem `0.8.4` (or above). We define the interface identifier as the XOR of all function selectors in the interface. This code example shows how to calculate an interface identifier: ```solidity interface ExampleInterface { function hello() external pure; function world(int) external pure; } contract Selector { function calculateSelector() public pure returns (bytes4) { return type(ExampleInterface).interfaceId; } } ``` Note: interfaces do not permit optional functions, therefore, the interface identity will not include them. ### How a Contract will Publish the Interfaces it Implements A contract that is compliant with CBC-165 shall implement the following interface (referred to as `CBC165.sol`): ```solidity interface CBC165 { /// @notice Query if a contract implements an interface /// @param interfaceID The interface identifier, as specified in CBC-165 /// @dev Interface identification is specified in CBC-165. This function /// uses less than 30,000 energy. /// @return `true` if the contract implements `interfaceID` and /// `interfaceID` is not 0xffffffff, `false` otherwise function supportsInterface(bytes4 interfaceID) external view returns (bool); } ``` The interface identifier for this interface is `0x80ada41b`. Ylem calculates it from the canonical function signature `supportsInterface(bytes4)` using Core's SHA3-256-based function selector algorithm. It can also be obtained as `type(CBC165).interfaceId` or `CBC165.supportsInterface.selector`. Therefore the implementing contract will have a `supportsInterface` function that returns: - `true` when `interfaceID` is `0x80ada41b` (CBC-165 interface) - `false` when `interfaceID` is `0xffffffff` - `true` for any other `interfaceID` this contract implements - `false` for any other `interfaceID` This function must return a bool and use at most 30,000 energy. Implementation note, there are several logical ways to implement this function. Please see the example implementations and the discussion on energy usage. ### How to Detect if a Contract Implements CBC-165 1. The source contract makes a `STATICCALL` to the destination address with input data: `0x80ada41b80ada41b00000000000000000000000000000000000000000000000000000000` and energy 30,000. This corresponds to `contract.supportsInterface(0x80ada41b)`. 2. If the call fails or returns false, the destination contract does not implement CBC-165. 3. If the call returns true, a second call is made with input data `0x80ada41bffffffff00000000000000000000000000000000000000000000000000000000`. 4. If the second call fails or returns true, the destination contract does not implement CBC-165. 5. Otherwise it implements CBC-165. ### How to Detect if a Contract Implements any Given Interface 1. If you are not sure if the contract implements CBC-165, use the above procedure to confirm. 2. If it does not implement CBC-165, then you will have to see what methods it uses the old-fashioned way. 3. If it implements CBC-165 then call `supportsInterface(interfaceID)` to determine if it implements an interface you can use. ## Rationale We tried to keep this specification as simple as possible. This implementation is also compatible with current Ylem versions and the Core Blockchain architecture. ## Backward Compatibility The mechanism described above (with `0xffffffff`) should work with most contracts created before this standard to determine that they do not implement CBC-165. ## Test Cases A conforming implementation should verify at least the following cases: - `supportsInterface(0x80ada41b)` returns `true`. - `supportsInterface(0xffffffff)` returns `false`. - An unknown interface identifier returns `false`. - A declared supported interface identifier returns `true`. - A reverted call, an empty response, or malformed return data is treated as lack of support. The following contract detects which interfaces other contracts implement: ```solidity contract CBC165Query { bytes4 constant InvalidID = 0xffffffff; bytes4 constant CBC165ID = 0x80ada41b; function doesContractImplementInterface(address _contract, bytes4 _interfaceId) external view returns (bool) { uint256 success; uint256 result; (success, result) = noThrowCall(_contract, CBC165ID); if ((success==0)||(result==0)) { return false; } (success, result) = noThrowCall(_contract, InvalidID); if ((success==0)||(result!=0)) { return false; } (success, result) = noThrowCall(_contract, _interfaceId); if ((success==1)&&(result==1)) { return true; } return false; } function noThrowCall(address _contract, bytes4 _interfaceId) internal view returns (uint256 success, uint256 result) { bytes4 cbc165ID = CBC165ID; assembly { let x := mload(0x40) // Find empty storage location using "free memory pointer" mstore(x, cbc165ID) // Place signature at beginning of empty storage mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature success := staticcall( 30000, // 30k energy _contract, // To addr x, // Inputs are stored at location x 0x24, // Inputs are 36 bytes long x, // Store output over input (saves space) 0x20) // Outputs are 32 bytes long if or(iszero(success), lt(returndatasize(), 0x20)) { success := 0 result := 0 } if success { result := mload(x) // Load the result if gt(result, 1) { success := 0 result := 0 } } } } } ``` ## Implementation This approach uses a mapping-backed `view` implementation of `supportsInterface`. It provides constant-time lookups at the cost of storing each supported interface during contract initialization. The `CBC165MappingImplementation` contract is generic and reusable. ```solidity import "./CBC165.sol"; contract CBC165MappingImplementation is CBC165 { /// @dev You must not set element 0xffffffff to true mapping(bytes4 => bool) internal supportedInterfaces; constructor() { supportedInterfaces[type(CBC165).interfaceId] = true; } function supportsInterface(bytes4 interfaceID) external view override returns (bool) { return interfaceID != 0xffffffff && supportedInterfaces[interfaceID]; } } interface Simpson { function is2D() external view returns (bool); function skinColor() external view returns (string memory); } contract Lisa is CBC165MappingImplementation, Simpson { constructor() { supportedInterfaces[type(Simpson).interfaceId] = true; } function is2D() external pure override returns (bool) { return true; } function skinColor() external pure override returns (string memory) { return "yellow"; } } ``` The following is a storage-free `pure` implementation of `supportsInterface`. Its execution cost increases linearly with the number of supported interfaces. ```solidity import "./CBC165.sol"; interface Simpson { function is2D() external view returns (bool); function skinColor() external view returns (string memory); } contract Homer is CBC165, Simpson { function supportsInterface(bytes4 interfaceID) external pure override returns (bool) { return interfaceID != 0xffffffff && (interfaceID == type(CBC165).interfaceId || interfaceID == type(Simpson).interfaceId); } function is2D() external pure override returns (bool) { return true; } function skinColor() external pure override returns (string memory) { return "yellow"; } } ``` The mapping approach trades initialization and storage costs for constant-time lookups. The storage-free approach avoids those costs, but each additional supported interface adds another comparison. Implementers should choose based on their deployment and call patterns while keeping `supportsInterface` below the required 30,000 energy limit. ## Security Considerations - A contract can falsely claim support for any interface. Callers MUST NOT treat a positive response as proof that the implementation is correct or trustworthy. - Callers SHOULD handle reverted calls, malformed return data, and contracts that consume the available energy. - Implementers MUST ensure that `supportsInterface(0xffffffff)` always returns `false`. - Implementers should consider energy costs when interface detection is used in frequently called functions. ## Conclusion The CBC-165 standard provides a standard method for interface detection on Core Blockchain, enabling smart contracts to dynamically discover and interact with each other based on their declared interfaces. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # CoreToken - Comprehensive CBC20 Token Standard Extension Source: https://cip.coreblockchain.net/cip/cbc/cip-200/ Repository path: cip/cbc/cip-200.md CoreToken - Comprehensive CBC20 Token Standard Extension ## Abstract This CIP introduces the "CoreToken" contract, a multifaceted extension of the BaseToken contract. CoreToken integrates functionalities from WrapperToken, EquivalentToken, ChequableToken, and BountiableToken contracts, offering a robust and versatile token standard. This integration allows CoreToken to encapsulate a wide range of functionalities, including token wrapping, price equivalence, cheque-based transactions, and bounty mechanisms, making it suitable for diverse applications in the Core Blockchain ecosystem. ## Motivation The need for a comprehensive token standard to meet various requirements within the Core Blockchain ecosystem inspired the creation of the CoreToken contract. By integrating multiple functionalities into a single contract, CoreToken offers a versatile solution for projects that require advanced token features such as value stability, enhanced transactional capabilities, and incentivization mechanisms. ## Specification ### Key Features - WrapperToken Functionality: Enables wrapping and unwrapping of another CBC20 token. - EquivalentToken Functionality: Integrates external price feeds to maintain value equivalence. - ChequableToken Functionality: Allows issuing and cashing cheques for controlled token allowances. - BountiableToken Functionality: Introduces a system for assigning and executing bounties in tokens. - BaseToken Features: Inherits standard CBC20 functions and additional utility functions. ### Technical Details - Ylem version: >0.8.0 - Constructor: Initializes the token with the required parameters for the inherited functionalities. ## Rationale Integrating various token functionalities into a single contract, CoreToken is designed to offer a versatile and comprehensive token standard. This multifunctionality approach addresses the needs of different applications within the Core Blockchain ecosystem, ranging from DeFi to governance and more. ## Backward Compatibility CoreToken is compatible with the CBC20 standard, ensuring interoperability with existing Core Blockchain systems and applications. ## Security Considerations Given its multifunctionality, the CoreToken contract requires thorough security audits and reviews to ensure the integrity of its various components. Particular attention should be paid to the interactions between different functionalities to mitigate potential vulnerabilities. ## Conclusion The CoreToken contract presents a unique and advanced approach to CBC20 token standards by combining multiple functionalities into a cohesive contract. This approach can significantly enhance the utility and flexibility of tokens in the Core Blockchain ecosystem, catering to a broad range of applications and use cases. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Core Signature Verification Standard Source: https://cip.coreblockchain.net/cip/cbc/cip-712/ Repository path: cip/cbc/cip-712.md Core Signature Verification Standard ## Abstract CIP-712 defines a standard for hashing and signing typed structured data in Core Blockchain applications. It adapts the data model and `\x19\x01` framing of EIP-712 to Core Blockchain while using Core network identifiers, addresses, hashing, and signatures. The standard provides domain separation between applications, contracts, and Core networks. Replay protection within a single domain is application-specific and is not provided by CIP-712 itself. ## Motivation Signing an opaque byte string does not communicate the meaning of a message to the signer. Typed structured data gives wallets and applications the information required to present the message as named fields and gives independent implementations a deterministic way to calculate the same digest. CIP-712 follows EIP-712 where its rules are compatible with Core Blockchain. The Core-specific rules in this document take precedence. In particular, Core uses `networkId`, Core addresses, SHA3-256, and Core EDDSA signatures. ## Specification The key words **MUST**, **MUST NOT**, **SHOULD**, and **MAY** in this document are to be interpreted as requirements on conforming implementations. ### Hash Function In this specification, `hash` means the result of Ylem's `keccak256` function, which uses SHA3-256 on Core Blockchain. Implementations MUST NOT substitute Ethereum's legacy Keccak-256 when calculating CIP-712 hashes. ### Typed Data Typed data consists of a primary struct type, the definitions of all referenced struct types, and the values of the primary struct's fields. Type names, field types, field names, and field order are part of the signed data. The canonical type encoding follows EIP-712. For example: ```text Mail(address to,string contents) ``` The type hash and struct hash are calculated as follows: ```text typeHash = hash(encodeType(typeOf(data))) structHash = hash(typeHash || encodeData(data)) ``` Values MUST be encoded according to the Ylem ABI. Core `address` values are 22-byte addresses and MUST be encoded as Ylem address values. Dynamic `bytes` and `string` values are represented by the hash of their contents. Nested structs are represented by their struct hash, and arrays are represented by the hash of their encoded elements. ### Domain Separator CIP-712 uses the following domain type: ```text EIP712Domain(string name,string version,uint256 networkId,address verifyingContract) ``` The literal name `EIP712Domain` is retained for compatibility with the Core OpenZeppelin implementation and MUST be used when calculating the domain type hash. Renaming the encoded type to `CIP712Domain` produces a different domain separator. The domain type hash and domain separator are calculated as follows: ```text domainTypeHash = hash("EIP712Domain(string name,string version,uint256 networkId,address verifyingContract)") domainSeparator = hash(abi.encode( domainTypeHash, hash(bytes(name)), hash(bytes(version)), networkId, verifyingContract )) ``` The fields have the following meanings: - `name`: the human-readable name of the application or protocol. - `version`: the current major version of the signing domain. - `networkId`: the current Core network identifier. In Ylem contracts this value is obtained from `block.chainid`. - `verifyingContract`: the Core address of the contract that will verify the signature. Changing any domain field invalidates signatures created for the previous domain. ### Signed Digest The digest presented to the Core signing algorithm MUST be: ```text digest = hash("\x19\x01" || domainSeparator || structHash) ``` Both `domainSeparator` and `structHash` are exactly 32 bytes. The prefix, domain separator, and struct hash are concatenated without additional ABI padding. ### Signatures The signer signs `digest` using the Core signing algorithm. A Core signature is a 171-byte blob consisting of a 114-byte signature and a 57-byte public key. CIP-712 signatures MUST be handled as a single `bytes` value and not as an Ethereum-style `(v, r, s)` tuple. ### Verification A verifier MUST reconstruct the domain separator and struct hash from the expected domain and message, calculate `digest`, recover the signer from the Core signature, and compare the recovered address with the expected signer. In Ylem, verification can be performed with `ecrecover(bytes32 digest, bytes signature)` or through a wrapper such as Core OpenZeppelin's `EDDSA.recover`. Implementations MUST reject signatures whose length is not 171 bytes and MUST treat signature recovery failure as invalid. ### Contract Implementation The Core OpenZeppelin `EIP712` contract implements the domain separator and final digest: ```solidity bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 networkId,address verifyingContract)"); function _buildDomainSeparator(bytes32 hashedName, bytes32 hashedVersion) private view returns (bytes32) { return keccak256( abi.encode(_TYPE_HASH, hashedName, hashedVersion, block.chainid, address(this)) ); } function _hashTypedDataV4(bytes32 structHash) internal view returns (bytes32) { return EDDSA.toTypedDataHash(_domainSeparatorV4(), structHash); } ``` The contract does not implement application-specific struct encoding. Applications MUST define their own type hashes and construct their own struct hashes according to this specification. Implementations MAY expose their domain through `eip712Domain()`. The Core OpenZeppelin implementation reports `name`, `version`, `networkId`, and `verifyingContract`. ### JSON-RPC Core clients that provide typed-data signing SHOULD expose it through `xcb_signTypedData_v4`. Typed-data payloads MUST use `networkId`, not Ethereum's `chainId`, and all addresses MUST be valid Core addresses for the selected network. ## Usage CIP-712 can be used for permits, orders, governance votes, meta-transactions, and other actions authorized by an off-chain signature. Applications SHOULD include all values that affect the authorized action in the signed struct. ## Backward Compatibility CIP-712 retains the EIP-712 typed-data model and final `\x19\x01` framing, but it is not byte-for-byte compatible with Ethereum EIP-712. Core-specific hashing, address encoding, network identifiers, and signatures produce different results. Existing Core applications that use the domain and digest construction specified above remain compatible. Applications MUST NOT assume that an Ethereum EIP-712 signature can be verified as a CIP-712 signature. ## Security Considerations CIP-712 does not provide replay protection within one signing domain. Applications authorizing a one-time action SHOULD include and validate a nonce, deadline, consumed-message record, or another application-specific replay-prevention mechanism. Including `networkId` and `verifyingContract` in the domain prevents a correctly constructed signature from being reused on a different Core network or by a different contract. Applications MUST verify the complete domain and MUST ensure that wallets display the same domain and message that the verifier will hash. Implementations MUST use the exact type definitions and field order agreed upon by the signer and verifier. A changed type name, domain type name, field order, hash function, or address encoding produces a different digest. Submission of a valid signature by an unintended third party may be possible. Applications MUST ensure that early submission either has the exact effect authorized by the signer or is otherwise safely rejected. ## Conclusion CIP-712 provides a deterministic Core-specific procedure for hashing, signing, and verifying typed structured data while retaining the established EIP-712 framing and domain-separation model. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # CoreNFT: Non-Fungible Token Standard for Core Blockchain Source: https://cip.coreblockchain.net/cip/cbc/cip-721/ Repository path: cip/cbc/cip-721.md CoreNFT: Non-Fungible Token Standard for Core Blockchain ## Abstract This standard defines the **CoreNFT** interface and provides an API for smart contracts to **track ownership**, **transfer NFTs**, and **enable interoperability** with wallets, marketplaces, and other decentralized applications on the Core Blockchain. ## Motivation To ensure NFTs created on Core Blockchain can be seamlessly integrated with a wide range of wallets, marketplaces, and tools, **a consistent standard is essential**. Inspired by Ethereum's ERC-721, CoreNFT addresses the need for a native NFT protocol adapted to Core's architecture and optimizations. ## Specification ### Interfaces #### CoreNFT (Required) ```solidity pragma solidity ^0.8.0; /// @title CoreNFT Standard Interface interface CBC721 /* is CBC165 */ { event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); function balanceOf(address owner) external view returns (uint256); function ownerOf(uint256 tokenId) external view returns (address); function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external payable; function safeTransferFrom(address from, address to, uint256 tokenId) external payable; function transferFrom(address from, address to, uint256 tokenId) external payable; function approve(address approved, uint256 tokenId) external payable; function setApprovalForAll(address operator, bool approved) external; function getApproved(uint256 tokenId) external view returns (address); function isApprovedForAll(address owner, address operator) external view returns (bool); } ``` #### Core165 (Required - Interface Detection) ```solidity interface CBC165 { function supportsInterface(bytes4 interfaceID) external view returns (bool); } ``` #### CoreNFTReceiver (Required for wallets/dApps that accept safe transfers) ```solidity interface CBC721Receiver { function onCBC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4); } ``` ### Optional Extensions #### CoreNFTMetadata (Optional) ```solidity interface CBC721Metadata /* is CBC721 */ { function name() external view returns (string memory); function symbol() external view returns (string memory); function tokenURI(uint256 tokenId) external view returns (string memory); } ``` #### CoreNFTEnumerable (Optional) ```solidity interface CBC721Enumerable /* is CBC721 */ { function totalSupply() external view returns (uint256); function tokenByIndex(uint256 index) external view returns (uint256); function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); } ``` ## Metadata JSON Schema ```json { "title": "CoreNFT Metadata", "type": "object", "properties": { "name": { "type": "string", "description": "Name of the NFT" }, "description": { "type": "string", "description": "Description of the NFT" }, "image": { "type": "string", "description": "URI of an image representing the NFT" } } } ``` ## Design Considerations ### Why Not CBC-20? Each CoreNFT is **non-fungible** — unique and not interchangeable. Unlike CBC-20 (or Core20) tokens, each NFT has individual ownership, metadata, and identity. ### Identification Each NFT is uniquely identified by a `uint256 tokenId` inside the contract. This ID **does not change** during the lifetime of the NFT. The combination of **contract address + tokenId** makes the NFT globally unique on Core Blockchain. ### Transfer Safety * Use `safeTransferFrom()` for smart contract-to-contract transfers. * Contracts should implement `onCoreNFTReceived()` to acknowledge receipt. * If not implemented correctly, tokens may be lost (irrecoverably locked). ## Use Cases * **Digital Collectibles**: Artwork, avatars, or gaming skins * **Tokenized Assets**: Real estate, cars, luxury goods * **Utility NFTs**: Access tokens, certificates, event tickets ## Best Practices * Implement CoreNFTMetadata and CoreNFTEnumerable if user experience and discovery are priorities. * Avoid unbounded loops to ensure scalability. * Avoid assumptions about tokenId patterns — always treat them as opaque. ## Rationale The CoreNFT standard provides a comprehensive framework for non-fungible tokens on the Core Blockchain. By defining clear interfaces for ownership tracking, transfer mechanisms, and metadata handling, this standard ensures interoperability across the Core ecosystem while maintaining the unique characteristics of NFTs. ## Backward Compatibility This standard is designed to be compatible with existing NFT implementations and can work alongside other token standards on the Core Blockchain. ## Security Considerations * Implement proper access controls for transfer and approval functions * Use safe transfer methods when interacting with smart contracts * Validate token ownership before allowing transfers * Consider energy optimization for batch operations * Ensure proper error handling for edge cases ## Conclusion The CoreNFT standard provides a robust foundation for non-fungible tokens on the Core Blockchain, enabling seamless integration with wallets, marketplaces, and decentralized applications while maintaining the unique characteristics that make NFTs valuable. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Token Standard Source: https://cip.coreblockchain.net/cip/cbc/cip-777/ Repository path: cip/cbc/cip-777.md A standardized smart contract interface for advanced token functionality on Core Blockchain (CBC), supporting operators, hooks, and improved composability over CBC-20. ## Abstract This standard defines an advanced token interface for Core Blockchain (CBC), introducing operator-based transfers, send/receive hooks, and improved composability compared to CBC-20. CBC-777 enables more flexible and secure token interactions, supporting use cases such as escrow, atomic swaps, and on-chain services. ## Motivation CBC-20 tokens are widely used but have limitations, such as lack of hooks for sending/receiving tokens and inflexible approval mechanisms. CBC-777 addresses these issues by introducing operators (trusted accounts that can send tokens on behalf of others) and hooks (functions called on send/receive), enabling safer and more powerful token workflows for DeFi, gaming, and enterprise applications. ## Specification ### Interface ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ICBC777 /* is ICBC165 */ { // --- Events --- event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes data, bytes operatorData); event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData); event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData); event AuthorizedOperator(address indexed operator, address indexed tokenHolder); event RevokedOperator(address indexed operator, address indexed tokenHolder); // --- Core Functions --- function name() external view returns (string memory); function symbol() external view returns (string memory); function granularity() external view returns (uint256); function totalSupply() external view returns (uint256); function balanceOf(address owner) external view returns (uint256); function send(address to, uint256 amount, bytes calldata data) external; function burn(uint256 amount, bytes calldata data) external; function isOperatorFor(address operator, address tokenHolder) external view returns (bool); function authorizeOperator(address operator) external; function revokeOperator(address operator) external; function operatorSend(address from, address to, uint256 amount, bytes calldata data, bytes calldata operatorData) external; function operatorBurn(address from, uint256 amount, bytes calldata data, bytes calldata operatorData) external; function defaultOperators() external view returns (address[] memory); } ``` ### Send and Receive Hooks Contracts can register as senders or recipients to receive notifications via hooks. Hooks are discovered using CBC-165 interface detection. ```solidity interface ICBC777Sender { function tokensToSend(address operator, address from, address to, uint256 amount, bytes calldata data, bytes calldata operatorData) external; } interface ICBC777Recipient { function tokensReceived(address operator, address from, address to, uint256 amount, bytes calldata data, bytes calldata operatorData) external; } ``` #### Hook Registration and Discovery - Contracts MUST implement CBC-165 and return `true` for the interface IDs of `ICBC777Sender` and/or `ICBC777Recipient` as appropriate. - If a hook is not registered, the token contract MUST proceed without calling it. - If a hook is registered but reverts, the token transfer MUST revert. - If a contract does not implement the hook, tokens may be locked; implementers should take care to avoid accidental lockup. ### Default Operators - CBC-777 supports a list of default operators (e.g., exchanges, custodians) that are operators for all token holders unless explicitly revoked by a holder. - The `defaultOperators()` function returns the list of default operator addresses. - Token holders can revoke or re-authorize default operators at any time. ### Method Behaviors - The `granularity` function MUST return the smallest transferable unit (usually 1). All token amounts MUST be a multiple of granularity. - The `send` and `operatorSend` functions MUST call the `tokensToSend` and `tokensReceived` hooks if registered. - Operators MAY be authorized or revoked by token holders at any time. Default operators can be revoked per holder. - Minting and burning MUST emit the appropriate events. - Contracts implementing CBC-777 MUST implement CBC-165 `supportsInterface`. - For CBC-20 compatibility, contracts MAY emit `Transfer` and `Approval` events as appropriate. ### Operator Authorization and Revocation - Any address can be authorized as an operator by a token holder using `authorizeOperator`. - Operators can be revoked at any time using `revokeOperator`. - The `isOperatorFor` function returns `true` if the operator is authorized for the token holder, either directly or as a default operator (unless revoked). - Operators can call `operatorSend` and `operatorBurn` on behalf of the token holder. ### Granularity and Decimals - `granularity()` defines the smallest transferable unit. For most tokens, this MUST be 1. - CBC-777 does not require a `decimals()` function, but it MAY be implemented for compatibility with CBC-20. ### Events - All token movements (send, mint, burn) MUST emit the appropriate events (`Sent`, `Minted`, `Burned`). - Operator authorization and revocation MUST emit `AuthorizedOperator` and `RevokedOperator` events. - For CBC-20 compatibility, `Transfer` and `Approval` events MAY be emitted. ## Reference Implementation A reference implementation can be provided upon request, following the CBC-777 interface and best practices for hooks and operator management. ## Rationale CBC-777 improves on CBC-20 by enabling advanced token flows, such as escrow, atomic swaps, and on-chain services, through hooks and operator permissions. Default operators simplify integration with custodians and exchanges. This design increases security and composability for modern dApps. ## Backward Compatibility CBC-777 is backward compatible with CBC-20. Implementers MAY include CBC-20 functions and events for compatibility with existing wallets and dApps. Dual implementation is recommended for maximum interoperability. ## Security Considerations - Implementers must ensure hooks are only called on contracts that register for them (using CBC-165). - Operator permissions should be managed carefully to prevent unauthorized transfers. Default operators should be chosen with care. - Granularity should be set to prevent rounding errors and ensure divisibility is as intended. - Upgradability and access control best practices should be followed. - Reentrancy: Hooks can call back into the token contract. Use reentrancy guards where appropriate. - Accidental token lockup: If tokens are sent to a contract that does not implement `tokensReceived`, they may be locked. User interfaces should warn users. - Operator abuse: Token holders should be able to easily view and manage their operators. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Multi Token Standard Source: https://cip.coreblockchain.net/cip/cbc/cip-1155/ Repository path: cip/cbc/cip-1155.md A standardized smart contract interface for managing multiple token types (fungible, non-fungible, and semi-fungible) in a single contract on Core Blockchain (CBC). ## Abstract This standard outlines a smart contract interface that can represent any number of fungible and non-fungible token types. Existing standards such as CBC-20 require deployment of separate contracts per token type. The CBC-721 standard's token ID is a single non-fungible index and the group of these non-fungibles is deployed as a single contract with settings for the entire collection. In contrast, the CBC-1155 Multi Token Standard allows for each token ID to represent a new configurable token type, which may have its own metadata, supply and other attributes. The `_id` argument contained in each function's argument set indicates a specific token or token type in a transaction. ## Motivation Tokens standards like CBC-20 and CBC-721 require a separate contract to be deployed for each token type or collection. This places a lot of redundant bytecode on Core Blockchain and limits certain functionality by the nature of separating each token contract into its own permissioned address. With the rise of blockchain games and platforms like Enjin Coin, game developers may be creating thousands of token types, and a new type of token standard is needed to support them. However, CBC-1155 is not specific to games and many other applications can benefit from this flexibility. New functionality is possible with this design such as transferring multiple token types at once, saving on transaction costs. Trading (escrow / atomic swaps) of multiple tokens can be built on top of this standard and it removes the need to "approve" individual token contracts separately. It is also easy to describe and mix multiple fungible or non-fungible token types in a single contract. ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. For non-zero address validation, "zero address" means both `address(0)` and `Checksum.zeroAddress()`. Implementations MUST reject both representations wherever a non-zero account is required. Minting and burning events MUST use the real zero address, `address(0)`, and MUST NOT use `Checksum.zeroAddress()` as the mint or burn marker. **Smart contracts implementing the CBC-1155 standard MUST implement all of the functions in the `CBC1155` interface.** **Smart contracts implementing the CBC-1155 standard MUST implement the CBC-165 `supportsInterface` function and MUST return the constant value `true` if `0x321439a9` is passed through the `interfaceID` argument.** ```solidity pragma solidity ^1.1.2; /** @title CBC-1155 Multi Token Standard Note: The CBC-165 identifier for this interface is 0x321439a9. */ interface CBC1155 /* is CBC165 */ { /** @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard). The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). The `_from` argument MUST be the address of the holder whose balance is decreased. The `_to` argument MUST be the address of the recipient whose balance is increased. The `_id` argument MUST be the token type being transferred. The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by. When minting/creating tokens, the `_from` argument MUST be set to `address(0)`. When burning/destroying tokens, the `_to` argument MUST be set to `address(0)`. */ event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value); /** @dev Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard). The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). The `_from` argument MUST be the address of the holder whose balance is decreased. The `_to` argument MUST be the address of the recipient whose balance is increased. The `_ids` argument MUST be the list of tokens being transferred. The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by. When minting/creating tokens, the `_from` argument MUST be set to `address(0)`. When burning/destroying tokens, the `_to` argument MUST be set to `address(0)`. */ event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values); /** @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled). */ event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); /** @dev MUST emit when the URI is updated for a token ID. URIs are defined in RFC 3986. The URI MUST point to a JSON file that conforms to the "CBC-1155 Metadata URI JSON Schema". */ event URI(string _value, uint256 indexed _id); /** @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call). @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). MUST revert if `_from` or `_to` is a zero address. MUST revert if balance of holder for token `_id` is lower than the `_value` sent. MUST revert on any other error. MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard). After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onCBC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). @param _from Source address @param _to Target address @param _id ID of the token type @param _value Transfer amount @param _data Additional data with no specified format, MUST be sent unaltered in call to `onCBC1155Received` on `_to` */ function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external; /** @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call). @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard). MUST revert if `_from` or `_to` is a zero address. MUST revert if length of `_ids` is not the same as length of `_values`. MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient. MUST revert on any other error. MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard). Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc). After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `CBC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard). @param _from Source address @param _to Target address @param _ids IDs of each token type (order and length must match _values array) @param _values Transfer amounts per token type (order and length must match _ids array) @param _data Additional data with no specified format, MUST be sent unaltered in call to the `CBC1155TokenReceiver` hook(s) on `_to` */ function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external; /** @notice Get the balance of an account's tokens. @dev MUST revert if `_owner` is a zero address. @param _owner The address of the token holder @param _id ID of the token @return The _owner's balance of the token type requested */ function balanceOf(address _owner, uint256 _id) external view returns (uint256); /** @notice Get the balance of multiple account/token pairs @dev MUST revert if the lengths of `_owners` and `_ids` differ or if any entry in `_owners` is a zero address. @param _owners The addresses of the token holders @param _ids ID of the tokens @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair) */ function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory); /** @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens. @dev MUST revert if `_operator` is a zero address. MUST emit the ApprovalForAll event on success. @param _operator Address to add to the set of authorized operators @param _approved True if the operator is approved, false to revoke approval */ function setApprovalForAll(address _operator, bool _approved) external; /** @notice Queries the approval status of an operator for a given owner. @param _owner The owner of the tokens @param _operator Address of authorized operator @return True if the operator is approved, false if not */ function isApprovedForAll(address _owner, address _operator) external view returns (bool); } ``` ### CBC-1155 Token Receiver **Smart contracts MUST implement all of the functions in the `CBC1155TokenReceiver` interface to accept transfers. See "Safe Transfer Rules" for further detail.** **Smart contracts MUST implement the CBC-165 `supportsInterface` function and signify support for the `CBC1155TokenReceiver` interface to accept transfers. See "CBC1155TokenReceiver CBC-165 rules" for further detail.** ```solidity pragma solidity ^1.1.2; /** Note: The CBC-165 identifier for this interface is 0xc08443c9. */ interface CBC1155TokenReceiver { /** @notice Handle the receipt of a single CBC1155 token type. @dev An CBC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated. This function MUST return `bytes4(keccak256("onCBC1155Received(address,address,uint256,uint256,bytes)"))` (i.e. 0x4f01cb00) if it accepts the transfer. This function MUST revert if it rejects the transfer. Return of any other value than the prescribed Core SHA3-256-generated value MUST result in the transaction being reverted by the caller. @param _operator The address which initiated the transfer (i.e. msg.sender) @param _from The address which previously owned the token @param _id The ID of the token being transferred @param _value The amount of tokens being transferred @param _data Additional data with no specified format @return `bytes4(keccak256("onCBC1155Received(address,address,uint256,uint256,bytes)"))` */ function onCBC1155Received(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4); /** @notice Handle the receipt of multiple CBC1155 token types. @dev An CBC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated. This function MUST return `bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` (i.e. 0x8f8588c9) if it accepts the transfer(s). This function MUST revert if it rejects the transfer(s). Return of any other value than the prescribed Core SHA3-256-generated value MUST result in the transaction being reverted by the caller. @param _operator The address which initiated the batch transfer (i.e. msg.sender) @param _from The address which previously owned the token @param _ids An array containing ids of each token being transferred (order and length must match _values array) @param _values An array containing amounts of each token being transferred (order and length must match _ids array) @param _data Additional data with no specified format @return `bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` */ function onCBC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4); } ``` ### Safe Transfer Rules To be more explicit about how the standard `safeTransferFrom` and `safeBatchTransferFrom` functions MUST operate with respect to the `CBC1155TokenReceiver` hook functions, a list of scenarios and rules follows. #### Scenarios **_Scenario#1 :_** The recipient is not a contract. * `onCBC1155Received` and `onCBC1155BatchReceived` MUST NOT be called on an EOA (Externally Owned Account). **_Scenario#2 :_** The transaction is not a mint/transfer of a token. * `onCBC1155Received` and `onCBC1155BatchReceived` MUST NOT be called outside of a mint or transfer process. **_Scenario#3 :_** The receiver does not implement the necessary `CBC1155TokenReceiver` interface function(s). * The transfer MUST be reverted with the one caveat below. * If the token(s) being sent are part of a hybrid implementation of another standard, that particular standard's rules on sending to a contract MAY now be followed instead. See "Backwards Compatibility" section. **_Scenario#4 :_** The receiver implements the necessary `CBC1155TokenReceiver` interface function(s) but returns an unknown value. * The transfer MUST be reverted. **_Scenario#5 :_** The receiver implements the necessary `CBC1155TokenReceiver` interface function(s) but throws an error. * The transfer MUST be reverted. **_Scenario#6 :_** The receiver implements the `CBC1155TokenReceiver` interface and is the recipient of one and only one balance change (e.g. `safeTransferFrom` called). * The balances for the transfer MUST have been updated before the `CBC1155TokenReceiver` hook is called on a recipient contract. * The transfer event MUST have been emitted to reflect the balance changes before the `CBC1155TokenReceiver` hook is called on the recipient contract. * One of `onCBC1155Received` or `onCBC1155BatchReceived` MUST be called on the recipient contract. * The `onCBC1155Received` hook SHOULD be called on the recipient contract and its rules followed. * See "onCBC1155Received rules" for further rules that MUST be followed. * The `onCBC1155BatchReceived` hook MAY be called on the recipient contract and its rules followed. * See "onCBC1155BatchReceived rules" for further rules that MUST be followed. **_Scenario#7 :_** The receiver implements the `CBC1155TokenReceiver` interface and is the recipient of more than one balance change (e.g. `safeBatchTransferFrom` called). * All balance transfers that are referenced in a call to an `CBC1155TokenReceiver` hook MUST be updated before the `CBC1155TokenReceiver` hook is called on the recipient contract. * All transfer events MUST have been emitted to reflect current balance changes before an `CBC1155TokenReceiver` hook is called on the recipient contract. * `onCBC1155Received` or `onCBC1155BatchReceived` MUST be called on the recipient as many times as necessary such that every balance change for the recipient in the scenario is accounted for. * The return magic value for every hook call MUST be checked and acted upon as per "onCBC1155Received rules" and "onCBC1155BatchReceived rules". * The `onCBC1155BatchReceived` hook SHOULD be called on the recipient contract and its rules followed. * See "onCBC1155BatchReceived rules" for further rules that MUST be followed. * The `onCBC1155Received` hook MAY be called on the recipient contract and its rules followed. * See "onCBC1155Received rules" for further rules that MUST be followed. **_Scenario#8 :_** You are the creator of a contract that implements the `CBC1155TokenReceiver` interface and you forward the token(s) onto another address in one or both of `onCBC1155Received` and `onCBC1155BatchReceived`. * Forwarding should be considered acceptance and then initiating a new `safeTransferFrom` or `safeBatchTransferFrom` in a new context. * The prescribed Core SHA3-256 acceptance value magic for the receiver hook being called MUST be returned after forwarding is successful. * The `_data` argument MAY be re-purposed for the new context. * If forwarding fails the transaction MAY be reverted. * If the contract logic wishes to keep the ownership of the token(s) itself in this case it MAY do so. **_Scenario#9 :_** You are transferring tokens via a non-standard API call i.e. an implementation specific API and NOT `safeTransferFrom` or `safeBatchTransferFrom`. * In this scenario all balance updates and events output rules are the same as if a standard transfer function had been called. * i.e. an external viewer MUST still be able to query the balance via a standard function and it MUST be identical to the balance as determined by `TransferSingle` and `TransferBatch` events alone. * If the receiver is a contract the `CBC1155TokenReceiver` hooks still need to be called on it and the return values respected the same as if a standard transfer function had been called. * However while the `safeTransferFrom` or `safeBatchTransferFrom` functions MUST revert if a receiving contract does not implement the `CBC1155TokenReceiver` interface, a non-standard function MAY proceed with the transfer. * See "Implementation specific transfer API rules". #### Rules **_safeTransferFrom rules:_** * Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section). * MUST revert if `_from` or `_to` is a zero address. * MUST revert if balance of holder for token `_id` is lower than the `_value` sent to the recipient. * MUST revert on any other error. * MUST emit the `TransferSingle` event to reflect the balance change (see "TransferSingle and TransferBatch event rules" section). * After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onCBC1155Received` on `_to` and act appropriately (see "onCBC1155Received rules" section). * The `_data` argument provided by the sender for the transfer MUST be passed with its contents unaltered to the `onCBC1155Received` hook function via its `_data` argument. **_safeBatchTransferFrom rules:_** * Caller must be approved to manage all the tokens being transferred out of the `_from` account (see "Approval" section). * MUST revert if `_from` or `_to` is a zero address. * MUST revert if length of `_ids` is not the same as length of `_values`. * MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient. * MUST revert on any other error. * MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "TransferSingle and TransferBatch event rules" section). * The balance changes and events MUST occur in the array order they were submitted (_ids[0]/_values[0] before _ids[1]/_values[1], etc). * After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onCBC1155Received` or `onCBC1155BatchReceived` on `_to` and act appropriately (see "onCBC1155Received and onCBC1155BatchReceived rules" section). * The `_data` argument provided by the sender for the transfer MUST be passed with its contents unaltered to the `CBC1155TokenReceiver` hook function(s) via their `_data` argument. **_TransferSingle and TransferBatch event rules:_** * `TransferSingle` SHOULD be used to indicate a single balance transfer has occurred between a `_from` and `_to` pair. * It MAY be emitted multiple times to indicate multiple balance changes in the transaction, but note that `TransferBatch` is designed for this to reduce energy consumption. * The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). * The `_from` argument MUST be the address of the holder whose balance is decreased. * The `_to` argument MUST be the address of the recipient whose balance is increased. * The `_id` argument MUST be the token type being transferred. * The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by. * When minting/creating tokens, the `_from` argument MUST be set to `address(0)`. See "Minting/creating and burning/destroying rules". * When burning/destroying tokens, the `_to` argument MUST be set to `address(0)`. See "Minting/creating and burning/destroying rules". * `TransferBatch` SHOULD be used to indicate multiple balance transfers have occurred between a `_from` and `_to` pair. * It MAY be emitted with a single element in the list to indicate a singular balance change in the transaction, but note that `TransferSingle` is designed for this to reduce energy consumption. * The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). * The `_from` argument MUST be the address of the holder whose balance is decreased for each entry pair in `_ids` and `_values`. * The `_to` argument MUST be the address of the recipient whose balance is increased for each entry pair in `_ids` and `_values`. * The `_ids` array argument MUST contain the ids of the tokens being transferred. * The `_values` array argument MUST contain the number of token to be transferred for each corresponding entry in `_ids`. * `_ids` and `_values` MUST have the same length. * When minting/creating tokens, the `_from` argument MUST be set to `address(0)`. See "Minting/creating and burning/destroying rules". * When burning/destroying tokens, the `_to` argument MUST be set to `address(0)`. See "Minting/creating and burning/destroying rules". * The total value transferred from `address(0)` minus the total value transferred to `address(0)` observed via the `TransferSingle` and `TransferBatch` events MAY be used by clients and exchanges to determine the "circulating supply" for a given token ID. * To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the `TransferSingle` event from `address(0)` to `address(0)`, with the token creator as `_operator`, and a `_value` of 0. * All `TransferSingle` and `TransferBatch` events MUST be emitted to reflect all the balance changes that have occurred before any call(s) to `onCBC1155Received` or `onCBC1155BatchReceived`. * To make sure event order is correct in the case of valid re-entry (e.g. if a receiver contract forwards tokens on receipt) state balance and events balance MUST match before calling an external contract. **_onCBC1155Received rules:_** * The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). * The `_from` argument MUST be the address of the holder whose balance is decreased. * `_from` MUST be `address(0)` for a mint. * The `_id` argument MUST be the token type being transferred. * The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by. * The `_data` argument MUST contain the information provided by the sender for the transfer with its contents unaltered. * i.e. it MUST pass on the unaltered `_data` argument sent via the `safeTransferFrom` or `safeBatchTransferFrom` call for this transfer. * The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("onCBC1155Received(address,address,uint256,uint256,bytes)"))` * If the return value is `bytes4(keccak256("onCBC1155Received(address,address,uint256,uint256,bytes)"))` the transfer MUST be completed or MUST revert if any other conditions are not met for success. * The recipient contract MAY reject an increase of its balance by calling revert. * If the recipient contract throws/reverts the transaction MUST be reverted. * If the return value is anything other than `bytes4(keccak256("onCBC1155Received(address,address,uint256,uint256,bytes)"))` the transaction MUST be reverted. * `onCBC1155Received` (and/or `onCBC1155BatchReceived`) MAY be called multiple times in a single transaction and the following requirements must be met: * All callbacks represent mutually exclusive balance changes. * The set of all calls to `onCBC1155Received` and `onCBC1155BatchReceived` describes all balance changes that occurred during the transaction in the order submitted. * A contract MAY skip calling the `onCBC1155Received` hook function if the transfer operation is transferring the token to itself. **_onCBC1155BatchReceived rules:_** * The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender). * The `_from` argument MUST be the address of the holder whose balance is decreased. * `_from` MUST be `address(0)` for a mint. * The `_ids` argument MUST be the list of tokens being transferred. * The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in `_ids`) the holder balance is decreased by and match what the recipient balance is increased by. * The `_data` argument MUST contain the information provided by the sender for the transfer with its contents unaltered. * i.e. it MUST pass on the unaltered `_data` argument sent via the `safeBatchTransferFrom` call for this transfer. * The recipient contract MAY accept an increase of its balance by returning the acceptance magic value `bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * If the return value is `bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transfer MUST be completed or MUST revert if any other conditions are not met for success. * The recipient contract MAY reject an increase of its balance by calling revert. * If the recipient contract throws/reverts the transaction MUST be reverted. * If the return value is anything other than `bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transaction MUST be reverted. * `onCBC1155BatchReceived` (and/or `onCBC1155Received`) MAY be called multiple times in a single transaction and the following requirements must be met: * All callbacks represent mutually exclusive balance changes. * The set of all calls to `onCBC1155Received` and `onCBC1155BatchReceived` describes all balance changes that occurred during the transaction in the order submitted. * A contract MAY skip calling the `onCBC1155BatchReceived` hook function if the transfer operation is transferring the token(s) to itself. **_CBC1155TokenReceiver CBC-165 rules:_** * The implementation of the CBC-165 `supportsInterface` function SHOULD be as follows: ```solidity function supportsInterface(bytes4 interfaceID) external view returns (bool) { return interfaceID == 0x80ada41b || // CBC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`). interfaceID == 0xc08443c9; // CBC-1155 `CBC1155TokenReceiver` support (i.e. `bytes4(keccak256("onCBC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). } ``` * The implementation MAY differ from the above but: * It MUST return the constant value `true` if `0x80ada41b` is passed through the `interfaceID` argument. This signifies CBC-165 support. * It MUST return the constant value `true` if `0xc08443c9` is passed through the `interfaceID` argument. This signifies CBC-1155 `CBC1155TokenReceiver` support. * It MUST NOT consume more than 10,000 energy. * This keeps it below the CBC-165 requirement of 30,000 energy, reduces the energy reserve needs and minimises possible side-effects of energy exhaustion during the call. **_Implementation specific transfer API rules:_** * If an implementation specific API function is used to transfer CBC-1155 token(s) to a contract, the `safeTransferFrom` or `safeBatchTransferFrom` (as appropriate) rules MUST still be followed if the receiver implements the `CBC1155TokenReceiver` interface. If it does not the non-standard implementation SHOULD revert but MAY proceed. * An example: 1. An approved user calls a function such as `function myTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values);`. 2. `myTransferFrom` updates the balances for `_from` and `_to` addresses for all `_ids` and `_values`. 3. `myTransferFrom` emits `TransferBatch` with the details of what was transferred from address `_from` to address `_to`. 4. `myTransferFrom` checks if `_to` is a contract address and determines that it is so (if not, then the transfer can be considered successful). 5. `myTransferFrom` calls `onCBC1155BatchReceived` on `_to` and it reverts or returns an unknown value (if it had returned `bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` the transfer can be considered successful). 6. At this point `myTransferFrom` SHOULD revert the transaction immediately as receipt of the token(s) was not explicitly accepted by the `onCBC1155BatchReceived` function. 7. If however `myTransferFrom` wishes to continue it MUST call `supportsInterface(0xc08443c9)` on `_to` and if it returns the constant value `true` the transaction MUST be reverted, as it is now known to be a valid receiver and the previous acceptance step failed. * NOTE: You could have called `supportsInterface(0xc08443c9)` at a previous step if you wanted to gather and act upon that information earlier, such as in a hybrid standards scenario. 8. If the above call to `supportsInterface(0xc08443c9)` on `_to` reverts or returns a value other than the constant value `true` the `myTransferFrom` function MAY consider this transfer successful. * NOTE: this MAY result in unrecoverable tokens if sent to an address that does not expect to receive CBC-1155 tokens. * The above example is not exhaustive but illustrates the major points (and shows that most are shared with `safeTransferFrom` and `safeBatchTransferFrom`): * Balances that are updated MUST have equivalent transfer events emitted. * A receiver address has to be checked if it is a contract and if so relevant `CBC1155TokenReceiver` hook function(s) have to be called on it. * Balances (and events associated) that are referenced in a call to an `CBC1155TokenReceiver` hook MUST be updated (and emitted) before the `CBC1155TokenReceiver` hook is called. * The return values of the `CBC1155TokenReceiver` hook functions that are called MUST be respected if they are implemented. * Only non-standard transfer functions MAY allow tokens to be sent to a recipient contract that does NOT implement the necessary `CBC1155TokenReceiver` hook functions. `safeTransferFrom` and `safeBatchTransferFrom` MUST revert in that case (unless it is a hybrid standards implementation see "Backwards Compatibility"). **_Minting/creating and burning/destroying rules:_** * A mint/create operation is essentially a specialized transfer and MUST follow these rules: * The recipient MUST be neither `address(0)` nor `Checksum.zeroAddress()`. * To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the `TransferSingle` event from `address(0)` to `address(0)`, with the token creator as `_operator`, and a `_value` of 0. * The "TransferSingle and TransferBatch event rules" MUST be followed as appropriate for the mint(s) (i.e. singles or batches) however the `_from` argument MUST be set to `address(0)` to flag the transfer as a mint to contract observers. * NOTE: This includes tokens that are given an initial balance in the contract. The balance of the contract MUST also be able to be determined by events alone meaning initial contract balances (for eg. in construction) MUST emit events to reflect those balances too. * A burn/destroy operation is essentially a specialized transfer and MUST follow these rules: * The holder MUST be neither `address(0)` nor `Checksum.zeroAddress()`. * The "TransferSingle and TransferBatch event rules" MUST be followed as appropriate for the burn(s) (i.e. singles or batches) however the `_to` argument MUST be set to `address(0)` to flag the transfer as a burn to contract observers. * When burning/destroying you do not have to actually transfer to `address(0)` (that is implementation-specific), only the `_to` argument in the event MUST be set to `address(0)` as above. * The total value transferred from `address(0)` minus the total value transferred to `address(0)` observed via the `TransferSingle` and `TransferBatch` events MAY be used by clients and exchanges to determine the "circulating supply" for a given token ID. * As mentioned above mint/create and burn/destroy operations are specialized transfers and so will likely be accomplished with custom transfer functions rather than `safeTransferFrom` or `safeBatchTransferFrom`. If so the "Implementation specific transfer API rules" section would be appropriate. * Even in a non-safe API and/or hybrid standards case the above event rules MUST still be adhered to when minting/creating or burning/destroying. * A contract MAY skip calling the `CBC1155TokenReceiver` hook function(s) if the mint operation is transferring the token(s) to itself. In all other cases the `CBC1155TokenReceiver` rules MUST be followed as appropriate for the implementation (i.e. safe, custom and/or hybrid). ##### A Ylem example of the Core SHA3-256-generated constants for the various magic values (these MAY be used by implementation) ```solidity bytes4 constant public CBC1155_CBC165 = 0x321439a9; // CBC-165 identifier for the main token standard. bytes4 constant public CBC1155_CBC165_TOKENRECEIVER = 0xc08443c9; // CBC-165 identifier for the `CBC1155TokenReceiver` support (i.e. `bytes4(keccak256("onCBC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). bytes4 constant public CBC1155_ACCEPTED = 0x4f01cb00; // Return value from `onCBC1155Received` call if a contract accepts receipt (i.e `bytes4(keccak256("onCBC1155Received(address,address,uint256,uint256,bytes)"))`). bytes4 constant public CBC1155_BATCH_ACCEPTED = 0x8f8588c9; // Return value from `onCBC1155BatchReceived` call if a contract accepts receipt (i.e `bytes4(keccak256("onCBC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). ``` ### Metadata The URI value allows for ID substitution by clients. If the string `{id}` exists in any URI, clients MUST replace this with the actual token ID in hexadecimal form. This allows for a large number of tokens to use the same on-chain string by defining a URI once, for that large number of tokens. * The string format of the substituted hexadecimal ID MUST be lowercase alphanumeric: `[0-9a-f]` with no 0x prefix. * The string format of the substituted hexadecimal ID MUST be leading zero padded to 64 hex characters length if necessary. Example of such a URI: `https://token-cdn-domain/{id}.json` would be replaced with `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` if the client is referring to token ID 314592/0x4CCE0. #### Metadata Extensions The optional `CBC1155Metadata_URI` extension can be identified with the CBC-165 Standard Interface Detection. If the optional `CBC1155Metadata_URI` extension is included: * The CBC-165 `supportsInterface` function MUST return the constant value `true` if `0x7ed7f64f` is passed through the `interfaceID` argument. * _Changes_ to the URI MUST emit the `URI` event if the change can be expressed with an event (i.e. it isn't dynamic/programmatic). * An implementation MAY emit the `URI` event during a mint operation but it is NOT mandatory. An observer MAY fetch the metadata uri at mint time from the `uri` function if it was not emitted. * The `uri` function SHOULD be used to retrieve values if no event was emitted. * The `uri` function MUST return the same value as the latest event for an `_id` if it was emitted. * The `uri` function MUST NOT be used to check for the existence of a token as it is possible for an implementation to return a valid string even if the token does not exist. ```solidity pragma solidity ^1.1.2; /** Note: The CBC-165 identifier for this interface is 0x7ed7f64f. */ interface CBC1155Metadata_URI { /** @notice A distinct Uniform Resource Identifier (URI) for a given token. @dev URIs are defined in RFC 3986. The URI MUST point to a JSON file that conforms to the "CBC-1155 Metadata URI JSON Schema". @return URI string */ function uri(uint256 _id) external view returns (string memory); } ``` #### CBC-1155 Metadata URI JSON Schema This JSON schema is loosely based on the "CBC721 Metadata JSON Schema", but includes optional formatting to allow for ID substitution by clients. If the string `{id}` exists in any JSON value, it MUST be replaced with the actual token ID, by all client software that follows this standard. * The string format of the substituted hexadecimal ID MUST be lowercase alphanumeric: `[0-9a-f]` with no 0x prefix. * The string format of the substituted hexadecimal ID MUST be leading zero padded to 64 hex characters length if necessary. ```json { "title": "Token Metadata", "type": "object", "properties": { "name": { "type": "string", "description": "Identifies the asset to which this token represents" }, "decimals": { "type": "integer", "description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation." }, "description": { "type": "string", "description": "Describes the asset to which this token represents" }, "image": { "type": "string", "description": "A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." }, "properties": { "type": "object", "description": "Arbitrary properties. Values may be strings, numbers, object or arrays." } } } ``` An example of an CBC-1155 Metadata JSON file follows. The properties array proposes some SUGGESTED formatting for token-specific display properties and metadata. ```json { "name": "Asset Name", "description": "Lorem ipsum...", "image": "https:\/\/s3.amazonaws.com\/your-bucket\/images\/{id}.png", "properties": { "simple_property": "example value", "rich_property": { "name": "Name", "value": "123", "display_value": "123 Example Value", "class": "emphasis", "css": { "color": "#ffffff", "font-weight": "bold", "text-decoration": "underline" } }, "array_property": { "name": "Name", "value": [1,2,3,4], "class": "emphasis" } } } ``` ##### Localization Metadata localization should be standardized to increase presentation uniformity across all languages. As such, a simple overlay method is proposed to enable localization. If the metadata JSON file contains a `localization` attribute, its content MAY be used to provide localized values for fields that need it. The `localization` attribute should be a sub-object with three attributes: `uri`, `default` and `locales`. If the string `{locale}` exists in any URI, it MUST be replaced with the chosen locale by all client software. ##### JSON Schema ```json { "title": "Token Metadata", "type": "object", "properties": { "name": { "type": "string", "description": "Identifies the asset to which this token represents", }, "decimals": { "type": "integer", "description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation." }, "description": { "type": "string", "description": "Describes the asset to which this token represents" }, "image": { "type": "string", "description": "A URI pointing to a resource with mime type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive." }, "properties": { "type": "object", "description": "Arbitrary properties. Values may be strings, numbers, object or arrays.", }, "localization": { "type": "object", "required": ["uri", "default", "locales"], "properties": { "uri": { "type": "string", "description": "The URI pattern to fetch localized data from. This URI should contain the substring `{locale}` which will be replaced with the appropriate locale value before sending the request." }, "default": { "type": "string", "description": "The locale of the default data within the base JSON" }, "locales": { "type": "array", "description": "The list of locales for which data is available. These locales should conform to those defined in the Unicode Common Locale Data Repository (http://cldr.unicode.org/)." } } } } } ``` ##### Localized Sample Base URI: ```json { "name": "Advertising Space", "description": "Each token represents a unique Ad space in the city.", "localization": { "uri": "ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/{locale}.json", "default": "en", "locales": ["en", "es", "fr"] } } ``` es.json: ```json { "name": "Espacio Publicitario", "description": "Cada token representa un espacio publicitario único en la ciudad." } ``` fr.json: ```json { "name": "Espace Publicitaire", "description": "Chaque jeton représente un espace publicitaire unique dans la ville." } ``` ### Approval The function `setApprovalForAll` allows an operator to manage one's entire set of tokens on behalf of the approver. To permit approval of a subset of token IDs, an interface such as CBC-1761 Scoped Approval Interface is suggested. The counterpart `isApprovedForAll` provides introspection into any status set by `setApprovalForAll`. An owner SHOULD be assumed to always be able to operate on their own tokens regardless of approval status, so should SHOULD NOT have to call `setApprovalForAll` to approve themselves as an operator before they can operate on them. ## Rationale ### Metadata Choices The `symbol` function (found in the CBC-20 and CBC-721 standards) was not included as we do not believe this is a globally useful piece of data to identify a generic virtual item / asset and are also prone to collisions. Short-hand symbols are used in tickers and currency trading, but they aren't as useful outside of that space. The `name` function (for human-readable asset names, on-chain) was removed from the standard to allow the Metadata JSON to be the definitive asset name and reduce duplication of data. This also allows localization for names, which would otherwise be prohibitively expensive if each language string was stored on-chain, not to mention bloating the standard interface. While this decision may add a small burden on implementers to host a JSON file containing metadata, we believe any serious implementation of CBC-1155 will already utilize JSON Metadata. ### Upgrades The requirement to emit `TransferSingle` or `TransferBatch` on balance change implies that a valid implementation of CBC-1155 redeploying to a new contract address MUST emit events from the new contract address to replicate the deprecated contract final state. It is valid to only emit a minimal number of events to reflect only the final balance and omit all the transactions that led to that state. The event emit requirement is to ensure that the current state of the contract can always be traced only through events. To alleviate the need to emit events when changing contract address, consider using the proxy pattern, such as described in CIP-2535. This will also have the added benefit of providing a stable contract address for users. ### Design decision: Supporting non-batch The standard supports `safeTransferFrom` and `onCBC1155Received` functions because they are significantly cheaper for single token-type transfers, which is arguably a common use case. ### Design decision: Safe transfers only The standard only supports safe-style transfers, making it possible for receiver contracts to depend on `onCBC1155Received` or `onCBC1155BatchReceived` function to be always called at the end of a transfer. ### Guaranteed log trace As the Core ecosystem continues to grow, many dapps are relying on traditional databases and explorer API services to retrieve and categorize data. The CBC-1155 standard guarantees that event logs emitted by the smart contract will provide enough data to create an accurate record of all current token balances. A database or explorer may listen to events and be able to provide indexed and categorized searches of every CBC-1155 token in the contract. ### Approver The function `setApprovalForAll` allows an operator to manage one's entire set of tokens on behalf of the approver. It enables frictionless interaction with exchange and trade contracts. Restricting approval to a certain set of token IDs, quantities or other rules MAY be done with an additional interface or an external contract. The rationale is to keep the CBC-1155 standard as generic as possible for all use-cases without imposing a specific approval scheme on implementations that may not need it. Standard token approval interfaces can be used, such as the suggested CBC-1761 Scoped Approval Interface which is compatible with CBC-1155. ## Backwards Compatibility There have been requirements during the design discussions to have this standard be compatible with existing standards when sending to contract addresses, specifically CBC-721 at time of writing. To cater for this scenario, there is some leeway with the revert logic should a contract not implement the `CBC1155TokenReceiver` as per "Safe Transfer Rules" section above, specifically "Scenario#3 : The receiver does not implement the necessary `CBC1155TokenReceiver` interface function(s)". Hence in a hybrid CBC-1155 contract implementation an extra call MUST be made on the recipient contract and checked before any hook calls to `onCBC1155Received` or `onCBC1155BatchReceived` are made. Order of operation MUST therefore be: 1. The implementation MUST call the function `supportsInterface(0xc08443c9)` on the recipient contract, providing at least 10,000 energy. 2. If the function call succeeds and the return value is the constant value `true` the implementation proceeds as a regular CBC-1155 implementation, with the call(s) to the `onCBC1155Received` or `onCBC1155BatchReceived` hooks and rules associated. 3. If the function call fails or the return value is NOT the constant value `true` the implementation can assume the recipient contract is not an `CBC1155TokenReceiver` and follow its other standard's rules for transfers. Note that a pure implementation of a single standard is recommended rather than a hybrid solution, but an example of a hybrid CBC-1155/CBC-721 contract is linked in the references section under implementations. An important consideration is that even if the tokens are sent with another standard's rules the CBC-1155 transfer events MUST still be emitted. This is so the balances can still be determined via events alone as per CBC-1155 standard rules. ## Usage This standard can be used to represent multiple token types for an entire domain. Both fungible and non-fungible tokens can be stored in the same smart-contract. ### Batch Transfers The `safeBatchTransferFrom` function allows for batch transfers of multiple token IDs and values. The design of CBC-1155 makes batch transfers possible without the need for a wrapper contract, as with existing token standards. This reduces energy costs when more than one token type is included in a batch transfer, as compared to single transfers with multiple transactions. Another advantage of standardized batch transfers is the ability for a smart contract to respond to the batch transfer in a single operation using `onCBC1155BatchReceived`. It is RECOMMENDED that clients and wallets sort the token IDs and associated values (in ascending order) when posting a batch transfer, as some CBC-1155 implementations offer significant energy cost savings when IDs are sorted. ### Batch Balance The `balanceOfBatch` function allows clients to retrieve balances of multiple owners and token IDs with a single call. ### Enumerating from events In order to keep storage requirements light for contracts implementing CBC-1155, enumeration (discovering the IDs and values of tokens) must be done using event logs. It is RECOMMENDED that clients such as exchanges and blockchain explorers maintain a local database containing the token ID, Supply, and URI at the minimum. This can be built from each TransferSingle, TransferBatch, and URI event, starting from the block the smart contract was deployed until the latest block. CBC-1155 contracts must therefore carefully emit `TransferSingle` or `TransferBatch` events in any instance where tokens are created, minted, transferred or destroyed. ### Non-Fungible Tokens The following strategies are examples of how you MAY mix fungible and non-fungible tokens together in the same contract. The standard does NOT mandate how an implementation must do this. #### Split ID bits The top 128 bits of the uint256 `_id` parameter in any CBC-1155 function MAY represent the base token ID, while the bottom 128 bits MAY represent the index of the non-fungible to make it unique. Non-fungible tokens can be interacted with using an index based accessor into the contract/token data set. Therefore to access a particular token set within a mixed data contract and a particular non-fungible within that set, `_id` could be passed as ``. To identify a non-fungible set/category as a whole (or a fungible) you COULD just pass in the base id via the `_id` argument as ``. If your implementation uses this technique this naturally means the index of a non-fungible SHOULD be 1-based. Inside the contract code the two pieces of data needed to access the individual non-fungible can be extracted with uint128(~0) and the same mask shifted by 128. ```solidity uint256 baseTokenNFT = 12345 << 128; uint128 indexNFT = 50; uint256 baseTokenFT = 54321 << 128; balanceOf(msg.sender, baseTokenNFT); // Get balance of the base token for non-fungible set 12345 (this MAY be used to get balance of the user for all of this token set if the implementation wishes as a convenience). balanceOf(msg.sender, baseTokenNFT + indexNFT); // Get balance of the token at index 50 for non-fungible set 12345 (should be 1 if user owns the individual non-fungible token or 0 if they do not). balanceOf(msg.sender, baseTokenFT); // Get balance of the fungible base token 54321. ``` Note that 128 is an arbitrary number, an implementation MAY choose how they would like this split to occur as suitable for their use case. An observer of the contract would simply see events showing balance transfers and mints happening and MAY track the balances using that information alone. For an observer to be able to determine type (non-fungible or fungible) from an ID alone they would have to know the split ID bits format on a implementation by implementation basis. ##### Natural Non-Fungible tokens Another simple way to represent non-fungibles is to allow a maximum value of 1 for each non-fungible token. This would naturally mirror the real world, where unique items have a quantity of 1 and fungible items have a quantity greater than 1. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Scoped Approval Interface Source: https://cip.coreblockchain.net/cip/cbc/cip-1761/ Repository path: cip/cbc/cip-1761.md A standardized smart contract interface for restricted (scoped) approval of token IDs, enabling fine-grained operator permissions for CBC-1155, CBC-721, and similar token contracts. ## Abstract This standard defines a common interface for restricted (scoped) approval in token contracts with an "ID" domain, such as CBC-1155 or CBC-721. CBC-1761 enables fine-grained operator permissions by defining "scopes" of one or more token IDs, allowing users to grant or revoke operator rights for specific groups of tokens. This improves security and flexibility for asset management, gaming, and enterprise use cases. ## Motivation Traditional approval mechanisms in CBC-1155 and CBC-721 allow operators to manage all of a user's tokens, which can lead to excessive risk if the operator is compromised. CBC-1761 introduces the concept of scopes, enabling users to restrict operator permissions to specific token groups or domains. This reduces risk and enables new use cases, such as regional asset management, game developer collaboration, and differentiated access for high- and low-value tokens. ## Specification ### Interface ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; /** * Note: The CBC-165 identifier for this interface is 0x00986e51. */ interface ICBC1761 { // --- Events --- event ApprovalForScope(address indexed owner, address indexed operator, bytes32 indexed scope, bool approved); event IdsAddedToScope(uint256 indexed idStart, uint256 indexed idEnd, bytes32 indexed scope); event IdsRemovedFromScope(uint256 indexed idStart, uint256 indexed idEnd, bytes32 indexed scope); event ScopeURI(string value, bytes32 indexed scope); // --- Core Functions --- function scopeCountForId(uint256 id) external view returns (uint32); function scopeForId(uint256 id, uint32 scopeIndex) external view returns (bytes32); function scopeUri(bytes32 scope) external view returns (string memory); function setApprovalForScope(address operator, bytes32 scope, bool approved) external; function isApprovedForScope(address owner, address operator, bytes32 scope) external view returns (bool); } ``` ### Scope Metadata JSON Schema This schema allows for localization and human-readable scope metadata. ```json { "title": "Scope Metadata", "type": "object", "required": ["name"], "properties": { "name": { "type": "string", "description": "Identifies the scope in a human-readable way." }, "description": { "type": "string", "description": "Describes the scope to allow users to make informed approval decisions." }, "localization": { "type": "object", "required": ["uri", "default", "locales"], "properties": { "uri": { "type": "string", "description": "The URI pattern to fetch localized data from. This URI should contain the substring {locale}." }, "default": { "type": "string", "description": "The locale of the default data within the base JSON." }, "locales": { "type": "array", "description": "The list of locales for which data is available." } } } } } ``` ### Method Behaviors - Implementations MAY define a fixed or dynamic set of scopes. - Scopes can be used to group token IDs by region, type, or any custom logic. - The `setApprovalForScope` function MUST emit the `ApprovalForScope` event. - Adding or removing IDs from a scope MUST emit the corresponding events. - The `scopeUri` function SHOULD return a valid URI for scope metadata, or an empty string if not available. ### Localization Metadata localization should be standardized to increase presentation uniformity across all languages. As such, a simple overlay method is proposed to enable localization. If the metadata JSON file contains a `localization` attribute, its content may be used to provide localized values for fields that need it. The `localization` attribute should be a sub-object with three attributes: `uri`, `default` and `locales`. If the string `{locale}` exists in any URI, it MUST be replaced with the chosen locale by all client software. ## Rationale CBC-1761 generalizes approval mechanisms for multi-token contracts, enabling safer and more flexible operator permissions. By supporting scopes, users can delegate control over specific assets without exposing their entire portfolio, and developers can build more granular access control systems for games, enterprises, and asset platforms. ### Metadata JSON The Scope Metadata JSON Schema was added in order to support human-readable scope names and descriptions in more than one language. ## Security Considerations - Implementers must ensure that scope assignment and approval logic is correct and cannot be bypassed. - Scopes should be clearly defined and documented to avoid user confusion. - Metadata URIs should be immutable or versioned to prevent tampering. - Access control and upgradability best practices should be followed. ## References - [JSON Schema](https://json-schema.org/) ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Pseudo-introspection Registry Contract Source: https://cip.coreblockchain.net/cip/cbc/cip-1820/ Repository path: cip/cbc/cip-1820.md A universal registry smart contract where any address (contract or regular account) can register which interface it supports and which smart contract is responsible for its implementation. This standard keeps backward compatibility with CBC-165. ## Abstract This standard defines a registry where smart contracts and regular accounts can publish which functionality they implement---either directly or through a proxy contract. Anyone can query this registry to ask if a specific address implements a given interface and which smart contract handles its implementation. This registry MAY be deployed on any Core network. Its network-specific ICAN address MUST be published for every deployment. Interfaces with zeroes (`0`) as the last 28 bytes are considered CBC-165 interfaces, and this registry SHALL forward the call to the contract to see if it implements the interface. This contract also acts as an CBC-165 cache to reduce energy consumption. ## Motivation Earlier pseudo-introspection approaches include CBC-165, which cannot be used by regular accounts, and reverse-name registries, which add complexity and depend on registry governance. This standard provides a simpler common lookup mechanism for contracts and regular accounts. Clients resolve the registry through its published network-specific Core address. ## Specification ### CBC-1820 Registry Smart Contract > This is the normative reference implementation of the CBC-1820 registry smart contract. ``` solidity /* CBC1820 Pseudo-introspection Registry Contract * This standard defines a universal registry smart contract where any address (contract or regular account) can * register which interface it supports and which smart contract is responsible for its implementation. * * Written in 2019 by Jordi Baylina and Jacques Dafflon * * To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to * this software to the public domain worldwide. This software is distributed without any warranty. * * You should have received a copy of the CC0 Public Domain Dedication along with this software. */ pragma solidity ^1.1.2; /// @dev The interface a contract MUST implement if it is the implementer of /// some (other) interface for any address other than itself. interface CBC1820ImplementerInterface { /// @notice Indicates whether the contract implements the interface 'interfaceHash' for the address 'addr' or not. /// @param interfaceHash keccak256 hash of the name of the interface /// @param addr Address for which the contract will implement the interface /// @return CBC1820_ACCEPT_MAGIC only if the contract implements 'interfaceHash' for the address 'addr'. function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32); } /// @title CBC1820 Pseudo-introspection Registry Contract /// @author Jordi Baylina and Jacques Dafflon /// @notice This contract is the official implementation of the CBC1820 Registry. contract CBC1820Registry { /// @notice CBC165 Invalid ID. bytes4 constant internal INVALID_ID = 0xffffffff; /// @notice Method ID for the CBC165 supportsInterface method (= `bytes4(keccak256('supportsInterface(bytes4)'))`). bytes4 constant internal CBC165ID = 0x80ada41b; /// @notice Magic value which is returned if a contract implements an interface on behalf of some other address. bytes32 constant internal CBC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("CBC1820_ACCEPT_MAGIC")); /// @notice mapping from addresses and interface hashes to their implementers. mapping(address => mapping(bytes32 => address)) internal interfaces; /// @notice mapping from addresses to their manager. mapping(address => address) internal managers; /// @notice flag for each address and CBC165 interface to indicate if it is cached. mapping(address => mapping(bytes4 => bool)) internal cbc165Cached; /// @notice Indicates a contract is the 'implementer' of 'interfaceHash' for 'addr'. event InterfaceImplementerSet(address indexed addr, bytes32 indexed interfaceHash, address indexed implementer); /// @notice Indicates 'newManager' is the address of the new manager for 'addr'. event ManagerChanged(address indexed addr, address indexed newManager); /// @notice Query if an address implements an interface and through which contract. /// @param _addr Address being queried for the implementer of an interface. /// (If '_addr' is the zero address then 'msg.sender' is assumed.) /// @param _interfaceHash Core SHA3-256 hash of the name of the interface as a string. /// E.g., 'web3.utils.keccak256("CBC777TokensRecipient")' for the 'CBC777TokensRecipient' interface. /// @return The address of the contract which implements the interface '_interfaceHash' for '_addr' /// or '0' if '_addr' did not register an implementer for this interface. function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) { address addr = _addr == address(0) ? msg.sender : _addr; if (isCBC165Interface(_interfaceHash)) { bytes4 cbc165InterfaceHash = bytes4(_interfaceHash); return implementsCBC165Interface(addr, cbc165InterfaceHash) ? addr : address(0); } return interfaces[addr][_interfaceHash]; } /// @notice Sets the contract which implements a specific interface for an address. /// Only the manager defined for that address can set it. /// (Each address is the manager for itself until it sets a new manager.) /// @param _addr Address for which to set the interface. /// (If '_addr' is the zero address then 'msg.sender' is assumed.) /// @param _interfaceHash Core SHA3-256 hash of the name of the interface as a string. /// E.g., 'web3.utils.keccak256("CBC777TokensRecipient")' for the 'CBC777TokensRecipient' interface. /// @param _implementer Contract address implementing '_interfaceHash' for '_addr'. function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external { address addr = _addr == address(0) ? msg.sender : _addr; require(getManager(addr) == msg.sender, "Not the manager"); require(!isCBC165Interface(_interfaceHash), "Must not be an CBC165 hash"); if (_implementer != address(0) && _implementer != msg.sender) { require( CBC1820ImplementerInterface(_implementer) .canImplementInterfaceForAddress(_interfaceHash, addr) == CBC1820_ACCEPT_MAGIC, "Does not implement the interface" ); } interfaces[addr][_interfaceHash] = _implementer; emit InterfaceImplementerSet(addr, _interfaceHash, _implementer); } /// @notice Sets '_newManager' as manager for '_addr'. /// The new manager will be able to call 'setInterfaceImplementer' for '_addr'. /// @param _addr Address for which to set the new manager. /// @param _newManager Address of the new manager for 'addr'. (Pass '0x0' to reset the manager to '_addr'.) function setManager(address _addr, address _newManager) external { require(getManager(_addr) == msg.sender, "Not the manager"); managers[_addr] = _newManager == _addr ? address(0) : _newManager; emit ManagerChanged(_addr, _newManager); } /// @notice Get the manager of an address. /// @param _addr Address for which to return the manager. /// @return Address of the manager for a given address. function getManager(address _addr) public view returns(address) { // By default the manager of an address is the same address if (managers[_addr] == address(0)) { return _addr; } else { return managers[_addr]; } } /// @notice Compute the keccak256 hash of an interface given its name. /// @param _interfaceName Name of the interface. /// @return The keccak256 hash of an interface name. function interfaceHash(string calldata _interfaceName) external pure returns(bytes32) { return keccak256(abi.encodePacked(_interfaceName)); } /* --- CBC165 Related Functions --- */ /* --- Developed in collaboration with William Entriken. --- */ /// @notice Updates the cache with whether the contract implements an CBC165 interface or not. /// @param _contract Address of the contract for which to update the cache. /// @param _interfaceId CBC165 interface for which to update the cache. function updateCBC165Cache(address _contract, bytes4 _interfaceId) external { interfaces[_contract][_interfaceId] = implementsCBC165InterfaceNoCache( _contract, _interfaceId) ? _contract : address(0); cbc165Cached[_contract][_interfaceId] = true; } /// @notice Checks whether a contract implements an CBC165 interface or not. // If the result is not cached a direct lookup on the contract address is performed. // If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling // 'updateCBC165Cache' with the contract address. /// @param _contract Address of the contract to check. /// @param _interfaceId CBC165 interface to check. /// @return True if '_contract' implements '_interfaceId', false otherwise. function implementsCBC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) { if (!cbc165Cached[_contract][_interfaceId]) { return implementsCBC165InterfaceNoCache(_contract, _interfaceId); } return interfaces[_contract][_interfaceId] == _contract; } /// @notice Checks whether a contract implements an CBC165 interface or not without using nor updating the cache. /// @param _contract Address of the contract to check. /// @param _interfaceId CBC165 interface to check. /// @return True if '_contract' implements '_interfaceId', false otherwise. function implementsCBC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) { uint256 success; uint256 result; (success, result) = noThrowCall(_contract, CBC165ID); if (success == 0 || result == 0) { return false; } (success, result) = noThrowCall(_contract, INVALID_ID); if (success == 0 || result != 0) { return false; } (success, result) = noThrowCall(_contract, _interfaceId); if (success == 1 && result == 1) { return true; } return false; } /// @notice Checks whether the hash is a CBC165 interface (ending with 28 zeroes) or not. /// @param _interfaceHash The hash to check. /// @return True if '_interfaceHash' is an CBC165 interface (ending with 28 zeroes), false otherwise. function isCBC165Interface(bytes32 _interfaceHash) internal pure returns (bool) { return _interfaceHash & 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0; } /// @dev Make a call on a contract without throwing if the function does not exist. function noThrowCall(address _contract, bytes4 _interfaceId) internal view returns (uint256 success, uint256 result) { bytes4 cbc165ID = CBC165ID; assembly { let x := mload(0x40) // Find empty storage location using "free memory pointer" mstore(x, cbc165ID) // Place signature at beginning of empty storage mstore(add(x, 0x04), _interfaceId) // Place first argument directly next to signature success := staticcall( 30000, // 30k energy _contract, // To addr x, // Inputs are stored at location x 0x24, // Inputs are 36 (4 + 32) bytes long x, // Store output over input (saves space) 0x20 // Outputs are 32 bytes long ) result := mload(x) // Load the result } } } ``` ### Deployment Method The Ethereum ERC-1820 deployment transaction and Nick's method based on recoverable ECDSA signatures cannot be reused on Core Blockchain. Core transactions use a 171-byte Ed448 signature blob that includes the public key, so the original sender-recovery construction does not apply. The CBC-1820 registry MUST be deployed separately on each Core network from the Ylem source in this specification. A canonical deployment MUST: 1. Compile the registry with Ylem `^1.1.2`. 2. Use the Core transaction format, the target `networkId`, and an address with the matching network prefix. 3. Fund the deployment account with enough XCB to cover the current `energyPrice` and deployment `energyLimit`; this standard does not prescribe a fixed amount. 4. Publish the verified runtime bytecode, deployment transaction hash, and registry ICAN address for that network. Until canonical deployments are published, clients MUST treat the registry address as network configuration and MUST NOT assume that one address is valid across all Core networks. ### Registry Contract Address No canonical CBC-1820 registry address or Core deployment transaction is currently specified by this document. These values MUST be added for each supported Core network before the registry can be relied upon as shared infrastructure. Compiler metadata and deployment bytecode are not embedded in this document. Canonical artifacts MUST be generated from the Ylem source above and published with each network deployment. ### Interface Name Any interface name is hashed using `keccak256` and sent to `getInterfaceImplementer()`. If the interface is part of a standard, it is best practice to explicitly state the interface name and link to this published CBC-1820 such that other people don't have to come here to look up these rules. For convenience, the registry provides a function to compute the hash on-chain: ``` solidity function interfaceHash(string _interfaceName) public pure returns(bytes32) ``` Compute the keccak256 hash of an interface given its name. > **identifier:** `408aa674` > **parameters** > **_interfaceName**: Name of the interface. > **returns:** The `keccak256` hash of an interface name. #### Approved CBCs If the interface is part of an approved CBC, it MUST be named `CBC###XXXXX` where `###` is the number of the CBC and XXXXX should be the name of the interface in CamelCase. The meaning of this interface SHOULD be defined in the specified CBC. Examples: - `keccak256("CBC20Token")` - `keccak256("CBC777Token")` - `keccak256("CBC777TokensSender")` - `keccak256("CBC777TokensRecipient")` #### CBC-165 Compatible Interfaces > The compatibility with CBC-165, including the CBC-165 Cache, has been designed and developed with William Entriken. Any interface where the last 28 bytes are zeroes (`0`) SHALL be considered an CBC-165 interface. ##### CBC-165 Lookup Anyone can explicitly check if a contract implements an CBC-165 interface using the registry by calling one of the two functions below: ``` solidity function implementsCBC165Interface(address _contract, bytes4 _interfaceId) public view returns (bool) ``` Checks whether a contract implements an CBC-165 interface or not. If the result is not cached a direct lookup on the contract address is performed. *NOTE*: If the result is not cached or the cached value is out-of-date, the cache MUST be updated manually by calling `updateCBC165Cache` with the contract address. (See CBC-165 Cache for more details.) > **identifier:** `6d99e76a` > **parameters** > **_contract**: Address of the contract to check. > **_interfaceId**: CBC-165 interface to check. > **returns:** `true` if `_contract` implements `_interfaceId`, `false` otherwise. ``` solidity function implementsCBC165InterfaceNoCache(address _contract, bytes4 _interfaceId) public view returns (bool) ``` Checks whether a contract implements an CBC-165 interface or not without using nor updating the cache. > **identifier:** `ee399005` > **parameters** > **_contract**: Address of the contract to check. > **_interfaceId**: CBC-165 interface to check. > **returns:** `true` if `_contract` implements `_interfaceId`, false otherwise. #### CBC-165 Cache Whether a contract implements an CBC-165 interface or not can be cached manually to save energy. If a contract dynamically changes its interface and relies on the CBC-165 cache of the CBC-1820 registry, the cache MUST be updated manually - there is no automatic cache invalidation or cache update. Ideally the contract SHOULD automatically update the cache when changing its interface. However anyone MAY update the cache on the contract's behalf. The cache update MUST be done using the `updateCBC165Cache` function: ``` solidity function updateCBC165Cache(address _contract, bytes4 _interfaceId) external ``` > **identifier:** `d3d23aa5` > **parameters** > **_contract**: Address of the contract for which to update the cache. > **_interfaceId**: CBC-165 interface for which to update the cache. #### **Private User-defined Interfaces** This scheme is extensible. You MAY make up your own interface name and raise awareness to get other people to implement it and then check for those implementations. Have fun but please, you MUST not conflict with the reserved designations above. ### Set An Interface For An Address For any address to set a contract as the interface implementation, it must call the following function of the CBC-1820 registry: ``` solidity function setInterfaceImplementer(address _addr, bytes32 _interfaceHash, address _implementer) external ``` Sets the contract which implements a specific interface for an address. Only the `manager` defined for that address can set it. (Each address is the manager for itself, see the manager section for more details.) *NOTE*: If `_addr` and `_implementer` are two different addresses, then: - The `_implementer` MUST implement the `CBC1820ImplementerInterface` (detailed below). - Calling `canImplementInterfaceForAddress` on `_implementer` with the given `_addr` and `_interfaceHash` MUST return the `CBC1820_ACCEPT_MAGIC` value. *NOTE*: The `_interfaceHash` MUST NOT be an CBC-165 interface - it MUST NOT end with 28 zeroes (`0`). *NOTE*: The `_addr` MAY be `0`, then `msg.sender` is assumed. This default value simplifies interactions via multisigs where the data of the transaction to sign is constant regardless of the address of the multisig instance. > **identifier:** `d40731a8` > **parameters** > **_addr**: Address for which to set the interface. (If `_addr` is the zero address then `msg.sender` is assumed.) > **_interfaceHash**: Core SHA3-256 hash of the name of the interface as a string, for example `web3.utils.keccak256('CBC777TokensRecipient')` for the CBC777TokensRecipient interface. > **_implementer**: Contract implementing `_interfaceHash` for `_addr`. ### Get An Implementation Of An Interface For An Address Anyone MAY query the CBC-1820 Registry to obtain the address of a contract implementing an interface on behalf of some address using the `getInterfaceImplementer` function. ``` solidity function getInterfaceImplementer(address _addr, bytes32 _interfaceHash) external view returns (address) ``` Query if an address implements an interface and through which contract. *NOTE*: If the last 28 bytes of the `_interfaceHash` are zeroes (`0`), then the first 4 bytes are considered an CBC-165 interface and the registry SHALL forward the call to the contract at `_addr` to see if it implements the CBC-165 interface (the first 4 bytes of `_interfaceHash`). The registry SHALL also cache CBC-165 queries to reduce energy consumption. Anyone MAY call the `updateCBC165Cache` function to update whether a contract implements an interface or not. *NOTE*: The `_addr` MAY be `0`, then `msg.sender` is assumed. This default value is consistent with the behavior of the `setInterfaceImplementer` function and simplifies interactions via multisigs where the data of the transaction to sign is constant regardless of the address of the multisig instance. > **identifier:** `94da2ef8` > **parameters** > **_addr**: Address being queried for the implementer of an interface. (If `_addr` is the zero address then `msg.sender` is assumed.) > **_interfaceHash**: keccak256 hash of the name of the interface as a string. E.g. `web3.utils.keccak256('CBC777Token')` > **returns:** The address of the contract which implements the interface `_interfaceHash` for `_addr` or `0` if `_addr` did not register an implementer for this interface. ### Interface Implementation (`CBC1820ImplementerInterface`) ``` solidity interface CBC1820ImplementerInterface { /// @notice Indicates whether the contract implements the interface `interfaceHash` for the address `addr` or not. /// @param interfaceHash keccak256 hash of the name of the interface /// @param addr Address for which the contract will implement the interface /// @return CBC1820_ACCEPT_MAGIC only if the contract implements `interfaceHash` for the address `addr`. function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32); } ``` Any contract being registered as the implementation of an interface for a given address MUST implement said interface. In addition if it implements an interface on behalf of a different address, the contract MUST implement the `CBC1820ImplementerInterface` shown above. ``` solidity function canImplementInterfaceForAddress(bytes32 interfaceHash, address addr) external view returns(bytes32) ``` Indicates whether a contract implements an interface (`interfaceHash`) for a given address (`addr`). If a contract implements the interface (`interfaceHash`) for a given address (`addr`), it MUST return `CBC1820_ACCEPT_MAGIC` when called with the `addr` and the `interfaceHash`. If it does not implement the `interfaceHash` for a given address (`addr`), it MUST NOT return `CBC1820_ACCEPT_MAGIC`. > **identifier:** `dab359c1` > **parameters** > **interfaceHash**: Hash of the interface which is implemented > **addr**: Address for which the interface is implemented > **returns:** `CBC1820_ACCEPT_MAGIC` only if the contract implements `ìnterfaceHash` for the address `addr`. The special value `CBC1820_ACCEPT_MAGIC` is defined as the `keccak256` hash of the string `"CBC1820_ACCEPT_MAGIC"`. ``` solidity bytes32 constant internal CBC1820_ACCEPT_MAGIC = keccak256(abi.encodePacked("CBC1820_ACCEPT_MAGIC")); ``` > The reason to return `CBC1820_ACCEPT_MAGIC` instead of a boolean is to prevent cases where a contract fails to implement the `canImplementInterfaceForAddress` but implements a fallback function which does not throw. In this case, since `canImplementInterfaceForAddress` does not exist, the fallback function is called instead, executed without throwing and returns `1`. Thus making it appear as if `canImplementInterfaceForAddress` returned `true`. ## Rationale This standards offers a way for any type of address (externally owned and contracts) to implement an interface and potentially delegate the implementation of the interface to a proxy contract. This delegation to a proxy contract is necessary for externally owned accounts and useful to avoid redeploying existing contracts such as multisigs and DAOs. The registry can also act as a CBC-165 cache in order to save energy when looking up if a contract implements a specific CBC-165 interface. This cache is intentionally kept simple, without automatic cache update or invalidation. Anyone can easily and safely update the cache for any interface and any contract by calling the `updateCBC165Cache` function. Canonical registry deployments MUST publish verified bytecode and network-specific addresses so clients can verify that each deployment matches this specification. ## Backward Compatibility This standard is backward compatible with CBC-165, as both methods MAY be implemented without conflicting with each other. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Diamonds, Multi-Facet Proxy Source: https://cip.coreblockchain.net/cip/cbc/cip-2535/ Repository path: cip/cbc/cip-2535.md A standard for modular smart contract systems (diamonds) that can be extended after deployment, supporting unlimited contract size and upgradeability via facets. ## Abstract This proposal standardizes diamonds, which are modular smart contract systems that can be upgraded/extended after deployment, and have virtually no size limit. More technically, a **diamond** is a contract with external functions that are supplied by contracts called **facets**. Facets are separate, independent contracts that can share internal functions, libraries, and state variables. ## Motivation There are a number of different reasons to use diamonds. Here are some of them: 1. **A single address for unlimited contract functionality.** Using a single address for contract functionality makes deployment, testing and integration with other smart contracts, software and user interfaces easier. 2. **Your contract exceeds the network's maximum contract size.** You may have related functionality that it makes sense to keep in a single contract, or at a single contract address. A diamond can exceed the per-contract size limit in aggregate because each facet is deployed separately. 3. **A diamond provides a way to organize contract code and data.** You may want to build a contract system with a lot of functionality. A diamond provides a systematic way to isolate different functionality and connect them together and share data between them as needed in a energy-efficient way. 4. **A diamond provides a way to upgrade functionality.** Upgradeable diamonds can be upgraded to add/replace/remove functionality. Because diamonds have no max contract size, there is no limit to the amount of functionality that can be added to diamonds over time. Diamonds can be upgraded without having to redeploy existing functionality. Parts of a diamond can be added/replaced/removed while leaving other parts alone. 5. **A diamond can be immutable.** It is possible to deploy an immutable diamond or make an upgradeable diamond immutable at a later time. 6. **A diamond can reuse deployed contracts.** Instead of deploying contracts to a blockchain, existing already deployed, onchain contracts can be used to create diamonds. Custom diamonds can be created from existing deployed contracts. This enables the creation of on-chain smart contract platforms and libraries. A deployed facet can be used by any number of diamonds. ## Specification ### Terms 1. A **diamond** is a facade smart contract that `delegatecall`s into its facets to execute function calls. A diamond is stateful. Data is stored in the contract storage of a diamond. 2. A **facet** is a stateless smart contract or Ylem library with external functions. A facet is deployed and one or more of its functions are added to one or more diamonds. A facet does not store data within its own contract storage but it can define state and read and write to the storage of one or more diamonds. The term facet comes from the diamond industry. It is a side, or flat surface of a diamond. 3. A **loupe facet** is a facet that provides introspection functions. In the diamond industry, a loupe is a magnifying glass that is used to look at diamonds. 4. An **immutable function** is an external function that cannot be replaced or removed (because it is defined directly in the diamond, or because the diamond’s logic does not allow it to be modified). 5. A **mapping** for the purposes of this CIP is an association between two things and does not refer to a specific implementation. The term **contract** is used loosely to mean a smart contract or deployed Ylem library. When this CIP uses **function** without specifying internal or external, it means external function. In this CIP the information that applies to external functions also applies to public functions. ### Overview A diamond calls functions from its facets using `delegatecall`. In the diamond industry diamonds are created and shaped by being cut, creating facets. In this standard diamonds are cut by adding, replacing or removing functions from facets. ### A Note on Implementing Interfaces Because of the nature of diamonds, a diamond can implement an interface in one of two ways: directly (`contract Contract is Interface`), or by adding functions to it from one or more facets. For the purposes of this proposal, when a diamond is said to implement an interface, either method of implementation is permitted. ### Fallback Function When an external function is called on a diamond its fallback function is executed. The fallback function determines which facet to call based on the first four bytes of the call data (known as the function selector) and executes that function from the facet using `delegatecall`. A diamond’s fallback function and `delegatecall` enable a diamond to execute a facet’s function as if it was implemented by the diamond itself. The `msg.sender` and `msg.value` values do not change and only the diamond’s storage is read and written to. Here is an illustrative example of how a diamond’s fallback function might be implemented: ```solidity // Find facet for function that is called and execute the // function if a facet is found and return any value. fallback() external payable { // get facet from function selector address facet = selectorTofacet[msg.sig]; require(facet != address(0)); // Execute external function from facet using delegatecall and return any value. assembly { // copy function selector and any arguments calldatacopy(0, 0, calldatasize()) // execute function call using the facet let result := delegatecall(energy(), facet, 0, calldatasize(), 0, 0) // get any return value returndatacopy(0, 0, returndatasize()) // return any return value or error back to the caller switch result case 0 {revert(0, returndatasize())} default {return (0, returndatasize())} } } ``` ### Storage A state variable or storage layout organizational pattern is needed because Ylem’s builtin storage layout system doesn’t support proxy contracts or diamonds. The particular layout of storage is not defined in this CIP, but may be defined by later proposals. Examples of storage layout patterns that work with diamonds are Diamond Storage and AppStorage. Facets can share state variables by using the same structs at the same storage positions. Facets can share internal functions and libraries by inheriting the same contracts or using the same libraries. In these ways facets are separate, independent units but can share state and functionality. ### Ylem Libraries as Facets Smart contracts or deployed Ylem libraries can be facets of diamonds. Only Ylem libraries that have one or more external functions can be deployed to a blockchain and be a facet. Ylem libraries that contain internal functions only cannot be deployed and cannot be a facet. Internal functions from Ylem libraries are included in the bytecode of facets and contracts that use them. Ylem libraries with internal functions only are useful for sharing internal functions between facets. Ylem library facets have a few properties that match their use as facets: * They cannot be deleted. * They are stateless. They do not have contract storage. * Their syntax prevents declaring state variables outside Diamond Storage. ### Adding/Replacing/Removing Functions #### `ICBCDiamond` Interface All diamonds must implement the `ICBCDiamond` interface. During the deployment of a diamond any immutable functions and any external functions added to the diamond must be emitted in the `DiamondCut` event. **A `DiamondCut` event must be emitted any time external functions are added, replaced, or removed.** This applies to all upgrades, all functions changes, at any time, whether through `diamondCut` or not. ```solidity interface ICBCDiamond { enum FacetCutAction {Add, Replace, Remove} // Add=0, Replace=1, Remove=2 struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); } ``` The `DiamondCut` event records all function changes to a diamond. #### `ICBCDiamondCut` Interface A diamond contains within it a mapping of function selectors to facet addresses. Functions are added/replaced/removed by modifying this mapping. Diamonds should implement the `ICBCDiamondCut` interface if after their deployment they allow modifications to their function selector mapping. The `diamondCut` function updates any number of functions from any number of facets in a single transaction. Executing all changes within a single transaction prevents data corruption which could occur in upgrades done over multiple transactions. `diamondCut` is specified for the purpose of interoperability. Diamond tools, software and user-interfaces should expect and use the standard `diamondCut` function. ```solidity interface ICBCDiamondCut is ICBCDiamond { /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; } ``` The `_diamondCut` argument is an array of `FacetCut` structs. Each `FacetCut` struct contains a facet address and array of function selectors that are updated in a diamond. For each `FacetCut` struct: * If the `action` is `Add`, update the function selector mapping for each `functionSelectors` item to the `facetAddress`. If any of the `functionSelectors` had a mapped facet, revert instead. * If the `action` is `Replace`, update the function selector mapping for each `functionSelectors` item to the `facetAddress`. If any of the `functionSelectors` had a value equal to `facetAddress` or the selector was unset, revert instead. * If the `action` is `Remove`, remove the function selector mapping for each `functionSelectors` item. If any of the `functionSelectors` were previously unset, revert instead. Any attempt to replace or remove an immutable function must revert. Being intentional and explicit about adding/replacing/removing functions helps catch and prevent upgrade mistakes. ##### Executing `_calldata` After adding/replacing/removing functions the `_calldata` argument is executed with `delegatecall` on `_init`. This execution is done to initialize data or setup or remove anything needed or no longer needed after adding, replacing and/or removing functions. If the `_init` value is `address(0)` then `_calldata` execution is skipped. In this case `_calldata` can contain 0 bytes or custom information. ### Inspecting Facets & Functions > A loupe is a small magnifying glass used to look at diamonds. Diamonds must support inspecting facets and functions by implementing the `ICBCDiamondLoupe` interface. #### `ICBCDiamondLoupe` Interface ```solidity // A loupe is a small magnifying glass used to look at diamonds. // These functions look at diamonds interface ICBCDiamondLoupe { struct Facet { address facetAddress; bytes4[] functionSelectors; } /// @notice Gets all facet addresses and their four byte function selectors. /// @return facets_ Facet function facets() external view returns (Facet[] memory facets_); /// @notice Gets all the function selectors supported by a specific facet. /// @param _facet The facet address. /// @return facetFunctionSelectors_ function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_); /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view returns (address[] memory facetAddresses_); /// @notice Gets the facet that supports the given selector. /// @dev If facet is not found return address(0). /// @param _functionSelector The function selector. /// @return facetAddress_ The facet address. function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_); } ``` The loupe functions can be used in user-interface software. A user interface calls these functions to provide information about and visualize diamonds. The loupe functions can be used in deployment functionality, upgrade functionality, testing and other software. ### Implementation Points A diamond must implement the following: 1. A diamond contains a fallback function and zero or more immutable functions that are defined within it. 2. A diamond associates function selectors with facets. 3. When a function is called on a diamond it executes immediately if it is an “immutable function” defined directly in the diamond. Otherwise the diamond’s fallback function is executed. The fallback function finds the facet associated with the function and executes the function using `delegatecall`. If there is no facet for the function then optionally a default function may be executed. If there is no facet for the function and no default function and no other mechanism to handle it then execution reverts. 4. Each time functions are added, replaced or removed a `DiamondCut` event is emitted to record it. 5. A diamond implements the DiamondLoupe interface. 6. All immutable functions must be emitted in the `DiamondCut` event as new functions added. And the loupe functions must return information about immutable functions if they exist. The facet address for an immutable function is the diamond’s address. Any attempt to delete or replace an immutable function must revert. A diamond may implement the following: 1. CIP-165’s `supportsInterface`. If a diamond has the `diamondCut` function then the interface ID used for it is `ICBCDiamondCut.diamondCut.selector`. The interface ID used for the diamond loupe interface is `ICBCDiamondLoupe.facets.selector ^ ICBCDiamondLoupe.facetFunctionSelectors.selector ^ ICBCDiamondLoupe.facetAddresses.selector ^ ICBCDiamondLoupe.facetAddress.selector`. The diamond address is the address that users interact with. The diamond address does not change. Only facet addresses can change by using the `diamondCut` function, or other function. ## Rationale ### Using Function Selectors User interface software can be used to retrieve function selectors and facet addresses from a diamond in order show what functions a diamond has. This standard is designed to make diamonds work well with user-interface software. Function selectors with the ABI of a contract provide enough information about functions to be useful for user-interface software. ### Energy Considerations Delegating function calls does have some energy overhead. This is mitigated in several ways: 1. Because diamonds do not have a max size limitation it is possible to add energy optimizing functions for use cases. For example someone could use a diamond to implement the CIP-721 standard and implement batch transfer functions to reduce energy (and make batch transfers more convenient). 2. Some contract architectures require calling multiple contracts in one transaction. Energy savings can be realized by condensing those contracts into a single diamond and accessing contract storage directly. 3. Facets can contain few external functions, reducing energy costs. Because it costs more energy to call a function in a contract with many functions than a contract with few functions. 4. The Ylem optimizer can be set to a high setting causing more bytecode to be generated but the facets will use less energy when executed. ### Versions of Functions Software or a user can verify what version of a function is called by getting the facet address of the function. This can be done by calling the `facetAddress` function from the `ICBCDiamondLoupe` interface. This function takes a function selector as an argument and returns the facet address where it is implemented. ### Default Function Ylem provides the `fallback` function so that specific functionality can be executed when a function is called on a contract that does not exist in the contract. This same behavior can optionally be implemented in a diamond by implementing and using a default function, which is a function that is executed when a function is called on a diamond that does not exist in the diamond. A default function can be implemented a number of ways and this standard does not specify how it must be implemented. ### Loupe Functions & `DiamondCut` Event To find out what functions a regular contract has it is only necessary to look at its verified source code. The verified source code of a diamond does not include what functions it has so a different mechanism is needed. A diamond has four standard functions called the loupe functions that are used to show what functions a diamond has. The loupe functions can be used for many things including: 1. To show all functions used by a diamond. 2. To query Core explorers or files to retrieve and show all source code used by a diamond. 3. To query Core explorers or files to retrieve ABI information for a diamond. 4. To test or verify that a transaction that adds/replaces/removes functions on a diamond succeeded. 5. To find out what functions a diamond has before calling functions on it. 6. To be used by tools and programming libraries to deploy and upgrade diamonds. 7. To be used by user interfaces to show information about diamonds. 8. To be used by user interfaces to enable users to call functions on diamonds. Diamonds support another form of transparency which is a historical record of all upgrades on a diamond. This is done with the `DiamondCut` event which is used to record all functions that are added, replaced or removed on a diamond. ### Sharing Functions Between Facets In some cases it might be necessary to call a function defined in a different facet. Here are ways to do this: 1. Copy internal function code in one facet to the other facet. 2. Put common internal functions in a contract that is inherited by multiple facets. 3. Put common internal functions in a Ylem library and use the library in facets. 4. A type safe way to call an external function defined in another facet is to do this: `MyOtherFacet(address(this)).myFunction(arg1, arg2)` 5. A more energy-efficient way to call an external function defined in another facet is to use delegatecall. Here is an example of doing that: ```solidity DiamondStorage storage ds = diamondStorage(); bytes4 functionSelector = bytes4(keccak256("myFunction(uint256)")); // get facet address of function address facet = ds.selectorToFacet[functionSelector]; bytes memory myFunctionCall = abi.encodeWithSelector(functionSelector, 4); (bool success, bytes memory result) = address(facet).delegatecall(myFunctionCall); ``` 6. Instead of calling an external function defined in another facet you can instead create an internal function version of the external function. Add the internal version of the function to the facet that needs to use it. ### Facets can be Reusable and Composable A deployed facet can be used by any number of diamonds. Different combinations of facets can be used with different diamonds. It is possible to create and deploy a set of facets that are reused by different diamonds over time. The ability to use the same deployed facets for many diamonds reduces deployment costs. It is possible to implement facets in a way that makes them usable/composable/compatible with other facets. It is also possible to implement facets in a way that makes them not usable/composable/compatible with other facets. A function signature is the name of a function and its parameter types. Example function signature: `myfunction(uint256)`. A limitation is that two external functions with the same function signature can’t be added to the same diamond at the same time because a diamond, or any contract, cannot have two external functions with the same function signature. All the functions of a facet do not have to be added to a diamond. Some functions in a facet can be added to a diamond while other functions in the facet are not added to the diamond. ## Backward Compatibility This standard makes upgradeable diamonds compatible with future standards and functionality because new functions can be added and existing functions can be replaced or removed. ## Security Considerations ### Ownership and Authentication > **Note:** The design and implementation of diamond ownership/authentication is **not** part of this standard. The examples given in this standard and in the reference implementation are just **examples** of how it could be done. It is possible to create many different authentication or ownership schemes with this proposal. Authentication schemes can be very simple or complex, fine grained or coarse. This proposal does not limit it in any way. For example ownership/authentication could be as simple as a single account address having the authority to add/replace/remove functions. Or a decentralized autonomous organization could have the authority to only add/replace/remove certain functions. Consensus functionality could be implemented such as an approval function that multiple different people call to approve changes before they are executed with the `diamondCut` function. These are just examples. The development of standards and implementations of ownership, control and authentication of diamonds is encouraged. ### Arbitrary Execution with `diamondCut` The `diamondCut` function allows arbitrary execution with access to the diamond’s storage (through `delegatecall`). Access to this function must be restricted carefully. ### Do Not Self Destruct Use of `selfdestruct` in a facet is heavily discouraged. Misuse of it can delete a diamond or a facet. ### Function Selector Clash A function selector clash occurs when two different function signatures hash to the same four-byte hash. This has the unintended consequence of replacing an existing function in a diamond when the intention was to add a new function. This scenario is not possible with a properly implemented `diamondCut` function because it prevents adding function selectors that already exist. ### Transparency Diamonds emit an event every time one or more functions are added, replaced or removed. All source code can be verified. This enables people and software to monitor changes to a contract. If any bad acting function is added to a diamond then it can be seen. Security and domain experts can review the history of change of a diamond to detect any history of foul play. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Permit Extension Source: https://cip.coreblockchain.net/cip/cbc/cip-2612/ Repository path: cip/cbc/cip-2612.md A standardized smart contract interface for approvals via signatures (CIP-712), enabling energyless approvals for CBC-20 tokens and similar standards. ## Abstract This standard defines a method for approvals via off-chain signatures (CIP-712), allowing users to approve token allowances for CBC-20 tokens without sending an on-chain transaction. CBC-2612 enables energyless approvals, improving UX and composability for wallets, dApps, and DeFi protocols. ## Motivation Traditional CBC-20 approvals require users to send an on-chain transaction, incurring energy costs and requiring two transactions for many workflows (approve + transferFrom). CBC-2612 introduces a `permit` function, allowing approvals to be made via signed messages, enabling energyless approvals and meta-transactions. ## Specification Compliant contracts MUST implement the following functions in addition to CBC-20: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; interface ICBC2612 /* is ICBC20 */ { function permit( address owner, address spender, uint256 value, uint256 deadline, bytes calldata signature ) external; function nonces(address owner) external view returns (uint256); function DOMAIN_SEPARATOR() external view returns (bytes32); } ``` The semantics are as follows: For all addresses `owner`, `spender`, uint256s `value`, `deadline` and `nonce`, and bytes `signature`, a call to `permit(owner, spender, value, deadline, signature)` MUST set `allowance[owner][spender]` to `value`, increment `nonces[owner]` by 1, and emit a corresponding `Approval` event, IF and ONLY IF all the following conditions are met: - The current blocktime is less than or equal to `deadline`. - Neither `owner` nor `spender` is `address(0)` or `Checksum.zeroAddress()`. - `nonces[owner]` (before the state update) is equal to `nonce`. - `signature` is a valid 171-byte Core signature from `owner` of the message: ```solidity keccak256(abi.encodePacked( hex"1901", DOMAIN_SEPARATOR, keccak256(abi.encode( keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"), owner, spender, value, nonce, deadline)) )) ``` where `DOMAIN_SEPARATOR` is defined according to CIP-712. It MUST use the `EIP712Domain` type with `name`, `version`, `networkId`, and `verifyingContract`, making the separator unique to the contract and Core network. It can be calculated as follows: ```solidity DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256('EIP712Domain(string name,string version,uint256 networkId,address verifyingContract)'), keccak256(bytes(name)), keccak256(bytes(version)), block.chainid, address(this) )); ``` The message is the CIP-712 typed structure: ```json { "types": { "EIP712Domain": [ { "name": "name", "type": "string" }, { "name": "version", "type": "string" }, { "name": "networkId", "type": "uint256" }, { "name": "verifyingContract", "type": "address" } ], "Permit": [ { "name": "owner", "type": "address" }, { "name": "spender", "type": "address" }, { "name": "value", "type": "uint256" }, { "name": "nonce", "type": "uint256" }, { "name": "deadline", "type": "uint256" } ] }, "primaryType": "Permit", "domain": { "name": cbc20name, "version": version, "networkId": networkId, "verifyingContract": tokenAddress }, "message": { "owner": owner, "spender": spender, "value": value, "nonce": nonce, "deadline": deadline } } ``` The caller of the `permit` function can be any address. If any of the above conditions are not met, the call MUST revert. ### Method Behaviors - The `permit` function MUST set `spender`'s allowance over `owner`'s tokens to `value` if a valid signature is provided, and MUST increment the owner's nonce. - The `permit` function MUST revert if the signature is invalid or expired. - The `DOMAIN_SEPARATOR` MUST be implemented according to CIP-712. - The `nonces` function MUST return the current nonce for an address. - The `Approval` event MUST be emitted on a successful permit. ## Backward Compatibility There are existing `permit` functions in some token contracts (e.g., DAI, Stake) with different semantics: - DAI uses a `bool allowed` instead of a `value` argument, and `expiry` instead of `deadline`. - Stake allows expiring approvals, only permitting `transferFrom` while `expiry >= block.timestamp`. CBC-2612 aligns with the Uniswap V2 implementation and the CBC-20 standard, using `value` and `deadline` as specified above. The requirement to revert if the permit is invalid is consistent with all major implementations. ## Reference Implementation A Core implementation is available in the [xToken repository](https://github.com/xmoneyx/xtoken/blob/fix/dependencies/src/v2/EIP2612.sol). It uses Core OpenZeppelin's `EDDSA` helpers, a single 171-byte signature, and the CIP-712 domain described above. ## Rationale CBC-2612 improves UX and composability by enabling energyless approvals, reducing friction for users and enabling new workflows for wallets and dApps. ## Security Considerations - Implementers must ensure signatures are unique and cannot be replayed (use nonces). - The `permit` function must be resistant to signature malleability. - The `DOMAIN_SEPARATOR` must be unique per contract and Core network. - Upgradability and access control best practices should be followed. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Batched Meta Transactions Source: https://cip.coreblockchain.net/cip/cbc/cip-3005/ Repository path: cip/cbc/cip-3005.md Batched Meta Transactions for CBC-20 and compatible tokens. ## Abstract This CIP defines a new function called `processMetaBatch()` that extends any fungible token standard, and enables batched meta transactions coming from many senders in one on-chain transaction. The function must be able to receive multiple meta transactions data and process it. This means validating the data and the signature, before proceeding with token transfers based on the data. The function enables senders to make energyless transactions, while reducing the relayer's energy cost due to batching. ## Motivation Meta transactions have proven useful as a solution for Core accounts that don't have any XCB, but hold CBC-20 tokens and would like to transfer them (energyless transactions). The current meta transaction relayer implementations only allow relaying one meta transaction at a time. Some also allow batched meta transactions from the same sender. But none offers batched meta transactions from **multiple** senders. The motivation behind this CIP is to find a way to allow relaying batched meta transactions from **many senders** in **one on-chain transaction**, which also **reduces the total energy cost** that a relayer needs to cover. ## Specification The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. The key words "MUST (BUT WE KNOW YOU WON'T)", "SHOULD CONSIDER", "REALLY SHOULD NOT", "OUGHT TO", "WOULD PROBABLY", "MAY WISH TO", "COULD", "POSSIBLE", and "MIGHT" in this document are to be interpreted as described in RFC 6919. ### Meta transaction data In order to successfully validate and transfer tokens, the `processMetaBatch()` function MUST process the following data about a meta transaction: - sender address - receiver address - token amount - relayer fee - a (meta tx) nonce - an expiration date (this COULD be a block number, or it COULD be a block timestamp) - a token address - a relayer address - a signature Not all of the data needs to be sent to the function by the relayer (see the function interface specification). Some of the data can be deduced or extracted from other sources (from transaction data and contract state). ### `processMetaBatch()` function input data The `processMetaBatch()` function MUST receive the following data: - sender address - receiver address - token amount - relayer fee - an expiration date (this COULD be a block number, or it COULD be a block timestamp) - a signature The following data is OPTIONAL to be sent to the function, because it can be extracted or derived from other sources: - a (meta tx) nonce - a token address - a relayer address ### Meta transaction data hash The pseudocode for creating a hash of meta transaction data is the following: ```solidity keccak256(address(sender) ++ address(recipient) ++ uint256(amount) ++ uint256(relayerFee) ++ uint256(nonce) ++ uint256(expirationDate) ++ address(tokenContract) ++ address(relayer) ) ``` The created hash MUST then be signed with the sender's private key. ### Validation rules - Nonce of a new transaction MUST always be bigger by exactly 1 from the nonce of the last successfully processed meta transaction of the same sender to the same token contract. - Sending to and from either `address(0)` or `Checksum.zeroAddress()` MUST be prohibited. - A meta transaction MUST be processed before the expiration date. - Each sender's token balance MUST be equal or greater than the sum of their respective meta transaction token amount and relayer fee. - A transaction where at least one meta transaction in the batch does not satisfy the above requirements MUST not be reverted. Instead, a failed meta transaction MUST be skipped or ignored. ### `processMetaBatch()` function interface The `processMetaBatch()` function MUST have the following interface: ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; interface ICBC3005 { function processMetaBatch(address[] calldata senders, address[] calldata recipients, uint256[] calldata amounts, uint256[] calldata relayerFees, uint256[] calldata blocks, bytes[] calldata signatures) external returns (bool); } ``` The overview of parameters that are passed: - `senders`: an array of meta transaction sender addresses (token senders) - `recipients`: an array of token recipients addresses - `amounts`: an array of token amounts that are sent from each sender to each recipient, respectively - `relayerFees`: an array of the relayer fees paid in tokens by senders. The fee receiver is a relayer (`msg.sender`) - `blocks`: an array of block numbers that represent an expiration date by which the meta transaction must be processed (alternatively, a timestamp could be used instead of a block number) - `signatures`: an array of complete 171-byte Core signatures Each entry in each of the arrays MUST represent data from one meta transaction. The order of the data is very important. Data from a single meta transaction MUST have the same index in every array. ### Meta transaction nonce The token smart contract must keep track of a meta transaction nonce for each token holder. ```solidity mapping (address => uint256) private _metaNonces; ``` The interface for the `nonceOf()` function is the following: ```solidity function nonceOf(address account) public view returns (uint256); ``` ### Token transfers After a meta transaction is successfully validated, the meta nonce of the meta transaction sender MUST be increased by 1. Then two token transfers MUST occur: - The specified token amount MUST go to the recipient. - The relayer fee MUST go to the relayer (`msg.sender`). ## Implementation The **reference implementation** adds a couple of functions to the existing CBC-20 token standard: - `processMetaBatch()` - `nonceOf()` ### `processMetaBatch()` The `processMetaBatch()` function is responsible for receiving and processing a batch of meta transactions that change token balances. ```solidity function processMetaBatch(address[] memory senders, address[] memory recipients, uint256[] memory amounts, uint256[] memory relayerFees, uint256[] memory blocks, bytes[] memory signatures) public returns (bool) { address sender; uint256 newNonce; uint256 relayerFeesSum = 0; bytes32 msgHash; uint256 i; // loop through all meta txs for (i = 0; i < senders.length; i++) { sender = senders[i]; newNonce = _metaNonces[sender] + 1; if(_isZeroAddress(sender) || _isZeroAddress(recipients[i])) { continue; // sender or recipient is a Core zero address, skip this meta tx } // the meta tx should be processed until (including) the specified block number, otherwise it is invalid if(block.number > blocks[i]) { continue; // if current block number is bigger than the requested number, skip this meta tx } // check if meta tx sender's balance is big enough if(_balances[sender] < (amounts[i] + relayerFees[i])) { continue; // if sender's balance is less than the amount and the relayer fee, skip this meta tx } // check if the signature is valid msgHash = keccak256(abi.encode(sender, recipients[i], amounts[i], relayerFees[i], newNonce, blocks[i], address(this), msg.sender)); if(sender != ecrecover(keccak256(abi.encodePacked("\x19Core Signed Message:\n32", msgHash)), signatures[i])) { continue; // if sig is not valid, skip to the next meta tx } // set a new nonce for the sender _metaNonces[sender] = newNonce; // transfer tokens _balances[sender] -= (amounts[i] + relayerFees[i]); _balances[recipients[i]] += amounts[i]; relayerFeesSum += relayerFees[i]; } // give the relayer the sum of all relayer fees _balances[msg.sender] += relayerFeesSum; return true; } ``` ### `nonceOf()` Nonces are needed due to the replay protection (see *Replay attacks* under *Security Considerations*). ```solidity mapping (address => uint256) private _metaNonces; // ... function nonceOf(address account) public view returns (uint256) { return _metaNonces[account]; } ``` > Note that the OpenZeppelin CBC-20 implementation was used here. Some other implementation may have named the `_balances` mapping differently, which would require minor changes in the `processMetaBatch()` function. ## Rationale ### All-in-one Alternative implementations (like GSN) use multiple smart contracts to enable meta transactions, although this increases energy usage. This implementation (CIP-3005) intentionally keeps everything within one function which reduces complexity and energy cost. The `processMetaBatch()` function thus does the job of receiving a batch of meta transactions, validating them, and then transferring tokens from one address to another. ### Function parameters As you can see, the `processMetaBatch()` function in the reference implementation takes the following parameters: - an array of **sender addresses** (meta txs senders, not relayers) - an array of **receiver addresses** - an array of **amounts** - an array of **relayer fees** (relayer is `msg.sender`) - an array of **block numbers** (a due "date" for meta tx to be processed) - an array of complete 171-byte Core **signatures** **Each item** in these arrays represents **data of one meta transaction**. That's why the **correct order** in the arrays is very important. If a relayer gets the order wrong, the `processMetaBatch()` function would notice that (when validating a signature), because the hash of the meta transaction values would not match the signed hash. A meta transaction with an invalid signature is **skipped**. ### The alternative way of passing meta transaction data into the function The reference implementation takes parameters as arrays. There's a separate array for each meta transaction data category (the ones that cannot be deduced or extracted from other sources). A different approach would be to bitpack all data of a meta transaction into one value and then unpack it within the smart contract. The data for a batch of meta transactions would be sent in an array, but there would need to be only one array (of packed data), instead of multiple arrays. ### Why is nonce not one of the parameters in the reference implementation? Meta nonce is used for constructing a signed hash (see the `msgHash` line where a `keccak256` hash is constructed - you'll find a nonce there). Since a new nonce has to always be bigger than the previous one by exactly 1, there's no need to include it as a parameter array in the `processMetaBatch()` function, because its value can be deduced. This also helps avoid the "Stack too deep" error. ### Can CIP-2612 nonces mapping be re-used? The CIP-2612 (`permit()` function) also requires a nonce mapping. At this point, I'm not sure yet if this mapping should be **re-used** in case a smart contract implements both CIP-3005 and CIP-2612. At the first glance, it seems the `nonces` mapping from CIP-2612 could be re-used, but this should be thought through (and tested) for possible security implications. ### Token transfers - alternative implementation Token transfers in the reference implementation could alternatively be done by calling the `_transfer()` function (part of the OpenZeppelin CBC-20 implementation), but it would increase the energy usage and it would also revert the whole batch if some meta transaction was invalid (the current implementation just skips it). Another energy usage optimization is to assign total relayer fees to the relayer at the end of the function, and not with every token transfer inside the for loop (thus avoiding multiple SSTORE calls that cost 5'000 energy). ## Backwards Compatibility The code implementation of batched meta transactions is backwards compatible with any fungible token standard, for example, CBC-20 (it only extends it with one function). ## Security Considerations Here is a list of potential security issues and how are they addressed in this implementation. ### Forging a meta transaction The solution against a relayer forging a meta transaction is for a user to sign the meta transaction with their private key. The `processMetaBatch()` function then verifies the signature using `ecrecover()`. ### Replay attacks The `processMetaBatch()` function is secure against two types of a replay attack: #### Using the same meta transaction twice in the same token smart contract A nonce prevents a replay attack where a relayer would send the same meta transaction more than once. #### Using the same meta transaction twice in different token smart contracts A token smart contract address must be added into the signed hash (of a meta transaction). This address does not need to be sent as a parameter into the `processMetaBatch()` function. Instead, the function uses `address(this)` when constructing a hash in order to verify the signature. This way a meta transaction not intended for the token smart contract would be rejected (skipped). ### Signature validation Signing a meta transaction and validating the signature is crucial for this whole scheme to work. The `processMetaBatch()` function validates a meta transaction signature, and if it's **invalid**, the meta transaction is **skipped** (but the whole on-chain transaction is **not reverted**). ```solidity msgHash = keccak256(abi.encode(sender, recipients[i], amounts[i], relayerFees[i], newNonce, blocks[i], address(this), msg.sender)); if(sender != ecrecover(keccak256(abi.encodePacked("\x19Core Signed Message:\n32", msgHash)), signatures[i])) { continue; // if sig is not valid, skip to the next meta tx } ``` Why not reverting the whole on-chain transaction? Because there could be only one problematic meta transaction, and the others should not be dropped just because of one rotten apple. That said, it is expected of relayers to validate meta transactions in advance before relaying them. That's why relayers are not entitled to a relayer fee for an invalid meta transaction. ### Malicious relayer forcing a user into over-spending A malicious relayer could delay sending some user's meta transaction until the user would decide to make the token transaction on-chain. After that, the relayer would relay the delayed meta transaction which would mean that the user would have made two token transactions (over-spending). **Solution:** Each meta transaction should have an "expiry date". This is defined in a form of a block number by which the meta transaction must be relayed on-chain. ```solidity function processMetaBatch(... uint256[] memory blocks, ...) public returns (bool) { //... // loop through all meta txs for (i = 0; i < senders.length; i++) { // the meta tx should be processed until (including) the specified block number, otherwise it is invalid if(block.number > blocks[i]) { continue; // if current block number is bigger than the requested number, skip this meta tx } //... ``` ### Front-running attack A malicious relayer could scout the Core transaction pool to steal meta transactions and front-run the original relayer. **Solution:** The protection that `processMetaBatch()` function uses is that it requires the meta transaction sender to add the relayer's Core address as one of the values in the hash (which is then signed). When the `processMetaBatch()` function generates a hash it includes the `msg.sender` address in it: ```solidity msgHash = keccak256(abi.encode(sender, recipients[i], amounts[i], relayerFees[i], newNonce, blocks[i], address(this), msg.sender)); if(sender != ecrecover(keccak256(abi.encodePacked("\x19Core Signed Message:\n32", msgHash)), signatures[i])) { continue; // if sig is not valid, skip to the next meta tx } ``` If the meta transaction was "stolen", the signature check would fail because the `msg.sender` address would not be the same as the intended relayer's address. ### A malicious (or too impatient) user sending a meta transaction with the same nonce through multiple relayers at once A user that is either malicious or just impatient could submit a meta transaction with the same nonce (for the same token contract) to various relayers. Only one of them would get the relayer fee (the first one on-chain), while the others would get an invalid meta transaction. **Solution:** Relayers could **share a list of their pending meta transactions** between each other (sort of an info mempool). The relayers don't have to fear that someone would steal their respective pending transactions, due to the front-running protection (see above). If relayers see meta transactions from a certain sender address that have the same nonce and are supposed to be relayed to the same token smart contract, they can decide that only the first registered meta transaction goes through and others are dropped (or in case meta transactions were registered at the same time, the remaining meta transaction could be randomly picked). At a minimum, relayers need to share this meta transaction data (in order to detect meta transaction collision): - sender address - token address - nonce ### Too big due block number The relayer could trick the meta transaction sender into adding too big due block number - this means a block by which the meta transaction must be processed. The block number could be far in the future, for example, 10 years in the future. This means that the relayer would have 10 years to submit the meta transaction. **One way** to solve this problem is by adding an upper bound constraint for a block number within the smart contract. For example, we could say that the specified due block number must not be bigger than 100'000 blocks from the current one. ```solidity // the meta tx should be processed until (including) the specified block number, otherwise it is invalid if(block.number > blocks[i] || blocks[i] > (block.number + 100000)) { // If current block number is bigger than the requested due block number, skip this meta tx. // Also skip if the due block number is too big (bigger than 100'000 blocks in the future). continue; } ``` This addition could open new security implications, that's why it is left out of this proof-of-concept. But anyone who wishes to implement it should know about this potential constraint, too. **The other way** is to keep the `processMetaBatch()` function as it is and rather check for the too big due block number **on the relayer level**. In this case, the user could be notified about the problem and could issue a new meta transaction with another relayer that would have a much lower block parameter (and the same nonce). ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Transfer With Authorization Source: https://cip.coreblockchain.net/cip/cbc/cip-3009/ Repository path: cip/cbc/cip-3009.md Transfer With Authorization for CBC-20 and compatible tokens. ## Abstract A set of functions to enable meta-transactions and atomic interactions with CBC-20 token contracts via signatures conforming to the CIP-712 typed message signing specification. This enables the user to: - delegate the energy payment to someone else, - pay for energy in the token itself rather than in XCB, - perform one or more token transfers and other operations in a single atomic transaction, - transfer CBC-20 tokens to another address, and have the recipient submit the transaction, - batch multiple transactions with minimal overhead, and - create and perform multiple transactions without having to worry about them failing due to accidental nonce-reuse or improper ordering by the miner. ## Motivation There is an existing spec, CIP-2612, that also allows meta-transactions, and it is encouraged that a contract implements both for maximum compatibility. The two primary differences between this spec and CIP-2612 are that: - CIP-2612 uses sequential nonces, but this uses random 32-byte nonces, and that - CIP-2612 relies on the CBC-20 `approve`/`transferFrom` ("CBC-20 allowance") pattern. The biggest issue with the use of sequential nonces is that it does not allow users to perform more than one transaction at time without risking their transactions failing, because: - DApps may unintentionally reuse nonces that have not yet been processed in the blockchain. - Miners may process the transactions in the incorrect order. This can be especially problematic if the energy prices are very high and transactions often get queued up and remain unconfirmed for a long time. Non-sequential nonces allow users to create as many transactions as they want at the same time. The CBC-20 allowance mechanism is susceptible to the multiple withdrawal attack, and encourages antipatterns such as the use of the "infinite" allowance. The wide-prevalence of upgradeable contracts have made the conditions favorable for these attacks to happen in the wild. The deficiencies of the CBC-20 allowance pattern brought about the development of alternative token standards such as CBC-777. However, they haven't been able to gain much adoption due to compatibility and potential security issues. ## Specification ### Interface ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; abstract contract ICBC3009 { event AuthorizationUsed( address indexed authorizer, bytes32 indexed nonce ); // keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x9d376c88a2a578a58e393a82b4e9f8fd6827fbc3fefda07e6c245208f172c6d5; // keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)") bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xe5b1053d7239a64a7f4b19e277dd6cd0f3a58ccfa14e164096e0c7e783587b4d; /** * @notice Returns the state of an authorization * @dev Nonces are randomly generated 32-byte data unique to the authorizer's * address * @param authorizer Authorizer's address * @param nonce Nonce of the authorization * @return True if the nonce is used or canceled */ function authorizationState( address authorizer, bytes32 nonce ) external view virtual returns (bool); /** * @notice Execute a transfer with a signed authorization * @param from Payer's address (Authorizer) * @param to Payee's address * @param value Amount to be transferred * @param validAfter The time after which this is valid (unix time) * @param validBefore The time before which this is valid (unix time) * @param nonce Unique nonce * @param signature 171-byte Core signature */ function transferWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, bytes calldata signature ) external virtual; /** * @notice Receive a transfer with a signed authorization from the payer * @dev This has an additional check to ensure that the payee's address matches * the caller of this function to prevent front-running attacks. (See security * considerations) * @param from Payer's address (Authorizer) * @param to Payee's address * @param value Amount to be transferred * @param validAfter The time after which this is valid (unix time) * @param validBefore The time before which this is valid (unix time) * @param nonce Unique nonce * @param signature 171-byte Core signature */ function receiveWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, bytes calldata signature ) external virtual; } ``` **Optional:** ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; abstract contract ICBC3009Cancellation { event AuthorizationCanceled( address indexed authorizer, bytes32 indexed nonce ); // keccak256("CancelAuthorization(address authorizer,bytes32 nonce)") bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH = 0xbdf4caf0e95f58bce6aa01a60e4d47bdb240c01f81ea450b7d8354a78ee6fc5c; /** * @notice Attempt to cancel an authorization * @param authorizer Authorizer's address * @param nonce Nonce of the authorization * @param signature 171-byte Core signature */ function cancelAuthorization( address authorizer, bytes32 nonce, bytes calldata signature ) external virtual; } ``` The `signature` argument MUST contain the complete 171-byte Core signature obtained using the CIP-712 typed message signing specification. It MUST NOT be split into an Ethereum-style `(v, r, s)` tuple. **Example:** ```solidity DomainSeparator := Keccak256(ABIEncode( Keccak256( "EIP712Domain(string name,string version,uint256 networkId,address verifyingContract)" ), Keccak256("USD Coin"), // name Keccak256("2"), // version 1, // Core networkId TokenAddress // 22-byte Core verifyingContract )) ``` With the domain separator, the typehash, which is used to identify the type of the CIP-712 message being used, and the values of the parameters, a Core SHA3-256 digest can be derived and signed using the token holder's private key. In Ylem, `keccak256` computes Core SHA3-256. **Example:** ```solidity // Transfer With Authorization TypeHash := Keccak256( "TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)" ) Params := { From, To, Value, ValidAfter, ValidBefore, Nonce } // ReceiveWithAuthorization TypeHash := Keccak256( "ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)" ) Params := { From, To, Value, ValidAfter, ValidBefore, Nonce } // CancelAuthorization TypeHash := Keccak256( "CancelAuthorization(address authorizer,bytes32 nonce)" ) Params := { Authorizer, Nonce } ``` ```solidity // "‖" denotes concatenation. Digest := Keccak256( 0x1901 ‖ DomainSeparator ‖ Keccak256(ABIEncode(TypeHash, Params...)) ) Signature := Sign(Digest, PrivateKey) // 171-byte Core signature blob ``` Smart contract functions that wrap `receiveWithAuthorization` call may choose to reduce the number of arguments by accepting the full ABI-encoded set of arguments for the `receiveWithAuthorization` call as a single argument of the type `bytes`. **Example:** ```solidity // keccak256("receiveWithAuthorization(address,address,uint256,uint256,uint256,bytes32,bytes)")[0:4] bytes4 private constant _RECEIVE_WITH_AUTHORIZATION_SELECTOR = 0x95b70f09; function deposit(address token, bytes calldata receiveAuthorization) external nonReentrant { (address from, address to, uint256 amount) = abi.decode( receiveAuthorization[0:96], (address, address, uint256) ); require(to == address(this), "Recipient is not this contract"); (bool success, ) = token.call( abi.encodePacked( _RECEIVE_WITH_AUTHORIZATION_SELECTOR, receiveAuthorization ) ); require(success, "Failed to transfer tokens"); ... } ``` ### Use with Core providers The signature for an authorization can be obtained using a Core provider with the `xcb_signTypedData_v4` method. **Example:** ```javascript const data = { types: { EIP712Domain: [ { name: "name", type: "string" }, { name: "version", type: "string" }, { name: "networkId", type: "uint256" }, { name: "verifyingContract", type: "address" }, ], TransferWithAuthorization: [ { name: "from", type: "address" }, { name: "to", type: "address" }, { name: "value", type: "uint256" }, { name: "validAfter", type: "uint256" }, { name: "validBefore", type: "uint256" }, { name: "nonce", type: "bytes32" }, ], }, domain: { name: tokenName, version: tokenVersion, networkId: selectedNetworkId, verifyingContract: tokenAddress, }, primaryType: "TransferWithAuthorization", message: { from: userAddress, to: recipientAddress, value: amountBN.toString(10), validAfter: 0, validBefore: Math.floor(Date.now() / 1000) + 3600, // Valid for an hour nonce: Web3.utils.randomHex(32), }, }; const signature = await provider.request({ method: "xcb_signTypedData_v4", params: [userAddress, JSON.stringify(data)], }); ``` The returned value is passed to the contract unchanged as the 171-byte `signature` argument. ## Rationale ### Unique Random Nonce, Instead of Sequential Nonce One might say transaction ordering is one reason why sequential nonces are preferred. However, sequential nonces do not actually help achieve transaction ordering for meta transactions in practice: - For native Core transactions, when a transaction with a nonce value that is too high is submitted to the network, it will stay pending until the transactions consuming the lower unused nonces are confirmed. - However, for meta-transactions, when a transaction containing a sequential nonce value that is too high is submitted, instead of staying pending, it will revert and fail immediately, resulting in wasted energy. - The fact that miners can also reorder transactions and include them in the block in the order they want (assuming each transaction was submitted to the network by different meta-transaction relayers) also makes it possible for the meta-transactions to fail even if the nonces used were correct. (e.g. User submits nonces 3, 4 and 5, but miner ends up including them in the block as 4,5,3, resulting in only 3 succeeding) - Lastly, when using different applications simultaneously, in absence of some sort of an off-chain nonce-tracker, it is not possible to determine what the correct next nonce value is if there exists nonces that are used but haven't been submitted and confirmed by the network. - Under high energy price conditions, transactions can often "get stuck" in the pool for a long time. Under such a situation, it is much more likely for the same nonce to be unintentionally reused twice. For example, if you make a meta-transaction that uses a sequential nonce from one app, and switch to another app to make another meta-transaction before the previous one confirms, the same nonce will be used if the app relies purely on the data available on-chain, resulting in one of the transactions failing. - In conclusion, the only way to guarantee transaction ordering is for relayers to submit transactions one at a time, waiting for confirmation between each submission (and the order in which they should be submitted can be part of some off-chain metadata), rendering sequential nonce irrelevant. ### Valid After and Valid Before - Relying on relayers to submit transactions for you means you may not have exact control over the timing of transaction submission. - These parameters allow the user to schedule a transaction to be only valid in the future or before a specific deadline, protecting the user from potential undesirable effects that may be caused by the submission being made either too late or too early. ### CIP-712 - CIP-712 ensures that the signatures generated are valid only for this specific instance of the token contract and cannot be replayed on a different network with a different network ID. - This is achieved by incorporating the contract address and Core network ID in a SHA3-256 digest called the domain separator. Implementations MUST use the `EIP712Domain` type with the `verifyingContract` and `networkId` fields defined by CIP-712. ## Backwards Compatibility New contracts benefit from being able to directly utilize CIP-3009 in order to create atomic transactions, but existing contracts may still rely on the conventional CBC-20 allowance pattern (`approve`/`transferFrom`). In order to add support for CIP-3009 to existing contracts ("parent contract") that use the CBC-20 allowance pattern, a forwarding contract ("forwarder") can be constructed that takes an authorization and does the following: 1. Extract the user and deposit amount from the authorization 2. Call `receiveWithAuthorization` to transfer specified funds from the user to the forwarder 3. Approve the parent contract to spend funds from the forwarder 4. Call the method on the parent contract that spends the allowance set from the forwarder 5. Transfer the ownership of any resulting tokens back to the user **Example:** ```solidity interface IDeFiToken { function deposit(uint256 amount) external returns (uint256); function transfer(address account, uint256 amount) external returns (bool); } contract DepositForwarder { bytes4 private constant _RECEIVE_WITH_AUTHORIZATION_SELECTOR = 0x95b70f09; IDeFiToken private _parent; ICBC20 private _token; constructor(IDeFiToken parent, ICBC20 token) { _parent = parent; _token = token; } function deposit(bytes calldata receiveAuthorization) external nonReentrant returns (uint256) { (address from, address to, uint256 amount) = abi.decode( receiveAuthorization[0:96], (address, address, uint256) ); require(to == address(this), "Recipient is not this contract"); (bool success, ) = address(_token).call( abi.encodePacked( _RECEIVE_WITH_AUTHORIZATION_SELECTOR, receiveAuthorization ) ); require(success, "Failed to transfer to the forwarder"); require( _token.approve(address(_parent), amount), "Failed to set the allowance" ); uint256 tokensMinted = _parent.deposit(amount); require( _parent.transfer(from, tokensMinted), "Failed to transfer the minted tokens" ); uint256 remainder = _token.balanceOf(address(this)); if (remainder > 0) { require( _token.transfer(from, remainder), "Failed to refund the remainder" ); } return tokensMinted; } } ``` ## Implementation ### CIP3009.sol ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; import { EDDSA } from "openzeppelin-contracts/src/utils/cryptography/EDDSA.sol"; import { ICBC20Transfer } from "./ICBC20Transfer.sol"; import { CIP712Domain } from "./CIP712Domain.sol"; abstract contract CIP3009 is ICBC20Transfer, CIP712Domain { bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x9d376c88a2a578a58e393a82b4e9f8fd6827fbc3fefda07e6c245208f172c6d5; bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xe5b1053d7239a64a7f4b19e277dd6cd0f3a58ccfa14e164096e0c7e783587b4d; bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH = 0xbdf4caf0e95f58bce6aa01a60e4d47bdb240c01f81ea450b7d8354a78ee6fc5c; mapping(address => mapping(bytes32 => bool)) internal _authorizationStates; event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce); event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce); function authorizationState(address authorizer, bytes32 nonce) external view returns (bool) { return _authorizationStates[authorizer][nonce]; } function transferWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, bytes calldata signature ) external { _requireValidAuthorization(from, nonce, validAfter, validBefore); _requireValidSignature( from, keccak256( abi.encode( TRANSFER_WITH_AUTHORIZATION_TYPEHASH, from, to, value, validAfter, validBefore, nonce ) ), signature ); _markAuthorizationAsUsed(from, nonce); _transfer(from, to, value); } function receiveWithAuthorization( address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, bytes calldata signature ) external { require(to == msg.sender, "CIP3009: caller must be payee"); _requireValidAuthorization(from, nonce, validAfter, validBefore); _requireValidSignature( from, keccak256( abi.encode( RECEIVE_WITH_AUTHORIZATION_TYPEHASH, from, to, value, validAfter, validBefore, nonce ) ), signature ); _markAuthorizationAsUsed(from, nonce); _transfer(from, to, value); } function cancelAuthorization( address authorizer, bytes32 nonce, bytes calldata signature ) external { _requireUnusedAuthorization(authorizer, nonce); _requireValidSignature( authorizer, keccak256( abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, authorizer, nonce) ), signature ); _authorizationStates[authorizer][nonce] = true; emit AuthorizationCanceled(authorizer, nonce); } function _requireValidAuthorization( address authorizer, bytes32 nonce, uint256 validAfter, uint256 validBefore ) private view { require(block.timestamp > validAfter, "CIP3009: authorization is not yet valid"); require(block.timestamp < validBefore, "CIP3009: authorization is expired"); _requireUnusedAuthorization(authorizer, nonce); } function _requireUnusedAuthorization( address authorizer, bytes32 nonce ) private view { require( !_authorizationStates[authorizer][nonce], "CIP3009: authorization is used or canceled" ); } function _markAuthorizationAsUsed(address authorizer, bytes32 nonce) private { _authorizationStates[authorizer][nonce] = true; emit AuthorizationUsed(authorizer, nonce); } function _requireValidSignature( address signer, bytes32 structHash, bytes calldata signature ) private view { require(signature.length == 171, "CIP3009: invalid signature length"); bytes32 digest = EDDSA.toTypedDataHash(_domainSeparator(), structHash); require(EDDSA.recover(digest, signature) == signer, "CIP3009: invalid signature"); } } ``` ### ICBC20Transfer.sol ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; abstract contract ICBC20Transfer { function _transfer( address sender, address recipient, uint256 amount ) internal virtual; } ``` ### CIP712Domain.sol ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; abstract contract CIP712Domain { function DOMAIN_SEPARATOR() external view returns (bytes32) { return _domainSeparator(); } function _domainSeparator() internal view virtual returns (bytes32); } ``` ### CIP712.sol ```solidity // SPDX-License-Identifier: MIT pragma solidity ^1.1.2; library CIP712 { // keccak256("EIP712Domain(string name,string version,uint256 networkId,address verifyingContract)") bytes32 public constant CIP712_DOMAIN_TYPEHASH = 0xad9882c79b5c42809b78a08339443ad633a6d4ca03d62385cd985c860094a9fa; function makeDomainSeparator(string memory name, string memory version) internal view returns (bytes32) { return keccak256( abi.encode( CIP712_DOMAIN_TYPEHASH, keccak256(bytes(name)), keccak256(bytes(version)), block.chainid, address(this) ) ); } } ``` ## Security Considerations Use `receiveWithAuthorization` instead of `transferWithAuthorization` when calling from other smart contracts. It is possible for an attacker watching the transaction pool to extract the transfer authorization and front-run the `transferWithAuthorization` call to execute the transfer without invoking the wrapper function. This could potentially result in unprocessed, locked up deposits. `receiveWithAuthorization` prevents this by performing an additional check that ensures that the caller is the payee. Additionally, if there are multiple contract functions accepting receive authorizations, the app developer can dedicate some leading bytes of the nonce as an identifier to prevent cross-use. When submitting multiple transfers simultaneously, be mindful of the fact that relayers and miners will decide the order in which they are processed. This is generally not a problem if the transactions are not dependent on each other, but for transactions that are highly dependent on each other, it is recommended that the signed authorizations are submitted one at a time. Implementations MUST reject both `address(0)` and `Checksum.zeroAddress()` wherever a non-zero authorizer, payer, or payee is required. Core signatures MUST be passed as a single 171-byte blob. Core signature recovery failures hard revert rather than returning the zero address; implementations must treat every recovery failure or signer mismatch as an invalid authorization. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Tokenized Vaults Standard Source: https://cip.coreblockchain.net/cip/cbc/cip-4626/ Repository path: cip/cbc/cip-4626.md A standardized smart contract interface for tokenized vaults with a single underlying CBC-20 token, supporting deposit, withdrawal, and share accounting. ## Abstract The following standard allows for the implementation of a standard API for tokenized Vaults representing shares of a single underlying CIP-20 token. This standard is an extension on the CIP-20 token that provides basic functionality for depositing and withdrawing tokens and reading balances. ## Motivation Tokenized Vaults have a lack of standardization leading to diverse implementation details. Some various examples include lending markets, aggregators, and intrinsically interest bearing tokens. This makes integration difficult at the aggregator or plugin layer for protocols which need to conform to many standards, and forces each protocol to implement their own adapters which are error prone and waste development resources. A standard for tokenized Vaults will lower the integration effort for yield-bearing vaults, while creating more consistent and robust implementation patterns. ## Specification All CIP-4626 tokenized Vaults MUST implement CIP-20 to represent shares. If a Vault is to be non-transferrable, it MAY revert on calls to `transfer` or `transferFrom`. The CIP-20 operations `balanceOf`, `transfer`, `totalSupply`, etc. operate on the Vault "shares" which represent a claim to ownership on a fraction of the Vault's underlying holdings. All CIP-4626 tokenized Vaults MUST implement CIP-20's optional metadata extensions. The `name` and `symbol` functions SHOULD reflect the underlying token's `name` and `symbol` in some way. CIP-4626 tokenized Vaults MAY implement CIP-2612 to improve the UX of approving shares on various integrations. ### Definitions - asset: The underlying token managed by the Vault. Has units defined by the corresponding CIP-20 contract. - share: The token of the Vault. Has a ratio of underlying assets exchanged on mint/deposit/withdraw/redeem (as defined by the Vault). - fee: An amount of assets or shares charged to the user by the Vault. Fees can exists for deposits, yield, AUM, withdrawals, or anything else prescribed by the Vault. - slippage: Any difference between advertised share price and economic realities of deposit to or withdrawal from the Vault, which is not accounted by fees. ### Methods #### asset The address of the underlying token used for the Vault for accounting, depositing, and withdrawing. MUST be an CIP-20 token contract. MUST _NOT_ revert. ```yaml - name: asset type: function stateMutability: view inputs: [] outputs: - name: assetTokenAddress type: address ``` #### totalAssets Total amount of the underlying asset that is "managed" by Vault. SHOULD include any compounding that occurs from yield. MUST be inclusive of any fees that are charged against assets in the Vault. MUST _NOT_ revert. ```yaml - name: totalAssets type: function stateMutability: view inputs: [] outputs: - name: totalManagedAssets type: uint256 ``` #### convertToShares The amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met. MUST NOT be inclusive of any fees that are charged against assets in the Vault. MUST NOT show any variations depending on the caller. MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. MUST NOT revert unless due to integer overflow caused by an unreasonably large input. MUST round down towards 0. This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the "average-user's" price-per-share, meaning what the average user should expect to see when exchanging to and from. ```yaml - name: convertToShares type: function stateMutability: view inputs: - name: assets type: uint256 outputs: - name: shares type: uint256 ``` #### convertToAssets The amount of assets that the Vault would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met. MUST NOT be inclusive of any fees that are charged against assets in the Vault. MUST NOT show any variations depending on the caller. MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. MUST NOT revert unless due to integer overflow caused by an unreasonably large input. MUST round down towards 0. This calculation MAY NOT reflect the "per-user" price-per-share, and instead should reflect the "average-user's" price-per-share, meaning what the average user should expect to see when exchanging to and from. ```yaml - name: convertToAssets type: function stateMutability: view inputs: - name: shares type: uint256 outputs: - name: assets type: uint256 ``` #### maxDeposit Maximum amount of the underlying asset that can be deposited into the Vault for the `receiver`, through a `deposit` call. MUST return the maximum amount of assets `deposit` would allow to be deposited for `receiver` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). This assumes that the user has infinite assets, i.e. MUST NOT rely on `balanceOf` of `asset`. MUST factor in both global and user-specific limits, like if deposits are entirely disabled (even temporarily) it MUST return 0. MUST return `2 ** 256 - 1` if there is no limit on the maximum amount of assets that may be deposited. MUST NOT revert. ```yaml - name: maxDeposit type: function stateMutability: view inputs: - name: receiver type: address outputs: - name: maxAssets type: uint256 ``` #### previewDeposit Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions. MUST return as close to and no more than the exact amount of Vault shares that would be minted in a `deposit` call in the same transaction. I.e. `deposit` should return the same or more `shares` as `previewDeposit` if called in the same transaction. MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the deposit would be accepted, regardless if the user has enough tokens approved, etc. MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `deposit` to revert. Note that any unfavorable discrepancy between `convertToShares` and `previewDeposit` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. ```yaml - name: previewDeposit type: function stateMutability: view inputs: - name: assets type: uint256 outputs: - name: shares type: uint256 ``` #### deposit Mints `shares` Vault shares to `receiver` by depositing exactly `assets` of underlying tokens. MUST emit the `Deposit` event. MUST support CIP-20 `approve` / `transferFrom` on `asset` as a deposit flow. MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `deposit` execution, and are accounted for during `deposit`. MUST revert if all of `assets` cannot be deposited (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. ```yaml - name: deposit type: function stateMutability: nonpayable inputs: - name: assets type: uint256 - name: receiver type: address outputs: - name: shares type: uint256 ``` #### maxMint Maximum amount of shares that can be minted from the Vault for the `receiver`, through a `mint` call. MUST return the maximum amount of shares `mint` would allow to be deposited to `receiver` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). This assumes that the user has infinite assets, i.e. MUST NOT rely on `balanceOf` of `asset`. MUST factor in both global and user-specific limits, like if mints are entirely disabled (even temporarily) it MUST return 0. MUST return `2 ** 256 - 1` if there is no limit on the maximum amount of shares that may be minted. MUST NOT revert. ```yaml - name: maxMint type: function stateMutability: view inputs: - name: receiver type: address outputs: - name: maxShares type: uint256 ``` #### previewMint Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions. MUST return as close to and no fewer than the exact amount of assets that would be deposited in a `mint` call in the same transaction. I.e. `mint` should return the same or fewer `assets` as `previewMint` if called in the same transaction. MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint would be accepted, regardless if the user has enough tokens approved, etc. MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `mint` to revert. Note that any unfavorable discrepancy between `convertToAssets` and `previewMint` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by minting. ```yaml - name: previewMint type: function stateMutability: view inputs: - name: shares type: uint256 outputs: - name: assets type: uint256 ``` #### mint Mints exactly `shares` Vault shares to `receiver` by depositing `assets` of underlying tokens. MUST emit the `Deposit` event. MUST support CIP-20 `approve` / `transferFrom` on `asset` as a mint flow. MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the `mint` execution, and are accounted for during `mint`. MUST revert if all of `shares` cannot be minted (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc). Note that most implementations will require pre-approval of the Vault with the Vault's underlying `asset` token. ```yaml - name: mint type: function stateMutability: nonpayable inputs: - name: shares type: uint256 - name: receiver type: address outputs: - name: assets type: uint256 ``` #### maxWithdraw Maximum amount of the underlying asset that can be withdrawn from the `owner` balance in the Vault, through a `withdraw` call. MUST return the maximum amount of assets that could be transferred from `owner` through `withdraw` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). MUST factor in both global and user-specific limits, like if withdrawals are entirely disabled (even temporarily) it MUST return 0. MUST NOT revert. ```yaml - name: maxWithdraw type: function stateMutability: view inputs: - name: owner type: address outputs: - name: maxAssets type: uint256 ``` #### previewWithdraw Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions. MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a `withdraw` call in the same transaction. I.e. `withdraw` should return the same or fewer `shares` as `previewWithdraw` if called in the same transaction. MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though the withdrawal would be accepted, regardless if the user has enough shares, etc. MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `withdraw` to revert. Note that any unfavorable discrepancy between `convertToShares` and `previewWithdraw` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing. ```yaml - name: previewWithdraw type: function stateMutability: view inputs: - name: assets type: uint256 outputs: - name: shares type: uint256 ``` #### withdraw Burns `shares` from `owner` and sends exactly `assets` of underlying tokens to `receiver`. MUST emit the `Withdraw` event. MUST support a withdraw flow where the shares are burned from `owner` directly where `owner` is `msg.sender`. MUST support a withdraw flow where the shares are burned from `owner` directly where `msg.sender` has CIP-20 approval over the shares of `owner`. MAY support an additional flow in which the shares are transferred to the Vault contract before the `withdraw` execution, and are accounted for during `withdraw`. SHOULD check `msg.sender` can spend owner funds, assets needs to be converted to shares and shares should be checked for allowance. MUST revert if all of `assets` cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. ```yaml - name: withdraw type: function stateMutability: nonpayable inputs: - name: assets type: uint256 - name: receiver type: address - name: owner type: address outputs: - name: shares type: uint256 ``` #### maxRedeem Maximum amount of Vault shares that can be redeemed from the `owner` balance in the Vault, through a `redeem` call. MUST return the maximum amount of shares that could be transferred from `owner` through `redeem` and not cause a revert, which MUST NOT be higher than the actual maximum that would be accepted (it should underestimate if necessary). MUST factor in both global and user-specific limits, like if redemption is entirely disabled (even temporarily) it MUST return 0. MUST NOT revert. ```yaml - name: maxRedeem type: function stateMutability: view inputs: - name: owner type: address outputs: - name: maxShares type: uint256 ``` #### previewRedeem Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions. MUST return as close to and no more than the exact amount of assets that would be withdrawn in a `redeem` call in the same transaction. I.e. `redeem` should return the same or more `assets` as `previewRedeem` if called in the same transaction. MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the redemption would be accepted, regardless if the user has enough shares, etc. MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. MUST NOT revert due to vault specific user/global limits. MAY revert due to other conditions that would also cause `redeem` to revert. Note that any unfavorable discrepancy between `convertToAssets` and `previewRedeem` SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by redeeming. ```yaml - name: previewRedeem type: function stateMutability: view inputs: - name: shares type: uint256 outputs: - name: assets type: uint256 ``` #### redeem Burns exactly `shares` from `owner` and sends `assets` of underlying tokens to `receiver`. MUST emit the `Withdraw` event. MUST support a redeem flow where the shares are burned from `owner` directly where `owner` is `msg.sender`. MUST support a redeem flow where the shares are burned from `owner` directly where `msg.sender` has CIP-20 approval over the shares of `owner`. MAY support an additional flow in which the shares are transferred to the Vault contract before the `redeem` execution, and are accounted for during `redeem`. SHOULD check `msg.sender` can spend owner funds using allowance. MUST revert if all of `shares` cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc). Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately. ```yaml - name: redeem type: function stateMutability: nonpayable inputs: - name: shares type: uint256 - name: receiver type: address - name: owner type: address outputs: - name: assets type: uint256 ``` ### Events #### Deposit `sender` has exchanged `assets` for `shares`, and transferred those `shares` to `owner`. MUST be emitted when tokens are deposited into the Vault via the `mint` and `deposit` methods. ```yaml - name: Deposit type: event inputs: - name: sender indexed: true type: address - name: owner indexed: true type: address - name: assets indexed: false type: uint256 - name: shares indexed: false type: uint256 ``` #### Withdraw `sender` has exchanged `shares`, owned by `owner`, for `assets`, and transferred those `assets` to `receiver`. MUST be emitted when shares are withdrawn from the Vault in `CIP-4626.redeem` or `CIP-4626.withdraw` methods. ```yaml - name: Withdraw type: event inputs: - name: sender indexed: true type: address - name: receiver indexed: true type: address - name: owner indexed: true type: address - name: assets indexed: false type: uint256 - name: shares indexed: false type: uint256 ``` ## Rationale The Vault interface is designed to be optimized for integrators with a feature complete yet minimal interface. Details such as accounting and allocation of deposited tokens are intentionally not specified, as Vaults are expected to be treated as black boxes on-chain and inspected off-chain before use. CIP-20 is enforced because implementation details like token approval and balance calculation directly carry over to the shares accounting. This standardization makes the Vaults immediately compatible with all CIP-20 use cases in addition to CIP-4626. The mint method was included for symmetry and feature completeness. Most current use cases of share-based Vaults do not ascribe special meaning to the shares such that a user would optimize for a specific number of shares (`mint`) rather than specific amount of underlying (`deposit`). However, it is easy to imagine future Vault strategies which would have unique and independently useful share representations. The `convertTo` functions serve as rough estimates that do not account for operation specific details like withdrawal fees, etc. They were included for frontends and applications that need an average value of shares or assets, not an exact value possibly including slippage or other fees. For applications that need an exact value that attempts to account for fees and slippage we have included a corresponding `preview` function to match each mutable function. These functions must not account for deposit or withdrawal limits, to ensure they are easily composable, the `max` functions are provided for that purpose. ## Backwards Compatibility CIP-4626 is fully backward compatible with the CIP-20 standard and has no known compatibility issues with other standards. For production implementations of Vaults which do not use CIP-4626, wrapper adapters can be developed and used. ## Security Considerations Fully permissionless use cases could fall prey to malicious implementations which only conform to the interface but not the specification. It is recommended that all integrators review the implementation for potential ways of losing user deposits before integrating. If implementors intend to support EOA account access directly, they should consider adding an additional function call for `deposit`/`mint`/`withdraw`/`redeem` with the means to accommodate slippage loss or unexpected deposit/withdrawal limits, since they have no other means to revert the transaction if the exact output amount is not achieved. The methods `totalAssets`, `convertToShares` and `convertToAssets` are estimates useful for display purposes, and do _not_ have to confer the _exact_ amount of underlying assets their context suggests. The `preview` methods return values that are as close as possible to exact as possible. For that reason, they are manipulable by altering the on-chain conditions and are not always safe to be used as price oracles. This specification includes `convert` methods that are allowed to be inexact and therefore can be implemented as robust price oracles. For example, it would be correct to implement the `convert` methods as using a time-weighted average price in converting between assets and shares. Integrators of CIP-4626 Vaults should be aware of the difference between these view methods when integrating with this standard. Additionally, note that the amount of underlying assets a user may receive from redeeming their Vault shares (`previewRedeem`) can be significantly different than the amount that would be taken from them when minting the same quantity of shares (`previewMint`). The differences may be small (like if due to rounding error), or very significant (like if a Vault implements withdrawal or deposit fees, etc). Therefore integrators should always take care to use the preview function most relevant to their use case, and never assume they are interchangeable. Finally, CIP-4626 Vault implementers should be aware of the need for specific, opposing rounding directions across the different mutable and view methods, as it is considered most secure to favor the Vault itself during calculations over its users: - If (1) it's calculating how many shares to issue to a user for a certain amount of the underlying tokens they provide or (2) it's determining the amount of the underlying tokens to transfer to them for returning a certain amount of shares, it should round _down_. - If (1) it's calculating the amount of shares a user has to supply to receive a given amount of the underlying tokens or (2) it's calculating the amount of underlying tokens a user has to provide to receive a certain amount of shares, it should round _up_. The only functions where the preferred rounding direction would be ambiguous are the `convertTo` functions. To ensure consistency across all CIP-4626 Vault implementations it is specified that these functions MUST both always round _down_. Integrators may wish to mimic rounding up versions of these functions themselves, like by adding 1 wei to the result. Although the `convertTo` functions should eliminate the need for any use of an CIP-4626 Vault's `decimals` variable, it is still strongly recommended to mirror the underlying token's `decimals` if at all possible, to eliminate possible sources of confusion and simplify integration across front-ends and for other off-chain users. ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # Core Denomination Units Source: https://cip.coreblockchain.net/cip/informational/cip-9/ Repository path: cip/informational/cip-9.md Informational specification describing Core's denomination units, canonical source-of-truth file, and normative client behaviors for display, parsing, and conversion between units. ## Abstract This Informational CIP documents the standard denomination units for the Core Blockchain and defines normative behaviors for clients when presenting amounts, parsing user input, and calculating fees. The canonical registry of units is maintained in a machine-readable JSON file. ## Motivation Divergent unit handling leads to UX confusion, rounding drift, and inconsistent fee displays across wallets, explorers, and SDKs. This CIP provides a single point of reference and prescribes minimal client rules so that amounts render, parse, and convert consistently across the Core ecosystem. ## Specification ### Denomination Units table | name | alias | unit | symbol | SI | value | | ----------- | ------ | ---- | ------ | --------- | ----- | | ore | ore | ø | 🔷 | atto (a) | 1E+00 | | wav | fecore | | 〰️ | femto (f) | 1E+03 | | grav | picore | | ➰ | pico (p) | 1E+06 | | nucle | nacore | ꞥ | ✴️ | nano (n) | 1E+09 | | atom | μcore | | ⚛️ | micro (μ) | 1E+12 | | moli | micore | ₥ | ❇️ | mili (m) | 1E+15 | | core | core | ₡ | 🟢️️ | | 1E+18 | | aer | kicore | ₳ | 🌀️ | kilo (k) | 1E+21 | | orb | Mecore | | ☄️ | mega (M) | 1E+24 | | plano | Gicore | Ᵽ | 🪐️ | giga (G) | 1E+27 | | tera | Tecore | | 🌐️ | tera (T) | 1E+30 | | sola | Pecore | | 💫️ | peta (P) | 1E+33 | | galx | Excore | Ǥ | ✨️ | exa (E) | 1E+36 | | cluster | Zecore | | 💠️ | zetta (Z) | 1E+39 | | supermatter | Yocore | ₷ | 🔱 | yotta (Y) | 1E+42 | ## JSON structure (schema) At a high level, the registry enumerates units from largest **display** units to the smallest **base** unit. Each entry specifies its **name**, **symbol**, **exponent** to the base, and any **aliases** suitable for parsing. > The following schema is illustrative; implementers MUST follow the fields and > semantics of the canonical JSON file. ```typescript /** * Denomination unit entry (example shape). * * - `name` : unit name (e.g., "moli") * - `alias` : SI-style prefixed base unit (e.g., "micore") * - `unit` : Unicode symbol for the unit (e.g., "₥") * - `symbol`: emoji symbol (e.g., "❇️") * - `si` : tuple [full SI name, short SI symbol] (e.g., ["mili","m"]) * - `value` : power-of-ten multiplier relative to the base unit (e.g., 1e15) */ export interface DenominationUnit { name: string; alias: string; unit: string; // Unicode glyph symbol: string; // Emoji si: [name: string, symbol: string]; value: number; // e.g., 1e15 } ``` ## Rationale * A single, machine-readable registry prevents drift and hard-coded unit tables across clients. * Defining the units in a single file allows the ecosystem to evolve denominations without protocol forks. * Normative but minimal client rules (base for accounting, display for UX, strict parsing, precise integer conversions) balance safety and usability. ## Backwards Compatibility This CIP is **non-breaking**. It does not modify Core consensus or transaction formats. Existing applications may continue with embedded unit tables; however, they are **RECOMMENDED** to migrate to the canonical registry to ensure consistency across the ecosystem. ## Security Considerations * **Phishing/typos**: Ambiguous or deceptive aliases MUST NOT be accepted in parsers. Wallets SHOULD present the symbol and code prominently. * **Rounding drift**: Always convert via the base unit and avoid floating-point arithmetic to prevent silent precision loss. * **Staleness**: Pin and periodically refresh the registry; stale unit tables can lead to inconsistent UX and fee suggestions. ## References * Canonical registry: [units.json at GitHub](https://github.com/core-coin/core-denomination/blob/master/units.json) * IPFS (may change with time): [units.json at IPFS](ipfs://bafkreif7bznw6b3wpx6a4ut32okhhupqnrsjrbw6nyr2fjn5a2chtux7hm) ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). --- # PayTo URI Scheme Source: https://cip.coreblockchain.net/cip/informational/cip-10/ Repository path: cip/informational/cip-10.md Informational specification describing how the Core ecosystem SHOULD use the RFC 8905 PayTo URI scheme for linking to payment targets on Core, and how wallets/SDKs SHOULD parse, validate, and act on those URIs. ## Abstract This CIP documents the adoption of the IETF PayTo URI scheme for Core. A PayTo URI always identifies the target of a payment and encodes a payment target type, a target identifier, and optional parameters like `amount` or `message`. We define Core-specific conventions so that wallets and applications can interoperate reliably across the ecosystem. ([RFC Editor][1]) ## Motivation Users and applications benefit from a common, linkable payment format that works across wallets, websites, QR codes, and desktop/mobile platforms. PayTo is a standardized URI scheme already used for bank accounts and cryptocurrency addresses. Standardizing Core usage enables: * clickable **"Pay via XCB"** links (XCB is the Core target type), * QR codes that launch Core wallets with prefilled details, * safer parsing and confirmation UX, * and long-term interoperability with other payment systems using the same URI scheme. ([RFC Editor][1]) ## Specification ### General 'PayTo' syntax (by reference) Core adopts the generic PayTo syntax and semantics from RFC 8905 (Sections 2–3, 5). In short: `payto://` *authority* `/` *path* `?` *options* Where the *authority* is the payment target type, the *path* encodes the target identifier, and *options* include `amount`, `receiver-name`, `sender-name`, `message`, and `instruction`. ([RFC Editor][1]) ### Core target type * **Target type name**: `xcb` for Mainnet, `xab` for Testnet, `xce` for Enterprise Network. * **Path**: an ICAN account address, `cb…` for Mainnet, `ab…` for Testnet, `ce…` for Enterprise Network. * **Semantics**: native CORE coin transfers to the address given in *path*. ### Options Core wallets and SDKs MUST support the **generic** options from RFC 8905: `amount`, etc. ([RFC Editor][1]) #### `amount` RFC 8905 defines: `amount = currency ":" unit [ "." fraction ]` with the constraints: `unit < 2^53` and at most **8** fractional digits. ([RFC Editor][1]) If no currency is specified, the default is `CORE` main unit. **Recommendations:** * For **native** amounts, use amounts without currency and `:` colon. For example, `amount=1` is 1 CORE. * For **contract-based** amounts, the amount is prefixed with contract address or contract ticker (for well-known contracts defined by application), divided by `:` colon. For example, `amount=ctn:1` is 1 CTN. * If you want to keep just currency without amount, keep `:` colon typed to avoid confusion. For example, `amount=ctn:` is CTN currency with custom amount. ### Examples * Human-friendly in CORE: `payto://xcb/cb…?amount=1.25` * Payment with CTN: `payto://xcb/cb…?amount=ctn:1.14` ### QR and deep-link handling * Wallets SHOULD register PayTo handlers on supported platforms to open URIs. * QR codes SHOULD embed the full `payto://` URI. * If multiple wallets are installed, the user SHOULD be able to choose which wallet to launch. ([RFC Editor][1]) ## Rationale * Reuse of an IETF-standard URI decouples Core from vendor-specific deep link formats and eases multi-wallet support. * Keeping token/contract semantics out of scope avoids fragmentation; native coin payments can adopt immediately. ## Backwards Compatibility This proposal is **non-breaking**. It does not modify Core consensus or transaction formats. Wallets without PayTo handling remain functional; links simply won’t auto-open those apps. ## Security Considerations * Wallets MUST require explicit user confirmation before sending and MUST take measures against clickjacking. * URIs received over unauthenticated channels might target the wrong account; wallets SHOULD display the full address and take care with homographs in labels/messages. * Avoid collecting unnecessary PII from `sender-name`/`receiver-name` fields; store only what is needed for receipts. ([RFC Editor][1]) ## References * RFC 8905: *The ‘payto’ URI Scheme for Payments* — RFC Editor (syntax, semantics, options). ([RFC Editor][1]) * Core PayTo scheme documentation — `core-laboratories/payto` (implementation guidance and examples). ([GitHub][2]) * PayTo link generator — [PayTo.Money](https://payto.money) ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). [1]: https://www.rfc-editor.org/rfc/rfc8905.html "RFC 8905: The 'payto' URI Scheme for Payments" [2]: https://github.com/core-laboratories/payto/blob/master/docs/scheme.md "payto/docs/scheme.md at master · core-laboratories/payto · GitHub"