Use this file to discover all available pages before exploring further.
In this page, we will explore strategies for reducing the size of smart contracts on NEAR. This is particularly useful for developers who want to optimize their contracts for deployment, especially in scenarios where contract size limits are a concern.
This page is made for developers familiar with lower-level concepts who wish to reduce their contract size significantly, perhaps at the expense of code readability.Some common scenarios where this approach may be helpful:
contracts intended to be tied to one’s account management
contracts deployed using a factory
future advancements similar to the EVM on NEAR
There have been a few items that may add unwanted bytes to a contract’s size when compiled. Some of these may be more easily swapped for other approaches while others require more internal knowledge about system calls.
You may want to experiment with using opt-level = "z" instead of opt-level = "s" to see if generates a smaller binary. See more details on this in The Cargo Book Profiles section. You may also reference this Shrinking .wasm Size resource.
Ensure that your manifest (Cargo.toml) doesn’t contain rlib unless it needs to. Some NEAR examples have included this:
Adds unnecessary bloat
[lib]crate-type = ["cdylib", "rlib"]
when it could be:
[lib]crate-type = ["cdylib"]
When using the Rust SDK, you may override the default JSON serialization to use Borsh instead. See this page for more information and an example.
When using assertions or guards, avoid using the standard assert macros like assert!, assert_eq!, or assert_ne! as these may add bloat for information regarding the line number of the error. There are similar issues with unwrap, expect, and Rust’s panic!() macro.
We have prepared a simple bash script that can be used to minify .wasm contract file. You can find it here.The current approach to minification is the following:
Snip (i.e. just replace with unreachable instruction) few known fat functions from the standard library (such as float formatting and panic-related) with wasm-snip.
Run wasm-gc to eliminate all functions reachable from the snipped functions.
Strip unneeded sections, such as names with wasm-strip.
Note that Aurora has found success using rjson as a lightweight JSON serialization crate. It has a smaller footprint than serde which is currently packaged with the Rust SDK. See this example of rjson in an Aurora repository, although implementation details will have to be gleaned by the reader and won’t be expanded upon here. This nesdie example also uses the miniserde crate, which is another option to consider for folks who choose to avoid using the Rust SDK.
Information on system calls
Expand to see what's available from <code>sys.rs</code>
Since Python smart contracts on NEAR are interpreted rather than compiled to WebAssembly directly, the optimization strategies differ from Rust contracts. The Python interpreter itself is already optimized, but you can still make your contract more efficient.
# Less efficient for lookupsuser_list = [] # O(n) lookup timefor user in user_list: if user["id"] == user_id: return user# More efficient for lookupsuser_dict = {} # O(1) lookup timeif user_id in user_dict: return user_dict[user_id]
String operations can be expensive. Minimize them when possible:
# Less efficientresult = ""for i in range(100): result += str(i) # Creates many intermediate strings# More efficientparts = []for i in range(100): parts.append(str(i))result = "".join(parts) # Creates strings once
Deep recursion can lead to stack overflow issues. Consider iterative approaches instead:
# Recursive - could cause issues with deep structuresdef process_tree(node): result = process_node(node) for child in node.children: result += process_tree(child) return result# Iterative - more efficientdef process_tree(root): result = 0 queue = [root] while queue: node = queue.pop(0) result += process_node(node) queue.extend(node.children) return result
If you have a very large contract, consider splitting it into multiple contracts with focused responsibilities. This can improve maintainability and reduce the cost of individual calls.
Go contracts are compiled to WASM via TinyGo, which already applies aggressive size optimizations compared to the standard Go compiler. The near-go build command uses size-optimized TinyGo settings by default.Below are additional strategies to further reduce your contract size.
Native Go slices and maps are fully serialized into the STATE key on every call, making the contract state grow with the collection. Use SDK collections to store entries as separate storage keys:
import "github.com/vlmoon99/near-sdk-go/collections"// Avoid for large datasets: native map is fully loaded on every call// @contract:statetype BadContract struct { Balances map[string]string `json:"balances"`}// Better: SDK LookupMap only loads entries on demand// @contract:statetype GoodContract struct { Balances *collections.LookupMap[string, string] `json:"balances"`}
Failing fast with env.PanicStr saves gas and avoids wasted computation. Validate all inputs and permissions before executing any logic:
// @contract:mutatingfunc (c *Contract) AdminAction(data string) { caller, _ := env.GetPredecessorAccountID() // Validate permissions immediately if caller != c.OwnerId { env.PanicStr("Only the owner can call this method") } // Validate input before processing if len(data) == 0 { env.PanicStr("Data must not be empty") } // ... proceed with the actual logic}