{"id":24054595,"url":"https://github.com/cardinal-cryptography/psp34","last_synced_at":"2026-03-06T19:35:00.775Z","repository":{"id":217276376,"uuid":"705584602","full_name":"Cardinal-Cryptography/PSP34","owner":"Cardinal-Cryptography","description":"PSP34 Implementation","archived":false,"fork":false,"pushed_at":"2025-03-11T11:50:03.000Z","size":81,"stargazers_count":9,"open_issues_count":1,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-09-23T09:28:11.335Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Cardinal-Cryptography.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-10-16T09:51:35.000Z","updated_at":"2025-03-11T11:50:08.000Z","dependencies_parsed_at":"2025-04-22T20:45:39.496Z","dependency_job_id":"e175207d-d113-4bff-88e2-5adb471526f7","html_url":"https://github.com/Cardinal-Cryptography/PSP34","commit_stats":null,"previous_names":["cardinal-cryptography/psp34"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Cardinal-Cryptography/PSP34","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cardinal-Cryptography%2FPSP34","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cardinal-Cryptography%2FPSP34/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cardinal-Cryptography%2FPSP34/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cardinal-Cryptography%2FPSP34/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Cardinal-Cryptography","download_url":"https://codeload.github.com/Cardinal-Cryptography/PSP34/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cardinal-Cryptography%2FPSP34/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30193649,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T19:07:06.838Z","status":"ssl_error","status_checked_at":"2026-03-06T18:57:34.882Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-09T03:48:49.115Z","updated_at":"2026-03-06T19:35:00.756Z","avatar_url":"https://github.com/Cardinal-Cryptography.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PSP34 Non-Fungible Token\n\n**Note that both the implementation and the psp34 standard are under development and are subjected to change. Not production-ready as of yet.**\n\nPSP34 is a non-fungible token standard for WebAssembly smart contracts running on blockchains based on the [Substrate][substrate] framework. It is an equivalent of Ethereum's [ERC-721][erc721]. The definition of the PSP34 standard can be found [here][psp34].\n\nThis repository contains a simple, minimal implementation of the PSP34 token in [ink!][ink] programming language.\n\n## How to use this repository\n\nTo use this crate please add the following line in your `Cargo.toml`:\n```\npsp34 = { git = \"https://github.com/Cardinal-Cryptography/PSP34.git\", default-features = false }\n```\n\nThe contents of this repository can be used in following ways:\n\n### 1. Ready to use contract\n\nThe file [`lib.rs`][lib] contains a ready to use implementation of basic PSP34 token contract. To use it, please check out this repository and compile its contents with [`cargo-contract`][cargo-contract] with the `\"contract\"` feature enabled:\n```\n$ cargo contract build --release --features \"contract\"\n```\n### 2. Cross contract calling with traits\n\nThe `PSP34` trait contains all the methods defined in the PSP34 standard. The trait can be used together with ink!'s [`contract_ref`][contract_ref] macro to allow for convenient cross-contract calling.\n\nIn your contract, if you would like to make a call to some other contract implementing the PSP34 standard, all you need to do is:\n```rust\nuse ink::contract_ref;\nuse psp34::PSP34;\n\nlet mut token: contract_ref!(PSP34) = other_address.into();\n\n// Now `token` has all the PSP34 methods\nlet balance = token.balance_of(some_account);\ntoken.transfer(recipient, value, vec![]); // returns Result\u003c(), PSP34Error\u003e\n```\n\nThe same method can be used with other traits (`PSP34Metadata`, `PSP34Burnable`, `PSP34Mintable`) defined in this crate. See the contents of [`traits.rs`][traits].\n\n\n### 3. Custom implementation of PSP34 logic with `PSP34Data`\n\nThe `PSP34Data` class can be used to extend your contract with PSP34 token logic. In other words, you can easily build contracts that implement PSP34 interface alongside some other functionalities defined by the business logic of your project.\n\nThe methods of the `PSP34Data` class correspond directly to queries and operations defined by the PSP34 token standard. To make your contract become a PSP34 token, you need to:\n - Put a single `PSP34Data` instance in your contract's storage and initialize it with some starting supply of tokens.\n - Add definitions of `Transfer`, `Approval` and `AttributeSet` events in the body of your contract.\n - Add the `impl PSP34 for [struct_name]` block with implementation of PSP34 trait messages using `PSP34Data` methods. Each method which mutates the state of the token database returns a `Result\u003cVec\u003cPSP34Event\u003e, PSP34Error\u003e` with all events generated by that operation. Please make sure to handle errors correctly and emit the resulting events (see the `emit_events` function).\n - Optionally implement also the `PSP34Metadata` trait to make your token play nice with other ecosystem tools.\n\nThe contract in [`lib.rs`][lib] contains an example implementation following all the above steps. Feel free to copy-paste parts of it.\n\n### 4. Burnable and Mintable extensions\n\nThe `PSP34Data` class contains also `burn` and `mint` methods, which can be used to implement `PSP34Burnable` and `PSP34Mintable` extensions and make your token burnable and/or mintable. An example implementation follows the same pattern as for the base trait:\n```rust\nimpl PSP34Burnable for Token {\n    #[ink(message)]\n    fn burn(\u0026mut self, value: u128) -\u003e Result\u003c(), PSP34Error\u003e {\n        // Check if the caller is allowed to burn!\n        let events = self.data.burn(self.env().caller(), value)?;\n        self.emit_events(events);\n        Ok(())\n    }\n}\n```\nPlease note that `PSP34Data` `burn` and `mint` methods do not enforce any form of access control. It's probably not a good idea to have a token which can be minted and burned by anyone anytime. When implementing Burnable and Mintable extensions, please make sure that their usage is restricted according to your project's business logic. For example:\n```rust\n#[ink(storage)]\npub struct Token {\n    data: PSP34Data,\n    owner: AccountId, // creator of the token\n}\n\nimpl Token {\n    #[ink(constructor)]\n    pub fn new() -\u003e Self {\n        Self {\n            data: PSP34Data::new(),\n            owner: Self::env().caller(),\n        }\n    }\n// ...\n}\n\nimpl PSP34Burnable for Token {\n    #[ink(message)]\n    fn burn(\u0026mut self, id: Id) -\u003e Result\u003c(), PSP34Error\u003e {\n        if self.env().caller() != self.owner {\n            return PSP34Error::Custom(String::from(\"Only owner can burn\"));\n        }\n        let events = self.data.burn(self.env().caller(), id)?;\n        self.emit_events(events);\n        Ok(())\n    }\n}\n```\n\n### 5. Enumerable extension\n\nThis is an optional extension that allows enumerating tokens on the chain. Enabling the extension will introduce a large gas overhead.\n\nCan be implemented by enabling `enumerable` feature enabled while compiling the contents of the repository. To access its messages simply implement the `PSP34Enumerable` trait for your token:\n```rust\n#[ink(storage)]\npub struct Token {\n    data: PSP34Data,\n}\n\n//...\n\nimpl PSP34Enumerable for Token {\n    #[ink(message)]\n    fn owners_token_by_index(\u0026self, owner: AccountId, index: u128) -\u003e Result\u003cId, PSP34Error\u003e {\n        self.data.owners_token_by_index(owner, index)\n    }\n\n    #[ink(message)]\n    fn token_by_index(\u0026self, index: u128) -\u003e Result\u003cId, PSP34Error\u003e {\n        self.data.token_by_index(index)\n    }\n}\n```\n\n### 6. Metadata extension\n\nWithin the crate's `metadata::Data` there is also a `set_attribute()` method. It is generally used in conjunction with `mint()` from the `PSP34Mintable`. Note that the `set_attribute()` method will emit the `AttributeSet` event.\n\n### 7. Unit testing\n\nThis crate comes with a suite of unit tests for PSP34 tokens. It can be easily added to your contract's unit tests with a helper macro `tests!`. For the macro to work you need to implement `PSP34Burnable` and `PSP34Mintable` traits. The macro should be invoked inside the main contract's module (the one annotated with `#[ink::contract]`):\n```rust\n#[ink::contract]\nmod mycontract {\n    ...\n    #[ink(storage)]\n    pub struct MyContract { ... }\n    ...\n    #[cfg(test)]\n    mod tests { \n        crate::tests!(Token, Token::new);\n    }\n}\n```\nAs you can see in the code snippet above, the `tests!` macro takes two arguments. The first one should be a name of a struct which implements `PSP34` trait (usually your contract storage struct). The second argument should be a token constructor for the contract. In other words, the second argument should be a name of a function that returns the `PSP34` struct.\n\n## Implementation-Specific Details\n\nIn certain scenarios, the PSP34 standard does not strictly define the behavior, and this section outlines the non-specified behavior in the current implementation. The methods discussed here can be found in [`data.rs`][data].\n\n### 1. Id type\n\nThe type does not have a custom equals method implemented. Consequently `Id::U8(1)` is not equal to `Id::U16(1)`, for example.\n\n### 2. Approval\n\nThe `approve()` method follows a \"fall-through\" approval logic for a single token. If an approved user calls this method, they will subsequently issue an approval for the owner's token to a third-party operator.\n\nThis behavior does not extend to \"blanket\" approvals. Approving for all tokens only grants approval for the caller's owned tokens. Additionally, for enhanced security, `approve()` does not allow revoking approval for a single token when the operator is approved for all tokens using \n```\napprove(caller, operator, None::\u003cId\u003e, true)\n```\n\n### 3. Metadata\n\nThe `set_attribute()` method recommended implementation is included into the [`metadata.rs`][metadata]\nIt is a good practice to use the method together with `mint()` method.\n\n### 4. Balance of\n\nReturn type of the `balance_of()` method is `u32`, while the `total_supply` value is `u128`, be wary of possible overflows.\n\n[data]: ./data.rs\n[lib]: ./lib.rs\n[traits]: ./traits.rs\n[ink]: https://use.ink\n[metadata]: ./metadata.rs\n[substrate]: https://substrate.io\n[cargo-contract]: https://github.com/paritytech/cargo-contract\n[erc721]: https://ethereum.org/en/developers/docs/standards/tokens/erc-721/\n[psp34]: https://github.com/w3f/PSPs/blob/master/PSPs/psp-34.md\n[contract_ref]: https://paritytech.github.io/ink/ink/macro.contract_ref.html","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcardinal-cryptography%2Fpsp34","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcardinal-cryptography%2Fpsp34","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcardinal-cryptography%2Fpsp34/lists"}