类型

Vyper 是一种静态类型语言。每个变量(状态和局部变量)的类型必须在编译时指定或至少已知。Vyper 提供了几种基本类型,可以组合起来形成复杂类型。

此外,类型可以在包含运算符的表达式中相互交互。

值类型

以下类型也称为值类型,因为这些类型的变量始终按值传递,即在用作函数参数或在赋值中时始终被复制。

布尔值

关键字: bool

布尔值是一种用于存储逻辑/真值的类型。

唯一可能的值是常量 TrueFalse

运算符

运算符

描述

not x

逻辑否定

x and y

逻辑合取

x or y

逻辑析取

x == y

相等

x != y

不相等

布尔运算符 (orand) 的短路与 Python 的行为一致。

带符号整数 (N 位)

关键字: intN (例如,int128)

一个带符号整数,可以存储正负整数。 N 必须是 8 到 256(含)之间的 8 的倍数。

从 -2N-1 到 (2N-1 - 1)(含)的带符号整数值。

即使小数部分为零,整数字面量也不能带小数点。例如,2.0 不能解释为整数。

运算符

比较运算

比较运算返回布尔值。

运算符

描述

x < y

小于

x <= y

小于或等于

x == y

等于

x != y

不等于

x >= y

大于或等于

x > y

大于

xy 必须是相同的类型。

算术运算符

运算符

描述

x + y

加法

x - y

减法

-x

一元减法/否定

x * y

乘法

x / y

除法

x**y

求幂

x % y

取模

xy 必须是相同的类型。

位运算符

运算符

描述

x & y

按位与

x | y

按位或

x ^ y

按位异或

xy 必须是相同的类型。

移位

运算符

描述

x << y

左移

x >> y

右移

移位仅适用于 256 位宽类型。也就是说,x 必须是 int256,而 y 可以是任何无符号整数。 int256 的右移编译为带符号右移(EVM SAR 指令)。

注意

虽然在运行时移位不受检查(即,它们可以是任意数量的位),但为了防止常见的错误,编译器在编译时更加严格,并将阻止越界移位。例如,在运行时,1 << 257 将评估为 0,而在编译时,该表达式将引发 OverflowException

无符号整数 (N 位)

关键字: uintN (例如,uint8)

一个无符号整数,可以存储正整数。 N 必须是 8 到 256(含)之间的 8 的倍数。

从 0 到 (2N-1) 的整数值。

即使小数部分为零,整数字面量也不能带小数点。例如,2.0 不能解释为整数。

注意

整数字面量默认解释为 int256。在 uint8 更合适的情况下,例如赋值,字面量可能会被解释为 uint8。示例:_variable: uint8 = _literal。为了将字面量显式转换为 uint8,请使用 convert(_literal, uint8)

运算符

比较运算

比较运算返回布尔值。

运算符

描述

x < y

小于

x <= y

小于或等于

x == y

等于

x != y

不等于

x >= y

大于或等于

x > y

大于

xy 必须是相同的类型。

算术运算符

运算符

描述

x + y

加法

x - y

减法

x * y

乘法

x / y

除法

x**y

求幂

x % y

取模

xy 必须是相同的类型。

位运算符

运算符

描述

x & y

按位与

x | y

按位或

x ^ y

按位异或

~x

按位取反

xy 必须是相同的类型。

注意

位运算符 not 目前仅适用于 uint256 类型。

移位

运算符

描述

x << y

左移

x >> y

右移

移位仅适用于 256 位宽类型。也就是说,x 必须是 uint256,而 y 可以是任何无符号整数。 uint256 的右移编译为带符号右移(EVM SHR 指令)。

注意

虽然在运行时移位不受检查(即,它们可以是任意数量的位),但为了防止常见的错误,编译器在编译时更加严格,并将阻止越界移位。例如,在运行时,1 << 257 将评估为 0,而在编译时,该表达式将引发 OverflowException

小数

关键字: decimal

小数是一种用于存储十进制定点值的类型。

精度为 10 位小数的数值,介于 -18707220957835557353007165858768422651595.9365500928 (-2167 / 1010) 和 18707220957835557353007165858768422651595.9365500927 ((2167 - 1) / 1010) 之间。

为了使字面量被解释为 decimal,它必须包含小数点。

decimal 的 ABI 类型(用于计算方法标识符)是 fixed168x10

运算符

比较运算

比较运算返回布尔值。

运算符

描述

x < y

小于

x <= y

小于或等于

x == y

等于

x != y

不等于

x >= y

大于或等于

x > y

大于

xy 必须是 decimal 类型。

算术运算符

运算符

描述

x + y

加法

x - y

减法

-x

一元减法/否定

x * y

乘法

x / y

除法

x % y

取模

xy 必须是 decimal 类型。

地址

关键字: address

地址类型保存一个以太坊地址。

地址类型可以保存一个以太坊地址,相当于 20 个字节或 160 位。地址字面量必须用十六进制表示法编写,以 0x 开头,并且必须是校验和

成员

成员

