DEV Community

Robin Olthuis
Robin Olthuis

Posted on

Building a Stellar Smart Wallet using React

This is a submission for the Build Better on Stellar: Smart Contract Challenge: Create a Tutorial

Tutorial

Smart wallets represent a significant advancement in cryptocurrency management, offering enhanced security and usability compared to traditional wallets. In this tutorial, we'll walk through the process of creating a smart wallet that integrates with the Stellar network, using React for the frontend and leveraging Stellar's smart contract capabilities.

To see a demo of the smart wallet in action, check out this video: Stellar Smart Wallet Demo

What Are Smart Wallets?

Smart wallets are advanced cryptocurrency wallets that offer additional features and security measures beyond simple storage and transfer of digital assets. Key characteristics include:

  1. Enhanced Security: Utilizing WebAuthn for authentication, which can include biometric data.
  2. No Trustlines Required: Unlike regular Stellar wallets, smart wallets don't need trustlines for assets, simplifying user experience.
  3. Flexibility: Ability to add multiple signers, improving accessibility and security.

Implementation Steps

1. Setting Up the Project

First, clone the smart-wallet-demo repository:

git clone https://github.com/orbit-cdp/smart-wallet-demo.git
cd smart-wallet-demo
Enter fullscreen mode Exit fullscreen mode

2. Building, Installing, and Deploying Smart Contracts

The contracts are located in the passkey-kit repository. Clone it and navigate to the contracts folder:

git clone https://github.com/kalepail/passkey-kit.git
cd passkey-kit/contracts
Enter fullscreen mode Exit fullscreen mode

Now use the Soroban CLI to build, install, and deploy the contracts:

stellar contract build

# Install the wallet WASM
stellar contract install --wasm path/to/wallet.wasm --network <network> --source-account <source_account>
# Note the returned hash

# Deploy the factory
stellar contract deploy --wasm path/to/factory.wasm --network <network> --source-account <source_account>
# Note the returned factory_id

# Initialize the factory
stellar contract invoke --id <factory_id> --network <network> --source-account <source_account> -- initialize --hash <wallet_hash>
Enter fullscreen mode Exit fullscreen mode

Replace <network> with the target network (e.g., testnet or public) and <source_account> with the account you're using for deployment.

3. Configuring the Wallet

Update the passkey.ts file with the deployed factory contract address:

// passkey.ts
export const account = new PasskeyKit({
    rpcUrl: "https://soroban-testnet.stellar.org",
    networkPassphrase: "Test SDF Network ; September 2015",
    factoryContractId: "YOUR_DEPLOYED_FACTORY_CONTRACT_ID",
});
Enter fullscreen mode Exit fullscreen mode

4. Implementing User Authentication (passkey.ts)

The passkey.ts file handles the WebAuthn-based authentication. Here's a snippet of how it's used:

export const connectWallet = createAsyncThunk(
    "wallet/connect",
    async (_, { dispatch }) => {
        const { keyId: kid, contractId: cid } = await account.connectWallet();

        // ... handle connection keep track of the contractId as this is the smart wallets address
    }
);
Enter fullscreen mode Exit fullscreen mode

5. State Management (walletSlice.ts)

The walletSlice.ts file uses Redux for efficient state management:

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { account, native, send_transaction } from "../lib/passkey";

// ... other imports and initial state ...

export const getWalletBalance = createAsyncThunk(
    "wallet/getBalance",
    async (_, { getState }) => {
        const state = getState() as { wallet: WalletState };
        const { contractId } = state.wallet;
        if (contractId) {
            const nativeBalance = await native.balance({ id: contractId });
            return {
                native: nativeBalance.result.toString(),
            };
        } else {
            return {
                native: "",
            };
        }
    }
);

// ... other async thunks and reducer logic ...
Enter fullscreen mode Exit fullscreen mode

6. Adding New Assets

To add new assets similar to how we create the native asset in passkey.ts, you can create a new variable with a new contract address. Here's an example of how to add a new asset:

// passkey.ts

// Existing native asset
export const native = sac.getSACClient(
    "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC"
);

// Adding a new asset (e.g., a custom token) use the same way as native
export const customToken = sac.getSACClient(
    "YOUR_CUSTOM_TOKEN_CONTRACT_ADDRESS"
);
Enter fullscreen mode Exit fullscreen mode

Replace "YOUR_CUSTOM_TOKEN_CONTRACT_ADDRESS" with the actual contract address of the token you want to add.

7. Signing Transactions with the Smart Wallet

An important feature of smart wallets is secure transaction signing. Here's a simplified example of how to sign a transaction:

export const signAndSendTransaction = async (xdr: string, keyId: string) => {
  try {
    // Sign the transaction using the smart wallet
    const signedXdr = await account.sign(xdr, { keyId });

    // Send the signed transaction using launchtube
    await send_transaction(signedXdr);

    // Handle success (e.g., update UI, fetch new balances)
  } catch (error) {
    // Handle error
  }
};
Enter fullscreen mode Exit fullscreen mode

This function takes an XDR (transaction) and the wallet's keyId, signs the transaction using WebAuthn, and sends it to the network. It demonstrates how the smart wallet's authentication mechanism is used to sign transactions securely.

Challenges and Solutions

  1. Domain Dependency: Implement multiple signer support to mitigate the risk of losing access if the WebAuthn domain becomes unavailable.

  2. Asset Transfer to Smart Wallets: Most Stellar wallets don't support sending assets directly to smart wallet addresses (C addresses). Use the Soroban CLI for transfers:

stellar contract invoke --network <network> --source-account <account> --id <token_address> -- transfer --to <smart_wallet_address> --amount <scalar_7_amount>
Enter fullscreen mode Exit fullscreen mode

Ensure the source account has sufficient balance for the transfer.

What I Created

This submission is a comprehensive guide for developers to create a secure and user-friendly smart wallet on the Stellar network. It supports the Stellar developer experience by:

  1. Demonstrating the integration of Soroban smart contracts with a React frontend
  2. Describing some of the current challenges with smart wallets and possible solutions

Developers can use this tutorial to build their own smart wallet applications or enhance existing projects with smart wallet functionality.

Journey

My journey in creating this tutorial began with extensive research into the new Smart Wallets that recently got introduced into the ecosystem.

During the content creation process, I:

  1. Experimented with different approaches to implement these into React
  2. Learned about state management with React
  3. Discovered challenges unique to smart wallet development, such as the complexities of asset transfers to contract addresses

I'm particularly proud of how the tutorial addresses real-world challenges that developers might face, providing practical solutions and workarounds. The section on using Soroban CLI for asset transfers, for instance, offers a valuable insight that's not commonly discussed.

This project has deepened my appreciation for the Stellar ecosystem and its potential to revolutionize digital asset management. I look forward to contributing more to the community and seeing how other developers build upon these concepts.

Team

  • Joostmbakker
  • Robeart

Top comments (0)