Skip to content
Volume S · The TensorCore.si Library

Solidity

Smart contracts for Ethereum.

Solidity is the main language for writing smart contracts: programs that run on Ethereum and compatible blockchains. Contracts hold and move digital assets according to code that anyone can verify. AI agents increasingly interact with blockchains, for example to pay for services or verify actions.

Prologue

Why learn Solidity?

Smart contract developers are in demand, and security skills here are especially valuable because bugs can lose real money.

  • Familiar syntax: looks like JavaScript, C++ and TypeScript.
  • Verifiable: code is public and runs exactly as written.
  • Agents and payments: AI agents can hold wallets and call contracts.
Prologue

Set up and run your first program

Start in the browser with Remix IDE (remix.ethereum.org). For projects, install Foundry, a fast toolkit for compiling, testing and deploying.

terminal
curl -L https://foundry.paradigm.xyz | bash
foundryup
forge init hello && cd hello
forge build && forge test
src/Hello.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Hello {
    string public greeting = "Hello, AI! Welcome to Solidity.";
}
TipAlways test on a local chain (Anvil) or a testnet before using real funds.
Chapter 1

Contracts, state and types

A contract is like a class whose state variables live permanently on the blockchain. uint256, address, bool and string are common types.

ModelRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract ModelRegistry {
    address public owner;
    uint256 public modelCount;
    string public latest;

    constructor() { owner = msg.sender; }

    function register(string calldata name) external {
        latest = name;
        modelCount += 1;
    }
}
Chapter 2

Functions and visibility

Functions are public, external, internal or private. view reads state and pure touches no state; both are free when called off-chain.

Maths.sol
contract Maths {
    function params(uint256 layers, uint256 width) public pure returns (uint256) {
        return 12 * layers * width * width;
    }
    uint256 private total;
    function add(uint256 x) external { total += x; }
    function getTotal() external view returns (uint256) { return total; }
}
Chapter 3

Mappings, structs and events

mapping is a key-value store. Structs group fields. Events write logs that apps and indexers can listen to.

GpuMarket.sol
contract GpuMarket {
    struct Listing { address seller; uint256 pricePerHour; bool active; }
    mapping(uint256 => Listing) public listings;
    uint256 public nextId;
    event Listed(uint256 indexed id, address seller, uint256 price);

    function list(uint256 price) external returns (uint256 id) {
        id = nextId++;
        listings[id] = Listing(msg.sender, price, true);
        emit Listed(id, msg.sender, price);
    }
}
Chapter 4

Access control and errors

require and custom errors stop invalid calls and undo all changes. Modifiers reuse checks such as "only the owner".

Owned.sol
contract Owned {
    address public owner = msg.sender;
    error NotOwner();
    modifier onlyOwner() {
        if (msg.sender != owner) revert NotOwner();
        _;
    }
    function transferOwnership(address next) external onlyOwner {
        require(next != address(0), "zero address");
        owner = next;
    }
}
Chapter 5

Receiving and sending ETH

payable functions accept Ether. Follow checks-effects-interactions: update state before sending funds to prevent reentrancy attacks.

Tips.sol
contract Tips {
    mapping(address => uint256) public balance;

    function tip(address to) external payable {
        require(msg.value > 0, "no value");
        balance[to] += msg.value;
    }

    function withdraw() external {
        uint256 amount = balance[msg.sender];
        balance[msg.sender] = 0;                       // effect first
        (bool ok, ) = payable(msg.sender).call{value: amount}("");
        require(ok, "transfer failed");                // then interaction
    }
}
Part II · AI with Solidity

Solidity and AI

AI and blockchains meet in a few places: AI agents that hold wallets and pay for API calls or compute, on-chain registries for model provenance, oracles that bring off-chain AI results on-chain, and LLM-assisted smart contract auditing. Heavy AI computation never runs on-chain because every node would have to repeat it.

The essential libraries

FoundryCompile, test and deploy contracts.
OpenZeppelinAudited contract building blocks.
web3.py / viemTalk to contracts from Python or TypeScript.
SlitherStatic analysis for contract bugs.

A model provenance registry

Record the hash of a model's weights so anyone can verify which version produced a result.

ModelProvenance.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract ModelProvenance {
    struct Model { bytes32 weightsHash; address publisher; uint64 timestamp; }
    mapping(string => Model[]) private versions;
    event Published(string name, uint256 version, bytes32 weightsHash);

    function publish(string calldata name, bytes32 weightsHash) external {
        versions[name].push(Model(weightsHash, msg.sender, uint64(block.timestamp)));
        emit Published(name, versions[name].length - 1, weightsHash);
    }

    function verify(string calldata name, uint256 v, bytes32 h) external view returns (bool) {
        return versions[name][v].weightsHash == h;
    }
}
Part III · Build an AI agent

An AI agent that reads a contract

The agent uses web3.py to call view functions on a local Anvil chain as a tool. Read-only calls cost nothing and cannot move funds, which is a safe starting point for on-chain agents.

Your goal→Model plans→Tool call→Your code runs the tool→Result back to model→Final answer
agent.py
# anvil &  (local chain)   then deploy ModelProvenance with forge create
import json
from web3 import Web3
from openai import OpenAI

w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
abi = json.load(open("out/ModelProvenance.sol/ModelProvenance.json"))["abi"]
reg = w3.eth.contract(address="0x5FbDB2315678afecb367f032d93F642f64180aa3", abi=abi)

def verify_model(name, version, sha256_hex):
    return str(reg.functions.verify(name, version, bytes.fromhex(sha256_hex)).call())

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
tools = [{"type": "function", "function": {"name": "verify_model", "description": "Check a model hash against the on-chain registry",
    "parameters": {"type": "object", "properties": {"name": {"type": "string"}, "version": {"type": "integer"},
    "sha256_hex": {"type": "string"}}, "required": ["name", "version", "sha256_hex"]}}}]
messages = [{"role": "user", "content": "Is version 0 of 'tc-llm' the weights with hash " + "ab" * 32 + "?"}]
for _ in range(5):
    msg = client.chat.completions.create(model="llama3.1", messages=messages, tools=tools).choices[0].message
    messages.append(msg)
    if not msg.tool_calls:
        print(msg.content); break
    for c in msg.tool_calls:
        messages.append({"role": "tool", "tool_call_id": c.id, "content": verify_model(**json.loads(c.function.arguments))})

Never give an AI agent a private key with real funds without strict limits and human approval.

Run it locallyInstall Ollama, run ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.
Part IV · Your path

Solidity learning roadmap

Tick items off as you go. Your progress is saved in this browser.

Beginner

Intermediate

Advanced

Part IV · Your path

Projects to build

TokenAn ERC-20 token using OpenZeppelin.
VotingA simple DAO poll with one vote per address.
Provenance registryPublish and verify AI model hashes.
Reference

Solidity cheat sheet

TaskSyntax
Versionpragma solidity ^0.8.24;
State varuint256 public count;
Functionfunction f(uint x) external returns (uint)
Mappingmapping(address => uint) bal;
Checkrequire(x > 0, "msg");
Eventemit Done(id);
Reference

Common mistakes

  • Sending funds before updating state opens reentrancy attacks.
  • Every write costs gas; loops over large arrays can make functions unusable.
  • Deployed code cannot be changed; test and audit before mainnet.
Reference

Where to go next

On TensorCore.si: JavaScript volume, TypeScript volume.

Keyboard: [ previous language · ] next language · / search the library