James Bachini

Creating A Web3 Chat Room

Talking Smart Contracts

Did you know that Chrome and other browsers have a built in speech-to-text API?

I thought I could use this to create an online chat room using web3 serverless technology. The result…

https://jamesbachini.github.io/Talking-Smart-Contracts

The frontend is hosted on Github pages and the backend is just a permissionless, immutable smart contract hosted on Stellar, a decentralized blockchain network.

Welcome to the future, let’s dive in.


The ChatRoom Contract

This contract only needed to be simple and fire an event whenever a public function is called.

#![no_std]
use soroban_sdk::{contract, contractimpl, Address, Env, Symbol, symbol_short};

#[contract]
pub struct TalkingContracts;

#[contractimpl]
impl TalkingContracts {
    pub fn talk(env: Env, me: Address, msg: Symbol) {
        me.require_auth();
        env.events().publish((me.clone(),), msg);
    }
}

We are accepting an address and message in the form of a Symbol. This is then pushed out as an event with the topic set to the address and the value set to the message.

The code for this along with unit tests is all open source on Github:
https://github.com/jamesbachini/Talking-Smart-Contracts/blob/main/contract/src/lib.rs

I deployed this using the Soroban Playground web app at: https://soropg.com/?codeUrl=https%3A%2F%2Fraw.githubusercontent.com%2Fjamesbachini%2FTalking-Smart-Contracts%2Fmain%2Fcontract%2Fsrc%2Flib.rs


Backend storage done. Now let’s move on to the frontend.

I’ll go through the highlights but the full code is here: https://github.com/jamesbachini/Talking-Smart-Contracts/blob/main/index.html

We are importing the Stellar SDK and then using it to generate a new address each time someone refreshes the page. This is then funded with tesnet XLM using friendbot.

const kp = StellarSdk.Keypair.random();
await fetch(`https://friendbot.stellar.org/?addr=${kp.publicKey()}`);

We then have this function that gets pooled for new messages once every ten seconds:

async function fetchMessages () {
  try {
    const latest   = await rpc.getLatestLedger();
    const fromLedg = latest.sequence - 8000;
    const evResp = await rpc.getEvents({
      startLedger: fromLedg,
      filters: [{ type: 'contract', contractIds: [contractId] }],
    });
    const msgs = evResp.events.map(ev => {
      const hexPub  = ev.topic[0]._value._value._value.toString('hex');
      const stellar = StellarSdk.StrKey.encodeEd25519PublicKey(hexToUint8Array(hexPub));
      return {
        txHash: ev.txHash?.toString?.(),
        addr  : stellar,
        msg   : ev.value?._value?.toString?.().split('_').join(' ')
      };
    });
    renderChat(msgs);
  } catch (e) {
    console.error('fetchMessages() failed', e);
  }
}

And finally the sendMessage function to put messages into transactions and send them to a RPC node, which acts as our entry point to the decentralized network.

async function sendMessage (spokenText, keypair) {
  const sanitizedStr = spokenText
    .split(' ').join('_')
    .replace(/[^a-zA-Z0-9_]/g, '')
    .slice(0, 32);
  const account = await rpc.getAccount(keypair.publicKey());
  let tx = new StellarSdk.TransactionBuilder(account, {
    fee: StellarSdk.BASE_FEE,
    networkPassphrase
  })
  .addOperation(
    contract.call(
      'talk',
      StellarSdk.Address.fromString(keypair.publicKey()).toScVal(),
      StellarSdk.nativeToScVal(sanitizedStr, { type: 'symbol' })
    )
  )
  .setTimeout(30)
  .build();
  tx = await rpc.prepareTransaction(tx);
  tx.sign(keypair);
  const res = await rpc.sendTransaction(tx);
  console.log('☑ Message sent. txHash:', res.hash);
  return res.hash;
}

This is a simple, fun example of what is possible with web3 serverless architecture. No databases, no servers, just a single index.html file and a 13 line smart contract ❤️


Get The Blockchain Sector Newsletter, binge the YouTube channel and connect with me on Twitter

The Blockchain Sector newsletter goes out a few times a month when there is breaking news or interesting developments to discuss. All the content I produce is free, if you’d like to help please share this content on social media.

Thank you.

James Bachini

Disclaimer: Not a financial advisor, not financial advice. The content I create is to document my journey and for educational and entertainment purposes only. It is not under any circumstances investment advice. I am not an investment or trading professional and am learning myself while still making plenty of mistakes along the way. Any code published is experimental and not production ready to be used for financial transactions. Do your own research and do not play with funds you do not want to lose.


Posted

in

, , , , , ,

by

Tags: