本次範例擷取自Solidity document其中一個example,Safe Remote Purchase是以ETH為主要交易貨幣下所使用的遠端買賣合約。合約提供三個功能,分別是停止販售、確認購買、確認接收,以及三個狀態階段來限制可以呼叫的function,每個function也有限制特定身分才能執行。
首先,seller在執行合約時,得一同支付等同於售價價格的ETH,而且這筆ETH必須是複數,因為Solidity不支援小數點,就算運算式結果會是小數,也只會取整數的部分。接著,seller啟動合約所支付的ETH會作為在合約的售價,並記錄在contract裡,在目前階段seller可以隨時啟動abort funtion 終止販售,並取回原先所放入的ETH。
當buyer確定要付款,呼叫 confirm()
function,不過buyer除了要支付原始價格,還得額外支付保證金,確保流程可以順利進行到下個階段;buyer收到貨物後,必須再次呼叫receive()
function,將之前所支付的保證金收回,seller也可以同時拿回收益+呼叫合約的金額。
pragma solidity^0.4.25;
contract SafePurchase{
//宣告三個變數可以公開身分及價格
uint public value;
address public seller;
address public buyer;
//三個階段
enum State{
Created,
Locked,
Inactive
}
//可以直接呼叫這個function來查看目前是哪個階段
State public state;
//由賣家呼叫contract,並投入金額
constructor() public payable{
seller = msg.sender;
value = msg.value/2;
require((2*value) == msg.value,"value isn't even number");
}
//四個modifier,判斷狀態、身分
modifier condition(bool _condition){
require(_condition);
_;
}
modifier inState(State _state){
require(_state == state,"Invalid state");
_;
}
modifier onlySeller(){
require(msg.sender == seller,"Not Seller");
_;
}
modifier onlyBuyer(){
require(msg.sender == buyer,"Not Buyer");
_;
}
//三個event,會將function觸發成功通知到blockchain上
event Aborted();
event PurchaseConfirm();
event ItemReceive();
//停止販售
function abort() public onlySeller inState(State.Created) {
emit Aborted();
state = State.Inactive;
seller.transfer(address(this).balance);
}
//確認購買
function confirm()
public
inState(State.Created)
condition(msg.value == (value*3))
payable
{
emit PurchaseConfirm();
buyer = msg.sender;
state = State.Locked;
}
//確認接收
function receive()
public
onlyBuyer
inState(State.Locked)
{
emit ItemReceive();
buyer.transfer(value);
seller.transfer(address(this).balance);
state = State.Inactive;
}
}
//三個階段
enum State{
Created,
Locked,
Inactive
}
//可以直接呼叫這個function來查看目前是哪個階段
State public state;
enum
type在solidity可以當作是階段的作用,呼叫enum
內的參數會依參數的排序回傳序號。在這個例子,針對買賣時的狀態,設定三個階段Created
、Locked
、Inactive
控管可以執行的function,並宣告enum State
的狀態變數state
,方便進行State
階段切換和判斷階段狀態
//四個modifier,判斷狀態、身分
modifier condiion(bool _condition){
require(_condition);
_;
}
modifier inState(State _state){
require(_state == state,"Invalid state");
_;
}
modifier onlySeller(){
require(msg.sender == seller,"Not Seller");
_;
}
modifier onlyBuyer(){
require(msg.sender == buyer,"Not Buyer");
_;
}
//三個event,會將function觸發成功通知到blockchain上
event Aborted();
event PurchaseConfirm();
event ItemReceive();
四個modifier
,分別判斷條件狀態、階段、身分,還有當執行三個function abort()
、confirm()
、receive()
後會觸發的三個event
//由seller呼叫contract,並投入金額
constructor() public payable{
seller = msg.sender;
value = msg.value/2;
require((2*value) == msg.value,"value isn't even number");
}
當seller deploy contract的時候,必須夾帶售價金額的ether,而且售價限定只能是偶數,也就不會有小數點的問題,變數value
會紀錄拆分一半的金額,並作為後續buyer付錢時需要多繳交的保證金,小弟認為多這一個機制比較可以保障sellers拿到錢,因為seller必須透過buyer執行receive()
才可能拿到錢
function abort() public onlySeller inState(State.Created) {
emit Aborted();
state = State.Inactive;
seller.transfer(address(this).balance);
}
放棄販售的function,seller取回放在合約的錢,Aborted event 會紀錄到transaction 的log,並將合約目前狀態改為Inactive,表示目前合約狀態無法呼叫任何一個function
function confirm()
public
inState(State.Created)
condition(msg.value == (value*3))
payable
{
emit PurchaseConfirm();
buyer = msg.sender;
state = State.Locked;
}
buyer執行確認購買,還需要多付一半的保證金,PurchaseConfirm event 會紀錄到transaction 的log,並將合約目前狀態改為Locked
function receive()
public
onlyBuyer
inState(State.Locked)
{
emit ItemReceive();
buyer.transfer(value);
seller.transfer(address(this).balance);
state = State.Inactive;
}
當buyer確實收到貨物,就可以執行receive()
function,拿回保證金,seller也可以拿到售價的錢,不過在執行function之前會先判斷目前狀態是不是符合Locked,ItemReceive event 會紀錄到transaction 的log,並將合約目前狀態改為Inactive,結束合約