Building with Solidity

Tools Within Smart Contracts

This lesson covers the built-in tools available inside Solidity smart contracts - global variables like msg.sender and block.timestamp, validation functions, error handling, events for external communication, and state management techniques that enable secure contract logic.

Tools Available Within Smart Contracts

Smart contracts on VeChain don’t run in isolation. Solidity provides a set of built-in tools that help contracts:

  • Track who called a function

  • Record when it happened

  • Validate actions

  • Emit logs

  • Manage gas and execution via VeChain’s transaction model

This lesson focuses on those internal tools that developers use inside a contract to write logic, enforce conditions, and connect with the outside world.

Global Variables (Built-in Tools)

Solidity gives you automatic access to a set of global variables that provide critical context during contract execution:

Tool

Description

Example Use

msg.sender

Address that called the function

Track who triggered the call

msg.value

Amount of native token (VET) sent

Check if payment was sent

block.timestamp

Time of current block

Enforce time delays or cooldowns

tx.origin

Originator of the full transaction

Rarely used; use msg.sender instead

These require no imports or setup. They’re always available in any function.

Example (used in your contract):

require(participants[msg.sender].timestamp == 0, "Already checked in");

Writing to State

Smart contracts store permanent data using state variables. When you change these, you are writing to the blockchain, which:

  • Requires a transaction

  • Consumes gas

  • Becomes part of the immutable ledger

Example:

participants[msg.sender] = Participant(name, block.timestamp);

Here, we store a user’s check-in info by updating a mapping. This change:

  • Persists forever (unless overwritten)

  • Can be read by anyone (if public or via view function)

require() – Guard Conditions

require() is the main tool for validating inputs or enforcing rules. If the condition is false, the function reverts, and no gas is spent beyond that point.

Example:

require(bytes(name).length > 0, "Name is required");

Use it for:

  • Input validation

  • Preventing re-entry or duplicates

  • Role checks (onlyOwner, isRegistered, etc.)

Using Custom Errors Instead of Strings

Instead of hardcoded strings, custom errors reduce gas costs and make your contract easier to audit. 

error UnauthorizedAccess(address caller);
modifier onlyOwner() {
    if (msg.sender != contractOwner) {
        revert UnauthorizedAccess(msg.sender);
    }
    _;
}

With this error, you can spot unauthorized access, reverted transaction, as well as the user who tried to access.

Try/Catch Pattern for Fallback Safety

try this.getUser(msg.sender) returns (User memory user) {
  return user;
} catch {
  revert("User not found");
}

Try/catch in Solidity is used to handle external or internal function calls that might fail. It allows you to catch and respond to errors gracefully, preventing contract execution from reverting entirely when something goes wrong.

emit + Events

Events let you signal external tools (like frontends, explorers, and analytics) that something happened. These are not stored as state but are recorded in the transaction logs.

Define an event:

event ParticipantCheckedIn(address indexed user, string name, uint256 timestamp);

Emitting the event:

emit ParticipantCheckedIn(msg.sender, name, block.timestamp);

Tools like VeChain Explorer or your frontend app can listen for these logs to update the UI or trigger notifications.

Tool

Purpose

msg.sender, block.timestamp

Provide transaction context

require()

Enforce logic and prevent invalid actions

emit + events

Log actions for frontends and explorers

Custom errors

Lower-gas reverts with structured metadata

try/catch

Handle failed function calls gracefully

State variables

Store persistent data on-chain

Write functions

Update storage; require gas and transactions

Clause

VeChain instruction wrapper for functions

Fee delegation

Let another wallet pay for transaction gas