如何使用 Solidity 编写 PayNow 函数?

在本教程中,将了解帮助我们将资金从一个帐户转移到另一个帐户的三个函数,以及推荐的方法。

1.png

介绍

web3经济正在蓬勃发展,学习这项技术的最佳时机就是现在。我们不能错过对web3开发者的全球需求。

区块链开发人员的平均年薪大约是146250美元,这是其他软件开发职业中最高的。

现在对web3开发人员的需求是如此之高,如果我们在接下来的3个月里致力于学习和构建区块链应用程序,我们将在最短的时间内获得一份利润丰厚的工作。

该如何开始呢?可以通过构建一个dApp来实现这一点。

现在让我们进入本教程的核心部分……

为什么应该在 Solidity 中掌握支付处理?

诸如Solidity之类的区块链编程语言是一种以处理货币为目的的编程语言。它们旨在彻底改变我们在线交易的方式。对于其他编程语言,如Java、c++、PHP等,我们就不能这么说了。

从一开始,使用Solidity编写智能合约就要求我们知道如何处理两个或多个账户之间的资金流动。

有了智能合约,所有的会计流程都变得自动化,对银行人员的需求变得毫无用处。

不利的一面是,相当多的黑客攻击都是针对智能合约中支付方式的弱点进行的。

在本教程中,将了解帮助我们将资金从一个帐户转移到另一个帐户的三个函数,以及推荐的方法。

PayNow智能合约示例

下面的代码片段是一个BookStore智能合约示例,它允许人们在线销售图书。该示例包括处理智能合约支付的三个函数。

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
​
contract BookStore {
    // Contains global store data
    uint256 taxFee;
    address immutable taxAccount;
    uint8 totalSupply = 0;
​
    // Specifies book information
    struct BookStruct {
        uint8 id;
        address seller;
        string title;
        string description;
        string author;
        uint256 cost;
        uint256 timestamp;
    }
​
    // Associates books with sellers and buyers
    BookStruct[] books;
    mapping(address => BookStruct[]) booksOf;
    mapping(uint8 => address) public sellerOf;
    mapping(uint8 => bool) bookExists;
​
    // Logs out sales record
    event Sale(
        uint8 id,
        address indexed buyer,
        address indexed seller,
        uint256 cost,
        uint256 timestamp
    );

    // Logs out created book record
    event Created(
        uint8 id,
        address indexed seller,
        uint256 timestamp
    );
​
    // Initializes tax on book sale
    constructor(uint256 _taxFee) {
        taxAccount = msg.sender;
        taxFee = _taxFee;
    }
​
    // Performs book creation
    function createBook(
        string memory title, 
        string memory description, 
        string memory author, 
        uint256 cost
) public returns (bool) {
        require(bytes(title).length > 0, "Title empty");
        require(bytes(description).length > 0, "Description empty");
        require(bytes(author).length > 0, "Description empty");
        require(cost > 0 ether, "Price cannot be zero");
​
        // Adds book to shop
        books.push(
            BookStruct(
                totalSupply++,
                msg.sender,
                title,
                description,
                author,
                cost,
                block.timestamp
            )
        );
​
        // Records book selling detail
        sellerOf[totalSupply] = msg.sender;
        bookExists[totalSupply] = true;
​
        emit Created(
            totalSupply,
            msg.sender,
            block.timestamp
        );
​
        return true;
    }
​
    // Performs book payment
    function payForBook(uint8 id)
        public payable returns (bool) {
        require(bookExists[id], "Book does not exist");
        require(msg.value >= books[id - 1].cost, "Ethers too small");
​
        // Computes payment data
        address seller = sellerOf[id];
        uint256 tax = (msg.value / 100) * taxFee;
        uint256 payment = msg.value - tax;
​
        // Bills buyer on book sale
        payTo(seller, payment);
        payTo(taxAccount, tax);
​
        // Gives book to buyer
        booksOf[msg.sender].push(books[id - 1]);
​
        emit Sale(
            id,
            msg.sender,
            seller,
            payment,
            block.timestamp
        );

        return true;
    }

    // Method 1: The transfer function
    function transferTo(
        address to,
        uint256 amount
) internal returns (bool) {
        payable(to).transfer(amount);
        return true;
    }

    // Method 2: The send function
    function sendTo(
        address to, 
        uint256 amount
) internal returns (bool) {
        require(payable(to).send(amount), "Payment failed");
        return true;
    }
​
    // Method 3: The call function
    function payTo(
        address to, 
        uint256 amount
) internal returns (bool) {
        (bool success,) = payable(to).call{value: amount}("");
        require(success, "Payment failed");
        return true;
    }
​
    // Returns books of buyer
    function myBooks(address buyer)
        external view returns (BookStruct[] memory) {
        return booksOf[buyer];
    }

    // Returns books in store
    function getBooks()
        external view returns (BookStruct[] memory) {
        return books;
    }

    // Returns a specific book by id
    function getBook(uint8 id)
        external view returns (BookStruct memory) {
        return books[id - 1];
    }
}

