The Move whitepaper was first published in 2018 at Facebook as part of the Diem project. It laid out a novel approach for securely creating digital assets centered around native scarcity and access control. Its semantics are inspired by Rust, and it aims to address the issues and short-comings the designers identified in the available languages at the time (both smart contract specific and general purpose). This article will explore some of Moves features, components and tools, how they compare with existing smart contract languages, and what this new ecosystem could mean for the Web3 gaming space.
At certain points within this article we refer to certain projects by name. This is not intended to be an endorsement of one ecosystem over another, it is merely due to the fact that we’re more familiar with certain projects. We are actively exploring multiple ecosystems and would love to speak to you if you’re building something interesting related to gaming, regardless of which blockchain you’ve chosen.
The introduction of smart contracts on Ethereum in 2015 was a huge leap forward for the blockchain space. They extended the decentralized p2p payments functionality pioneered by Bitcoin into a globally distributed computing platform. This innovation enabled the creation of DeFi, NFTs, DAOs and many other emergent technologies. Since then, smart contracts have been implemented in multiple languages across different chains. In spite of this, the vast majority of smart contracts today are still written in Solidity and run on various EVM implementations across Ethereum, BNB Chain, Polygon, Avalanche, Optimism, Arbitrum and many others.
That being said, Solidity has known security vulnerabilities, some of which have been responsible for high profile hacks like the $611m access control exploit of Poly Network in August 2021. Due to the lack of native security protections, Solidity developers have to explicitly defend against vulnerabilities and exploits, and mistakes can become extremely costly. All this detracts attention away from what developers should be focusing on – building great user experiences – and represents a primary blocker to mainstream adoption of blockchain technology. Risks can be mitigated to a certain extent with high test coverage and code audits. However, these are expensive and are still no guarantee of complete safety.
Close behind Solidity in terms of smart contract developer adoption is the general purpose language, Rust. This is the language of choice on Near, Solana, Elrond and Polkadot. Other projects, in an effort to improve upon the current smart contract platforms, have opted to create specialized languages. A few of these include Marlowe and Plutus on Cardano, Clarity on Bitcoin, or Cadence on Flow.
Do we need another smart contract language?
Putting smart contracts aside for a minute, as the quote from Sam Blackshear below alludes to, the first question one should be asking is why we need another smart contract programming language.
“The most valuable asset for any programming language is its community and its tooling in the existing ecosystem. If someone tells you they want to create a new programming language you should be very skeptical, and there has to be really strong motivation for it.”
An additional consideration, especially for newer languages, is whether they bear any similarities with existing languages, as this will smooth the learning curve when onboarding new developers. As mentioned earlier, Move is inspired by Rust and Rust has been ranked the number one loved language in Stackoverflow’s annual developer survey every year since 2016. This bodes well for Move, and a number of founders we’ve spoken to have commented that onboarding Rust developers into the Move ecosystem is relatively straightforward and can be done in a matter of weeks. In spite of this, the EVM maintains strong network effects due to the number of Solidity developers relative to other blockchains. Access to this talent pool is a primary reason why prospective projects choose to build on the EVM.
So taking the above into account, why design a completely new smart contract programming language? In order to be effective within the highly specialized domain of smart contracts, languages need some very specific properties. They must be deterministic, type and memory-safe and support the notion of metered execution (for a more in-depth explanation of these, check the appendices). There aren’t any general purpose languages that have all of these properties. Rust is type and memory safe but is non-deterministic and doesn’t support metered execution. As a result, rust-based contracts are written using a restricted subset of the language and in the case of Solana, metered execution is calculated by the protocol (see appendix D). Solidity is deterministic and supports metered execution but is not memory-safe, and as discussed earlier has some security flaws.
Moreover, the feature set required from a smart contract language is very narrow. It needs to be able to guarantee scarcity of assets and securely manage ownership and access control. Beyond that, any additional features add unnecessary complexity. For this reason, rather than mutate an existing language and end up with something that is hard to work with, the design team at Facebook chose to start with a blank slate and create a lean and highly specialized language.
“The Move hypothesis is that if we provide first-class abstractions for these key concepts [asset scarcity and access control], we can significantly improve both the safety of smart contracts and the productivity of smart contract programmers—having the right vocabulary for the task at hand changes everything.”
Programmable Resources and Modules
Move is often referred to as a resource oriented programming language. Within the Move paradigm, digital assets are represented by the built-in resource type with semantics inspired by linear logic. In layman’s terms, this simply means an asset cannot be copied (double spent) or implicitly discarded (accidently dropped), only “moved” between storage locations. Because all assets are represented by this resource type, no particular asset has special status, and scarcity is enforced natively for all assets. This is in contrast to Ethereum’s model, where Ether is the only native asset and every other asset is implemented using token standards (e.g. ERC20) who’s scarcity rules must be explicitly encoded and checked against in a smart contract. It’s important to note that these Move semantics are not new, they originated in C++11, as a way to avoid expensive copying of data to new memory locations. They were then later implemented in the Rust language.
One of the main differences between Move and Solidity is the storage location of assets and the scope within which they can be used. In Solidity, digital assets never leave the boundaries of their smart contract. One’s ownership of a digital asset is represented by an entry inside a mapping, which associates a user’s public address with a balance or token ID. Transferring ownership of any asset simply involves changing this mapping entry to point to the new owner address.
In Move, assets are defined within a module (akin to a smart contract in Solidity) and are represented by structs (resource types) rather than integers. This allows developers to be more expressive with the kinds of assets they create. Contained within these modules are also a set of procedures encoding the rules for who can interact with the asset and in what way.
Although resources are defined within a module, they are stored independently in global memory, keyed by address/type pairs. This individual, object-like abstraction more closely resembles a real-world asset in digital form. It is definable and independent from everyone else’s rather than just being a mapped integer. This also means that individual assets can be passed as arguments, directly or by reference, into any procedure from any module, whilst still guaranteeing safety through strong access controls. Any module is able to read a resource’s data, however procedures encoding critical operations (such as creation, modification, and destruction) are encapsulated within their declaring module and enforce role-based invocation.
Besides the safety guarantees provided by Moves type system, the team behind Move deliberately engineered the language to be amenable to static analysis. They also provided tooling to help catch errors (bytecode verifier) and check the correctness of the code against its intended behavior (Move Prover).
Amenability to Static Analysis
During development of mission-critical applications, static code analysis can be used to help detect bugs, vulnerabilities, and anti-patterns. Usually this is an automated process that is triggered every time a new code commit is made. It will scan through the code’s call graph and compare it against a set of standards and best practices, then flag any issues for developers to address. Move has been designed in a way that makes it particularly amenable to this type of analysis.
Firstly, Move does not support dynamic dispatch (the process of selecting which implementation of a polymorphic function to call at runtime). This enables a Move program’s call graph to be determined at compile time, which reduces the complexity the analyser has to deal with, and enables thorough verification. Furthermore, there is no fallback function in Move, as seen in Solidity. This function has been the cause of many re-entrancy hacks in Ethereum’s past, so Move is also much less susceptible to these sorts of bugs.Secondly, mutations to values in Move occur through references rather than directly. This allows a “borrow checker” (taken from Rust) to enforce that only a single mutable reference to a value exists at any one time and enables the static analyser to reason about the effects of a write operation more easily. Finally, as critical operations on a specific resource type are encapsulated within the declaring module, the static analyser can isolate the functionality and perform exhaustive verification without having to factor in external variables.
On-Chain Bytecode Verifier
Once Move source code is compiled to bytecode and deployed to the blockchain, it is the job of the on-chain bytecode verifier to run type, resource, and memory safety checks immediately before execution in the VM. By using an on-chain bytecode verifier, the compiler can be moved out of the trusted computing base (TCB) whilst still maintaining safety guarantees. This is accepted good practice, as compilers are considered complex systems with a relatively high probability of containing bugs and vulnerabilities themselves, which could compromise the entire TCB. The Ethereum Foundation is very open about the known bugs in the Solidity compiler.
Formal Verification with the Move Prover
Formal verification is used in mission-critical applications to mathematically prove that the code acts in accordance with specified behavior. Within the Move ecosystem this is provided by the Move Prover. The process involves creating a separate document, written in a dedicated specification language, that defines exactly how the application should behave. This document is then executed against the source code to ensure correctness for all possible inputs and states. Any discrepancy between the application state and the specification will be flagged as an error for the developers to investigate.
Having a tool like this is a great step towards achieving better security standards in Web3. It’s worth noting however that writing and maintaining formal specifications relies on development teams having clearly defined software requirements. It can also add a significant amount of time to the development process, so a risk assessment weighing up the cost-to-benefit may be prudent.
Why Are There Multiple Versions of Move?
So far, we’ve tried to keep this overview as general as possible without going into any specific blockchain implementations of Move, but it would be remiss not to mention what the team at Mysten Labs are building with Sui. The Sui ecosystem is one area we’re keeping a close eye on due to their focus on becoming the next generation blockchain for Web3 games. Furthermore, the Mysten team includes many of the original minds behind Move, including its chief designer, Sam Blackshear.
So why are there multiple versions of Move? Some of the original language design choices were made with a permissioned blockchain in mind (Diem). Furthermore, the release of the initial version predated the rise in popularity of NFTs. As a result, the Mysten team claim that in its original form, the Move language is suboptimal for some of today’s use cases, particularly around NFTs and games. They therefore opted to create a new optimized version called Sui Move, made possible due to the aforementioned embedded design of Move’s core language. The main differences between the Sui Move and the original “Diem” Move are primarily around the data storage model.As discussed earlier, in the original version of Move, global storage is keyed by address/type pairs. This means only one type of asset per address can be stored at a given key. An address can of course own many different resource types, but this requires creating a new key within global storage for each new asset created. If a game developer wanted to airdrop a new item to each player address this would require a new key associated with that resource type to be created at each player address. Every user would also have to explicitly opt-in to receiving that asset. Put another way, for every user receiving an item, two transactions would need to be executed. In Sui Move there exists the notion of objects and global storage is keyed by an objects unique ID. When a resource is created in Sui Move, it is associated with a unique global object ID and has metadata assigned to it to indicate ownership addresses. With this storage model an item could be airdropped to millions of users in a single transaction. A more detailed justification can be found here.
What Does This All Mean for Web3 Gaming?
It is our thesis at BITKRAFT that Web3 is going to be the next major paradigm shift in gaming, resulting in billions of new users coming on-chain. All it would take is for a single game as popular as World of Warcraft to go Web3, and that would equate to more daily active users than the entire Ethereum network. The designers of the Move language claim that due to the resource-centric data model of Move, they are able to achieve unlimited “Web2-style” horizontal scaling. Because the global state is organized around individual resources, transactions can explicitly declare which resources will be impacted and the protocol can ignore all others. This is different from the EVM, where smart contracts frequently cause points of contention (bottlenecks). Transactions that require ordering because they touch the same resources are processed sequentially, whereas transactions with no interdependencies can be processed in parallel. Moreover, simple transactions on Sui (like a p2p payment) that don’t have complex interdependencies can actually forgo consensus altogether and take the faster, low latency Byzantine Consistent Broadcast path. For a more in-depth explanation see the Sui docs.
We may see interested game developers more readily adopt Move-based ecosystems due to the similarities with asset representation. The way assets are encoded as data structures in Move is more akin to the ECS (Entity Component System) model that game developers are used to working with. Conversely, mappings of asset ownership that point to external storage locations, as seen in traditional smart contracts, is more of a foreign model.
“Why shouldn’t that be as simple as, rather than storing it [game asset] in an internal database, just storing it on a blockchain? But for that to be possible, you need to have the same way of representing assets, which current blockchains don’t allow. They have to conform to some arbitrary standard. The entry becomes a URL pointing to some asset. The current blockchains are only networks for ownership records. They’re not networks for assets.”
As mentioned earlier, Move bears many similarities with Rust and Rust is already used to write games today, with titles like the voxel RPG, Veloren. Therefore, it’s not too much of a stretch to see how Move could be a good choice for game developers. In fact, the Mysten team have already written the game logic for a number of fully on-chain games, which can be found on their GitHub. We’ve also heard through multiple conversations with builders in the Move ecosystem that it’s trivial to onboard a Rust developer into the Move ecosystem. In fact Solana developers have been actively exploring these new Move-based chains, partly due to the developer grant programs but also due to the simplicity of launching prototypes.
“Technically speaking, Move, which is used by Aptos, offers a programming experience that is noticeably less choppy and more intuitive than Rust. As Move has a straightforward syntax and a comprehensive stdlib library, it requires far less code during development to accomplish the same goals as Rust.”
What are the Drawbacks of Choosing Move?
Due to the fact that Move is a relatively new language it hasn’t had years of “battle-hardening” in production like Rust or Solidity, so bugs and vulnerabilities may yet come to the surface. It also doesn’t yet have the wealth of resources, tools, frameworks, libraries, and tutorials that established languages have. Furthermore, the platforms adopting Move are also new so there may be some teething problems bringing them up to full capacity. Additionally, as Move is quite different from Solidity it may incur a switching cost for EVM-based developers. On the other hand, the number of smart contract developers today is miniscule compared with the number of game developers worldwide and the true test for Move will be whether it can onboard new developers from outside the blockchain space.
At the time of writing many of the Move-based blockchains have either just gone live or are still in testing, so Move is yet to be proven in production. However, the designers had the benefit of seeing where “first-generation” smart contract languages fell short and were able to improve on the model. The design principles of safety, native scarcity and access control, along with the new resource-centric data model are a novel approach and effectively negate many vulnerabilities seen in today’s smart contracts. Move’s safety guarantees, as well as advanced formal verification tools like the Move Prover, could help lessen the burden of responsibility on developers and allow them to focus on building great apps and games. Move is based on Rust, which is already used in game development and is consistently voted the most loved language in Stackoverflow’s annual developer survey. Furthermore, Rusts adoption on existing blockchain platforms could be indicative that Move will also be popular. Finally, the similarities between how assets are represented in Move and in game development, coupled with the scalability potential that this data model enables, may help to onboard fresh game developers into the ecosystem and usher in a new wave of Web3 game innovation.
Appendix A: Statically vs Dynamically Typed Languages
Programming languages can be categorized as statically or dynamically typed. There are divided opinions amongst developers as to which is best, but generally speaking, statically typed languages are compiled and require developers to explicitly declare variable types upfront. Any value assigned to a variable must be of the same type to avoid errors during compilation. For this reason, statically typed languages are considered more secure than dynamically typed languages but also more but verbose and less readable. It’s worth noting that even if the code compiles successfully, it doesn’t mean the application will be clear of logical errors. Depending on the executable produced, execution will be much faster than interpreted code as there is no overhead added by an interpreter.Dynamically typed languages are not compiled and do not require the developer to declare types in the code. As a result, these languages are more succinct. A variable’s type is inferred by an interpreter at run time and may change depending on what value is assigned to it. The lack of a compilation step speeds up debugging, increasing developer productivity and also allows code to be fetched from a server to be executed client-side, making it well suited for dynamic web development. However, because type safety is not guaranteed before program execution, there’s a potential for unexpected runtime errors. This is clearly an issue when deploying immutable code into an adversarial environment, as these potential errors could be exploited by malicious actors. Smart contract languages usually take a middle ground. They are statically typed but often compiled into an intermediate format known as bytecode, which is then converted into machine code by an interpreter and executed by the machine CPU. This intermediate bytecode format makes the language more portable across different platforms and underlying hardware.
Appendix B: Memory Safety
Memory safe languages help prevent certain types of bugs and memory-related vulnerabilities in software and are thus considered more secure than non-memory safe languages. When a program displays information to a user, it first has to retrieve this data from a specific memory address. Let’s say this data is a to-do list with ten items. If the programmer made a mistake and the application actually requests the eleventh item, a memory safe language would throw an error and cease execution. A non-memory safe language would just grab whatever data is stored at the next memory address and display it for the user, which could be another user’s data. A similar problem occurs when writing data to memory, where a mistake in the code could result in another user’s data being unintentionally overwritten. This is obviously a huge security issue and, where possible, developers should look to use a memory safe language, especially when dealing with sensitive or valuable data. Rust is known for its memory safety whereas Solidity is not considered a memory-safe language.
Appendix C: Deterministic Execution
A blockchain at its most fundamental level is a replicated state machine. At a specific point in time, the network will be in a certain state representing the current ownership of assets. A blockchain’s runtime defines the rules that validators (state replicators) use to execute transactions and transition the network to a new state. If the state transition involves non-deterministic logic, validators executing exactly the same set of transactions may arrive at different network states. In this situation, the validator set would fail to achieve consensus and the blockchain would grind to a halt. Most general purpose languages have non-deterministic features, such as iterating over hash tables or the use of floating point numbers. For this reason, smart contracts are either written in specialized deterministic languages like Solidity or in a restricted deterministic subset of an existing language, as is the case with Rust on Solana.
Appendix D: Metered Execution
Commonly referred to as ‘gas’, this is the notion of applying a computational budget to a transaction. When Solidity source code is compiled and deployed on-chain, the result is an intermediate representation called bytecode, which is not directly executable by the machine’s CPU. When a transaction is submitted to the smart contract, the interpreter contained within the EVM will convert the bytecode line by line into a series of opcodes for the validators CPU to execute. Each of these opcodes has an associated gas cost, which is paid by the user sending the transaction. The more complex the transaction, the higher the total cost will be. This ensures validators are compensated for the computational resources they provide, prevents transactions of arbitrary length from blocking subsequent executions, avoids infinite loops, and makes it prohibitively expensive to spam the network. Rust on the other hand, compiles to machine code and is directly executed by the node’s CPU. As a result, transaction fees on Solana have to be calculated by the protocol using a bandwidth mechanism that takes into account network load and transaction size. This means that transaction fees are not subject to the spikes we see on Ethereum with the gas auction model. However, in this instance, rather than being a feature of the language, the fee mechanism is tightly coupled with the underlying protocol and is therefore not portable across different chains as is the case with Solidity and Move.