Vyper的智能合约简介
Vyper是一种智能的面向合约的pythonic编程语言,面向以太坊虚拟机(EVM)。
它旨在遵循Python的类似简单性,安全性和可读性原则,并提供以下功能:
- 边界和溢出检查:在数组访问和算术级别上。
- 支持有符号整数和小数定点数
- 可判定性:应该可以计算任何函数调用的气体消耗的精确上限。
- 强类型:包括对单位的支持(例如,时间戳,timedelta,秒,wei,wei /秒,米每秒平方)。
- 小而易懂的编译器代码
- 对纯函数的有限支持:任何标记为常量的内容都不允许更改状态。
并遵循类似的原则,Vyper不支持:
- 修饰符
- 类继承
- 内联汇编
- 函数重载
- 运算符重载
- 递归调用
- 无限长的循环
- 二进制定点
介绍公开拍卖示例
作为在Vyper中编写的智能合约的介绍性示例,我们从公开的拍卖合约开始。所有Vyper语法都是有效的Python3语法,但并不是所有的Python3功能都可以在Vyper中使用。
在此合约中,参与者可以在有限的时间段内提交出价。当拍卖期结束时,预定的受益人接收最高出价金额。
# Open Auction
# Auction params
# Beneficiary receives money from the highest bidder
beneficiary: public(address)
auctionStart: public(timestamp)
auctionEnd: public(timestamp)
# Current state of auction
highestBidder: public(address)
highestBid: public(wei_value)
# Set to true at the end, disallows any change
ended: public(bool)
# Keep track of refunded bids so we can follow the withdraw pattern
pendingReturns: public(map(address, wei_value))
# Create a simple auction with `_bidding_time`
# seconds bidding time on behalf of the
# beneficiary address `_beneficiary`.
@public
def __init__(_beneficiary: address, _bidding_time: timedelta):
self.beneficiary = _beneficiary
self.auctionStart = block.timestamp
self.auctionEnd = self.auctionStart + _bidding_time
# Bid on the auction with the value sent
# together with this transaction.
# The value will only be refunded if the
# auction is not won.
@public
@payable
def bid():
# Check if bidding period is over.
assert block.timestamp < self.auctionEnd
# Check if bid is high enough
assert msg.value > self.highestBid
# Track the refund for the previous high bidder
self.pendingReturns[self.highestBidder] += self.highestBid
# Track new high bid
self.highestBidder = msg.sender
self.highestBid = msg.value
# Withdraw a previously refunded bid. The withdraw pattern is
# used here to avoid a security issue. If refunds were directly
# sent as part of bid(), a malicious bidding contract could block
# those refunds and thus block new higher bids from coming in.
@public
def withdraw():
pending_amount: wei_value = self.pendingReturns[msg.sender]
self.pendingReturns[msg.sender] = 0
send(msg.sender, pending_amount)
# End the auction and send the highest bid
# to the beneficiary.
@public
def endAuction():
# It is a good guideline to structure functions that interact
# with other contracts (i.e. they call functions or send Ether)
# into three phases:
# 1. checking conditions
# 2. performing actions (potentially changing conditions)
# 3. interacting with other contracts
# If these phases are mixed up, the other contract could call
# back into the current contract and modify the state or cause
# effects (Ether payout) to be performed multiple times.
# If functions called internally include interaction with external
# contracts, they also have to be considered interaction with
# external contracts.
# 1. Conditions
# Check if auction endtime has been reached
assert block.timestamp >= self.auctionEnd
# Check if this function has already been called
assert not self.ended
# 2. Effects
self.ended = True
# 3. Interaction
send(self.beneficiary, self.highestBid)
此示例只有一个构造函数,两个要调用的方法,以及用于管理合约状态的变量。这就是我们拍卖智能合约基本实施所需的全部内容。
让我们开始吧
# Auction params
# Beneficiary receives money from the highest bidder
beneficiary: public(address)
auctionStart: public(timestamp)
auctionEnd: public(timestamp)
# Current state of auction
highestBidder: public(address)
highestBid: public(wei_value)
# Set to true at the end, disallows any change
ended: public(bool)
我们首先声明变量以跟踪我们的合约状态。我们通过调用public
数据类型来初始化全局变量受益者address
。受益人将是最高出价者的资金接收者。我们还初始化变量auctionStart
并auctionEnd
使用数据类型timestamp
来管理公开拍卖期,并highestBid
使用数据类型wei_value
(以太小的以太面额)来管理拍卖状态。该变量ended
是一个布尔值,用于确定拍卖是否正式结束。
所有变量都传递给public函数。通过声明变量public,变量可以通过外部契约调用。在没有公共函数的情况下初始化变量默认为私有声明,因此只能访问同一合约中的方法。公共函数还为变量创建了一个“getter”函数,可通过外部调用访问contract.beneficiary()
。
现在,构造函数。
合约初始化有两个参数:_beneficiary
类型address
和_bidding_time
类型timedelta
,拍卖开始和结束之间的时差。我们这两条信息存储到合约的变量self.beneficiary
和self.auctionEnd
。我们可以通过电话访问当前时间block.timestamp
。block
是任何Vyper合约中可用的对象,并在调用时提供有关块的信息。与块类似,合约中我们可以使用的另一个重要对象是msg
,它提供有关方法调用者的信息。
通过初始设置,让我们看看我们的用户如何进行出价。
所述@payable
装饰允许用户一些ether发送到合约以调用装饰方法。在这种情况下,想要进行投标的用户bid()
在发送等于其期望出价(不包括汽油费)的金额时调用该方法。在调用合约中的任何方法时,我们都会提供一个内置变量msg
,我们可以访问任何方法调用者的公共地址msg.sender
。同样,可以通过调用访问用户发送的以太坊数量msg.value
。
我们首先使用assert
带有任何布尔语句的函数检查当前时间是否在拍卖结束时间之前。我们还会检查新出价是否高于最高出价。如果两个断言语句通过,我们可以安全地继续下一行; 否则,该bid()
方法抛出错误并恢复事务。如果两个断言陈述和前一个出价不等于零通过的检查,我们可以有把握地得出结论,我们有一个有效的新的最高出价。我们将之前的内容发回给前highestBid
一个highestBidder
并设置我们的新highestBid
和highestBidder
。
使用该endAuction()
方法,我们检查当前时间是否超过了auctionEnd
我们在合约初始化时设置的时间。我们还检查self.ended
以前没有设置过True
。如果拍卖已经结束,我们这样做是为了防止对该方法的任何调用,如果没有进行检查,这可能是恶意的。然后,我们通过设置self.ended
并向True
受益人发送最高出价金额来正式结束拍卖。
下一步
这个介绍和示例取自官方Vyper文档,这是您最好的资源。推荐的后续步骤是: