Day 4 Sophia 智能合约基础

学习 Sophia 语言语法和合约编译流程。

学习目标
  • 学习 Sophia 语言基础语法
  • 理解智能合约的基本结构
  • 掌握合约编译流程
  • 生成和解析 Calldata

1. Sophia 语言概述

1.1 语言特性
特性说明
函数式基于 ML 家族,无副作用
强类型编译时类型检查
状态受限状态修改必须显式声明
安全优先防止常见漏洞
FATE 编译高效的虚拟机执行
1.2 与 Solidity 对比
特性SophiaSolidity
范式函数式面向对象
类型系统强类型、推断强类型、显式
状态修改stateful 关键字默认可修改
重入攻击语言级防护需手动防护
整数溢出自动检查需 SafeMath

2. 合约结构

2.1 基本结构
// 编译器版本要求
@compiler >= 6

// 合约定义
contract MyContract =

    // 状态定义
    record state = {
        owner : address,
        value : int
    }

    // 初始化函数(构造函数)
    entrypoint init(initial_value : int) = {
        owner = Call.caller,
        value = initial_value
    }

    // 只读函数(不修改状态)
    entrypoint get_value() : int =
        state.value

    // 状态修改函数
    stateful entrypoint set_value(new_value : int) =
        put(state{ value = new_value })
2.2 函数修饰符
修饰符说明
entrypoint外部可调用
function内部函数
stateful可修改状态
payable可接收 AE
private私有函数
2.3 数据类型
// 基础类型
let a : int = 42                    // 整数(任意精度)
let b : bool = true                 // 布尔
let c : string = "hello"            // 字符串
let d : address = ak_xxx            // 地址
let e : hash = #abc123              // 哈希

// 复合类型
let g : list(int) = [1, 2, 3]       // 列表
let h : option(int) = Some(42)      // 可选值
let i : map(string, int) = {}       // 映射
let j : (int, string) = (1, "a")    // 元组

// 自定义类型
record person = { name : string, age : int }
datatype color = Red | Green | Blue

3. 实践任务

任务 4.1: 编写简单存储合约
from aeternity.compiler import CompilerClient

# 简单存储合约
simple_storage = '''
@compiler >= 6

contract SimpleStorage =

    record state = { value : int }

    entrypoint init(initial : int) = { value = initial }

    entrypoint get() : int = state.value

    stateful entrypoint set(new_value : int) =
        put(state{ value = new_value })

    stateful entrypoint add(amount : int) =
        put(state{ value = state.value + amount })
'''

# 编译合约
compiler = CompilerClient()

try:
    bytecode = compiler.compile(simple_storage)
    print("✅ 编译成功!")
    print(f"字节码长度: {len(bytecode)} 字符")
    print(f"字节码: {bytecode[:50]}...")
except Exception as e:
    print(f"❌ 编译失败: {e}")
任务 4.2: 理解 ACI
from aeternity.compiler import CompilerClient
import json

compiler = CompilerClient()

# 合约源码
contract_source = '''
@compiler >= 6

contract Calculator =

    entrypoint add(a : int, b : int) : int = a + b
    
    entrypoint sub(a : int, b : int) : int = a - b
    
    entrypoint mul(a : int, b : int) : int = a * b
    
    entrypoint div(a : int, b : int) : int = a / b
'''

# 生成 ACI (Application Contract Interface)
aci = compiler.generate_aci(contract_source)

print("=== ACI (Application Contract Interface) ===\n")
print(json.dumps(aci, indent=2))

# 解析函数信息
print("\n=== 函数列表 ===")
contract_info = aci.get('contract', {})
for func in contract_info.get('functions', []):
    name = func.get('name')
    args = func.get('arguments', [])
    returns = func.get('returns')
    
    args_str = ", ".join([f"{a['name']}: {a['type']}" for a in args])
    print(f"  {name}({args_str}) -> {returns}")
任务 4.3: 生成 Calldata
from aeternity.compiler import CompilerClient

compiler = CompilerClient()

contract_source = '''
@compiler >= 6

contract Demo =
    entrypoint init(value : int) = value
    entrypoint greet(name : string) : string = 
        String.concat("Hello, ", name)
    entrypoint add(a : int, b : int) : int = a + b
'''

# 编译合约
bytecode = compiler.compile(contract_source)

print("=== 生成 Calldata ===\n")

# 1. init 函数的 calldata
init_calldata = compiler.encode_calldata(contract_source, "init", ["42"])
print(f"init(42):")
print(f"  Calldata: {init_calldata[:50]}...")

# 2. greet 函数的 calldata
greet_calldata = compiler.encode_calldata(contract_source, "greet", ['"World"'])
print(f"\ngreet(\"World\"):")
print(f"  Calldata: {greet_calldata[:50]}...")

# 3. add 函数的 calldata
add_calldata = compiler.encode_calldata(contract_source, "add", ["10", "20"])
print(f"\nadd(10, 20):")
print(f"  Calldata: {add_calldata[:50]}...")

# 参数格式说明
print("""
=== 参数格式说明 ===
- int: "42", "-1", "1000000"
- string: '"hello"' (需要引号)
- bool: "true", "false"
- address: '"ak_xxx..."'
- list: "[1, 2, 3]"
- tuple: "(1, \"hello\")"
- record: "{ name = \"test\", value = 42 }"
""")
任务 4.4: 控制流和高阶函数
@compiler >= 6

contract ControlFlow =

    // if-else 表达式
    entrypoint abs(x : int) : int =
        if(x >= 0) x else -x
    
    // 模式匹配
    entrypoint describe(n : int) : string =
        switch(n)
            0 => "zero"
            1 => "one"
            _ => "many"
    
    // Option 处理
    entrypoint safe_div(a : int, b : int) : option(int) =
        if(b == 0)
            None
        else
            Some(a / b)
    
    // 列表操作
    entrypoint sum(xs : list(int)) : int =
        List.sum(xs)
    
    entrypoint double_all(xs : list(int)) : list(int) =
        List.map((x) => x * 2, xs)
    
    entrypoint filter_positive(xs : list(int)) : list(int) =
        List.filter((x) => x > 0, xs)
    
    // 递归
    entrypoint factorial(n : int) : int =
        if(n <= 1) 1
        else n * factorial(n - 1)

4. 练习题

@compiler >= 6

contract Counter =
    record state = { count : int }
    
    entrypoint init() = { count = 0 }
    
    entrypoint get() : int = state.count
    
    stateful entrypoint increment() =
        put(state{ count = state.count + 1 })
    
    stateful entrypoint decrement() =
        put(state{ count = state.count - 1 })
    
    stateful entrypoint reset() =
        put(state{ count = 0 })

@compiler >= 6

contract Voting =
    record state = {
        votes : map(string, int),
        voted : map(address, bool)
    }
    
    entrypoint init() = {
        votes = {},
        voted = {}
    }
    
    entrypoint get_votes(candidate : string) : int =
        Map.lookup_default(candidate, state.votes, 0)
    
    entrypoint has_voted(voter : address) : bool =
        Map.lookup_default(voter, state.voted, false)
    
    stateful entrypoint vote(candidate : string) =
        require(!has_voted(Call.caller), "Already voted")
        let current = get_votes(candidate)
        put(state{
            votes = state.votes{ [candidate] = current + 1 },
            voted = state.voted{ [Call.caller] = true }
        })
知识检查点
  • 理解 Sophia 语言特性
  • 编写基本的合约结构
  • 使用各种数据类型
  • 使用编译器编译合约
  • 生成和解码 Calldata