让我们从头开始。

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
contract BookStore {
// Codes goes here...
​
}

上面这段代码代表了智能合约的基本结构。

许可证标识符对 Solidity 编译器非常重要,当不包含它时,它就会报错。

我们指定了能够成功编译智能合约的solidity编译器的范围。最后,我们将合约名称提供为BookStore。

// Contains global store data
uint256 taxFee;
address immutable taxAccount;
​
uint8 totalSupply = 0;

我们在智能合约的开始部分包含了一些状态变量,它们将保存书店向卖家收取的每笔交易的税费。其中包括了一个税务帐户,它是对该商店征收的所有税款的接收地址。总供应量变量是一个计数器,用于跟踪书店的总图书数。

// Specifies book information
struct BookStruct {
uint8 id;
address seller;
string title;
string description;
string author;
uint256 cost;
uint256 timestamp;
​
}
​

接下来是图书结构,这是一个复杂的数据结构,用于存储关于图书的信息。一本书有一个Id、一个卖家、一个标题、一个描述、一个作者、一个价格和一个时间戳,表明它是何时被添加到我们的商店的。

// Associates books with sellers and buyers
BookStruct[] books;
mapping(address => BookStruct[]) booksOf;
mapping(uint8 => address) public sellerOf;
​
mapping(uint8 => bool) bookExists;

现在,在上面的代码中,我们使用新创建的图书结构来指定要在商店中收集的所需数据的数组。

因此,books变量是一个图书数组,而 booksOf 映射包含特定买家拥有的所有书籍。SellerOf 映射返回由图书 ID 标识的卖家地址。最后,如果存储中有指定Id的图书,则bookExists映射返回true或false。

// Logs out sales record
event Sale(
uint8 id,
address indexed buyer,
address indexed seller,
uint256 cost,
uint256 timestamp
);
// Logs out created book record
event Created(
uint8 id,
address indexed seller,
uint256 timestamp
);

上述代码是EVM(以太坊虚拟机)事件,每当卖方向我们的商店添加一本书或买方从我们的商店购买一本书时,就会触发或记录这些事件。

// Initializes tax on book sale
constructor(uint256 _taxFee) {
taxAccount = msg.sender;
taxFee = _taxFee;
}

在上面的代码中,我们将税务帐户指定为部署智能合约的人的地址。我们必须知道,一旦在部署期间分配了它,它就不能再被更改,这就是为什么我们附加了一个称为immutable的修饰符。税费是我们希望智能合约对商店中任何一本书的每笔销售收取的百分比。

// Performs book creation
function createBook(
string memory title,
string memory description,
string memory author,
uint256 cost
) public returns (bool) {
require(bytes(title).length > 0, "Title empty");
require(bytes(description).length > 0, "Description empty");
require(bytes(author).length > 0, "Description empty");
require(cost > 0 ether, "Price cannot be zero");
// Adds book to shop
books.push(
BookStruct(
totalSupply++,
msg.sender,
title,
description,
author,
cost,
block.timestamp
)
);
// Records book selling detail
sellerOf[totalSupply] = msg.sender;
bookExists[totalSupply] = true;
emit Created(
totalSupply,
msg.sender,
block.timestamp
);
return true;}

上面的函数收集一本书的信息,如卖方地址、标题、描述和价格。

接下来,它执行验证以检查卖方没有向我们的智能合约提供空数据。然后,它使用前面定义的图书结构将图书推送到books数组。

将一本书添加到书店后,立即保存重要的记录,以跟踪那些买了这本书的人和这本书的卖家。

最后,在函数完成一个布尔值之前,它向EVM发出或记录一些重要信息。

// Performs book payment
function payForBook(uint8 id)
public payable returns (bool) {
require(bookExists[id], "Book does not exist");
require(msg.value >= books[id - 1].cost, "Ethers too small");
// Computes payment data
address seller = sellerOf[id];
uint256 tax = (msg.value / 100) * taxFee;
uint256 payment = msg.value - tax;
// Bills buyer on book sale
payTo(seller, payment);
payTo(taxAccount, tax);
// Gives book to buyer
booksOf[msg.sender].push(books[id - 1]);
emit Sale(
id,
msg.sender,
seller,
payment,
block.timestamp
);
return true;
}

