[solidity] 簡單的遠程購買合約

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.7;

/*
此合約是一個類似”網購“的功能,通常我們使用的網購存在一個可信任的第三方中介,當買家收貨確認后,賣家才收到錢
但是此合約由于沒有第三方中介,所以使用了押金的方式,當雙方都有押金在合約中鎖定,可有意愿去解決出現的末知問題
由賣家發(fā)起網購合約,設定商品的價錢value,然后雙方都支付兩倍value的價錢作為押金,并鎖定。
完成交易收到貨后,賣家可以退回3倍value(押金+貨款),買家可以退回1倍value(押金)
部署地址 https://rinkeby.etherscan.io/tx/0x641a0f38bf11b377cd7fe926a4ed508769e7d643bf1cb9a4f4268f1757e66558
 */

contract Purchase {

    uint public value; // 約定的商品價錢
    uint public value2; // 約定的商品價錢
    address payable public seller; // 賣家地址
    address payable public buyer; // 買家地址

    enum State { Created, Locked, Release, Inactive } // 訂單狀態(tài)枚舉
    State public state; // 當前訂單狀態(tài)

    // 條件判斷修飾符
    modifier condition(bool condition_) {
        require(condition_);
        _;
    }

    /// 只有買家可以調用此函數
    error OnlyBuyer();
    /// 只有賣家可以調用此函數
    error OnlySeller(); 
    /// 當前訂單狀態(tài)不允許調用此函數
    error InvalidState();
    /// 傳入的參數必須是偶數
    error ValueNotEven();

    // modifier修飾符:判斷是否買家在調用函數
    modifier onlyBuyer() {
        if (msg.sender != buyer) {
            revert OnlyBuyer(); // 不是則回滾狀態(tài)、向外報錯
        }
        _;
    }

    // modifier修飾符:判斷是否買家在調用函數
    modifier onlySeller() {
        if(msg.sender != seller) {
            revert OnlySeller(); // 不是則回滾狀態(tài)、向外報錯
        }
        _;
    }

    // modifier修飾符:判斷是否在`state`狀態(tài)
    modifier inState(State state_) {
        if (state != state_) {
            revert InvalidState(); // 不是則回滾狀態(tài)、向外報錯
        }
        _;
    }

    event Aborted(); // 交易結束
    event PurchaseConfirmed(); // 交易確定
    event ItemReceived(); // 已收到貨
    event SellerRefunded(); // 賣家退款
    
    /// 需要確定`msg.value`是一個偶數
    /// 因為solidity在除法計數中,小數會直接截斷(如 99 / 2 = 49.5 會截斷為49)
    /// 使用乘法 * 2去判斷傳入的value是否偶數
    constructor() payable {
        seller = payable(msg.sender);
        value = msg.value / 2;
        if ((2 * value) != msg.value) {
            revert ValueNotEven();
        }
    }

    /// 中止交易,并取回以太幣。
    /// 只能在合同開始前,由賣方調用
    function abort() 
        external
        onlySeller
        inState(State.Created)
    {
        emit Aborted();
        state = State.Inactive;// 更改合約狀態(tài)為Inactive

        // 我們直接在這里使用轉賬。它是重入安全的,
        // 因為我們是最后一行調用transfer的,并且已經在之前修改了State狀態(tài)。
        seller.transfer(address(this).balance);
    }

    /// 以買家身份確定交易
    /// 交易必須包含`2 * value`的以太幣(value是constructor方法賣家確定的)
    /// 這些以太幣會鎖定直到交易完成(confirmReceived函數被調用)
    function confirmPurchase() 
        external
        inState(State.Created)
        condition(msg.value == (2 * value))
        payable
    {
        emit PurchaseConfirmed(); // 告知交易確認
        buyer = payable(msg.sender); // 記錄買家
        state = State.Locked; // 更改合約狀態(tài)為Locked
    }

    /// 確認收貨,這將會解鎖以太幣
    function confirmReceived() 
        external
        onlyBuyer
        inState(State.Locked)
    {
        emit ItemReceived(); // 告知已收貨
        // 首先更改狀態(tài)是很重要的,否則再次調用此函數,會再次發(fā)起transfer轉賬
        state = State.Release;
        // 留下貨款,取回押金
        buyer.transfer(value);
    }

    /// 此函數給賣家退回押金和貨款
    function refundSeller() 
        external
        onlySeller
        inState(State.Release)
    {
        emit SellerRefunded();
        // 首先更改狀態(tài)是很重要的,否則再次調用此函數,會再次發(fā)起transfer轉賬
        state = State.Inactive;
        // 取回押金和貨款
        seller.transfer(3 * value);
    }
}
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容