James Bachini

Creating Landing Pages Using Web3 Backends

product Validation

In this tutorial we will be demonstrating how web3 technology can be used to store data on-chain rather than in a database when building a landing page.

James On YouTube

We will be using a simple Soroban smart contract to store a counter which increases each time a transaction is called. To try this at home you’ll need the following:

Let’s start with the smart contract which is a modified version of the increment contract in Soroban examples.

Soroban Smart Contract

We start by importing the Soroban SDK before creating the contract iteself. The counter_key is a utility function which creates a key for each user. Then there are two public functions.

  • Increment – write function which increases each wallet addresses individual counter
  • Read – which returns any wallets count
#![no_std]
use soroban_sdk::{contract, contractimpl, log, symbol_short, Address, Env, Symbol};

#[contract]
pub struct MultiUserIncrementContract;

#[contractimpl]
impl MultiUserIncrementContract {
    fn counter_key(user: &Address) -> (Symbol, Address) {
        (symbol_short!("CTR"), user.clone())
    }

    pub fn increment(env: Env, caller: Address) -> u32 {
        caller.require_auth();
        let key = Self::counter_key(&caller);
        let mut count: u32 = env.storage().instance().get(&key).unwrap_or(0);
        log!(&env, "User: {}, count: {}", caller, count);
        count += 1;
        env.storage().instance().set(&key, &count);
        env.storage().instance().extend_ttl(50, 100);
        count
    }

    pub fn read(env: Env, user: Address) -> u32 {
        let key = Self::counter_key(&user);
        env.storage().instance().get(&key).unwrap_or(0)
    }
}

It’s worth noting that the read function is free to call and would be executed as a simulated transaction.

This contract can be deployed to Stellar using the following commands or you can use the following contract address on testnet.
CASEN2ZFCC5MY3QQSWPFPGZZIM3VM5GSTASKBKO7AO7UCFH4NJ2PGGOA

cargo build --target wasm32-unknown-unknown --release

stellar contract deploy  --wasm target/wasm32-unknown-unknown/release/multi_user_increment.wasm --source james --network testnet

Any user can interact with the contract and all on-chain data is public, which is worth noting that you can’t store private information (at least without encrypting it first).


Landing Page

The landing page is a simple index.html file which is designed to validate a product idea.

Full code is here: https://github.com/jamesbachini/Stellar-Product-Validation

We are importing the Stellar SDK which provides the logic to read and write onchain data.

<script src="https://cdnjs.cloudflare.com/ajax/libs/stellar-sdk/13.1.0/stellar-sdk.js"></script>

We can then send write transactions like this:

const rpc = new StellarSdk.rpc.Server('https://soroban-testnet.stellar.org');
const contract = new StellarSdk.Contract(CONTRACT_ID);
const networkPassphrase = StellarSdk.Networks.TESTNET;
const sourceKeypair = StellarSdk.Keypair.fromSecret(SECRET_KEY);
const scValAddress = StellarSdk.nativeToScVal(PUBLIC_KEY, { type: "address" });
const sourceAccount = await rpc.getAccount(PUBLIC_KEY);
const tx = new StellarSdk.TransactionBuilder(sourceAccount, {
    fee: StellarSdk.BASE_FEE,
    networkPassphrase: StellarSdk.Networks.TESTNET,
}).addOperation(contract.call("increment", scValAddress)).setTimeout(30).build();
const preparedTx = await rpc.prepareTransaction(tx);
preparedTx.sign(sourceKeypair);
const txResult = await rpc.sendTransaction(preparedTx);
console.log('txResult', txResult);

And read transactions are simulated like this:

const tx = new StellarSdk.TransactionBuilder(sourceAccount, {
    fee: StellarSdk.BASE_FEE,
    networkPassphrase: StellarSdk.Networks.TESTNET,
})
.addOperation(contract.call("read", scValAddress))
.setTimeout(30)
.build();
rpc.simulateTransaction(tx).then((sim) => {
    const decoded = StellarSdk.scValToNative(sim.result?.retval);
    alert(`Clicks: ${decoded}`);
});

The final product is a simple web page that stores information on a decentralized p2p network rather than a database.

Product Validation Website


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.