这是这个智能合约中最重要的功能,因为当我们把它连接到前端时,它会为我们赚钱。

当被调用时,该函数接收一个图书Id,检查图书是否存在,还检查我们是否发送了足够的以太坊来购买图书。

接下来,该函数执行一些支付计算,并将支付百分比发送给接收端的各方。

一旦付款完成,就将书交给买家,并且该销售信息在EVM上注销。

// Returns books of buyer
function myBooks(address buyer)
external view returns (BookStruct[] memory) {
return booksOf[buyer];
}

这个函数返回特定买家购买的图书列表。使用此功能时,必须提供买方地址。

// Returns books in store
function getBooks()
external view returns (BookStruct[] memory) {
return books;
​
}

上面的函数返回“添加到书店的所有图书”。任何人都可以从我们的智能合约中调用这个函数,它将以BookStruct数组格式检索并显示所有图书。

// Returns a specific book by id
function getBook(uint8 id)
external view returns (BookStruct memory) {
return books[id - 1];
}

最后,这个函数从我们的书店返回到一本书。为了成功检索,调用者必须指定图书的Id。

既然我们已经讨论了组成智能合约的各种属性和功能,现在该讨论支付处理功能了。下面的功能使我们能够使用以太坊执行交易,但是哪一个更好呢?

智能合约的3个必备函数

transferTo()函数

// Method 1: The transfer 
functionfunction transferTo(
address to,
uint256 amount
) internal returns (bool) {
payable(to).transfer(amount);
return true;
​
}

这个函数有一个内置的required方法,所以它不需要额外的方法。它快速地帮助我们将一定数量的以太坊转移到指定的地址。

sendTo()函数

// Method 2: The send function
function sendTo(
address to,
uint256 amount) internal returns (bool) {
require(payable(to).send(amount), "Payment failed");
return true;
}

现在,该函数添加了验证支付是否成功执行所需的方法。它也像transferTo函数一样简单明了。

payTo()函数

// Method 3: The call 
functionfunction payTo(
address to,
uint256 amount
) internal returns (bool) {
(bool success,) = payable(to).call{value: amount}("");
require(success, "Payment failed");
return true;
}

这比之前的函数稍微复杂一点,但它完美地完成了工作。

transferTo() VS sendTo() VS payTo()

我们应该知道使用它们的利弊。

transferTo函数:

该函数使用如下代码所示的传输方法。

receiver.transfer(amount);

有两种情况会导致转账函数失效:

  • 如果发送方在智能合约上余额不足,接收方会拒绝支付。
  • 在发生故障时,转账函数会恢复。

在发生故障时,转账函数恢复。当支付时,接收合约的fallback()或receive()函数被调用。这允许接收合约对付款作出响应。

2300gas转移到接收合约。这是一个非常小的量,但足以引起麻烦。仅仅运行复杂的代码是不够的。

sendTo函数:

这个函数使用send方法,如下面的代码所示。

receiver.send(amount);

发送和转账在行为上几乎是同义词。但是,如果付款失败,它将不会被恢复。它会返回false。调用合约负责处理故障。

如果支付了款项,则调用接收合约的fallback()或receive()函数。这允许接收合约对付款作出响应。

Send还向接收合约发送2300gas。

payTo函数:

该函数使用调用方法,如下面的代码所示。

(bool success,) = receiver.call{value: amount}("");

call()函数用于智能合约之间更个性化的交互。它能够按名称调用函数并向其发送以太坊。它现在是从智能合约发送以太坊的首选方法。

结论

我们完成了本教程,希望我们能从中获得价值。

Source:https://medium.com/coinsbench/how-to-code-a-paynow-function-with-solidity-the-3-must-know-smart-contract-methods-78c68ae6c780

关于

ChinaDeFi - ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。

本文首发于:https://mp.weixin.qq.com/s/S8xbmP1_6DoWrFKj5XZRrQ

点赞 0
收藏 2
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
ChinaDeFi 去中心化金融社区
ChinaDeFi 去中心化金融社区
ChinaDeFi.com 是一个研究驱动的DeFi创新组织,同时我们也是区块链开发团队。每天从全球超过500个优质信息源的近900篇内容中,寻找思考更具深度、梳理更为系统的内容,以最快的速度同步到中国市场提供决策辅助材料。