{"id":13566140,"url":"https://github.com/LeTurt333/LeBundler","last_synced_at":"2025-04-03T23:31:10.631Z","repository":{"id":185516491,"uuid":"636650769","full_name":"LeTurt333/LeBundler","owner":"LeTurt333","description":"CosmWasm query bundler + Example usage of Raw Queries","archived":false,"fork":false,"pushed_at":"2023-08-02T06:18:47.000Z","size":36,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-04T20:42:13.039Z","etag":null,"topics":["cosmwasm","cw20","cw721","nft"],"latest_commit_sha":null,"homepage":"","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/LeTurt333.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}},"created_at":"2023-05-05T10:30:07.000Z","updated_at":"2023-08-27T13:53:06.000Z","dependencies_parsed_at":null,"dependency_job_id":"87a74804-38dd-4f7c-91ba-d0ff95af0b75","html_url":"https://github.com/LeTurt333/LeBundler","commit_stats":null,"previous_names":["leturt333/lebundler"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeTurt333%2FLeBundler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeTurt333%2FLeBundler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeTurt333%2FLeBundler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LeTurt333%2FLeBundler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LeTurt333","download_url":"https://codeload.github.com/LeTurt333/LeBundler/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247097714,"owners_count":20883124,"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":["cosmwasm","cw20","cw721","nft"],"created_at":"2024-08-01T13:02:03.040Z","updated_at":"2025-04-03T23:31:08.019Z","avatar_url":"https://github.com/LeTurt333.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# LeBundler\n\nA tooling contract to bundle `CW721` and `CW20` queries, as well as a basic example of using CosmWasm Raw Queries to save on gas\n\n# The Problem\n\n### Getting NFT information from a CW721 contract takes a lot of network requests, constrained by query gas limits \n\n\u003c/br\u003e\n\nTypically, when thinking about `cw721-base` (or any of it's variants), it will take you quite a few network requests (ie RPC calls) to get information about NFTs. First, you have to query the contract and get `Token IDs`, but you can only retrieve 100 at a time. Then, for each `Token ID`, you'll need an additional RPC call to get information like the NFT's metadata. The number of requests it would take to get **all** NFTs + their information can be depicted as:\n\n```\nlet num_nfts = total number of NFTs;\nlet rem = num_nfts % 100;\nlet token_id_requests = match rem {\n    0 =\u003e num_nfts / 100,\n    _ =\u003e num_nfts / 100 + 1\n}\nlet total_requests = token_id_requests + num_nfts\n```\n\nSo, given a colleciton with 10,000 NFTs, you'd be looking at 10,100 requests. That's a lot!\n\n\u003c/br\u003e\n\n# Solving the problem\n\n## `1: QueryMsg::BundleQueryIds`\n\n```\nBundleQueryIds {\n    loop_limit: u32,\n    contract: String,\n    start_after: Option\u003cString\u003e\n}\n```\n\nThis method will simply query the `contract` up to `loop_limit` times, and return the token IDs. If any of the queries return less than 100 results, the queries stop executing and the result is returned.\n\nIn manual tests, this method succeeded with a `loop_limit` of up to 7 (8+ hit query gas limits)\n\n\u003e **Note:** Manual tests were done on Juno testnet using an NFT contract with integer-incremented String token IDs - `\"1\", \"2\", \"3\"...`\n\nIf we take our equation from before, and give ourselves a bit of a safety margin at a `loop_limit` of 5, the new equation looks like this:\n\n```\nlet num_nfts = total number of NFTs;\nlet rem = num_nfts % 100;\nlet token_id_requests = match rem {\n    0 =\u003e (num_nfts / 5) / 100,\n    _ =\u003e (num_nfts / 5) / 100 + 1\n}\nlet total_requests = token_id_requests + num_nfts\n```\n\nSo, given a collection with 10,000 NFTs, we'd be looking at 10,020 requests\n\n| Method                            | Requests |\n|-----------------------------------|----------|\n| Without Bundler                   | 10,100   |\n| BundleQueryIds                    | 10,020   |\n\n**Reduction from original: 0.79%**\n\nAny reduction is a good reduction, but we can do better\n\n---\n\n## `2: QueryMsg::BundleQuerySmart`\n\n```\nBundleQuerySmart {\n    token_ids: Vec\u003cString\u003e,\n    contract: String\n}\n```\n\nThis method iterates through `token_ids` and broadcasts a `TokenInfo` query to the `contract` for each. As you can tell by the name, this method uses CosmWasm's `SmartQuery` like you are probably used to\n\n\u003e **Note:** Because this method uses `SmartQuery`, you **must** know the type of `Extension` the CW721 contract is using if you want it to be included in the response. If you don't care about getting the Extension, you can cast the query results to `StdResult\u003cNftInfoResponse\u003cEmpty\u003e\u003e` for it to be left out of the response\n\nIn manual tests, this method succeeded with up to about 40 token_ids before hitting query gas limits.\n\n\u003e **Note:** The NFT contract used for testing included the default `Extension` from `cw721-metadata-onchain` and 8 Trait values per NFT.\n\nIf we take our equation from before, and give ourselves a 10% safety margin at 36 `token_ids` per query, the new equation looks like this:\n\n\n```\nlet num_nfts = total number of NFTs;\nlet rem = num_nfts % 100;\nlet token_id_requests = match rem {\n    0 =\u003e (num_nfts / 5) / 100,\n    _ =\u003e (num_nfts / 5) / 100 + 1\n}\n\nlet rem_two = num_nfts % 36;\nlet nft_info_requests = match rem_two {\n    0 =\u003e num_nfts / 36,\n    _ =\u003e num_nfts / 36 + 1\n}\n\nlet total_requests = token_id_requests + nft_info_requests;\n```\n\nGiven a collection with 10,000 NFTs, using both the `BundleQueryIds` and `BundleQuerySmart` methods, we'd be looking at 298 requests\n\n\n| Method                            | Requests |\n|-----------------------------------|----------|\n| Without Bundler                   | 10,100   |\n| BundleQueryIds                    | 10,020   |\n| BundleQueryIds + BundleQuerySmart | 298      |\n\n**Reduction from previous: 97.03%**\n\n**Reduction from original: 97.05%**\n\nNow we're talking! Yet I yearn for more...\n\n---\n\n## `3: QueryMsg::BundleQueryRaw`\n\n```\nBundleQueryRaw {\n    token_ids: Vec\u003cString\u003e,\n    contract: String\n}\n```\n\nThis method is essentially the same as `BundleQuerySmart`, except it uses `RawQuery` instead of `SmartQuery`\n\nIn a nutshell, Raw Queries look up storage entries *directly* by key. You might be thinking \"Aren't smart queries also looking up entries by key?\", to which you'd be right, except that the *key* being referred to is a bit different\n\n**It's important to note that Smart Contract storage in CosmWasm is essentially a *wrapper* around on-chain storage**\n\nWhen you use a Smart Query, you only need to provide the wrapper-abstracted key (ie. the Key to a `cw_storage_plus::Map`), and the CosmWasm engine handles the on-chain storage look up for you\n\nWhen using a Raw Query, you need to provide the actual underlying on-chain storage key (or, a less abstracted version of it at least), which requires more work from you, but less work from CosmWasm. You can get an idea of how these keys are constructed in `/query_bundler/src/encoding.rs`\n\nThe result is that Raw Queries require much less gas to complete than Smart Queries do. In regards to this tool, this means fewer network calls to get the NFT info we need. \n\n**TLDR:** Raw Queries are a lot less computationally expensive than Smart Queries. But...how much?\n\nIn manual tests (using the same CW721 contract as before), this method succeeded with up to about 440 token_ids before hitting query gas limits\n\nIf we take our equation from before, and give ourselves a 10% safety margin at 400 `token_ids` per query, the new equation looks like this:\n\n```\nlet num_nfts = total number of NFTs;\nlet rem = num_nfts % 100;\nlet token_id_requests = match rem {\n    0 =\u003e (num_nfts / 5) / 100,\n    _ =\u003e (num_nfts / 5) / 100 + 1\n}\n\nlet rem_two = num_nfts % 400;\nlet nft_info_requests = match rem_two {\n    0 =\u003e num_nfts / 400,\n    _ =\u003e num_nfts / 400 + 1\n}\n\nlet total_requests = token_id_requests + nft_info_requests;\n```\n\nGiven a collection with 10,000 NFTs, using both the `BundleQueryIds` and `BundleQueryRaw` methods, we'd now be looking at 45 requests\n\n| Method                            | Requests |\n|-----------------------------------|----------|\n| Without Bundler                   | 10,100   |\n| BundleQueryIds                    | 10,020   |\n| BundleQueryIds + BundleQuerySmart | 298      |\n| BundleQueryIds + BundleQueryRaw   | 45       |\n\n**Reduction from previous: 84.90%**\n\n**Reduction from original: 99.55%**\n\n\u003c/br\u003e\n\n![image](https://github.com/LeTurt333/cw721-query-bundler/assets/89463679/e39f5a24-75f0-4420-93c3-32ec410ba3ea)\n\n---\n\n## Follow me on Twitter at [@LeTurt_](https://twitter.com/leturt_)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLeTurt333%2FLeBundler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLeTurt333%2FLeBundler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLeTurt333%2FLeBundler/lists"}