{"id":14956173,"url":"https://github.com/lil-org/how-to-sync-nft-folders","last_synced_at":"2025-07-19T15:04:09.922Z","repository":{"id":242946906,"uuid":"811023894","full_name":"lil-org/how-to-sync-nft-folders","owner":"lil-org","description":"organize nfts onchain","archived":false,"fork":false,"pushed_at":"2025-04-16T19:53:35.000Z","size":89,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-17T05:22:49.662Z","etag":null,"topics":["eas","ipfs","nfts"],"latest_commit_sha":null,"homepage":"","language":"Markdown","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lil-org.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2024-06-05T19:48:12.000Z","updated_at":"2025-04-16T19:53:38.000Z","dependencies_parsed_at":"2024-08-13T21:27:07.746Z","dependency_job_id":"e479a908-f1ec-42ec-bf15-8347622afbf9","html_url":"https://github.com/lil-org/how-to-sync-nft-folders","commit_stats":{"total_commits":63,"total_committers":1,"mean_commits":63.0,"dds":0.0,"last_synced_commit":"4ff897d59ed19f23bb67928282086b9d021a9722"},"previous_names":["lil-org/onchain-folders"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lil-org/how-to-sync-nft-folders","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lil-org%2Fhow-to-sync-nft-folders","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lil-org%2Fhow-to-sync-nft-folders/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lil-org%2Fhow-to-sync-nft-folders/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lil-org%2Fhow-to-sync-nft-folders/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lil-org","download_url":"https://codeload.github.com/lil-org/how-to-sync-nft-folders/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lil-org%2Fhow-to-sync-nft-folders/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265950393,"owners_count":23853753,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["eas","ipfs","nfts"],"created_at":"2024-09-24T13:12:26.128Z","updated_at":"2025-07-19T15:04:09.912Z","avatar_url":"https://github.com/lil-org.png","language":"Markdown","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e organize [your own nfts](https://warpcast.com/hot/0x7455c2f6) or assemble collections [across different owners and authors](https://x.com/artignatyev/status/1803864822129529197)\n\n# how to sync nft folders\n\nuse [ethereum attestation service](https://base.easscan.org/schema/view/0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b)\n\n1 folder = 1 attestation\n\nread and write folders in 5 steps:\n\n### 1️⃣ upload `FolderSnapshot` json to ipfs\n```swift\nstruct FolderSnapshot: Codable {\n    let tokens: [Token]\n    let description: String?\n    let cover: String?\n}\n\nstruct Token: Codable {\n    let id: String\n    let address: String\n    let chainId: String\n    let comment: String?\n}\n```\n\n### 2️⃣ create an attestation with `FolderSnapshot` ipfs cid\n\n```swift\nlet cid = \"bafkreia4i7jwb5qq4upcdl5knvgtvvrqhjchsqsot6vozd26vcabgoed44\"\nlet folderName = \"zorbs\"\nlet nameAndCid = folderName + \" \" + cid\nlet folderType: UInt8 = 42\n\n// use 42 when you organize your own nfts\n// use 69 when assembling custom boards\n\nlet schemaId = \"0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b\"\nlet arguments = String.paddedHexString(folderType, nameAndCid)\n\nlet template = \"#template=::0:false:\\(arguments)\"\nlet url = \"https://base.easscan.org/attestation/attestWithSchema/\" + schemaId + template\n```\nsee [example new attestation url](https://base.easscan.org/attestation/attestWithSchema/0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b#template=::0:true:0x000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000417a6f726273206261666b726569613469376a776235717134757063646c356b6e76677476767271686a63687371736f7436766f7a64323676636162676f6564343400000000000000000000000000000000000000000000000000000000000000)\n\n\u003e [!TIP]\n\u003e use `multiAttest` to batch multiple attestations into a single transaction\n\n### 3️⃣ get folders\nuse [easscan graphql api](https://docs.attest.org/docs/developer-tools/api)\n\nattestations with an empty `refUID` correspond to all created folders\n\nattestations with a non-zero `refUID` correspond to folder edits\n\n#### 📁 for your own nfts organized\n```graphql\nquery Attestation {\n    attestations(\n        take: 20,\n        skip: 0,\n        orderBy: { timeCreated: desc },\n        where: { \n            schemaId: { equals: \"0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b\" }, \n            attester: { equals: \"0xE26067c76fdbe877F48b0a8400cf5Db8B47aF0fE\" }, # owner address\n            refUID: { equals: \"0x0000000000000000000000000000000000000000000000000000000000000000\" }, # created folders only\n            revoked: { equals: false },\n            data: { startsWith: \"0x000000000000000000000000000000000000000000000000000000000000002a\"} # corresponds to folderType 42\n        }\n    ) {\n        decodedDataJson\n        refUID\n        id\n    }\n}\n```\n```sh\ncurl --request POST --header 'content-type: application/json' --url 'https://base.easscan.org/graphql' --data '{\"query\":\"query Attestation { attestations(take: 20, skip: 0, orderBy: { timeCreated: desc }, where: { schemaId: { equals: \\\"0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b\\\" }, attester: { equals: \\\"0xE26067c76fdbe877F48b0a8400cf5Db8B47aF0fE\\\" }, refUID: { equals: \\\"0x0000000000000000000000000000000000000000000000000000000000000000\\\" }, revoked: { equals: false }, data: { startsWith: \\\"0x000000000000000000000000000000000000000000000000000000000000002a\\\" } } ) { decodedDataJson refUID id } }\"}'\n```\n\n#### 🍒 for custom boards assembled by you\n```graphql\nquery Attestation {\n    attestations(\n        take: 20,\n        skip: 0,\n        orderBy: { timeCreated: desc },\n        where: { \n            schemaId: { equals: \"0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b\" }, \n            attester: { equals: \"0xE26067c76fdbe877F48b0a8400cf5Db8B47aF0fE\" }, # curator address\n            refUID: { equals: \"0x0000000000000000000000000000000000000000000000000000000000000000\" }, # created folders only\n            revoked: { equals: false },\n            data: { startsWith: \"0x0000000000000000000000000000000000000000000000000000000000000045\"} # corresponds to folderType 69\n        }\n    ) {\n        decodedDataJson\n        refUID\n        id\n    }\n}\n```\n```sh\ncurl --request POST --header 'content-type: application/json' --url 'https://base.easscan.org/graphql' --data '{\"query\":\"query Attestation { attestations(take: 20, skip: 0, orderBy: { timeCreated: desc }, where: { schemaId: { equals: \\\"0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b\\\" }, attester: { equals: \\\"0xE26067c76fdbe877F48b0a8400cf5Db8B47aF0fE\\\" }, refUID: { equals: \\\"0x0000000000000000000000000000000000000000000000000000000000000000\\\" }, revoked: { equals: false }, data: { startsWith: \\\"0x0000000000000000000000000000000000000000000000000000000000000045\\\" } } ) { decodedDataJson refUID id } }\"}'\n```\n\n#### 💦 latest edits\n```graphql\nquery Attestation {\n    attestations(\n        take: 20,\n        skip: 0,\n        orderBy: { timeCreated: desc },\n        where: { \n            schemaId: { equals: \"0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b\" }, \n            attester: { equals: \"0xE26067c76fdbe877F48b0a8400cf5Db8B47aF0fE\" },\n            refUID: { notIn: \"0x0000000000000000000000000000000000000000000000000000000000000000\" }, # edits only\n            revoked: { equals: false },\n        },\n        distinct: [refUID] # unique and latest\n    ) {\n        decodedDataJson\n        refUID\n        id\n    }\n}\n```\n```sh\ncurl --request POST --header 'content-type: application/json' --url 'https://base.easscan.org/graphql' --data '{\"query\":\"query Attestation { attestations(take: 20, skip: 0, orderBy: { timeCreated: desc }, where: { schemaId: { equals: \\\"0x24a31e6646f2d422a173165d76984a4ee1cfd2bea26be543ba15f7e9319bca4b\\\" }, attester: { equals: \\\"0xE26067c76fdbe877F48b0a8400cf5Db8B47aF0fE\\\" }, refUID: { notIn: \\\"0x0000000000000000000000000000000000000000000000000000000000000000\\\" }, revoked: { equals: false } }, distinct: [refUID] ) { decodedDataJson refUID id } }\"}'\n\n```\n\n### 4️⃣ get `FolderSnapshot` jsons corresponding to the latest attestations\nhttps://ipfs.decentralized-content.com/ipfs/bafkreia4i7jwb5qq4upcdl5knvgtvvrqhjchsqsot6vozd26vcabgoed44\n\n### 5️⃣ get nfts from an api of your choice\nuse the latest `FolderSnapshot` values to display nfts in folders\n\n---\n\n### 📝 edit a folder\nuse [referenced attestations](https://docs.attest.org/docs/tutorials/referenced-attestations)\n\ncreate a new attestation with the `refUID` of the initial attestation for that folder\n\nthe next edit for that folder should contain the same `refUID` of the initial attestation\n\n\n### 🗑️ remove a folder\nset `nameAndCid` value to an empty string\n\nor [revoke](https://docs.attest.org/docs/core--concepts/revocation) the initial attestation for that folder\n\n# projects syncing folders\n\n[CANCELED]\n\n# feedback\n### [create an issue](https://github.com/lil-org/how-to-sync-nft-folders/issues) or [create a pull request](https://github.com/lil-org/how-to-sync-nft-folders/pulls)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flil-org%2Fhow-to-sync-nft-folders","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flil-org%2Fhow-to-sync-nft-folders","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flil-org%2Fhow-to-sync-nft-folders/lists"}