1 场景
在网购的应用场景中,我们定义如下几个关键要素:
- 卖家,商品出售者
- 买家,商品购买者
- 保证金,商品交易过程中锁定的资金
2 逻辑
- 所有参与者持有一个区块链账户
- 卖方创建合约,指定商品价格
- 买方使用代币购买,钱款锁定
- 卖方发货
- 买方确认收货,钱款打给卖方
3 完整代码
源代码地址 https://solidity.readthedocs.io/en/v0.5.1/solidity-by-example.html
pragma solidity >=0.4.22 <0.6.0;
contract Purchase {
uint public value;
address payable public seller;
address payable public buyer;
enum State { Created, Locked, Inactive }
State public state;
constructor() public payable {
seller = msg.sender;
value = msg.value / 2;
require((2 * value) == msg.value, "Value has to be even.");
}
modifier condition(bool _condition) {
require(_condition);
_;
}
modifier onlyBuyer() {
require(
msg.sender == buyer,
"Only buyer can call this."
);
_;
}
modifier onlySeller() {
require(
msg.sender == seller,
"Only seller can call this."
);
_;
}
modifier inState(State _state) {
require(
state == _state,
"Invalid state."
);
_;
}
event Aborted();
event PurchaseConfirmed();
event ItemReceived();
function abort()
public
onlySeller
inState(State.Created)
{
emit Aborted();
state = State.Inactive;
seller.transfer(address(this).balance);
}
function confirmPurchase()
public
inState(State.Created)
condition(msg.value == (2 * value))
payable
{
emit PurchaseConfirmed();
buyer = msg.sender;
state = State.Locked;
}
function confirmReceived()
public
onlyBuyer
inState(State.Locked)
{
emit ItemReceived();
state = State.Inactive;
buyer.transfer(value);
seller.transfer(address(this).balance);
}
}
4 解析
4.1 数据结构
买家卖家都需要进行代币的交易活动,因此账户定义为 address payable
。这个合约出现了 enum
枚举类型。
uint public value; // 公开的价格数据
address payable public seller;
address payable public buyer;
enum State { Created, Locked, Inactive }
State public state;
4.2 构造函数
此处要求商家打入的资金为商品售价的2倍作为保证金,因此 msg.value
必须为偶数,才能确保 ÷2 算出的商品价格是正常值。
constructor() public payable {
seller = msg.sender;
value = msg.value / 2;
require((2 * value) == msg.value, "Value has to be even.");
}
4.3 修改器
modifier
指示函数修改器。本示例中,这种修改器以嵌入的方式加到被作用函数上。运行时,_;
部分会用被作用函数的原有代码替代。
modifier condition(bool _condition) { require(_condition); _; }
modifier onlyBuyer() {
require(msg.sender == buyer, "Only buyer can call this.");
_;
}
modifier onlySeller() {
require(msg.sender == seller, "Only seller can call this.");
_;
}
modifier inState(State _state) {
require(state == _state, "Invalid state.");
_;
}
4.4 购买下单函数
该函数使用了多个修饰器来进行条件检查。要求 msg.value
的值要恰好等于设置的商品价格的2倍作为保证金。调用时会触发 PurchaseConfirmed
事件。购买确认后,买家的对应代币被锁定。
event PurchaseConfirmed();
function confirmPurchase()
public inState(State.Created)
condition(msg.value == (2 * value)) payable
{
emit PurchaseConfirmed();
buyer = msg.sender;
state = State.Locked;
}
}
4.5 收货确认函数
买家在收到货物后,调用该函数确认打款。将触发 ItemReceived
事件。这里的函数设计流程再一次体现了先改状态再做实际操作的思路,避免同时多次调用,重复转账。address(this).balance
是指当前合约中的余额,在实际中是卖家的保证金 + 商品钱款。由于合约是卖家创建的,所以应该退回给卖家。
this
的定义如下:
The current contract, explicitly convertible to address
event ItemReceived();
function confirmReceived() public onlyBuyer inState(State.Locked)
{
emit ItemReceived();
state = State.Inactive;
// 退回买家等同于商品价格的保证金
buyer.transfer(value);
// 退回卖家保证金及货款
seller.transfer(address(this).balance);
}
4.6 下架函数
卖家在商品被购买前可以进行商品下架操作,触发 Aborted
事件,并退回相关资金。
event Aborted();
function abort() public onlySeller inState(State.Created)
{
emit Aborted();
state = State.Inactive;
// 退回卖家保证金
seller.transfer(address(this).balance);
}
(完)
网友评论