类型

描述

balance

uint256

地址的余额

codehash

bytes32

地址处代码的 Keccak,如果未部署合约,则为 EMPTY_BYTES32

codesize

uint256

部署在地址处的代码大小(以字节为单位)

is_contract

bool

指示是否在地址处部署了合约的布尔值

code

Bytes

合约字节码

语法如下:_address.<member>,其中 _addressaddress 类型,而 <member> 是上述关键字之一。

注意

诸如 SELFDESTRUCTCREATE2 之类的操作允许在地址处删除和替换字节码。你不应该假设地址成员的值在将来不会改变。

注意

_address.code 需要使用 slice 显式地提取合约字节码的一部分。如果提取的部分超过了字节码的范围,这将抛出异常。你可以使用 _address.codesize 检查 _address.code 的大小。

M 字节宽固定大小字节数组

关键字: bytesM 这是一个 M 字节宽的字节数组,与动态大小的字节数组类似。在 ABI 级别上,它被注释为 bytesM(例如,bytes32)。

示例

# Declaration
hash: bytes32
# Assignment
self.hash = _hash

some_method_id: bytes4 = 0x01abcdef

运算符

关键字

描述

keccak256(x)

返回 keccak256 哈希值作为 bytes32。

concat(x, ...)

连接多个输入。

slice(x, start=_start, len=_len)

返回从 _start 开始的长度为 _len 的切片。

其中 x 是一个字节数组,_start_len 是整数值。

字节数组

关键字: Bytes

一个具有最大大小的字节数组。

语法为 Bytes[maxLen],其中 maxLen 是一个整数,表示字节的最大数量。在 ABI 级别上,固定大小的字节数组被注释为 bytes

字节字面量可以作为字节字符串给出。

bytes_string: Bytes[100] = b"\x01"

字符串

关键字: String

固定大小的字符串可以保存字符数量等于或少于字符串最大长度的字符串。在 ABI 级别上,固定大小的字节数组被注释为 string

example_str: String[100] = "Test String"

枚举

关键字: enum

枚举是自定义定义的类型。枚举必须至少有一个成员,并且最多可以包含 256 个成员。成员由 uint256 值表示,形式为 2n,其中 n 是成员在范围 0 <= n <= 255 中的索引。

# Defining an enum with two members
enum Roles:
    ADMIN
    USER

# Declaring an enum variable
role: Roles = Roles.ADMIN

# Returning a member
return Roles.ADMIN

运算符

比较

比较运算返回布尔值。

运算符

描述

x == y

等于

x != y

不等于

x in y

x 在 y 中

x not in y

x 不在 y 中

按位运算符

运算符

描述

x & y

按位与

x | y

按位或

x ^ y

按位异或

~x

按位取反

枚举成员可以使用上面的按位运算符进行组合。虽然枚举成员的值是二的幂,但枚举成员组合可能不是。

innot in 运算符可以与枚举成员组合一起使用来检查成员资格。

enum Roles:
    MANAGER
    ADMIN
    USER

# Check for membership
@external
def foo(a: Roles) -> bool:
    return a in (Roles.MANAGER | Roles.USER)

# Check not in
@external
def bar(a: Roles) -> bool:
    return a not in (Roles.MANAGER | Roles.USER)

请注意,in 与严格相等 (==) 不同。 in 检查两个枚举对象上的任何标志是否同时设置,而 == 检查两个枚举对象是否按位相等。

以下代码使用按位运算从给定的 Roles 对象添加和撤销权限。

引用类型

引用类型是指其组件可以在不复制的情况下原地赋值的类型。例如,数组和结构成员可以单独赋值,而不覆盖整个数据结构。

注意

在调用约定方面,引用类型按值传递,而不是按引用传递。这意味着,调用函数不需要担心被调用函数修改传递的结构的数据。

固定大小列表

固定大小列表保存有限数量的元素,这些元素属于指定的类型。

列表可以用 _name: _ValueType[_Integer] 声明,除了 Bytes[N]String[N] 和枚举。

# Defining a list
exampleList: int128[3]

# Setting values
exampleList = [10, 11, 12]
exampleList[2] = 42

# Returning a value
return exampleList[0]

多维列表也是可能的。声明的符号与其他一些语言相比是相反的,但访问符号没有反转。

二维列表可以用 _name: _ValueType[inner_size][outer_size] 声明。元素可以用 _name[outer_index][inner_index] 访问。

# Defining a list with 2 rows and 5 columns and set all values to 0
exampleList2D: int128[5][2] = empty(int128[5][2])

# Setting a value for row the first row (0) and last column (4)
exampleList2D[0][4] = 42

# Setting values
exampleList2D = [[10, 11, 12, 13, 14], [16, 17, 18, 19, 20]]

# Returning the value in row 0 column 4 (in this case 14)
return exampleList2D[0][4]

注意

在存储中定义一个大小明显大于 2**64 的数组会导致安全漏洞,因为存在溢出风险。

动态数组

动态数组表示有界数组,其长度可以在运行时修改,直到类型中指定的边界为止。它们可以用 _name: DynArray[_Type, _Integer] 声明,其中 _Type 可以是值类型或引用类型(映射除外)。

# Defining a list
exampleList: DynArray[int128, 3]

# Setting values
exampleList = []
# exampleList.pop()  # would revert!
exampleList.append(42)  # exampleList now has length 1
exampleList.append(120)  # exampleList now has length 2
exampleList.append(356)  # exampleList now has length 3
# exampleList.append(1)  # would revert!

myValue: int128 = exampleList.pop()  # myValue == 356, exampleList now has length 2

# myValue = exampleList[2]  # would revert!

# Returning a value
return exampleList[0]

注意

尝试访问数组运行时长度之外的数据、从空数组中 pop() 或向满数组 append() 将导致运行时 REVERT。尝试将大于数组边界的数组传递到 calldata 中将导致运行时 REVERT

注意

为了使代码易于理解,在使用数组作为迭代器时不允许修改数组。例如,以下用法不允许

for item in self.my_array:
    self.my_array[0] = item

在 ABI 中,它们表示为 _Type[]。例如,DynArray[int128, 3] 表示为 int128[]DynArray[DynArray[int128, 3], 3] 表示为 int128[][]

注意

在存储中定义一个大小明显大于 2**64 的动态数组会导致安全漏洞,因为存在溢出风险。

结构体

结构体是自定义定义的类型,可以对多个变量进行分组。

结构体类型可以在映射和数组中使用。结构体可以包含数组和其他结构体,但不能包含映射。

结构体成员可以通过 struct.argname 访问。

# Defining a struct
struct MyStruct:
    value1: int128
    value2: decimal

# Declaring a struct variable
exampleStruct: MyStruct = MyStruct({value1: 1, value2: 2.0})

# Accessing a value
exampleStruct.value1 = 1

映射

映射是 哈希表,它们是虚拟初始化的,这样每个可能的键都存在并映射到一个值,该值的字节表示都是零:类型的 默认值

键数据没有存储在映射中。相反,它的 keccak256 哈希值用于查找值。因此,映射没有长度,也没有“设置”键或值的的概念。

映射类型声明为 HashMap[_KeyType, _ValueType]

  • _KeyType 可以是任何基本类型或字节类型。映射、数组或结构体不支持作为键类型。

  • _ValueType 实际上可以是任何类型,包括映射。

注意

映射只允许作为状态变量。

# Defining a mapping
exampleMapping: HashMap[int128, decimal]

# Accessing a value
exampleMapping[0] = 10.1

注意

映射没有长度的概念,因此无法迭代。

初始值

与大多数编程语言不同,Vyper 没有 null 的概念。相反,每个变量类型都有一个默认值。要检查变量是否为空,必须将其与给定类型的默认值进行比较。

要将变量重置为其默认值,请将其赋值给内置的 empty() 函数,该函数为该类型构造一个零值。

注意

内存变量必须在声明时分配一个值。

在这里你可以找到所有类型和默认值的列表

类型

默认值

地址

0x0000000000000000000000000000000000000000

bool

bytes32

0x0000000000000000000000000000000000000000000000000000000000000000

十进制

0.0

uint8

0

int128

0

int256

0

uint256

0

注意

Bytes 中,数组以所有字节都设置为 '\x00' 开始。

注意

在引用类型中,所有类型的成员都设置为其初始值。

类型转换

Vyper 中的所有类型转换都必须使用内置的 convert(a: atype, btype) 函数显式地进行。Vyper 中的类型转换旨在安全直观。所有类型转换都将检查输入是否在输出类型的范围内。一般原则如下

  • 除了涉及十进制和布尔值的转换外,输入按位保存。

  • 转换为布尔值将所有非零输入映射为 1。

  • 从十进制转换为整数时,输入被截断为零。

  • address 类型被视为 uint160,但与带符号整数和十进制数的转换不允许。

  • 将右填充类型(bytesBytesString)转换为左填充类型,会进行旋转以转换填充。例如,从 bytes20 转换为 address 将导致输入向右旋转 12 个字节。

  • 在有符号整数和无符号整数之间转换,如果输入为负数,则会反转。

  • 缩窄转换(例如,int256 -> int128)会检查输入是否在输出类型的范围内。

  • 在字节和整数类型之间转换,如果输出类型是有符号的,则会进行符号扩展。例如,将 0xff (bytes1) 转换为 int8 将返回 -1

  • 在大小不同的字节和整数类型之间转换,遵循先经过最接近的整数类型规则。例如,bytes1 -> int16 等同于 bytes1 -> int8 -> int16(符号扩展,然后加宽)。uint8 -> bytes20 等同于 uint8 -> uint160 -> bytes20(向左旋转 12 个字节)。

  • 枚举只能转换为和从 uint256 转换。

一个小的 Python 参考实现作为 Vyper 测试套件的一部分维护,可以在 这里 找到。规则的动机和更详细的讨论可以在 这里 找到。