---
# System prepended metadata

title: ETH HUB Romania - Arbitrum Stylus workshop - Demo
tags: [Arbitrum, ETH HUB Romania]

---

# VisitorBook

lib.rs

```rust
#![cfg_attr(not(feature = "export-abi"), no_main)]
extern crate alloc;

use stylus_sdk::{
    alloy_primitives::{Address, U256},
    msg,
    prelude::*,
};

sol_storage! {
    #[entrypoint]
    pub struct VisitorBook {
        address[] visitors;
        mapping(address => bool) has_visited;
    }
}

#[public]
impl VisitorBook {
    // Function to record a new visitor
    pub fn sign_guestbook(&mut self) {
        let visitor = msg::sender();

        // Check if the address has already visited
        if !self.has_visited.get(visitor) {
            // Add to visitors array
            self.visitors.push(visitor);
            // Mark as visited
            self.has_visited.setter(visitor).set(true);
        }
    }

    // Get total number of unique visitors
    pub fn get_total_visitors(&self) -> U256 {
        U256::from(self.visitors.len())
    }

    // Get visitor at specific index
    pub fn get_visitor_at_index(&self, index: U256) -> Address {
        self.visitors.get(index).unwrap()
    }

    // Check if an address has visited
    pub fn has_address_visited(&self, address: Address) -> bool {
        self.has_visited.get(address)
    }
}

```


# Fancy VisitorBook


Cargo.toml

```toml
[dependencies]
stylus-sdk = "0.6.0"
alloy-primitives = { version = "=0.7.6", default-features = false }
alloy-sol-types = { version = "=0.7.6", default-features = false }
alloy-sol-macro = { version = "=0.7.6", default-features = false }
alloy-sol-macro-expander = { version = "=0.7.6", default-features = false }
alloy-sol-macro-input = { version = "=0.7.6", default-features = false }
```

lib.rs

```rust
#![cfg_attr(not(feature = "export-abi"), no_main)]
extern crate alloc;

use stylus_sdk::{
    alloy_primitives::{Address, U256},
    alloy_sol_types::sol,
    call::transfer_eth,
    contract, evm, msg,
    prelude::*,
};

sol! {
    event Visit(address indexed sender, string message);
    error InsufficientPayment(address visitor, uint256 payment);
    error TransferFailed(address recipient, uint256 amount);
    error AlreadyVisited();
    error IndexOutOfBounds();
}

sol_storage! {
    #[entrypoint]
    pub struct VisitorBook {
        uint256 fee;
        address[] visitors;
        mapping(address => bool) has_visited;
    }
}

#[derive(SolidityError)]
pub enum VisitorBookErrors {
    InsufficientPayment(InsufficientPayment),
    TransferFailed(TransferFailed),
    AlreadyVisited(AlreadyVisited),
    IndexOutOfBounds(IndexOutOfBounds),
}

#[public]
impl VisitorBook {
    pub fn initialize(&mut self) {
        // Initialize constants
        self.fee.set(U256::from(100));
    }

    // Function to record a new visitor
    pub fn sign_guestbook(&mut self, message: String) -> Result<(), VisitorBookErrors> {
        let visitor = msg::sender();
        let value = msg::value();

        // Require a payment of 100 wei
        if value < self.fee.get() {
            return Err(VisitorBookErrors::InsufficientPayment(
                InsufficientPayment {
                    visitor,
                    payment: value,
                },
            ));
        }

        // Check if the address has already visited
        if self.has_visited.get(visitor) {
            return Err(VisitorBookErrors::AlreadyVisited(AlreadyVisited {}));
        }

        // Add to visitors array
        self.visitors.push(visitor);
        // Mark as visited
        self.has_visited.setter(visitor).set(true);

        // Emit event
        evm::log(Visit {
            sender: visitor,
            message,
        });

        // Transfer reward
        if contract::balance() >= self.fee.get() {
            if let Err(_) = transfer_eth(visitor, self.fee.get()) {
                return Err(VisitorBookErrors::TransferFailed(TransferFailed {
                    recipient: visitor,
                    amount: self.fee.get(),
                }));
            }
        }

        Ok(())
    }

    // Get total number of unique visitors
    pub fn get_total_visitors(&self) -> U256 {
        U256::from(self.visitors.len())
    }

    // Get visitor at specific index
    pub fn get_visitor_at_index(&self, index: U256) -> Result<Address, VisitorBookErrors> {
        self.visitors
            .get(index)
            .ok_or(VisitorBookErrors::IndexOutOfBounds(IndexOutOfBounds {}))
    }

    // Check if an address has visited
    pub fn has_address_visited(&self, address: Address) -> bool {
        self.has_visited.get(address)
    }
}
```