Implementing a Sui wallet
All
Game Development
Web3 Games
Implementing a Sui wallet
January 30, 2023

If you’re new to the world of Sui and web3, the sheer volume of information and concepts can be daunting. This guide is designed to help demystify the topic of wallets and related concepts, making them easily understandable for developers at any level.

What is a wallet?

A wallet in the context of blockchain and cryptocurrency refers to a piece of software that allows an individual to securely store, manage, and send digital assets. The two main functions of the wallet is to keep your private key safe and allow you to interact securely with the blockchain.

At the heart of all blockchain technology lies the concept of public-key cryptography, which allows for the generation of a key-pair consisting of a private key (to be kept secret) and a public key that can be shared with anyone. In the context of blockchains, specific addresses are generated based on this key-pair, acting as public keys that enable the receipt of coins and tokens. Only the holder of the corresponding private key has access to these digital assets and can send them to a marketplace or to another address. Here is an example of what a key-pair and Sui address looks like:

The security of private keys is a critical issue for users, as the responsibility for their protection falls solely on the individual. Unlike traditional online accounts, there is no “Forgot password” option for private keys, and the only way to recover a lost key is through a recovery phrase, which must be written down and stored in a secure location beforehand. Furthermore, if a private key is shared, anyone who obtains it will have control over the associated digital assets, with no means of reversing unauthorized transactions. Given the severity of these risks, it is crucial to have secure wallet software to store private keys and handle blockchain transactions without the risk of exposing keys. Wallets come in a few different forms:

  1. Software wallet: Pure software wallet where your private keys are stored encrypted on a computer. This is the easiest to set up, but comes with risk as you have to trust the wallet provider to keep your secrets secure. Certain web3 marketplaces and wallets are described as non-custodial, meaning that you don’t have access to the private key and instead rely on conventional email/password login to access your funds. In these cases, you don’t have full control over your assets, and if the company were to go bankrupt or shut down, you could potentially lose everything. Many wallets do give you access to the private key and allow exporting your assets to another address.
  2. Hardware wallet: A security-hardened physical device that generates and stores your private key. When you create your key-pair, a recovery phrase is generated that you physically write down and store in case you lose your physical device. After that, your private key never leaves this device for any reason. These wallets also come with software and the main distinction with pure software wallets is that the private key is completely under your control.
  3. Paper wallet: A paper wallet is just a piece of paper with a key-pair printed on it — there is no actual wallet functionality here. It’s a piece of paper…

How does a wallet interact with the blockchain without exposing your private key? By utilizing the concept of transactions. When a user connects their wallet to a website, transactions can be prepared and sent to the wallet for review and approval. No digital assets can be transferred out of the wallet without the user’s explicit authorization. If the user chooses to proceed with the transaction, the private key is used to sign the transaction, and it is then submitted to the network for execution. There is no way for the malicious actors to reverse the user’s signature on the transaction and deduce their private key. Additionally, without access to the user’s private key, it is impossible for anyone to impersonate their signature.

These days, most wallets are available as browser extensions. The reason for this is because it’s the easiest way to bridge the web2 world of websites with the web3 world of blockchains. Browser extensions can alter any part of any website and help to make the experience of interacting with web3 much nicer, but that comes at the cost of having all these extensions being able to read and alter every website you access. It becomes very important to keep track of what extensions you are installing and whether they can be trusted. There will undoubtedly be better solutions to integrating web2 and web3 in the future, but for now this is the best option we have to cross that bridge.

Wallet software patterns

If you’ve been exploring blockchains like Sui and Solana, you may have come across terms like “wallet adapter”, “wallet standard”, “wallet adapter wallet”, “mobile wallet adapter” and so on. Understanding these concepts can be challenging, so let’s clarify the reasons behind these terms.

First off, these concepts are extremely young and are in constant development. Developers are trying to find the simplest way for applications to interact with wallets and establish a standard interface. Let’s say we’re working on a website that needs to interact with a wallet. Initially, there was no standard way to do this and every wallet would simply have it’s own API — meaning you would have to implement code for every wallet provider you want to interact with. If you decided to stick with a single wallet provider, many users would not be able to use your application.

This is where the wallet adapter pattern comes in. The wallet adapter serves as a single package that allows support for many different wallets. It exposes a standard interface and wallet developers implement this interface to internally interact with their own API. Afterwards, they make a pull request on Github to get their final adapter code into the package. This pattern makes it easier for application developers to interact with wallets, but maintaining the single adapter package is cumbersome for both the maintainers and the wallet developers. There is also an issue with code bloat as application developers need to package the code for all these wallets alongside their own code. This shouldn’t really be necessary, and that’s why the idea of the wallet standard was recently developed.

The wallet standard provides a standard interface for wallet developers to implement, but they don’t have to go through the process of submitting their code to a central package. Instead, wallet-standard provides a method that allows any wallet to register in the browser window. Website developers can now interact directly with the wallet-standard package and get a list of registered wallets to interact with. This approach avoids the code bloat and maintenance associated with the adapter pattern. Instead, the wallet extension provides the wallet code exposed via the standard interface.

Implementing the wallet

The wallet-standard library can be found here. This provides the standard interface that can be used as a base for any blockchain wallet. Sui provides their own wallet-standard package here. This is a very light package that provides some Sui-specific types and constants to ease development of Sui-compatible wallets. This package also imports the core features from the original wallet-standard, so it won’t be necessary for us to import both packages in our wallet code.

Implementing the standard is simple enough: We create a class and implement a series of required functions. I’ll include a copy of the entire class at the bottom of this post. Keep in mind that this post is purely about implementing the wallet standard, there are many further security considerations to make when building a production-ready wallet extension. Starting out with the basic functions the scaffolding of our class looks like this:

You can read the comments to see what the functions do - most of them are pretty straightforward. The notable function here is features which is a part of the wallet-standard interface that gives developers an easy way to check which features are supported, and then call those functions. The standard provides two required features "standard:connect" and "standard:events". There are also optional features available, for example "standard:disconnect" which allows us to run code on disconnect. The features are intended to be extendable by any blockchain, so Sui requires the inclusion of a third feature "sui:signAndExecuteTransaction".This is how our features function will look:

For each feature that we want to support, we implement a corresponding function and reference that function here. The events function is used for setting up event handling. The signAndExecuteTransaction function is basically all we need for a simple wallet. The function receives a transaction for approval, which the wallet then signs and sends to the Sui network to execute. We have four features defined here, so we’ll need to implement the four functions referenced above. Before that, we need to add a constructor and some internal state to our class:

We add a listeners variable for events handling and a single account which will store the connected account details. For this simple example, we will pass an existing keypair to the wallet on creation. Production-ready wallets have password protection and utilize a vault / encryption to reduce risk, which is out of scope for this blog post. We also define a provider and signer. These are types provided by Sui. The signer, as you might have guessed, is used to sign and execute the transaction. The provider handles JSON RPC calls for us. If you don’t what that is, RPC stands for “remote procedure call” and is a simple communication protocol used by many blockchains. We don’t need to worry about the details of RPC, the most important thing to note about the provider is that it will connect to a network of our choosing and send transactions to that network.

Note that we can also implement the accounts getter since we now have an #account variable to reference. Now that we have our constructor, let’s implement the event handling feature since it will be referenced by other features. These functions have been copied from the wallet-standard example here. This is boilerplate event handling code, feel free to substitute whatever events library or approach you prefer.

After we have events set up, we can now implement the connect method:

As you can see, the connect method doesn’t really do much of anything. We setup a ReadonlyWalletAccount based on the keypair and network, and then emit an event. There’s not much to do here since connecting just means sharing our address with the application, we aren’t really taking any action.

Next up, let’s implement the disconnect method — which simply clears the account and emits an event:

Finally, we can implement the workhorse of our wallet - the signAndExecuteTransaction method:

Sui takes care of the heavy lifting for us via the RawSigner object and it’s signAndExecuteTransaction function. The return type of our function is SuiSignAndExecuteTransactionOutput which extends the SuiTransactionResponse type. So essentially we’re returning an object that conforms to this type:

That’s it! We now support the 3 required features needed for the wallet implementation. We support events handling, and have implemented the basic functions needed by the interface. However, there are still some optional functions that we can add for common functionality and testing:

The requestFromFaucet function makes it easier to acquire coins when testing on devnet and testnet, and can also be used during unit tests. The transferObject and executeMoveCall functions represent basic Sui actions that users might want to take and are useful for all sorts of testing.

While we’ve talked about implementing this wallet, we’ve glossed over how to actually use it. As a final step, inside the code of our application, we have to create a wallet instance and register it on the window:

Once the wallet has been registered, it will be available to interact with websites when they click the “Connect” button.

For reference, the completed class (and interactable code sections) can be found in the Medium version of this post here.

About the author

My name is Christiaan Keyter. I'm a web3 game developer at Lucky Kat Studios, where our team is diving into the Sui ecosystem and exploring its amazing possibilities in the gaming world.